加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

F2812地址映射以及cmd文件详解

(2013-08-05 19:05:39)
标签:

f2812

地址映射

cmd文件详解

it

分类: DSP

用过F2812的朋友一定会对cmd文件很熟悉,因为这个文件中为每个程序和数据分配了相应的地址。我们常用的cmd文件包括连个:

(1)     DSP281x_Headers_nonBIOS.cmd

(2)     F2812_EzDSP_RAM_lnk.cmd

DSP281x_Headers_nonBIOS.cmd

上面第一个文件用于对DSP外设分配地址,而第二个文件是为系统的程序和数据分配地址。当然,如果DSP的外设地址我们用C语言已经自己定义,那第一个文件我们就可以不用了,笔者就是自己定义的,所以没有用到第一个文件。对于为什么要自己定义外设寄存器以及中断地址,有这几个原因:

(1)            自己定义外设寄存器地址可以很清楚的了解DSP的工作原理,虽然这样很耗费时间,但是会了解到DSP的中断等等是怎么工作的。

(2)            因为DSP外设寄存器地址的分配时采用寄存器形式分配到的。举个例子,以sci串口通信为例,其他的外设以及中断都一样。

比如我们设置波特率, 肯定是设置某个寄存器的相应位来实现。而DSP是这样实现的:

    ScibRegs.SCIHBAUD = (BRRVal >> 8);

    ScibRegs.SCILBAUD = (BRRVal); 

上面的程序, ScibRegs是串口的寄存器结构体,也就说说DSP的每个寄存器都是以结构体为单位来分配地址的,而每个寄存器的具体地址通过偏移来分配,因为在DSP中,某个外设的寄存器地址是基本连续的。而这个结构体的定义是这样的:

extern  volatile  struct  SCI_REGS  SciaRegs;

extern  volatile  struct  SCI_REGS  ScibRegs;

大家可以看到,这里定义了两个结构体,一个是SciaRegs ,一个是ScibRegs,因为DSP内部有两个串行通信,AB,这两个都是结构体,而类型就是前面的SCI_REGS的,这个结构体类型是这样定义的:

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;     // Recieve status register

   Uint16               SCIRXEMU;  // Recieve emulation buffer register

   union SCIRXBUF_REG   SCIRXBUF;   // Recieve data buffer 

   Uint16               rsvd1;       // reserved

   Uint16               SCITXBUF;   // Transmit data buffer

   union SCIFFTX_REG    SCIFFTX;     // FIFO transmit register

   union SCIFFRX_REG    SCIFFRX;    // FIFO recieve register

   union SCIFFCT_REG    SCIFFCT;     // FIFO control register

   Uint16               rsvd2;      // reserved

   Uint16               rsvd3;      // reserved

   union SCIPRI_REG     SCIPRI;      // FIFO Priority control  

};

也是就就是说,在分配地址的时候,只要给定义好的结构体一个起始地址,后面的寄存器地址就可以根据后面的字长进行分配。rsvd1rsvd2rsvd3表示不分配给任何寄存器,只是为了获得后面地址的偏移而已。这个结构体中还有union union SCIFFTX_REG等,而这个共同体是这样定义:

union SCIFFTX_REG {

   Uint16               all;

   struct SCIFFTX_BITS  bit;

}

对这个共同体进行操作,要么是all,要么是struct SCIFFTX_BITS  bit,而后面的这个结构体是这样定义的:

struct  SCIFFTX_BITS {       // bit    description

   Uint16 TXFFILIL:5;        // 4:0    Interrupt level

   Uint16 TXFFIENA:1;        // 5      Interrupt enable

   Uint16 TXINTCLR:1;        // 6      Clear INT flag

   Uint16 TXFFINT:1;         // 7      INT flag

   Uint16 TXFFST:5;          // 12:8   FIFO status

   Uint16 TXFIFOXRESET:1;    // 13     FIFO reset

