../src/lowrisc_tlul_socket_1n_0.1/rtl/tlul_socket_1n.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // TL-UL socket 1:N module
6: //
7: // configuration settings
8: // device_count: 4
9: //
10: // Verilog parameters
11: // HReqPass: if 1 then host requests can pass through on empty fifo,
12: // default 1
13: // HRspPass: if 1 then host responses can pass through on empty fifo,
14: // default 1
15: // DReqPass: (one per device_count) if 1 then device i requests can
16: // pass through on empty fifo, default 1
17: // DRspPass: (one per device_count) if 1 then device i responses can
18: // pass through on empty fifo, default 1
19: // HReqDepth: Depth of host request FIFO, default 2
20: // HRspDepth: Depth of host response FIFO, default 2
21: // DReqDepth: (one per device_count) Depth of device i request FIFO,
22: // default 2
23: // DRspDepth: (one per device_count) Depth of device i response FIFO,
24: // default 2
25: //
26: // Requests must stall to one slave until all responses from other slaves
27: // have returned. Need to keep a counter of all outstanding requests and
28: // wait until that counter is zero before switching slaves.
29: //
30: // This module will return a request error if the input value of 'dev_select'
31: // is not within the range 0..N-1. Thus the instantiator of the socket
32: // can indicate error by any illegal value of dev_select. 4'b1111 is
33: // recommended for visibility
34: //
35: // The maximum value of N is 15
36:
37: `include "prim_assert.sv"
38:
39: module tlul_socket_1n #(
40: parameter int unsigned N = 4,
41: parameter bit HReqPass = 1'b1,
42: parameter bit HRspPass = 1'b1,
43: parameter bit [N-1:0] DReqPass = {N{1'b1}},
44: parameter bit [N-1:0] DRspPass = {N{1'b1}},
45: parameter bit [3:0] HReqDepth = 4'h2,
46: parameter bit [3:0] HRspDepth = 4'h2,
47: parameter bit [N*4-1:0] DReqDepth = {N{4'h2}},
48: parameter bit [N*4-1:0] DRspDepth = {N{4'h2}},
49: localparam int unsigned NWD = $clog2(N+1) // derived parameter
50: ) (
51: input clk_i,
52: input rst_ni,
53: input tlul_pkg::tl_h2d_t tl_h_i,
54: output tlul_pkg::tl_d2h_t tl_h_o,
55: output tlul_pkg::tl_h2d_t tl_d_o [N],
56: input tlul_pkg::tl_d2h_t tl_d_i [N],
57: input [NWD-1:0] dev_select
58: );
59:
60: `ASSERT_INIT(maxN, N < 16)
61:
62: // Since our steering is done after potential FIFOing, we need to
63: // shove our device select bits into spare bits of reqfifo
64:
65: // instantiate the host fifo, create intermediate bus 't'
66:
67: // FIFO'd version of device select
68: logic [NWD-1:0] dev_select_t;
69:
70: tlul_pkg::tl_h2d_t tl_t_o;
71: tlul_pkg::tl_d2h_t tl_t_i;
72:
73: tlul_fifo_sync #(
74: .ReqPass(HReqPass),
75: .RspPass(HRspPass),
76: .ReqDepth(HReqDepth),
77: .RspDepth(HRspDepth),
78: .SpareReqW(NWD)
79: ) fifo_h (
80: .clk_i,
81: .rst_ni,
82: .tl_h_i,
83: .tl_h_o,
84: .tl_d_o (tl_t_o),
85: .tl_d_i (tl_t_i),
86: .spare_req_i (dev_select),
87: .spare_req_o (dev_select_t),
88: .spare_rsp_i (1'b0),
89: .spare_rsp_o ());
90:
91:
92: // We need to keep track of how many requests are outstanding,
93: // and to which device. New requests are compared to this and
94: // stall until that number is zero.
95:
96: logic [7:0] num_req_outstanding;
97: logic [NWD-1:0] dev_select_outstanding;
98: logic hold_all_requests;
99: logic accept_t_req, accept_t_rsp;
100:
101: assign accept_t_req = tl_t_o.a_valid & tl_t_i.a_ready;
102: assign accept_t_rsp = tl_t_i.d_valid & tl_t_o.d_ready;
103:
104: always_ff @(posedge clk_i or negedge rst_ni) begin
105: if (!rst_ni) begin
106: num_req_outstanding <= 8'h0;
107: dev_select_outstanding <= '0;
108: end else if (accept_t_req) begin
109: if (!accept_t_rsp) begin
110: `ASSERT_I(NotOverflowed_A, num_req_outstanding != '1)
111: num_req_outstanding <= num_req_outstanding + 8'h1;
112: end
113: dev_select_outstanding <= dev_select_t;
114: end else if (accept_t_rsp) begin
115: num_req_outstanding <= num_req_outstanding - 8'h1;
116: end
117: end
118:
119: assign hold_all_requests =
120: (num_req_outstanding != 8'h0) &
121: (dev_select_t != dev_select_outstanding);
122:
123: // Make N copies of 't' request side with modified reqvalid, call
124: // them 'u[0]' .. 'u[n-1]'.
125:
126: tlul_pkg::tl_h2d_t tl_u_o [N+1];
127: tlul_pkg::tl_d2h_t tl_u_i [N+1];
128:
129: for (genvar i = 0 ; i < N ; i++) begin : gen_u_o
130: assign tl_u_o[i].a_valid = tl_t_o.a_valid &
131: (dev_select_t == NWD'(i)) &
132: ~hold_all_requests;
133: assign tl_u_o[i].a_opcode = tl_t_o.a_opcode;
134: assign tl_u_o[i].a_param = tl_t_o.a_param;
135: assign tl_u_o[i].a_size = tl_t_o.a_size;
136: assign tl_u_o[i].a_source = tl_t_o.a_source;
137: assign tl_u_o[i].a_address = tl_t_o.a_address;
138: assign tl_u_o[i].a_mask = tl_t_o.a_mask;
139: assign tl_u_o[i].a_data = tl_t_o.a_data;
140: assign tl_u_o[i].a_user = tl_t_o.a_user;
141: end
142:
143: tlul_pkg::tl_d2h_t tl_t_p ;
144:
145: // for the returning reqready, only look at the slave we're addressing
146: logic hfifo_reqready;
147: always_comb begin
148: hfifo_reqready = tl_u_i[N].a_ready; // default to error
149: for (int idx = 0 ; idx < N ; idx++) begin
150: //if (dev_select_outstanding == NWD'(idx)) hfifo_reqready = tl_u_i[idx].a_ready;
151: if (dev_select_t == NWD'(idx)) hfifo_reqready = tl_u_i[idx].a_ready;
152: end
153: if (hold_all_requests) hfifo_reqready = 1'b0;
154: end
155: // Adding a_valid as a qualifier. This prevents the a_ready from having unknown value
156: // when the address is unknown and the Host TL-UL FIFO is bypass mode.
157: assign tl_t_i.a_ready = tl_t_o.a_valid & hfifo_reqready;
158:
159: always_comb begin
160: tl_t_p = tl_u_i[N];
161: for (int idx = 0 ; idx < N ; idx++) begin
162: if (dev_select_outstanding == NWD'(idx)) tl_t_p = tl_u_i[idx];
163: end
164: end
165: assign tl_t_i.d_valid = tl_t_p.d_valid ;
166: assign tl_t_i.d_opcode = tl_t_p.d_opcode;
167: assign tl_t_i.d_param = tl_t_p.d_param ;
168: assign tl_t_i.d_size = tl_t_p.d_size ;
169: assign tl_t_i.d_source = tl_t_p.d_source;
170: assign tl_t_i.d_sink = tl_t_p.d_sink ;
171: assign tl_t_i.d_data = tl_t_p.d_data ;
172: assign tl_t_i.d_user = tl_t_p.d_user ;
173: assign tl_t_i.d_error = tl_t_p.d_error ;
174:
175:
176: // accept responses from devices when selected if upstream is accepting
177: for (genvar i = 0 ; i < N+1 ; i++) begin : gen_u_o_d_ready
178: assign tl_u_o[i].d_ready = tl_t_o.d_ready;
179: end
180:
181: // finally instantiate all device FIFOs and the error responder
182: for (genvar i = 0 ; i < N ; i++) begin : gen_dfifo
183: tlul_fifo_sync #(
184: .ReqPass(DReqPass[i]),
185: .RspPass(DRspPass[i]),
186: .ReqDepth(DReqDepth[i*4+:4]),
187: .RspDepth(DRspDepth[i*4+:4])
188: ) fifo_d (
189: .clk_i,
190: .rst_ni,
191: .tl_h_i (tl_u_o[i]),
192: .tl_h_o (tl_u_i[i]),
193: .tl_d_o (tl_d_o[i]),
194: .tl_d_i (tl_d_i[i]),
195: .spare_req_i (1'b0),
196: .spare_req_o (),
197: .spare_rsp_i (1'b0),
198: .spare_rsp_o ());
199: end
200:
201: assign tl_u_o[N].a_valid = tl_t_o.a_valid &
202: (dev_select_t == NWD'(N)) &
203: ~hold_all_requests;
204: assign tl_u_o[N].a_opcode = tl_t_o.a_opcode;
205: assign tl_u_o[N].a_param = tl_t_o.a_param;
206: assign tl_u_o[N].a_size = tl_t_o.a_size;
207: assign tl_u_o[N].a_source = tl_t_o.a_source;
208: assign tl_u_o[N].a_address = tl_t_o.a_address;
209: assign tl_u_o[N].a_mask = tl_t_o.a_mask;
210: assign tl_u_o[N].a_data = tl_t_o.a_data;
211: assign tl_u_o[N].a_user = tl_t_o.a_user;
212: tlul_err_resp err_resp (
213: .clk_i,
214: .rst_ni,
215: .tl_h_i (tl_u_o[N]),
216: .tl_h_o (tl_u_i[N]));
217:
218: endmodule
219: