| // 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 |