| // Copyright lowRISC contributors (OpenTitan project). |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| // TL-UL socket M:1 module |
| // |
| // Verilog parameters |
| // M: Number of host ports. |
| // HReqPass: M bit array to allow requests to pass through the host i |
| // FIFO with no clock delay if the request FIFO is empty. If |
| // 1'b0, at least one clock cycle of latency is created. |
| // Default is 1'b1. |
| // HRspPass: Same as HReqPass but for host response FIFO. |
| // HReqDepth: Mx4 bit array. bit[i*4+:4] is depth of host i request FIFO. |
| // Depth of zero is allowed if ReqPass is true. A maximum value |
| // of 16 is allowed, default is 2. |
| // HRspDepth: Same as HReqDepth but for host response FIFO. |
| // DReqPass: Same as HReqPass but for device request FIFO. |
| // DRspPass: Same as HReqPass but for device response FIFO. |
| // DReqDepth: Same as HReqDepth but for device request FIFO. |
| // DRspDepth: Same as HReqDepth but for device response FIFO. |
| |
| `include "prim_assert.sv" |
| |
| module tlul_socket_m1_128 |
| #(parameter int unsigned M = 4, |
| parameter bit [M - 1 : 0] HReqPass = {M{1'b1}}, |
| parameter bit [M - 1 : 0] HRspPass = {M{1'b1}}, |
| parameter bit [M * 4 - 1 : 0] HReqDepth = {M{4'h1}}, |
| parameter bit [M * 4 - 1 : 0] HRspDepth = {M{4'h1}}, |
| parameter bit DReqPass = 1'b1, |
| parameter bit DRspPass = 1'b1, |
| parameter bit [3 : 0] DReqDepth = 4'h1, |
| parameter bit [3 : 0] DRspDepth = 4'h1) |
| (input clk_i, |
| input rst_ni, |
| |
| input kelvin_tlul_pkg_128::tl_h2d_t tl_h_i[M], |
| output kelvin_tlul_pkg_128::tl_d2h_t tl_h_o[M], |
| |
| output kelvin_tlul_pkg_128::tl_h2d_t tl_d_o, |
| input kelvin_tlul_pkg_128::tl_d2h_t tl_d_i); |
| |
| `ASSERT_INIT(maxM, M < 16) |
| |
| // Signals |
| // |
| // tl_h_i/o[0] | tl_h_i/o[1] | ... | tl_h_i/o[M-1] |
| // | | | |
| // u_hostfifo[0] u_hostfifo[1] u_hostfifo[M-1] |
| // | | | |
| // hreq_fifo_o(i) / hrsp_fifo_i(i) |
| // --------------------------------------- |
| // | request/grant/req_data | |
| // | | |
| // | PRIM_ARBITER | |
| // | | |
| // | arb_valid / arb_ready / arb_data | |
| // --------------------------------------- |
| // | |
| // dreq_fifo_i / drsp_fifo_o |
| // | |
| // u_devicefifo |
| // | |
| // tl_d_o/i |
| // |
| // Required ID width to distinguish between host ports |
| // Used in response steering |
| localparam int unsigned IDW = top_pkg::TL_AIW; |
| localparam int unsigned STIDW = $clog2(M); |
| |
| kelvin_tlul_pkg_128::tl_h2d_t hreq_fifo_o[M]; |
| kelvin_tlul_pkg_128::tl_d2h_t hrsp_fifo_i[M]; |
| |
| logic [M - 1 : 0] hrequest; |
| logic [M - 1 : 0] hgrant; |
| |
| kelvin_tlul_pkg_128::tl_h2d_t dreq_fifo_i; |
| kelvin_tlul_pkg_128::tl_d2h_t drsp_fifo_o; |
| |
| logic arb_valid; |
| logic arb_ready; |
| kelvin_tlul_pkg_128::tl_h2d_t arb_data; |
| |
| // Host Req/Rsp FIFO |
| for (genvar i = 0; i < M; i++) begin : gen_host_fifo |
| kelvin_tlul_pkg_128::tl_h2d_t hreq_fifo_i; |
| |
| // ID Shifting |
| logic [STIDW - 1 : 0] reqid_sub; |
| logic [IDW - 1 : 0] shifted_id; |
| assign reqid_sub = i; // can cause conversion error? |
| assign shifted_id = {tl_h_i[i].a_source[0 +: (IDW - STIDW)], reqid_sub}; |
| |
| `ASSERT(idInRange, tl_h_i[i].a_valid |-> |
| tl_h_i[i].a_source[IDW - 1 -: STIDW] == '0) |
| |
| // assign not connected bits to nc_* signal to make lint happy |
| logic [IDW - 1 : IDW - STIDW] unused_tl_h_source; |
| assign unused_tl_h_source = tl_h_i[i].a_source[IDW - 1 -: STIDW]; |
| |
| // Put shifted ID |
| assign hreq_fifo_i = '{ |
| a_valid: tl_h_i[i].a_valid, |
| a_opcode: tl_h_i[i].a_opcode, |
| a_param: tl_h_i[i].a_param, |
| a_size: tl_h_i[i].a_size, |
| a_source: shifted_id, |
| a_address: tl_h_i[i].a_address, |
| a_mask: tl_h_i[i].a_mask, |
| a_data: tl_h_i[i].a_data, |
| a_user: tl_h_i[i].a_user, |
| d_ready: tl_h_i[i].d_ready |
| }; |
| |
| tlul_fifo_sync_128 #(.ReqPass(HReqPass[i]), |
| .RspPass(HRspPass[i]), |
| .ReqDepth(HReqDepth[i * 4 +: 4]), |
| .RspDepth(HRspDepth[i * 4 +: 4]), |
| .SpareReqW(1)) |
| u_hostfifo(.clk_i, |
| .rst_ni, |
| .tl_h_i(hreq_fifo_i), |
| .tl_h_o(tl_h_o[i]), |
| .tl_d_o(hreq_fifo_o[i]), |
| .tl_d_i(hrsp_fifo_i[i]), |
| .spare_req_i(1'b0), |
| .spare_req_o(), |
| .spare_rsp_i(1'b0), |
| .spare_rsp_o()); |
| end |
| |
| // Device Req/Rsp FIFO |
| tlul_fifo_sync_128 #(.ReqPass(DReqPass), |
| .RspPass(DRspPass), |
| .ReqDepth(DReqDepth), |
| .RspDepth(DRspDepth), |
| .SpareReqW(1)) |
| u_devicefifo(.clk_i, |
| .rst_ni, |
| .tl_h_i(dreq_fifo_i), |
| .tl_h_o(drsp_fifo_o), |
| .tl_d_o(tl_d_o), |
| .tl_d_i(tl_d_i), |
| .spare_req_i(1'b0), |
| .spare_req_o(), |
| .spare_rsp_i(1'b0), |
| .spare_rsp_o()); |
| |
| // Request Arbiter |
| for (genvar i = 0; i < M; i++) begin : gen_arbreqgnt |
| assign hrequest[i] = hreq_fifo_o[i].a_valid; |
| end |
| |
| assign arb_ready = drsp_fifo_o.a_ready; |
| |
| if (kelvin_tlul_pkg_128::ArbiterImpl == "PPC") begin : gen_arb_ppc |
| prim_arbiter_ppc #(.N(M), |
| .DW($bits(kelvin_tlul_pkg_128::tl_h2d_t))) |
| u_reqarb(.clk_i, |
| .rst_ni, |
| .req_chk_i(1'b0), |
| // TL-UL allows dropping valid without ready. See #3354. |
| .req_i(hrequest), |
| .data_i(hreq_fifo_o), |
| .gnt_o(hgrant), |
| .idx_o(), |
| .valid_o(arb_valid), |
| .data_o(arb_data), |
| .ready_i(arb_ready)); |
| end else if (kelvin_tlul_pkg_128::ArbiterImpl == "BINTREE") |
| begin : gen_tree_arb |
| prim_arbiter_tree #(.N(M), |
| .DW($bits(kelvin_tlul_pkg_128::tl_h2d_t))) |
| u_reqarb(.clk_i, |
| .rst_ni, |
| .req_chk_i(1'b0), |
| // TL-UL allows dropping valid without ready. See #3354. |
| .req_i(hrequest), |
| .data_i(hreq_fifo_o), |
| .gnt_o(hgrant), |
| .idx_o(), |
| .valid_o(arb_valid), |
| .data_o(arb_data), |
| .ready_i(arb_ready)); |
| end else begin : gen_unknown |
| `ASSERT_INIT(UnknownArbImpl_A, 0) |
| end |
| |
| logic [M - 1 : 0] hfifo_rspvalid; |
| logic [M - 1 : 0] dfifo_rspready; |
| logic [IDW - 1 : 0] hfifo_rspid; |
| logic dfifo_rspready_merged; |
| |
| // arb_data --> dreq_fifo_i |
| // dreq_fifo_i.hd_rspready <= dfifo_rspready |
| |
| assign dfifo_rspready_merged = |dfifo_rspready; |
| assign dreq_fifo_i = '{ |
| a_valid: arb_valid, |
| a_opcode: arb_data.a_opcode, |
| a_param: arb_data.a_param, |
| a_size: arb_data.a_size, |
| a_source: arb_data.a_source, |
| a_address: arb_data.a_address, |
| a_mask: arb_data.a_mask, |
| a_data: arb_data.a_data, |
| a_user: arb_data.a_user, |
| |
| d_ready: dfifo_rspready_merged |
| }; |
| |
| // Response ID steering |
| // drsp_fifo_o --> hrsp_fifo_i[i] |
| |
| // Response ID shifting before put into host fifo |
| assign hfifo_rspid = {{STIDW{1'b0}}, drsp_fifo_o.d_source[IDW - 1 : STIDW]}; |
| for (genvar i = 0; i < M; i++) begin : gen_idrouting |
| assign hfifo_rspvalid[i] = |
| drsp_fifo_o.d_valid & (drsp_fifo_o.d_source[0 +: STIDW] == i); |
| assign dfifo_rspready[i] = hreq_fifo_o[i].d_ready & |
| (drsp_fifo_o.d_source[0 +: STIDW] == i) & |
| drsp_fifo_o.d_valid; |
| |
| assign hrsp_fifo_i[i] = '{ |
| d_valid: hfifo_rspvalid[i], |
| d_opcode: drsp_fifo_o.d_opcode, |
| d_param: drsp_fifo_o.d_param, |
| d_size: drsp_fifo_o.d_size, |
| d_source: hfifo_rspid, |
| d_sink: drsp_fifo_o.d_sink, |
| d_data: drsp_fifo_o.d_data, |
| d_user: drsp_fifo_o.d_user, |
| d_error: drsp_fifo_o.d_error, |
| a_ready: hgrant[i] |
| }; |
| end |
| |
| // this assertion fails when rspid[0+:STIDW] not in [0..M-1] |
| `ASSERT(rspIdInRange, drsp_fifo_o.d_valid |-> |
| drsp_fifo_o.d_source[0 +: STIDW] >= 0 && |
| drsp_fifo_o.d_source[0 +: STIDW] < M) |
| endmodule |