snitch_axi_adapter.sv 8.53 KB
Newer Older
sakundu committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
// Copyright 2018-2019 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// File:   axi_adapter.sv
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
// Date:   1.8.2018
//
// Description: Manages communication with the AXI Bus

module snitch_axi_adapter #(
  parameter int unsigned WriteFIFODepth = 2,
  parameter int unsigned ReadFIFODepth = 2,
  parameter type addr_t = logic,
  parameter type data_t = logic,
  parameter type strb_t = logic,
  parameter type axi_mst_req_t  = logic,
  parameter type axi_mst_resp_t = logic
) (
  input  logic          clk_i,
  input  logic          rst_ni,
  // AXI port
  input  axi_mst_resp_t axi_resp_i,
  output axi_mst_req_t  axi_req_o,

  input  addr_t         slv_qaddr_i,
  input  logic          slv_qwrite_i,
  input  logic [3:0]    slv_qamo_i,
  input  data_t         slv_qdata_i,
  input  logic [2:0]    slv_qsize_i,
  input  strb_t         slv_qstrb_i,
  input  logic [7:0]    slv_qrlen_i,
  input  logic          slv_qvalid_i,
  output logic          slv_qready_o,
  output data_t         slv_pdata_o,
  output logic          slv_perror_o,
  output logic          slv_plast_o,
  output logic          slv_pvalid_o,
  input  logic          slv_pready_i
);

  localparam DataWidth     = $bits(data_t);
  localparam StrbWidth     = $bits(strb_t);
  localparam SlvByteOffset = $clog2($bits(strb_t));
  localparam AxiByteOffset = $clog2($bits(axi_req_o.w.strb));

  typedef enum logic [3:0] {
    AMONone = 4'h0,
    AMOSwap = 4'h1,
    AMOAdd  = 4'h2,
    AMOAnd  = 4'h3,
    AMOOr   = 4'h4,
    AMOXor  = 4'h5,
    AMOMax  = 4'h6,
    AMOMaxu = 4'h7,
    AMOMin  = 4'h8,
    AMOMinu = 4'h9,
    AMOLR   = 4'hA,
    AMOSC   = 4'hB
  } amo_op_t;

  typedef struct packed {
    data_t data;
    strb_t strb;
  } write_t;

  logic   write_full;
  logic   write_empty;
  logic   read_full;
  write_t write_data_in;
  write_t write_data_out;

  assign axi_req_o.aw.addr   = slv_qaddr_i;
  assign axi_req_o.aw.prot   = 3'b0;
  assign axi_req_o.aw.region = 4'b0;
  assign axi_req_o.aw.size   = slv_qsize_i;
  assign axi_req_o.aw.len    = '0;
  assign axi_req_o.aw.burst  = axi_pkg::BURST_INCR;
  assign axi_req_o.aw.lock   = 1'b0;
  assign axi_req_o.aw.cache  = axi_pkg::CACHE_MODIFIABLE;
  assign axi_req_o.aw.qos    = 4'b0;
  assign axi_req_o.aw.id     = '0;
  assign axi_req_o.aw.user   = '0;
  assign axi_req_o.aw_valid  = ~write_full & slv_qvalid_i & slv_qwrite_i;

  always_comb begin
    write_data_in.data = slv_qdata_i;
    write_data_in.strb = slv_qstrb_i;
    unique case (amo_op_t'(slv_qamo_i))
      // RISC-V atops have a load semantic
      AMOSwap: axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ATOMICSWAP};
      AMOAdd:  axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_ADD};
      AMOAnd:  begin
        // in this case we need to invert the data to get a "CLR"
        write_data_in.data = ~slv_qdata_i;
        axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_CLR};
      end
      AMOOr:   axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SET};
      AMOXor:  axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_EOR};
      AMOMax:  axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMAX};
      AMOMaxu: axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMAX};
      AMOMin:  axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_SMIN};
      AMOMinu: axi_req_o.aw.atop  = {axi_pkg::ATOP_ATOMICLOAD, axi_pkg::ATOP_LITTLE_END, axi_pkg::ATOP_UMIN};
      default: axi_req_o.aw.atop = '0;
    endcase
  end

  localparam int unsigned ShiftWidth = (SlvByteOffset == AxiByteOffset) ? 1 : AxiByteOffset - SlvByteOffset;
  typedef logic [ShiftWidth-1:0] shift_t;
  typedef struct packed {
    write_t data;
    shift_t shift;
  } write_ext_t;

  if (SlvByteOffset == AxiByteOffset) begin : gen_w_data
    // Write
    fifo_v3 #(
      .DEPTH      ( WriteFIFODepth                             ),
      .dtype      ( write_t                                    )
    ) i_fifo_w_data (
      .clk_i,
      .rst_ni,
      .flush_i    ( 1'b0                                       ),
      .testmode_i ( 1'b0                                       ),
      .full_o     ( write_full                                 ),
      .empty_o    ( write_empty                                ),
      .usage_o    ( /* NC */                                   ),
      .data_i     ( write_data_in                              ),
      .push_i     ( slv_qvalid_i & slv_qready_o & slv_qwrite_i ),
      .data_o     ( write_data_out                             ),
      .pop_i      ( axi_req_o.w_valid & axi_resp_i.w_ready     )
    );
    assign axi_req_o.w.data = write_data_out.data;
    assign axi_req_o.w.strb = write_data_out.strb;

    // Read
    assign read_full   = 1'b0;
    assign slv_pdata_o = axi_resp_i.r.data;
  end else begin  : gen_w_data
    // Write
    write_ext_t write_data_ext_in, write_data_ext_out;

    fifo_v3 #(
      .DEPTH      ( WriteFIFODepth                             ),
      .dtype      ( write_ext_t                                )
    ) i_fifo_w_data (
      .clk_i,
      .rst_ni,
      .flush_i    ( 1'b0                                       ),
      .testmode_i ( 1'b0                                       ),
      .full_o     ( write_full                                 ),
      .empty_o    ( write_empty                                ),
      .usage_o    ( /* NC */                                   ),
      .data_i     ( write_data_ext_in                          ),
      .push_i     ( slv_qvalid_i & slv_qready_o & slv_qwrite_i ),
      .data_o     ( write_data_ext_out                         ),
      .pop_i      ( axi_req_o.w_valid & axi_resp_i.w_ready     )
    );

    assign write_data_ext_in.data  = write_data_in;
    assign write_data_ext_in.shift = slv_qaddr_i[AxiByteOffset-1:SlvByteOffset];
    assign axi_req_o.w.data  = {'0, write_data_ext_out.data.data} << ($bits(data_t) * write_data_ext_out.shift);
    assign axi_req_o.w.strb  = {'0, write_data_ext_out.data.strb} << ($bits(strb_t) * write_data_ext_out.shift);

    // Read
    shift_t read_shift;

    fifo_v3 #(
      .DEPTH      ( ReadFIFODepth                               ),
      .DATA_WIDTH ( AxiByteOffset-SlvByteOffset                 )
    ) i_fifo_r_shift (
      .clk_i,
      .rst_ni,
      .flush_i    ( 1'b0                                        ),
      .testmode_i ( 1'b0                                        ),
      .full_o     ( read_full                                   ),
      .empty_o    ( /* NC */                                    ),
      .usage_o    ( /* NC */                                    ),
      .data_i     ( slv_qaddr_i[AxiByteOffset-1:SlvByteOffset]  ),
      .push_i     ( slv_qvalid_i & slv_qready_o & ~slv_qwrite_i ),
      .data_o     ( read_shift                                  ),
      .pop_i      ( axi_resp_i.r_valid & slv_pready_i           )
    );

    assign slv_pdata_o       = axi_resp_i.r.data >> ($bits(data_t) * read_shift);
  end
  assign axi_req_o.w.last    = 1'b1;
  assign axi_req_o.w.user    = '0;
  assign axi_req_o.w_valid   = ~write_empty;

  assign axi_req_o.b_ready   = 1'b1;

  assign axi_req_o.ar.addr   = slv_qaddr_i;
  assign axi_req_o.ar.prot   = 3'b0;
  assign axi_req_o.ar.region = 4'b0;
  assign axi_req_o.ar.size   = slv_qsize_i;
  assign axi_req_o.ar.len    = slv_qrlen_i;
  assign axi_req_o.ar.burst  = axi_pkg::BURST_INCR;
  assign axi_req_o.ar.lock   = 1'b0;
  assign axi_req_o.ar.cache  = axi_pkg::CACHE_MODIFIABLE;
  assign axi_req_o.ar.qos    = 4'b0;
  assign axi_req_o.ar.id     = '0;
  assign axi_req_o.ar.user   = '0;
  assign axi_req_o.ar_valid  = ~read_full & slv_qvalid_i & ~slv_qwrite_i;

  assign slv_perror_o      = (axi_resp_i.r.resp inside {axi_pkg::RESP_EXOKAY, axi_pkg::RESP_OKAY}) ? 1'b0 : 1'b1;
  assign slv_plast_o       = axi_resp_i.r.last;
  assign slv_pvalid_o      = axi_resp_i.r_valid;
  assign axi_req_o.r_ready = slv_pready_i;

  assign slv_qready_o = (axi_resp_i.ar_ready & axi_req_o.ar_valid)
                      | (axi_resp_i.aw_ready & axi_req_o.aw_valid);

  `ifndef VERILATOR
  // pragma translate_off
  hot_one : assert property (
    @(posedge clk_i) disable iff (!rst_ni) (slv_qvalid_i & slv_qwrite_i & slv_qready_o) |-> (slv_qrlen_i == 0))
      else $warning("Bursts are not supported for write transactions");
  // pragma translate_on
  `endif
endmodule