AXI4 FULL SLAVE的Verilog实现(二)


前文是 AXI4 FULL SLAVE的Verilog实现(一)。这篇文章在之前的基础上作了一些改进。

参考官方IP之后的改进思路

如果主机发送不合理信号怎么办? 我们的从机必须要有一定的鲁棒性,考虑到之前在配置BRAM的时候,有一个busy信号我们不清楚它的用途(详见Vivado Block Memory Generator v8.4学习总结),结合官方IP的代码,我们发现这两个变量*可以用于指示slave是否正在读写*。如果slave正在读写,那么master即使发送ARVALID,也不会导致arready的拉高。

代码及改进效果

`timescale 1ns / 1ps

module noFSM_axi4_slave
/*
#(
   parameter integer DATA_BUS_WIDTH = 32,
   parameter integer ID_WIDTH = 4,
   parameter integer ADDR_WIDTH = 32
)
*/
(
  //global sig
  input wire aclk,
  input wire arstn,
  //aw channel
  input wire [3:0] awid,
  input wire [31:0] awaddr,
  input wire [7:0] awlen,
  input wire [2:0] awsize,
  input wire [1:0] awburst,
  input wire awvalid,
  output reg awready,
  //w channel
  input wire [31:0] wdata,
  input wire [3:0] wstrb,
  input wire wlast,
  input wire wvalid,
  output reg wready,
  //b channel
  input wire bready,
  output reg [3:0] bid,
  output reg [1:0] bresp,
  output reg bvalid,
  //ar channel
  input wire [3:0] arid,
  input wire [31:0] araddr,
  input wire [7:0] arlen,
  input wire [2:0] arsize,
  input wire [1:0] arburst,
  input wire arvalid,
  output reg arready,
  //r channel
  input wire rready,
  output reg [3:0] rid,
  output reg [31:0] rdata,
  output reg [1:0] rresp,
  output reg rlast,
  output reg rvalid,
  //busy sig
  output reg is_writing,
  output reg is_reading
 );
 
 //new idea: these 5 channels are seperated, so we just need to check the signals every clock cycle
 //to know what every channel is doing, and generate signals and operations. 
 //difficulties: some signals have dependency on others; read and write process are quite annoying in AXI4-FULL; 
 //unexpcted situations should be considered
 
 //RAM
 reg [31:0] mem [0:1023];

 //addr reg and temp reg
 reg [31:0] waddr_reg;
 reg [31:0] start_waddr;
 reg [31:0] raddr_reg;
 reg [31:0] start_raddr;
 reg [7:0] rcnt;
 reg [31:0] WWRAP_Boundary;
 reg [31:0] RWRAP_Boundary;

 //para reg
 reg [7:0] AWLEN;
 reg [7:0] ARLEN;
 reg [2:0] AWSIZE;
 reg [2:0] ARSIZE;
 reg [3:0] AWID;
 reg [3:0] ARID;
 reg [3:0] WSTRB;
 
 //initializing the mem
 /*
 always @(posedge aclk) begin
   if (!arstn) begin
     mem[0] <= 32'h33221100;
     mem[1] <= 32'h77665544;
     mem[2] <= 32'hBBAA9988;
     mem[3] <= 32'hFFEEDDCC;
     mem[4] <= 32'h00112233;
     mem[5] <= 32'h44556677;
     mem[6] <= 32'h8899AABB;
     mem[7] <= 32'hCCDDEEFF;
   end
 end
 */
 //write addr channel
 always @(posedge aclk) begin
    if (!arstn) begin
      awready <= 1'b0;
      waddr_reg <= 32'hFFFFFFFF;
      is_writing <= 1'b0;
    end else if (!awready && awvalid == 1'b1 && !is_writing && !is_reading) begin
      awready <= 1'b1;
      is_writing <= 1'b1;
    end else
      awready <= 1'b0;
 end
 //para transport
always @(posedge aclk) begin
    if (!arstn) begin
        AWLEN <= 8'b0;
        AWSIZE <= 3'b0;
        waddr_reg <= 32'b0;
    end else if(awvalid == 1'b1 && !is_writing) begin
        AWLEN <= awlen;
        AWSIZE <= awsize;
        AWID <= awid;
        WSTRB <= wstrb;
        waddr_reg <= (awaddr >> AWSIZE) << AWSIZE;//align the address
        start_waddr <= awaddr;
    end
end

 //write data channel
 always @(posedge aclk) begin
    if (!arstn)
      wready <= 1'b0;
    else if (awvalid == 1'b1 && awready == 1'b1) begin
      wready <= 1'b1;
      awready <= 1'b0;
    end else if (wlast == 1'b1) begin
      wready <= 1'b0;
      is_writing <= 1'b0;
    end
 end
  //write to the memory
 always @(posedge aclk) begin
    if (!arstn) begin
      wready <= 1'b0;
      waddr_reg <= 32'h0;
    end else if (wready == 1'b1 && wvalid == 1'b1) begin
      bid <= AWID;
      WSTRB <= wstrb;
      //calculate where to write and update the address register according to the type
      WWRAP_Boundary <= (start_waddr >> (AWLEN * 2**(AWSIZE))) << (AWLEN * 2**(AWSIZE));
      //calculate next addr
      case (awburst)
        2'b00 : begin
          //FIXED
          waddr_reg <= waddr_reg;
        end 
        2'b01 : begin
          //INCR
          if (!wlast) begin
            waddr_reg <= waddr_reg + 2**(AWSIZE);
          end
        end
        2'b10 : begin
          //WRAP
          if (!wlast) begin
            if (waddr_reg >= WWRAP_Boundary + AWLEN * 2**(AWSIZE)) begin
              waddr_reg <= WWRAP_Boundary;
            end else begin
              waddr_reg <= waddr_reg + 2**(AWSIZE);
            end
          end
        end
        //default: ;
      endcase
      if (WSTRB[0] == 1'b1) begin
        mem[waddr_reg[31:2]][7:0] <= wdata[7:0];
      end
      if (WSTRB[1] == 1'b1) begin
        mem[waddr_reg[31:2]][15:8] <= wdata[15:8];
      end
      if (WSTRB[2] == 1'b1) begin
        mem[waddr_reg[31:2]][23:16] <= wdata[23:16];
      end
      if (WSTRB[3] == 1'b1) begin
        mem[waddr_reg[31:2]][31:24] <= wdata[31:24];
      end
    end
 end

 //write response channel
 always @(posedge aclk) begin
    if (!arstn) begin
      bvalid <= 1'b0;
      bresp <= 2'b00;
    end else if (wlast == 1'b1 && wready == 1'b1 && wvalid == 1'b1) begin
      bvalid <= 1'b1;
      bresp <= 2'b00;
    end else if (bvalid == 1'b1 && bready == 1'b1) begin
      bvalid <= 1'b0;
    end
 end

 //read addr channel
 always @(posedge aclk) begin
    if (!arstn) begin
      arready <= 1'b0;
      is_reading <=1'b0;
    end else if (arvalid == 1'b1 && !arready && !is_writing && !is_reading) begin
      arready <= 1'b1;
      is_reading <=1'b1;
    end else
      arready <= 1'b0;
 end
  //para transport
 always @(posedge aclk) begin
    if (!arstn) begin
        ARLEN <= 8'b0;
        ARSIZE <= 3'b0;
        raddr_reg <= 32'hFFFFFFFF;
    end else if (arvalid == 1'b1 && !is_reading) begin
        ARLEN <= arlen;
        ARSIZE <= arsize;
        ARID <= arid;
        raddr_reg <= araddr;
        start_raddr <= araddr;
    end
 end

 //read data channel
 always @(posedge aclk) begin
    if (!arstn)
      rvalid <= 1'b0;
    else if (arready == 1'b1 && arvalid == 1'b1) begin
      rvalid <= 1'b1;
      arready <= 1'b0;
      rid <= ARID;
    end
 end
  //caculate address
 always @(posedge aclk) begin
  if (!arstn) begin
    rvalid <= 1'b0;
    rresp <= 2'b00;
  end else if(rvalid == 1'b1 && rready == 1'b1) begin
    rresp <= 2'b00;
    RWRAP_Boundary <= (start_raddr >> (ARLEN * 2**(ARSIZE))) << (ARLEN * 2**(ARSIZE));
    raddr_reg <= (raddr_reg >> ARSIZE) << ARSIZE;//align the address with byte
    case (arburst)
      2'b00 : begin
        raddr_reg <= raddr_reg;
      end
      2'b01 : begin
        if (!rlast) begin
          raddr_reg <= raddr_reg + 2**(ARSIZE);
        end
      end
      2'b10 : begin
        if (raddr_reg <= araddr + ARLEN * 2**(ARSIZE)) begin
          raddr_reg <= raddr_reg + 2**(ARSIZE);
        end else begin
          raddr_reg <= RWRAP_Boundary;
        end
      end
      //default: 
    endcase
  end
 end
 //data out
 always @(posedge aclk) begin
  if (!arstn) begin
    rdata <= 32'h0;
    rcnt <= 8'd0;
    rlast <= 1'b0;
  end else if (rvalid == 1'b1 && rready == 1'b1) begin
    rdata <= mem[raddr_reg[31:2]];
    if (rcnt < ARLEN) begin
      rcnt <= rcnt + 8'd1;
    end else begin
      rlast <= 1'b1;
      rcnt <= 8'b0;
    end
  end
 end
 always @(posedge aclk) begin
  if (!arstn) begin
    rlast <= 1'b0;
  end else if (rlast == 1'b1) begin
    rlast <= 1'b0;
    rvalid <= 1'b0;
    is_reading <= 1'b0;
  end
 end
 
endmodule

通过is_writing和is_reading这两个变量的指示,判断slave是否能够接收下一个地址。
仿真结果如下所示:
在这里插入图片描述
按照之前的逻辑,黄线处本应拉高arready,但由于此时正在读写,如果此时参数发生变化,则会导致错误;加入状态指示变量之后,即使主机发送了不合理的ARVALID,arready信号也不会拉高,避免了类似的错误,提高了鲁棒性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值