   Uint16 SCIFFENA:1;        // 14     Enhancement enable

   Uint16 SCIRST:1;          // 15     SCI reset rx/tx channels

};

大家一看就知道这是每个寄存器的位,而后面的数字,例如TXFFILIL:5后面的5,表示这个寄存器位是连续使用5个位。其实说到这里,大家应该明白DSP是怎么使用外设寄存器的了吧,例如下面:

ScibRegs.SCIRXST.bit.RXRDY = 1;

就是把ScibRegs 结构体中,SCIRXST 寄存器的RXRDY位置1 ,而

ScibRegs.SCIRXST.all = 1;就是把SCIRXST所有位置1.

DSP的外设和系统寄存器都是这样分配的。这样有一个好处,就是这样看到每个寄存器每个位,省去了查阅datasheet的麻烦,也会时操作更明显,当然,任何事物都有两面性,这样调用至少一个结构体,会浪费很多堆栈,使系统运行迟钝。说到这里还没说地址到底置怎么分配,好我们看文件DSP281x_GlobalVariableDefs.c。还是串口通信的例子。

//----------------------------------------

#ifdef __cplusplus

#pragma DATA_SECTION("SciaRegsFile")

#else

#pragma DATA_SECTION(SciaRegs,"SciaRegsFile");

#endif

volatile  struc t  SCI_REGS  SciaRegs;

 

//----------------------------------------

#ifdef __cplusplus

#pragma DATA_SECTION("ScibRegsFile")

#else

#pragma DATA_SECTION(ScibRegs,"ScibRegsFile");

#endif

volatile  struct  SCI_REGS  ScibRegs;

 

看到上面的程序,我又必要说一个#pragma .DSP中,pragma对象告诉编译器的预处理器如何对待函数,而且必须在声明、定义、引用程序或数据之前指定 pragma #pragma后面一般接两种形式:

(1)     #pragma DATA_SECTION

(2)     #pragma CODE_SECTION

顾名思义,这两种一个是针对数据,一个是针对程序。举例说明:

#pragma CODE_SECTION(funcA,“codeA ”);

 char funcA(int i);       //对函数funcA 的声明

上面中,funcA函数一定是外部声明或者定义的函数,CODE_SECTIONfuncA 在一个名为codeA 的块中指定空间。如果你有一个代码对象并想将其链接到不同于.txt 的空间时,该语法就非常有用。而codeA.cmd文件中指定了物理地址。

#pragma DATA_SECTION(bufferB,“my_sect”);

 char bufferB [512];       // bufferB的定义放在pragma 的后面

DATA_SECTIONbufferB 在一个名为 my_sect 的块中指定空间。如果你有一个数据对象并想将其链接到不同于.bss 的空间时,该语法就非常有用. 数据块bufferB被定位于my_sect段中,my_sect段在 .cmd 文件中规定了物理地址。

看完上面的一段解释,估计您会对#pragma有了很好的了解了吧 我们使用的结构体是数据而不是函数,所以我们使用DATA_SECTION。其实就是把SciaRegsScibRegs分别在块SciaRegsFileScibRegsFile中指定空间,而块SciaRegsFileScibRegsFile.cmd文件中指定了物理地址。这样一来,是不是结构体SciaRegsScibRegs的起始地址就知道了?答案是肯定的。我们在看分析.cmd文件。.cmd文件的源代码如下:

 

MEMORY

