本文原始文档及代码工程上传至https://download.csdn.net/download/botao_li/10804031
也许对新入门FPGA的朋友有所帮助
软件版本:Quartus Prime Standard Edition 18.0 Windows
启动Quartus Prime选择New Project Wizard
设置工程名及工作目录
根据提示使用默认配置直到器件选择页面,选择FPGA型号
在File菜单选择New
在弹出窗口中选择Block Diagram/Schematic File
完成后弹出模块图设计界面
板上时钟进入FPGA即使频率保持不变,最好也经过时钟模块以保证FPGA内的时钟质量
在Tools菜单选择IP Catalog
在IP Catalog页选择PLL Intel FPGA IP,双击
在弹出窗口输入模块名称及保存路径
在弹出的配置窗口中进行PLL功能配置
在弹出的确认窗口中选择将生成的IP加入当前工程
双击模块图设计界面的空白处,选择加入时钟模块
保存当前模块图设计
保存后的模块图设计界面
在Tools菜单选择Platform Designer
Platform Designer窗口的File菜单选择保存
在System Contents页双击clk_0
在弹出的Parameters页设置时钟频率与时钟模块输出频率一致
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软件编写)章节。
在IP Catalog页双击On-Chip Memory (RAM or ROM) Intel FPGA IP
在弹出的配置窗口作如下配置
为区分,在System Contents页右键Rename改名为onchip_memory2_rom
在IP Catalog页双击On-Chip Memory (RAM or ROM) Intel FPGA IP
在弹出的配置窗口作如下配置
为区分,在System Contents页右键Rename改名为onchip_memory2_ram
在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
用于自动校验的一个只读模块,用于确保软件程序与Nios的硬件配置相匹配
由Platform Designer的IP Catalog页查找并且添加System ID Peripheral Intel FPGA IP,保持默认参数
JTAG UART模块可以通过JTAG实现PC与Nios的串口通信
由Platform Designer的IP Catalog页查找并且添加JTAG UART Intel FPGA IP,保持默认参数
添加完上述各模块后的System Contents页如下
上图左侧中的空心圆圈表示信号可连接但是未连接,鼠标点击空心圆圈后变成实心圆圈,表示建立连接。
给各模块分配基地址用于基功能寄存器的操作,选择System菜单的Assign Base Addresses
对于已分配地址空间的模块,在添加新模块重新执行该指令时会被重新分配地址,在开发过程中为了防止出现地址变化,可以将已分配的地址空间锁定,锁定方法为鼠标点击Base地址旁的锁图标,由开锁状态转为闭锁状态
设置CPU的Vector
在Vectors页,设置Reset vector memory和Exception vector memory
Reset Vector指的是CPU复位后启动时指向的存储器类型和地址偏移量,一般为掉电不可擦除型,如外接的Flash
Exception Vector指的是CPU异常情况时指向的存储器类型和地址偏移量,存储器为掉电即擦除型,如SDRAM
——“NiosII的奇幻漂流”——不理解
在Platform Designer的Generate菜单选择Generate HDL
配置生成选项后点击Generate按钮
回到Quartus窗口,在Project菜单选择Add/Remove Files in Project
在弹出窗口添加nios_kernel.qsys文件
在模块图设计界面双击空白处,在弹出窗口选择添加nios_kernel模块
用鼠标拖动的方式连接pll的输出时钟与nios_kernel的输入时钟
在pll模块上右键选择Generate Pins for Symbol Ports
添加了pll上未连接的端口连接
由于板上按键保持高电平,按下为低电平,用于复位的话需要加入非门,于是添加非门模块
在Tasks页双击运行Analysis & Synthesis
在Assignments菜单打开Pin Planner
根据板卡原理图设置Location,其它部分按默认自动设置
关闭Pin Planner回到Quartus界面发现模块图形界面已将管脚分配更新至界面
在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文件
在Processing菜单选择Start Compilation完成编译
USB Blaster加载器连接PC,以及板卡的JTAG插口
板卡上电
在Tools菜单打开Programmer
在Hardware Setup选择USB-Blaster,Mode选择JTAG
在Processing菜单选择Auto Detect,给FPGA作Change File指向编译输出的sof文件
保存后点击Start按钮
当Progress显示100%,表示烧写成功
在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
#include //引入usleep()
int main()
{
while (1)
{
printf("Hello from Nios II!\n");
//参考nios handbook中的HAL API Reference,实现等待us
usleep(1000000);
}
return 0;
}
在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的编程操作,包括:
在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模块分配寄存器地址
从IP Catalog页打开PIO (Parallel I/O) Intel FPGA IP配置窗口,设置为1位输入电平中断
用与上述一致的方法连接模块端口,引出PIO的输入端口
注意中断序号可以手动修改,但是一般情况下按默认分配就可以了
在System菜单选择Assign Base Addresses,给PIO模块分配寄存器地址
参考[生成](#11. 生成)章节的说明生成Nios模块
在Quartus界面双击顶层模块test.bdf,打开模块图形界面
删除原有nios_kernel模块后,添加新的nios_kernel模块,并且如下图连接
参考[管脚约束](#13. 管脚约束)章节添加led和irq的管脚Location
在Processing菜单选择Start Compilation编译FPGA工程
参考前述,在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"
参考Embedded Peripherals IP User Guide中PIO Core的说明
修改main.c内代码如下:
#include
#include //引入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;
}
usleep(500000);
}
}
运行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"
在main.c中注册中断,并且添加切换模式的代码,由于RAM空间不足导致编译错误
#include
#include //引入usleep()
#include //引入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");
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;
}
usleep(500000);
}
}
于是修改代码如下:
#include
#include //引入usleep()
#include //引入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);
//注册中断
alt_ic_isr_register(PIO_1_IRQ_INTERRUPT_CONTROLLER_ID,
PIO_1_IRQ,//中断序号
irq_cb,//回调函数
NULL,//在回调函数中传入的指针isr_context
0);
while (i < 5)
{
usleep(1000000);
}
}
为了减少RAM,不能使用printf()
函数,因此在原工程上右键选择Debug As,Nios II Hardware
用断点查看中断的回调函数响应,确认功能正确。