Altera FPGA NIOS-II之Hello World

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tq384998430/article/details/84953257

1、什么是NIOS II?

NIOS II就是一款CPU,和51、ARM、MIPS、X86的概念是一样的。但是与其他处理器架构相比NIOS II最大的特点是运行在(Intel Altera)FPGA上的软核处理器,说白了就是使用Verilog HDL或者VHDL语言在FPGA内部实现了一个处理器,这是一个庞大的系统,相当于在ARM处理器上编写一个操作系统,所以不是所有人都可以创建一个自己的CPU系统,而且就算自己编写了一个CPU系统,也没有配套的编译器和开发环境的支持,只能使用二进制指令进行编程,这是何等的痛苦。当然了Altera公司早就考虑到这点了,NIOS软核应运而生,NIOS II是在NIOS的进化版本,现在好像都是使用NIOS II系统了。同时Altera公司为NIOS II软核提供了完整的工具链包括软核的定制、丰富的外设IP库、NIOS II软件开发环境、编译器等。

FPGA系统上使用NIOS II处理器之后相当于传统的FPGA+MCU协作,各自发挥各自的优势,FPGA能够实现高效的数据处理,MCU能够处理繁杂的任务。

 

2、定制NIOS II系统

2.1 前期准备

手头有现成的一块Cyclone II开发板,拿这个板子进行试验。打开Quartus II软件,创建一个工程,顶层模块名叫做HelloWorld,选择好了器件之后可以开始定制NIOS II系统了,这里说的NIOS II系统指的不仅仅是一个NIOS II CPU软核,它包含了CPU、时钟、存储、总线、外设等部分。在Quartus软件的工具栏上打开Qsys工具,如下图:

进入到Qsys界面:

这就是NIOS II系统的定制软件,在右侧的System Contents栏中可以看到已经默认存在一个clk_0模块了,可能就是为了告诉我时钟是必不可少的。左侧是Altera提供的丰富的IP核,种类非常的多,可以构建一个资源丰富的单片机系统。

HelloWorld工程需要时钟、CPU、ROM、RAM、JTAG_UART、SYSID、PIO(后面会有为什么需要这个PIO外设)这些模块。我们可以挨个在左侧的Library中搜索,然后双击之后进行添加和配置。

 

2.2 添加配置模块

2.2.1 配置系统时钟

双击clk_0模块进行配置:

开发板上的晶振是50MHz的,不使用PLL的情况下输入给系统的时钟就是外部晶振的时钟频率。

2.2.2 定制NIOS II

添加一个NIOS II系统,

NIOS II一共三款类型,我选择了NIOS II/e,这是最小体积但是性能最差的一款处理器,因为我的开发板是Cyclone II的EC2C5T144C8,内部资源比较有限,如果选择NIOS II/f的话会导致内部资源不够用。下面还有Reset Vector和Exception Vector没有配置,因为现在还配置不了,这些向量是跟程序运行地址相关的,但是还没有添加系统存储器。

2.2.3 添加ROM存储器

搜索on chip memery,然后添加配置:

存储器类型选择ROM,Data Width选择32,Total memery size选择2048,不能太大,器件空间有限。

2.2.4 添加RAM存储器

步骤和上面的ROM定制一样,存储器类型选择RAM,Data Width选择32,Total memery size选择6144,这个值是恰好的值,小了导致NIOS II应用程序无法运行,大了导致RAM使用率超出FPGA内部总RAM,会在编译的Place&Route过程中出错。

2.2.5 添加JTAG_UART调试器

这个模块主要是为了下载程序、单步调试和数据打印使用的,使用默认配置就行了。

2.2.6 添加System ID peripheral

添加这个的目的我也没仔细研究,好像是Eclipse在连接CPU的时候会进行ID检测。直接添加即可,32 bit System ID就写0x00000000即可。

2.2.7 添加PIO

PIO就是Parallel I/O,相当于51单片机的P0、STM32的GPIOA。PIO模块的具体配置如下图:

2.3 线路连接

添加完所有模块之后还需要进行线路连接,这里有一些准则:

1、clk时钟需要连接到所有模块,因为所有模块都是时序器件,都需要时钟驱动。

2、clk_reset也连接到所有模块,因为如果时钟模块被复位了,其他连接了clk时钟的器件都要复位。注意clk模块的clk_in_reset是外部输入的复位信号,clk_reset信号是clk模块输入给其他器件的复位输出信号。

3、jtag_uart模块的复位输出连接到所有模块,因为调试模块有权利进行系统复位。

4、NIOS II软核的data_master数据总线连接到所有器件,因为所有器件只有被分配到CPU的总线中去之后才能被CPU所调用(器件需要能够被寻址,并能够读取或者写入数据)。

5、NIOS II软核的instruction_master指令总线连接到存储器中去,包括onchip_rom和onchip_ram,也可以是SDRAM控制器等。

根据上面的准则进行连线,最后的连线图如下:

连线完成之后需要设置NIOS II IP核的Reset Vector和Exception Vector,双击nios2_qsys进行配置:

