Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(前导)
四、AXI转FIFO接口模块设计
1.AXI接口知识
AXI协议是基于 burst的传输,并且定义了以下 5 个独立的传输通道:
- 读地址通道(Read Address Channel, AR)
- 读数据通道(Read Data Channel, R)
- 写地址通道(Write Address Channel, AW)
- 写数据通道(Write Data Channel, W)
- 写响应通道(Write Response Channel, B)
- 这 5 条独立的通道都包含一个双路的 VALD、 READY 握手机制。信息源通过 VALID 信号来指示通道中的数据和控制信息什么时候有效。目地源用READY 信号来表示何时准备好接收数据。传输地址信息和数据都是在 VALID和 READY 同时为高时有效。
- 读数据和写数据通道都包括一个 LAST 信号,用来指明一个事物传输的最后一个数据。
- 读/写事务都有自己的地址通道,地址通道携带着传输事务所必需的地址和控制信息。
- 读数据通道传送着从设备到主机的读数据和读响应信息。读响应信息指明读事务的完成状态。
- 写数据通路传送着主机向从设备的写数据。写响应通道提供了设备响应写事务的一种方式。在每一次突发式写会产生一个完成信号。
写事务时序
读事务时序
AXI 协议支持乱序传输。每一个通过接口的事务有一个 IDtag。协议要求相同 ID tag 的事务必须有序完成,而不同 ID tag 可以乱序完成
1.1写地址通道信号
1.2写数据通道信号
1.3写响应通道信号
1.4读地址通道信号
1.5读数据通道信号
2.ZynqAXI接口
红绿蓝三种颜色的箭头代表了几种不同位宽的 AXI 总线。 红色线条框是PL 端访问 DDR控制器的路径,其中, High Performamce AXI 32/64b Slave Ports 便是我们所说的HP 接口, 该接口为 AXI 接口,通常用于大量数据的高速传输。
PS 端通过硬件电路实现 AXI 总线,而 PL 端则需要用户通过逻辑资源搭建 AXI 总线。
3.AXI转换模块设计(fifo_axi4_adapter)
目的:为了解决上下游模块常常处于不同时钟域的问题。
fifo_axi4_adapter负责FIFO接口到AXI4接口的转换,FIFO中的数据可以通过该模块写入PS端,PS端的数据也可以由该模块接收写入FIFO。通过这种方式,用户只需要操作FIFO,就能实现PL端数据对PS端DDR的读写。
wr_ddr3_fifo 和 rd_ddr3_fifo 为上文中我们提到的读写 FIFO,用于存储读写数据,同时解决时钟域等问题。fifo2axi4 为 AXI4 接口转换模块,负责fifo 与 axi4 接口间的转换, 将写侧 FIFO 里的数据读出然后存储在 DDR 存储器以及将 DDR 存储器读出的数据存放到读侧 FIFO 缓存。
3.1接口转换模块设计(fifo2axi4)
写流程:主机向写地址通道写入地址和控制信息→写数据通道突发写入数据→收到设备的写数据响应。
读流程:主机向读地址通道写入地址和控制信息→ 收到设备的读数据响应和读的数据。
根据写时序进行状态机设计:
//写信号状态机
always@(posedge clk or posedge reset)
begin
if(reset)
curr_wr_state = WR_AXI_BYTE_ADDR_END)
m_ax服务器托管网i_awaddr
根据读时序进行状态机设计:
//读状态机
always@(posedge clk or posedge reset)
begin
if(reset)
curr_rd_state = RD_AXI_BYTE_ADDR_END)
m_axi_araddr
其他信号定义:
assign m_axi_awid = AXI_ID[AXI_ID_WIDTH-1:0];
assign m_axi_awsize = DATA_SIZE;
assign m_axi_awburst = 2'b01;
assign m_axi_awlock = 1'b0;
assign m_axi_awcache = 4'b0000;
assign m_axi_awprot = 3'b000;
assign m_axi_awqos = 4'b0000;
assign m_axi_awregion= 4'b0000;
assign m_axi_awlen = AXI_BURST_LEN[7:0];
assign m_axi_wstrb = 16'hffff;
assign m_axi_wdata = wr_fifo_rddata;
assign m_axi_bready = 1'b1;
assign m_axi_arid = AXI_ID[AXI_ID_WIDTH-1:0];
assign m_axi_arsize = DATA_SIZE;
assign m_axi_arburst = 2'b01;
assign m_axi_arlock = 1'b0;
assign m_axi_arcache = 4'b0000;
assign m_axi_arprot = 3'b000;
assign m_axi_arqos = 4'b0000;
assign m_axi_arregion= 4'b0000;
assign m_axi_arlen = AXI_BURST_LEN[7:0];
assign m_axi_rready = ~rd_fifo_alfull;
assign wr_fifo_rdreq = (~axi_awaddr_clr) && m_axi_wvalid && m_axi_wready;
assign rd_fifo_wrreq = (~axi_araddr_clr) && m_axi_rvalid && m_axi_rready;
assign rd_fifo_wrdata = m_axi_rdata;
assign wr_req_cnt_thresh = (m_axi_awlen == 'd0)? 1'b1 : (AXI_BURST_LEN[7:0]+1'b1-2'd2);//计数比实际数量少 2
assign rd_req_cnt_thresh = AXI_BURST_LEN[7:0];
assign wr_ddr3_req = (wr_fifo_rst_busy == 1'b0) && (wr_fifo_rd_cnt >= wr_req_cnt_thresh) ? 1'b1:1'b0;
assign rd_ddr3_req = (rd_fifo_rst_busy == 1'b0) && (rd_fifo_wr_cnt
3.2FIFO IP创建(wr_ddr3_fifo、rd_ddr3_fifo)
wr_ddr3_fifo:
设置写FIFO接口类型,写入和读出的时钟不一致, 所以这里需要创建一个独立时钟 FIFO。
设置写FIFO端口
rd_ddr3_fifo:
设置读FIFO接口类型,写入和读出的时钟不一致, 所以这里需要创建一个独立时钟 FIFO。
设置读 FIFO 端口
设置读FIFO数据计数
3.3 封装接口转换模块
module f服务器托管网ifo_axi4_adapter #(
parameter FIFO_DW = 16 ,
parameter WR_AXI_BYTE_ADDR_BEGIN = 0 ,
parameter WR_AXI_BYTE_ADDR_END = 1023 ,
parameter RD_AXI_BYTE_ADDR_BEGIN = 0 ,
parameter RD_AXI_BYTE_ADDR_END = 1023 ,
parameter AXI_DATA_WIDTH = 128 ,
parameter AXI_ADDR_WIDTH = 29 ,
parameter AXI_ID_WIDTH = 4 ,
parameter AXI_ID = 4'b0000,
parameter AXI_BURST_LEN = 8'd31 //burst length = 32
)
(
// clock reset
input clk ,
input reset ,
// wr_fifo wr Interface
input wrfifo_clr ,
input wrfifo_clk ,
input wrfifo_wren ,
input [FIFO_DW-1:0] wrfifo_din ,
output wrfifo_full ,
output [15:0] wrfifo_wr_cnt ,
// rd_fifo rd Interface
input rdfifo_clr ,
input rdfifo_clk ,
input rdfifo_rden ,
output [FIFO_DW-1:0] rdfifo_dout ,
output rdfifo_empty ,
output [15:0] rdfifo_rd_cnt ,
// Master Interface Write Address Ports
output [AXI_ID_WIDTH-1:0] m_axi_awid ,
output [AXI_ADDR_WIDTH-1:0] m_axi_awaddr ,
output [7:0] m_axi_awlen ,
output [2:0] m_axi_awsize ,
output [1:0] m_axi_awburst ,
output [0:0] m_axi_awlock ,
output [3:0] m_axi_awcache ,
output [2:0] m_axi_awprot ,
output [3:0] m_axi_awqos ,
output [3:0] m_axi_awregion,
output m_axi_awvalid ,
input m_axi_awready ,
// Master Interface Write Data Ports
output [AXI_DATA_WIDTH-1:0] m_axi_wdata ,
output [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb ,
output m_axi_wlast ,
output m_axi_wvalid ,
input m_axi_wready ,
// Master Interface Write Response Ports
input [AXI_ID_WIDTH-1:0] m_axi_bid ,
input [1:0] m_axi_bresp ,
input m_axi_bvalid ,
output m_axi_bready ,
// Master Interface Read Address Ports
output [AXI_ID_WIDTH-1:0] m_axi_arid ,
output [AXI_ADDR_WIDTH-1:0] m_axi_araddr ,
output [7:0] m_axi_arlen ,
output [2:0] m_axi_arsize ,
output [1:0] m_axi_arburst ,
output [0:0] m_axi_arlock ,
output [3:0] m_axi_arcache ,
output [2:0] m_axi_arprot ,
output [3:0] m_axi_arqos ,
output [3:0] m_axi_arregion,
output m_axi_arvalid ,
input m_axi_arready ,
// Master Interface Read Data Ports
input [AXI_ID_WIDTH-1:0] m_axi_rid ,
input [AXI_DATA_WIDTH-1:0] m_axi_rdata ,
input [1:0] m_axi_rresp ,
input m_axi_rlast ,
input m_axi_rvalid ,
output m_axi_rready
);
wire wrfifo_rden;
wire [AXI_DATA_WIDTH-1:0] wrfifo_dout;
wire [5 : 0] wrfifo_rd_cnt;
wire wrfifo_empty;
wire wrfifo_wr_rst_busy;
wire wrfifo_rd_rst_busy;
wire rdfifo_wren;
wire [AXI_DATA_WIDTH-1:0] rdfifo_din;
wire [5 : 0] rdfifo_wr_cnt;
wire rdfifo_full;
wire rdfifo_wr_rst_busy;
wire rdfifo_rd_rst_busy;
reg wrfifo_clr_sync_clk;
reg wr_addr_clr;
reg rdfifo_clr_sync_clk;
reg rd_addr_clr;
wr_ddr3_fifo wr_ddr3_fifo
(
.rst (wrfifo_clr ),
.wr_clk (wrfifo_clk ),
.rd_clk (clk ),
.din (wrfifo_din ),
.wr_en (wrfifo_wren ),
.rd_en (wrfifo_rden ),
.dout (wrfifo_dout ),
.full (wrfifo_full ),
.empty (wrfifo_empty ),
.rd_data_count (wrfifo_rd_cnt ),
.wr_data_count (wrfifo_wr_cnt ),
.wr_rst_busy (wrfifo_wr_rst_busy ),
.rd_rst_busy (wrfifo_rd_rst_busy )
);
rd_ddr3_fifo rd_ddr3_fifo
(
.rst (rdfifo_clr ),
.wr_clk (clk ),
.rd_clk (rdfifo_clk ),
.din (rdfifo_din ),
.wr_en (rdfifo_wren ),
.rd_en (rdfifo_rden ),
.dout (rdfifo_dout ),
.full (rdfifo_full ),
.empty (rdfifo_empty ),
.rd_data_count (rdfifo_rd_cnt ),
.wr_data_count (rdfifo_wr_cnt ),
.wr_rst_busy (rdfifo_wr_rst_busy ),
.rd_rst_busy (rdfifo_rd_rst_busy )
);
always@(posedge clk)
begin
wrfifo_clr_sync_clk
4.AXI 接口转换模块仿真
1.仿真设计结构
2.DDR控制器(MIG IP)创建
1、配置MIG IP
DDR3存储器驱动的时钟周期(Clock Period)设置为2500ps(即400MHz),这个时钟是用于 FPGA 输出给到 DDR 存储器时钟管脚的时钟。注意这里根据实际情况是有设置区间范围的,并非可以设置任意值,这里的区间范围为 2500~3300ps(即 300~400MHz)。
TMD(Time DivisionMultiplexing),该设置读写优先级相同,读写交替进行。
仿真选New Design,上板测试选Fixed Pin Out。
在 Vivado 的 Source 窗口中,出现了新配置生成的 IP 核文件mig_7series_0(mig_7series_0.xci)。
在上面窗口的 IP Sources 一栏,可以找到 IP 例化模板文件(mig_7series_0.veo)。
3.产生时钟和复位、数据产生和数据读取的激励
DDR控制器时钟200MHz(T=5ns,每2.5ns翻转一次)
wr_ddr3_fifo、rd_ddr3_fifo时钟100MHz(T=10ns,每5ns翻转一次)
fifo_axi4_adapter模块时钟使用DDR控制器输出的供用户侧使用的ui_clk。
仿真的时间单位1ns,精度100ps。具体可以看一下Verilog(未完待续)里面的编译指令timescale部分。每2.5ns翻转一次,它的最低精度就是0.1ns=100ps,时间单位就是1ns。
`timescale 1ns/100ps
initial sys_clk_i = 1'b1;
always #2.5 sys_clk_i = ~sys_clk_i;
initial wrfifo_clk = 1'b1;
always #2.5 wrfifo_clk = ~wrfifo_clk;
initial rdfifo_clk = 1'b1;
always #2.5 rdfifo_clk = ~rdfifo_clk;
数据产生和读取的激励设计上将其封装成任务 task 形式,方便调用。
//向 wr_ddr3_fifo 中写入以起始数据 data_begin 开始递增的wr_data_cnt 个数据。
task wr_data;
input [15:0]data_begin;
input [15:0]wr_data_cnt;
begin
wrfifo_wren = 1'b0;
wrfifo_din = data_begin;
@(posedge wrfifo_clk);
#1 wrfifo_wren = 1'b1;
repeat(wr_data_cnt)
begin
@(posedge wrfifo_clk);
wrfifo_din = wrfifo_din + 1'b1;
end
#1 wrfifo_wren = 1'b0;
end
endtask
//向 rd_ddr3_fifo 中读出个 rd_data_cnt 数据
task rd_data;
input [15:0]data_begin;
input [15:0]rd_data_cnt;
begin
rdfifo_rden = 1'b0;
expect_rd_data = data_begin;
@(posedge rdfifo_clk);
#1 rdfifo_rden = 1'b1;
repeat(rd_data_cnt)
begin
@(posedge rdfifo_clk);
if(rdfifo_dout != expect_rd_data)
begin
$display("SIM is failed");
$finish;
end
expect_rd_data = expect_rd_data + 1'b1;
end
#1 rdfifo_rden = 1'b0;
end
endtask
/*
先产生 DDR 控制器的复位以及 FIFO 的复位。
等 DDR 控制器内部锁相环锁定后,延时 200ns 后对 FIFO 解复位。
等待 DDR 初始化校准完成后,延时 200n 往 wr_ddr3_fifo 写入 1024 个数据,
数据写完后,对rd_ddr3_fifo 进行复位,清空里面的缓存,
等 FIFO 复位结束一段时间后,等待rd_ddr3_fifo 非空后开始对 rd_ddr3_fifo 进行读取数据,
读取 1024 个数据。整个仿真结束后打印“SIM is successfully”信息
*/
initial begin
sys_rst = 1'b0;
aresetn = 1'b0;
expect_rd_data = 16'd0;
wrfifo_clr = 1'b1;
wrfifo_wren = 1'b0;
wrfifo_din = 16'd0;
rdfifo_clr = 1'b1;
rdfifo_rden = 1'b0;
#201;
sys_rst = 1'b1;
aresetn = 1'b1;
@(posedge mmcm_locked);
#200;
wrfifo_clr = 1'b0;
rdfifo_clr = 1'b0;
@(posedge init_calib_complete);
#200;
wr_data(SIM_DATA_BEGIN,SIM_DATA_CNT);
#2000;
rdfifo_clr = 1'b1;
#20;
rdfifo_clr = 1'b0;
#2000;
wait(rdfifo_empty == 1'b0)
rd_data(SIM_DATA_BEGIN,SIM_DATA_CNT);
#5000;
$display("SIM is successfully");
$stop;
end
4.运行仿真
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
写在前面 偷懒,先写了数组,列表要画图,所以今天就先不写了 数组的定义 数组是由n个相同类型的数据元素构成的有限序列。每个数据元素被称为一个数组元素,每个元素在n个线性关系中的序号称为该元素的下标,下标的取值范围称为数组的维界。 数组与线性表的关系:数组是线性…