blob: c6dd6eda885e588a6c900563f29031b5f2678186 [file] [log] [blame]
// 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'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 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