stream_to_mem.sv 5.08 KB
Newer Older
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
// Copyright 2020 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

// Authors:
// - Andreas Kurth <akurth@iis.ee.ethz.ch>

/// `stream_to_mem`: Allows to use memories with flow control (`valid`/`ready`) for requests but without flow
/// control for output data to be used in streams.
`include "common_cells/registers.svh"
module stream_to_mem #(
  /// Memory request payload type, usually write enable, write data, etc.
  parameter type         mem_req_t  = logic,
  /// Memory response payload type, usually read data
  parameter type         mem_resp_t = logic,
  /// Number of buffered responses (fall-through, thus no additional latency).  This defines the
  /// maximum number of outstanding requests on the memory interface. If the attached memory
  /// responds in the same cycle a request is applied, this MUST be 0. If the attached memory
  /// responds at least one cycle after a request, this MUST be >= 1 and should be equal to the
  /// response latency of the memory to saturate bandwidth.
  parameter int unsigned BufDepth   = 32'd1
) (
  /// Clock
  input  logic      clk_i,
  /// Asynchronous reset, active low
  input  logic      rst_ni,
  /// Request stream interface, payload
  input  mem_req_t  req_i,
  /// Request stream interface, payload is valid for transfer
  input  logic      req_valid_i,
  /// Request stream interface, payload can be accepted
  output logic      req_ready_o,
  /// Response stream interface, payload
  output mem_resp_t resp_o,
  /// Response stream interface, payload is valid for transfer
  output logic      resp_valid_o,
  /// Response stream interface, payload can be accepted
  input  logic      resp_ready_i,
  /// Memory request interface, payload
  output mem_req_t  mem_req_o,
  /// Memory request interface, payload is valid for transfer
  output logic      mem_req_valid_o,
  /// Memory request interface, payload can be accepted
  input  logic      mem_req_ready_i,
  /// Memory response interface, payload
  input  mem_resp_t mem_resp_i,
  /// Memory response interface, payload is valid
  input  logic      mem_resp_valid_i
);

  typedef logic [$clog2(BufDepth+1):0] cnt_t;

  cnt_t cnt_d, cnt_q;
  logic buf_ready,
        req_ready;

  if (BufDepth > 0) begin : gen_buf
    // Count number of outstanding requests.
    always_comb begin
      cnt_d = cnt_q;
      if (req_valid_i && req_ready_o) begin
        cnt_d++;
      end
      if (resp_valid_o && resp_ready_i) begin
        cnt_d--;
      end
    end

    // Can issue another request if the counter is not at its limit or a response is delivered in
    // the current cycle.
    assign req_ready = (cnt_q < BufDepth) | (resp_valid_o & resp_ready_i);

    // Control request and memory request interface handshakes.
    assign req_ready_o = mem_req_ready_i & req_ready;
    assign mem_req_valid_o = req_valid_i & req_ready;

    // Buffer responses.
    stream_fifo #(
      .FALL_THROUGH ( 1'b1       ),
      .DEPTH        ( BufDepth   ),
      .T            ( mem_resp_t )
    ) i_resp_buf (
      .clk_i,
      .rst_ni,
      .flush_i    ( 1'b0             ),
      .testmode_i ( 1'b0             ),
      .data_i     ( mem_resp_i       ),
      .valid_i    ( mem_resp_valid_i ),
      .ready_o    ( buf_ready        ),
      .data_o     ( resp_o           ),
      .valid_o    ( resp_valid_o     ),
      .ready_i    ( resp_ready_i     ),
      .usage_o    ( /* unused */     )
    );

    // Register
    `FFARN(cnt_q, cnt_d, '0, clk_i, rst_ni)

  end else begin : gen_no_buf
    // Control request, memory request, and response interface handshakes.
    assign mem_req_valid_o = req_valid_i;
    assign resp_valid_o    = mem_req_valid_o & mem_req_ready_i & mem_resp_valid_i;
    assign req_ready_o     = resp_ready_i    & resp_valid_o;

    // Forward responses.
    assign resp_o = mem_resp_i;
  end

  // Forward requests.
  assign mem_req_o = req_i;

// Assertions
// pragma translate_off
`ifndef VERILATOR
  if (BufDepth > 0) begin : gen_buf_asserts
    assert property (@(posedge clk_i) mem_resp_valid_i |-> buf_ready)
      else $error("Memory response lost!");
    assert property (@(posedge clk_i) cnt_q == '0 |=> cnt_q != '1)
      else $error("Counter underflowed!");
    assert property (@(posedge clk_i) cnt_q == BufDepth |=> cnt_q != BufDepth + 1)
      else $error("Counter overflowed!");
  end else begin : gen_no_buf_asserts
    assume property (@(posedge clk_i) mem_req_valid_o & mem_req_ready_i |-> mem_resp_valid_i)
      else $error("Without BufDepth = 0, the memory must respond in the same cycle!");
  end
`endif
// pragma translate_on
endmodule