FPGA学习专题-NIOS ii中的数据与Verilog进行交互(一)

在实际应用开发中,在verilog层面上开发完成简单的功能后,如果遇到比较复杂的逻辑,就会借助类似NIOS这样的片上CPU(其实本身就是一个IP核),在上面应用C语言进行编写操作。

那么问题就有了,在verilog中已经处理好的数据怎么传到NIOS中给C语言处理?或者从NIOS传到Verilog层面上去呢?

我将应用一个简单例子说明一下。两者在通信中借助AVALON总线,哪怕简单理解成一条线,把两者连接起来。如果NIOS想得到Verilog中的数据,就执行读总线(这个读过程是要带地址的),总线会根据给出的地址找到你想读的寄存器,并把值拿到NIOS中。相反,如果想把数据发送给Verilog,就执行写总线操作(写过程同样是要带地址的,就是目标地址),总线就会根据给出的地址把数据发送到给定的地址上,也就发到了对应的寄存器。

简单理解就是下图,两边的寄存器都挂在AVALON总线上,所有的地址都可以通过AVALON总线找到,这里的地址是由自己定的,后面告诉你怎么定。

在这里插入图片描述
本文实现现象:从NIOS II中发送变量值到Verilog中,控制灯的闪烁。
请注意:不是用的PIO!!!

1.首先在Verilog中建立闪灯的逻辑

module data_deal(clk,
rst,
data_in,
data_out
);input clk;
input rst;
input [7:0] data_in;//NIOS发送过来的控制值
output reg  data_out;//FPGA引脚输出,相当于是Verilog中的变量,这个值也可以被NIOS 读取always@(posedge clk or negedge rst)if(!rst)data_out <= 1'b1;else if(data_in == 8'd5) //当NIOS中发送过来的值为5,就亮灯,否则,灭灯。data_out <= 1'b0;else data_out <= 1'b1;endmodule

注释写在代码中,就不分析了。重点说下实现avalon总线的内容。

/*******************说明**********************/
/*这个module就是实现的通过avalon总线进行数据交互的功能*/
/*整个的模块可以说是一个通用标准了,使用中只需要修改成自己的变量即可*/
/*这里的as_writedata 和as_readdata 这两个数据寄存器,理解成NIOS可以操作的寄存器*/
/*往as_writedata里写数据就是发送给Verilog,读as_readdata就是从Verilog取数据*/
module av_data(clk,rst,led_out,as_address,			//地址,这个地址就是用来给Verilog中的变量和NIOS中的变量确定地址的as_chipselect,		//设备选择as_write,			//写请求,NIOS向Verilog中写数据as_writedata,		//要写数据 as_read,			//读请求,NIOS从Verilog中读数据as_readdata			//读取的数据);input clk;input rst;output led_out;input [2:0] as_address;//地址位宽根据你需要操作多少个变量决定input as_chipselect;input as_write;input [15:0] as_writedata;	//操作数的位宽,一般定义成16bitinput as_read;output reg [15:0] as_readdata;//写数据reg [7:0] data;always@(posedge clk or negedge rst)if(!rst)data <= 1'b0;else if((as_chipselect && as_write && (as_address == 0)))//注意这里的写条件,当地址为0的时候,就通过as_writedata这个写寄存器data <= as_writedata[7:0];											//把数据写到Verilog中的data这个变量//读数据always@(posedge clk or negedge rst)if(!rst)as_readdata <= 16'd0;else if(as_chipselect && as_read)begincase(as_address)0:as_readdata <= {8'd0, data};//同样还是根据地址,将Verilog中的变量值读到读寄存器中1:as_readdata <= {15'd0, led_out};default:as_readdata <= 16'd0;endcase	end//例化的控制亮灯的模块data_deal data_deal(.clk(clk),.rst(rst),.data_in(data),.data_out(led_out));endmodule

重点也都备注在代码中,还有不懂的可以一起讨论。两个关键的模块写好了,我们在工程中建立一个名为“ip”的文件夹,将上面的两个模块放到这个文件夹中,因为后续操作的时候,系统需要识别到ip文件夹才能操作。

在这里插入图片描述
在这里插入图片描述

上面都操作完成,再添加一个用于简单逻辑控制与引脚定义的顶层文件。

module nios_test(clk,rst,	work_led,led_out,	uart_tx,uart_rx);input clk;
input rst;
output reg work_led;
output led_out;input uart_rx;
output uart_tx;//500ms计时 25000000
reg [24:0] cnt;
always@(posedge clk or negedge rst)if(!rst)cnt <= 1'b0;else if(cnt == 25'd24999999)cnt <= 1'b0;elsecnt <= cnt +1'b1;always@(posedge clk or negedge rst)if(!rst)work_led <= 1'b1;else if(cnt == 25'd24999999)work_led <= ~work_led;endmodule

