| // 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)), | 
 |       .EnReqStabA (0) | 
 |     ) u_reqarb ( | 
 |       .clk_i, | 
 |       .rst_ni, | 
 |       .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)), | 
 |       .EnReqStabA (0) | 
 |     ) u_reqarb ( | 
 |       .clk_i, | 
 |       .rst_ni, | 
 |       .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 |