赞
踩
本教程介绍设计自定义IP组件并添加到Platform Designer系统,设计基于Nios V的软件工程,控制DE2-115开发板上的8个七段数码管循环显示十六进制数0~F。
一、 系统功能实现
本实验的Nios V系统由Nios V/m处理器、On-Chip Memory内存、JTAG接口和自定义七段数码管IP组成。该系统可使用Platform Designer工具来实现,Platform Designer工具不仅包含像Nios V/m Processor、JTAG UART、PIO等组件,还可以借助Platform Designer自定义添加IP组件。
自定义SEG7_IF IP组件
DE2-115 CD-ROM/DE2_115_demonstrations/DE2_115_golden_sopc/ip/TERASIC_SEG7,这是本实验所用的IP组件并再稍作修改(后续会有说明)。
关于七段数码管的控制显示原理,还可以参考《基于FPGA的贪吃蛇游戏设计(二)——数码管驱动模块》。
SEG7_IF组件的顶层信号接口如下表:clk和reset_n为该组件的时钟和复位信号;s_address、s_write、s_writedata、s_read、s_readdata为该组件的Avalon-MM接口,通过该接口实现与Nios V处理器的数据读写;SEG7为该组件的Avalon-Conduit接口,该接口被导出到顶层设计,并与DE2-115开发板上的8个七段数码管相连接。
下图为该组件的模块示意图,每个七段数码管都有一个寄存器用来存放要显示的十六进制数0 ~ F。可以看出SEG7_IF模块主要由模块对外接口和寄存器存储电路组成。
SEG7_IF组件端口
模块端口主要分为Clock/Reset接口、Avalon-MM Slave接口和Avalon Conduit接口,以下是模块的端口信息,定义了一些参数如数码管的个数、Avalon-MM Slave读写地址的宽度、复位后所有七段数码管的状态、数码管是高电平还是低电平点亮,提高了可重用性。
- module SEG7_IF(
- //===== avalon MM s1 slave (read/write)
- // write
- s_clk,
- s_address,
- s_read,
- s_readdata,
- s_write,
- s_writedata,
- s_reset,
- //
- //===== avalon MM s1 to export (read)
- // read/write
- SEG7
- )
- ;
- /*****************************************************************************
- * Parameter Declarations *
- *****************************************************************************/
- parameter SEG7_NUM = 8;
- parameter ADDR_WIDTH = 3;
- parameter DEFAULT_ACTIVE = 1;
- parameter LOW_ACTIVE = 1;
- ......
- /*****************************************************************************
- * Port Declarations *
- *****************************************************************************/
- // s1
- input s_clk;
- input [(ADDR_WIDTH-1):0] s_address;
- input s_read;
- output [7:0] s_readdata;
- input s_write;
- input [7:0] s_writedata;
- input s_reset;
- //===== Interface to export
- // s1
- output [(SEG7_NUM*8-1):0] SEG7;
SEG7_IF组件寄存器读写
寄存器存储电路定义了SEG7_NUM个8位寄存器reg_file,每个寄存器存储一个数码管显示的字符,如reg_file[0]存储数码管HEX0上要显示的字符,reg_file[1]存储数码管HEX1上要显示的字符。复位时寄存器存储的数据由参数DEFAULT_ACTIVE决定,当DEFAULT_ACTIVE=1时,所有寄存器存储的数据为1'b1,即所有的数码管熄灭不显示;DEFAULT_ACTIVE=0时,所有的寄存器存储的数据为1'b0,即所有的数码管会显示字符“8”。
当进行写操作时,Avalon-MM Slave接口的写数据s_writedata将会存储到地址为s_address的寄存器中;当进行读操作时,会将地址为s_address的寄存器中的数据读取至s_readdata中。
- /*****************************************************************************
- * Internal Wire/Register *
- *****************************************************************************/
- reg [7:0] base_index;
- reg [7:0] write_data;
- reg [7:0] read_data;
- reg [(SEG7_NUM*8-1):0] reg_file;
- /*****************************************************************************
- * Sequence logic *
- *****************************************************************************/
- always @ (negedge s_clk)
- begin
- if (s_reset)
- begin
- integer i;
- for(i=0;i<SEG7_NUM*8;i=i+1)
- begin
- reg_file[i] = (DEFAULT_ACTIVE)?1'b1:1'b0; // trun on or off
- end
- end
- else if (s_write)
- begin
- integer j;
- write_data = s_writedata;
- base_index = s_address;
- base_index = base_index << 3;
- for(j=0;j<8;j=j+1)
- begin
- reg_file[base_index+j] = write_data[j];
- end
- end
- else if (s_read)
- begin
- integer k;
- base_index = s_address;
- base_index = base_index << 3;
- for(k=0;k<8;k=k+1)
- begin
- read_data[k] = reg_file[base_index+k];
- end
- end
- end
- /*****************************************************************************
- * Combinational logic *
- *****************************************************************************/
- assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file;
- assign s_readdata = read_data;
SEG7_IF组件8个寄存器都是8-bit,可以使用IORD/IOWR来读写这8个寄存器。例如若要使数码管HEX0显示字符“F”,则要向第0个寄存器中写入数值0x0F,使用以下语句,其中SEG7_IF_BASE为Seg7_IF组件的基地址:
IOWR(SEG7_IF_BASE, 0, 0x0F) //七段数码管HEX0显示字符“F”
若要使数码管HEX1显示字符“A”,则要向第1个寄存器中写入数值0x0A,使用以下语句,以此类推:
IOWR(SEG7_IF_BASE, 1, 0x0A) //七段数码管HEX1显示字符“A”
Nios V .c应用程序
要控制七段数码管,除了需要SEG7_IF组件的驱动程序,还需要完整的Nios V .c应用程序。在应用程序的开头,要包含必要的头文件。
- #include <stdio.h>
- #include "system.h"
- #include "io.h"
- #include "alt_types.h"
stdio.h文件提供了标准输入输出函数比如printf函数等,所以要包含这个头文件。IOWR函数定义在io.h文件内。alt_types.h定义了数据类型,下面是alt_types.h文件内容的片段,比如我们程序里面定义了HEX_display类型为alt_u16 ,实际上就是定义一个无符号字符类型的16位数据。
- #ifndef ALT_ASM_SRC
- typedef signed char alt_8;
- typedef unsigned char alt_u8;
- typedef signed short alt_16;
- typedef unsigned short alt_u16;
- typedef signed long alt_32;
- typedef unsigned long alt_u32;
- typedef long long alt_64;
- typedef unsigned long long alt_u64;
- #endif
system.h提供了关于Nios V系统硬件信息的描述,是硬件和软件之间的桥梁。
以下代码定义并驱动七段数码管显示十六进制数0~F,比如HEX_display[0]为十进制数64,转换为二进制数为1000000,七段数码管显示即为0,依此类推HEX_display[15]为十进制数14,表示数码管显示十六进制数F。
- /* 数码管控制数组HEX0~HEX7循环显示0~F,低电平点亮*/
- const alt_u64 HEX_display[] = {
- 64, //7'b1000000,数码管显示0
- 121, //7'b1111001,数码管显示1
- 36, //7'b0100100,数码管显示2
- 48, //7'b0110000,数码管显示3
- 25, //7'b0011001,数码管显示4
- 18, //7'b0010010,数码管显示5
- 2, //7'b0000010,数码管显示6
- 120, //7'b1111000,数码管显示7
- 0, //7'b0000000,数码管显示8
- 24, //7'b0011000,数码管显示9
- 8, //7'b0001000,数码管显示a
- 3, //7'b0000011,数码管显示b
- 70, //7'b1000110,数码管显示c
- 33, //7'b0100001,数码管显示d
- 6, //7'b0000110,数码管显示E
- 14 //7'b0001110,数码管显示F
- };
在main()函数中,先使用printf语句打印输出“Realize the display function of eight HEX from 0 to F”信息,然后定义了一些变量。
- int main(){
- printf("Realize the display function of eight HEX from 0 to F\n");
- alt_u16 i;
- alt_u32 delay;
然后是while()循环语句,对8个七段数码管进行写操作,将HEX_display[0]~HEX_display[15]这十六个值写入8个数码管,使数码管循环显示0-F。
- while(1){
- for(i = 0; i<16; i++){ //HEX共有16种状态
- IOWR(SEG7_IF_BASE, 0, HEX_display[i]); //对HEX0进行写操作
- IOWR(SEG7_IF_BASE, 1, HEX_display[i]); //对HEX1进行写操作
- IOWR(SEG7_IF_BASE, 2, HEX_display[i]); //对HEX2进行写操作
- IOWR(SEG7_IF_BASE, 3, HEX_display[i]); //对HEX3进行写操作
- IOWR(SEG7_IF_BASE, 4, HEX_display[i]); //对HEX4进行写操作
- IOWR(SEG7_IF_BASE, 5, HEX_display[i]); //对HEX5进行写操作
- IOWR(SEG7_IF_BASE, 6, HEX_display[i]); //对HEX6进行写操作
- IOWR(SEG7_IF_BASE, 7, HEX_display[i]); //对HEX7进行写操作
- delay = 0;
- while(delay < 2000000)
- delay++;
- }
- }
- return 0;
- }
二、创建Quartus工程
1. 参考《基于DE1-SOC的Nios V工程—My First Nios V》,使用Quartus Standard Edition v22.1创建Quartus硬件工程,FPGA器件选择DE2-115的Cyclone IV E EP4CE115F29C7,打开Quartus硬件工程。
2. 在Quartus工程路径下创建一个文件夹命名为ip,再将DE2-115 CD-ROM/DE2_115_demonstrations/DE2_115_golden_sopc/ip里的TERASIC_SEG7文件夹复制到刚才创建的ip文件夹里。
3. 打开SEG7_IF.v文件,输入以下代码替换。相对于源码,我们将assign SEG7 = (LOW_ACTIVE)?~reg_file:reg_file修改为assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file,这样保证后续的main.c程序里数码管驱动代码也以低电平点亮数码管。
- /*******************************************************************************
- 功能: 控制七段数码管的显示
- 参数:
- s_address: 七段数码管索引,如0指示第一个七段数码管,1指示第二个七段数码管
- s_writedata: 映射到七段数码的8位二进制数(0:点亮, 1:熄灭)
- 0
- ------
- | |
- 5| 6 |1
- ------
- | |
- 4| |2
- ------ . 7
- 3
- 映射矩阵:
- unsigned char szMap[] = {
- 64, 121, 36, 48, 25, 18, 2, 120,
- 0, 24, 8, 3, 70, 33, 6, 14
- }; //依次表示十进制数0,1,2,....9, a, b, c, d, E, F
- 参数:
- SEG7_NUM: 指定七段数码管的个数,默认值为8
- ADDR_WIDTH: log2(SEG7_NUM),默认值为3
- DEFAULT_ACTIVE:
- 复位后所有的七段数码管显示状态
- 1: 默认值,将所有七段数码管熄灭,即显示0
- 0: 将所有七段数码管显示8
- LOW_ACTIVE:
- 1: 共阳数码管,即低电平点亮数码管
- 0: 共阴数码管,即高电平点亮数码管
- ******************************************************************************/
- module SEG7_IF(
- //===== avalon MM s1 slave (read/write)
- // write
- s_clk,
- s_address,
- s_read,
- s_readdata,
- s_write,
- s_writedata,
- s_reset,
- //
- //===== avalon MM s1 to export (read)
- //read/write
- SEG7
- );
-
- /*****************************************************************************
- * Parameter Declarations *
- *****************************************************************************/
- parameter SEG7_NUM = 8;
- parameter ADDR_WIDTH = 3;
- parameter DEFAULT_ACTIVE = 1;
- parameter LOW_ACTIVE = 1;
- /*****************************************************************************
- * Internal Wire/Register *
- *****************************************************************************/
- reg [7:0] base_index;
- reg [7:0] write_data;
- reg [7:0] read_data;
- reg [(SEG7_NUM*8-1):0] reg_file;
- /*****************************************************************************
- * Port Declarations *
- *****************************************************************************/
- // s1
- input s_clk;
- input [(ADDR_WIDTH-1):0] s_address;
- input s_read;
- output [7:0] s_readdata;
- input s_write;
- input [7:0] s_writedata;
- input s_reset;
- //===== Interface to export
- // s1
- output [(SEG7_NUM*8-1):0] SEG7;
- /*****************************************************************************
- * Sequence logic *
- *****************************************************************************/
- always @ (negedge s_clk)
- begin
- if (s_reset)
- begin
- integer i;
- for(i=0;i<SEG7_NUM*8;i=i+1)
- begin
- reg_file[i] = (DEFAULT_ACTIVE)?1'b1:1'b0; // trun on or off
- end
- end
- else if (s_write)
- begin
- integer j;
- write_data = s_writedata;
- base_index = s_address;
- base_index = base_index << 3;
- for(j=0;j<8;j=j+1)
- begin
- reg_file[base_index+j] = write_data[j];
- end
- end
- else if (s_read)
- begin
- integer k;
- base_index = s_address;
- base_index = base_index << 3;
- for(k=0;k<8;k=k+1)
- begin
- read_data[k] = reg_file[base_index+k];
- end
- end
- end
- /*****************************************************************************
- * Combinational logic *
- *****************************************************************************/
- assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file;
- assign s_readdata = read_data;
- endmodule
三、创建Nios V系统
1. 参考《基于DE1-SOC的Nios V工程——My First Nios V》,添加Nios V/m Processor Intel FPGA IP、On-Chip Memory (RAM or ROM) Intel FPGA IP以及JTAG UART Intel FPGA IP。其中On-Chip Memory (RAM or ROM) Intel FPGA IP的Total Memory Size修改为204800,其余IP等参数不变。
2. 添加自定义SEG7_IF IP组件:在Terasic Technologies Inc./DE2-115里可以看到自定义创建的SEG7_IF IP组件,双击添加该IP,保持默认参数不变。
3. 依次将clk_0、intel_niosv_m_0、onchip_memory2_0、jtag_uart_0以及SEG7_IF_0重命名为clk、intel_niosv_m、onchip_memory2、jtag_uart以及SEG7_IF。
4. 按下图所示连接各IP,最后双击箭头处将SEG7_IF导出。
5. 修改Nios V/m IP的参数设置,勾选Enable Reset from Debug Module,Reset Agent选择onchip_memory2.s1。
6. 点击Platform Designer的菜单System—>Assign Base Addresses给每个模块分配地址空间。
7. Message栏仍然有错误提示,将intel_niosv_m的ndm_reset_in信号与系统reset信号连接起来。最后点击窗口右下角的Generate HDL按钮生成qsys文件。
8. 点击Quartus菜单Project—>Add/remove Files in Project,将niosv.qsys文件就可以将该文件添加进工程里面去。
9. 点击Quartus菜单File—>New—>Verilog HDL File—>OK,创建niosv_hex.v顶层文件。在该.v文件里面输入如下代码并保存。
- module niosv_hex(
- input CLOCK_50,
- output [6:0] HEX0,
- output [6:0] HEX1,
- output [6:0] HEX2,
- output [6:0] HEX3,
- output [6:0] HEX4,
- output [6:0] HEX5,
- output [6:0] HEX6,
- output [6:0] HEX7
- );
- 7-SEG //
- wire HEX0P;
- wire HEX1P;
- wire HEX2P;
- wire HEX3P;
- wire HEX4P;
- wire HEX5P;
- wire HEX6P;
- wire HEX7P;
- niosv u0 (
- .clk_clk(CLOCK_50), //clk.clk
- .seg7_if_conduit_end_export({
- HEX7P,HEX7,HEX6P,HEX6,
- HEX5P,HEX5,HEX4P,HEX4,
- HEX3P,HEX3,HEX2P,HEX2,
- HEX1P,HEX1,HEX0P,HEX0}), // hex_decoder_x8_conduit_end.new_signal
- .reset_reset_n(1'b1) //reset.reset_n
- );
- endmodule
10. 点击Analysis & Synthesis进行分析与综合。
11. 按DE2-115开发板的相关引脚进行引脚分配,最后再点击Start Compilation编译Quartus工程,最终生成.sof文件。
四、创建Niov软件工程
1. 参考《基于DE1-SOC的Nios V工程——My First Nios V》,在niosv_hex工程文件夹路径下新建software文件。
2. 运行niosv-bsp -c -t=hal --sopcinfo=niosv.sopcinfo software/hex_bsp/settings.bsp命令在software文件夹创建Nios V BSP。
3. 在software文件夹路径下再创建hex文件夹,并在hex文件夹下创建空白main.c文件。
4.运行niosv-app -a=software/hex -b=software/hex_bsp -s=software/hex/main.c命令在hex文件夹生成CMakeLists.txt文件。
5. 打开Quartus v22.1.2下的RiscFree IDE,workspace指向software文件夹路径。然后点击Launch启动软件。
6. 点击Create a project,在New Project窗口,选择C/C++下的C Project;在打开的C Project窗口,Project name命名为hex,Location指向hex所在文件夹,Project Type选择CMake driven下的Empty Project;接下来的Select Configurations窗口保持默认设置不变,点击Finish即可。
7. 在main.c文件里面输入以下代码并保存。
- #include <stdio.h>
- #include "system.h"
- #include "io.h"
- #include "alt_types.h"
- /* 数码管控制数组HEX0~HEX7循环显示0~F,低电平点亮*/
- const alt_u64 HEX_display[] = {
- 64, //7'b1000000,数码管显示0
- 121, //7'b1111001,数码管显示1
- 36, //7'b0100100,数码管显示2
- 48, //7'b0110000,数码管显示3
- 25, //7'b0011001,数码管显示4
- 18, //7'b0010010,数码管显示5
- 2, //7'b0000010,数码管显示6
- 120, //7'b1111000,数码管显示7
- 0, //7'b0000000,数码管显示8
- 24, //7'b0011000,数码管显示9
- 8, //7'b0001000,数码管显示a
- 3, //7'b0000011,数码管显示b
- 70, //7'b1000110,数码管显示c
- 33, //7'b0100001,数码管显示d
- 6, //7'b0000110,数码管显示E
- 14 //7'b0001110,数码管显示F
- };
- //
- //
- // function: main
- // description: perform cyclically lighting the eight HEXs from 0 to F
- // parameter: none
- // return: 0
- //
- //
- int main(){
- printf("Realize the display function of eight HEX from 0 to F\n"); //在Nios V console窗口打印信息
- alt_u16 i;
- alt_u32 delay;
- while(1){
- for(i = 0; i<16; i++){ //HEX共有16种状态
- IOWR(SEG7_IF_BASE, 0, HEX_display[i]); //对HEX0进行写操作
- IOWR(SEG7_IF_BASE, 1, HEX_display[i]); //对HEX1进行写操作
- IOWR(SEG7_IF_BASE, 2, HEX_display[i]); //对HEX2进行写操作
- IOWR(SEG7_IF_BASE, 3, HEX_display[i]); //对HEX3进行写操作
- IOWR(SEG7_IF_BASE, 4, HEX_display[i]); //对HEX4进行写操作
- IOWR(SEG7_IF_BASE, 5, HEX_display[i]); //对HEX5进行写操作
- IOWR(SEG7_IF_BASE, 6, HEX_display[i]); //对HEX6进行写操作
- IOWR(SEG7_IF_BASE, 7, HEX_display[i]); //对HEX7进行写操作
- delay = 0;
- while(delay < 2000000)
- delay++;
- }
- }
- return 0;
- }
8. 左键选中hex,点击Build Project编译main.c,编译完成可以看到生成hex.elf文件。
五、 配置FPGA与测试
1. 点击Quartus软件工具栏的Tools--> Programmer,烧录Quartus硬件工程的niosv_hex.sof文件。
2. 在Nios V Command Shell里运行juart-terminal。
3. 在RiscFree IDE窗口左键选中hex,选择菜单里的Run As-->3 Ashling RISC-V(auto-detect)Hardware Debugging,烧录.elf并运行程序。
4. 运行完成后即可在DE2-115开发板上看到8个七段数码管循环显示0~F;同时在Nios V Command Shell会打印输出:Realize the display function of eight HEX from 0 to F。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。