FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

common_cells リポジトリに含まれる stream_モジュールの構成分析 (7. ストリーム・インタフェースの変換モジュール)

stream_to_mem.sv

フロー制御付きのリクエストを持つが、出力データにフロー制御がないメモリインターフェースを、ストリームインターフェースで使用できるように変換するモジュールである。

/// `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.
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
`ifndef COMMON_CELLS_ASSERTS_OFF
  if (BufDepth > 0) begin : gen_buf_asserts
    `ASSERT(memory_response_lost, mem_resp_valid_i |-> buf_ready, clk_i, !rst_ni,
            "Memory response lost!")
    `ASSERT(counter_underflowed, cnt_q == '0 |=> cnt_q != '1, clk_i, !rst_ni,
            "Counter underflowed!")
    `ASSERT(counter_overflowed, cnt_q == BufDepth |=> cnt_q != BufDepth + 1, clk_i, !rst_ni,
            "Counter overflowed!")
  end else begin : gen_no_buf_asserts
    `ASSUME(no_memory_response, mem_req_valid_o & mem_req_ready_i |-> mem_resp_valid_i,
            clk_i, !rst_ni, "Without BufDepth = 0, the memory must respond in the same cycle!")
  end
`endif
endmodule

BufDepthが0の場合は、メモリが同じサイクルで応答することを前提とする。 BufDepthが1以上の場合は、stream_fifoを使用してレスポンスをバッファリングし、outstandingリクエスト数をカウントする。