DSP

TI DSP位域寄存器文件(Bit Field and Register-File Structur

2019-07-13 17:13发布

很多初学者往往对TI DSP的大量的寄存器定义和组织形式感到迷茫,我从TI文档中翻译了这篇文章,希望能对初学者有所帮助。   以往寄存器定义一般用宏定义。例如: /******************************************************************** * Traditional header file ********************************************************************/ #define Uint16 unsigned int #define Uint32 unsigned long // Memory Map // Addr Register #define SCICCRA (volatile Uint16 *)0x7050 // 0x7050 SCI-A Communications Control #define SCICTL1A (volatile Uint16 *)0x7051 // 0x7051 SCI-A Control Register 1 #define SCIHBAUDA (volatile Uint16 *)0x7052 // 0x7052 SCI-A Baud Register, High Bits #define SCILBAUDA (volatile Uint16 *)0x7053 // 0x7053 SCI-A Baud Register, Low Bits #define SCICTL2A (volatile Uint16 *)0x7054 // 0x7054 SCI-A Control Register 2 #define SCIRXSTA (volatile Uint16 *)0x7055 // 0x7055 SCI-A Receive Status #define SCIRXEMUA (volatile Uint16 *)0x7056 // 0x7056 SCI-A Receive Emulation Data Buffer #define SCIRXBUFA (volatile Uint16 *)0x7057 // 0x7057 SCI-A Receive Data Buffer #define SCITXBUFA (volatile Uint16 *)0x7059 // 0x7059 SCI-A Transmit Data Buffer #define SCIFFTXA (volatile Uint16 *)0x705A // 0x705A SCI-A FIFO Transmit #define SCIFFRXA (volatile Uint16 *)0x705B // 0x705B SCI-A FIFO Receive #define SCIFFCTA (volatile Uint16 *)0x705C // 0x705C SCI-A FIFO Control #define SCIPRIA (volatile Uint16 *)0x705F // 0x705F SCI-A Priority Control #define SCICCRB (volatile Uint16 *)0x7750 // 0x7750 SCI-B Communications Control #define SCICTL1B (volatile Uint16 *)0x7751 // 0x7751 SCI-B Control Register 1 #define SCIHBAUDB (volatile Uint16 *)0x7752 // 0x7752 SCI-B Baud Register, High Bits #define SCILBAUDB (volatile Uint16 *)0x7753 // 0x7753 SCI-B Baud Register, Low Bits #define SCICTL2B (volatile Uint16 *)0x7754 // 0x7754 SCI-B Control Register 2 #define SCIRXSTB (volatile Uint16 *)0x7755 // 0x7755 SCI-B Receive Status #define SCIRXEMUB (volatile Uint16 *)0x7756 // 0x7756 SCI-B Receive Emulation Data Buffer #define SCIRXBUFB (volatile Uint16 *)0x7757 // 0x7757 SCI-B Receive Data Buffer #define SCITXBUFB (volatile Uint16 *)0x7759 // 0x7759 SCI-B Transmit Data Buffer #define SCIFFTXB (volatile Uint16 *)0x775A // 0x775A SCI-B FIFO Transmit #define SCIFFRXB (volatile Uint16 *)0x775B // 0x775B SCI-B FIFO Receive #define SCIFFCTB (volatile Uint16 *)0x775C // 0x775C SCI-B FIFO Control #define SCIPRIB (volatile Uint16 *)0x775F // 0x775F SCI-B Priority Control
使用位域寄存器文件结构,可以更加灵活和高效的访问DSP的寄存器。 寄存器文件结构:用C/C++中结构体成员的方式将DSP各种外设的寄存器的集合在一起,称之为寄存器文件结构。 位域定义:位域用于定义寄存器中每个功能块的名字和长度。 1)寄存器文件的定义   F2812SCI外设寄存器为例介绍寄存器文件的定义。下面的程序段用C/C++结构体成员的方式将SCI各个寄存器组合成为一个结构体类型SCI_REGS /******************************************************************** * SCI header file * Defines a register file structure for the SCI peripheral ********************************************************************/ #define Uint16 unsigned int #define Uint32 unsigned long struct SCI_REGS { Uint16 SCICCR_REG SCICCR; // Communications control register Uint16 SCICTL1_REG SCICTL1; // Control register 1 Uint16 SCIHBAUD; // Baud rate (high) register Uint16 SCILBAUD; // Baud rate (low) register Uint16 SCICTL2_REG SCICTL2; // Control register 2 Uint16 SCIRXST_REG SCIRXST; // Receive status register Uint16 SCIRXEMU; // Receive emulation buffer register Uint16 SCIRXBUF_REG SCIRXBUF; // Receive data buffer Uint16 rsvd1; // reserved Uint16 SCITXBUF; // Transmit data buffer Uint16 SCIFFTX_REG SCIFFTX; // FIFO transmit register Uint16 SCIFFRX_REG SCIFFRX; // FIFO receive register Uint16 SCIFFCT_REG SCIFFCT; // FIFO control register Uint16 rsvd2; // reserved Uint16 rsvd3; // reserved Uint16 SCIPRI_REG SCIPRI; // FIFO Priority control };   单纯的结构体定义本身并没有生成任何变量,下面的代码段用结构体类型SCI_REGS定义了DSP的两个SCI模块的寄存器文件SciaRegsScibRegs /******************************************************************** * Source file using register-file structures * Create a variable for each of the SCI register files ********************************************************************/ volatile struct SCI_REGS SciaRegs; volatile struct SCI_REGS ScibRegs; 上面代码中的关键字volatile非常重要,一个被声明为volatile的变量可以随时被程序本身之外的事件改变。例如,外设寄存器可以被DSP硬件本身或中断改变,如果寄存器没有被声明为volatile,编译器就会假设寄存器只会在它在代码中出现的地方被改变,从而编译器可能会把在其他地方访问寄存器看作不必要而优化掉。一个被声明了volatile的变量的访问是不会被优化掉的。 2)寄存器文件结构的空间分配 编译器为代码和数据分配和重定位的存储块,这些块称为section。将section指定到存储空间是在链接命令文件(linker command file)中完成的。 默认情况下,编译器会将SciaRegsScibRegs这样的全局和静态变量指定到.ebss或者.bss section。但是,在基于硬件抽象层(abstraction layer, hardware abstraction layer是操作系统中逻辑层中的硬件层,通过硬件抽象层,可以使操作系统忽略硬件细节,以一种抽象方式来访问硬件)的情况下,寄存器文件变量被定位到同一个外设寄存器区域。应用编译器的预处理指令DATA_SECTION,每一个寄存器变量都被指定到.bss/.ebss之外的一个特定的数据段。 C中,预处理指令DATA_SECTION的语法是: #pragma DATA_SECTION (symbol,"section name") C++中,预处理指令DATA_SECTION的语法是: #pragma DATA_SECTION ("section name") DATA_SECTION预处理指令将symbol定位到名为section name的存储空间中。下面的代码中,应用预处理指令DATA_SECTION将寄存器文件结构变量SciaRegsScibRegs分别定位到名为SciaRegsFileScibRegsFilesection中,SciaRegsFileScibRegsFile是被DSPSCI外设寄存器占据的空间。 /******************************************************************** * Assign variables to data sections using the #pragma compiler statement * C and C++ use different forms of the #pragma statement * When compiling a C++ program, the compiler will define __cplusplus automatically ********************************************************************/ //---------------------------------------- #ifdef __cplusplus #pragma DATA_SECTION("SciaRegsFile") #else #pragma DATA_SECTION(SciaRegs,"SciaRegsFile"); #endif volatile struct SCI_REGS SciaRegs; //---------------------------------------- #ifdef __cplusplus #pragma DATA_SECTION("ScibRegsFile") #else #pragma DATA_SECTION(ScibRegs,"ScibRegsFile"); #endif volatile struct SCI_REGS ScibRegs; 重复使用这种方式可将DSP每个外设寄存器文件配置到特定的寄存器空间中去。外设寄存器存储空间地址是在链接命令文件(linker command)中指定的,例如F2812SCI-A寄存器空间地址是从0x7050开始的,因此寄存器文件结构体变量SciaRegs被分配到从0x7050开始的空间中,空间分配是在命令文件(.cmd)中定义的,如下面代码所示: /******************************************************************** * Memory linker .cmd file * Assign the SCI register-file structures to the corresponding memory ********************************************************************/ MEMORY { ... PAGE 1: SCIA : origin = 0x007050, length = 0x000010 /* SCI-A registers */ SCIB : origin = 0x007750, length = 0x000010 /* SCI-B registers */ ... } SECTIONS { ... SciaRegsFile : > SCIA, PAGE = 1 ScibRegsFile : > SCIB, PAGE = 1 ... } 将寄存器结构体变量分配到DSP寄存器空间地址中,就可以在C/C++代码通过引用结构体成员的方式来访问寄存器。如 /******************************************************************** * User's source file ********************************************************************/ ... SciaRegs.SCICCR = SCICCRA_MASK; ScibRegs.SCICCR = SCICCRB_MASK; ... 3)位域定义 在实际中经常会对寄存器的某些特点位进行读写,位域定义可以灵活的完成这些操作。这里应用了C/C++中位段的概念。 C28x器件中,遵循如下位域规则: <1>在存储空间中,位域成员存储顺序是从右到左。即最低有效位(零位),对应寄存器的第一个位域。 <2>C28x编译器限制位域的长度为最大16位。 <3>如果在一个结构体中定义的所有位域的总位数超过16位,那么位域会在下一个字中连续存放。 SCICCRSCICTL1寄存器为例说明位域的定义,两个寄存器位定义如下图所示:       应用位域定义可以将上面的寄存器用下面代码表示:  /******************************************************************** SCI header file ********************************************************************/ //---------------------------------------------------------- // SCICCR communication control register bit definitions: // struct SCICCR_BITS { // bit description Uint16 SCICHAR:3; // 2:0 Character length control Uint16 ADDRIDLE_MODE:1; // 3 ADDR/IDLE Mode control Uint16 LOOPBKENA:1; // 4 Loop Back enable Uint16 PARITYENA:1; // 5 Parity enable Uint16 PARITY:1; // 6 Even or Odd Parity Uint16 STOPBITS:1; // 7 Number of Stop Bits Uint16 rsvd1:8; // 15:8 reserved }; //------------------------------------------- // SCICTL1 control register 1 bit definitions: // struct SCICTL1_BITS { // bit description Uint16 RXENA:1; // 0 SCI receiver enable Uint16 TXENA:1; // 1 SCI transmitter enable Uint16 SLEEP:1; // 2 SCI sleep Uint16 TXWAKE:1; // 3 Transmitter wakeup method Uint16 rsvd:1; // 4 reserved Uint16 SWRESET:1; // 5 Software reset Uint16 RXERRINTENA:1; // 6 Receive interrupt enable Uint16 rsvd1:9; // 15:7 reserved }; 4)应用共用体 位域定义可以实现对寄存器位的操作,但是对整个寄存器的操作也是必需的。应用共用体可以方便的实现这个功能。对SCI communications control 寄存器和控制寄存器1的共用体定义如下面代码所示: /******************************************************************** * SCI header file ********************************************************************/ union SCICCR_REG { Uint16 all; struct SCICCR_BITS bit; }; union SCICTL1_REG { Uint16 all; struct SCICTL1_BITS bit; }; 一旦寄存器的位域和共用体定义建立起来,那么寄存器文件结构体可以以共用体为成员建立。如下面代码所示,注意并不是所有寄存器都有位域定义,有的寄存器,例如SCITXBUF,只会被整体访问,没有必要对它的某些位进行访问。 /******************************************************************** * SCI header file ********************************************************************/ //--------------------------------------------------------------------------- // SCI Register File: // struct SCI_REGS { union SCICCR_REG SCICCR; // Communications control register union SCICTL1_REG SCICTL1; // Control register 1 Uint16 SCIHBAUD; // Baud rate (high) register Uint16 SCILBAUD; // Baud rate (low) register union SCICTL2_REG SCICTL2; // Control register 2 union SCIRXST_REG SCIRXST; // Receive status register Uint16 SCIRXEMU; // Receive emulation buffer register union SCIRXBUF_REG SCIRXBUF; // Receive data buffer Uint16 rsvd1; // reserved Uint16 SCITXBUF; // Transmit data buffer union SCIFFTX_REG SCIFFTX; // FIFO transmit register union SCIFFRX_REG SCIFFRX; // FIFO receive register union SCIFFCT_REG SCIFFCT; // FIFO control register Uint16 rsvd2; // reserved Uint16 rsvd3; // reserved union SCIPRI_REG SCIPRI; // FIFO Priority control };   经过这样定义后,可以方便灵活在C/C++中对DSP的寄存器进行读写,如下面代码所示: /******************************************************************** * User's source file ********************************************************************/ // Access registers without a bit field definition (.all, .bit not used) SciaRegs.SCIHBAUD = 0; SciaRegs.SCILBAUD = 1; // Write to bit fields in SCI-A SCICTL1 SciaRegs.SCICTL1.bit.SWRESET = 0; SciaRegs.SCICTL1.bit.SWRESET = 1; SciaRegs.SCIFFCT.bit.ABDCLR = 1; SciaRegs.SCIFFCT.bit.CDC = 1; // Poll (i.e., read) a bit while(SciaRegs.SCIFFCT.bit.CDC == 1) { } // Write to the whole SCI-B SCICTL1/2 registers (use .all) ScibRegs.SCICTL1.all = 0x0003; ScibRegs.SCICTL2.all = 0x0000;