APP下载

DSP覆盖(Overlay)程序设计

2014-01-15李尚柏

电子设计工程 2014年21期
关键词:拷贝存储器程序设计

吴 洁,李尚柏

(四川大学 原子核科学技术研究所 辐射物理及技术教育部重点实验室,四川 成都 610064)

通常DSP程序是被加载到DSP的内部存储器中执行的。调试过程中,用CCS集成环境将编译好的代码通过JTAG接口下载到DSP的内部存储器执行;调试完成后,将程序代码烧录到DSP的外部存储器,在 DSP上电时,自动将程序代码装载到内部存储器执行[1]。但是,DSP的内部存储器有限的。如果程序代码及其使用的数据区大于DSP的内部存储器,就会限制DSP程序的开发。解决这种困局的一个有效的方法是使用Overlay程序设计技术。

早期计算机的内存是非常小的,又要完成较为复杂的任务,开发人员提出了Overlay的程序设计思想。随着大规模集成电路技术的发展,拥有更多的计算机内存已不是奢望,Overlay技术似乎已逐渐被遗忘。然而,对嵌入式系统和DSP而言,内部存储器仍然是稀缺资源,Overlay技术仍然具有用武之地[2]。本文将讨论DSP Overlay程序的设计技术和实现步骤,并通过一个具体的实例在C6000系列DSP上来实现Overlay程序设计技术。

1 链接命令文件

在DSP上,对目标系统存储器的配置以及对段(代码段、数据段等)的配置是实现Overlay的关键技术之一。链接命令文件是一个以.cmd为后缀的文本文件,其主要用途就是定义目标存储器的模型,以及指定模块要加载的位置。它包含:要链接的目标文件名、链接器选项、MEMORY指令(定义目标系统的存储器的配置)、SECTIONS指令 (定义段在目标系统中的配置)。表1给出了链接命令文件中主要的保留字。

表1 命令链接文件主要保留字Tab.1 The main reserved words of command link file

UNION:产生一个联合段,其中的输出段具有相同的运行地址。当程序代码很大,不能加载到目标系统的内部存储器中执行时,希望互相独立的段运行在相同的地址[3]。

2 Overlay源程序设计

本文讨论的DSP Overlay程序设计主要是解决内存资源不足的问题,方法是用若干模块构建一个联合段,使联合段中各成员模块共享一块运行内存,需要执行哪个模块就把它从装载内存复制到运行内存来执行。由此可见,联合段中的成员模块具有互斥性,也就是说,在同一时间只能运行一个联合段中的模块。因此,本DSP Overlay源程序的设计就是把要完成的任务合理的划分成多个功能模块[4]。

2.1 Overlay设计实例

在本DSP Overlay程序设计实例中,设计了4个功能模块和两个公用模块。每个功能模块的源代码分别存储为独立的源文件task1.c(计算出两个整数的和,再乘以比例系数ratio)、task2.c(调用公共函数 IntSub,并返回 IntSub 的结果)、task3.c(计算两个整数的积)、task4.c(计算两个整数的平方和)。公用模块vectors.asm包含中断矢量的映射,main.c包含硬件设备的初始化、中断服务程序和公用函数。链接命令文件overly.cmd是每个DSP程序不可或缺的。命令链接文件是Overlay设计的关键,下面将重点讨论其使用和Overlay程序的设计思想。

1)链接命令文件overlay.cmd,如程序1所示:程序1链接命令文件

/*目标系统存储器配置*/

MEMORY

{

RAM :origin=0x00000000,len=0x010000/*数据存储器*/

OVLMEM :origin=0x00010000,len=0x010000/*Overlay模块运行存储器*/

ROM :origin=0x00020000,len=0x020000/*程序存储器*/

SDRAM :origin=0x80000000,len=0x1000000/*扩展存储器*/

}

/*段配置*/

SECTIONS

