4.Avalon-ST总线
相对于Avalon-MM总线基于地址映射的访问方式,Avalon-ST总线更适合于高带宽、低时延的单向数据流传输。举个实例来说,如图6-12所示,对于一个简单的图像采集显示系统,在衔接一些高数据吞吐量的接口上就可以让Avalon-ST总线派上用场。假设这个实例系统中NIOS II处理器负责将前端采集的图像进行解码或是其他处理,然后再送给显示终端。那么在图像采集的前端就会产生大量的数据吞吐量,而在图像显示刷新的后端也会有大量的数据搬运工作,那么这些任务交给Avalon-ST并且配合DMA来搞定最合适不过了。
我们同样可以用一个最简单的Avalon-ST接口来领会其工作原理,如图6-13所示。在这个Avalon-ST的源端(source)和宿端(sink)之间只用了2个控制信号valid和ready就可以轻松完成单向数据流data的传输。其实这样简单的传输控制方式我们在逻辑设计中也是非常频繁用到的,但是若深入研究Avalon-ST总线的一整套传输机制,那可还真是有不少学问。
通信的机理也是比较简单的,ready信号用于指示数据宿(DATA Sink)端是否准备就绪,是否可以接受数据源(DATA Source)端传输过来的数据信号data。而数据源端在需要发送数据的时候也会先检查ready信号是否处于有效状态,若是有效,则拉高valid使之有效,同时将需要传输的数据赋给数据信号data。数据宿端根据valid信号的有效与否决定是否接收当前的数据。当然,这里省略了时钟信号,每次数据传送通常都是按照时钟信号一个节拍一个节拍的工作。
如图6-14所示,对于这个简单的Avalon-ST总线传输而言,在宿端拉高ready信号以后源端发送过来的数据(valid有效时)才会被接收。图中的clk 0时刻valid信号有效,但是ready信号无效,所以此时传输的数据无效(Invalid);clk 1时刻valid信号和ready同时有效,那么数据DATA1就能被宿端接收;clk 2时刻valid无效则不锁存任何数据;clk 3时刻DATA2被锁存;clk 4时刻由于ready信号无效则传输数据无效;后续依次类推。
简单的接口模型和示意图让我们看清了Avalon-ST总线的本来面目,在实践中应用Avalon-ST总线也就不再困难。
5.创建自定义组件
①打开Qsys界面后,如图6-15所示,双击Library面板中的Project→New Component。
②此时弹出如图6-16所示的组件编辑界面。在“Component Type”页面中,设定“Name”和“Display Name”均为“flash_controller”,“Version”为“1.0”,其他选项可以根据需要填写。
③在Files选项卡中,依次执行以下操作。
·首先在“Synthesis Files”下面点击“+”按钮,将我们已经编辑好的3个存放在工程目录下的Verilog源文件“flash_avalon.v”、“flash_ctrl.v”和“flash_top.v”添加进来。
·在“Top-level Module”后面的下拉框中选择“flash_top”模块,即flash_top模块作为这3个加载源文件的顶层模块。
·如图6-17所示,点击“Analyze Synthesis Files”按钮对前面添加的3个模块进行综合。
④如图6-18所示,在Parameters选项卡中,若设计源码中有一些可配置的参数,则会自动出现在这里。供设计者选择是否作为用户最终添加组件时的可更改配置参数,而我们这个组件没有这样的参数。
⑤接下来在Signals选项卡中对该模块的所有接口进行映射,具体配置如图6-19所示。这个映射接口可以分为三类,系统信号主要是时钟和复位信号,即clock_sink和reset_sink接口;与Avalon总线直接连接的部分是avalon_slave_0接口;需要引出到Qsys系统外部的接口,包括任何需要与FPGA器件外部芯片连接的信号以及作为中间传输信号与FPGA内部其他模块连接的信号,归为conduit_end接口。信号类型(Signal Type)也需要根据实际情况进行匹配设置,conduit_end接口部分都是默认选择export,即连接到外部的意思。其他信号都可以在下拉列表框中选择特定的信号类型。信号位宽(Width)和方向(Direction)都是系统根据信号名(综合结果)自动匹配,无需手动设置。
图6-19 信号接口配置页面
⑥在Interfaces选项卡中,需要分别设置avalon_slave_0、clock_sink、conduit_end和reset_sink的接口时序参数,如图6-20至图6-23所示。
avalon_slave_0配置项中,Timing下的Setup、Read wait、Write wait和Hold等参数可以更改。大家可以尝试更改不同的时序参数值,并观察读时序波形(Read Waveforms)和写时序波形(Write Waveforms)的变化情况。当然了,如果最终按照更改的这些参数值进行组件集成,那么设计者必须保证自己设计的Avalon从接口能够满足这个时序波形的要求。总之这里有一个小原则,就是要不同应用区别设置,无论如何,不同参数下的不同波形只要能够符合设计要求即可。
⑦最后单击Finish按钮完成新组件的创建。如图6-24所示,我们在Library面板中已经可以看到这个名为“flash_controller”的新组件了。
⑧双击“flash_controller”组件,如图6-25所示,无需任何配置,直接单击Finish按钮便可以将它添加到我们的Qsys系统中。
6.移植自定义组件
对于自定义的Qsys组件,若是不希望每次都重复进行新组建的配置编辑,是否有快速简单地移植自定义组建的办法呢?当然有,下面我们就介绍一下。
我们先回到工程实例4的工程文件夹下,找到Verilog源文件“flash_avalon.v”、“flash_ctrl.v”和“flash_top.v”,以及名为“flash_controller_hm.tcl”的文件,如图6-27所示,复制它们,然后粘贴到工程实例3的工程文件夹下。
关闭之前打开的工程实例3的Qsys界面,重新开启,此时如图6-28所示,自定义组件flash_controller已经出现了。
话说这个自定义组件的移植非常简单,将自定义组件的源代码文件和Qsys配置生成的“*_hw.tcl”文件一起做一次“复制”、“粘贴”操作就好了。
6.3 Verilog代码解析
本实例有3个大模块(vip_qsys全部作为一个模块而言),3个层级,其层次结构如图6-29所示。
·vip.v是顶层模块,其下例化了两个子模块,即sys_ctrl.v模块和vip_qsys.v模块。该模块仅仅用于子模块间的接口连接,以及和FPGA外部的接口定义,该模块中未作任何的逻辑处理。
·sys_ctrl.v二级子模块中例化了PLL模块,并且对输入PLL的复位信号以及PLL锁定后的复位信号进行“异步复位,同步释放”的处理,确保系统的复位信号稳定可靠。
·vip_qsys.v模块则是Qsys系统的例化,该模块例化了一个NIOS II处理器,作为Avalon-MM总线的主机;Avalon-MM总线上可访问的从机有片内RAM、System ID、JTAG UART、PIO、Timer和一
个自定义组件NAND Flash控制器等外设。由于这个NAND Flash控制器模块是我们自己用逻辑设计的一个组件,接下来我们会详细讲解该模块的设计。
1.vip.v模块代码解析 vip.v模块的代码如下。 //VIP工程顶层模块 module vip( ext_clk,ext_rst_n, led, flash_r_bn,flash_cle,flash_ale,flash_ce_n, flash_re_n,flash_we_n,flash_wp_n,flash_db, ); //外部输入时钟和复位接口 input ext_clk; //外部25MHz输入时钟 input ext_rst_n; //外部低电平复位信号输入 //LED指示灯接口 output led; //用于测试的LED指示灯 //FPGA与Flash接口 input flash_r_bn; //NAND Flash准备好(1)或忙(0)标志位 output flash_cle; //NAND Flash命令锁存使能信号 output flash_ale; //NAND Flash地址锁存使能信号 output flash_ce_n; //NAND Flash片选信号,低电平有效 output flash_re_n; //NAND Flash读使能信号,低电平有效 output flash_we_n; //NAND Flash写使能信号,低电平有效 output flash_wp_n; //NAND Flash写保护信号,低电平有效 inout[7:0] flash_db; //NAND Flash数据/地址/命令复用总线//////////////////////////////////////////////////// //系统内部时钟和复位产生模块例化 //PLL输出复位和时钟,用于FPGA内部系统 wire sys_rst_n; //系统复位信号,低电平有效 wire clk_25m; //PLL输出25MHz wire clk_33m; //PLL输出33MHz wire clk_50m; //PLL输出50MHz wire clk_65m; //PLL输出65MHz wire clk_100m; //PLL输出100MHz sys_ctrl uut_sys_ctrl( .ext_clk(ext_clk), .ext_rst_n(ext_rst_n), .sys_rst_n(sys_rst_n), .clk_25m(clk_25m), .clk_33m(clk_33m), .clk_50m(clk_50m), .clk_65m(clk_65m), .clk_100m(clk_100m) ); //////////////////////////////////////////////////// //Qsys系统例化 vip_qsys vip_qsys_inst( .clk_clk (clk_50m), .reset_reset_n (sys_rst_n), .pio_led_external_connection_export (led), .flash_controller_conduit_end_flash_r_bn(flash_r_bn), .flash_controller_conduit_end_flash_cle(flash_cle), .flash_controller_conduit_end_flash_ale(flash_ale), .flash_controller_conduit_end_flash_ce_n(flash_ce_n), .flash_controller_conduit_end_flash_re_n(flash_re_n), .flash_controller_conduit_end_flash_we_n(flash_we_n), .flash_controller_conduit_end_flash_wp_n(flash_wp_n), .flash_controller_conduit_end_flash_db(flash_db), .flash_controller_conduit_end_flashph_addr (), .flash_controller_conduit_end_flashph_data (), .flash_controller_conduit_end_flashph_rdy () ); endmodule ①以下接口为FPGA器件与NAND Flash芯片之间的接口。 //FPGA与Flash接口 input flash_r_bn; //NAND Flash准备好(1)或忙(0)标志位 output flash_cle; //NAND Flash命令锁存使能信号 output flash_ale; //NAND Flash地址锁存使能信号 output flash_ce_n; //NAND Flash片选信号,低电平有效 output flash_re_n; //NAND Flash读使能信号,低电平有效 output flash_we_n; //NAND Flash写使能信号,低电平有效 output flash_wp_n; //NAND Flash写保护信号,低电平有效 inout[7:0] flash_db; //NAND Flash数据/地址/命令复用总线 如图6-30所示,在NAND Flash芯片的原理图引脚定义上,我们可以一一对应地找到上面这些信号接口。②Vip_qsys模块的例化如下所示。这里“flash_controller_*”为前缀的信号均为我们自定义的一个挂在Qsys系统的Avalon-MM总线上的一个组件(我们称之为flash_controller)。这个flash_controller组件用于实现NIOS II软件程序对NAND Flash的读写操作。
vip_qsys vip_qsys_inst(
.clk_clk (clk_50m), .reset_reset_n (sys_rst_n), .pio_led_external_connection_export (led), .flash_controller_conduit_end_flash_r_bn(flash_r_bn), .flash_controller_conduit_end_flash_cle(flash_cle), .flash_controller_conduit_end_flash_ale(flash_ale), .flash_controller_conduit_end_flash_ce_n(flash_ce_n), .flash_controller_conduit_end_flash_re_n(flash_re_n), .flash_controller_conduit_end_flash_we_n(flash_we_n), .flash_controller_conduit_end_flash_wp_n(flash_wp_n), .flash_controller_conduit_end_flash_db(flash_db), .flash_controller_conduit_end_flashph_addr (), .flash_controller_conduit_end_flashph_data (), .flash_controller_conduit_end_flashph_rdy ()
);
2.sys_ctrl.v模块代码解析
略。
3.flash_top.v模块代码解析
flash_top.v模块、flash_avalon.v模块和flash_ctrl.v模块是对NAND Flash进行读写操作以及和FPGA内部数据交互的一个Avalon-MM从机组件。这三个模块相互之间,以及它们和整个工程的其他模块之间的关系大体如图6-31所示。
flash_top.v模块的代码请参看vip_ex4工程源码。
①flash_top.v模块是flash_controller组件的顶层模块,该模块代码不实行任何的具体逻辑功能,而只是对其下两个子模块进行例化,将它们的接口进行互联;同时该模块也是和外部接口信号
连接的“窗口”。在对外接口上,除了时钟和复位信号,余下的接口可以大致归为三类。
第一类为FPGA器件和外部NAND Flash芯片的接口。
input flash_r_bn; //NAND Flash准备好(1)或忙(0)标志位
output flash_cle; //NAND Flash命令锁存使能信号
output flash_ale; //NAND Flash地址锁存使能信号
output flash_ce_n; //NAND Flash片选信号,低电平有效
output flash_re_n; //NAND Flash读使能信号,低电平有效
output flash_we_n; //NAND Flash写使能信号,低电平有效
output flash_wp_n; //NAND Flash写保护信号,低电平有效
inout[7:0] flash_db; //NAND Flash数据/地址/命令复用总线
第二类为作为Avalon-MM总线的从机接口,与NIOS II主机进行数据交互的接口。
input avaflash_cs_n; //avalon总线flash外设读片选,低电平有效
input avaflash_wr_n; //avalon总线flash外设写使能信号,低电平有效
input avaflash_rd_n; //avalon总线flash外设读使能信号,低电平有效
input[3:0] avaflash_addr; //avalon总线flash外设地址信号
input[15:0] avaflash_wrdata; //avalon总线flash外设数据信号
output[15:0] avaflash_rddata; //avalon总线flash外设数据信号
第三类是一个预留的作为FPGA内部逻辑接口的DMA数据流传输接口。在本实例中,暂时不会用到这个接口。而在后续的实例中,我们可以不通过NIOS II处理器,直接通过这组接口将大量的
数据从Flash中搬运到我们的其他逻辑模块中进行处理。
output[21:0] flashph_addr; //读flash数据写入“SDRAM写FIFO”地址
output[15:0] flashph_data; //读flash数据写入“SDRAM写FIFO”数据
output flashph_rdy; //flash数据读出有效,同时可以写入缓存FIFO
②Flash_top.v模块是一个顶层模块,其下两个子模块flash_ctrl.v模块和flash_avalon.v模块之间有一些数据的交互,它们的接口如下。
wire sysfpwr_req; //flash页写请求信号,高电平有效一直保持到操作结束
wire sysfprd_req; //flash页读请求信号,高电平有效一直保持到操作结束
wire sysfbytewr_req; //flash字节写请求信号,高电平有效一个时钟周期
wire[27:0] sysfp_ab; //flash页写入/读出地址
wire[7:0] sysfpwr_db; //flash页写入数据
wire[7:0] sysfprd_db; //flash页读出数据
wire sysfprd_rdy; //flash数据读出有效,同时可以写入缓存FIFO
wire sysfbera_req; //flash块擦除请求信号,高电平有效一直保持到操作结束
wire[9:0] sysfbera_ab; //flash块擦除地址
wire sysf_state; //flash当前状态:1--ready,0--busywire dbalig; //数据对齐控制位,1的时候表示有两个字节数据
wire flrd_avareq; //flash读数据,用于avalone总线读取操作
wire flrd_phoreq; //flash读数据,用于对图片显示进行直接操作
flash_ctrl.v模块和flash_avalon.v模块之间,及其和外部的接口连接如图6-32所示。