软件版本:Quartus Prime Standard Edition 18.0 Windows
1. 建立工程
启动Quartus Prime选择New Project Wizard
设置工程名及工作目录
根据提示使用默认配置直到器件选择页面,选择FPGA型号
2. 建立顶层模块
在File菜单选择New
在弹出窗口中选择Block Diagram/Schematic File
完成后弹出模块图设计界面
3. 添加时钟模块
板上时钟进入FPGA即使频率保持不变,最好也经过时钟模块以保证FPGA内的时钟质量
在Tools菜单选择IP Catalog
在IP Catalog页选择PLL Intel FPGA IP,双击
在弹出窗口输入模块名称及保存路径
在弹出的配置窗口中进行PLL功能配置
在弹出的确认窗口中选择将生成的IP加入当前工程
双击模块图设计界面的空白处,选择加入时钟模块
保存当前模块图设计
保存后的模块图设计界面
4. 创建Platform Designer模块(原QSys)
在Tools菜单选择Platform Designer
Platform Designer窗口的File菜单选择保存
在System Contents页双击clk_0
在弹出的Parameters页设置时钟频率与时钟模块输出频率一致
5. 添加片上ROM和RAM
Nios工作必须像PC一样连接ROM和RAM,ROM用于存储程序文件,RAM用于程序运行。
一般情况下使用板上外接的ROM(比如Flash)和RAM(比如SDRAM)。
由于本演示程序规模极小(而且试验板上没有外接),因此使用片上ROM和RAM。两者都是使用FPGA的RAM资源。
由于ROM数据在FPGA烧写时才能写入,因此完成Nios软件编程后,需要重新编译FPGA工程。
**在后续的Nios软件编写过程中发现此处设置的存储空间太小了,不足以存储Nios程序,因此ROM和RAM的存储空间大小都应当放大。**目前试验的空间大小为65536 bytes。见[Nios软件编写](#17. Nios软件编写)章节。
片上ROM
在IP Catalog页双击On-Chip Memory (RAM or ROM) Intel FPGA IP
在弹出的配置窗口作如下配置
为区分,在System Contents页右键Rename改名为onchip_memory2_rom
片上RAM
在IP Catalog页双击On-Chip Memory (RAM or ROM) Intel FPGA IP
在弹出的配置窗口作如下配置
为区分,在System Contents页右键Rename改名为onchip_memory2_ram
6. 添加CPU模块
在Platform Designer窗口的IP Catalog页(与Quartus的IP Catalog页内容不同)双击Nios II Processor
在弹出的配置窗口的Main页中选择Nios II/f
在Caches and Memory Interface页,选择设置Data Cache的Size为None
Data Cache相当于CPU内的数据高速缓冲,用于保证数据传输速度,而将部分数据打包进行传输。如果处理数据量较少并且需要突发处理,使用Data Cache可能导致数据在缓冲中保存而不会完成传输,在这种情况下可以设置Size为None,或者使用most-significant address位置1的方式将寄存器的最高位设置为1,使用该寄存器的数据传输不经过Data Cache
7. 添加System ID模块
用于自动校验的一个只读模块,用于确保软件程序与Nios的硬件配置相匹配
由Platform Designer的IP Catalog页查找并且添加System ID Peripheral Intel FPGA IP,保持默认参数
8. 添加JTAG UART模块
JTAG UART模块可以通过JTAG实现PC与Nios的串口通信
由Platform Designer的IP Catalog页查找并且添加JTAG UART Intel FPGA IP,保持默认参数
9. 连接各模块信号
添加完上述各模块后的System Contents页如下
上图左侧中的空心圆圈表示信号可连接但是未连接,鼠标点击空心圆圈后变成实心圆圈,表示建立连接。
简单起见,可以把所有的空心圆圈全部建立连接。
10. 分配基地址
给各模块分配基地址用于基功能寄存器的操作,选择System菜单的Assign Base Addresses
对于已分配地址空间的模块,在添加新模块重新执行该指令时会被重新分配地址,在开发过程中为了防止出现地址变化,可以将已分配的地址空间锁定,锁定方法为鼠标点击Base地址旁的锁图标,由开锁状态转为闭锁状态
11. 生成
设置CPU的Vector
在Vectors页,设置Reset vector memory和Exception vector memory
Reset Vector指的是CPU复位后启动时指向的存储器类型和地址偏移量,一般为掉电不可擦除型,如外接的Flash
Exception Vector指的是CPU异常情况时指向的存储器类型和地址偏移量,存储器为掉电即擦除型,如SDRAM
——“NiosII的奇幻漂流”——不理解
在Platform Designer的Generate菜单选择Generate HDL
配置生成选项后点击Generate按钮
12. 向FPGA顶层添加Nios模块
回到Quartus窗口,在Project菜单选择Add/Remove Files in Project
在弹出窗口添加nios_kernel.qsys文件
在模块图设计界面双击空白处,在弹出窗口选择添加nios_kernel模块
用鼠标拖动的方式连接pll的输出时钟与nios_kernel的输入时钟
在pll模块上右键选择Generate Pins for Symbol Ports
添加了pll上未连接的端口连接
由于板上按键保持高电平,按下为低电平,用于复位的话需要加入非门,于是添加非门模块
13. 管脚约束
在Tasks页双击运行Analysis & Synthesis
在Assignments菜单打开Pin Planner
根据板卡原理图设置Location,其它部分按默认自动设置
关闭Pin Planner回到Quartus界面发现模块图形界面已将管脚分配更新至界面
14. 时钟约束
在Tasks页双击运行Fitter
正确完成后,在Tools菜单打开Timing Analyzer
在Timing Analyzer界面的Tasks页双击运行Create Timing Netlist
完成后在Contraints菜单选择Create Clock
在弹出窗口中完成配置并且点击Run按钮
在Tasks页首先双击Update Timing Netlist,完成更新后双击Write SDC File,写入SDC文件
关闭Timing Analyzer后,回到Quartus界面,在Assignment菜单打开Settings,进入Timing Analyzer页,添加刚才在Timing Analyzer生成的SDC文件
15. 编译FPGA
在Processing菜单选择Start Compilation完成编译
16. JTAG烧写
USB Blaster加载器连接PC,以及板卡的JTAG插口
板卡上电
在Tools菜单打开Programmer
在Hardware Setup选择USB-Blaster,Mode选择JTAG
在Processing菜单选择Auto Detect,给FPGA作Change File指向编译输出的sof文件
保存后点击Start按钮
当Progress显示100%,表示烧写成功
17. Nios软件编写
在Quartus的Tools菜单选择Nios II Software Build Tools for Eclipse
首先设置Workspace路径
在打开的Eclipse界面的File菜单选择新建Nios II Application and BSP from Template
在向导中首先设置sopc info文件及工程名,模板一般就选最简单的Hello World
向导下一页是BSP工程配置,在BSP工程中包含了访问Nios硬件的函数包,按默认配置,点击Finish按钮
每个Nios软件都需要一个Nios工程(称为原工程)和一个对应的BSP工程
建立工程后Project Explorer页出现2个工程:原工程与BSP工程
双击打开
原始代码修改成以下代码
#include <stdio.h>
#include <unistd.h>//引入usleep()
int main()
{
while (1)
{
printf(“Hello from Nios II!\n”);
//参考nios handbook中的HAL API Reference,实现等待us
usleep(1000000);
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在Project菜单选择Build All
由于有2个工程,也可以在Project Explorer右键选择工程先Build BSP工程,再Build原工程
由于使用片上ROM和RAM用于Nios,如果空间较小,原工程的程序无法装入。在[添加片上ROM和RAM](#5. 添加片上ROM和RAM)章节已加入放大存储空间的说明。
完成Build后,在原工程中右键选择Run As: Nios Hardware
在Nios II Console页弹出"Hello from Nios II!",来自于Nios内的JTAG Uart模块
至此已完成Nios基本功能模块的添加流程。
以下内容通过添加的功能说明Nios的编程操作,包括:
- LED灯闪烁
- 板卡按键作为中断改变LED的闪烁模式
A. 添加PIO模块用于LED灯控制
在Quartus内双击nios_kernel.qsys文件打开Platform Designer
在Platform Designer的IP Catalog中选择并双击PIO (Parallel I/O) Intel FPGA IP
在弹出的配置窗口中设置功能
在System Contents页连接各端口并引出PIO的输出端口
在System菜单选择Assign Base Addresses,给PIO模块分配寄存器地址
B. 添加PIO模块用于接收按键事件并产生中断
从IP Catalog页打开PIO (Parallel I/O) Intel FPGA IP配置窗口,设置为1位输入电平中断
用与上述一致的方法连接模块端口,引出PIO的输入端口
注意中断序号可以手动修改,但是一般情况下按默认分配就可以了
在System菜单选择Assign Base Addresses,给PIO模块分配寄存器地址
参考[生成](#11. 生成)章节的说明生成Nios模块
C. 编译FPGA
在Quartus界面双击顶层模块test.bdf,打开模块图形界面
删除原有nios_kernel模块后,添加新的nios_kernel模块,并且如下图连接
参考[管脚约束](#13. 管脚约束)章节添加led和irq的管脚Location
在Processing菜单选择Start Compilation编译FPGA工程
D. Nios软件编写
参考前述,在Eclipse中建立工程cc和cc_bsp,在Project Explorer中右键关闭之前建立的tt和tt_bsp工程
如果需要继续使用原有工程,则需要在bsp工程右键菜单中的Nios II选项,选择Generate BSP
双击打开system.h文件,找到pio_0的硬件配置定义
/*
* pio_0 configuration
*
*/
#define ALT_MODULE_CLASS_pio_0 altera_avalon_pio
#define PIO_0_BASE 0x41020
#define PIO_0_BIT_CLEARING_EDGE_REGISTER 0
#define PIO_0_BIT_MODIFYING_OUTPUT_REGISTER 1
#define PIO_0_CAPTURE 0
#define PIO_0_DATA_WIDTH 2
#define PIO_0_DO_TEST_BENCH_WIRING 0
#define PIO_0_DRIVEN_SIM_VALUE 0
#define PIO_0_EDGE_TYPE “NONE”
#define PIO_0_FREQ 50000000
#define PIO_0_HAS_IN 0
#define PIO_0_HAS_OUT 1
#define PIO_0_HAS_TRI 0
#define PIO_0_IRQ -1
#define PIO_0_IRQ_INTERRUPT_CONTROLLER_ID -1
#define PIO_0_IRQ_TYPE “NONE”
#define PIO_0_NAME “/dev/pio_0”
#define PIO_0_RESET_VALUE 0
#define PIO_0_SPAN 32
#define PIO_0_TYPE “altera_avalon_pio”
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
参考Embedded Peripherals IP User Guide中PIO Core的说明
修改main.c内代码如下:
#include <stdio.h>
#include <unistd.h>//引入usleep()
#include "../cc_bsp/system.h"
#define LED (((unsigned long int)(PIO_0_BASE+0)))//数据偏移为0
int led_mode = 0;//0值表示同步闪烁,1值表示交替闪烁
int main()
{
while (1)
{
switch (led_mode)
{
case 0:
if (LED 0) LED = 3;
else LED = 0;
break;
case 1:
if (LED 1) LED = 2;
else LED = 2;
break;
default:
LED = 0;
break;
}
<span class="token function">usleep</span><span class="token punctuation">(</span><span class="token number">500000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
运行Nios程序发现LED灯按代码设计同步闪烁
在system.h文件,找到pio_1的硬件配置定义
/*
* pio_1 configuration
*
*/
#define ALT_MODULE_CLASS_pio_1 altera_avalon_pio
#define PIO_1_BASE 0x41050
#define PIO_1_BIT_CLEARING_EDGE_REGISTER 0
#define PIO_1_BIT_MODIFYING_OUTPUT_REGISTER 0
#define PIO_1_CAPTURE 0
#define PIO_1_DATA_WIDTH 1
#define PIO_1_DO_TEST_BENCH_WIRING 0
#define PIO_1_DRIVEN_SIM_VALUE 0
#define PIO_1_EDGE_TYPE “NONE”
#define PIO_1_FREQ 50000000
#define PIO_1_HAS_IN 1
#define PIO_1_HAS_OUT 0
#define PIO_1_HAS_TRI 0
#define PIO_1_IRQ 1
#define PIO_1_IRQ_INTERRUPT_CONTROLLER_ID 0
#define PIO_1_IRQ_TYPE “LEVEL”
#define PIO_1_NAME “/dev/pio_1”
#define PIO_1_RESET_VALUE 0
#define PIO_1_SPAN 16
#define PIO_1_TYPE “altera_avalon_pio”
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
在main.c中注册中断,并且添加切换模式的代码,由于RAM空间不足导致编译错误
#include <stdio.h>
#include <unistd.h>//引入usleep()
#include <sys/alt_irq.h>//引入alt_ic_isr_register()
#include "../cc_bsp/system.h"
#define LED (((unsigned long int)(PIO_0_BASE+0)))//数据偏移为0
int led_mode = 0;//0值表示同步闪烁,1值表示交替闪烁
void irq_cb(void* isr_context)
{
if (led_mode == 0) led_mode = 1;
else led_mode = 0;
}
int main()
{
//用mask寄存器使能中断
(unsigned long int)(PIO_1_BASE+sizeof(unsigned long int)*2) = 0xFFFFFFFF;
//注册中断
int ret = 0;
ret = alt_ic_isr_register(PIO_1_IRQ_INTERRUPT_CONTROLLER_ID,
PIO_1_IRQ,//中断序号
irq_cb,//回调函数
NULL,//在回调函数中传入的指针isr_context
0);
if (ret == 0) printf(“register irq successfullly”);
else printf(“register irq failed”);
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">switch</span> <span class="token punctuation">(</span>led_mode<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token keyword">case</span> <span class="token number">0</span><span class="token punctuation">:</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>LED <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> LED <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
<span class="token keyword">else</span> LED <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">case</span> <span class="token number">1</span><span class="token punctuation">:</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>LED <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span> LED <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
<span class="token keyword">else</span> LED <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">default</span><span class="token punctuation">:</span>
LED <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">usleep</span><span class="token punctuation">(</span><span class="token number">500000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
于是修改代码如下:
#include <stdio.h>
#include <unistd.h>//引入usleep()
#include <sys/alt_irq.h>//引入alt_ic_isr_register()
#include "../ff_bsp/system.h"
//使用BSP中的IP功能接口函数
#include "../ff_bsp/drivers/inc/altera_avalon_pio_regs.h"
unsigned char i = 0;
void irq_cb(void* isr_context)
{
i = i+1;//不能在回调函数中使用printf,会导致中断阻塞
}
int main()
{
//设置mask寄存器使能中断
IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_1_BASE, 1);
<span class="token comment">//注册中断</span>
<span class="token function">alt_ic_isr_register</span><span class="token punctuation">(</span>PIO_1_IRQ_INTERRUPT_CONTROLLER_ID<span class="token punctuation">,</span>
PIO_1_IRQ<span class="token punctuation">,</span><span class="token comment">//中断序号</span>
irq_cb<span class="token punctuation">,</span><span class="token comment">//回调函数</span>
<span class="token constant">NULL</span><span class="token punctuation">,</span><span class="token comment">//在回调函数中传入的指针isr_context</span>
<span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span>i <span class="token operator"><</span> <span class="token number">5</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token function">usleep</span><span class="token punctuation">(</span><span class="token number">1000000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
为了减少RAM,不能使用printf()
函数,因此在原工程上右键选择Debug As,Nios II Hardware
用断点查看中断的回调函数响应,确认功能正确。
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-095d4a0b23.css" rel="stylesheet">
</div>
</article>