blob: 8ab6bdb2fd067938014a651d2da4da70de2011cf [file] [log] [blame]
// TL-UL socket 1:N module
//
// configuration settings
// device_count: 4
//
// Verilog parameters
// HReqPass: if 1 then host requests can pass through on empty fifo,
// default 1
// HRspPass: if 1 then host responses can pass through on empty fifo,
// default 1
// DReqPass: (one per device_count) if 1 then device i requests can
// pass through on empty fifo, default 1
// DRspPass: (one per device_count) if 1 then device i responses can
// pass through on empty fifo, default 1
// HReqDepth: Depth of host request FIFO, default 2
// HRspDepth: Depth of host response FIFO, default 2
// DReqDepth: (one per device_count) Depth of device i request FIFO,
// default 2
// DRspDepth: (one per device_count) Depth of device i response FIFO,
// default 2
//
// Requests must stall to one device until all responses from other devices
// have returned. Need to keep a counter of all outstanding requests and
// wait until that counter is zero before switching devices.
//
// This module will return a request error if the input value of 'dev_select_i'
// is not within the range 0..N-1. Thus the instantiator of the socket
// can indicate error by any illegal value of dev_select_i. 4'b1111 is
// recommended for visibility
//
// The maximum value of N is 15
module tlul_socket_1n #(
parameter int unsigned N = 4,
parameter bit HReqPass = 1'b1,
parameter bit HRspPass = 1'b1,
parameter bit [N-1:0] DReqPass = {N{1'b1}},
parameter bit [N-1:0] DRspPass = {N{1'b1}},
parameter bit [3:0] HReqDepth = 4'h2,
parameter bit [3:0] HRspDepth = 4'h2,
parameter bit [N*4-1:0] DReqDepth = {N{4'h2}},
parameter bit [N*4-1:0] DRspDepth = {N{4'h2}},
localparam int unsigned NWD = $clog2(N+1) // derived parameter
) (
input clk_i,
input rst_ni,
input tlul_pkg::tl_h2d_t tl_h_i,
output tlul_pkg::tl_d2h_t tl_h_o,
output tlul_pkg::tl_h2d_t tl_d_o [N],
input tlul_pkg::tl_d2h_t tl_d_i [N],
input [NWD-1:0] dev_select_i
);
// Since our steering is done after potential FIFOing, we need to
// shove our device select bits into spare bits of reqfifo
// instantiate the host fifo, create intermediate bus 't'
// FIFO'd version of device select
logic [NWD-1:0] dev_select_t;
tlul_pkg::tl_h2d_t tl_t_o;
tlul_pkg::tl_d2h_t tl_t_i;
tlul_fifo_sync #(
.ReqPass(HReqPass),
.RspPass(HRspPass),
.ReqDepth(HReqDepth),
.RspDepth(HRspDepth),
.SpareReqW(NWD)
) fifo_h (
.clk_i,
.rst_ni,
.tl_h_i,
.tl_h_o,
.tl_d_o (tl_t_o),
.tl_d_i (tl_t_i),
.spare_req_i (dev_select_i),
.spare_req_o (dev_select_t),
.spare_rsp_i (1'b0),
.spare_rsp_o ());
// We need to keep track of how many requests are outstanding,
// and to which device. New requests are compared to this and
// stall until that number is zero.
localparam int MaxOutstanding = 4**tlul_pkg::TL_AIW; // Up to 256 ounstanding
localparam int OutstandingW = $clog2(MaxOutstanding+1);
logic [OutstandingW-1:0] num_req_outstanding;
logic [NWD-1:0] dev_select_outstanding;
logic hold_all_requests;
logic accept_t_req, accept_t_rsp;
assign accept_t_req = tl_t_o.a_valid & tl_t_i.a_ready;
assign accept_t_rsp = tl_t_i.d_valid & tl_t_o.d_ready;
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
num_req_outstanding <= '0;
dev_select_outstanding <= '0;
end else if (accept_t_req) begin
if (!accept_t_rsp) begin
num_req_outstanding <= num_req_outstanding + 1'b1;
end
dev_select_outstanding <= dev_select_t;
end else if (accept_t_rsp) begin
num_req_outstanding <= num_req_outstanding - 1'b1;
end
end
assign hold_all_requests =
(num_req_outstanding != '0) &
(dev_select_t != dev_select_outstanding);
// Make N copies of 't' request side with modified reqvalid, call
// them 'u[0]' .. 'u[n-1]'.
tlul_pkg::tl_h2d_t tl_u_o [N+1];
tlul_pkg::tl_d2h_t tl_u_i [N+1];
for (genvar i = 0 ; i < N ; i++) begin : gen_u_o
assign tl_u_o[i].a_valid = tl_t_o.a_valid &
(dev_select_t == NWD'(i)) &
~hold_all_requests;
assign tl_u_o[i].a_opcode = tl_t_o.a_opcode;
assign tl_u_o[i].a_param = tl_t_o.a_param;
assign tl_u_o[i].a_size = tl_t_o.a_size;
assign tl_u_o[i].a_source = tl_t_o.a_source;
assign tl_u_o[i].a_address = tl_t_o.a_address;
assign tl_u_o[i].a_mask = tl_t_o.a_mask;
assign tl_u_o[i].a_data = tl_t_o.a_data;
end
tlul_pkg::tl_d2h_t tl_t_p ;
// for the returning reqready, only look at the device we're addressing
logic hfifo_reqready;
always_comb begin
hfifo_reqready = tl_u_i[N].a_ready; // default to error
for (int idx = 0 ; idx < N ; idx++) begin
//if (dev_select_outstanding == NWD'(idx)) hfifo_reqready = tl_u_i[idx].a_ready;
if (dev_select_t == NWD'(idx)) hfifo_reqready = tl_u_i[idx].a_ready;
end
if (hold_all_requests) hfifo_reqready = 1'b0;
end
// Adding a_valid as a qualifier. This prevents the a_ready from having unknown value
// when the address is unknown and the Host TL-UL FIFO is bypass mode.
assign tl_t_i.a_ready = tl_t_o.a_valid & hfifo_reqready;
always_comb begin
tl_t_p = tl_u_i[N];
for (int idx = 0 ; idx < N ; idx++) begin
if (dev_select_outstanding == NWD'(idx)) tl_t_p = tl_u_i[idx];
end
end
assign tl_t_i.d_valid = tl_t_p.d_valid ;
assign tl_t_i.d_opcode = tl_t_p.d_opcode;
assign tl_t_i.d_param = tl_t_p.d_param ;
assign tl_t_i.d_size = tl_t_p.d_size ;
assign tl_t_i.d_source = tl_t_p.d_source;
assign tl_t_i.d_sink = tl_t_p.d_sink ;
assign tl_t_i.d_data = tl_t_p.d_data ;
assign tl_t_i.d_error = tl_t_p.d_error ;
// accept responses from devices when selected if upstream is accepting
for (genvar i = 0 ; i < N+1 ; i++) begin : gen_u_o_d_ready
assign tl_u_o[i].d_ready = tl_t_o.d_ready;
end
// finally instantiate all device FIFOs and the error responder
for (genvar i = 0 ; i < N ; i++) begin : gen_dfifo
tlul_fifo_sync #(
.ReqPass(DReqPass[i]),
.RspPass(DRspPass[i]),
.ReqDepth(DReqDepth[i*4+:4]),
.RspDepth(DRspDepth[i*4+:4])
) fifo_d (
.clk_i,
.rst_ni,
.tl_h_i (tl_u_o[i]),
.tl_h_o (tl_u_i[i]),
.tl_d_o (tl_d_o[i]),
.tl_d_i (tl_d_i[i]),
.spare_req_i (1'b0),
.spare_req_o (),
.spare_rsp_i (1'b0),
.spare_rsp_o ());
end
assign tl_u_o[N].a_valid = tl_t_o.a_valid &
(dev_select_t == NWD'(N)) &
~hold_all_requests;
assign tl_u_o[N].a_opcode = tl_t_o.a_opcode;
assign tl_u_o[N].a_param = tl_t_o.a_param;
assign tl_u_o[N].a_size = tl_t_o.a_size;
assign tl_u_o[N].a_source = tl_t_o.a_source;
assign tl_u_o[N].a_address = tl_t_o.a_address;
assign tl_u_o[N].a_mask = tl_t_o.a_mask;
assign tl_u_o[N].a_data = tl_t_o.a_data;
tlul_err_resp err_resp (
.clk_i (clk_i),
.rst_ni (rst_ni),
.tl_h_i (tl_u_o[N]),
.tl_h_o (tl_u_i[N]));
endmodule