找回密码
 注册
关于网站域名变更的通知
查看: 287|回复: 1
打印 上一主题 下一主题

TI DSP CMD文件编写

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-5-21 10:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
最近在学习使用TI的DSP,CMD文件的编写始终是个无法绕过的坎,TI官网上能够提供的资料非常有限,也可能是我英文水平太低了。还好度娘帮忙找到不少国人撰写的CMD文件的编写方法,其中感觉写的最通俗最全面的是网名玄德写的《CMD文件的原理》,我也是通过它才算真正理解CMD。
! q0 s- R9 g7 P' @  q2 L- L
    下面我总结下自己对CMD文件的理解和编写方法,一方面是帮助自己记忆和备忘,另一方面给有同样疑惑的童鞋们做个参考。自己也是初学者,理解上可能会有些错误,希望有大虾指正。
    CMD的专业名称叫链接器配置文件,主要功能是描述程序代码编译后产生的各代码段在DSP地址空间中的存放位置。CCS(TIDSP开发工具软件)编译后会产生一个*.map的文件,在这个文件里描述了编译完成后会产生的各种代码段以及段名,这些是需要用CMD文件来帮助CCS定位安排到DSP的地址空间的。
如下所例:下面是从*.map中截取的一段描述程序编译后会产生的代码段以及段名,可以看到各个段已经被CCS编译器自动安排好具体存储空间。假如没有CMD文件定义地址空间分配,CCS编译器会自动按照某个规则自己定义,这个规则嘛我也不清楚,当然CCS胡乱定义的结果就是程序根本无法被载入DSP。
SEGMENT ALLOCATION MAP
run origin  load origin   length   initlength attrs members
$ E- T) G. |8 w) q1 j: N----------  ----------- ---------- ----------- ----- -------% s5 z1 h0 V  T, [/ G& E0 u: A
00800000    00800000    00005d40   00005d40    r-x
- W# n) Q$ c) @% E: k: E5 o00800000    00800000    00005d40   00005d40    r-x.text
& c. f+ @3 x1 A# k00805d40    00805d40    00001000   00000000    rw-
. @1 Y! J; `1 ~' w) f0 t/ e4 O00805d40    00805d40    00000800   00000000    rw-.stack+ e7 c) a5 g+ X2 L5 V  R
00806540    00806540    00000800   00000000    rw-.sysmem
: h; M4 }" f$ O6 ~/ Y00806d40    00806d40    0000031c   0000031c    rw-
4 a  F; F& o+ N$ s& f) a00806d40    00806d40    0000031c   0000031c    rw-.fardata
& A1 M. o' E2 D* l) D/ \! G$ k00807060    00807060    00000218   00000000    rw-- }3 I4 O) H/ R! o& j1 ?8 V# p1 E
00807060    00807060    00000218   00000000    rw-.far0 w+ ^0 b* M" L- c" \
00807280    00807280    00000134   00000134    r--
) M; u5 U2 N6 _00807280    00807280    00000134   00000134    r--.const
# M7 B# {! Z* h008073b4    008073b4    00000120   00000000    rw-- x5 C7 l1 y" F/ X6 V- k# Z  Y4 T
008073b4    008073b4    00000120   00000000    rw-.cio5 C  b% a; f! m1 y! P5 b$ Z" w
008074d4    008074d4    00000094   00000094    r--4 w. Z) a4 @: m/ l( W' f# K
008074d4    008074d4    00000094   00000094    r--.cinit
    根据上面的列表,我们知道了在CMD中需要定位哪些东西了。
    CMD文件分成两个部分:一个是声明DSP所拥有的存储空间,另一个是分配这些存储空间给程序代码。如下所示:
(PS:在CMD文件中写注释,必须使用来分隔,不允许用//,这点和C语言不同)
MEMORY
( I! q2 f9 k- X$ j4 ]8 Q{6 r8 B4 X' l" j5 Y- r
   L1D:    o=0x00f00000l=0x00008000
2 h9 b8 |, I* G4 |1 j# ^   L1P:    o=0x00e00000l=0x00008000
4 S+ k+ Z, F8 ]   L2:     o=0x00800000l=0x001F0000
; \, `+ _8 J( Q* q7 j& Z9 f* D* d" d; ^5 [   L2A:    o=0x009F0000l=0x000100001 A1 Y9 R$ U/ N3 o0 D6 E+ S0 v3 o( r
   RESMEM: o=0xE0000000 l=0x01000000/ z1 ^. P  Q0 c/ V5 v. N) P
}
    MEMORY是一个关键字必须大写,后面的{}里声明了DSP所拥有的存储空间,不需要把全部的可用存储空间都声明一遍,只需要将你需要用到的部分声明一次,当然你多声明一点也没关系。在很多资料里经常会看到PAGE0、PAGE1这样的关键字,和我这里列的不同,其实这些只是助记用的,CMD的写法规则里并没有说非得定义这些,只是程序员们为了区分地址空间的类别,方便记忆,减少错误用的,而编译器并不会去识别这些东西。可以理解成这只是DSP程序员们之间的潜规则,哈哈。通常PAGE0用来定义ROM(掉电非易失型存储器),PAGE1用来定义RAM(掉电易失型存储器),也许还能分的更详细些,不过我见过的CMD文件一般只分这两类。在《CMD文件的原理》一文中,将RAM和ROM严格区分开,在我看来是没有必要的,都是DSP内部寻址的地址空间,编译器已经将各个编译好的代码段分类,变量,堆栈这类经常需要改变,对速度很敏感但掉电后丢失无所谓的数据就塞入RAM空间,如果对速度还有进一步要求的可以指定到DSP内部RAM空间。而对程序指令代码,常量参数这类需要掉电继续保存不会被改变的数据,则应该被安排到ROM空间。
    这里我要提醒各位同学,DSP编程也是需要很多硬件知识的,DSP内部带的ROM和RAM空间都很小,甚至有些DSP就没有带ROM,只带很小的RAM空间,就这点RAM空间想跑大型程序,那基本是妄想,所以我们经常需要外扩RAM和ROM。这里说一句,片内的RAM都是都是1个时钟周期完成1次读或同时完成1次读写的高速RAM,比外扩的RAM速度快很多。
    先说ROM,常用的ROM有EEPROM、NOR FLASH、NANO FLASH,具体的区别参见我的博客:
《EEPROM/NOR FLASH/NANO FLASH的区别》一文,附送传送门
    EEPROM因为容量通常不大,已经渐渐淡出DSP的应用,现在用的比较多的是NOR FLASH和NANO FLASH,尤其是性价比最高的NANO FLASH,虽然操作最麻烦,但在成本高于一切的IT领域,NANO FLASH才是王道。DSP有一个专门的接口比如I2C、UHPI等借口来驱动这些外部ROM存储器,寻址范围需要查阅DSP的芯片手册。
再说说RAM,一般DSP都会用DRAM来作为RAM的扩充,比如早期的SDRAM,DDR,DDR2,一直到现在的DDR3。也是通过一个专门的驱动接口比如EMIF接口来挂载这些RAM空间。
    对我们程序员来说,我们不需要去深入理解这些外扩的RAM和ROM是如何被驱动的,这里我们只谈CMD的编写,所以我们只要知道这些RAM和ROM对应的寻址空间是可用的,并在CMD文件中加以声明。
    再解释下 L1D:    o=0x00f00000l=0x00008000; A& P( [6 t7 f' v% @+ E+ r
    L1D是自己定义的,你想叫什么都可以,只要你自己能理解这是什么就成,当然也不能太长。它代表的是从0x00f00000开始,长度为0x00008000的一段存储空间。o和l分别是org和length的简写,这两个是关键字。
0 h- D; a0 t; @! @& [
    DSP拥有哪些存储空间呢?这个你同样得去查DSP的芯片手册了,英文不好的同学赶紧加紧补习英文吧,不要求能说会道,起码能看懂芯片手册,谁让这些高端的DSP都是人家老外做的。
    这里以我自己在学习的TMS320TCI6614为例:
    从TI的官网上下载到这个DSP的芯片手册sprs671c.pdf,看到2.2 Memory Map Summary章节,TMS320TCI6614是一个很大的DSP,下面我只截取其中相关的一小段。
Logical 32 bit Address Physical 36 bitAddress9 l3 P% c! k" J3 I
Start     End          Start       End         Bytes  Description  B1 n0 L5 \7 i) Y  D
0000 0000 007F FFFF    0 0000 0000 0 007F FFFF8M     Reserved8 L. g) A" h" z1 ~/ S
0080 0000 008F FFFF    0 0080 0000 0 008FFFFF 1M     L2 SRAM
2 d" \" K7 D; F' v( L6 g; z0 q7 o0090 0000 00DF FFFF    0 0090 0000 0 00DFFFFF 5M     Reserved
/ r) s$ [# x4 O2 `00E0 0000 00E0 7FFF    0 00E0 0000 000E0 7FFF 32K    L1P SRAM/ H8 j' k& H6 b4 n4 v$ e8 u3 U1 Q5 A
00E0 8000 00EF FFFF    0 00E0 8000 000EF FFFF 1M-32K Reserved
, l: s7 R9 x, @5 [% ~9 L3 v, o8 f0 ^, _00F0 0000 00F0 7FFF    0 00F0 0000 0 00F07FFF 32K    L1D SRAM: C1 X: s0 c3 e: r2 M% S
00F0 8000 00FF FFFF    0 00F0 8000 0 00FFFFFF 1M-32K Reserved
    从中各位同学应该能发现其中的规律,我就不多费口舌了。
    下面再说下对这些声明过的存储空间
, i  Y: F& v% m# f' E# DSECTIONS* l' i! z+ o& O* C8 ?" T
{6 @3 t6 F* ?( o0 u
  .bss        >L2
0 S3 z- J$ S! r3 b  .far        >L2/ a# R  X$ }" S$ T1 a# k# G( m+ L
  .text       >L29 H& X; {) s% ^0 i/ M* O
  .cinit      >L2' u6 g/ s, V, j% u
  .const      >L2
. a* w! h& f5 V& i. Y9 P  .stack      >L2
8 m+ @9 q# Y1 C9 s& C6 {" C6 U  .cio        >L2
8 t& c, g- a) s+ F; ]  .sysmem     >L2
6 t8 k  ~+ A6 I5 L4 q/ N  .switch     >L2% G4 ^- m  H$ d
  .fardata    > L25 V( l& w2 m# M  {' x  H
  .neardata   > L2
5 U0 L7 M* I( e/ C  \) X: i. x  .rodata     >L2
" C; d- ]3 T& \# P! d  .test       >L27 a5 `6 ^- Y5 F& a
  .init       >L2
9 b5 K$ `1 x  o( o  W5 J0 ?}
    如前文所述,DSP需要分配存储资源的数据都在*.map文件中能够找到,所以要合理分配这些存储资源对优化DSP执行效率非常重要。
    SECTIONS是分配存储空间的关键字,必须大写,后面跟着的{}中是给各个数据段分配的空间,至于这些数据段在被分配的空间中如何具体安排,这些是编译器自动完成的,不需要我们操心。
    上面只是一个非常简单的例子,把所有的段落都定义到了L2这个存储空间,当时我只是用来跑hello world不需要掉电保存这些功能,也不要外部去扩充RAM。“.bss”是数据段的名称,部分是关键字,具体含义后面解释,也可以自定义一些数据段,具体做法也在后面详述;“〉”是指定的意思;“L2”则是前面声明过的存储空间的一部分。语法很简单,如果有做过PAGE1这类多层声明,则应该再写成“.bss > L2 PAGE1”
    其实上面的是简略写法,实际的语法结构应该是:
    .bss:  {所有.bss输入段名}    load=加载地址  run =运行地址
    “{所有.bss输入段名}”这段内容用来说明连接器输出段的.text段由哪些子目标文件的段组成。如果没有则省略{}也是可以的。
    load和run,链接器为每个输出段都在目标存储器里分配两个地址:一个是加载地址,一个是运行地址。通常情况下两个地址是相同的,可以认为输出段只有一个地址,这时就可以不加“run =运行地址”这条语句了;但有时需要将两个地址分开,比如将程序加载到FLASH,然后放到RAM中高速运行,这就用到了运行地址和加载地址的分别配置了。
, ^) q0 Q2 M  r* i- [    load和run有一些简化写法,首先“load”关键字可以省略,“=”可以写成“>”, “加载地址”可以是:地址值、存储区间的名字、PAGE关键词等,所以大家见到“.text:{ } > 0×0080”这样的语句可千万不要奇怪。“run =运行地址”中的“ = ”可以用“>”其它的简化写法就没有了。大家不要乱用。
    下面介绍下这些字段都是什么意思
    .text   关键字  程序指令代码编译后形成的二进制数据,需要放入ROM类型存储器中
    .vector关键字  中断跳转服务程序的入口地址数据,需要放入ROM类型存储器中(这一点还是有点晕)
    .cinit  关键字  程序中的变量初值和常数,需要放入ROM类型存储器中
    .stack  关键字  系统堆栈,用于保存返回地址、函数间的参数传递、存储局部变量和保存中间结果
    .bss    关键字  程序中的全局变量和静态变量,需要放入RAM类型存储器中
    .const  关键字  程序中的字符常量、浮点常量以及用const声明的常量
    .sysmem关键字  用于程序中的malloc、calloc 、和realoc 函数动态分配
    .test   自定义  自定义的数据段,怎么放看你的需求
自定义的段落用法:
int a[200];//定义了1个数组
#pragma DATA_SECTION (a".test")//将这个数组变量归入test这个段落,假如没有这个段落,a这个数组变量将会归入.bss段落,这样独立出来好处是能对某些变量定义到速度更快的空间,提高运行效率。
PS:其他关键字和用法等待补充。

  U4 e! k; ^: J/ e1 b% a/ o' R1 e
注:本文转自网络,如侵权请联系删除。
- _' a* M- f9 ?5 N, g1 n- t
  • TA的每日心情
    开心
    2022-11-22 15:53
  • 签到天数: 2 天

    [LV.1]初来乍到

    2#
    发表于 2020-5-21 10:57 | 只看该作者
    同作者,我的英语也是硬伤
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-26 02:00 , Processed in 0.156250 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表