{

.vectors>ROM /*中断映射段配置*/

.text >ROM /*公共代码段配置*/

.bss >RAM /*未初始化数据段配置*/

.cinit>RAM /*已初始化数据段配置*/

.const>RAM /*常数段配置*/

.far >RAM /*远指针段配置*/

.stack >RAM /*堆栈段配置*/

.cio >RAM /*流式I/O函数缓冲区配置*/

.sysmem>RAM/*系统堆内存段配置*/

/*联合段配置*/

UNION

{

.task12:{debug ask1.obj (.text), debug ask2.obj(.text)}

load >> SDRAM, table (BINIT),table(_task12_ctbl)

.task34:{debug ask3.obj (.text), debug ask4.obj(.text)}

load>>SDRAM, table(_task34_ctbl)

}run=OVLMEM

.ovly:{}>RAM/*联合段拷贝表段配置*/

.binit:{}>RAM/*引导时的联合段拷贝表配置*/

}

在上述程序1MEMORY命令中,定义了目标系统的存储器配置,设置了数据存储器 (RAM)、联合段模块运行存储器(OVLMEM)、程序存储器(ROM)、扩展存储器(SDRAM)的起始地址和长度。在SECTIONS命令中,首先定义了输出段的配置,将可执行代码段 (.text和.vectors)配置到程序存储器(ROM), 将 其 它 的 数 据 段 (.bss、.cinit、.const、.far、.cio 和 .sysmem)配置到数据存储器(RAM);其次还配置了联合段,在联合段中使用的table(arg)算符使得链接器生成了一个拷贝表(此拷贝表必须具有唯一的名字),同时用arg生成一个变量名,应用程序可以用这个变量名访问拷贝表。如果指定table(BINIT)(arg为BINIT),则链接器产生一个引导时的拷贝表,也即产生一个默认的拷贝表段,供系统初始加载时使用[5]。

图1 实例的内存配置和段配置Fig.1 The instance of memory configuration and section configuration

本程序设计中,链接器根据链接命令文件(程序1)产生的目标系统内存配置和段配置如图1所示。图中左侧部分是MEMORY所定义的系统存储器配置,右侧部分是根据SECTIONS的描述产生的模块定位信息。链接器把所有的数据段定位到RAM存储器。实际使用数据空间的段依次为系统堆栈段.stack(长度为 0x7d0)、已初始化数据段.cinit(长度为 0x264)、远指针段.far(长度为 0x250)、常数段.const(长度为 0x20)、未初始化段.bss(长度为 0x14)、拷贝表段.ovly(长度为0x20)、引导加载拷贝表段.binit(长度为0x10)。链接器把代码段.text定位在ROM存储器,占用存储空间0x20a0字节。联合段的.task12和.task34分别定位在SDRAM存储器的0x8000000和0x80000100处,分别占用存储空间0x100和0x0e0字节。当程序运行时,根据需要把联合段的成员拷贝到OVLMEM存储空间来执行。

对本DSP Overlay程序设计实例的工程项目编译链接后,打开映射文件(*.map)可以看到链接器产生的详细信息,下面给出内存配置信息、段定位信息和拷贝表信息:

①内存配置信息(MEMORY CONFIGURATION)

name origin length used attr fill

RAM 00000000 00010000 00000d00 RWIX

OVLMEM 00010000 00010000 00000100 RWIX

ROM 00020000 00040000 00001fa0 RWIX

SDRAM 80000000 01000000 000001e0 RWIX

由于本实例比较简单,所以内存的使用也较少。对于复杂的任务,或内存资源较少的目标系统,ROM空间可能不足以加载所有代码。

②段定位信息(SECTION ALLOCATION MAP)

output attributes/

section page origin length input sections

-------- ----- ---------- ----------

.task12 0 80000000 00000100 RUN ADDR=00010000

80000000 000000c0 task1.obj(.text)

800000c0 00000040 task2.obj (.text)

.task34 0 80000100 000000e0 RUN ADDR=00010000

80000100 00000080 task3.obj (.text)

80000180 00000060 task4.obj (.text)

.ovly 0 00000cd0 00000030

联合段.task12和.task34的运行地址均为0x00010000,但各自的加载地址不同,.task12的加载地址为0x80000000,.task34的加载地址为0x80000100。拷贝表段.ovly起始地址为0x00000cd0,长度为 0x30,包含 _task12_ctbl、_task34_ctbl和BINIT3个表项。

③拷贝表信息(LINKER GENERATED COPY TABLES)

