|  | // Copyright lowRISC contributors. | 
|  | // 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 #( | 
|  | 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'h2}}, | 
|  | parameter bit [M*4-1:0] HRspDepth = {M{4'h2}}, | 
|  | parameter bit           DReqPass  = 1'b1, | 
|  | parameter bit           DRspPass  = 1'b1, | 
|  | parameter bit [3:0]     DReqDepth = 4'h2, | 
|  | parameter bit [3:0]     DRspDepth = 4'h2 | 
|  | ) ( | 
|  | input                     clk_i, | 
|  | input                     rst_ni, | 
|  |  | 
|  | input  tlul_pkg::tl_h2d_t tl_h_i [M], | 
|  | output tlul_pkg::tl_d2h_t tl_h_o [M], | 
|  |  | 
|  | output tlul_pkg::tl_h2d_t tl_d_o, | 
|  | input  tlul_pkg::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); | 
|  |  | 
|  | tlul_pkg::tl_h2d_t hreq_fifo_o [M]; | 
|  | tlul_pkg::tl_d2h_t hrsp_fifo_i [M]; | 
|  |  | 
|  | logic [M-1:0] hrequest; | 
|  | logic [M-1:0] hgrant; | 
|  |  | 
|  | tlul_pkg::tl_h2d_t dreq_fifo_i; | 
|  | tlul_pkg::tl_d2h_t drsp_fifo_o; | 
|  |  | 
|  | logic arb_valid; | 
|  | logic arb_ready; | 
|  | tlul_pkg::tl_h2d_t arb_data; | 
|  |  | 
|  | // Host Req/Rsp FIFO | 
|  | for (genvar i = 0 ; i < M ; i++) begin : gen_host_fifo | 
|  | tlul_pkg::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 #( | 
|  | .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 #( | 
|  | .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 (tlul_pkg::ArbiterImpl == "PPC") begin : gen_arb_ppc | 
|  | prim_arbiter_ppc #( | 
|  | .N          (M), | 
|  | .DW         ($bits(tlul_pkg::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 (tlul_pkg::ArbiterImpl == "BINTREE") begin : gen_tree_arb | 
|  | prim_arbiter_tree #( | 
|  | .N          (M), | 
|  | .DW         ($bits(tlul_pkg::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 |