{

 PAGE 0:   

 PAGE 1:   

   DEV_EMU     : origin = 0x000880, length = 0x000180    

   PIE_VECT     : origin = 0x000D00, length = 0x000100    

   FLASH_REGS  : origin = 0x000A80, length = 0x000060    

   CSM          : origin = 0x000AE0, length = 0x000010    

   XINTF        : origin = 0x000B20, length = 0x000020    

   CPU_TIMER0  : origin = 0x000C00, length = 0x000008      

 

   PIE_CTRL     : origin = 0x000CE0, length = 0x000020    

   ECANA       : origin = 0x006000, length = 0x000040    

   ECANA_LAM   : origin = 0x006040, length = 0x000040    

   ECANA_MOTS  : origin = 0x006080, length = 0x000040    

   ECANA_MOTO  : origin = 0x0060C0, length = 0x000040    

   ECANA_MBOX  : origin = 0x006100, length = 0x000100    

   SYSTEM       : origin = 0x007010, length = 0x000020    

   SPIA          : origin = 0x007040, length = 0x000010    

   SCIA          : origin = 0x007050, length = 0x000010    

   XINTRUPT    : origin = 0x007070, length = 0x000010    

   GPIOMUX     : origin = 0x0070C0, length = 0x000020    

   GPIODAT     : origin = 0x0070E0, length = 0x000020    

   ADC          : origin = 0x007100, length = 0x000020    

   EVA          : origin = 0x007400, length = 0x000040    

   EVB           : origin = 0x007500, length = 0x000040    

   SCIB          : origin = 0x007750, length = 0x000010    

   MCBSPA       : origin = 0x007800, length = 0x000040    

   CSM_PWL     : origin = 0x3F7FF8, length = 0x000008    

}

SECTIONS

{

   PieVectTableFile : > PIE_VECT,   PAGE = 1

 

   DevEmuRegsFile   : > DEV_EMU,       PAGE = 1

   FlashRegsFile      : > FLASH_REGS,    PAGE = 1

   CsmRegsFile      : > CSM,             PAGE = 1

   XintfRegsFile      : > XINTF,           PAGE = 1

   CpuTimer0RegsFile : > CPU_TIMER0,     PAGE = 1 

   PieCtrlRegsFile    : > PIE_CTRL,        PAGE = 1     

 

   SysCtrlRegsFile     : > SYSTEM,        PAGE = 1

   SpiaRegsFile        : > SPIA,           PAGE = 1

   SciaRegsFile       : > SCIA,            PAGE = 1

   XIntruptRegsFile    : > XINTRUPT,      PAGE = 1

   GpioMuxRegsFile   : > GPIOMUX,       PAGE = 1

   GpioDataRegsFile  : > GPIODAT         PAGE = 1

   AdcRegsFile       : > ADC,            PAGE = 1

   EvaRegsFile       : > EVA,             PAGE = 1

   EvbRegsFile       : > EVB,            PAGE = 1

   ScibRegsFile      : > SCIB,            PAGE = 1

   McbspaRegsFile    : > MCBSPA,        PAGE = 1

 

   ECanaRegsFile     : > ECANA,          PAGE = 1

   ECanaLAMRegsFile  : > ECANA_LAM    PAGE = 1  

   ECanaMboxesFile   : > ECANA_MBOX    PAGE = 1

   ECanaMOTSRegsFile : > ECANA_MOTS    PAGE = 1

   ECanaMOTORegsFile : > ECANA_MOTO   PAGE = 1

 

   CsmPwlFile        : > CSM_PWL,     PAGE = 1

}

上面的程序可以看到,  

SciaRegsFile       : > SCIA,            PAGE = 1    

ScibRegsFile       : > SCIB,            PAGE = 1

Section的作用就是把输入段定义到那个地址空间,以及哪一页。伪指令 MEMORY用来标示每个存储器的名字、起始范围和长度。Page0 是第0 页,通常规定为程序存储器;Page1 是第1 页,规定为数据存储器。伪指令SECTIONS 描述输入段怎样被组合到输出段并规定在存储器内如何放置输出段。这样,SciaRegsFile的地址就是第一页的SCIA的地址,ScibRegsFile的地址就是第二页的SCIB的地址,在伪指令MEMORY中,SCIASCIB是这样定义的:

   SCIA          : origin = 0x007050, length = 0x000010    

   SCIB          : origin = 0x007750, length = 0x000010    

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有