NIOS软件设计流程和方法
1软件开发包
NIOS嵌入式处理器是经过专门优化过的软核CPU,以便于在可编程逻辑器件上实现SOPC设计。Altera。公司提供SOPC Buildei。系统开发工具,来帮助用户创建采用NIOS处理器的软硬件系统:硬件设计师利用SOPC Builder构建软硬件开发的基础,SOPC Builder的生成结果将作为特定用户设计的开发软件的起点。
当SOPC Builder生成一个NIOS处理器设计时,会完成以下工作:
1.系统存储器映像的一致性检查。对外设地址和中断优先级进行惟一性验证,检查其是否在CPU的有效访问范围之内。如果不是,报告相应的错误,并在继续下一步以前修正。
2.为NIOS系统生成一个定制的软件开发包(Soflw~e Development Kit,SDK)。
3.生成NIOS处理器系统的硬件设计文件。硬件设计师可以用这些文件来创建NIOS系统硬件。
1.1软件开发流程
在进行NIOS软件开发时,通常按照下面的过程进行。
步骤1:获得目标N10S系统的SDK
从SOPC Builder创建的工程目录中(或从负责NIOS处理器硬件系统的设计师那里)得到目标NIOS处理器系统的SDK。软件开发环境的基础是由SOPC Builder生成的SDK目录。SDK中包含的头文件和库文件,提供了硬件映像地址和一些基本的硬件访问子程序,有效地减少了原有分工模式下的软硬件衔接工作。
步骤2:建立和编译应用软件
首先使用文本编辑器,用C/C++或汇编语言编写应用程序的源代码(.c或.s);然后使用nios-build命令或Makefile将源代码编译成可执行代码。编译产生的二进制码以S-record的格式(.sere)存储。对于一个小型或中型的软件项目,使用nios-build就可以编译生成可执行代码:对于一个大型项目,则必须参照由SOPC Builder生成的示例Makefile编写自己的Makefile,来编译生成可执行代码。
步骤3:下载可执行代码到目标板 如果用户的目标系统中包含了GERMS监控程序,则它将扮演起调试器的角色,允许用户运行可执行代码、读写存储器、下载代码(或数据)段到存储器和擦除闪存。将GERMS监控程序分配到处理器的引导地址(通常位于片上ROM),用户可以立即进行代码开发、下载和调试。
如果用户的目标系统中不包含GERMS监控程序,而是包含了OCI调试模块,则可以通过JTAG端口对目标系统进行代码的下载和调试。
步骤4:调试代码
如果采用了GERMS监控程序,那么调试信息通过printf()函数由标准输入输出设备来发送。标准输入输出设备可以是UART,也可以是NIOS OCI调试模块。nios.mn命令可以用作一个终端来显示这些调试信息。
如果需要更细致的调试,可以通过JTAG下载电缆,用NIOS OCI调试模块来访问NIOSCPU。通过NIOS OCI调试模块,能够执行单步调试代码,检查存储器和寄存器内容等。
也可以通过把编译器调试选项设成ON,而重新编译代码,使用GNu调试器(GDB)。更为高级的调试T具可以从第三方供应商那里得到,在第八章里会对这些调试手段进行比较详细的介绍。
步骤5:转变威自日l导代码
一旦应用程序代码经充分调试之后,用户可能希望把可执行代码存储到目标系统的非易失存储器里,以便在NIOS CPU复位后立刻执行用户程序。有两种方式存储代码到非易失存储器中:
● 存储代码到片外闪存
有两种方法可以把代码存储到片外存储器。使用NIOS开发包提供的工具可以方便地把程序代码存储到闪存。在CPU初始化之后,GERMS监控程序能自动执行这些代码。下面讲述把代码存储到片外存储器的两种方法:
1.如果保留GERMS监控程序:使用srec2flash命令把以.srec格式存储的目标代码转换成能烧到板上闪存里的.flash文件。该命令在代码转换过程中在目标代码前添加一小段代码,使得系统启动时把闪存内的可执行代码复制到SRAM(或SDRAM)后,然后执行SRAM中的代码。用户得到.flash文件后,可以用Ilr命令将代码存储到闪存中。注意这个烧录闪存的过程只对AMD29系列的闪存(或兼容产品,如ssT的39系列)有效,如果不兼容,用户需要修改GERMS内关于FLASH的相关子程序。
2.完全去除GERMS监控程序后重新编译硬件:改变NIOS CPL7的复位地址,使其指向闪存里的程序。对闪存的烧录过程需要用户自己编写相应的程序。
● 存储代码到片上存储器
如果代码规模足够小,把代码存储到片上存储器是一种很不错的选择。硬件设计师用SOPC Builder为片上RAM或ROM块指定存储器初始化文件。在这种情况下,设计师可以完全去除GERMS监控程序,并以自动引导的应用程序代替。
1.2 SDK树目录简介
SOPC Builder为每个新创建的NIOS cPu生成一个定制软件开发包(SDK)。SDK为用户提供软件开发的基础,SDK由下面几部分组成:
● c语言头文件,包括每个外设在存储器中的地址映像和相应数据结构
● 与外设相关的子程序被编译后生成的编译库
● 用于重新编译库的Makefile文件
● 示例源代码和Makefile文件
默认情况下,SOPC Builder生成的SDK目录将作为Quartus Ⅱ硬件工程的子目录,当然用户也可以复制SDK目录到任意子目录中使用。SDK目录以CPU的名称(CPU的名称在SOPC Builder中命名)为前缀,通过附加_sdk来命名。例如,NIOS开发包中的大部分硬件参考设计都包含一个名为cpu的简单NIOS CPU。SOPC Builder为每个参考设计生成一个名为cpu_sdk的SDK,那么最后生成的SDK目录如下所示:
…/_cpu_sdk/ │ +———inc/ │ +———lib/ │ +———src/
1.2.1 inc目录
SDK的头文件目录(inc)包含应用程序需要使用的头文件。这些文件定义了外设的地址、中断优先级、寄存器结构以及其他有用的常量和宏。如果在程序中要使用这些特性,需要在用c或c++编写的源文件中包含excalibur.h,在用汇编语言编写的源文件中包含excalibur.s。
图5.1显示了一个典型的inc目录,具体的内容会根据定制的NIOS系统而改变。
图5.1 inc目录示例
excalibu.h (excalibur.s)头文件不仅包括系统中每个外设的寄存器在存储器地址上的映像,还包括每个外设相关的c语言子程序。对于c程序,寄存器映像以结构体的形式提供。下面的代码是以定时器模块为例,从excalibur.h文件中摘录的一段内容。物理地址根据不同的NIOS系统可能与下面的例子不同。
前缀np_代表NIOS的外设。
NIOS系统中的每个寄存器都定义为整型变量,使得无论是32位还是16位的NIOS处理器的软件都能以同样方式使用该结构体。
如果需要对寄存器的位操作,通常需要定义额外的常量,如掩码和位数来引用这些控制位(NIOS汇编语言指令SKP0和SKPl都需要指明测试的是寄存器中的哪一位)。
excalibur.h和excalibur.s同时提供了所有外设的地址、中断号及其他有用的常量。下面的代码摘录了excatLibur.h文件部分内容。
na_timerl这个名字由外设Timer和前缀na_(代表“NIOS地址”)构成。na_timerl是一个被强制转换为类型“np_timer*”的数值,这就使得na_timerl可作为一个指向定时器结构的指针。下面是一个访问定时器的示例代码:
在excalibur.h文件里的选项
在excalibur.h文件中定义了以下的选项:
__nios_catch_irqs__
当_nios_catch_irqs_设置成1时,系统默认为每个中断设定一个默认的中断服务程序。把设置改为0可以节省少量的代码空间。
_nios_use_constructors_
当_nios_use_constructors__设置成1时,NIOS库中会包含一部分初始化代码,这些初始化代码用于静态地分配c++类。默认__nios_use_constructors__被设置成1。如果不需要C++类的静态初始化,那么设置为0可以减少编译后的软件代码量。这在建立需要小容量ROM存储器的软件时非常有用。
_nios_use_cwpmgr_ 当_nios_use_cwpmgr__设置成1时,NIOS库中包含了用于处理寄存器窗口下溢的代码。设置为0可以减少编译后软件的代码量,只有在代码调用深度没有超过寄存器文件大小的时候才能这么做。
_nios_use_fast_mul_.
这个选项在excalibur.h文件里定义,但是必须和.../lib/Makefile文件里的NIOS_USE MULTIPLY和NIOS_USE_MSTEP设置联合起来才起作用。
_nios_use_small_printf__
在GNU库里的标准printf()子程序大约有40KB的NIOS代码。它支持完整的ANSIprintf()规范,包括对浮点数的支持。当__nios_use_small_printf__设置成1时,NIOS库中将只包含一个精简的,大概1KB代码的printf()版本。这个“small printf”仅仅支持整数和%c,%s,%d,%x及%x的格式。该选项设置(为1)时,系统可以发送调试信息到标准输入输出设备(一个UART),且不会显着增加代码量。
nios_macros.s
这个文件包括各种有用的汇编语言宏和伪指令,具体请参阅附录一。
1.2.2 lib目录
SDK库目录(1ib)包含NIOS系统软件库的Makefile文件、档案文件、源代码和目标文件等(如图5.4)。
一些源文件是用汇编语言编写的,其他是用C语言编写的。此外也包含每个文件中子程序的汇编(或编译)版本,可以直接被链接到用户程序中。
图5.4 SDK库目录示例
nios-build命令在建立32位系统时使用libnios32.a文件,而在建立16位的nios系统时用libniosl6.a文件。Makefile包含了重新编译存档文件的指令。
1.3 编译后代码结构
用nios-build命令编译c语言程序,最终会生成S-record文件,通过该文件可以看到编译后的代码,存储器地址映像如表5.1所示:
表5.1存储器布局
注意:nasys_program_mem默认是在SOPC Builder中指定的程序存储器地址,在nios builder命令中提供相关选项修改该值。
1.4 NIOS库函数
NIOS系统中的SDK包括预编译的库文件libnios32.a(对于32位NIOS系统)或libniosl6.a(对于16位NIOS系统)。根据NIOS系统外设的不同而选择可用的子程序。以 下为一些常用的子程序。
1.4.1 C语言运行支持函数
在一个C语言程序运行之前,系统必须完成一些初始化工作。所以,当用nios-build编译和链接一个C语言程序后,第一个执行的子程序是“_start”,它完成C语言运行环境的初始化工作,然后调用main()子程序。此外,标准的C语言库还依赖于几个底层与平台相关的子程序。
表5.2列出了一些由NIOS库提供的底层C语言运行环境支持子程序。
表5.2 C语言运行支持子程序
(1)注意:这个子程序比标准子程序快,它使用MUL或MSTEP指令(如果有的话),而不用寄存器窗口。但比标准子程序 使用更多的代码空间。
_Start
NIOS CPU在启动时执行的第一条指令是跳转至_start的跳转指令,然后才执行实际的_start代码指令。在用户软件能够运行之前,_start子程序必须执行系统的初始化工作,初始化的步骤有:
1.初始化栈指针,使之指向nasys_stack_top 。 2.将在_bss_start与_end之间的程序存储器空间清零。
3.把“_end”赋值给内部一个名叫“RAMLimit”的变量(从这里开始向上分配存储器空间)。
4.选择性地安装CWP管理器。
5.选择性地调用C++的静态构造函数。
6.调用子程序main(),它通常是用户C子程序的主入口。
7.如果main()由返回值返回,忽略它的返回值,然后重新启动监控程序(如果有的话)。
1.4.2 系统服务程序
NIOS SDK通常包括本节讨论的系统服务程序,用户在Makefile文件中可以指定或取消这些程序。
中断服务处理子程序
NIOS处理器允许63个中断,中断号从0到62。中断号越小,优先级越高。中断0到 15保留给系统服务,其余的47个中断向量留给用户应用程序,并可采用以下子程序安装用户中断的服务子程序。
nr_installuserisr
该子程序为一个指定的中断号安装用户中断服务子程序。这是一种方便的但低效的安装中断服务程序的方法。
如果采用nr_installuserisr建立中断向量表,则任何标准的C函数都能被指定为中断服务子程序。这对于那些对NIOS底层中断机制不是很熟悉的软件设计师而言是很有用的。该函数在头文件excalibur.h中声明。
如果用户需要直接操纵中断向量表,那么必须透彻理解NIOS的寄存器窗口及控制寄存器等的机制,只有这样中断请求才能够被处理并且正常地返回。
当用户中断服务子程序被调用时只接收一个context参数。中断服务子程序必须清除所服务的外设的中断触发源。
nr_installuSeriSr2 这个子程序与nr_installuserisr相似,除了当用户中断服务子程序被调用时,中断号、被中断的PC值和context参数都被传递给用户中断服务程序。
第七章中有使用这些子程序的代码实例。
当前寄存器窗口指针(CWP)管理器
编写NIOS软件并不需要详细地了解窗口寄存器文件。在调用子程序时,当前寄存器窗口指针(CWP)管理器程序处理寄存器文件操作的细节。大部分用户的最终软件都需要CWP管理器。
void_nr_installcwpmanager(Void)
如果_nios_use_cwpmgr_选项为1,则_start()自动调用此子程序。它为NIOS CPU寄存器窗口的上溢及下溢异常安装中断服务程序。此函数在头文件excalibur.h中声明。
缓存管理子程序
数据缓存和nios-elf-gcc优化编译器的任务都是加速总线和存储器之间的通讯速度。数据缓存和编译器都是通过跟踪当前读写的存储器地址来实现此目的的。尽可能使用这些存储在数据缓存中的数据,而不直接访问存储器。不过其前提条件都是假设存储器的内容只能被当前运行的软件改变。如果存储器被另一个能改变它的CPU共享,或如果访问的地址是硬件外设,则不需要使用数据缓存和编译优化。
这时,必须通过声明volatile类型来告之编译器既不优化也不访问数据缓存。使得编译器总是访问存储器而不访问数据缓存;此外,如果使用了_mdcache选项,则将执行PFXIO指令。
下面是一个外设结构使用volatile关键字的例子:
为了操作NIOS指令和数据缓存,NlOS库中有如下子程序如表5-3所示,这些子程序仅在含有缓存的NIOS系统中才能正确运行。
表5.3 缓存管理子程序
系统提供的其他程序
NIOS的SDK中还提供了两个子程序,方便用户编写及调试软件。
●void nr_delay (int milliseconds)
这个子程序会使程序暂停执行若干毫秒。实际上该程序是根据NIOS CPU的时钟频率,执行相应次数的循环来完成延时功能的。此函数在头文件excalibur.h 中声明。
milliseconds参数是指明延时时间长度(单位:毫秒)。在这段时间里,程序将被挂起。该子程序没有使用硬件定时器,因此定时并不是特别精确。
● void nr_zerorange (char*rangeStart,int rangeByteCount)
该子程序从rangeStart开始写入rangeByteCount个零。此函数在头文件excalibur.h中声明。
两个参数分别为需要清零的第一个字节地址和清零的字节数。
1.5高级C语言支持
NIOS SDK一般总是含有以下两个子程序,如果需要,用户可以在Makefile文件中指定取消这些子程序,如表5.4所示。
表5.4 高级C支持子程序
2软件开发常用命令
NIOS SDK Shell提供几个通用软件开发命令,它通过在PC平台上提供一个类UNIX bash环境来进行软件开发,熟悉UNIX使用起来会更方便一些。另外,开发包中还包括许多为生成和调试NIOS软件用的专用命令,如表5.5所示。
表5.5 NIOS命令
注:在nios SDK Shell提示符下,可使用——h选项来查询任意NIOS命令的用法细节。
下面只介绍部分常用命令的用法,对于其他命令请参考Altera的相关手册。
2.1 hexout2flaSh
Quartus Ⅱ和MAX+PLUS Ⅱ软件能够生成用于下载到Altera PLD的配置文件。QuartusⅡ生成的一种配置文件格式为.hexout。hexout2flash用于将.hexout文件转换为能写入开发板闪存的.flash文件。Hexout2flash会创建一系列GERMS监控命令来擦除一段闪存空间,并且.hexout文件复位位到已擦除的空间开始写入数据。
用法如下:
hexout2flash [options] <filename> [.hexout]
选项,如表5.6所示。
表5.6 hexout2flash选项(部分列表)
实例
1.对于名为my_design.hexout的文件,输入下面命令转换出可写入FLASH的文件:
hexout2flash my_design.hexout
hexout2flash将my_design.hexout转换为mydesign.hexout.flash
2.输入下列命令下载.flash文件到开发板:
nios-run my_design.hexout.flash
2.2 nios_build
NIOS-build调用相关工具进行编译、汇编、连接NIOS源代码。它确保标准C语言库和NIOS库能与用户源代码链接,同时“include”路径也可访问。大多数程序都可以用不带命令行选项的nios_build编译。
nios-build命令是Makefile文件的一种简单替换方式。NIOS软件开发环境完全支持使用Makefile。Makefile的例子可以参考.../scr/makefile_example。
nios-build命令生成一个后缀为.srec的文件,并以命令行的最后一个源文件名作为srec文件的文件名。此文件可下载到NIOS开发板上。下载前需要GERMS监控程序已在NIOS开发板上运行或者CPU内包含OCI调试模块。
源文件罗列在命令行选项后面。如果只指定一个源文件名,nios-build在当前目录搜寻那些基本名相同并带有下划线的文件(见下面的实例)。
以.S和.asm结尾的文件被传递给nios-elf-as。以.c结尾的文件被传递给nios-elf-gcc。以.0结尾的文件被传递给nios-elf-ld。
用法如下:
nios-build[options] <sourcefile>.[sco]
选项如表5.7所示
表5.7 nios-build选项(部分列表)
实例如下:
niOS-build foo.C bar.s
编译多个源文件,生成的可执行文件名为bar.srec。
niosuild helloworld.c
如果文件helloworld_2.C和helloworld_3.S在同样的目录下,它们也将被一起编译,最后结果生成文件helloworld.srec。
2.3 nios-console
NIOS-console命令打开一个OCI调试控制台并装载srec文件,以便进行调试。此外,它还将设置ociBase配置参数,PC寄存器(如有可能),并将调试控制台的工作目录更改为调用nios-console的目录。
NIOS-console会在.../sopc_builder/bin/FS2/bin目录中调用一个usentcl文件。这个文件包含调试控制台的Tcl命令,这些命令在调试控制台启动时自动运行。
用法如下:
nios-console [options] filename]
选项如表5.8所示。
表5.8 nios_console 选项(部分列表)
实例
nios-console foo.srec
启动OCI调试控制台,通过JTAG立即下载foo.srec到目标系统。
2.4 nios-conved
NIOS-convert将文件从一种格式转换为另一种格式。源文件可以是.srec或者.mif;目标文件可以是.mif或者.dat 。 NIOS-convert的主要功能如F:
● 将可执行软件代码或数据文件(.srec格式)转换为Altera PLD中的片上存储器的初始化文件(.mif格式)
● 转换数据宽度。这非常有用,比如,将32位数据存储到16位片外闪存
● 将较宽数据分割成多个字节段。比如,将32位数据分割为2个16位数据段,以 便写到两个并行使用的16位片外闪存
如果不指定目标文件名,目标文件名同源文件名。
用法
nios-convert [options] <sourcefile> [destFile]
选项如表5.9所示。
表5.9 nios-convert 选项(部分列表)
实例
nios-convert bootcode.srec bootcode.mif
将文件bootcode.srec转换为bootcode.mif。
2.5 nios-debug
NIOS—debug命令启动图形调试环境。该命令设置适当的环境变量,并将指定的.out文件下载到目标开发板上。NIOS-debug也可以指定一个.srec文件,这种情况下,该命令会搜索一个与之匹配的.out文件。
用法
Nios_debug [options]<filename>
选项如表5.10所示
表5.10 nios-debug选项(部分列表)
实例
nios-debug foo.out
连接到目标板,装载可执行代码,并启动执行,而后在main()处中断。
nios-debug foo.srec
效果同nios-debug foo.out。
2.6 nios-run
GERMS监控程序在运行状态下,nios-run命令可以下载代码或数据到NIOS目标系统。nios-run也可用作一个终端程序,同运行在NIOS开发板上的GERMS监控程序或其他程序进行通信。
当给定一文件名作为参数时,nios-run发送此文件中的内容到NIOS开发板。通常是.srec格式或.flash格式的文件,它们被分别下载到SRAM和闪存中。
用法
nios-run [option(s)] [filename]
选项如表5.11所示。
表5.11 nios_run 选项
例子
nios-run hello_world.srec
通过OCI调试模块下载hello_world到开发板上。如果没有OCI调试模块,则通过COMl下载。
ios-run-P com2 hello_world.srec
通过COM2 口把可执行文件hello_world.srec下载到开发板。
2.7 srec2flash
srec2flash命令将.srec文件转换成.flash文件。.flash文件可下载到开发板上的闪存中。
在系统启动时,GERMS监控程序查看闪存中nasys_main_flash+0x40000地址处的代码。如在闪存中找到用户代码,则执行之。srec2flash把.srec文件转换为一个.flash文件。.flash文件内容其实就是一系列GERMS命令的集合。这些命令初始化闪存,然后把.srec文件中的内容烧录到闪存中。
srec2flash命令在转换用户软件时,会在开始处增加一小段代码。当执行的时候,这段代码序把falsh中的用户软件复制到SRAM中。代码复制完成后,程序从SRAM开始执行。
用法
Srec2flash <filename>
例子
Srec2flash hello_world.srec
产生的hello_world.flash文件 (部分内容如下):
|
文章评论(0条评论)
登录后参与讨论