blob: 2b333f00a32fb8df19252d4d1b58e62652bcf9c9 [file] [log] [blame]
// Copyright lowRISC contributors (OpenTitan project).
// 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
// 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
// ExplicitErrs: This module always returns a request error if dev_select_i
// is greater than N-1. If ExplicitErrs is set then the width
// of the dev_select_i signal will be chosen to make sure that
// this is possible. This only makes a difference if N is a
// power of 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 63
`include "prim_assert.sv"
module tlul_socket_1n_128
#(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'h1,
parameter bit [3 : 0] HRspDepth = 4'h1,
parameter bit [N * 4 - 1 : 0] DReqDepth = {N{4'h1}},
parameter bit [N * 4 - 1 : 0] DRspDepth = {N{4'h1}},
parameter bit ExplicitErrs = 1'b1,
// The width of dev_select_i. We must be able to select any of the N
// devices (i.e. values 0..N-1). If ExplicitErrs is set, we also need to
// be able to represent N.
localparam int unsigned NWD = $clog2(ExplicitErrs ? N + 1 : N))
(input clk_i,
input rst_ni,
input kelvin_tlul_pkg_128::tl_h2d_t tl_h_i,
output kelvin_tlul_pkg_128::tl_d2h_t tl_h_o,
output kelvin_tlul_pkg_128::tl_h2d_t tl_d_o[N],
input kelvin_tlul_pkg_128::tl_d2h_t tl_d_i[N],
input [NWD - 1 : 0] dev_select_i);
`ASSERT_INIT(maxN, N < 64)
// 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;
kelvin_tlul_pkg_128::tl_h2d_t tl_t_o;
kelvin_tlul_pkg_128::tl_d2h_t tl_t_i;
tlul_fifo_sync_128 #(.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 =
2 ** top_pkg::TL_AIW; // Up to 256 outstanding
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
`ASSERT(NotOverflowed_A, accept_t_req && !accept_t_rsp ->
num_req_outstanding <= MaxOutstanding)
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]'.
kelvin_tlul_pkg_128::tl_h2d_t tl_u_o[N + 1];
kelvin_tlul_pkg_128::tl_d2h_t tl_u_i[N + 1];
// ensure that when a device is not selected, both command
// data integrity can never match
kelvin_tlul_pkg_128::tl_a_user_t blanked_auser;
assign blanked_auser = '{
rsvd: tl_t_o.a_user.rsvd,
instr_type: tl_t_o.a_user.instr_type,
cmd_intg: kelvin_tlul_pkg_128::get_bad_cmd_intg(tl_t_o),
data_intg: kelvin_tlul_pkg_128::get_bad_data_intg(
kelvin_tlul_pkg_128::BlankedAData)
};
// if a host is not selected, or if requests are held off, blank the bus
for (genvar i = 0; i < N; i++) begin : gen_u_o
logic dev_select;
assign dev_select = dev_select_t == NWD'(i) & ~hold_all_requests;
assign tl_u_o[i].a_valid = tl_t_o.a_valid & dev_select;
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 =
dev_select ? tl_t_o.a_data : kelvin_tlul_pkg_128::BlankedAData;
assign tl_u_o[i].a_user = dev_select ? tl_t_o.a_user : blanked_auser;
assign tl_u_o[i].d_ready = tl_t_o.d_ready;
end
kelvin_tlul_pkg_128::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_user = tl_t_p.d_user;
assign tl_t_i.d_error = tl_t_p.d_error;
// Instantiate all the device FIFOs
for (genvar i = 0; i < N; i++) begin : gen_dfifo
tlul_fifo_sync_128 #(.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
// Instantiate the error responder. It's only needed if a value greater than
// N-1 is actually representable in NWD bits.
if ($clog2(N + 1) <= NWD) begin : gen_err_resp
assign tl_u_o[N].d_ready = tl_t_o.d_ready;
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]));
end else begin : gen_no_err_resp // block: gen_err_resp
assign tl_u_o[N] = '0;
assign tl_u_i[N] = '0;
logic unused_sig;
assign unused_sig = ^tl_u_o[N];
end
endmodule