NIOS Ⅱ系统实例,参考自特权同学《勇敢的芯-伴你玩转NIOS Ⅱ》
一些基础操作就不再赘述
目录
1.创建Quartus项目
1.2 进入Platform Designer添加组件并设置
1.2.1 设置时钟频率50MHz;
1.2.2 添加Nios Ⅱ组件
1.2.3 添加RAM组件
1.2.4 设置Nios Ⅱ复位向量与异常向量地址,使其指向RAM
1.2.5 添加System ID组件
1.2.6 添加JTAG UART组件
1.2.7 添加Timer组件
1.2.8 添加UART组件
1.2.9 添加蜂鸣器PIO组件
1.2.10 添加拨码开关PIO组件
1.2.11 数码管组件
1.2.12 ADC组件
1.2.13 DAC组件
1.2.14 超声波测距组件
1.215 RTC组件
1.2.16 矩阵按键组件
1.3 信号连接
1.4 中断连接
1.5 地址分配
1.6 系统生成并例化模板
1.7 完成Quartus工程
2.创建BSP工程
2.1 创建BSP工程
2.2 BSP Editor
2.3 创建C代码文件
3.Nios Ⅱ实例
3.1 Hello NIOS Ⅱ
3.2 System ID与Timestamp
3.3 蜂鸣器定时鸣叫
3.4 拨码开关输入GIO控制
4.FPGA器件的代码固化
4.1 嵌入式软件HEX文件生成
4.2 程序存储器初始化文件加载
4.3 JIC文件生成和烧录配置
1.创建Quartus项目
1.2 进入Platform Designer添加组件并设置
1.2.1 设置时钟频率50MHz;
1.2.2 添加Nios Ⅱ组件
选择Nios II/f模式;
1.2.3 添加RAM组件
设置Data width为32,Total memory size为22528,其他设置为默认
1.2.4 设置Nios Ⅱ复位向量与异常向量地址,使其指向RAM
1.2.5 添加System ID组件
设置32 bit System ID为0x01223344
1.2.6 添加JTAG UART组件
设置Write FIFO和Read FIFO得存储量为16,勾选Construct using registers instead of memory blocks,其他为默认
1.2.7 添加Timer组件
设置模式为Full-featured,周期Period为1,单位为s,其他默认
1.2.8 添加UART组件
设置校验位(Parity)为NONE、8个数据位(Data bits)、1个停止位(Stop bits)、同步周期(Synchronizer stages)为 2、波特率(Baud rate)为9600、不勾选Fixed baud rate(即波特率为Nios Ⅱ处理器软件可更改)
1.2.9 添加蜂鸣器PIO组件
设置位宽(Width)为1、方向(Direction)Output、默认值(Output Port Reset Value)为0x0,其他默认。
1.2.10 添加拨码开关PIO组件
设置位宽(Width)为4、方向(Direction)为Input、勾选Edge capture register-Synchronously capture选项,同时选中边沿类型(Edge Type)为 ANY、勾选InterruptGenerate IRQ选项,中断类型(IRQ Type)为 EDGE
1.2.11 数码管组件
数码管组件通过Avalon-MM总线从机接口实现 Nios Ⅱ处理器将4位数字显示到数码管上。该组件定时进行数码管显示驱动刷新,将 Nios Ⅱ处理器发送到数据寄存器的32位数据显示到数码管上。
将source_code/digital_tube_controller下三个.v文件添加自定义数码管组件并设置信号相应参数,然后添加数码管组件
Parameters中可以设置组件源码中定义的参数是否在用户加载组件时可配置
这里ADDR_SIZE为固定值,因此不勾选
注意dtube_cs_n和dtube_data的export名字不能相同
1.2.12 ADC组件
ADC组件通过Avalon-MM总线机接口实现Nios Ⅱ处理器与ADC芯片TLC549之间的数据传输。该组件产生TLC549芯片数据读取所需的接口时序,定时读取ADC芯片的采样数据存储到Avalon-MM 总线可访问的数据寄存器中,供Nios Ⅱ处理器读取。
将source_code/adc_controller下三个.v文件添加自定义ADC组件并设置信号相应参数,然后添加ADC组件
1.2.13 DAC组件
DAC组件通过Avalon-MM总线从机接口实现 Nios Ⅱ处理器与DAC芯片DAC5571之间的数据传输。该组件产生DAC5571芯片读写访问的IIC总线接口所需的时序,在Avalon-MM总线可访问的数据寄存器值发生变化时,执行DAC5571芯片DAC转换数据的写入。
将source_code/dac_controller下三个.v文件添加自定义DAC组件并设置信号相应参数,然后添加DAC组件
1.2.14 超声波测距组件
ultrasound_controller组件通过Avalon-MM总线从机接口实现 Nios Ⅱ处理器对超声波模块获取的最新距离数据信息的采集。该组件定时产生超声波测距模块所需的触发脉冲信号,回采反馈脉冲,并且对反馈脉冲进行中值滤波和距离值换算,最终获得以毫米为单位的距离信息。该距离信息存储到 Avalon-MM总线可访问的数据寄存器中,供 Nios Ⅱ处理器读取。
将source_code/ultrasound_controller下六个.v文件以及mult文件夹(乘法器IP核)下mul.qip文件添加自定义超声波测距组件并设置信号相应参数,然后添加超声波测距组件
1.215 RTC组件
RTC组件通过Avalon-MM总线从机接口实现Nios Ⅱ处理器对RTC芯片PCF8563T的时间和日期数据进行读写操作。该组件定时读取最新的RTC数据,存储到Avalon-MM总线可访问的数据寄存器中,Nios Ⅱ处理器随时可以读取最新的RTC数据也可以写人修改的数据更新到RTC芯片中。
将source_code/rtc_controller下五个.v文件添加自定义RTC组件并设置信号相应参数,然后添加RTC组件
1.2.16 矩阵按键组件
矩阵按键组件通过Avalon-MM总线从机接口实现 Nios Ⅱ处理器对4X4矩阵按键值的采集。该组件定时产生矩阵按键扫描所需的各种电平变换,采集到按键扫描值存储到Avalon-MM总线可访问的数据寄存器中并且产生可配置开关状态的中断信号给Nios Ⅱ处理器,Nios Ⅱ处理器可通过中断状态实时读取按键值。
将source_code/Button_controller下四个.v文件添加自定义RTC组件并设置信号相应参数,然后添加矩阵按键组件
1.3 信号连接
将各个组件的时钟、复位信号连接到Clock组件的相应信号上
为了Nios Ⅱ处理器能够访问到各个组件,实现对组件数据的读取、传输等功能,则需要将Nios Ⅱ处理器的数据总线(data_master)连接到各个组件(s1、control_slave、avalon_jtag_slave)上
此外UART外设接口(RX和TX信号)需要引出到QSYS系统外部连接到FPGA的引脚上,因此需要双击UART外设external_connection一行的Double-click to,当出现接口符号,说明该接口已引出
PIO和自定义组件同理
1.4 中断连接
在IRQ一列中与Nios Ⅱ处理器相连接中断信号有timer、pio_switch、jtag_uart、uart 和 ButtonController组件。
点击这些IRQ和 NiosⅡ处理器的空心圆连接点进行连接,连接后出现的数字表示中断号,数字越低优先级越高。
1.5 地址分配
在Address Map窗口列出了所有外设的地址范围。目前地址还未分配,默认都是从0x0000开始作为基地址,所以各个外设间出现了地址冲突
在菜单栏中选择System一Assign Base Address让工具自动进行地址分配,以8bit为单位进行寻址
这这里应该所有报错都解决了
1.6 系统生成并例化模板
菜单栏Generate->Genarate HDL...
在Quartus的工具栏Assignments->Settings->Files->File name中找到生成的.qip文件并添加
添加成功后会在Project Navigator窗口中看到添加的qip文件
1.7 完成Quartus工程
最后建立工程的顶层模块,在Platform Designer中点击Generate->Show Instantiation Template生成例化代码
将例化代码复制到顶层文件cy4.v中并连接好端口
//Qsys系统
module cy4(
input ext_clk_25m, //外部输入25MHz时钟信号
input ext_rst_n, //外部输入复位信号,低电平有效
input uart_rx, //UART接收数据信号
output uart_tx, //UART发送数据信号
input[3:0] switch, //拨码开关SW3输入,ON-低电平、OFF-高电平
output beep, //蜂鸣器控制信号,1-响、0-不响
output[3:0] dtube_cs_n, //7段数码管位选信号
output[7:0] dtube_data, //7段数码管段选信号(包括小数点为8段)
input adc_data, //ADC芯片TLC549的SPI数据信号
output adc_cs_n, //ADC芯片TLC549的SPI片选信号,低电平有效
output adc_clk, //ADC芯片TLC549的SPI时钟信号
output dac_iic_sck, //DAC5571的IIC接口SCL
inout dac_iic_sda, //DAC5571的IIC接口SDA
output ultrasound_trig, //超声波测距模块脉冲激励信号,10us的高脉冲
input ultrasound_echo, //超声波测距模块回响信号
output rtc_iic_sck, //RTC芯片的IIC时钟信号
inout rtc_iic_sda, //RTC芯片的IIC数据信号
input[3:0] key_v, //4个列按键输入,未按下为高电平,按下后为低电平
output[3:0] key_h //4个行按键输入
);
wire clk_12m5; //PLL输出12.5MHz时钟
wire clk_25m; //PLL输出 25 MHz时钟
wire clk_50m; //PLL输出 50 MHz时钟
wire clk_100m; //PLL输出100 MHz时钟
wire sys_rst_n; //PLL输出的locked信号作为FPGA内部的复位信号,低电平复位,高电平正常工作
//-------------------------------------
//PLL例化
pll_controller pll_controller_inst (
.rst ( !ext_rst_n ),
.refclk ( ext_clk_25m ),
.outclk_0 ( clk_12m5 ),
.outclk_1 ( clk_25m ),
.outclk_2 ( clk_50m ),
.outclk_3 ( clk_100m ),
.locked ( sys_rst_n )
);
//-------------------------------------
//Qsys系统例化
cy4_qsys uun_cy4_qsys (
.adc_controller_1_conduit_end_export_clk (adc_clk), //adc_controller_1_conduit_end.export_clk
.adc_controller_1_conduit_end_export_cs_n (adc_cs_n), //.export_cs_n
.adc_controller_1_conduit_end_export_data (adc_data), //.export_data
.buttoncontroller_0_conduit_end_1_export_v (key_v), //buttoncontroller_0_conduit_end_1.export_v
.buttoncontroller_0_conduit_end_1_export_h (key_h), //.export_h
.clk_clk (clk_50m), //clk.clk
.dac_controller_0_conduit_end_export_scl (dac_iic_sck), //dac_controller_0_conduit_end.export_scl
.dac_controller_0_conduit_end_export_sda (dac_iic_sda), //.export_sda
.digital_tube_controller_conduit_end_export_cs_n (dtube_cs_n), //digital_tube_controller_conduit_end.export_cs_n
.digital_tube_controller_conduit_end_export_data (dtube_data), //.export_data
.pio_beep_external_connection_export (beep), //pio_beep_external_connection.export
.pio_switch_external_connection_export (switch), //pio_switch_external_connection.export
.reset_reset_n (sys_rst_n), //reset.reset_n
.rtc_controller_0_conduit_end_export_rtcsda (rtc_iic_sda), //rtc_controller_0_conduit_end.export_rtcsda
.rtc_controller_0_conduit_end_export_rtcsck (rtc_iic_sck), //.export_rtcsck
.uart_external_connection_rxd (uart_rx), //uart_external_connection.rxd
.uart_external_connection_txd (uart_tx), //.txd
.ultrasound_controller_0_conduit_end_export_trig (ultrasound_trig), //ultrasound_controller_0_conduit_end.export_trig
.ultrasound_controller_0_conduit_end_export_echo (ultrasound_echo) //.export_echo
);
endmodule
添加一个PLL IP核
进行语法分析,引脚分配,系统编译,完成Quartus Ⅱ工程
2.创建BSP工程
2.1 创建BSP工程
进行Nios Ⅱ嵌入式软件开发
点击Tool->Nios Ⅱ Software Buid Tools for Eclipse,创建工作空间
点击File->New->Nios Ⅱ Application and BSP from Template新建项目
点击Target hardware information中的[...] 按钮选择Quartus工程下的.sopcinfo文件,Nios II SBT for Eclipse软件会自动识别Qsys系统中CPU的名称
然后给创建的 Nios Ⅱ工程命名(名称需要以英文字母开头,可以包含字母、数字和下划线,不能有中文及特殊字符)
在取消勾选Use default location后可以修改然后将工程存放的位置
在Project template一栏中列出了一些工程模板,也可以选择创建空白工程 (Bland Project)
点击下方Next系统会自动创建一个BSP(Board Support Package,板级支持包)工程,提供了访问底层硬件(Qsys 系统)的函数库
.sopcinfo文件是Qsys系统生成时一同产生,包含了所有Qsys系统的硬件信息,将它导入到BSP工程使得BSP获得全部硬件信息
在Project Explorer下出现了nios2bsp_bsp工程,可以看到这个文件夹下包含了各种和当前Qsys系统相关的板级动源文件和头文件,供应用软件调用。
头文件system.h将Qsys系统中的 Nios Ⅱ处理器和所有外设的名称、基地址、中断有无以及优先级号码等相关硬件信息进行了定义。
其中nios2bsp是C/C++应用工程,nios2bsp_bsp是函数库
2.2 BSP Editor
在BSPEditor中可以对板级驱动层进行一些定制化的配置,比如代码裁剪、标准输入/输出外设和定时器外设的设置等。
在Project Explorer下工程名右键点击Nios Ⅱ->BSP Editor
在左侧一栏选中Common,然后在右侧勾选两个选项:
enable_reduced_device_drivers: BSP为处理器的外设提供了两个版本的驱动库:一种是执行速度快但代码量比较大的版本:另一种是封装小的版本。默认使用的是代码量大的版本,这里通过[enable reduced device drivers]选项来选择封装小的版本,从而减少代码量。
enalbe_small_c_library: 完整的ANSIC标准库通常不适用于嵌入式系统,BSP提供了一系列经过裁剪的ANSIC标准库,占用资源比较少,通过[enalbe small c lbrary]选项来选择精简的ANSIC标准库。
在左侧一栏选中Settings,将右侧两个选项(默认勾选) 取消勾选:
enable_c_plus_plus: 使用 C 语言来编写软件程序,因此不需要使能 C+。
enable clean exit: 当选中该选项时,系统库在主函数main()返回时会调用exit()。调用 exit0时,首先会清理I/O的缓冲区,然后再调用exit()。当不选中该选项时,系统库会只调用exit(),这样将会节省程序空间。对于嵌入式系统程序来说,一般都不会从 main()返回,所以可以不勾选该选项。
设置完成后先点击Generate然后再点击Exit
2.3 创建C代码文件
在Project Explorer下工程名右键点击New->Source File新建C代码源文件
在Project Explorer下工程名右键Build Project (CTRL+B)进行软件工程编译
3.Nios Ⅱ实例
3.1 Hello NIOS Ⅱ
通过JTAG UART在Nios Console中每隔3s打印一串“Hello NIOS II”的字符串
#include "system.h" //定义Qsys中各个外设的地址、中断优先级等基本硬件信息
#include <stdio.h> //定义标准输入、输出函数
#include <unistd.h> //包含了延时函数usleep()函数的生命
///
//功能:每隔3s通过JTAG UART打印一条字符串“Hello NIOS Ⅱ”
///
int main(void)
{
while(1)
{
printf("Hello NIOS II!\n");
usleep(3000000);
}
return 0;
}
printf对应的设备在BSP Editor中设定
下载验证
先把.sof文件下载到开发板中,下载完成后开发板上没有任何实验现象
再下载.elf 文件(注意一定要先下载 sof 文件,再下载 elf 文件),Nios Ⅱ SBT for Eclipse中工具栏Run->Run AS->Nios II Hardware,将编译生成可执行文件.elf下载到硬件系统中。
如果在程序下载的过程中弹出了“Run Configurations”窗口,提示找不到与Nios II硬件系统的连接
那么在这个窗口中点击Target Connection标签,点击Refresh Connections按钮,软件使会自动识别开发板上的 Qsys 系统,并显示 Qsys 系统的相关信息,接着点击[Apply] [Run],软件会把.elf 文件下载至开发板中
下载结束后,程序自动开始运行,在软件下方的“Nios II Console”中会打印“Hello,Nios Ⅱ!”信息
3.2 System ID与Timestamp
读取 System ID外设的两个寄存器值,一个是id 值,另一个是Testamp值
地址偏移 | 寄存器名称 | 读/写 | 功能描述 |
0 | id | 读 | 基于Qsys系统定义的唯一的32位数值,该id值类似于校验和;不同组件、不同选项配置的Qsys系统产生不同的id值 |
1 | timestamp | 读 | 基于系统生成时间的唯一32位值,该值等效于从1970年1月1日以来所经过的总秒数 |
#include "alt_types.h" //对altera定义的数据类型进行宏定义声明,alt_u32表示32位的无符号整型
#include "system.h"
#include <stdio.h>
#include "altera_avalon_sysid_qsys_regs.h" //定义了System ID硬件寄存器访问的接口函数
///
//功能:读取System ID的ID值核timestamp值,通过JTAG UART打印
///
int main(void)
{
//读取%sy_id值
alt_u32 hardware_id = IORD_ALTERA_AVALON_SYSID_QSYS_ID(SYSID_BASE); //读取定义的System ID外设的id值,SYSID_BASE是定义的System ID外设的基址
//读取%sy_id的timestap值
alt_u32 hardware_timestamp = IORD_ALTERA_AVALON_SYSID_QSYS_TIMESTAMP(SYSID_BASE); //读取定义的System ID外设的Timestamp值
printf("System ID is 0x%8x\n",hardware_id);
printf("System timestamp is 0x%8x\n",hardware_timestamp);
while(1);
return 0;
}
3.3 蜂鸣器定时鸣叫
使用Timer定时器产生秒中断信号,驱动蜂鸣器发出响声
Timer 定时器组件内部有可编程的32位定时计数器,通过编程访问该组件的控制和状态寄存器实现定时中断功能。
Timer定时器外设寄存器定义
地址偏移 | 寄存器名称 | 读/写 | 功能描述 |
0 | status | 读写 | bit 15~2:保留 bit 1:运行指示位。当计数寄存器运行时,该位置高。 bit 0:定时结束指示位。当计数回零时,该位置高。一旦定时结束事件发生,该位置高,直到对该寄存器读写后该位清零 |
1 | control | 读写 | bit 15~4:保留 bit 3:停止位。该位写1将停止当前定时计数功能。 bit 2:启动位。该位写1将启动定时计数功能。 bit 1:运行指示位。该位置1后,计数器清零后将继续计数,即执行连续计数;该位清0后,计数器清零后将停止计数,即只执行一次计数。 bit 0:中断表示位。该位提高后,一旦status寄存器的bit 0拉高,即产生IRQ中断。 |
2 | periodl | 读写 | 定时计数周期值-1(高16位) |
3 | periodh | 读写 | 定时计数周期值-1(低16位) |
4 | snapl | 读写 | 当前计数值(高16位) |
5 | snaph | 读写 | 当前计数值(低16位) |
#include "alt_types.h"
#include "altera_avalon_pio_regs.h" //声明PIO外设函数,如IOWR_ALTERA_AVALON_PIO_DATA()函数
#include "altera_avalon_timer_regs.h" //声明Timer定时器外设函数,如IOWR_ALTERA_AVALON_TIMER_STATUS()/CONTROL()
#include "sys/alt_irq.h" //声明中断相关的函数,如all_irq_register
#include "system.h"
int flag,second;
///
//功能:Timer定时器初始化函数
///
void init_timer(void)
{
//注册定时器中断函数
//TIMER_IRQ是system.h中定义的Timer定时器组件的中断号,TIMER_BASE是Timer定时器组件的基址,handle_time_interrupts为中断函数
alt_irq_register(TIMER_IRQ,TIMER_BASE,handle_time_interrupts);
//对Timer定时器组件的control寄存器写数据7,即启动timer定时计数功能,持续循环计数产生IRQ中断中断
IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER_BASE,7);
//清除标志位
flag = 0;
//初始化函数没有对periodl和periodh寄存器进行设置,使用默认的计数值,即在Qsys中定义的1s计数周期
}
///
//功能:秒定时中断处理函数
///
static void handle_time_interrupts(void)
{
//函数写Timer定时器组件的status寄存器,即清TO标志
IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER_BASE,0);
flag = 1;
second++;
}
///
//功能:秒定时中断处理函数
///
int main(void)
{
IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0); //拨码开关OFF
init_timer(); //Timer定时器初始化函数
while(1)
{
if(flag)
{
flag = 0;
//该函数对基址为PIO_BEEP_BASE的PIO外设写数据1/0
if(second & 0x01) IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,1); //拨码开关ON
else IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0); //拨码开关OFF
}
}
return 0;
}
3.4 拨码开关输入GIO控制
使用4个拨码开关产生中断,对不同拨码开关值进行判断,相应驱动蜂鸣器发出1~4此响声
PIO组件寄存器定义
地址偏移 | 寄存器名称 | 读/写 | 功能描述 |
0 | data | 读写 | 作为输入PIO时,读控制获取当前输入PIO的电平值; 作为输出PIO时,写控制将数据输出到PIO上 |
1 | direction | 读写 | 每个PIO引脚单独的方向控制。电平0设置PIO为输入;电平1设置PIO为输出 |
2 | interruptmask | 读写 | 每个PIO引脚对应的IRQ中断使能。电平1设置IRQ中断使能 |
3 | edgecapture | 读写 | 每个PIO引脚的边沿变化状态捕获 |
#include "alt_types.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include "system.h"
#include <stdio.h>
#include <unistd.h>
#include <io.h>
/
//函数申明
void init_switch_pio(void); //switch GIO初始化函数
void beep_didi(alt_u8 time); //蜂鸣器发出“滴滴”响声函数
/
//宏定义
/
//变量申明
alt_u8 edge_capture_value; //当前拨码开关值
/
//功 能: 拨码开关中断服务函数
/
static void handle_switch_interrupts(void)
{
//捕获当前PIO值
edge_capture_value = IORD_ALTERA_AVALON_PIO_DATA(PIO_SWITCH_BASE);
//清除边沿中断标志位
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SWITCH_BASE,0x00);
}
/
//功 能: 主函数,拨码开关从off到on拨动时,蜂鸣器发出几声清脆的“滴”响声
/
int main(void)
{
IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0); //beep off
init_switch_pio(); //switch GIO初始化函数
while(1)
{
if(~edge_capture_value & 0x01) //蜂鸣器发出1声“滴”
{
beep_didi(1);
}
else if(~edge_capture_value & 0x02) //蜂鸣器发出2声“滴”
{
beep_didi(2);
}
else if(~edge_capture_value & 0x04) //蜂鸣器发出3声“滴”
{
beep_didi(3);
}
else if(~edge_capture_value & 0x08) //蜂鸣器发出4声“滴”
{
beep_didi(4);
}
}
return 0;
}
/
//功 能: switch GIO初始化函数
/
void init_switch_pio(void)
{
//初始化拨码开关状态值
edge_capture_value = 0xff;
//使能3个拨码开关中断
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_SWITCH_BASE, 0xff);
//复位拨码开关边沿状态
IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SWITCH_BASE, 0x00);
//拨码开关输入GIO中断复位申明
alt_irq_register(PIO_SWITCH_IRQ,PIO_SWITCH_BASE,handle_switch_interrupts);
}
/
//功 能: 蜂鸣器发出“滴滴”响声函数
//参 数: alt_u8 time 发出响声的次数
/
void beep_didi(alt_u8 time)
{
alt_u8 i;
for(i=0;i<time;i++)
{
IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,1); //beep on
usleep(20000); //delay 100ms
IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0); //beep off
usleep(20000); //delay 100ms
}
}
其他实例可参照参考书籍进行
4.FPGA器件的代码固化
4.1 嵌入式软件HEX文件生成
基于Nios Ⅱ处理器的FPGA器件代码固化和一般只有 FPGA 逻辑的代码固化不同
除了正常的FPGA逻辑部分代码需要固化外,还有Nios Ⅱ处理器的程序代码也需要固化。
使用FPGA片内存储器作为Nios Ⅱ处理器的代码存储器,在生成逻辑代码部分的烧录文件时,将 Nios Ⅱ处理器的代码集成在一起,就可以使用一个烧录文件完成 FPGA 器件的固化
打开工程的BSP Editor页面,选择Setting->Advanced->hal->linker,勾选allow_code_at_reset选项,然后重新编译C/C++软件工程文件和函数库文件
设置该选项的目的是在确保FPGA使用了片内存储器作Nios Ⅱ处器代码存储器时,在FPGA逻辑运行起来以后,Nios Ⅱ处理器复位后直接可以从片内存储器开始运行代码
然后右键软件工程文件->Make Targets->Build,选择men_init-generate,点击Build
然后就可以在软件工程文件夹下发现.hex文件,为软件工程代码对应的十六进制HEX文件,即Nios Ⅱ处理器的软件代码
4.2 程序存储器初始化文件加载
进入Qsys界面,双击onchip_men组件进入配置界面的Memery initialization
勾选Initialize memory content和Enable non-default initialization file,并在User created initialization file中添加刚才生成的.hex文件
重新生成Qsys工程并且重新编译整个Quartus工程并产生新的.sof 文件
此时.sof 文件通过JTAG在线烧录到FPGA器件中则会直接运行软件程序
但.sof 文件只能在线烧录,因此需要将.sof文件转换为jic文件,实现SPI Flash的固化
4.3 JIC文件生成和烧录配置
在Quartus中选择File->Convert Programming Files
在Programming file type中选择JTAG Indirect Configuration File(.jic)
Configuration device选择配置设备,File name输入转换后的文件名(output_files 文件夹下)
在Input files to convert中
单击Flash Loader所在的行,然后单击右侧Add Device按钮在Select Devices 窗口中选择设备
单击SOF Data所在行,然后单击右侧的Add File按钮,在弹出的窗口中选择output files文件夹下的 .sof文件
完成设置后,单击 Generate 生成 *.jic,弹出如图所示的提示信息,表示成功生成jic文件
打开 Quartus的 Programmer 页面,单击Add File按钮加载的jic 文件(File下若有其他文件,请删除),并且确保勾选 Program/Configure 所在列
单击 Start 按钮执行下载操作,完成下载后,开发板默认处于不工作状态,需要重启开发板,重启后就能看到最新下载的代码已经固化到 SPI Flash 中并且掉电后重启仍然可以运行。