基于Quartus Prime的NiosII基础开发流程

基于Quartus Prime的NiosII基础开发流程

本文原始文档及代码工程上传至https://download.csdn.net/download/botao_li/10804031
也许对新入门FPGA的朋友有所帮助

软件版本:Quartus Prime Standard Edition 18.0 Windows

1. 建立工程

启动Quartus Prime选择New Project Wizard
基于Quartus Prime的NiosII基础开发流程_第1张图片
设置工程名及工作目录
基于Quartus Prime的NiosII基础开发流程_第2张图片
根据提示使用默认配置直到器件选择页面,选择FPGA型号
基于Quartus Prime的NiosII基础开发流程_第3张图片

2. 建立顶层模块

在File菜单选择New
基于Quartus Prime的NiosII基础开发流程_第4张图片
在弹出窗口中选择Block Diagram/Schematic File
基于Quartus Prime的NiosII基础开发流程_第5张图片
完成后弹出模块图设计界面
基于Quartus Prime的NiosII基础开发流程_第6张图片

3. 添加时钟模块

板上时钟进入FPGA即使频率保持不变,最好也经过时钟模块以保证FPGA内的时钟质量

在Tools菜单选择IP Catalog
基于Quartus Prime的NiosII基础开发流程_第7张图片
在IP Catalog页选择PLL Intel FPGA IP,双击
基于Quartus Prime的NiosII基础开发流程_第8张图片
在弹出窗口输入模块名称及保存路径
基于Quartus Prime的NiosII基础开发流程_第9张图片
在弹出的配置窗口中进行PLL功能配置
基于Quartus Prime的NiosII基础开发流程_第10张图片
在弹出的确认窗口中选择将生成的IP加入当前工程
基于Quartus Prime的NiosII基础开发流程_第11张图片
双击模块图设计界面的空白处,选择加入时钟模块
基于Quartus Prime的NiosII基础开发流程_第12张图片
保存当前模块图设计
基于Quartus Prime的NiosII基础开发流程_第13张图片
保存后的模块图设计界面
基于Quartus Prime的NiosII基础开发流程_第14张图片

4. 创建Platform Designer模块(原QSys)

在Tools菜单选择Platform Designer
基于Quartus Prime的NiosII基础开发流程_第15张图片
Platform Designer窗口的File菜单选择保存
基于Quartus Prime的NiosII基础开发流程_第16张图片
在System Contents页双击clk_0
基于Quartus Prime的NiosII基础开发流程_第17张图片
在弹出的Parameters页设置时钟频率与时钟模块输出频率一致
基于Quartus Prime的NiosII基础开发流程_第18张图片

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
基于Quartus Prime的NiosII基础开发流程_第19张图片
在弹出的配置窗口作如下配置
基于Quartus Prime的NiosII基础开发流程_第20张图片
为区分,在System Contents页右键Rename改名为onchip_memory2_rom

片上RAM

在IP Catalog页双击On-Chip Memory (RAM or ROM) Intel FPGA IP

在弹出的配置窗口作如下配置
基于Quartus Prime的NiosII基础开发流程_第21张图片
为区分,在System Contents页右键Rename改名为onchip_memory2_ram

6. 添加CPU模块

在Platform Designer窗口的IP Catalog页(与Quartus的IP Catalog页内容不同)双击Nios II Processor
基于Quartus Prime的NiosII基础开发流程_第22张图片
在弹出的配置窗口的Main页中选择Nios II/f
基于Quartus Prime的NiosII基础开发流程_第23张图片
在Caches and Memory Interface页,选择设置Data Cache的Size为None
基于Quartus Prime的NiosII基础开发流程_第24张图片
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,保持默认参数
基于Quartus Prime的NiosII基础开发流程_第25张图片

8. 添加JTAG UART模块

JTAG UART模块可以通过JTAG实现PC与Nios的串口通信

由Platform Designer的IP Catalog页查找并且添加JTAG UART Intel FPGA IP,保持默认参数
基于Quartus Prime的NiosII基础开发流程_第26张图片

9. 连接各模块信号