_task12_ctbl@ 00000cd0 records:1, size/record:12,table size:16

.task12:copy 256 bytes from load addr=80000000 to run addr=00010000

_task34_ctbl@ 00000ce0 records:1, size/record:12,table size:16

.task34:copy 224 bytes from load addr=80000100 to run addr=00010000

BINT@00000cf0 records:1,size/record:12,table size:16

.task12:copy 256 bytes from load addr=80000000 to run addr=00010000

拷贝表信息有3条记录,第一条记录说明拷贝表段task12_ctbl的存储地址为 0x0cd0,有 1个记录,每个记录的大小为12字节,拷贝表的大小为16字节。.task12段的加载地址为0x80000000,运行地址为0x00010000,拷贝的长度为256字节。加载.task12模块时从0x8000000拷贝256个字节到0x00010000;同理,第二条记录列出了拷贝表段task34_ctbl的具体信息;第三记录是引导时加载的拷贝表信息,由于在链接命令文件中指定.task12段为引导时的加载段,所以第三条记录与第一条相同。

2)公共模块程序设计

公共模块包含了硬件设备的初始化、中断服务程序、公用函数和运行主函数。从图2可以看出,主函数主要完成两项任务,初始化和Overlay模块的加载执行。初始化工作主要包括芯片支持库初始化,系统时钟初始化。在初始化系统时钟之前要求关闭所有中断。对外部总线进行初始化才能使目标板上扩展的SDRAM存储器正常工作。本实例使用定时器调度Overlay模块的执行,所以还需进行定时器初始化和中断初始化。从图2也可以看出,在定时器事件的驱动下,两个Overlay模块交替加载和执行。当第一个定时器事件发生时,ovlyFlag=0,将.task34段拷贝到运行内存,然后依次执行task3()、task4()和 IntSub()函数。 task3()和 task4()是联合段.task34中的函数,必须先将.task34段拷贝到内存中才能调用这两个函数。IntSub()是公共模块中的函数,它常驻内存,随时都可以执行。当第二个定时器事件发生时,ovlyFlag=1,将.task12段拷贝到运行内存,然后依次执行 task1()、task2()和IntAdd()函数。这三个函数是联合段.task12中的函数,必须先将.task12段拷贝到内存中才能调用。

3)功能模块设计

本应用实例设计了4个功能模块。模块1设计了该模块的局部函数 int IntAdd(int,int)(实现两个整数的加法)和功能函数 int task1(int, int)(加载后由主函数调用)。 task1 调用局部函数IntAdd,并将所得到的结果与全局变量ratio相乘。模块2设计了一个功能函数task2(int,int),加载后由主函数调用。task2调用全局函数IntSub(int,int),实现两个整数的减法。模块3和模块4分别设计了一个功能函数task3(int,int)和 task4(int, int),task3 实现了两个整数的乘积,task4 实现了两个整数的平方和。

2.2 Overlay模块的动态加载

多个Overlay模块共享同一块运行内存,因而必须根据需要动态地加载Overlay模块。要实现这个目的,必须知道链接器所产生的拷贝表结构。拷贝表的结构信息定义在运行支持库的头文件cpy_tbl.h中。对于每个需要动态加载的模块,链接器都会为其产生一个COPY_RECORD结构对象(包含装载地址、运行地址和需要拷贝的代码长度)。链接器将所有COPY_RECORD结构对象组织成一个COPY_TABLE结构对象。根据COPY_TABLE的内容,将代码从装载地址拷贝到运行地址[6]。装载的task34模块的程序代码如程序2所示。

图2 main函数的执行流程Fig.2 The execution process of main funtion

程序2 Overlay段task34的装载和执行

copy_in(&task34_ctbl); //拷贝.task34 段代码

CACHE_invAllL1p(); //更新 cache

asm(“nop 5”); //消除流水线的影响

val1=task3(taskIn1,taskIn1); //调用 task3,执行 a*b

val2=task4(taskIn1,taskIn1); //调 用 task4, 执 行a*a+b*b

val3=IntSub(val1,val2);//调用全局函数,执行 2a-3b

