niosiilinux uart writee函数怎么循环周期 写数 读数

新浪广告共享计划>
广告共享计划
基于NiosII的SOPC多处理器系统设计方法[转]
基于NiosII的SOPC多处理器系统设计方法
来源:单片机及嵌入式系统应用
作者:李兰英 李霄燕
细地阐述基于NiosIl和FPGA的多处理器系统的实现机制,讨论利用硬件互斥核实现多处理器资源共享的方法,并给出硬件设计的具体步骤以及软件设计、
调试方法和关键技术;利用Altera公司提供的QuartusII、SOPC
Builder和NiosII
IDE等开发工具,通过一个3处理器系统设计实例,验证了设计方法的正确性,实现了3处理器对存储器资源的共享。
关键词 SOPC NiosII 多处理器系统
共享存储器
两个或多个微处理器一起工作来完成某个任务的系统称为“多处理器系统”。传统基于单片机的多处理器系统结构复杂,可靠性差;而基于32位的嵌入式软核处理
器NiosII的SOPC(可编程片上系统)多处理器系统解决方案,从根本上改变了多处理器系统的设计理念和方法。使用Altera公司的NiosII软
核处理器和SOPC
Builder工具,可以快速地设计和建立共享资源的多处理器系统。多处理器系统一般用于工作站和使用分载(load-sharing)的复杂算法(称为
“对称多处理器SMP”)的高端PC计算。对于大部分嵌入式系统,当SMP的开销太大时,使用多个处理器执行不同的任务,实现不同的功能正引起越来越多的
关注。Altera公司的FPGA为开发非对称的嵌入式多处理器系统提供了一个理想的平台。为了提供理想的系统性能,使用SOPC
Builder工具可以很容易地对硬件进行修改和调整,从而很快完成不同配置系统的没计、编译和评估。
本文将对基于NiosII的OSPC多处理器系统的实现原理、设计流程和方法进行详细的讨论。
1 NiosII多处理器系统硬件设计 QuartusII
5.O及以上版本支持多处理器系统的创建和调试。多个NiosII处理器能够有效地共享系统资源。由于SOPC
Builder允许用户轻松添加多个处理器到系统中,因此建立多处理器系统的难点已不再是硬件的排列和连接,而在于多个处理器的软件设计,使它们正常操
作,相互之间不产生冲突。NiosII多处理器系统分为2类:一类是共享资源的多处理器系统;另一类处理器相互独立,之间不进行信息交换。
2 NiosII多处理器系统的资源共享
资源共享是多处理器系统的强大功能,但必须仔细考虑所要共享的资源,以及不同处理器如何使用共享资源。
2.1 共享存储器
在多处理器系统中最普遍的共享资源是存储器。共享存储器用于存放任何数据,从指示处理器间通信状态的简单标志,到被多个处理器同时进行计算的复杂数据结构。
如果存储器中包含不只一个处理器的程序代码,那么每个处理器需要有不同的存储地址。对于程序空间,处理器不能共享存储器的同一区域。如果共享数据存储器,
则存储器的数据需要从端口与共享存储器的处理器的数据主端口连接。多处理器之问共享数据存储器比共享指令存储器困难,原因是数据存储器可读/写。如果某一
处理器正在对共享存储器的特定区域进行写操作,而同时另一个处理器正在对同一区域进行读或写操作,则很可能出现数据错误,至少使应用程序出错,甚至使系统
共享存储器的处理器需要一个机制来通知其他处理器何时正在使用共享资源,以便不受其他处理器的干扰。
2.2 硬件互斥核
NiosII处理器允许使用其硬件互斥核部件对共享资源进行保护处理。这个硬件互斥核不是一个NiosII处理器内部的部件,而是一个称为Mutex的SOPC
Builder组件。
互斥核也可看作一种共享资源,提供一个原子的“测试和置位”操作,处理器测试Mutex是否可行。如果可行,就在某个操作中获取它。当处理器结束与
Mutex相关的共享资源使用时,释放该Mutex;此时,另一个处理器可能获取了Mutex,使用共享资源。互斥核在物理上并不能防止资源同时被多个处
理器访问。运行在处理器上的软件必须被设计为在访问相关共享资源之前总是获取Mutex的。
在大部分情况下,多个处理器之间应该使用互斥核来保护共享资源。然而,也有一些不需要互斥核的,例如对于单方向或循环的消息缓冲队列,此时只有一个处理器往存储器的某个特殊位置写数据。
一般地,NiosII不支持多个处理器之间非存储器外设的共享,NiosII硬件抽象层(HAL)库也不支持。NiosII
HAL提供访问Mutex核的API函数如表l所列。
2.3 多处理器地址空间的重叠
在单处理器系统中,不允许多于一个的从外设具有相同的地址空间,原因是这将引起矛盾。然而,在多处理器系统中,只要外设被不同的处理器控制,那么不同的从外设就可以具有相同的基地址。
3 NiosII多处理器系统软件设计
3.1 程序存储器
在多处理器系统中,多个处理器可能使用同一个程序存储器,每个处理器的程序必须存放在不同的位置。NiosII和SOPC
Builder提供一个简单的存储器分区模式,允许多个处理器在同一存储器的不同区域运行各自的软件。分区模式使用处理器的异常地址,可以在SOPC
Builder中进行设置。NiosII
IDE负责根据异常地址计算出不同代码段链接的位置。如果2个不同的处理器被链接到同一存储器,那么每个处理器的异常地址用来决定处理器软件存放的基地
址,其末地址由下一个异常地址或者存储器的末地址决定。对于每个处理器,软件有5个主要的代码段需要被链接到存储器中的固定地址。分别是:
·text 实际的可执行代码;
·rodata代码段执行时所使用的常量数据;
·rwdata读/写变量和指针;
·heap 动态分配的存储器;
·stack 函数调用参数和其他临时数据。
在多处理器系统中,对于每个处理器,都希望使用连续的存储区域存储其所有的代码段。在这种情况下,异常地址用来定义2个处理器之间代码存放的分界。
值得注意的是,异常地址的低6位总是设置为Ox20,因为偏移量OxO是NiosII的复位地址,所以异常地址必须位于其他位置。偏移量选择为Ox20,原因是它与一条指令的缓存行有关。Ox20字节的复位代码初始化指令缓存行,然后跳转到系统的起始代码处。
3.2 启动地址
在多处理器系统中,每个处理器必须从自己的存储区域启动。为了从同一个非易失性存储器中的不同区域启动多处理器,简单地设置每个处理器的复位地址为所期望的启动地址。在启动地址之间要留出足够的空间存放启动代码。
NiosII Flash
Programmet能够将多个处理器的启动代码编程到一个Flash器件中。Flash
Programmer根据每个处理器的复位地址计算Flash内的编程地址。
3.3 Niosll IDE中多处理器系统的运行和调试
IDE中包含许多帮助开发多处理器系统软件的工具,最重要的是具有对多处理器同时进行在片调试的能力。在多处理器系统上,多个debug(调试)可同时运
行;每个处理器可以单独暂停和恢复,也可以单独设置每个处理器的断点。某个处理器停在一个断点处,并不影响其他处理器的操作。每个debug通道也可以单
独打开和停止。在NiosII
IDE中,利用一项称为“处理器集合(mul-tlprocessor
collections)”的功能,一个操作就可以打开多个处理器的debug通道。multiprocessor
collections是被连接在一个配置名字下的每个处理器的debug配置组。使用multiprocessor
collections的好处是无论何时打开collections,NiosII
IDE都可以打开每个debug通道,而不
用手动打开。也町以用一个操作停止multiprocessor
col-lections,但是同时暂停和恢复multiprocessor
collections目前不支持。
multiprocessor
collections的打开和停止不是同时的,这意味着在collections中的处理器不能在同一个时钟周期开始执行代码。事实上,不同处理器的启
动可能有几秒的延迟。multiprocessor
collections的目的是方便打开多处理器系统的debug通道,而不是为了同步处理器。如果需要在较短的时间内启动多个处理器,则需要构建单独的
硬件和软件机制。
4 NiosII多处理器系统设计实例
下面将利用SOPC
Builder建立一个基于标准模板的3处理器、共享片上存储器的NiosII系统,之后在NiosIIIDE中为每个处理器建立一个软件工程。系统功能
是:3个CPU的软件将产生要显示的消息,使用硬件互斥核将所产生的不同消息放在共享的消息缓冲区中。cpul将连续检查缓冲区中的新消息,如果发现新消
息,就通过jtag_uart显示出来。
实例的开发环境是QuartusII
5.0或以上版本,开发套件CycloneII
Edition和niosII_cycloneII_2c35开发板。
4.1 创建硬件系统
在标准硬件实例standard.qp的设计基础上,增加2个处理器、2个定时器和1个硬件互斥核组件;另外增加1个消息缓存区message_buffer_ram(片上RAM),用作3个处理器的消息缓存区。按如下步骤连接共享资源:
①使用连接矩阵,将SDRAM连接到每个处理器的指令和数据主端口。允许3个处理器访问SDRAM。
②将ext_ram_bus连接到每个处理器的指令和数据主端口。允许3个处理器访问外部RAM和Flash。
③将message_buffer_ram连接到每个处理器数据主端口。允许3个处理器访问该存储器。
④去除在message_buffer_ram和cpul指令主端口之间的缺省连接。
⑤选择System→Auto-Assign Base
Addresses,为每个外设分配一个唯一的基地址。
完成以上操作后,系统配置如图1所示。3个处理器的数据主端口与共享存储器的同一从端口连接。因为cpul、cpu2和cpu3在物理上能够同时将数据写
到共享存储器中,软件必须仔细设计以保证存储在共享存储器上数据的完整性。注意:图l所示的系统配置中,只有cpul的数据主端口与jtag_uart相
最后,为3个CPU设置复位和异常地址,创建和编译系统,并下载FPGA的设计文件.sof文件到开发板。
4.2 为多处理器系统创建软件 在NiosII
IDE环境下,为3个处理器系统分别创建6个软件工程,为每个处理器创建一个应用工程和一个系统库工程。之后对软件工程进行编译、运行和调试。
软件使用硬件Mutex共享一个消息缓存区。3个处理器分别写消息到消息缓存区(count)且循环加1。cpul读消息且通过jtag_uart显示消
息。每个处理器运行同样的C文件,但处理器的操作稍有不同。这是通过使用NiosII的cpuid实现的。在NiosII处理器系统中,某个处理器通过写
其cpuid控制寄存器的值到Mutex寄存器的OwrqER域来对Mt-tex加锁。cpuid寄存器保持一个静态值,在多处理器系统中,该值唯一地识
别一个处理器,且在系统创建时确定。软件执行某个处理器的函数时,首先检查处理器的cpuid,如果cpuid正确,则执行相应函数。工程中的文件为
hello_world_multi.c,其中将信息写入缓冲区的功能由以下程序段实现:
如果将信息从jtag_uart输出,那么程序首先判断id是否等于3。因为硬件设计时,只有cpul与jtag_uart相连,而cpul的id的值为
3(在系统创建时确定),cpu2、cpu3的id分别为1和2,且id的值等于cpuid控制寄存器的值加l,可在NiosII
IDE环境下读取cpuid控制寄存器的内容。其信息输出的程序如下:
Library属性中,第1个工程选择jtag_uart为stdin、stderr和stdout,选择cpul_timer为the
Systemclock timer;第2个工程选择cpu2_timer为System
clocktimer,验证stdin、stderr和stdout为null,因为这个处理器不与jtag_uart连接;第3个工程选择
cpu3_timer为Systemclock
timer,其余同工程2。验证这3个工程的SDRAM被选择为Program
memory、Read-only data memory、Read/write data memory、Heap
memory和Stack memory。
分别经编译、下载、运行后,在终端上显示这3个处理器产生的消息,如图2所示。
结果表明,3处理器系统通过硬件互斥核,实现了存储器的共享。在此实例的基础上,按同样的方法添加处理器及相应的硬件组件,并开发相关应用软件,即可实现满足不同需求的多处理器系统。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。niosii的UART串口通信_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
niosii的UART串口通信
上传于||暂无简介
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,查找使用更方便
还剩1页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢NIOS II 那些事儿Qsys 版黑金动力社区编 黑金动力社区特别声明Http://www.heijin.org文档实验演示是基于黑金动力社区开发板 DB4CE15,请配合开发板完成相关实验。 Quartus II 的版本为 11.1 sp1 32 位版,Nios II 的软件开发使用 Nios II Software Build Tools for Eclipse, Modelsim SE 为 10.0c 32 位版。文档仅供学习
、讨论使用,请勿用于商业用途。 在使用该文档过程中有任何疑问请到黑金动力社区进行交流。前言其实写文档本身就是一种学习,写文档过程中查阅资料和实际实验的时间一般都几 倍于真正用于写文档的时间,这本身就是一种知识积累的过程。所以希望有更多的童鞋 可以分享自己的文档。写这篇文档主要是为了配合黑金社区的 Cyclone IV 开发板以帮助1新手尽快入门,掌握 Nios II 的使用方法。由于我不是专业作家,语言上可能会口语化一 些,但我会力争保证词能达意。秉承黑金社区的“打造精品,宁缺勿滥”的精神,文档 有可能写的会慢一些,我希望写的内容充实一些,实际有用一些。由于水平有限,时间 有限,所以出现错误在所难免,请发现错误的童鞋与我联络,我会随时进行更正。本文 档的完成绝不是两三个月的事儿,在此过程中希望能得到更多童鞋的支持。最后,特别 致谢黑金动力社区的 AVIC,可以说没有 AVIC 邀约,就没有这篇文档的诞生。第一章 永远的”hello world”章节简介几乎所有的编程语言中”hello world”都是第一个演示实例,在基础篇中我们也会采 用”hello world”来进行 Nios II 的第一个演示, 通过这个演示我们可以对如何构建基于 Qsys 黑金动力社区Http://www.heijin.org的 Nios II 有一个初步的认识。在进阶篇中我们将通过”hello_world”这个示例揭示 Nios II 后台到底完成了哪些工作。基础篇---最简单的 Nios II硬件设计建立新项目 首先打开 Quartus II 软件。2使用向导建立新的项目。 黑金动力社区Http://www.heijin.org3 黑金动力社区Next。Http://www.heijin.org4Next。 黑金动力社区Http://www.heijin.org5选择 EP4CE15F17C8,Next。 黑金动力社区Http://www.heijin.org6Next。 黑金动力社区Http://www.heijin.org7Finish,工程建立完成。如下图:设计项目顶层文件 选择菜单栏中”File”下的”New”。 黑金动力社区Http://www.heijin.org8 黑金动力社区选择 Block Diagram/Schematic File,点 OK 继续。Http://www.heijin.org9将文件保存为 top_level.bdf(注意保存目录的路径是否正确)。 黑金动力社区Http://www.heijin.org10 黑金动力社区点击保存。Http://www.heijin.org11进行 Qsys 系统设计 点击下图鼠标所指图标启动 Qsys。 黑金动力社区Http://www.heijin.org12将文件保存为 kernel.qsys 黑金动力社区Http://www.heijin.org13在“文件名”窗口中输入 kernel,点击保存。 黑金动力社区Http://www.heijin.org返回 Qsys 主界面后点击” Clock Settings”标签栏准备修改时钟。如下图所示:14将时钟名 clk_0 修改为 clk, 时钟频率由初始的 50MHz 修改为 100MHz。 如下图所示 (双击即可修改): 黑金动力社区Http://www.heijin.org15修改完成后回到”System Contents”标签栏,可以看到变更已经生效。 黑金动力社区Http://www.heijin.org16?这里设置的 clk 的频率,要用来作为 Nios II 的主时钟,所以根据不同的 FPGA 芯 片主时钟的最高频率会有不同限制,具体的可以参照 Altera 的官方文档《Nios II Performance Benchmarks》。截图如下: 黑金动力社区Http://www.heijin.org由上图得知 EP4CGX30CF19C6 芯片在使用 Nios II/f 时最大的频率是 150M,所以我 们这款芯片选择 100MHz 问题不大,当然有兴趣的童鞋也可以自己尝试提高一些频率。添加 Nios II 核 从下图左侧”Component Library”标签栏中找到”Nios II Processor”后点击 Add(在查 找窗口中输入 nios 即可)。17在”Core Nios II”标签栏中选择”Nios II/f”,其它选项保持默认。如下图所示: 黑金动力社区Http://www.heijin.org18?关于 Nios II/e、Nios II/s、Nios II/f 的区别可以参照 Altera 的官方文档《Nios II Processor Reference Handbook》中的章节。截图如下:在”Caches and Memory Interfaces”标签栏中保持默认设置。如下图所示: 黑金动力社区Http://www.heijin.org19在”Advanced Features”标签栏中保持默认设置,如下图所示:在”MMU and MPU Settings”标签栏中保持默认设置,如下图所示: 黑金动力社区Http://www.heijin.org20在”JTAG Debug Module”标签栏中保持默认设置,如下图所示: 黑金动力社区Http://www.heijin.org点击右下的 Finish 后将”Nios II Processor”加入到 Qsys 中。返回”System Contents”标 签栏可以看到新加入的”Nios II”核。如下图所示:21在”Name”列中将 nios2_qsys_0 改名为 nios2。操作如下图所示: 黑金动力社区Http://www.heijin.org22点击”Rename”即可重新命名,完成后如下图所示: 黑金动力社区Http://www.heijin.org23进行时钟连接,连接后如下图所示: 黑金动力社区Http://www.heijin.org24添加 On-Chip Memory(RAM)核 从下图左侧”Component Library”标签栏中的查找窗口输入 On Chip 找到”On-Chip Memory(RAM or ROM)”后点击 Add。 黑金动力社区Http://www.heijin.org25在”Size”栏中的”Total memory size”窗口中输入 40960 即片上内存的大小为 40KB) ( , 其余选项保持默认,点击 Finish。如下图所示: 黑金动力社区Http://www.heijin.org26返回”System Contents”标签栏可以看到新加入的”On-Chip Memory”核。在”Name”列 中将 onchip_memory2_0 改名为 onchip_ram。完成后如下图所示: 黑金动力社区Http://www.heijin.org27进行时钟、数据端口、指令端口的连接,连接后如下图所示: 黑金动力社区Http://www.heijin.org28?关于数据和指令端口的连接的疑问,这是初用 Qsys 的童鞋们很困惑的问题,之 前使用 SOPC Builder 建立 Nios II 时并不手动连接这些端口。 对于一个添加的 IP 核来说什 么时候连接数据端口,什么时候连接指令端口呢?这里有一个连线的规则:如果是存储 器这类的 IP 核, 需要将其 Slave 端口同 Nios II 的 data_master 和 instruction_master 相连, 而其他非存储器 IP 核则只需连接到 Nios II 的 data_master 即可。可以参照 Altera 的官方 文档《Nios II Processor Reference Handbook》对 Nios II 的指令和数据总线的描述。截图 如下: 黑金动力社区Http://www.heijin.org添加 System ID Peripheral 核 从下图左侧”Component Library”标签栏中的查找窗口输入 sys 找到” System ID29Peripheral”后点击 Add。 黑金动力社区保持默认选项,单击 Finish。如下图所示:Http://www.heijin.org30?之前在 SOPC Builder 中 System ID 是自动生成的,但是在 Qsys 里已经不再自动生 成。在 System ID 中可以输入一个 32 位的十进制整数值,保持 0 当然也可以。返回”System Contents”标签栏可以看到新加入的” System ID Peripheral”核。 在”Name” 列中将 sysid_qsys_0 改名为 sysid。完成后如下图所示: 黑金动力社区Http://www.heijin.org31进行时钟、数据端口的连接。如下图所示: 黑金动力社区Http://www.heijin.org32添加 JTAG UART 核 从下图左侧”Component Library”标签栏中的查找窗口输入 jtag 找到”JTAG UART”后 点击 Add。 黑金动力社区Http://www.heijin.org33保持默认选项,点击 Finish。如下图所示: 黑金动力社区Http://www.heijin.org34返回”System Contents”标签栏可以看到新加入的”JTAG UART”核。在”Name”列中将 jtag_uart_0 改名为 jtag_uart。完成后如下图所示: 黑金动力社区Http://www.heijin.org35进行时钟、数据端口的连接,完成后如下图所示: 黑金动力社区Http://www.heijin.org36完成 Qsys 设计的后续工作指定 NIos II 的复位和异常地址 从”System Contents”标签栏中双击建立好的 nios2 进入 Nios II Processor 的配置界面, 配置 Reset Vector 和 Exception Vector 为”onchip_ram.s1”,点击 Finish。如下图所示: 黑金动力社区Http://www.heijin.org37连接复位信号 点击 Qsys 主界面菜单栏中的”System”下的”Create Global Reset Network”。如下图所 示: 黑金动力社区Http://www.heijin.org38完成后会自动连接所有复位端口。如下图所示: 黑金动力社区Http://www.heijin.org39进行基地址分配 点击 Qsys 主界面菜单栏中的”System”下的”Assign Base Addresses”。如下图所示: 黑金动力社区Http://www.heijin.org40完成后”Base”栏将出现不会重复的具体的地址,如下图: 黑金动力社区Http://www.heijin.org41进行中断号的分配 在”IRQ”标签栏下点选”Avalon_jtag_slave”和 IRQ 的连接点就会为”jtag_uart”核添加 一个值为 0 的中断号。如下图所示: 黑金动力社区Http://www.heijin.org42生成 Qsys 系统 点选”Generation”标签栏中 Generate 按钮生成 Qsys 系统。如下图所示: 黑金动力社区Http://www.heijin.org43如果提示是否保存.qsys 文件,请选择保存。完成后会显示类似如下窗口:点击 Close 后关闭窗口后,再关闭 Qsys 主界面。 黑金动力社区添加 Qsys 组件到项目顶层文件 回到 Quartus II 主界面。如下图:Http://www.heijin.org44在空白处双击将已生成的 kernel 加入 top_level.bdf 中。如下图所示: 黑金动力社区Http://www.heijin.org点击”OK”放入。如下图所示:45 黑金动力社区加入 Quartus II IP File 文件Http://www.heijin.org为了以后编译成功, 请务必将对应的 Quartus II IP File (.qip) 加入项目中。 步骤如下:46 黑金动力社区Http://www.heijin.org47 黑金动力社区Http://www.heijin.org48 黑金动力社区Http://www.heijin.org49至此 Qsys 组件构建完成。添加锁相环 PLL 是一个基本的时钟模块,外部时钟源一般是经过 PLL 处理后供 FPGA 各模块使 用。FPGA 芯片内部有专门的时钟布线网络,这样以保证时钟具有最低抖动和时延。在 top_level.bdf 的空白处双击,如下图所示: 黑金动力社区Http://www.heijin.org点击 MegaWizard Plug-In Manager。50 黑金动力社区Http://www.heijin.org选择”Create a new custom megafunction variation”然后点击 Next,选中 ALTPLL,输出 文件的描述语言选择”Verilog HDL”,以”PLL”为输出文件的 name,然后点击 Next。如下图所 示:51将输入的时钟由 100MHz 改为 50MHz。其他选项保持默认,点击 Next。如下图所 示: 黑金动力社区Http://www.heijin.org52?”Which device speed grade will you be using?”窗口选择 8 是因为开发板 FPGA 芯片 为 EP4CE15F17C8,输入时钟选择 50MHz 是根据板上的时钟源是 50MHz。 参照以下两张截 图: 黑金动力社区Http://www.heijin.org我们继续 ALTPLL 的设置,保持如下设置,点击 Next。如下图所示:53保持下图设置,Next。如下图所示: 黑金动力社区Http://www.heijin.org54保持下图设置,Next。 黑金动力社区Http://www.heijin.org55不用做任何修改,Next。 黑金动力社区Http://www.heijin.org56保持下图设置,这里 c0 的输出是给 Qsys 组件使用的(包括之前建立的 nios2), 所以设为 100MHz.点击 Next。如下图所示: 黑金动力社区Http://www.heijin.org57不设置 c1,保持默认设置,Next。 黑金动力社区Http://www.heijin.org58不设置 c2,保持默认设置,Next。 黑金动力社区Http://www.heijin.org59不设置 c3,保持默认设置,Next。 黑金动力社区Http://www.heijin.org60不设置 c4,保持默认设置,Next。 黑金动力社区Http://www.heijin.org61保持默认设置,Next。 黑金动力社区Http://www.heijin.org62添加一个实例化的模板文件,Finish。如下图所示: 黑金动力社区Http://www.heijin.org63点击 Yes 添加 Quartus II IP Files。这时就会出现 PLL 模块的符号,点击 OK 将 PLL 符号加入到项目。如下图所示: 黑金动力社区Http://www.heijin.org完成后如下图所示:64 黑金动力社区进行逻辑连接和生成管脚Http://www.heijin.org将 PLL 模块的 c0 与 kernel 模块的 clk_clk 相连接,如下图所示:65在 PLL 模块内点击鼠标右键选取 Generate Pin for Symbol Ports 生成管脚。如下图所 示: 黑金动力社区Http://www.heijin.org66 黑金动力社区Http://www.heijin.org在 kernel 模块内点击鼠标右键选取 Generate Pin for Symbol Ports 生成管脚。如下图 所示:67 黑金动力社区Http://www.heijin.org68将管脚”inclk0”改名为 CLOCK,管脚”reset_reset_n”改名为 RESET。如下图所示: 黑金动力社区Http://www.heijin.org69配置芯片 按下图所示操作配置芯片: 黑金动力社区Http://www.heijin.org70 黑金动力社区Http://www.heijin.org71 黑金动力社区Http://www.heijin.org72 黑金动力社区Http://www.heijin.org73 黑金动力社区Http://www.heijin.org74 黑金动力社区Http://www.heijin.org75 黑金动力社区Http://www.heijin.org76编译项目 回到 Quartus II 主界面后编译项目。如下图所示: 黑金动力社区Http://www.heijin.org77编译成功后显示如下提示:点 OK 完成。分配物理针脚 点击下图所指快捷键进入 Pin Planner 界面 黑金动力社区Http://www.heijin.org78按照黑金提供的 tcl 脚本对应管脚名称分配针脚。如下图所示: 黑金动力社区Http://www.heijin.org79 黑金动力社区Http://www.heijin.org完成后关闭 Pin Planner,回到 Quartus II 主界面后再次编译项目。至此完成项目的硬 件设计。软件设计 下面使用 Nios II Software Build Tools for Eclipse 来完成当前项目的软件开发。按照 下图所示点击 Nios II Software Build Tools for Eclipse 打开 Nios II SBT for Eclipse。80Workspace 选择当前的项目目录,点 OK。 黑金动力社区Http://www.heijin.org出现 Nios II SBT for Eclipse 主界面81建立新的软件应用,如下图所示: 黑金动力社区Http://www.heijin.org82点击后出现下图: 黑金动力社区Http://www.heijin.org83在”SOPC Information File name”窗口中选择 kernel.sopcinfo 文件,以便将生成硬件 配置信息和软件应用关联。在”Project name”输入”hello_world,”Project template”选择 Hello_World。点击 Finish。 黑金动力社区Http://www.heijin.org84完成后回到 Eclipse 主界面,如下图: 黑金动力社区Http://www.heijin.org85按下图操作编译当前软件项目: 黑金动力社区Http://www.heijin.org86编译完成后如下图所示: 黑金动力社区Http://www.heijin.org87运行项目 然后连接 JTAG 到开发板,确定 A.已正确安装驱动。B 防火墙不会影响到 JTAG 的正 常工作。最后给开发板上电。先按照下图所示下载硬件配置信息到开发板: 黑金动力社区Http://www.heijin.org88点击后出现如下界面: 黑金动力社区Http://www.heijin.org89按下图操作加入文件: 黑金动力社区Http://www.heijin.org90点 Start 开始下载,完成后如下图: 黑金动力社区Http://www.heijin.org91关闭后回到 Eclipse 主机面,按下图所示操作运行软件: 黑金动力社区Http://www.heijin.org92有可能出现如下界面: 黑金动力社区Http://www.heijin.org93转到”Target Connection”标签栏。 黑金动力社区Http://www.heijin.org点击右侧的 Refresh Connections 将 USB-Blaster 加入,如下图:94Apply 后,Run。完成后如下图所示: 黑金动力社区Http://www.heijin.org当你在开发板上看到上图红框内的文字,恭喜你,第一个基于 Qsys 系统的 NIos II 程序已经运行成功啦!进阶篇---揭开 Nios II 背后的秘密(前篇)当我们完成了第一个基于 Qsys 的 Nios II 时,也许很多童鞋会很好奇到底使用 Qsys 设 计后台都进行了哪些工作?这些工作都会有什么影响?以下内容就是来说明后台是怎 样工作的。Nios II 架构95由上面的结构图我们可以看到 Nios II 的主要架构,蓝色方框表示必备的,灰色方框 表示可选的。 黑金动力社区寄存器组(Registers)Http://www.heijin.orgNios II 包括了两组必备的寄存器:通用寄存器(General Purpose Registers)、控制寄存 器(Control Registers)和一组可选的影子寄存器(Shadow Registers Sets)。通用寄存器组(General Purpose Registers) 寄存器编号 r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 寄存器名称 zero at 始终为 0。 汇编器临时寄存器 返回值寄存器 返回值寄存器 参数寄存器 参数寄存器 参数寄存器 参数寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 功能说明96 黑金动力社区r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 et bt gp sp fp ea ba ra 调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 被调用者保存寄存器 异常临时寄存器 断点临时寄存器 全局指针寄存器 栈指针寄存器 帧指针寄存器 异常返回地址寄存器 断点返回地址寄存器 返回地址寄存器Http://www.heijin.org97r25 r26 r27 r28 r29 r30 r31控制寄存器组(Control Registers) 寄存器编号 寄存器名称 功能说明 黑金动力社区0 1 2 3 4 5 6 7 8 9 10 status estatus bstatus ienable ipending cpuid Reserved exception pteaddr tlbacc tlbmic Reserved badaddr config mpubase mpuacc Reserved 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 调用者保存寄存器 保留 保留 状态寄存器。Http://www.heijin.org处理器进入非 break 异常状态时作为 status 值的备份。 处理器进入 break 异常状态时作为 status 值的备份。 32 位内部硬件中断的独立开关。 32 位内部硬件中断挂起标志寄存器9811 12 13 14 15 16~31影子寄存器组(Shadow Register Sets) 影子寄存器组是可选的,它是通用寄存器组的一个完整的备用,用于在 ISR 中维持 一个独立的上下文环境。当影子寄存器组被实现的,status.CRS 指示着当前使用的寄存 黑金动力社区Http://www.heijin.org器组。Nios II 最多可以有 n 个影子寄存器组(1-63)。0 意味着常规寄存器组。通常影 子寄存器组会和 EIC 联合使用。系统软件可以通过 rdprs 和 wrprs 指令读写 status.PRS。构建 Qsys 系统的流程99我们在完成首个示例时,基本是遵循着一个上述的流程。硬件流部分不是我们关心 的重点。我们主要关注 Qsys 和软件流两部分。 黑金动力社区Qsys 系统输出文件Http://www.heijin.org10在 Qsys 设计过程中我们选择了几个 IP 核 (Nios II 核、 On-Chip Memory 核、 System ID Peripheral 核、JTAG UART 核)进行逻辑连接后生成了 Qsys 系统。此时系统会为我们自 动生成若干文件,具体的文件目录结构如下图:首先在 hello_world 项目根文件夹下会生成四个比较重要的文件。 黑金动力社区? Kernel.bsf:Http://www.heijin.org这是 Qsys 生成的一个顶层的符号文件,之前我们有一步添加 Qsys 组件建到项 目顶层文件,因为项目的顶层文件就是使用的符号文件(top_level.bdf)文件,所以我 们加入 Qsys 时使用的就是这个文件。 ? Kernel.qsys 这个文件包含了 Qsys 系统中所有添加的 IP 核,及核连接和核参数。 ? Kermel.sopcinfo 这个文件包含了完整的 Qsys 系统描述, 后续的软件项目依据此文件自动生成相 关的软件驱动。 ? Kernel.html 系统 report 文件。10其次在 kernel 文件夹下会生成一个 synthesis 的子文件夹,这个 synthesis 的文件夹 下有一个 submodules 的子文件夹,这里生成了包括所有出现核可综合的代码文件。 synthesis 文件夹下面还有两个重要的文件: ? Kernel.qip 这是 Qsys 的 IP 文件, 前面已经说明, 不加入这个文件到项目, 编译时会报错。 ? Kernel.v 这是一个 Qsys 系统包含所有添加核实例化的顶层 HDL 文件。当我们使用 HDL 语言设计一个项目只将 Qsys 系统作为一个子模块时,就可以使用这个文件和其他 模块进行交互数据。构建软件系统的流程 依据开发工具的不同创建软件项目的流程也不一样,见下图: 黑金动力社区Http://www.heijin.org如果使用 Nios II IDE 开发软件需要两个文件(.ptf 和.sopcinfo),而如果使用 Nios II SBT10for Eclipse 开发软件只需要一个.sopcinfo 文件即可。我们使用的是 Nios II SBT for Eclipse 来发软件。当我们设置软件项目名称后就可以建立一个软件项目了,建立软件项目后就 就可以添加自己的源文件然后编译运行。 黑金动力社区Http://www.heijin.org10编译软件项目之前系统的输出文件 根据.sopcinfo 文件构建软件系统后系统输出的文件目录结构如下图: 黑金动力社区Http://www.heijin.org10软件应用层 Software 目录下的 hello_world 属于软件应用层面, 这个文件夹下有几个比较重要的 文件: create-this-app 这是一个脚本文件,如果你有 UNIX 或者 linux 下 shell 编程的经验的话,会很容易 看懂里面的内容。 该脚本文件主要的功能是复制使用模板下的相关源文件到当前目录下; 创建 Makefile. hello_world.c C 的源文件。 黑金动力社区板级开发包(BSP)Http://www.heijin.orgSoftware 目录下的 hello_world_bsp 是该软件应用的板级开发包, 这个文件夹下有几 个比较重要的文件:linker.x(代码解读) 链接脚本文件,鉴于此文件比较重要,我们逐步分析一下这个文件。MEMORY { reset : ORIGIN = 0x10000, LENGTH = 32 onchip_memory : ORIGIN = 0x10020, LENGTH = 40928 } //此段脚本将实际的存储空间分成两个区,一个 reset 区,一个 onchip_memory 区。10/* Define symbols for each memory base-address */ __alt_mem_onchip_memory = 0x10000; //我们只定义了一个片上存储器,所以只有这个片上存储器的基址。OUTPUT_FORMAT( &elf32-littlenios2&, &elf32-littlenios2&, &elf32-littlenios2& ) //输出格式是小端格式 OUTPUT_ARCH( nios2 ) ENTRY( _start ) //设置_start 符号值为程序的入口点地址/* * The alt_load() facility is enabled. This typically happens when there isn't * an external bootloader (e.g. flash bootloader). * The LMA (aka physical address) of each loaded section is 黑金动力社区* set to the .text memory device. * The HAL alt_load() routine called from crt0 copies sections from * the .text memory to RAM as needed. */Http://www.heijin.orgSECTIONS //节点定义 {/* * Output sections associated with reset and exceptions (they have to be first) */10.entry : //定义输出节点.entry { KEEP (*(.entry)) //保留所有目标文件中.entry 的输入节点 } & reset //将.entry 节点映射到之前定义的 reset 区.exceptions : //定义输出节点.exceptions { PROVIDE (__ram_exceptions_start = ABSOLUTE(.)); //返回当前的定位计数器的绝对值,”.”是定位计 数器。 对于链接脚本定义的符号进行赋值使用 PROVIDE 可以避免用户程序中定义同名符号的多重定义错误。 . = ALIGN(0x20); //返回当前 20 字节对其的地址。 KEEP (*(.irq)); KEEP (*(.exceptions.entry.label)); 黑金动力社区KEEP (*(.exceptions.entry.user)); KEEP (*(.exceptions.entry)); KEEP (*(.exceptions.irqtest.user)); KEEP (*(.exceptions.irqtest)); KEEP (*(.exceptions.irqhandler.user)); KEEP (*(.exceptions.irqhandler)); KEEP (*(.exceptions.irqreturn.user)); KEEP (*(.exceptions.irqreturn)); KEEP (*(.exceptions.notirq.label)); KEEP (*(.exceptions.notirq.user)); KEEP (*(.exceptions.notirq));Http://www.heijin.org10KEEP (*(.exceptions.soft.user)); KEEP (*(.exceptions.soft)); KEEP (*(.exceptions.unknown.user)); KEEP (*(.exceptions.unknown)); KEEP (*(.exceptions.exit.label)); KEEP (*(.exceptions.exit.user)); KEEP (*(.exceptions.exit)); KEEP (*(.exceptions)); PROVIDE (__ram_exceptions_end = ABSOLUTE(.)); } & onchip_memory //将.exceptions 节点映射到之前定义的 onchip_memory 区。PROVIDE (__flash_exceptions_start = LOADADDR(.exceptions)); //将.exceptions 节点的加载地址(LMA)赋给 定义的__flash_exceptions_start 符号。LOADADDR(section)返回该节点的加载地址。 黑金动力社区Http://www.heijin.org.text : //定义默认的输出节点.text。存放程序代码。 { /* * All code sections are merged into the text output section, along with * the read only data sections. * */PROVIDE (stext = ABSOLUTE(.));10*(.interp) *(.hash) *(.dynsym) *(.dynstr) *(.gnu.version) *(.gnu.version_d) *(.gnu.version_r) *(.rel.init) *(.rela.init) *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) *(.rel.fini) *(.rela.fini) 黑金动力社区*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) *(.rel.ctors) *(.rela.ctors) *(.rel.dtors)Http://www.heijin.org10*(.rela.dtors) *(.rel.got) *(.rela.got) *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) 黑金动力社区*(.rel.plt) *(.rela.plt) *(.rel.dyn)Http://www.heijin.orgKEEP (*(.init)) *(.plt) *(.text .stub .text.* .gnu.linkonce.t.*)/* .gnu.warning sections are handled specially by elf32.em. */*(.gnu.warning.*)11KEEP (*(.fini)) PROVIDE (__etext = ABSOLUTE(.)); PROVIDE (_etext = ABSOLUTE(.)); PROVIDE (etext = ABSOLUTE(.));*(.eh_frame_hdr) /* Ensure the __preinit_array_start label is properly aligned. We could instead move the label definition inside the section, but the linker would then create the section even if it turns out to be empty, which isn't pretty. */ . = ALIGN(4); PROVIDE (__preinit_array_start = ABSOLUTE(.)); *(.preinit_array) 黑金动力社区PROVIDE (__preinit_array_end = ABSOLUTE(.)); PROVIDE (__init_array_start = ABSOLUTE(.)); *(.init_array) PROVIDE (__init_array_end = ABSOLUTE(.)); PROVIDE (__fini_array_start = ABSOLUTE(.)); *(.fini_array) PROVIDE (__fini_array_end = ABSOLUTE(.)); SORT(CONSTRUCTORS) KEEP (*(.eh_frame)) *(.gcc_except_table) *(.dynamic)Http://www.heijin.org11PROVIDE (__CTOR_LIST__ = ABSOLUTE(.)); KEEP (*(.ctors)) KEEP (*(SORT(.ctors.*))) PROVIDE (__CTOR_END__ = ABSOLUTE(.)); PROVIDE (__DTOR_LIST__ = ABSOLUTE(.)); KEEP (*(.dtors)) KEEP (*(SORT(.dtors.*))) PROVIDE (__DTOR_END__ = ABSOLUTE(.)); KEEP (*(.jcr)) . = ALIGN(4); } & onchip_memory = 0x3a880100 /* Nios II NOP instruction */ //0x3a880100 我不太明白这个值的出处, 望知道的朋友可以告知,先谢过。 黑金动力社区.rodata : //定义默认的输出节点.rodata。存放程序常量。 { PROVIDE (__ram_rodata_start = ABSOLUTE(.)); . = ALIGN(4); *(.rodata .rodata.* .gnu.linkonce.r.*) *(.rodata1) . = ALIGN(4); PROVIDE (__ram_rodata_end = ABSOLUTE(.)); } & onchip_memoryHttp://www.heijin.orgPROVIDE (__flash_rodata_start = LOADADDR(.rodata));11/* * * This section's LMA is set to the .text region. * crt0 will copy to this section's specified mapped region virtual memory address (VMA) * * .rwdata region equals the .text region, and is set to be loaded into .text region. * This requires two copies of .rwdata in the .text region. One read writable at VMA. * and one read-only at LMA. crt0 will copy from LMA to VMA on reset * */ 黑金动力社区Http://www.heijin.org.rwdata LOADADDR (.rodata) + SIZEOF (.rodata) : AT ( LOADADDR (.rodata) + SIZEOF (.rodata)+ SIZEOF (.rwdata) ) //定义默认的输出节点.rwdata。存放程序已初始化变量。.rwdata 紧随.rodata 存放。 { PROVIDE (__ram_rwdata_start = ABSOLUTE(.)); . = ALIGN(4); *(.got.plt) *(.got) *(.data1) *(.data .data.* .gnu.linkonce.d.*)_gp = ABSOLUTE(. + 0x8000); //全局变量指针 PROVIDE(gp = _gp);11*(.rwdata .rwdata.*) *(.sdata .sdata.* .gnu.linkonce.s.*) *(.sdata2 .sdata2.* .gnu.linkonce.s2.*). = ALIGN(4); _edata = ABSOLUTE(.); PROVIDE (edata = ABSOLUTE(.)); PROVIDE (__ram_rwdata_end = ABSOLUTE(.)); } & onchip_memoryPROVIDE (__flash_rwdata_start = LOADADDR(.rwdata)); 黑金动力社区/* * * This section's LMA is set to the .text region.Http://www.heijin.org* crt0 will copy to this section's specified mapped region virtual memory address (VMA) * */.bss LOADADDR (.rwdata) + SIZEOF (.rwdata) : AT ( LOADADDR (.rwdata) + SIZEOF (.rwdata) ) //定义默认输 出节点.bss。存放程序未初始化变量 { __bss_start = ABSOLUTE(.); PROVIDE (__sbss_start = ABSOLUTE(.)); PROVIDE (___sbss_start = ABSOLUTE(.));11*(.dynsbss) *(.sbss .sbss.* .gnu.linkonce.sb.*) *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) *(.scommon)PROVIDE (__sbss_end = ABSOLUTE(.)); PROVIDE (___sbss_end = ABSOLUTE(.));*(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) 黑金动力社区*(COMMON)Http://www.heijin.org. = ALIGN(4); __bss_end = ABSOLUTE(.); } & onchip_memory/* * * One output section mapped to the associated memory device for each of * the available memory devices. These are not used by default, but can * be used by user applications by using the .section directive.11* * The output section used for the heap is treated in a special way, * i.e. the symbols &end& and &_end& are added to point to the heap start. * * Because alt_load() is enabled, these sections have * their LMA set to be loaded into the .text memory region. * However, the alt_load() code will NOT automatically copy * these sections into their mapped memory region. * *//* * 黑金动力社区* This section's LMA is set to the .text region.Http://www.heijin.org* crt0 will copy to this section's specified mapped region virtual memory address (VMA) * */.onchip_memory LOADADDR (.bss) + SIZEOF (.bss) : AT ( LOADADDR (.bss) + SIZEOF (.bss) ) { PROVIDE (_alt_partition_onchip_memory_start = ABSOLUTE(.)); *(.onchip_memory. onchip_memory.*) . = ALIGN(4); PROVIDE (_alt_partition_onchip_memory_end = ABSOLUTE(.));11_end = ABSOLUTE(.); end = ABSOLUTE(.); __alt_stack_base = ABSOLUTE(.); } & onchip_memoryPROVIDE (_alt_partition_onchip_memory_load_addr = LOADADDR(.onchip_memory));/* * Stabs debugging sections. * */.stab0 : { *(.stab) } 黑金动力社区.stabstr .stab.excl 0 : { *(.stabstr) } 0 : { *(.stab.excl) }Http://www.heijin.org.stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) }.stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) }/* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section so we begin them at 0. /* DWARF 1 */ .debug 0 : { *(.debug) } 0 : { *(.line) } */11.line/* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info .debug_abbrev .debug_line .debug_frame .debug_str 0 : { *(.debug_info .gnu.linkonce.wi.*) } 0 : { *(.debug_abbrev) } 0 : { *(.debug_line) } 0 : { *(.debug_frame) } 0 : { *(.debug_str) } 黑金动力社区.debug_loc 0 : { *(.debug_loc) }Http://www.heijin.org.debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) }/* Altera debug extensions */ .debug_alt_sim_info 0 : { *(.debug_alt_sim_info) } }11/* provide a pointer for the stack *//* * Don't override this, override the __alt_stack_* symbols instead. */ __alt_data_end = 0x1a000;/* * The next two symbols define the location of the default stack. You can * override them to move the stack to a different memory. */ PROVIDE( __alt_stack_pointer = __alt_data_end ); //定义栈顶 黑金动力社区PROVIDE( __alt_stack_limit = __alt_stack_base ); //定义栈底Http://www.heijin.org/* * This symbol controls where the start of the heap is. If the stack is * contiguous with the heap then the stack will contract as memory is * allocated to the heap. * Override this symbol to put the heap in a different memory. */ PROVIDE( __alt_heap_start PROVIDE( __alt_heap_limit = end ); //定义堆的起始地址 = 0x1a000 ); //定义堆的结束地址对于初次接触链接脚本的童鞋,可能会对相关的语法比较困惑,建议在分析 linker.x11这个文件时同时打开帮助文档 ld.html 查阅相关信息。ld.html 位 于”nioseds_install\document\gnu-tools\binutils\”,见下图:linker.h 包含链接器的内存布局信息。System.h 包含了这个头文件。 黑金动力社区alt_sys_init.c 用来初始化设备驱动。 system.hHttp://www.heijin.org包括了添加 IP 核的地址信息和软件应用所需要的相关系统信息。 settings.bsp 包含了所有板级开发包的信息。我们可以使用 BSP Editor 比较直观的查看里边的内 容。如下图所示打开 BSP Editor:12 黑金动力社区Http://www.heijin.org我们点选”Linler Script”标题栏中的”Memory Map”按钮就可以看到系统的存储布局, 如下图所示:12create-this-bsp 创建 BSP 的脚本文件。 黑金动力社区Http://www.heijin.orghello_world_bsp 文件夹下的 drivers 子文件夹存放着之前在 Qsys 中添加 IP 核驱动程 序的头文件和源文件方便我们在自己的应用程序里调用。hello_world_bsp 文件夹下的 HAL 子文件夹存放着 HAL 系统库室生成的相关文件。编译软件项目之后系统的输出文件 编译软件项目之后除了生成相关的目标文件和库文件。还会在 hello_world 文件夹 下生成三个重要的文件: hello_world.elf 软件项目生成的最终可执行文件。 hello_world.map12链接映射表文件,也可以看出哪些目标文件链接到最终的可执行文件中 (hello_world.elf)。 hello_world.objdump 当选中”Create object dump”选项时,就会在编译软件项目时生成.objdump 文件。如 下图所示: 黑金动力社区Http://www.heijin.org12这个文件包含了最终生成的可执行文件的 dump 信息。Run as Nios II Hardware 的综合分析 回顾一下我们在开发板上运行 hello_world 过程,首先我们将包含 FPGA 硬件配置信 息的文件 hello.sof 下载到开发板,然后在软件项目上点选 Run as Nios II Hardware。程序 就在开发板上运行了。下面我们来看看具体发生了什么。 黑金动力社区Http://www.heijin.org12从官方文档的描述可以看出关键点是.elf, 这个清楚了, 也就明白程序到底是怎么运 行的了。因此我们先解析一下 hello.elf。按照下图操作启动 Nios II Command Shell. 黑金动力社区Http://www.heijin.org12确定当前的路径在软件应用的目录夹下,如下图所示: 黑金动力社区Http://www.heijin.org确认 hello.elf 文件在当前文件夹下(如果没有编译一下项目就会生成),如下图所 示:12输出 hello.elf 文件信息到 elf_information 这个临时文件。如下图所示: 黑金动力社区我们打开 elf_information 这个临时文件。如下图:Http://www.heijin.org12如果你有 Unix 或 Linux 下的编程经验, elf 文件会相对熟悉一些。 Executable and 对 elf(Linking Format)首先在 Unix 系统上开发并使用,它作为应用程序二进制接口 (Application Binary Interface,ABI)的一部分。elf 文件是基于目标文件格式的。目 标文件是程序的二进制表示,可以在处理器上直接执行,包括三种类型:可重定位 文件(Relocatable File), 可执行文件(Executable File), 共享目标文件(Shared Object File)。elf 文件既要参与程序链接又要参与程序执行,所以可以从两中并行的视图 去解析:从链接器去看 elf 文件是 Section 的集合,而从加载器角度去看 elf 文件是 Segment 的集合。如下图所示: 黑金动力社区Http://www.heijin.org12从我们生成的 elf_information 文件的 ELF Hearder 中我们可以得到如下信息:? ? ?Nios II 结构基于小端格式(低字节在低地址)。 elf 入口点地址在 0x。 程序头表(program headers)的开始地址是 0x34(52),包含 4 个程序头(每个程序头描 述一个段),每个程序头占 32 个字节。?节点表(section headers)的开始地址是 0x3a044(237636),包含了 34 个节点头, 每个节 点头占 40 个字节。?ELF header 从地址 0x0 至 0x33 共占用 52 字节。根据 elf_information 的信息我们可以得出如下布局图: 黑金动力社区Http://www.heijin.org从上图我们可以看出链接器生成的的节点(section)经合并后映射成四个段(segment)12到系统的存储器中(之前我们在介绍 settings.bsp 时在 BSP Editor 打开”Memory Map”,0x---0x0001_9FFF 指向的是我们添加的 onchip_memory,共 40k 字节)去 执行。 ? segment_00 只包含了一个节点(section): .entry。打开我们之前提及的 hello_world.map 文件,定 位光标到下图所示: 黑金动力社区Http://www.heijin.org可以看到.entry 节对应的代码是 crt0.S 中的__reset,具体到这个项目我们可以在 hello_world\software\hello_world_bsp\HAL\src\crt0.S 中找到这个标号,其实这就是复位13代码的执行地址,后续章节会详细分析。 ? segment_01 这个段里依次包含了三个节点(section):.exception,.text,.rodata。.exception 在后续讨 论中断章节时会详述, 我们先看一下.text。 打开我们之前提及的 hello_world.map 文件, 定位光标到下图所示: 黑金动力社区Http://www.heijin.org13我们可以看到第一个执行的代码的地址是 0x, 很熟悉吧?这个地址正是可 执行文件的入口点地址,整个程序的执行就从这里开始。那么到底做了些什么呢?我们 从上图可以得知,这个地址执行的源代码是在 hello_world\software\hello_world_bsp\HAL\src\crt0.S 中的_start 标号处, 我们打开 crt0.S, 定位到_start 标号处,如下图所示: 黑金动力社区Http://www.heijin.org13crt0.S(代码解读) 为了更好的理解后台运行的情况, 我们详细解析一下_start 标号处代码的运行过程。 在标号之前有如下代码:/* * Start of the .text section, and also the code entry point when * the code is executed by a bootloader rather than directly from reset. */ .section .text //定义下面的代码为.text。 .align 2 //2 的 2 次方即 4 字节 32 位地址对齐。.globl _start //声明一个全局符号_start 黑金动力社区.type _start, @function //_start 的类型是 functionHttp://www.heijin.org.section,.globl,.type 都是伪指令,具体的语法可以参见 as.html 中的”Pseudo Ops”部 分,as.html 位于”nioseds_install\document\gnu-tools\binutils\”。如下图所示:13/* * Initialize the data cache if present (i.e. size & 0) and not * optimizing for RTL simulation. * RTL simulations can ensure the data cache is already initialized * so skipping this loop speeds up RTL simulation. */#if NIOS2_DCACHE_SIZE & 0 && !defined(ALT_SIM_OPTIMIZE) // NIOS2_DCACHE_SIZE 在 system.h 中可以查到是 2048,而 ALT_SIM_OPTIMIZE 在当前项目中并没有定义。所以程序会进入此条件语句。 黑金动力社区Http://www.heijin.org/* Assume the data cache size is always a power of two. */ #if NIOS2_DCACHE_SIZE & 0x8000 //NIOS2_DCACHE_SIZE=2048,不会执行此条件语句。 movhi r2, %hi(NIOS2_DCACHE_SIZE) #else movui r2, NIOS2_DCACHE_SIZE //入口处第一条执行的语句:此语句使 r2=2048。 #endif0: initd 0(r2) //关联 r2 的值到数据缓存行 addi r2, r2, -NIOS2_DCACHE_LINE_SIZE //NIOS2_DCACHE_LINE_SIZE 在 system.h 中可以查到是 32, 此语句13就是将 r2 中的值减去 32。 bgt r2, zero, 0b //如果 r2 & 0 就跳转到上面的 0 标号处,此处构成一个循环,循环跳出后就会完成数据 缓存的清除。 1:/* * The following debug information tells the ISS not to run the loop above * but to perform its actions using faster internal code. */ .pushsection .debug_alt_sim_info .int 2, 1, 0b, 1b .popsection#endif /* Initialize Data Cache */ 黑金动力社区Http://www.heijin.orgALT_SIM_OPTIMIZE 是否定义可以参见 BSP Editor 的”enable_sim_optimize”选项,选中 后在项目中就会定义 ALT_SIM_OPTIMIZE。见下图:13上述代码我们开始接触 Nios II 的指令,我们可以结合之前提到的 hello_world.objdump 文件对比来看看实际的运行情况。打开 hello_world.objdump,定位 到下图所示位置: 黑金动力社区Http://www.heijin.org可以看到第一条执行语句的反汇编的信息” 101b4:movuir2,2048”。“101b4”是运行时执行语句的地址,
是这个地址存储的十六进制的 32 位值, 这13个值其实就代表了”movui r2,2048”这条语句。”movui”是一个伪指令,参见下图:可以得出”movui r2,2048”就是”ori r2, r0, 2048”。而”ori”指令可以参见下图: 黑金动力社区Http://www.heijin.org由上图可以得出 0-5 位的二进制值是”010100b”,换成十六进制是”0x14”; 6-21 位的二 进制值是”0000b”,换成十进制就是”2048”;22-26 位的二进制数值应该13是”00010b”就是 r2 寄存器的索引”2”。27 位到 31 位的二进制数值应该是”00000b”就是 r0 寄存器的索引”0”。我们把整个 32 位值拼接起来就是 “”,换算成十六进制就是”0x820014”了, 这样和上面 提及的” ”就完全对应上了。 通过”hello_world.objdump”这个文件的反汇编部分, 我们可以知道源代码中的哪些语句会在程序运行时执行, 而且能够知道执行语句的地址。 这在分析程序是非常有用的信息。我们继续分析 crt0.S 的代码。/* Log that caches have been initialized. */ ALT_LOG_PUTS(alt_log_msg_cache)/* Log that the stack pointer is about to be setup. */ ALT_LOG_PUTS(alt_log_msg_stackpointer) 黑金动力社区Http://www.heijin.org#if (NIOS2_NUM_OF_SHADOW_REG_SETS == 0) // NIOS2_NUM_OF_SHADOW_REG_SETS 在 system.h 中可以查 到是 0 /* * Now that the caches are initialized, set up the stack pointer. * The value provided by the linker is assumed to be correctly aligned. */ //下面两条语句的作用是将__alt_stack_pointer 的值赋给 sp。__alt_stack_pointer 可以从 linker.x 中 查到是 0x1a000。 movhi sp, %hi(__alt_stack_pointer) //%hi(0x1a000)=1。 ori sp, sp, %lo(__alt_stack_pointer) //%lo(0x1a000)=0xa000。/* Set up the global pointer. */ //下面两条语句将_gp 赋给 gp。_gp 可以从 linker.x 中查到 movhi gp, %hi(_gp) //13ori gp, gp, %lo(_gp) //#else /* NIOS2_NUM_OF_SHADOW_REG_SETS & 0 */此条件分支不会执行,暂不分析。/* * Set up the GP and SP in all shadow register sets. *//* * Check current register set number, if CPU resets into a shadow register * set, switch register set to 0 by writing zero to SSTATUS register and * execute an ERET instruction that just jumps to the next PC address * (use the NEXTPC instruction to get this). 黑金动力社区*/Http://www.heijin.orgrdctl r2, status/* Get the current register set number (STATUS.CRS). */ andi r3, r2, NIOS2_STATUS_CRS_MSK/* Skip switch register set if STATUS.CRS is 0. */ beq r3, zero, .Lskip_switch_reg_set.set nobreak13/* Current register set is non-zero, set SSTATUS to 0. */ mov sstatus, zero/* Get next pc and store in ea. */ nextpc ea/* Point to instruction after eret. */ addi ea, ea, 8/* * Execute ERET instruction that just jumps to the next PC address */ 黑金动力社区eretHttp://www.heijin.org.Lskip_switch_reg_set: mov r2, zero/* Reset STATUS register */ wrctl status, r2movui r3, NIOS2_NUM_OF_SHADOW_REG_SETS/* Set up the stack pointer in register set 0. */14movhi sp, %hi(__alt_stack_pointer) ori sp, sp, %lo(__alt_stack_pointer)/* Set up the global pointer in register set 0. */ movhi gp, %hi(_gp) ori gp, gp, %lo(_gp).Lsetup_sp_and_gp_loop: /* * Setup GP and SP for shadow register set * from NIOS2_NUM_OF_SHADOW_REG_SETS to 0 */ 黑金动力社区/* Skip if number of register sets is 0. */ beq r3, zero, .Lno_shadow_register_setHttp://www.heijin.org/* Add previous register set STATUS.PRS by 1 */ movhi r4, 1 add r2, r2, r4/* Write STATUS */ wrctl status, r214/* Clear r0 in the shadow register set (not done by hardware) */ wrprs r0, r0/* Write the GP in previous register set */ wrprs gp, gp/* Only write the SP in previous register set * if using the seperate exception stack. For normal case (single stack), * funnel code would read the SP from previous register set. */ #ifdef ALT_INTERRUPT_STACKmovhi et, %hiadj(__alt_interrupt_stack_pointer) 黑金动力社区addi et, et, %lo(__alt_interrupt_stack_pointer) wrprs sp, etHttp://www.heijin.org#endif /* ALT_INTERRUPT_STACK *//* Decrease number of register set counter by 1 */ addi r3, r3, -1br .Lsetup_sp_and_gp_loop .Lno_shadow_register_set:14#endif /* NIOS2_NUM_OF_SHADOW_REG_SETS */ /* * Clear the BSS if not optimizing for RTL simulation. * * This uses the symbols: __bss_start and __bss_end, which are defined * by the linker script. They mark the begining and the end of the bss * region. The linker script guarantees that these values are word aligned. */ #ifndef ALT_SIM_OPTIMIZE //项目中没有定义 ALT_SIM_OPTIMIZE,所以会进入此条件分支。 /* Log that the BSS is about to be cleared. */ ALT_LOG_PUTS(alt_log_msg_bss)movhi r2, %hi(__bss_start) 黑金动力社区Http://www.heijin.orgori r2, r2, %lo(__bss_start) //这两条语句将__bss_start 的值赋给寄存器 r2。__bss_start 在 linker.x 定义。movhi r3, %hi(__bss_end) ori r3, r3, %lo(__bss_end) //这两条语句将__bss_end 的值赋给寄存器 r3。__bss_start 在 linker.x 定义。beq r2, r3, 1f //r2 与 r3 的值并不相等,所以继续执行下一条语句。0: //将.bss 段清 0。 stw zero, (r2) addi r2, r2, 4 bltu r2, r3, 0b141:/* * The following debug information tells the ISS not to run the loop above * but to perform its actions using faster internal code. */ .pushsection .debug_alt_sim_info .int 3, 1, 0b, 1b .popsection #endif /* ALT_SIM_OPTIMIZE */ /* * The alt_load() facility is normally used when there is no bootloader. 黑金动力社区* It copies some sections into RAM so it acts like a mini-bootloader. */Http://www.heijin.org#ifdef CALL_ALT_LOAD //已定义 CALL_ALT_LOAD,所以程序会进入此条件分支。#ifdef ALT_STACK_CHECK //项目中并没有定义 ALT_STACK_CHECK /* * If the user has selected stack checking then we need to set up a safe * value in the stack limit register so that the relocation functions * don't think the stack has overflowed (the contents of the rwdata * section aren't defined until alt_load() has been called). */14mov #endifet, zerocall alt_load //调用 alt_load()函数。#endif /* CALL_ALT_LOAD */ALT_SIM_OPTIMIZE 是否定义可以参见 BSP Editor 的”enable_sim_optimize”选项,选中 后在项目中就会定义 ALT_SIM_OPTIMIZE。此项目中默认未选中此项。见下图: 黑金动力社区Http://www.heijin.orgCALL_ALT_LOAD 是否定义可以参见 BSP Editor 的”enable_alt_load”选项,选中后在项 目中就会定义 CALL_ALT_LOAD。此项目中默认选中此项。见下图:14 黑金动力社区Http://www.heijin.orgALT_STACK_CHECK 是否定义可以参见 BSP Editor 的”enable_runtime_stack_checking” 选项,选中后在项目中就会定义 ALT_STACK_CHECK。 此项目中默认未选中此项。 见下图:14alt_load()函数的源代码位于 alt_load.c 中,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp\HAL\src”中找到这个文件。代码解读 见后。我们继续分析 crt0.S 的代码。#ifdef ALT_STACK_CHECK //由于 ALT_STACK_CHECK 没有定义,所以不会执行下条语句 /* * Set up the stack limit (if required). The linker has set up the * copy of the variable which is in memory. */ 黑金动力社区ldw #endif et, %gprel(alt_stack_limit_value)(gp)Http://www.heijin.org/* Log that alt_main is about to be called. */ ALT_LOG_PUTS(alt_log_msg_alt_main)/* Call the C entry point. It should never return. */ call alt_main //调用 alt_main()函数。/* Wait in infinite loop in case alt_main does return. */ alt_after_alt_main:14br alt_after_alt_main.size _start, . - _start/* * Add information about the stack base if stack overflow checking is enabled. */ #ifdef ALT_STACK_CHECK .globl alt_stack_limit_value .section .sdata,&aws&,@progbits .align 2 .type .size alt_stack_limit_value, @object alt_stack_limit_value, 4 黑金动力社区alt_stack_limit_value: .long #endif __alt_stack_limitHttp://www.heijin.org调用完 alt_load()函数后,紧接着会调用 alt_main()函数,alt_main()函数的源代码位 于 alt_main.c 中,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp\HAL\src”中找到这个文件。代码解读 见后。我们先来分析 alt_load.c 的代码。alt_load.c(代码解读)void alt_load (void) {14/* * Copy the .rwdata section. */alt_load_section (&__flash_rwdata_start, &__ram_rwdata_start, &__ram_rwdata_end); //加载.rwdata 段。/* * Copy the exception handler. */alt_load_section (&__flash_exceptions_start, 黑金动力社区&__ram_exceptions_start,Http://www.heijin.org&__ram_exceptions_end); ////加载.exception 段。/* * Copy the .rodata section. */alt_load_section (&__flash_rodata_start, &__ram_rodata_start, &__ram_rodata_end); //加载.rodata 段。14/* * Now ensure that the caches are in synch. */alt_dcache_flush_all(); //同步数据缓存。 alt_icache_flush_all(); //同步指令缓存。 }其中__flash_rwdata_start,__ram_rwdata_start,__ram_rwdata_end, __flash_rodata_start, __ram_rodata_start, __ram_rodata_end, __flash_exceptions_start, __ram_exceptions_start, __ram_exceptions_end 你可以从之前解读过的 linker.x 查找到这 些符号的定义。alt_load_section 的定义可以在 alt_load.h 中找到,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp\HAL\inc\sys”中找到这个文件。 黑金动力社区Http://www.heijin.orgalt_dcache_flush 的定义可以在 alt_dcache_flush.c 中找到,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp\HAL\src”中找到这个文件。 alt_icache_flush 的定义可以在 alt_icache_flush.c 中找到,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp\HAL\src”中找到这个文件。alt_main.c(代码解读)void alt_main (void) { #ifndef ALT_NO_EXIT //没有定义 ALT_NO_EXIT。 #endif15/* ALT LOG - please see HAL/sys/alt_log_printf.h for details */ ALT_LOG_PRINT_BOOT(&[alt_main.c] Entering alt_main, calling alt_irq_init.\r\n&); /* Initialize the interrupt controller. */ alt_irq_init (NULL); //初始化中断控制器/* Initialize the operating system */ ALT_LOG_PRINT_BOOT(&[alt_main.c] Done alt_irq_init, calling alt_os_init.\r\n&); ALT_OS_INIT(); //初始化操作系统。/* * Initialize the semaphore used to control access to the file descriptor * list. 黑金动力社区*/Http://www.heijin.orgALT_LOG_PRINT_BOOT(&[alt_main.c] Done OS Init, calling alt_sem_create.\r\n&); ALT_SEM_CREATE (&alt_fd_list_lock, 1);/* Initialize the device drivers/software components. */ ALT_LOG_PRINT_BOOT(&[alt_main.c] Calling alt_sys_init.\r\n&); alt_sys_init(); //系统初始化 ALT_LOG_PRINT_BOOT(&[alt_main.c] Done alt_sys_init.\r\n&);#if !defined(ALT_USE_DIRECT_DRIVERS) && (defined(ALT_STDIN_PRESENT) || defined(ALT_STDOUT_PRESENT)15|| defined(ALT_STDERR_PRESENT))/* * Redirect stdio to the apropriate devices now that the devices have * been initialized. This is only done if the user has requested these * devices be present (not equal to /dev/null) and if direct drivers * aren't being used. */ALT_LOG_PRINT_BOOT(&[alt_main.c] Redirecting IO.\r\n&); alt_io_redirect(ALT_STDOUT, ALT_STDIN, ALT_STDERR); #endif 黑金动力社区Http://www.heijin.org#ifndef ALT_NO_C_PLUS_PLUS //项目默认没有定义 ALT_NO_C_PLUS_PLUS /* * Call the C++ constructors */ALT_LOG_PRINT_BOOT(&[alt_main.c] Calling C++ constructors.\r\n&); _do_ctors (); //调用 C++的构造器,具体的可以查看 alt_do_ctors.c #endif /* ALT_NO_C_PLUS_PLUS */#if !defined(ALT_NO_C_PLUS_PLUS) && !defined(ALT_NO_CLEAN_EXIT) && !defined(ALT_NO_EXIT) //此条件为 真,说明见后。 /* * Set the C++ destructors to be called at system shutdown. This is only done * if a clean exit has been requested (i.e. the exit() function has not been * redefined as _exit()). This is in the interest of reducing code footprint, * in that the atexit() overhead is removed when it's not needed. */15ALT_LOG_PRINT_BOOT(&[alt_main.c] Calling atexit.\r\n&); atexit (_do_dtors); #endif/* * Finally, call main(). The return code is then passed to a subsequent 黑金动力社区* call to exit() unless the application is never supposed to exit. */Http://www.heijin.orgALT_LOG_PRINT_BOOT(&[alt_main.c] Calling main.\r\n&);#ifdef ALT_NO_EXIT // ALT_NO_EXIT 在项目中没有定义。 main (alt_argc, alt_argv, alt_envp); #else result = main (alt_argc, alt_argv, alt_envp); //调用我们生成的 main()函数。 close(STDOUT_FILENO); exit (result);15#endifALT_LOG_PRINT_BOOT(&[alt_main.c] After main - we should not be here?.\r\n&); }alt_irq_init (NULL)的定义可以在 alt_sys_init.c 中找到,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp”中找到这个文件。这个函数又包含了 两个子函数,其一是 ALTERA_NIOS2_QSYS_IRQ_INIT ( NIOS2, nios2),它的定义可以在 altera_nios2_irq.c 找到,对于本项目你可以在 “C:\nios2\hello_world\software\hello_world_bsp\HAL\src”中找到这个文件。它的作用 就是将 ctl3(ienable)寄存器清零, 禁止所有中断。 其二是 alt_irq_cpu_enable_interrupts(), 它的定义可以在 alt_irq.h 找到,对于本项目你可以在” 黑金动力社区Http://www.heijin.orgC:\nios2\hello_world\software\hello_world_bsp\HAL\inc\sys”中找到这个文件。它的作用 是将 ctl0(status)寄存器的 PIE 位置 1,允许中断的产生。alt_sys_init()的定义可以在 alt_sys_init.c 中找到,对于本项目你可以在” C:\nios2\hello_world\software\hello_world_bsp”中找到这个文件。 这个函数又包含了两个 子函数,其一是 ALTERA_AVALON_JTAG_UART_INIT ( JTAG_UART, jtag_uart),它的定义可 以在 altera_avalon_jtag_uart_init.c 中找到,对于本项目你可以在” C:\nios2\hello_world\software\hello_world_bsp\drivers\src”中找到这个文件。它的作用就 是对之前添加的 JTAG_UART 核进行初始化。其二是 ALTERA_AVALON_SYSID_QSYS_INIT ( SYSID, sysid),它的定义可以在 altera_avalon_sysid_qsys.h 找到,对于本项目你可以在”15C:\nios2\hello_world\software\hello_world_bsp\drivers\inc”中找到这个文件。ALT_NO_C_PLUS_PLUS 是否定义可以参见 BSP Editor 的”enable_c_plus_plus”选项,未 选中在项目中就会定义 ALT_NO_C_PLUS_PLUS。见下图: 黑金动力社区Http://www.heijin.org15ALT_NO_EXIT 是否定义可以参见 BSP Editor 的”enable_exit”选项,未选中在项目中就 会定义 ALT_NO_EXIT。见下图: 黑金动力社区Http://www.heijin.orgALT_NO_CLEAN_EXIT 是否定义可以参见 BSP Editor 的”enable_clean_exit”选项,未选中 在项目中就会定义 ALT_NO_CLEAN_EXIT。见下图:15Run as Nios II Hardware 的分析验证 经过之前的分析我们从代码的角度上剖析了 Run as Nios II Hardware 的执行过程, 相 对抽象了一些,现在我们就在开发板上实际调试一下已验证我们之前的分析。为了方便 调试,调试之前我们先进行一些设置。在 Nios II Software Build Tools for Eclipse 的菜单栏 中选择”Windows”下的”Preferences”。如下所示: 黑金动力社区Http://www.heijin.org15在代码编辑器中显示代码行。如下图所示: 黑金动力社区Http://www.heijin.org15将变量、表达式、寄存器的格式设置为 16 进制数显示。如下图所示: 黑金动力社区Http://www.heijin.org15点击 OK 完成设置。打开 crt0.S 在 237 处、241 处、408 处、437 处添加断点,如下 图所示: 黑金动力社区Http://www.heijin.org16连接开发板与主机的 JTAG 线,打开开发板电源,下载硬件配置信息(hello.sof)到开 发板,运行”Debug as Nios II Hardware”.如下图所示: 黑金动力社区Http://www.heijin.org16进入调试界面后点选”Instruction Stepping Mode”,如下图所示: 黑金动力社区Http://www.heijin.org16打开寄存器栏,在右侧的反汇编栏中定位到””处。如下图所示: 黑金动力社区Http://www.heijin.org16之前我们分析过执行的第一条指令在地址 0x 处, 对应的执行语句是”movui r2, NIOS2_DCACHE_SIZE”我们在此处设立了第一个断点。 Debug 直接执行了这条语句 (为 什么我也不清楚,有知道的朋友望告知!!!)停在了下一条语句的断点处,我们可以看到 由于已经执行了第一条语句,寄存器 r2 的值已经变为 0x800 即 2048。我们在 Memory 栏中输入地址 0x.如下图所示: 黑金动力社区Http://www.heijin.org点击 OK 后可以看到 0x 地址处正是我们之前分析的第一条执行语句的机器 码 08200)。如下图所示:16执行若干次”Resume(F8)”后跳出循环体(此处操作有点坑爹),会执行到下一个断 点”call alt_load”处,如下图所示: 黑金动力社区Http://www.heijin.org16 黑金动力社区此时我们执行”Step Into(F5)”,如下图所示:Http://www.heijin.org16 黑金动力社区Http://www.heijin.org16此时程序进入到 alt_load.c 中的 alt_load(void)函数中执行。 我们执行”Step Return(F7)” 跳出函数体,就会返回到我们在 crt0.S 中的下一条执行语句”call atl_main”处,如下图所 示: 黑金动力社区Http://www.heijin.org16 黑金动力社区Http://www.heijin.org我们继续执行”Step Into(F5)”进入函数体,可以看到此时程序进入到了 alt_main.c 中 的 alt_main(void)中执行。如下图所示:16执行&Step Over(F6)&使程序运行到”alt_irq_init(NULL)”处。如下图所示: 黑金动力社区Http://www.heijin.org17继续执行&Step Into(F5)&进入函数体,可以看到程序进入到了 alt_sys_init.c 中的 void alt_irq_init ( const void* base )中运行。如下图所示: 黑金动力社区Http://www.heijin.org17执行&Step Return(F7)&跳出函数体,程序返回到 alt_main.c 的下一条执行语句处。如 下图所示: 黑金动力社区Http://www.heijin.org17再次执行&Step Into(F5)&进入函数体,可以看到程序进入到了 alt_sys_init.c 中的 void alt_sys_init( void )中运行。如下图所示: 黑金动力社区Http://www.heijin.org17继续执行&Step Return(F7)&跳出函数体, 程序返回到 alt_main.c 的下一条执行语句处。 如下图所示: 黑金动力社区Http://www.heijin.org17执行&Step Over(F6)&执行语句到”result = main (alt_argc, alt_argv, alt_envp)”处,如下 图所示: 黑金动力社区Http://www.heijin.org17这时执行&Step Into(F5)&,可以看到程序进入到我们生成代码的源文件 hello_world.C 中 main()函数中执行。如下图所示: 黑金动力社区Http://www.heijin.org17执行&Step Return(F7)&后返回 alt_main.c 后,选择&Step Into(F5)&单步执行程序,在” close(STDOUT_FILENO)”语句处可以看到程序会进入 alt_close.c 中的 int ALT_CLOSE (int fildes)函数中运行。如下图所示: 黑金动力社区Http://www.heijin.org17执行&Step Return(F7)&返回到 alt_main.c 中的”exit (result)”处以&Step Into(F5)&继续运 行程序,可以看到程序会进入 exit.c 中的_DEFUN(exit, (code), int code)中运行。如下图所 示: 黑金动力社区Http://www.heijin.org17执行&Step Return(F7)&后,调试程序结束运行。通过在开发板上的实际调试运行, 可以看到和我们之前对代码分析得到的结果是一致的。 黑金动力社区Run as Nios II Hardware 的流程图Http://www.heijin.org17从上图可以看出在执行用户的 main()函数时,Nios II 已经在后台完成了很多工作。 黑金动力社区小结Http://www.heijin.org在本章的基础篇中我们构建了一个基于 Qsys 的最小 Nios II 系统,通过这个最小系 统我们完成了第一个软件示例”hello_world”。在进阶篇中我们通过这个简单的示例分析 了整个 Nios II 后台的运行情况。 建议各位童鞋在了解代码运行的情况后一定在自己的开 发板上实际运行一下,这样对整个 Nios II 就会有一个比较感性的认识。本章是本篇文档 的基础,以后关于很多代码优化,调试都或多或少的基于本章所讲到的内容,完成本章 后相信 Nios II 在给位童鞋面前就没什么神秘的了。18
更多相关文档}

我要回帖

更多关于 niosii 读写片内flash 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信