添加完上述各模块后的System Contents页如下
基于Quartus Prime的NiosII基础开发流程_第27张图片
上图左侧中的空心圆圈表示信号可连接但是未连接,鼠标点击空心圆圈后变成实心圆圈,表示建立连接。

简单起见,可以把所有的空心圆圈全部建立连接。
基于Quartus Prime的NiosII基础开发流程_第28张图片

10. 分配基地址

给各模块分配基地址用于基功能寄存器的操作,选择System菜单的Assign Base Addresses
基于Quartus Prime的NiosII基础开发流程_第29张图片
对于已分配地址空间的模块,在添加新模块重新执行该指令时会被重新分配地址,在开发过程中为了防止出现地址变化,可以将已分配的地址空间锁定,锁定方法为鼠标点击Base地址旁的锁图标,由开锁状态转为闭锁状态
基于Quartus Prime的NiosII基础开发流程_第30张图片

11. 生成

设置CPU的Vector

在Vectors页,设置Reset vector memory和Exception vector memory
基于Quartus Prime的NiosII基础开发流程_第31张图片

Reset Vector指的是CPU复位后启动时指向的存储器类型和地址偏移量,一般为掉电不可擦除型,如外接的Flash

Exception Vector指的是CPU异常情况时指向的存储器类型和地址偏移量,存储器为掉电即擦除型,如SDRAM

——“NiosII的奇幻漂流”——不理解

在Platform Designer的Generate菜单选择Generate HDL
基于Quartus Prime的NiosII基础开发流程_第32张图片
配置生成选项后点击Generate按钮
基于Quartus Prime的NiosII基础开发流程_第33张图片

12. 向FPGA顶层添加Nios模块

回到Quartus窗口,在Project菜单选择Add/Remove Files in Project
基于Quartus Prime的NiosII基础开发流程_第34张图片
在弹出窗口添加nios_kernel.qsys文件
在这里插入图片描述
在模块图设计界面双击空白处,在弹出窗口选择添加nios_kernel模块
基于Quartus Prime的NiosII基础开发流程_第35张图片
用鼠标拖动的方式连接pll的输出时钟与nios_kernel的输入时钟
基于Quartus Prime的NiosII基础开发流程_第36张图片
在pll模块上右键选择Generate Pins for Symbol Ports
基于Quartus Prime的NiosII基础开发流程_第37张图片
添加了pll上未连接的端口连接
基于Quartus Prime的NiosII基础开发流程_第38张图片
由于板上按键保持高电平,按下为低电平,用于复位的话需要加入非门,于是添加非门模块
基于Quartus Prime的NiosII基础开发流程_第39张图片

13. 管脚约束

在Tasks页双击运行Analysis & Synthesis
基于Quartus Prime的NiosII基础开发流程_第40张图片
在Assignments菜单打开Pin Planner
基于Quartus Prime的NiosII基础开发流程_第41张图片
根据板卡原理图设置Location,其它部分按默认自动设置
基于Quartus Prime的NiosII基础开发流程_第42张图片
关闭Pin Planner回到Quartus界面发现模块图形界面已将管脚分配更新至界面
基于Quartus Prime的NiosII基础开发流程_第43张图片

14. 时钟约束

在Tasks页双击运行Fitter
基于Quartus Prime的NiosII基础开发流程_第44张图片
正确完成后,在Tools菜单打开Timing Analyzer
基于Quartus Prime的NiosII基础开发流程_第45张图片
在Timing Analyzer界面的Tasks页双击运行Create Timing Netlist
基于Quartus Prime的NiosII基础开发流程_第46张图片
完成后在Contraints菜单选择Create Clock
基于Quartus Prime的NiosII基础开发流程_第47张图片
在弹出窗口中完成配置并且点击Run按钮
基于Quartus Prime的NiosII基础开发流程_第48张图片
在Tasks页首先双击Update Timing Netlist,完成更新后双击Write SDC File,写入SDC文件
基于Quartus Prime的NiosII基础开发流程_第49张图片
关闭Timing Analyzer后,回到Quartus界面,在Assignment菜单打开Settings,进入Timing Analyzer页,添加刚才在Timing Analyzer生成的SDC文件
基于Quartus Prime的NiosII基础开发流程_第50张图片

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按钮
基于Quartus Prime的NiosII基础开发流程_第51张图片
当Progress显示100%,表示烧写成功
在这里插入图片描述

