You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
7.2 KiB
Systemverilog

module axi_bram #(
parameter AXI_IDWIDTH = 4, // AXI的id宽度
parameter AXI_AWIDTH = 64, // AXI的 地址总线宽度
parameter AXI_DWIDTH = 256, // AXI的数据总线宽度 一次读取的数据位数
parameter MEM_AWIDTH = 12 // BRAM size = MEM_AWIDTH*C_M_AXI_DATA_WIDTH (bits) = MEM_AWIDTH*C_M_AXI_DATA_WIDTH/8 (bytes)
) (
input wire rstn,
input wire clk,
// AXI-MM AW interface ----------------------------------------------------
output wire s_axi_awready, // 从写地址准备好
input wire s_axi_awvalid, // 主准备好
input wire [ AXI_AWIDTH-1:0] s_axi_awaddr,
input wire [ 7:0] s_axi_awlen,
input wire [ AXI_IDWIDTH-1:0] s_axi_awid,
// AXI-MM W interface ----------------------------------------------------
output wire s_axi_wready,
input wire s_axi_wvalid,
input wire s_axi_wlast,
input wire [ AXI_DWIDTH-1:0] s_axi_wdata,
input wire [(AXI_DWIDTH/8)-1:0] s_axi_wstrb,
// AXI-MM B interface ----------------------------------------------------
input wire s_axi_bready,
output wire s_axi_bvalid,
output wire [ AXI_IDWIDTH-1:0] s_axi_bid,
output wire [ 1:0] s_axi_bresp,
// AXI-MM AR interface ----------------------------------------------------
output wire s_axi_arready, // 表示S 已经可以接受 读地址信号
input wire s_axi_arvalid, // 表示M 地址已经准备就绪
input wire [ AXI_AWIDTH-1:0] s_axi_araddr, // M 发送的地址
input wire [ 7:0] s_axi_arlen, // 突发长度
input wire [ AXI_IDWIDTH-1:0] s_axi_arid,
// AXI-MM R interface ----------------------------------------------------
input wire s_axi_rready, // 只有当M ready的时候, S才可以发数据
output wire s_axi_rvalid, // S的数据已经准备好, 可以发送到M
output wire s_axi_rlast,
output reg [ AXI_DWIDTH-1:0] s_axi_rdata,
output wire [ AXI_IDWIDTH-1:0] s_axi_rid,
output wire [ 1:0] s_axi_rresp
);
function automatic int log2 (input int x);
int xtmp = x, y = 0;
while (xtmp != 0) begin
y ++;
xtmp >>= 1;
end
return (y == 0) ? 0 : (y - 1);
endfunction
// ---------------------------------------------------------------------------------------
// AXI READ state machine
// ---------------------------------------------------------------------------------------
enum reg [0:0] {R_IDLE, R_BUSY} rstate = R_IDLE;
reg [AXI_IDWIDTH-1:0] rid = '0; // 从 读地址 传过来的id
reg [ 7:0] rcount = '0; // 从 读地址 传过来的读取次数
reg [ MEM_AWIDTH-1:0] mem_raddr, mem_raddr_last; // 当前操作的地址, 和上次操作的地址
assign s_axi_arready = (rstate == R_IDLE); // 读地址 可以进行 接收
assign s_axi_rvalid = (rstate == R_BUSY); // 读数据 以准备就绪
assign s_axi_rlast = (rstate == R_BUSY) && (rcount == 8'd0); // 是否是最后一个数据, 直接连线
assign s_axi_rid = rid; // 直接连线
assign s_axi_rresp = '0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
rstate <= R_IDLE;
rid <= '0;
rcount <= '0;
end
else begin
case (rstate)
R_IDLE :
if (s_axi_arvalid) begin // M发起 读地址 请求
rstate <= R_BUSY; // S切换到忙碌
rid <= s_axi_arid; // 记录本次 M读地址 ID
rcount <= s_axi_arlen; // 记录本次 M的读长度
end
R_BUSY :
if (s_axi_rready) begin
if (rcount == 8'd0) // 如果是 忙碌状态, 且 是最后一个数据
rstate <= R_IDLE; // 则切换 S状态 为 空闲, 状态结束
rcount <= rcount - 8'd1; // 每次忙碌状态 rcount 都会减1
end
endcase
end
// 组合逻辑块,根据当前状态计算内存读地址
always_comb
if (rstate == R_IDLE && s_axi_arvalid) // 如果空闲状态 且 M已经准备好地址
mem_raddr = (MEM_AWIDTH)'(s_axi_araddr >> log2(AXI_DWIDTH/8)); // 计算地址,
// 因为每次数据都会 传256位数据, 这里除以8, 得到需要传的次数, 每次1字节传输, 256/8就是32次
// 看能整除多少次2, 然后 s_axi_araddr 进行右移, 移动到传过来的地址的 AXI_DWIDTH的字节边界 start起始的位置
else if (rstate == R_BUSY && s_axi_rready) // 如果繁忙, 且 M 已经准备好 接收 S发来的数据
mem_raddr = mem_raddr_last + (MEM_AWIDTH)'(1); // 计算下一个内存地址, 加一即可, 这是一个二维数组
else
mem_raddr = mem_raddr_last; // 保持地址不变
// 在每个时钟上升沿,更新上一次的读地址
always @ (posedge clk)
mem_raddr_last <= mem_raddr;
// ---------------------------------------------------------------------------------------
// AXI WRITE state machine
// ---------------------------------------------------------------------------------------
enum reg [1:0] {W_IDLE, W_BUSY, W_RESP} wstate = W_IDLE;
reg [AXI_IDWIDTH-1:0] wid = '0;
reg [ 7:0] wcount = '0;
reg [ MEM_AWIDTH-1:0] mem_waddr = '0;
assign s_axi_awready = (wstate == W_IDLE);
assign s_axi_wready = (wstate == W_BUSY);
assign s_axi_bvalid = (wstate == W_RESP);
assign s_axi_bid = wid;
assign s_axi_bresp = '0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
wstate <= W_IDLE;
wid <= '0;
wcount <= '0;
mem_waddr <= '0;
end else begin
case (wstate)
W_IDLE :
if (s_axi_awvalid) begin
wstate <= W_BUSY;
wid <= s_axi_awid;
wcount <= s_axi_awlen;
mem_waddr <= (MEM_AWIDTH)'(s_axi_awaddr >> log2(AXI_DWIDTH/8));
end
W_BUSY :
if (s_axi_wvalid) begin
if (wcount == 8'd0 || s_axi_wlast)
wstate <= W_RESP;
wcount <= wcount - 8'd1;
mem_waddr <= mem_waddr + (MEM_AWIDTH)'(1);
end
W_RESP :
if (s_axi_bready)
wstate <= W_IDLE;
default :
wstate <= W_IDLE;
endcase
end
// ---------------------------------------------------------------------------------------
// a block RAM
// ---------------------------------------------------------------------------------------
reg [AXI_DWIDTH-1:0] mem [ 1<<MEM_AWIDTH ]; // 这是一个二维数组
always @ (posedge clk)
s_axi_rdata <= mem[mem_raddr]; // 一次读出 AXI_DWIDTH 位的数据
always @ (posedge clk)
if (s_axi_wvalid & s_axi_wready)
for (int i=0; i<(AXI_DWIDTH/8); i++)
if (s_axi_wstrb[i])
mem[mem_waddr][i*8+:8] <= s_axi_wdata[i*8+:8];
endmodule