blob: a26e1c5f826c5e003b50e405e634210156f92e5e [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 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
// DiReqPass: (one per device_count) if 1 then device i requests can
// pass through on empty fifo, default 1
// DiRspPass: (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
// DiReqDepth: (one per device_count) Depth of device i request FIFO,
// default 2
// DiRspDepth: (one per device_count) Depth of device i response FIFO,
// default 2
//
// Requests must stall to one slave until all responses from other slaves
// have returned. Need to keep a counter of all outstanding requests and
// wait until that counter is zero before switching slaves.
//
// This module will return a request error if the input value of 'dev_select'
// is not within the range 0..N-1. Thus the instantiator of the socket
// can indicate error by any illegal value of dev_select. 4'b1111 is
// recommended for visibility
//
// The maximum value of N is 15
module tlul_socket_1n #(
parameter 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}},
parameter 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
);
`ASSERT_INIT(paramCheckNWD, NWD == $clog2(N+1))
`ASSERT_INIT(maxN, N < 16)
// 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;
// TODO: Create direct connection if Depth are 0 and Pass are 1
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),
.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.
logic [7: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;
// TODO: Assert overflow
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
num_req_outstanding <= 8'h0;
dev_select_outstanding <= '0;
end else if (accept_t_req) begin
if (!accept_t_rsp) begin
num_req_outstanding <= num_req_outstanding + 8'h1;
end
dev_select_outstanding <= dev_select_t;
end else if (accept_t_rsp) begin
num_req_outstanding <= num_req_outstanding - 8'h1;
end
end
assign hold_all_requests =
(num_req_outstanding != 8'h0) &
(dev_select_t != dev_select_outstanding);
// Make N copies of 't' request side with modified reqvalid, call
// them 'u0' .. 'un-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;
assign tl_u_o[i].a_user = tl_t_o.a_user;
end
tlul_pkg::tl_d2h_t tl_t_p ;
// for the returning reqready, only look at the slave 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;
end
if (hold_all_requests) hfifo_reqready = 1'b0;
end
assign tl_t_i.a_ready = 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_user = tl_t_p.d_user ;
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
// TODO: Create direct connection if Depth are 0 and Pass are 1
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;
assign tl_u_o[N].a_user = tl_t_o.a_user;
tlul_err_resp err_resp (
.clk_i,
.rst_ni,
.tl_h_i (tl_u_o[N]),
.tl_h_o (tl_u_i[N]));
endmodule