https://download.csdn.net/download/mvpkuku/90855619
一、AXI_FULL协议的前提知识
1. 各端口的功能
2. 4K边界问题
3. outstanding
4.时序仿真体验
可通过VIVADO自带ADMA工程观察仿真波形图
二、FPGA实现 (主要用于读写DDR)
1.功能模块及框架
将用户端的写/读相关信号转换为AXI4_MASTER接口信号。因此顶层设计主要分为用户端接口以及AXI4协议主机接口
1、换存数据量较小的时候,FIFO建议采用DRAM
DRAM是由逻辑资源搭建而成的,消耗的是LUT的资源
缓存命令、地址、帧长度等等。2、缓存数据量比较大的时候,采用BRAM,
BRAM是一种资源。18k = 18 * 1024bit = 36bit(最大宽度) * 512(深度)= 18bit * 1024 = 9bit * 2048
36k = 2 * 18k = 72bit(最大宽度) * 512(深度)= 36bit * 1024 = 18bit * 2048
37bit = 36bit + 36bitFIFO使用了一个18k的bram,即使FIFo的深度和宽度开的很小,很多空间没有使用,
这个FIFO的消耗的BRAM,不能被其他的模块(FIFO)使用
FIFO采用BRAM的时候,消耗最少相同的BARM数量,建议把FIFO的深度和宽度开到最大AXI_DATA_WIDTH == 64
64bit + 1bit = 65bit = 36bit + 36bit 至少2个18k bram
2.写通道实现
2.1 wr_ctrl模块
写入用户端信号(写使能,写数据,写地址)
1.对复位信号以及ddr初始化完成信号进行同步,进而对用户端信号进行有效寄存
2.对用户端写数据进行计数(位宽转换,突发写字节数)
3.生成数据及数据有效,写数据最后一位,写突发长度
4.生成写请求和写地址
ps:3、4输出信号均给wr_buffer模块进行跨时钟与缓存
下一个模块的cmd_wren在wr_req_en为1时拉高,并且此模块在此时更新地址
wr_req_en作为wr_buffer状态机启动信号
2.2 wr_buffer模块
1.复位信号进行跨时钟域同步(用户时钟/AXI4时钟)
2.将地址和length写入CMD fifo (clk),cmd_wren在wr_req_en为1时拉高,并且此时更新地址
3..将数据和last 写入data fifo
写数据随着上层数据有效写入data_fifo
4.状态机设计(输出传给axi4_master模块)
(axi_aw_req_en和axi_aw_ready)握手机制控制是否开始写状态
//表示AXI4写请求 本模块时序控制,状态机一个状态产生请求信号,握手成功之后拉低
//axi_aw_req_en 和axi_aw_ready同时为高,开启一次AXI4写传输
并在该信号跳转开始写数据状态的同时,拉高cmd_rden,读出数据突发长度以及写地址
5.输出信号的时序
突发长度和地址cmd_dout分配
其次是是数据有效同上,已进入写数据状态就拉高
数据和last由当data_en为高时由data_dout分配
三个信号同步
2.3 axi_master模块
1.对除了与下游从机交互的握手信号以及写地址,写突发长度的信号进行固定赋值;
2. axi_aw_req_en启动状态机
3.组合逻辑输出作为丛机给wr_buffer的握手准备信号,一个由状态机产生,一个由下游从机输入
4.对和下游从机交互的握手信号以及写地址,写突发长度以及数据,数据最后一位的信号 时序控制
2.4 实现顶层代码
`timescale 1ns / 1ps
//
// Description: 写通道
//
module axi_wr_channel#(
parameter USER_WR_DATA_WIDTH = 16 ,
parameter AXI_DATA_WIDTH = 128 ,
parameter AXI_ADDR_WIDTH = 32
)(
input user_wr_clk , //用户端写时钟
input axi_clk , // AXI4端时钟
input reset ,
input ddr_init_done ,
/*-------------用户写端口信号------------------------*/
input user_wr_en ,
input [USER_WR_DATA_WIDTH-1:0] user_wr_data ,
input [AXI_ADDR_WIDTH-1:0] user_wr_base_addr , //一定要被4096整除
input [AXI_ADDR_WIDTH-1:0] user_wr_end_addr , //一定要被4096整除
/*-------------AXI写通道端口信号---------------------*/
output m_axi_awvalid ,
input m_axi_awready ,
output [AXI_ADDR_WIDTH-1:0] m_axi_awaddr ,
output [3:0] m_axi_awid ,
output [7:0] m_axi_awlen ,
output [1:0] m_axi_awburst ,
output [2:0] m_axi_awsize ,
output [2:0] m_axi_awport ,
output [3:0] m_axi_awqos ,
output m_axi_awlock ,
output [3:0] m_axi_awcache ,
output m_axi_wvalid ,
input m_axi_wready ,
output [AXI_DATA_WIDTH-1:0] m_axi_wdata ,
output [AXI_DATA_WIDTH/8-1:0] m_axi_wstrb ,
output m_axi_wlast ,
input [3:0] m_axi_bid ,
input [1:0] m_axi_bresp ,
input m_axi_bvalid ,
output m_axi_bready
);
wire wr_req_en;
wire [7:0] wr_burst_length;
wire [AXI_ADDR_WIDTH-1:0] wr_data_addr;
wire wr_data_valid;
wire [AXI_DATA_WIDTH-1:0] wr_data_out;
wire wr_data_last;
wire axi_aw_req_en;
wire axi_aw_ready;
wire [7:0] axi_aw_burst_len;
wire [AXI_ADDR_WIDTH-1:0] axi_aw_addr;
wire axi_w_valid;
wire axi_w_ready;
wire [AXI_DATA_WIDTH-1:0] axi_w_data;
wire axi_w_last;
wr_ctrl #(
.USER_WR_DATA_WIDTH(USER_WR_DATA_WIDTH),
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),
.WR_BURST_LENGTH(1024)
) wr_ctrl (
.clk (user_wr_clk),
.reset (reset),
.ddr_init_done (ddr_init_done),
.user_wr_en (user_wr_en),
.user_wr_data (user_wr_data),
.user_wr_base_addr (user_wr_base_addr),
.user_wr_end_addr (user_wr_end_addr),
.wr_req_en (wr_req_en),
.wr_burst_length (wr_burst_length),
.wr_data_addr (wr_data_addr),
.wr_data_valid (wr_data_valid),
.wr_data_out (wr_data_out),
.wr_data_last (wr_data_last)
);
wr_buffer #(
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH)
) wr_buffer (
.clk (user_wr_clk),
.axi_clk (axi_clk),
.reset (reset),
.wr_req_en (wr_req_en),
.wr_burst_length (wr_burst_length),
.wr_data_addr (wr_data_addr),
.wr_data_valid (wr_data_valid),
.wr_data_in (wr_data_out),
.wr_data_last (wr_data_last),
.axi_aw_req_en (axi_aw_req_en),
.axi_aw_ready (axi_aw_ready),
.axi_aw_burst_len (axi_aw_burst_len),
.axi_aw_addr (axi_aw_addr),
.axi_w_valid (axi_w_valid),
.axi_w_ready (axi_w_ready),
.axi_w_data (axi_w_data),
.axi_w_last (axi_w_last)
);
axi_wr_master #(
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH)
) axi_wr_master (
.axi_clk (axi_clk),
.reset (reset),
.axi_aw_req_en (axi_aw_req_en),
.axi_aw_ready (axi_aw_ready),
.axi_aw_burst_len (axi_aw_burst_len),
.axi_aw_addr (axi_aw_addr),
.axi_w_valid (axi_w_valid),
.axi_w_ready (axi_w_ready),
.axi_w_data (axi_w_data),
.axi_w_last (axi_w_last),
.m_axi_awvalid (m_axi_awvalid),
.m_axi_awready (m_axi_awready),
.m_axi_awaddr (m_axi_awaddr),
.m_axi_awid (m_axi_awid),
.m_axi_awlen (m_axi_awlen),
.m_axi_awburst (m_axi_awburst),
.m_axi_awsize (m_axi_awsize),
.m_axi_awport (m_axi_awport),
.m_axi_awqos (m_axi_awqos),
.m_axi_awlock (m_axi_awlock),
.m_axi_awcache (m_axi_awcache),
.m_axi_wvalid (m_axi_wvalid),
.m_axi_wready (m_axi_wready),
.m_axi_wdata (m_axi_wdata),
.m_axi_wstrb (m_axi_wstrb),
.m_axi_wlast (m_axi_wlast),
.m_axi_bid (m_axi_bid),
.m_axi_bresp (m_axi_bresp),
.m_axi_bvalid (m_axi_bvalid),
.m_axi_bready (m_axi_bready)
);
endmodule
2.5 部分仿真时序图
3.读通道实现
3.1 rd_ctrl模块
基本上与wr_ctrl模块类似,主要注意几个区别:
1.状态机由读请求的上升沿在ddr初始化完成之后启动;
2.给用户端读忙的信号,只要不处于初始态 user_rd_req_busy
3.wr-buffer输入fifo是否写满的信号 rd_req_ready
3.2 rd_buffer模块
同理:仍然与wr_buffer对比
1. rd_cmd_fifo
通过 cmd_wrcount 给出 fifo是否写满的信号 rd_req_ready,在这个前提下cmd开始写入fifo
cmd_rden由下游axi_master的握手信号控制
2. rd_data_fifo
由下游axi_master 数据写入 (axi_clk)data_wren/data_din;
data_rden 需要data_fifo非空,并且不在data_fifo非读,一周期拉低
rd_data_fifo_out :在data_rden一次性读取位宽128的数据,然后在rd_data_flag进行数据的移位
rd_data_fifo_last:通过data_dout[64]判断是否为1,持续最后一个128位宽数据时间
3. 状态机起始跳转需要cmd非空而且data_fifo没写满;
结束跳转的条件
4.最后将rd_data_fifo_out截取16位给到用户端口,同步数据有效,rd_data_fifo_last计数最后一个移位数据给到用户端口
3.3 axi_rd_master模块
m_axi控制拉低,axi_控制拉高
`timescale 1ns / 1ps
//
// Description:读通道
/
module axi_rd_channel#(
parameter AXI_DATA_WIDTH = 128,
parameter AXI_ADDR_WIDTH = 32 ,
parameter USER_RD_DATA_WIDTH = 16
)(
input user_rd_clk , //用户端读时钟
input axi_clk , //axi的时钟
input reset ,
input ddr_init_done ,
/*--------------用户端读请求信号----------------------*/
input user_rd_req , //上升沿触发用户读请求
input [AXI_ADDR_WIDTH-1:0] user_rd_base_addr , //一定要被4096整除
input [AXI_ADDR_WIDTH-1:0] user_rd_end_addr , //一定要被4096整除
output user_rd_req_busy , //用户读请求忙标志
output user_rd_valid ,
output user_rd_last ,
output [USER_RD_DATA_WIDTH-1:0] user_rd_data ,
/*-------------AXI读通道端口信号---------------------*/
output m_axi_arvalid ,
input m_axi_arready ,
output [AXI_ADDR_WIDTH-1:0] m_axi_araddr ,
output [3:0] m_axi_arid ,
output [7:0] m_axi_arlen ,
output [1:0] m_axi_arburst ,
output [2:0] m_axi_arsize ,
output [2:0] m_axi_arport ,
output [3:0] m_axi_arqos ,
output m_axi_arlock ,
output [3:0] m_axi_arcache ,
input [3:0] m_axi_rid ,
input m_axi_rvalid ,
output m_axi_rready ,
input [AXI_DATA_WIDTH-1:0] m_axi_rdata ,
input m_axi_rlast ,
input [1:0] m_axi_rresp
);
wire rd_req_en ;
wire [7:0] rd_burst_length ;
wire [AXI_ADDR_WIDTH-1:0] rd_data_addr ;
wire rd_req_ready ;
wire axi_ar_req_en ;
wire axi_ar_ready ;
wire [7:0] axi_ar_burst_len;
wire [AXI_ADDR_WIDTH-1:0] axi_ar_addr ;
wire axi_r_valid ;
wire [AXI_DATA_WIDTH-1:0] axi_r_data ;
wire axi_r_last ;
rd_ctrl #(
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),
.RD_BURST_LENGTH(1024)
) rd_ctrl (
.clk (user_rd_clk),
.reset (reset),
.ddr_init_done (ddr_init_done),
.user_rd_req (user_rd_req),
.user_rd_base_addr (user_rd_base_addr),
.user_rd_end_addr (user_rd_end_addr),
.user_rd_req_busy (user_rd_req_busy),
.rd_req_en (rd_req_en),
.rd_burst_length (rd_burst_length),
.rd_data_addr (rd_data_addr),
.rd_req_ready (rd_req_ready)
);
rd_buffer #(
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH),
.USER_RD_DATA_WIDTH(USER_RD_DATA_WIDTH)
) rd_buffer (
.clk (user_rd_clk),
.axi_clk (axi_clk),
.reset (reset),
.rd_req_en (rd_req_en),
.rd_burst_length (rd_burst_length),
.rd_data_addr (rd_data_addr),
.rd_req_ready (rd_req_ready),
.axi_ar_req_en (axi_ar_req_en),
.axi_ar_ready (axi_ar_ready),
.axi_ar_burst_len (axi_ar_burst_len),
.axi_ar_addr (axi_ar_addr),
.axi_r_valid (axi_r_valid),
.axi_r_data (axi_r_data),
.axi_r_last (axi_r_last),
.user_rd_valid (user_rd_valid),
.user_rd_last (user_rd_last),
.user_rd_data (user_rd_data)
);
axi_rd_master #(
.AXI_DATA_WIDTH(AXI_DATA_WIDTH),
.AXI_ADDR_WIDTH(AXI_ADDR_WIDTH)
) axi_rd_master (
.axi_clk (axi_clk),
.reset (reset),
.axi_ar_req_en (axi_ar_req_en),
.axi_ar_ready (axi_ar_ready),
.axi_ar_burst_len (axi_ar_burst_len),
.axi_ar_addr (axi_ar_addr),
.axi_r_valid (axi_r_valid),
.axi_r_data (axi_r_data),
.axi_r_last (axi_r_last),
.m_axi_arvalid (m_axi_arvalid),
.m_axi_arready (m_axi_arready),
.m_axi_araddr (m_axi_araddr),
.m_axi_arid (m_axi_arid),
.m_axi_arlen (m_axi_arlen),
.m_axi_arburst (m_axi_arburst),
.m_axi_arsize (m_axi_arsize),
.m_axi_arport (m_axi_arport),
.m_axi_arqos (m_axi_arqos),
.m_axi_arlock (m_axi_arlock),
.m_axi_arcache (m_axi_arcache),
.m_axi_rid (m_axi_rid),
.m_axi_rvalid (m_axi_rvalid),
.m_axi_rready (m_axi_rready),
.m_axi_rdata (m_axi_rdata),
.m_axi_rlast (m_axi_rlast),
.m_axi_rresp (m_axi_rresp)
);
endmodule