表示复位之后程序从0x00005000地址处的ROM中开始运行。产生中断/异常之后从0x00002020地址处的RAM中运行,这就是中断向量表。

下面开始基地址分配。我们知道NIOS II是32位的内核,寻址空间是4GB,我们定义了那么多器件,还有存储器,存储器的空间各不相同,每个器件挂载在什么地址上面呢?我们可以自己计算然后配置地址,但是Qsys提供了分配工具,点击菜单栏中的【System】→【Assign Base Addresses】,Qsys工具会自动为各个IP核分配基地址。基地址分配完成后,可以看到0Error和0 Warning的提示,如图所示:.

到这里已经配置好一个NIOS II系统了,最后一步就是生成NIOS II系统了。点击右侧菜单栏中的【Generate】栏,进入生成Qsys系统的设置界面:

完成之后如果没有错误就会生成一个icpu.qip模块,在icpu的synthesis文件夹中,这个模块就是我们定制的IP核,在顶层模块中可以添加这个模块。记得要保存这个Qsys系统,下次要修改icpu的时候直接打开修改然后generate即可。

2.4 设置顶层模块

在Qsys界面里面有一个HDL example:

直接复制这个程序到顶层模块中调用即可:

这里我加了一个LED闪烁模块,用来查看系统是不是在运行。然后Pin planner、Programmer......这里不再赘述,常规操作。烧写到FPGA开发板上之后led按照2.5Hz的频率进行闪烁,说明系统已经在运行。

 

3、软件设计

现在已经有一个属于我们自己的CPU系统在FPGA中待着了,可不能要它就这么干耗着,要做点什么不是么?和STM32使用KEIL MDK集成开发环境一样,NIOS II也是使用集成开发环境进行开发的,但是遗憾的是这个开发环境是基于Eclips的,着实是不怎么好用,希望Altera好好优化一下。打开Quartus软件在菜单栏的Tools栏下选择Nios II Software Build Tools for Eclips,打开之后选择一个工作空间,然后File--New--Nios II Application and BSP from Template,使用模板为例创建一个工程:

上面Target hardware information中的SOPC Information File name选择刚才创建NIOS II系统是generate出来的文件,这个文件就是你创建的系统的配置文件。下面的Templates选择Hello World Small,这个程序的占用空间比Hello World工程要小很多。创建完成之后会生成两个项目:

HelloWorld_bsp是开发环境根据SOPC Information File生成的,就是根据我设计的CPU生成的独有的bsp库,开发应用程序的时候可以直接使用。

打开工程HelloWorld的hello_world_small.c文件主函数中只有一个打印字符串的功能。右击两个工程选择Build Project进行编译,如果没有问题就可以将程序下载到系统是实验,右击工程--Run as--Nios II Hardware,首次下载会弹出下面的Run Configuration窗口进行配置。

配置完之后就可以直接在Eclips的工具栏上直接进行运行了:

左边的是Debug,右边的是Run,电机向下箭头选择Debug/Run类型。

遗憾的是下载进去之后没有反应:

Nios II Console窗口中没有任何数据。试了很多次都没有成功。但是偶然我实验了一次Debug:

点击红框中的Resume之后开始运行程序,发现有数据打印出来了,然后将程序修改成循环打印数据,再进行调试:

的确有数据循环打印,但是停止Debug之后又没有数据打印了,难道停止Debug之后CPU停止运行了么?还是说jtag_uart传输有问题呢?这时候我就想使用PIO来控制LED灯来查看程序是否在运行,程序如下:

#include "sys/alt_stdio.h"
#include "altera_avalon_pio_regs.h"

int main()
{ 
	while(1)
	{
		IOWR_ALTERA_AVALON_PIO_DATA(0x6000,0xFFFF);
		alt_putstr("Hello world!\n");
		usleep(100000);
		IOWR_ALTERA_AVALON_PIO_DATA(0x6000,0x0000);
		alt_putstr("Hello world!\n");
		usleep(100000);
	}

  /* Event loop never exits. */
  while (1);

  return 0;
}

进入Debug模式之后程序可以运行,LED灯闪烁,有数据打印,但是数据打印很顿挫,按道理打印频率是10Hz,不应该顿挫,然后停止Debug,发现LED不闪烁,数据不打印,按下复位按键之后发现LED闪烁了3次,然后就停了,说明程序其实还是运行了,但是到后来卡住了似的。我猜测是卡在了jtag_uart打印的地方,所以把alt_putstr函数都注释了,再次实验发现退出Debug并复位之后LED可以正常闪烁,并且直接下载程序之后LED也能闪烁。那很有可能就是jtag_uart打印出现了问题,jtag_uart模块的打印是使用jtag的几根线传输打印数据的,和真正的UART是不一样的。倒是也可以解决这个问题,直接在我的NIOS II系统中添加一个UART IP核即可。但是这个问题还是要解决的,有明白人希望可以解答我的疑惑。

 

 

本文章参考正点原子的新起点FPGA开发指南和新起点Nios II开发指南,非常感谢原子提供的非常丰富的学习资料!

猜你喜欢

转载自blog.csdn.net/tq384998430/article/details/84953257
今日推荐