上面一段代码主要就是将工程中需要连接的引脚进行定义,并点亮了一颗LED用于显示程序运行情况。全部写完再进行一次全编译。

接下来开始在qsys中进行操作,打开qsys平台,如下:

在这里插入图片描述
打开之后在按下操作:

在这里插入图片描述

知道我么要干什么吗?建立一个IP核,没错,自己都会建IP核了,了不得了。

在这里插入图片描述
按部就班操作,在file一栏要注意一下:将刚刚建立的两个module文件添加进来,并要执行一次分析综合,你可能会遇到报错,应该不是可能是肯定会,不要怕,接着往下。

在这里插入图片描述

到这一步的时候,你或许不会像我的这么整齐,因为我是已经调好的。你是不是发现这里显示的值都很眼熟,是的,这里的引脚可都是我们自己定义的,既然都是自己定义的那不要太了解了。但是你要做的,是要修改成正确的类型,类型特别特别重要,因为自动识别(就是上面建立ip文件夹的作用)不会很准确的,你要按照图中将每个引脚的类型对应上才可以。

红框中标注了特别主要注意的地方,reset引脚要注意是reset还是reset_n。这个坑搞了我一晚上没睡着,惭愧。提示有Associated Reset的地方要选上reset,这里不会默认选上,不选不行。

关注一下led_out这个脚,这个是需要连接到设备的实际的引脚,需要定义成conduit类型,这样才可以导出并连接。

在这里插入图片描述
全部执行完成后FINISH。然后双击刚刚建立的IP核,添加到NIOS系统中

在这里插入图片描述
在这里插入图片描述
一定要注意,led_out要双击导出。然后执行重要的地方,将NIOS例化到第三个引脚定义的模块中去。不要忘了Generate!

在这里插入图片描述
在这里插入图片描述
所以上面的代码就成了下面的样子:

module nios_test(clk,rst,	work_led,led_out,	uart_tx,uart_rx);input clk;
input rst;
output reg work_led;
output led_out;input uart_rx;
output uart_tx;//500ms计时 25000000
reg [24:0] cnt;
always@(posedge clk or negedge rst)if(!rst)cnt <= 1'b0;else if(cnt == 25'd24999999)cnt <= 1'b0;elsecnt <= cnt +1'b1;always@(posedge clk or negedge rst)if(!rst)work_led <= 1'b1;else if(cnt == 25'd24999999)work_led <= ~work_led;//相比较上面的程序,就多了这一块mysystem u0 (.clk_clk       (clk),       //    clk.clk.reset_reset_n (rst), //  reset.reset_n.uart_0_rxd    (uart_rx),    // uart_0.rxd.uart_0_txd    (uart_tx),     //       .txd.av_sys_data_0_led_out(led_out));endmodule

然后整个工程全编译,ctrl+L即可。
然后打开eclipse
在这里插入图片描述
写代码


#include "sys/alt_stdio.h"
#include "io.h"				//这个头文件就是操作读写数据寄存器的需要
#include "system.h"
#include "unistd.h"int main()
{ alt_putstr("Hello from Nios II!\n");/* Event loop never exits. */while (1){IOWR_16DIRECT(AV_SYS_DATA_0_BASE,0,0x05);//通过这个写函数,向地址0上写一个5,对应Verilog中就会写到data中,此时data_out输出为0,灯亮printf("data is %x\r\n",IORD_16DIRECT(AV_SYS_DATA_0_BASE,2));//注意这里的读函数,地址写的是2,我们在Verilog中定义的读led_out的地址是1usleep(100000);											   //这个就是地址对应的关系,Verilog中是16bit,NIOS中是8bit,需要2个对应上1个IOWR_16DIRECT(AV_SYS_DATA_0_BASE,0,0x01);	//写一个1,灯会灭						printf("data is %x\r\n",IORD_16DIRECT(AV_SYS_DATA_0_BASE,2));//读出Verilog中灯的输出状态usleep(100000);}return 0;
}

代码说明也在注释中,看下就可,烧录后对应灯会闪烁,串口也会打印出led_out引脚对应的数值。
在这里插入图片描述
以上就实现了Verilog与NIOS双向通信的过程,附上测试工程,供大家参考。
链接:https://pan.baidu.com/s/1MBQ8Yd6Q1Kl2rjjC-Kl3Sw
提取码:wrcy


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部