../src/lowrisc_ip_pinmux_component_0.1/rtl/pinmux.sv Cov: 94%
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: // Pinmux toplevel.
6: //
7:
8: `include "prim_assert.sv"
9:
10: module pinmux import pinmux_pkg::*; import pinmux_reg_pkg::*; (
11: input clk_i,
12: input rst_ni,
13: // Slow always-on clock
14: input clk_aon_i,
15: input rst_aon_ni,
16: // Wakeup request, running on clk_aon_i
17: output logic aon_wkup_req_o,
18: // Sleep enable, running on clk_i
19: input sleep_en_i,
20: // Strap sample request
21: input lc_strap_req_t lc_pinmux_strap_i,
22: output lc_strap_rsp_t lc_pinmux_strap_o,
23: // Bus Interface (device)
24: input tlul_pkg::tl_h2d_t tl_i,
25: output tlul_pkg::tl_d2h_t tl_o,
26: // Muxed Peripheral side
27: input [NMioPeriphOut-1:0] periph_to_mio_i,
28: input [NMioPeriphOut-1:0] periph_to_mio_oe_i,
29: output logic [NMioPeriphIn-1:0] mio_to_periph_o,
30: // Dedicated Peripheral side
31: input [NDioPads-1:0] periph_to_dio_i,
32: input [NDioPads-1:0] periph_to_dio_oe_i,
33: output logic [NDioPads-1:0] dio_to_periph_o,
34: // Pad side
35: // MIOs
36: output logic [NMioPads-1:0] mio_out_o,
37: output logic [NMioPads-1:0] mio_oe_o,
38: input [NMioPads-1:0] mio_in_i,
39: // DIOs
40: output logic [NDioPads-1:0] dio_out_o,
41: output logic [NDioPads-1:0] dio_oe_o,
42: input [NDioPads-1:0] dio_in_i
43: );
44:
45: ////////////////////////////
46: // Parameters / Constants //
47: ////////////////////////////
48:
49: // TODO: these need to be parameterizable via topgen at some point.
50: // They have been placed here such that they do not generate
51: // warnings in the C header generation step, since logic is not supported
52: // as a data type yet.
53: localparam logic [pinmux_reg_pkg::NMioPeriphOut-1:0] MioPeriphHasSleepMode
54: = {pinmux_reg_pkg::NMioPeriphOut{1'b1}};
55: localparam logic [pinmux_reg_pkg::NDioPads-1:0] DioPeriphHasSleepMode
56: = {pinmux_reg_pkg::NDioPads{1'b1}};
57: localparam logic [pinmux_reg_pkg::NDioPads-1:0] DioPeriphHasWkup
58: = {pinmux_reg_pkg::NDioPads{1'b1}};
59:
60: //////////////////////////////////
61: // Regfile Breakout and Mapping //
62: //////////////////////////////////
63:
64: pinmux_reg2hw_t reg2hw;
65: pinmux_hw2reg_t hw2reg;
66:
67: pinmux_reg_top i_reg_top (
68: .clk_i ,
69: .rst_ni ,
70: .tl_i ,
71: .tl_o ,
72: .reg2hw ,
73: .hw2reg ,
74: .devmode_i(1'b1)
75: );
76:
77: /////////////////////
78: // Sleep registers //
79: /////////////////////
80:
81: logic sleep_en_q;
82: logic [NMioPads-1:0] mio_out_sleep_d, mio_oe_sleep_d;
83: logic [NMioPads-1:0] mio_out_sleep_q, mio_oe_sleep_q;
84: logic [NDioPads-1:0] dio_out_sleep_d, dio_oe_sleep_d;
85: logic [NDioPads-1:0] dio_out_sleep_q, dio_oe_sleep_q;
86: // these are external due to their WARL behavior
87: logic [NDioPads-1:0][1:0] dio_out_sleep_val_d, dio_out_sleep_val_q;
88:
89: // latch MIO/DIO state when going to sleep
90: // 0: drive low
91: // 1: drive high
92: // 2: high-z
93: // 3: previous value
94: for (genvar k = 0; k < NMioPads; k++) begin : gen_mio_sleep
95: assign mio_out_sleep_d[k] = (reg2hw.mio_out_sleep_val[k].q == 0) ? 1'b0 :
96: (reg2hw.mio_out_sleep_val[k].q == 1) ? 1'b1 :
97: (reg2hw.mio_out_sleep_val[k].q == 2) ? 1'b0 : mio_out_o[k];
98:
99: assign mio_oe_sleep_d[k] = (reg2hw.mio_out_sleep_val[k].q == 0) ? 1'b1 :
100: (reg2hw.mio_out_sleep_val[k].q == 1) ? 1'b1 :
101: (reg2hw.mio_out_sleep_val[k].q == 2) ? 1'b0 : mio_oe_o[k];
102: end
103:
104: // since DIO pads are permanently mapped to a specific peripheral,
105: // we only need to support retention regs on non-always on peripherals,
106: // outputs / inouts.
107: for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_sleep
108: if (DioPeriphHasSleepMode[k]) begin : gen_warl_connect
109: assign hw2reg.dio_out_sleep_val[k].d = dio_out_sleep_val_q[k];
110:
111: assign dio_out_sleep_val_d[k] = (reg2hw.dio_out_sleep_val[k].qe) ?
112: reg2hw.dio_out_sleep_val[k].q :
113: dio_out_sleep_val_q[k];
114:
115: assign dio_out_sleep_d[k] = (dio_out_sleep_val_q[k] == 0) ? 1'b0 :
116: (dio_out_sleep_val_q[k] == 1) ? 1'b1 :
117: (dio_out_sleep_val_q[k] == 2) ? 1'b0 : dio_out_o[k];
118:
119: assign dio_oe_sleep_d[k] = (dio_out_sleep_val_q[k] == 0) ? 1'b1 :
120: (dio_out_sleep_val_q[k] == 1) ? 1'b1 :
121: (dio_out_sleep_val_q[k] == 2) ? 1'b0 : dio_oe_o[k];
122: end else begin : gen_warl_tie0
123: // these signals will be unused
124: assign hw2reg.dio_out_sleep_val[k].d = 2'b10; // default value defined in hjson
125: assign dio_out_sleep_val_d[k] = 2'b10; // default value defined in hjson
126: assign dio_out_sleep_d[k] = '0;
127: assign dio_oe_sleep_d[k] = '0;
128: end
129: end
130:
131: always_ff @(posedge clk_i or negedge rst_ni) begin : p_sleep
132: if (!rst_ni) begin
133: sleep_en_q <= 1'b0;
134: dio_out_sleep_val_q <= {NDioPads{2'b10}}; // default value defined in hjson
135: mio_out_sleep_q <= '0;
136: mio_oe_sleep_q <= '0;
137: dio_out_sleep_q <= '0;
138: dio_oe_sleep_q <= '0;
139: end else begin
140: sleep_en_q <= sleep_en_i;
141: dio_out_sleep_val_q <= dio_out_sleep_val_d;
142:
143: if (sleep_en_i & !sleep_en_q) begin
144: mio_out_sleep_q <= mio_out_sleep_d;
145: mio_oe_sleep_q <= mio_oe_sleep_d;
146: dio_out_sleep_q <= dio_out_sleep_d;
147: dio_oe_sleep_q <= dio_oe_sleep_d;
148: end
149: end
150: end
151:
152: ///////////////
153: // Input Mux //
154: ///////////////
155:
156: for (genvar k = 0; k < NMioPeriphIn; k++) begin : gen_mio_periph_in
157: logic [2**$clog2(NMioPads+2)-1:0] data_mux;
158: // stack input and default signals for convenient indexing below
159: // possible defaults: constant 0 or 1
160: assign data_mux = $bits(data_mux)'({mio_in_i, 1'b1, 1'b0});
161: // index using configured insel
162: assign mio_to_periph_o[k] = data_mux[reg2hw.periph_insel[k].q];
163: end
164:
165: ////////////////
166: // Output Mux //
167: ////////////////
168:
169: for (genvar k = 0; k < NMioPads; k++) begin : gen_mio_out
170: logic sleep_en;
171: logic [2**$clog2(NMioPeriphOut+3)-1:0] data_mux, oe_mux, sleep_mux;
172: // stack output data/enable and default signals for convenient indexing below
173: // possible defaults: 0, 1 or 2 (high-Z)
174: assign data_mux = $bits(data_mux)'({periph_to_mio_i, 1'b0, 1'b1, 1'b0});
175: assign oe_mux = $bits(oe_mux)'({periph_to_mio_oe_i, 1'b0, 1'b1, 1'b1});
176: assign sleep_mux = $bits(sleep_mux)'({MioPeriphHasSleepMode, 1'b1, 1'b1, 1'b1});
177:
178: // check whether this peripheral can actually go to sleep
179: assign sleep_en = sleep_mux[reg2hw.mio_outsel[k].q] & sleep_en_q;
180: // index using configured outsel
181: assign mio_out_o[k] = (sleep_en) ? mio_out_sleep_q[k] : data_mux[reg2hw.mio_outsel[k].q];
182: assign mio_oe_o[k] = (sleep_en) ? mio_oe_sleep_q[k] : oe_mux[reg2hw.mio_outsel[k].q];
183: end
184:
185: /////////////////////
186: // DIO connections //
187: /////////////////////
188:
189: // Inputs are just fed through
190: assign dio_to_periph_o = dio_in_i;
191:
192: for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_out
193: // Since this is a DIO, this can be determined at design time
194: if (DioPeriphHasSleepMode[k]) begin : gen_sleep
195: assign dio_out_o[k] = (sleep_en_q) ? dio_out_sleep_q[k] : periph_to_dio_i[k];
196: assign dio_oe_o[k] = (sleep_en_q) ? dio_oe_sleep_q[k] : periph_to_dio_oe_i[k];
197: end else begin : gen_nosleep
198: assign dio_out_o[k] = periph_to_dio_i[k];
199: assign dio_oe_o[k] = periph_to_dio_oe_i[k];
200: end
201: end
202:
203: //////////////////////
204: // Wakeup detectors //
205: //////////////////////
206:
207: localparam int AlignedMuxSize = (NMioPads + 2 > NDioPads) ? 2**$clog2(NMioPads + 2) :
208: 2**$clog2(NDioPads);
209: logic [NWkupDetect-1:0] aon_wkup_req;
210: logic [AlignedMuxSize-1:0] dio_data_mux, mio_data_mux;
211: assign mio_data_mux = AlignedMuxSize'({mio_in_i, 1'b0, 1'b0});
212:
213: // Only connect DIOs that are not excempt
214: for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_wkup
215: if (DioPeriphHasWkup[k]) begin : gen_dio_wkup_connect
216: assign dio_data_mux[k] = dio_in_i[k];
217: end else begin : gen_dio_wkup_tie_off
218: assign dio_data_mux[k] = 1'b0;
219: end
220: end
221:
222: for (genvar k = NDioPads; k < AlignedMuxSize; k++) begin : gen_dio_data_mux_tie_off
223: assign dio_data_mux[k] = 1'b0;
224: end
225:
226: for (genvar k = 0; k < NWkupDetect; k++) begin : gen_wkup_detect
227: logic pin_value;
228: assign pin_value = (reg2hw.wkup_detector[k].miodio.q) ?
229: dio_data_mux[reg2hw.wkup_detector_padsel[k]] :
230: mio_data_mux[reg2hw.wkup_detector_padsel[k]];
231:
232: pinmux_wkup i_pinmux_wkup (
233: .clk_i,
234: .rst_ni,
235: .clk_aon_i,
236: .rst_aon_ni,
237: // config signals. these are synched to clk_aon internally
238: .wkup_en_i ( reg2hw.wkup_detector_en[k].q ),
239: .filter_en_i ( reg2hw.wkup_detector[k].filter.q ),
240: .wkup_mode_i ( wkup_mode_e'(reg2hw.wkup_detector[k].mode.q)),
241: .wkup_cnt_th_i ( reg2hw.wkup_detector_cnt_th[k].q ),
242: .pin_value_i ( pin_value ),
243: // cause reg signals. these are synched from/to clk_aon internally
244: .wkup_cause_valid_i ( reg2hw.wkup_cause[k].qe ),
245: .wkup_cause_data_i ( reg2hw.wkup_cause[k].q ),
246: .wkup_cause_data_o ( hw2reg.wkup_cause[k].d ),
247: // wakeup request signals on clk_aon (level encoded)
248: .aon_wkup_req_o ( aon_wkup_req[k] )
249: );
250: end
251:
252: // OR' together all wakeup requests
253: assign aon_wkup_req_o = |aon_wkup_req;
254:
255: ////////////////////
256: // Strap Sampling //
257: ////////////////////
258:
259: logic [NStraps-1:0] lc_strap_taps;
260: lc_strap_rsp_t lc_strap_d, lc_strap_q;
261:
262: for (genvar k = 0; k < NStraps; k++) begin : gen_strap_taps
263: assign lc_strap_taps[k] = mio_in_i[MioStrapPos[k]];
264: end
265:
266: assign lc_pinmux_strap_o = lc_strap_q;
267: assign lc_strap_d = (lc_pinmux_strap_i.sample_pulse) ?
268: '{valid: 1'b1, straps: lc_strap_taps} :
269: lc_strap_q;
270:
271: always_ff @(posedge clk_i or negedge rst_ni) begin : p_strap_sample
272: if (!rst_ni) begin
273: lc_strap_q <= '0;
274: end else begin
275: lc_strap_q <= lc_strap_d;
276: end
277: end
278:
279: ////////////////
280: // Assertions //
281: ////////////////
282:
283: `ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid)
284: `ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready)
285: // `ASSERT_KNOWN(MioToPeriphKnownO_A, mio_to_periph_o)
286: `ASSERT_KNOWN(MioOeKnownO_A, mio_oe_o)
287: // `ASSERT_KNOWN(DioToPeriphKnownO_A, dio_to_periph_o)
288: `ASSERT_KNOWN(DioOeKnownO_A, dio_oe_o)
289: `ASSERT_KNOWN(LcPinmuxStrapKnownO_A, lc_pinmux_strap_o)
290:
291: // TODO: need to check why some outputs are not valid (e.g. SPI device MISO)
292: // for (genvar k = 0; k < NMioPads; k++) begin : gen_mio_known_if
293: // `ASSERT_KNOWN_IF(MioOutKnownO_A, mio_out_o[k], mio_oe_o[k])
294: // end
295:
296: // for (genvar k = 0; k < NDioPads; k++) begin : gen_dio_known_if
297: // `ASSERT_KNOWN_IF(DioOutKnownO_A, dio_out_o[k], dio_oe_o[k])
298: // end
299:
300: // running on slow AON clock
301: `ASSERT_KNOWN(AonWkupReqKnownO_A, aon_wkup_req_o, !rst_aon_ni, clk_aon_i)
302:
303: endmodule : pinmux
304: