[资料分享] TI DSP位域寄存器文件(Bit Field and Register-File Struc...

Aguilera   2020-2-7 20:09 楼主

很多初学者往往对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))寄存器文件的定义

 

以F2812的SCI外设寄存器为例介绍寄存器文件的定义。下面的程序段用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模块的寄存器文件SciaRegs和ScibRegs。

/********************************************************************

* 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)中完成的。

默认情况下,编译器会将SciaRegs和ScibRegs这样的全局和静态变量指定到.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将寄存器文件结构变量SciaRegs和ScibRegs分别定位到名为SciaRegsFile和ScibRegsFile的section中,SciaRegsFile和ScibRegsFile是被DSP的SCI外设寄存器占据的空间。

/********************************************************************

* 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)中指定的,例如F2812的SCI-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位,那么位域会在下一个字中连续存放。

以SCICCR和SCICTL1寄存器为例说明位域的定义,两个寄存器位定义如下图所示:

 

   

应用位域定义可以将上面的寄存器用下面代码表示:

 /********************************************************************

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;

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复