17. Nios软件编写

在Quartus的Tools菜单选择Nios II Software Build Tools for Eclipse
基于Quartus Prime的NiosII基础开发流程_第52张图片
首先设置Workspace路径
基于Quartus Prime的NiosII基础开发流程_第53张图片
在打开的Eclipse界面的File菜单选择新建Nios II Application and BSP from Template
基于Quartus Prime的NiosII基础开发流程_第54张图片
在向导中首先设置sopc info文件及工程名,模板一般就选最简单的Hello World
基于Quartus Prime的NiosII基础开发流程_第55张图片
向导下一页是BSP工程配置,在BSP工程中包含了访问Nios硬件的函数包,按默认配置,点击Finish按钮
基于Quartus Prime的NiosII基础开发流程_第56张图片
每个Nios软件都需要一个Nios工程(称为原工程)和一个对应的BSP工程

建立工程后Project Explorer页出现2个工程:原工程与BSP工程
基于Quartus Prime的NiosII基础开发流程_第57张图片
双击打开
基于Quartus Prime的NiosII基础开发流程_第58张图片
原始代码修改成以下代码

#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
基于Quartus Prime的NiosII基础开发流程_第59张图片
在Nios II Console页弹出"Hello from Nios II!",来自于Nios内的JTAG Uart模块
在这里插入图片描述


至此已完成Nios基本功能模块的添加流程。

以下内容通过添加的功能说明Nios的编程操作,包括:

  • LED灯闪烁
  • 板卡按键作为中断改变LED的闪烁模式

A. 添加PIO模块用于LED灯控制

在Quartus内双击nios_kernel.qsys文件打开Platform Designer
基于Quartus Prime的NiosII基础开发流程_第60张图片
在Platform Designer的IP Catalog中选择并双击PIO (Parallel I/O) Intel FPGA IP
基于Quartus Prime的NiosII基础开发流程_第61张图片
在弹出的配置窗口中设置功能
基于Quartus Prime的NiosII基础开发流程_第62张图片
在System Contents页连接各端口并引出PIO的输出端口
基于Quartus Prime的NiosII基础开发流程_第63张图片
在System菜单选择Assign Base Addresses,给PIO模块分配寄存器地址
基于Quartus Prime的NiosII基础开发流程_第64张图片

B. 添加PIO模块用于接收按键事件并产生中断

从IP Catalog页打开PIO (Parallel I/O) Intel FPGA IP配置窗口,设置为1位输入电平中断
基于Quartus Prime的NiosII基础开发流程_第65张图片
用与上述一致的方法连接模块端口,引出PIO的输入端口

注意中断序号可以手动修改,但是一般情况下按默认分配就可以了
基于Quartus Prime的NiosII基础开发流程_第66张图片
在System菜单选择Assign Base Addresses,给PIO模块分配寄存器地址

参考[生成](#11. 生成)章节的说明生成Nios模块

C. 编译FPGA

在Quartus界面双击顶层模块test.bdf,打开模块图形界面
基于Quartus Prime的NiosII基础开发流程_第67张图片
删除原有nios_kernel模块后,添加新的nios_kernel模块,并且如下图连接
基于Quartus Prime的NiosII基础开发流程_第68张图片
参考[管脚约束](#13. 管脚约束)章节添加led和irq的管脚Location
基于Quartus Prime的NiosII基础开发流程_第69张图片
在Processing菜单选择Start Compilation编译FPGA工程

D. Nios软件编写

参考前述,在Eclipse中建立工程cc和cc_bsp,在Project Explorer中右键关闭之前建立的tt和tt_bsp工程
基于Quartus Prime的NiosII基础开发流程_第70张图片
如果需要继续使用原有工程,则需要在bsp工程右键菜单中的Nios II选项,选择Generate BSP
基于Quartus Prime的NiosII基础开发流程_第71张图片
双击打开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
基于Quartus Prime的NiosII基础开发流程_第72张图片
用断点查看中断的回调函数响应,确认功能正确。

你可能感兴趣的:(FPGA)