维普资讯 http://www.cqvip.com 科技论文 在TMS320C28x DSP中 创建C语言可调用的汇编程序 李志一 摘要:TMS320C28xDSP, ̄片是TI-公司C2000, ̄.列定点DsP芯片的最新型号。本文举 例讨论了在开发该型号芯片程序时的三种创建c语言可调用汇编程序的方法。最后 对三种方法的利弊进行了比较。 关键词:数字信号处理c编译器shell程序交叉列表 1引言 TJ公司推出的C2000系列DSP是广泛应用在数字马达控制、工业自动化、电力转 换等领域的高性价比定点DSP。该公司新推出的TMS320C28x系列是目前C2000系列中 最先进的32位定点DSP芯片,它不但运行速度高,处理功能强大,而且具有丰富的片 内外围设备,非常便于接口和模块化的设计。为了配合其强大的32位定点运算功能,该 型芯片增加了堆栈寻址方式,丰富了DSP汇编指令集。然而,由于该型号芯片较新,目 前国内掌握该型号芯片的开发设计技术的人员不多,相关的技术资料较少,要迅速掌握 该型芯片的汇编语言有一定难度,所以,采用c语言对其开发是事半功倍的方法。 如何快速、简便地产生正确的可供c语言调用汇编代码模块,是开发TMS320C28x 一个共性难题,也是本文探讨的重点内容。 2创建一个c语言可调用的汇编语言程序 2.1必要性 DSP芯片的设计开发包括硬件和软件两个方面。在软件方面,用DSP芯片的汇编 语言编写程序是一件比较繁杂的事情,一般说来,不同公司不同类型的DSP芯片的汇编 指令是不同的。所以,用汇编语言开发基于DSP芯片的产品的周期较长,而且程序的可 读性、可移植性相对高级语言来说较差,这对于软件的修改升级是十分困难的。因此, 各个DSP芯片公司都推出了相应的高级语言(如C语言)编译器,使得DSP芯片的软件 可以直接用高级语言编写,从而令DSP芯片的开发周期大大缩短,程序的修改、移植也 变得相对简单。 但是在某些情形下,C代码的效率还是无法与手工编写的汇编代码的效率相比。即 使是最佳的 编译器也无法在所有情况下最优最合理地利用DSP芯片提供的各种资源。 特别是在某 实时性要求较高的场合(如高速的AD转换),用C语言编写的代码往往 39 维普资讯 http://www.cqvip.com 电信技术研究 2006年第5期 存在大量多余的代码,从而降低了代码执行的效率,影响了程序的实时性。此外,用C 语言实现对DSP芯片的某些硬件控制也不如汇编方便,有些甚至无法用C语言实现。 因此,大多数情况下DSP芯片的软件开发往往需要用C语言与汇编语言相结合的 混合编程方法来实现。 2.2 C与汇编语言混合编程方法 C语言与汇编语言相结合的混合编程主要有以下三种方法: (1)分别单独编写C语言、汇编语言源代码,分别编译、汇编成各自的目标代码 模块;然后用链接器将C模块和汇编模块链接起来。 (2)直接在C语言源程序中嵌入汇编语言。 (3)对C语言源程序进行编译,生成相应的汇编程序,然后对汇编程序手工优化、 修改。 无论采用何种方法,均要保证汇编模块不会破坏C语言模块的运行环境。当用C 语言调用汇编时,汇编语言必须保护所有C语言函数用到的寄存器。 2.2.1方法一 使用该方法时,C语言和汇编语言之间是依靠一些全局“符号”结合在一起的。汇编 程序中出现的一些变量(符号)应在C语言中声明过。见以下例子: WDDISABLE.set I —.ref C int00 .sect”codeStart” .if WD DISABLE==l LB wd disable .else LB C intO0 .endif .if WD DISABLE==1 .text wd disable: SETC OBJMODE EALLOW MOVZ DE#7029h>>6 MOV@7029h,#0068h EDIS LB C int00 .endif .end 上述汇编例程是在DSP芯片启动后,在代码开始阶段用汇编语言写的用来关闭看门 狗模块的代码,代码中符号_cint00是将程序最终引向C语言main()函数的传递符号。 40 维普资讯 http://www.cqvip.com 科技论文 c—intO0是由Tl公司随C2000系列DSP芯片发布的rts2xx.1ib目标文件库(包含了针 对TMS320C2xx系列DSP的ANSI标准运行支持函数及编译公用程序函数)中定义的函 数符号。在汇编语言中,调用C语言中函数符号、变量符号时,在符号前应加入“._”符。 以下是汇编后的截图。 一一一一一一一一一一一一一一 一一一一一一一一 ▲ ~ 曩}E8000 ~text text一,Code Stort.asm:34:465,wd—disab1e: 摩3EB口00 56IF SETC OBJMODE . 3E800l 7622 EALLOW — 3E8002 B9C0 M0VZ DP, 448 3E8003 2829 MOV 皂4 1,#Ox0068 3E8005 76lA EDIS FR006 007F LB c int00 芏闭善门置 F; 业盎}莓II :n+nn^ ,} 三LJ 一一一—一 一 一 进入c—intO0函数代码,并最终进入C语言的main()主函数: 妇 l } 一 3EFBE9 6F05 SB 5,UNC 3EFBEA 8F3F MOVL XAR4, 0x3FFFFF 3EFBEC 92C4 MOV AL,-+XAR4【0】 3EFBED DC02 ADDB XAR4, 2 3E FB EE 767ELC~—一——R一 ,l mo in 跳转入C语言的mainf1主函数 l 由以上各图我们可以清楚的看到“符号”在将汇编语言和C语言结合在一起是如何 起作用的。 下面我们再举一例,看如何单独书写一段’汇编程序,然后将其变成可供C语言调用 的函数。见下列汇编代码: .defDelayUS —.text DelayUS: RPT≠}143 I INOP SUBB ACC.≠}1 BFDelayUS,GT —LRETR .end 41 维普资讯 http://www.cqvip.com 电信技术研究 2006年第5期 这是一段用于产生us延时的汇编程序,且又有如下一段C语言程序调用Delay us 函数: extern void Delayus(int); _main() {intx; x=5: Delayus(x); } 竹 即印即即 一即即印即 模块中被声明成外部函数来调用。编译链接后,导入DSP芯片执行情况如下图: ~明叭 盯 M 船 由上面程序所示,在汇编模块中声明并定义的全局变量符- ̄-_Delay_us,在C语言 一 旺 % ¨ 臻奄囊 。 ~” 叩 ~叩叭∞ m 肿肿 嗨Ⅷv ~舯刚:若 .PR 明 盯 ; 舅 ‘ m …一 ・ 即子函数Delay us(int) i Q j .u晏正 丑王 ACC. 1 Delayus.asm:17:275.GT _从图中可以看到C语言是如何通过“符号”调用汇编模块的。 2.2.2方法二 直接在C语言中嵌入汇编语句时,书写有特殊的语法,见下面例子: asm(”AND IFR,#OxO”); / 清CPU级中断的INT1,INT9 / asm(”OR IER,#Oxl 0 ; 使能CPU级中断的INT1,INT9 / ‘ asm(”CLRC INTM”); / 开所有可屏蔽中断 / 如例子语句所示,在C语言中直接用asm语句即可嵌入汇编语句,这样在DSP的C 编译器编译C语言时,编译器直接将汇编代码汇编进目标代码。 下图是编译后产生的汇编语言目标代码截图。 j 图可以清楚地看到C编译器是如何将C语言中嵌入的汇编语句编译在程序中的。 42 维普资讯 http://www.cqvip.com 科技论文 2.2.3方法三 在方法一的第二个例子中,我们单独写了一段汇编程序,然后在C程序中对其进行 了调用。这种方式需要分别对c代码、汇编代码进行调试,需要对汇编指令集有较深入 的了解,所以开发周期较长调试代码工作繁琐,且容易出错。于是我们还可以通过另一 方法来实现上述目的: 首先,用c语言书写可被c语言主函数调用的子函数,即这段c语言程序是为了 生成最后的汇编模块的shell程序(外壳程序);接着通过c编译器编译shell程序,产生 带交叉引用列表的汇编程序;然后再对产生的汇编程序手工修改、优化,生成一个可供 c语言调用的汇编函数模块;最后,在c语言主函数中调用这个汇编函数模块。 这是一种快速简便的方法,无需对汇编指令集深入的了解,且容易生成正确、高效 的汇编代码,但在修改、优化带交叉引用列表的汇编程序时应注意不能破坏c语言的运 行环境。下面举例对这一方法进行讨论: 第一步:有如下c语言的子函数(shell程序)要被主函数调用 eltterll iat ̄ample(int,int,int); iat m,a,b,c; -J{ 一j llRil n() a : b。 : C : 主函数 ,l ~ m—example(a,b,c1; :~ 上i int example(int x,int y,iat z) : iat n; n=x+y-z: l ̄atllra(n): ’ 主函数(sheIl程序) j ● 第二步:要产生带交叉引用列表的汇编程序,必须对c编译器环境进行一些配置,这里 作者使用的是TI公司发布的CCS3.1集成开发环境: (1)在CCS中建立一个工程,如ccallasm.pjt。 (2)向工程中添加链接命令文件12812nonBIOSram.cmd、库文件rts2800一m1.1ib、 一—需要的头文件.h,等等必须文件。 (3)将主函数文件C call asm.C、shell程序文件example.C加入工程。 (4)配置编译器选项,即在Build Options对话框中设置选项。产生带交叉引用列 表的汇编程序使用了.k、.SS、.al、.d,,LARGE MODEL”、.ml、.v28等选项。其中: 一k 保留汇编文件 -SS 对汇编源程序和C进行内部列表; 产生汇编语言列表文件; MODEL”和-m l选项是选择大存储模式,是选择C28xx为目标机的默认 v28选择C28xx为目标机; -al --LARGE选项。 43 维普资讯 http://www.cqvip.com 电信技术研究 2006年第5期 (5)编译后产生带交叉引用列表的汇编程序 第三步:修改、优化交叉引用列表汇编程序: 编译器编译后的交叉引用列表程序如上面二图所示,黄色部分为一些注释和函数符 号调试信息。去掉程序中的黄色部分,剩下的程序如下图所示: 对剩下的程序进行优化。因为程序用AR4,AL,AH传递函数的参数,所以可以不 用堆栈寻址方式。于是程序被进一步优化,如下图: 第四步:在C语言主函数中调用这个汇编函数模块 44 维普资讯 http://www.cqvip.com 科技论文 保存该汇编程序为exampleoptimize.asm文件。将原工程中的example.C文件从工 _程中移除,并将exampleoptimize.asm文件添加进工程,重新编译并链接程序。运行程 _序如下: 3,J、结 使用第一种方法时,必须遵循DSP有关的调用规则和寄存器规则,以保证汇编模块 不破坏C语言的运行环境。特别是在用汇编语言写中断程序服务子程序时,必须保护C 语言环境中用到的所有寄存器。这需要熟悉芯片的硬件资源,掌握大量的汇编指令集。 因此开发效率较低,且代码容易出错。但是这种方法可以产生执行效率较高的代码,有 利于程序的实时性能。 第二种方法可以在C语言程序中实现一些C语言无法实现的对硬件的控制功能,如 修改中断寄存器、使中断使能或无效、读取状态寄存器和中断标志寄存器等等功能。但 是这种方法容易破坏C语言的运行环境,而且采用这种方法后,对程序编译时不能采用 优化功能,否则将使程序产生不可预知的结果 第三种方法其实是快速正确地实现第一种方案的另一种方法,它能较好的避免采用 第一种方案时的缺点,但在优化程序时仍要注意不能破坏C语言的运行环境。 综上所述,三种在C语言中调用汇编的方法各有利弊。在不同的情况下,我们应根 据程序的需求选用适当的方法。 参考文献 f 1 1 TM¥320F/C24x DSP Controllers Reretrice Guide:CPU and Instruction Set.Literayure Number:SPRU 1 60C.June 1 999 [21 TMS320C 1 x/C2x/C2xx/C5x Assembly language Tools User’S Guide.Literature Number: SPRUO18D.1995 [3】TMS320C2x/C2xx/5x 0ptimizing C Compiler User’S Guide.Literature Number: SPRU024E,Augu ̄1 999 [4】An Easy Way of Creating a C—callable Assembly Function for the TMS320C28x DSP. I itcrature Number:SPRA806.December 200 1 45