一、软件平台与硬件平台
软件平台:
操作系统:Windows 8.1 64-bit
开发套件:Vivado2015.4.2
硬件平台:
评估板:ZYNQ-7 ZC706 Evaluation Board
二、打开例子工程
1、新建工程,并在IP Catalog中找到Serial RapidIO Gen2
2、双击Serial RapidIO Gen2进入核的配置界面,所用参数全部保持默认,然后直接点击OK
3、在弹出的的对话框中直接点击Generate
4、在弹出的对话框中点击OK
5、等待srio_gen2_0核综合完毕,可能会花几分钟的时间
6、IP核综合完毕以后,单击srio_gen2_0,并点击鼠标右键,在弹出的菜单栏中单击Open IP Example Design…
7、在弹出的的对话框中选择例子工程的存放目录(这个目录可任意选择),然后单击OK
8、等一段时间以后例子工程就会自动打开
三、例子工程详解
3.1 工程概述
RapidIO核的例子工程的仿真顶层代码里面例化了两个实体,一个叫做srio_example_top_primary,另外一个叫做srio_example_top_mirror,如下图所示
其中每个例化实体都能被配置为发送支持的包类型,检测接收包失配以及上报链路的仿真细节。下表列出了例子工程中各个主要模块的功能简介
文件名
功能
srio_example_top.v
例子工程的顶层模块
srio_request_gen.v
生成请求事务的模块
instruction_list.vh
这是一个Verilog头文件,里面定义了120个事务,它被包含在srio_request_gen.v模块中,srio_request_gen.v模块会把里面的所有事务依次发出去
srio_response_gen.v
这个模块用来产生有响应事务的响应包
srio_condensed_gen.v
当IP核的端口配置为Condensed I/O模式时,这个文件才会出现在例子工程中。它用来产生Condensed I/O模式的请求事务。
srio_quick_start.v
这个模块与IP核的维护端口相连,用来发起维护事务。
maintenance_list.vh
这是一个Verilog头文件,里面定义了一些维护事务,它被包含在srio_quick_start.v模块中,srio_quick_start.v模块会把里面的所有维护事务依次发出去用来配置相关寄存器
srio_report.v
在仿真时,这个模块用来产生包接收和发送的时间戳,在硬件上运行的时候这个模块可以删除
srio_statistics.v
这个模块用来收集核的统计信息并通过寄存器接口提交一些信息,提交的信息可以通过Vivado的调试特征以及用户设计来访问
srio_sim.v
srio_sim.v是仿真顶层文件,它例化了两个核,分别是primary和mirror,并把它们连接到一起用于仿真。它也包含了上报测试是否成功的机制与超时(timeout)功能。
3.2 工程结构
例子工程的顶层模块例化了所有核的所有组件和在硬件上执行所需要的例子代码,整个工程的结构如下图所示。
整个结构包括时钟模块,复位模块,配置结构以及产生RapidIO事务的激励模块。
srio_quick_start模块在顶层srio_example_top.v中例化,它与IP核的维护端口相连用来产成维护事务,维护事务在maintenance_list.vh中进行定义,用户可以根据需要编辑maintenance_list.vh文件来添加,修改和移除维护事务。
srio_request_gen.v模块也在顶层srio_example_top.v中例化,它用来产生I/O事务与消息事务。这个模块也存储了期望的响应并把接收的响应与期望值进行比较。
srio_response_gen.v模块也在顶层srio_example_top.v中例化,它用来为接收到的请求事务生成对应的响应事务。
通过上图可以看出,产生I/O事务一共有两种方式:第一种是通过例子工程中自带的srio_request_gen.v产生I/O事务;第二种是通过顶层模块中Initiator和Target的resquest/response接口自己编写代码产生I/O事务。同理,产生维护事务也有两种方式:第一种是通过例子工程中自带的srio_quick_start.v模块产生维护事务;第二种是通过顶层模块中的维护接口自己编写代码产生维护事务。I/O事务的端口类型是AXI4-Stream类型,维护事务的端口类型是AXI4-Lite类型。
默认情况下,由于例子工程的顶层srio_example_top.v模块中,VALIDATION_FEATURES与QUICK_STARTUP两个参数均被设置为1,如下图所示
所以,例子工程是采用自带的srio_request_gen.v和srio_quick_start.v分别产生I/O事务与维护事务,另外,推荐注释掉外部接口以节省管脚和改善时序。
如果要使用外部接口产生I/O事务,那么需要设置参数VALIDATION_FEATURES=0,并且取消顶层模块srio_example_top.v中外部接口(axis_ireq_*,axis_tresp_*, axis_iresp_*, axis_treq_*, axis_iotx_*, axis_iorx_*)的注释。如果要使用外部接口产生维护事务,那些需要设置参数QUICK_STARTUP=0,并且取消顶层模块srio_example_top.v中外部接口(axis_maintr_*)的注释。使用外部接口的工程结构如下图所示
3.3 工程分析
I/O事务与维护事务
默认情况下,例子工程会使用srio_request_gen.v模块和srio_quick_start.v模块来产生I/O事务与维护事务。其中instruction_list.vh头文件定义了待发送的I/O事务,maintenance_list.vh头文件定义了待发送的维护事务。
当核被复位以后,maintenance_list.vh中的维护事务可以对核进行配置,维护事务可以在maintenance_list.vh进行添加、修改或移除。当没有处理器时,srio_quick_start.v模块可以用来管理公共的维护事务。当核复位以后,这是一种比较好的配置核的方法。
srio_request_gen.v模块和srio_response_gen.v模块可以用来产生I/O事务,srio_request_gen.v模块可以用来产生定义在instruction_list.vh中的I/O请求事务。instruction_list.vh中的I/O事务可以被添加、修改或移除,I/O事务的顺序是随机的,但是每次重新仿真时都是按照相同的顺序产生的。而且,只有IP核端口支持的事务才能被产生。srio_request_gen.v模块还可以追踪期望的响应并且比较接收的响应与期望值,这在仿真的时候可以很方便的确定事务的收发情况。
srio_response_gen.v模块用来产生接收到的请求所对应的目标响应I/O事务。如果写事务目标地址的第23位到16位为8’h12(address[23:16]=8’h12),那么数据负载会被存储在本地存储器。不需要响应的写事务会被丢弃,并且srio_response_gen.v模块不会响应SWRITE事务。如果读事务目标地址的第23位到16位为8’h12(address[23:16]=8’h12),那么数据将会从实际地址读出。对于那些地址不满足第23位到16位为8’h12(address[23:16]=8’h12)的事务,srio_response_gen.v模块将以地址递增的方式写入I/O事务携带的数据。响应会根据接收到的请求的顺序依次产生,所以不会有无序的事务产生。在所有的情况中,响应事务的优先级等于请求事务的优先级加1。
注意:每个srio_request_gen.v模块会消耗一个块RAM(Block RAM),每个srio_response_gen.v模块会消耗两个块RAM(Block RAM)。
配置结构
配置空间分布在RapidIO核的所有块中,配置结构的参考设计在例子设计的cfg_fabric模块中,用来管理每个块配置空间的访问情况。块(Block)的配置模块在配置总线(AXI4-Lite)上是作为从机存在的,cfg_fabric模块是作为主机存在的。维护事务的读写操作是从本地或者远程被发起,它是通过逻辑层的配置主端口接入配置模块,配置模块会把读写事务送入对应的块中,但如果地址不在各自配置寄存器的有效范围内,配置模块不会移交任何读写事务。往非法的空间写事务会被丢弃,读非法的空间将返回0。
时钟模块
例子设计的时钟模块与IP核的时钟模块是相同的。srio_clk模块有1个MMCM_ADV,1个IBUFDS和3个或4个BUFGs组成,在2x或4x模式,其中一个BUFGs将被转化为BUFGMUX。srio_clk模块会根据配置的不同产生合适的参考频率与用户时钟。
复位模块
srio_rst模块会把每个时钟域里面的异步复位信号转化为一个脉冲扩展的同步复位信号。当被用户设计调用的时候,srio_rst模块会用一个状态机强制对核重新初始化。
四、工程源码分析
3.1 顶层模块srio_example_top.v源码分析
顶层模块srio_example_top.v源码的端口定义如下
顶层模块包含5个参数:SIM_VERBOSE,VALIDATION_FEATURES,QUICK_STARTUP,STATISTICS_GATHERING和C_LINK_WIDTH。
SIM_VERBOSE设为1时,可以利用srio_report.v模块生成不可综合的报告。为0时,此功能被屏蔽。
VALIDATION_FEATURES设为1时,选择例子工程中的srio_request_gen.v和srio_response_gen.v产生相应的请求事务与响应事务对IP核的功能进行测试。为0时,选择外部的用户接口由用户自己编写请求事务与响应事务的代码对IP进行测试。
QUICK_STARTUP设为1时,选择例子工程中的srio_quick_start.v产生相应的维护事务对IP核的配置空间进行访问。为0时,选择外部的维护接口由用户自己编写维护事务的代码对IP核的配置空间进行访问。
STATISTICS_GATHERING设为1时,可以利用srio_statistics.v模块搜集IP的性能细节,这个模块是可综合的,可以通过Chipscope或ILA进行访问。为0时,此功能被屏蔽。
顶层模块的差分时钟sys_clkp和sys_clkn就是IP核配置界面第一页的参考时钟,由于例子工程中采用的全部是默认参数,所以这里这个时钟为125MHz,这个时钟可以由FPGA外部的有源晶振或锁相环芯片输出。
sys_rst为IP核的复位信号,高电平有效。
差分信号srio_rxn0和srio_rxp0为串行接收数据信号,srio_txn0和srio_txp0为串行发送数据信号。
sim_train_en信号是一个用来减少仿真时间的控制信号,仿真时把这个信号置1可以加快仿真速度,但是代码在硬件上运行时这个信号必须赋值为0。
led0是led指示信号,可以把port_initialized和link_initialized两个信号接到led0中,这样在硬件上执行时可以方便观察链路状态。
顶层模块srio_example_top.v中的第466行到第590行例化了RapidIO核,部分源码如下图所示
第612行到631行是例化了srio_report.v模块用来收集ireq接口的统计信息。除此以外,还例化了3个srio_report.v模块用来收集iresp,treq,tresp接口的统计信息。下图是收集ireq统计信息的源代码。源码如下图所示
第637行到684行srio_request_gen.v模块用来生成请求事务。这个模块有7个参数,分别为SEND_SWRITE,SEND_NWRITER,SEND_NWRITE,SEND_NREAD,SEND_FTYPE9,SEND_DB和SEND_MSG。当他们设置为1时,srio_request_gen.v模块将会把instruction_list.vh模块中对应的事务发送给SRIO IP核。部分源码如下图所示
第779行到804行例化了srio_response_gen.v模块,这个模块用来产生响应事务。部分源码如下图所示
第890行到937行例化了srio_quick_start.v模块,这个模块产生维护事务来访问配置空间。部分源码如下图所示
第944行到988行例化了srio_statistics.v模块,这个模块用来收集统计信息,它是一个可综合的模块。部分源码如下图所示
3.2 模块srio_request_gen.v源码分析
模块srio_request_gen.v的作用是产生RapidIO请求事务,上篇文章《Xilinx RapidIO核详解》(链接:https://www.cnblogs.com/liujinggang/p/10072115.html)已经提到过,RapidIO核为了简化包的构建过程,设计了一种精简的包格式——HELLO格式来完成包的构建,然后按照HELLO格式的时序把数据发送给RapidIO核,RapidIO核会把HELLO格式的包转化为标准的RapidIO串行物理层的包。这样,用户在设计请求事务的Verilog代码时只需要对HELLO格式的包与时序有所了解,而不需要过多的关注RapidIO的协议与RapidIO包格式。这里重新复习一下HELLO格式的包结构与HELLO格式的时序。
HELLO格式的包结构如下图所示:
HELLO格式的时序图如下图所示:
事实上,整个srio_request_gen.v源代码的核心就是先构建HELLO格式的包,然后把包头(Header)和数据按照HELLO格式的时序传给RapidIO核就可以了。下面详细分析一下。
分析instruction_list.vh头文件
在分析srio_request_gen.v源代码之前先来分析一下instruction_list.vh头文件。因为srio_request_gen.v文件中包含了instruction_list.vh头文件,instruction_list.vh头文件中定义了srio_request_gen.v将要发送的所有事务。
instruction_list.vh头文件的第1行到第9行定义了事务的个数以及所有事务的总个数,每种事务的个数由一个控制变量进行选择,源码如下:
第11行到50行是37个SWRITE事务(流写事务),其中第1列的12位数据是由8-bit的保留位(SWRITE事务没有srcTID字段),1-bit的保留位,2-bit的prio和1-bit的CRF组成,第2列是FTYPE字段,第3列是保留字段(SWRITE事务没有TTYPE字段),第4列的36-bit数据是由最高2-bit的保留位和34-bit的address字段组成,第5列是8-bit保留字段(SWRITE事务没有size字段),不管这个字段的值为多少,目标设备都会把这个值当做0来处理。源代码如下图所示
第52行到72行是19个NWRITE_R事务(带响应的写事务),其中第1列的12位数据是由8-bit的srcTID,1-bit的保留位,2-bit的prio和1-bit的CRF组成,第2列是FTYPE字段,第3列是TTYPE字段,第4列的36-bit数据是由最高2-bit的保留位和34-bit的address字段组成,第5列是size字段,这个字段的值为实际的数据量减1。比如size=0,表示实际传输的数据量为1。源代码如下图所示
第74行到94行是19个NWRITE事务(写事务),其中第1列的12位数据是由8-bit的srcTID,1-bit的保留位,2-bit的prio和1-bit的CRF组成,第2列是FTYPE字段,第3列是TTYPE字段,第4列的36-bit数据是由最高2-bit的保留位和34-bit的address字段组成,第5列是size字段,这个字段的值为实际的数据量减1。比如size=0,表示实际传输的数据量为1。源代码如下图所示
第96行到123行是26个NREAD事务(读事务),其中第1列的12位数据是由8-bit的srcTID,1-bit的保留位,2-bit的prio和1-bit的CRF组成,第2列是FTYPE字段,第3列是TTYPE字段,第4列的36-bit数据是由最高2-bit的保留位和34-bit的address字段组成,第5列是size字段,这个字段的值为实际的数据量减1。比如size=0,表示实际传输的数据量为1。源代码如下图所示
第125行到128行是两个DOORBELL事务(门铃事务),其中第1列的12位数据是由8-bit的srcTID,1-bit的保留位,2-bit的prio和1-bit的CRF组成,第2列是FTYPE字段,第3列是保留字段(DOORBELL事务没有TTYPE字段),第4列的36-bit数据是由最高4-bit的保留位,8-bit的信息高8位,8-bit的信息低8位以及最后的16-bit保留位组成。第5列是8-bit保留字段(DOORBELL事务没有size字段)。源代码如下图所示
第130行到148行是17个MESSAGE事务(消息事务),其中第1列的12位数据是由4-bit的msglen,4-bit的msgseg,1-bit的保留位,2-bit的prio和1-bit的CRF组成,第2列是FTYPE字段,第3列是保留字段(MESSAGE事务没有TTYPE字段),第4列的36-bit数据是由最高26-bit的保留位,6-bit的mailbox字段,2-bit的保留位以及2-bit的ltr组成。第5列是8-bit的size字段。源代码如下图所示
第150行到152行是1个Data Streaming事务,这是Xilinx定义的第9类事务,由于这种事务用的不多,我自己也不太了解,所以这里就不进行分析了。源代码如下图所示
分析srio_request_gen.v源码
在分析完毕instruction_list.vh头文件以后接下来开始着手分析srio_request_gen.v源码。分析过程中始终要记住的一点是:srio_request_gen.v的核心功能就是组装HELLO包头并把包头和数据按照HELLO格式的时序发送出去。
srio_request_gen.v源码的第50行到92行是参数与接口的定义。它包含7个参数,分别为SEND_SWRITE、SEND_NWRITER、SEND_NWRITE、SEND_NREAD、SEND_DB、SEND_FTYPE9和SEND_MSG,它们的默认值被设置为0,但是在顶层模块中例化的时候,它们的值分别被设置为1、1、1、1、0、1、1。如下图所示
根据上面分析instruction_list.vh头文件可知,如果上面的7个参数为1的话,各个事务的个数应该由instruction_list.vh头文件中NUM_SWRITES、NUM_NWRITERS、NUM_NWRITES、NUM_NREADS、NUM_DBS、NUM_MSGS和NUM_FTYPE9决定。
接下来就是端口的定义,其中log_clk与log_rst均由IP输出,log_clk的频率与链路线速率和链路宽度有关,具体的对应关系在上篇文章中能找到。接下来就是设备ID,目的ID以及源ID的定义。然后定义了ireq与iresp,它们都采用了AXI4-Stream协议。link_initialized信号由IP核输出,当它为高时表明链路成功初始化。以user_*开头的几个变量可以方便用户自定义HELLO格式包头中的各个字段。
第96到110行定义了HELLO格式包头中的FTYPE字段与TTYPE字段的值,这两个字段的值与事务的类型有关,源码如下图所示
第120行定义了一个memory类型变量,综合的时候ram的类型为分布式ram,121行导入了instruction_list.vh头文件,126行定义的变量可以看做AXI4-Stream总线上数据有效的标志位,当这个信号为高时,AXI4-Stream总线上tdata上的数据为有效数据。源代码如下图所示
第178行到205行用来初始化事务列表,instruction[ii]存放的是instruction_list.vh头文件中定义的各个事务的相关字段值,源代码如下图所示
第208到第219行是一些简单的赋值操作。其中第208行的val_ireq_tkeep和val_ireq_tuser是根据HELLO格式的时序要求赋值的,HELLO格式的时序要求tkeep为8’hFF,tuser在第一个有效的时钟(log_clk)周期内由src_id与dest_id拼接而成。第213行和第217行的go变量用来选择HELLO格式包头各个字段由用户定义还是由instruction_list.vh定义。第219行的header_beat变量就是HELLO格式的包头,在第一个有效时钟(log_clk)周期,tdata上的数据必须为header_beat。源代码如下图所示
第266行到288行主要用来产生有效时钟计数器current_beat_cnt与有效时钟计数总数number_of_data_beats。当AXI4_Stream总线的tvalid和tready信号同时为高时,时钟为有效时钟,current_beat_cnt加1,当检测到tlast信号为高时表明最后一个数据已发送完毕,所以把current_beat_cnt清0。由于tdata的宽度为64-bit,也就是8个字节,所以有效时钟计数总数number_of_data_beats为current_size右移3位。源代码如下图所示
第294行到306行用来产生AXI4_Stream总线中tlast信号,tlast为高时表明发送的是最后一个数据,所以tlast信号相当于一帧数据的边界。
第307行到328行用来产生AXI4_Stream总线中的tdata信号,当有效时钟计数器current_beat_cnt为0时,tdata为包头header_beat,包头header_beat后面是待发送的数据data_beat,例子工程为了简单起见,发送的数据是累加数,每个累加数为8-bit,然后把每个累加数拼接8次作为待发送的数据,也就是说,待发送的数据为64’h0000000000000000,64’h0101010101010101,64’h0202020202020202,……..,后面以此类推。
第330行到345行主要用来产生AXI4_Stream总线中的tvalid信号,同时也产生了一些其他的标志信号。至此,整个AXI4_Stream总线中的所有信号的逻辑都编写完毕,并且整个逻辑都是完全符合HELLO格式的时序。代码的核心部分全部分析完毕。
第351行到第371行与数据的存储相关。每当current_beat_cnt的值为0时,request_address变量加1, request_address也被赋给了tid,也就是说request_address也是事务ID值,同时request_address也是instruction的索引值。源代码如下图所示
第382行到第406行例化了一个RAMB36SDP原语,RAMB36SDP是一个大小为36Kb的简单双口Block RAM(SDP=Simple Dual Port)。它的作用是把请求事务的tid,current_ftype和current_size写入RAM中存起来,对于有响应的事务,RapidIO会收到一个响应事务,收到响应事务事务以后可以把存放在RAMB36SDP中的数据读出来与响应事务中对应的字段进行对比从而对整个事务的交互过程的正确性进行一个初步判断。关于RAMB36SDP原语的详细解释请阅读下一小节。源代码如下图所示
第458行到482行就是把存储在RAM中请求事务的tid,current_ftype和current_size与响应事务对应的字段进行对比,在仿真时给出对应的提示信息。
至此,整个产生请求事务的代码全部分析完毕,其他未分析到的代码大家自己尝试分析。
3.3 RAMB36SDP原语分析
RAMB36SDP是一个大小为36Kb的简单双口Block RAM(SDP=Simple Dual-Port),它其实是Virtex-5系列FPGA的一个原语,Vivado里面并没有RAMB36SDP的语法模板,ISE中才有它的语法模板,如下图所示
RAMB36SDP原语的完整代码如下所示
// RAMB36SDP : In order to incorporate this function into the design, // Verilog : the forllowing instance declaration needs to be placed // instance : in the body of the design code. The instance name // declaration : (RAMB36SDP_inst) and/or the port declarations within the // code : parenthesis may be changed to properly reference and // : connect this function to the design. All inputs // : and outputs must be connected. // <—–Cut code below this line—-> // RAMB36SDP: 72×512 Simple Dual-Port BlockRAM w/ ECC // Virtex-5 // Xilinx HDL Language Template, version 14.7 RAMB36SDP #( .SIM_MODE(“SAFE“), // Simulation: “SAFE” vs. “FAST”, see “Synthesis and Simulation Design Guide” for details .DO_REG(0), // Optional output register (0 or 1) .EN_ECC_READ(“FALSE“), // Enable ECC decoder, “TRUE” or “FALSE” .EN_ECC_WRITE(“FALSE“), // Enable ECC encoder, “TRUE” or “FALSE” .INIT(72h000000000000000000), // Initial values on output port .SIM_COLLISION_CHECK(“ALL“), // Collision check enable “ALL”, “WARNING_ONLY”, // “GENERATE_X_ONLY” or “NONE” .SRVAL(72h000000000000000000), // Set/Reset value for port output // The forllowing INIT_xx declarations specify the initial contents of the RAM .INIT_00(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_01(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_02(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_03(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_04(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_05(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_06(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_07(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_08(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_09(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_0A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_0B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_0C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_0D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_0E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_0F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_10(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_11(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_12(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_13(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_14(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_15(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_16(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_17(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_18(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_19(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_1A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_1B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_1C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_1D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_1E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_1F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_20(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_21(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_22(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_23(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_24(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_25(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_26(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_27(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_28(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_29(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_2A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_2B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_2C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_2D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_2E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_2F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_30(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_31(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_32(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_33(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_34(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_35(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_36(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_37(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_38(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_39(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_3A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_3B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_3C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_3D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_3E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_3F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_40(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_41(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_42(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_43(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_44(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_45(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_46(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_47(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_48(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_49(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_4A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_4B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_4C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_4D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_4E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_4F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_50(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_51(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_52(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_53(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_54(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_55(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_56(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_57(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_58(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_59(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_5A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_5B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_5C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_5D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_5E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_5F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_60(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_61(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_62(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_63(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_64(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_65(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_66(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_67(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_68(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_69(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_6A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_6B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_6C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_6D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_6E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_6F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_70(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_71(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_72(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_73(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_74(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_75(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_76(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_77(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_78(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_79(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_7A(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_7B(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_7C(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_7D(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_7E(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), .INIT_7F(256h0000000000000000_0000000000000000_0000000000000000_0000000000000000), // The next set of INITP_xx are for the parity bits .INITP_00(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_01(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_02(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_03(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_04(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_05(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_06(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_07(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_08(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_09(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_0A(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_0B(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_0C(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_0D(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_0E(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00), .INITP_0F(256h00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00_00) ) RAMB36SDP_inst ( .DBITERR(DBITERR), // 1-bit double bit error status output .SBITERR(SBITERR), // 1-bit single bit error status output .DO(DO), // 64-bit data output .DOP(DOP), // 8-bit parity data output .ECCPARITY(ECCPARITY), // 8-bit generated error correction parity .RDCLK(RDCLK), // 1-bit read port clock .RDEN(RDEN), // 1-bit read port enable .REGCE(REGCE), // 1-bit register enable input .SSR(SSR), // 1-bit synchronous output set/reset input .WRCLK(WRCLK), // 1-bit write port clock .WREN(WREN), // 1-bit write port enable .WRADDR(WRADDR), // 9-bit write port address input .RDADDR(RDADDR), // 9-bit read port address input .DI(DI), // 64-bit data input .DIP(DIP), // 8-bit parity data input .WE(WE) // 8-bit write enable input ); // End of RAMB36SDP_inst instantiation
每个36Kb简单双口块RAM(Simple dual-port block RAM)都能被配置为512×64(32Kb)大小的RAM和一个内置的汉明纠错编码(Hamming Error Correction)块,由于要用到汉明纠错编码,所以数据位宽需要多用到8-bit,这样数据位宽就被扩展到72-bit,当数据位宽为72-bit,深度为512时,RAM的大小刚好为36Kb(512 * 72 / 1024 = 36),其中汉明纠错编码操作对用户是不可见的。每次写操作都会产生8-bit的保护位(原语中的ECCPARITY信号),这个8-bit的保护位在每次读操作的过程中可以用来纠正任何单比特的错误,或者检测(但不能纠正)任何双比特的错误。ECCPARITY输出信号没有可选的输出寄存器。原语中两个状态输出信号(SBITERR和DBITERR)的组合指示了三种可能的读操作结果:无错误(No Error),纠正了单比特错误(Signal Error Corrected)和检测到了双比特错误(Double Error Detected)。在读写操作的ECC(Error Correcting Code)模式均开启时(EN_ECC_READ = TRUE 并且 EN_ECC_WRITE = TRUE,EN_ECC_READ和EN_ECC_WRITE为原语的两个参数),读操作不能再存储器阵列中直接纠正错误,而只能把已经纠正完毕的数据输出给原语中的DO信号。ECC配置选项只有RAMB36SDP原语或FIFO36原语才支持。(此部分内容参考ug190的107页到155页)
Block RAM的ECC(Error Correcting Code)结构如下图所示
RAMB36SDP各个端口的定义如下表所示
端口名
方向
信号描述
DI[63:0]
Input
数据输入总线
DIP[7:0]
Input
数据输入奇偶校验总线
WRADDR[8:0]
Input
写地址总线
RDADDR[8:0]
Input
读地址总线
WREN
Input
写使能。当WREN=1时,数据将被写入存储器,当WREN=0,写操作不使能。
RDEN
Input
读使能。当RDEN=1时,数据将被从存储器读出,当RDEN=0,读操作不使能。
SSR
Input
同步设置/复位(Synchronous Set/Reset),这个信号用来复位输出寄存器的值为SRVAL,SRVAL是RAMB36SDP原语的一个参数。这个信号不会影响存储器存储单元的内容。
REGCE
Input
寄存器使能(Register Enable),端口输出寄存器使能信号。
WE[7:0]
Input
8位的字节写使能输入,由于输出数据总线为64-bit,一共为8个字节,所以字节写使能输入为8位,详细解释见表后的内容。
WRCLK
Input
写操作的时钟
RDCLK
Input
读操作的时钟
DO[63:0]
Output
数据输出总线
DOP[7:0]
Output
数据输出奇偶校验总线
SBITERR
Output
单比特错误(Signal Bit Error)状态
DBITERR
Output
双比特错误(Double Bit Error)状态
ECCPARITY
Output
ECC编码器输出数据总线
字节写使能(Byte-Writes):(此部分内容参考pg058的第50页到51页)
当字节写使能功能打开时,WE总线的宽度为输入数据总线DI中包含的字节个数。例如,输入数据总线DI为64位,包含8个字节,所以WE总线的宽度为8。其中WE总线的最高位对于输入数据总线DI的最高位的字节,WE总线的最低位对于输入数据总线DI的最低位的字节,在写操作过程中,只有WE总线中为1的位对应的字节才能被写入存储器中,为0的位保持原来的值不变。
假设输入数据总线的宽度为24位,它包含3个字节,所以WE的宽度为3,下图是一个字节写使能开启时往RAM的0地址写入数据的时序图(假设RAM的初始值全部为0)
由上图可知,当WEA(也就是上文的WE)为3’b011时,DINA数据24’hFF EE DD的后两个字节会写入存储器的0地址,而存储器0地址的最高字节保持00不变,所以第一次写操作完毕以后存储器0地址的数据为24’h00 EE DD;当WEA(也就是上文的WE)为3’b010时,DINA数据24’hCC BB AA的中间那个字节BB会写入存储器的0地址,而存储器0地址的最高字节和最低字节则保持前一次的00和DD值不变,所以第二次写操作完毕以后存储器0地址的数据为24’h00 BB DD。后面几次操作依次类推即可。
3.4 模块srio_response_gen.v源码分析
模块srio_response_gen.v的作用是产生RapidIO响应事务。RapidIO协议中只有NREAD、DOORBELL、MESSAGE以及NWRITE_R这几种事务有响应事务。
srio_response_gen.v源码的第51行到72行定义了模块的端口。log_clk和log_rst分别为逻辑层时钟与复位,tresp和treq为两个AXI4-Stream通道,tresp是响应事务传输通道,treq是请求事务传输通道。源码如下图所示
第77行到90行定义了HELLO格式包头中的FTYPE字段与TTYPE字段的值,这两个字段的值与事务的类型有关,源码如下图所示
第99行到100行定义了AXI4-Stream总线上有效数据的标志,当tvalid和tready同时为高时,tdata上的数据为有效数据。源代码如下图所示
第161行到163行按照HELLO格式的时序要求,把tkeep信号赋值为8’hFF,把tuser信号由src_id和dest_id拼接而成,并在第一个有效时钟发出去。源代码如下图所示
第193行到203行用来产生HELLO时序中的tready。源代码如下图所示
第205行到220行用来产生treq通道第一拍的标志信号,并把tdata中的数据按照HELLO格式的定义把对应的关键字段剥离出来,值得注意的是第218行优先级字段prio为tdata重对应的字段加1,原因是响应事务的优先级为请求事务的优先级加1,上篇博客也提到过这一点。这部分源代码如下图所示
第224行到第235行用来生成有响应事务的标志,其中RapidiO协议中只有NREAD、DOORBELL、MESSAGE和NWRITE_R这几种事务有对应的响应事务。这部分源代码如下图所示
第240行到274行与本地数据的存储有关。其中第240行到250行的逻辑用来产生数据存取使能位data_store_wen,本文第3.3节提到过,只有地址字段的第23位到第16位为8’h12时,写事务的数据才能被存放到本地存储器,原因就是第243行的判断语句加上了这个判断。第252行到260行的逻辑用来产生数据存储的写地址信号data_store_waddr。第262行到274行的逻辑用来产生数据存储的读地址信号data_store_raddr。源代码如下图所示
第276行到302行主要例化了一个RAMB36SDP用来存储写事务发送过来的数据。其中第276行用来产生数据存储的读使能标志。源代码如下图所示
第307行到第335行是把请求事务的包头(Header)信息存储在RAM中。其中第307行到308行把请求事务中HELLO格式定义的各个字段进行拼接作为RAM的输入,第325行到第335行用来产生RAM的读地址与写地址。
第337行例化了第二个Block RAM,它用来存储请求事务的HELLO格式信息。
第363行到第372行把从RAM中读出来的数据对应的字段剥离出来从而得到响应事务HELLO格式对应的字段。
第378行到第388行用来产生有效时钟的总数number_of_data_beats以及有效时钟计数器current_beat_cnt。这段逻辑与srio_request_gen.v中对应的逻辑完全一致。源代码如下图所示
第390行到400行用来产生tlast信号,发送最后一个数据时,tlast信号拉高。
第402行和第403行用来产生响应事务的包头(Header),第404行到414行用来产生响应事务的数据,其中响应事务的第一个有效时钟用来发送包头(Header),后面的有效时钟用来发送数据,这段逻辑也与srio_request_gen.v中对应的逻辑类似。源代码如下图所示
第459行到第469行用来产生AXI4-Stream中的tvalid信号,源代码如下
至此,整个响应事务的源代码分析完毕,其余未分析到的代码请自行分析。
3.5 模块srio_quick_start.v源码分析
模块srio_quick_start.v的作用是产生维护事务(Maintenance Transaction)访问本地设备和远程设备的配置空间。维护事务采用的接口协议为AXI4-Lite,它是一种轻量级的AXI4协议,有地址总线与数据总线,常用来实现单个寄存器的读写。在分析srio_quick_start.v源码之前首先分析一下maintenance_list.vh头文件。
分析maintenance_list.vh头文件
maintenance_list.vh头文件主要定义了维护事务相关的字段,其中第1列为2-bit的保留位,第2列为1-bit的远程/本地控制位,第3列为24-bit的地址,第4列为读/写控制位,第5列为32-bit的数据,第6列为4-bit的数据掩码(DATA MASK),部分源代码如下图所示
分析srio_quick_start.v源文件
srio_quick_start.v源文件的第68行到第101行是接口定义。log_clk和log_rst分别为逻辑层的时钟和复位。以maintr_*开头的信号为AXI4-Lite通道的信号,以user_*开头的信号是用户自定义的维护事务的相关字段,go变量用来在用户定义和maintenance_list.vh头文件定义的字段中进行选择
第105行到122行对maintenance_list.vh头文件中的参数进行了赋值,并引入了头文件,源代码如下图所示。
第192行到229行主要用来构建请求包,其本质就是产生AXI4-Lite的协议把数据发出去。部分源代码如下图所示
第233行到386行例化了一个RAMB36SDP原语,并用maintenance_list.vh头文件中定义的数据进行初始化。部分源代码如下图所示
第397行到416行主要负责响应侧的检测,通过maint_autocheck_error和maint_done指示检测状态。源代码如下图所示
至此,维护事务的代码分析完毕,它的代码逻辑相对来说比较简单。
另外,例子工程中还有两个模块srio_report.v和srio_statistics.v,它们只在仿真有用,在硬件上实现时最好删掉。这里不再作过多分析。只要理解了srio_request_gen.v与srio_response_gen.v,用RapidIO完成相关应用就足够了。
五、仿真
工程源码分析完毕以后,接下来就可以开始仿真了。例子工程中的代码一行都不要改,直接左键单击Run Simulation,在弹出的菜单中点击Run Behavioral Simulation,如下图所示
接着会出现下面的滚动框,在这个界面需要等待一会,如下图所示
上面的滚动框运行完毕以后就出现了波形界面,如下图所示
然后再点击下图中圈出的小图标把波形复位一下,如下图所示
复位波形以后在Tcl Console中输入指令:log_wave –r /* 。输入完毕以后按回车。这条指令的作用是记录所有中间变量的波形,这样后面要观察任何变量的波形只需要把它拖到波形界面中它的波形就会自动显示出来而不需要重新仿真,大大节约了时间。如下图所示
最后设置一个比较大的时间,我设置为10ms,然后点击它左边的按钮开始仿真,大约20分钟以后仿真会自动结束,如下图所示
仿真的过程中,在相应的时间点上,Tcl Console中会打印出包的收发情况
最后仿真结束以后,重新回到波形界面可以看到波形都正常产生了。这里最重要的两个信号就是link_initialized与port_initialized,当他们为高表示整个链路初始化完毕。至此,整个仿真过程全部结束,下篇文章会详细介绍每种事务的时序图以及需要注意的一些细节。
六、总结
本节主要分析了一下请求事务与响应事务的Verilog源码,事实上,它们的源码就是按照pg007_srio_gen2.pdf把HELLO格式与HELLO时序描述出来而已。当然官方的这套源码为了尽可能保证测试到核的所有功能,写的比较混乱,但在实际项目中可以充分借鉴它的设计思想,然后根据你自己的需求对不需要的地方进行修改来达到你的目标。除此以外,HELLO格式的时序其通过一个状态机来实现可能更加简洁易懂。总而言之,这套代码具有很大的参考价值,但是要想把这个核玩透还是得自己多在项目中理解。
下篇文章将会教大家如何在Vivado抓出每种事务的时序来对各种RapidIO的交互过程有一个更加清晰的理解。
审核编辑 :李倩免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:Xilinx RapidIO核例子工程源码分析-xilinx ram wea https://www.yhzz.com.cn/a/4088.html