值得注意的是,当所使用的DSP具有cache(高速缓存)时,在调用完copy_in函数之后,执行刚刚加载的代码之前,应该刷新程序cache的内容。

3 Overlay程序的调试和运行

多个Overlay模块共享一块运行内存,代码是动态加载的,程序的跟踪调试有所不同。

3.1 加载Overlay代码模块到外部内存

在实际的运行中,为了调试方便,暂时将Overlay模块的加载地址定位在SDRAM,待调试完成后,再将其定位到目标系统的程序存储器。调试时,在下载.task12段和.task34段时可能会出现错误提示,可以暂时忽略此错误。当下载完成后,在外总线初始化函数EMIFInit()之后设置断点,然后运行程序到所设置的断点处,此时外部总线已被初始化,重新下载*.out文件,就能避免这个错误。

3.2 Overlay代码的跟踪调试

在跟踪调试时,如果想跟踪调试某一Overlay模块,可以暂时不把该模块放在联合段中,待调试完成后,再把它添加到联合段中。为此,除了要修改链接命令文件外,还要修改Overlay模块的加载代码。例如要调试.task34模块,可以修改链接器命令文件中部分代码如下:

SECTIONS

……

/*.task34:{debug ask3.obj(.text), debug ask4.obj(.text) }

load>>SDRAM, table(_task34_ctbl)*/

……

}

由于在联合段中删除了.task34段,链接器将把目标文件task3.obj和task4.obj的.text段链接到统一的.text输出段中,并把它定位到ROM存储器。经上述修改,函数task3(int,int)和task4(int,int)将成为公共代码的一部分,编译、链接、下载后,即可在源代码级跟踪调试它们。此方法是将要调试的模块作为公共模块的一部分,任何时候都可在源代码级跟踪调试。

4 结束语

文中通过讨论DSP覆盖(Overlay)程序设计技术的开发方法,有效解决了由于DSP的内部存储器有限,当程序代码及其使用的数据区大于DSP的内部存储器时,则会限制DSP程序的开发这一困局,并在TMS320C6713B上通过调试和运行,成功应用于基于DSP的继电保护测试仪中,取得了满意的结果。

[1]马喜强,刘维亚,郑喜凤.基于多通信方式实现DSP程序在线编程[J].电子器件,2013,36(1):112-115.MA Xi-qiang,LIU Wei-ya,ZHENG Xi-feng.On-line programming of DSP based on multiple communications[J].Chinese Journal of Electron Devices,2013,36(1):112-115.

[2]李声飞,代华山.基于串口通信的DSP程序动态加载技术[J].电讯技术,2011(6):121-124.LI Sheng-fei,DAI Hua-shan.Dynamic locading technology for DSP program based on serial communication[J].Telecommunication Engineering,2011(6):121-124.

[3]孙滨,周杨,郭晓东.动态链接库技术及其应用[M].电脑编程技巧与维护,2009.SUN Bin,ZHOU Yang,GUO Xiao-dong.Dynamic link library technology and application[M].Computer Programming Skills and Maintenance,2009.

[4]王楠.基于Overlay期刊的网络开放学术资源建设与服务研究[D].兰州:兰州大学,2010.

[5]夏爽.DSP的二级加载及Bootloader研究 [J].电脑编程技巧与维护,2009(10):8-11,16.XIA Shuang.The secondrey loading based on DSP and bootloader research[M].Computer Programming Skills and Maintenance,2009(10):8-11,16.

[6]杨音颖.基于Overlay网络的应用层组播系统的研究与实现[J].计算机应用,2005,24(9):61-64.YANG Yin-ying.Application level multicast system based on overlay network[J].Computer Applications,2005,24(9):61-64.

猜你喜欢

拷贝存储器程序设计
静态随机存储器在轨自检算法
基于Visual Studio Code的C语言程序设计实践教学探索
从细节入手,谈PLC程序设计技巧
唐氏综合征是因为“拷贝”走样了
文化拷贝应该如何“拷”
高职高专院校C语言程序设计教学改革探索
任意2~k点存储器结构傅里叶处理器
PLC梯形图程序设计技巧及应用
存储器——安格尔(墨西哥)▲
基于硬盘还原卡的数据传送技术在高校网络机房中的应用