
module axi_bram #(
    parameter AXI_IDWIDTH = 4,  // AXI的id宽度
    parameter AXI_AWIDTH  = 64,  // AXI的 地址总线宽度
    parameter AXI_DWIDTH  = 256,  // AXI的数据总线宽度 一次读取的数据位数, 每次数据传输的bit位宽
    parameter MEM_AWIDTH  = 12      // 内存二维数组的长度len的宽度 也就是索引一共 1<<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,  // 从S 写地址准备好接收了
    input  wire                        s_axi_awvalid,  // 主准备好
    input  wire   [    AXI_AWIDTH-1:0] s_axi_awaddr,   // 传入的地址, 注意这个地址是bit, 后面计算出来通过右移得到二维数组中的索引
    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 已经可以接受 M读地址信号
    input  wire                        s_axi_arvalid,  // 表示M 地址已经准备就绪
    input  wire   [    AXI_AWIDTH-1:0] s_axi_araddr,   // M 发送的地址, 这是个bit的地址, 后面通过右移计算得到索引
    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;  // 直接连线, 反馈给 M
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));  // 计算地址, 字节对齐     //不太懂这个为啥除以8
              // 因为每次数据都会 传256位数据, 这里除以8, 得到需要传的次数, 每次1字节传输, 256/8就是32次
              // 看能整除多少次2, 然后 s_axi_araddr 进行右移, 移动到传过来的地址的 AXI_DWIDTH的字节边界 start起始的位置
              // 将AXI_DWIDTH除以8的原因是为了从字节（byte）粒度转换到数据宽度的粒度。在很多基于AXI协议的系统中，地址通常是按字节对齐的，而数据宽度（AXI_DWIDTH）可能不是8的整数倍，比如可能是16位、32位、64位等

    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 ];  // 这是一个二维数组, 数组中每个元素为 AXI_DWIDTH 宽, 数组长度为 MEM_AWIDTH

always @ (posedge clk)
    s_axi_rdata <= mem[mem_raddr];  // 一次读出索引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];  // 每次根据地址写入8bit
    
endmodule


