Skip to content

Commit be7bd57

Browse files
committed
[pinmux_aon,rtl] Improve JTAG Muxing
The three jtag request (lc_jtag, dft_jtag, rv_jtag) are now gated with an AND-gate instead of a combinatorial process. This eliminates the risk of a glitch on the jtag clock or reset line. Signed-off-by: Michael Gautschi <mgautschi@lowrisc.org>
1 parent e0950bf commit be7bd57

16 files changed

Lines changed: 344 additions & 76 deletions

File tree

hw/ip_templates/pinmux/lint/pinmux.waiver.tpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ waive -rules RESET_MUX -location {pinmux_strap_sampling.sv} -regexp {Asynchronou
2323
waive -rules {CLOCK_DRIVER CLOCK_MUX} -location {pinmux_strap_sampling.sv} -regexp {'(lc|rv)_jtag_req.tck' is driven( by a multiplexer)? here,( and)? used as a clock 'tck_i' at dmi_jtag_tap.sv} ${"\\"}
2424
-comment "These signals are muxed using the JTAG Selection Mux."
2525

26+
waive -rules {CLOCK_DRIVER CLOCK_MUX} -location {pinmux_jtag_buf.sv} -regexp {'tck_gated' is driven by instance 'u_prim_and2_tck' of module 'prim_and2', and used as a clock 'tck_i' at dmi_jtag_tap.sv} ${"\\"}
27+
-comment "TCK is gated per TAP by a hand-instantiated prim_and2 cell inside pinmux_jtag_buf, enabled by the TAP Selection Mux, before being re-buffered onto req_o.tck by prim_clock_buf."
28+
2629
waive -rules CLOCK_MUX -location {pinmux_strap_sampling.sv pinmux.sv} -regexp {Clock '(in_padring_i\[38\]|mio_in_i\[38\]|jtag_req.tck)' reaches a multiplexer here, used as a clock 'tck_i' at dmi_jtag_tap.sv} ${"\\"}
2730
-comment "The 'mio_in_i[TckPadIdx]' input signal is connected to 'jtag_req.tck' which eventually feeds into the JTAG Selection Mux."
2831
% else:

hw/ip_templates/pinmux/pinmux.core.tpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ filesets:
1212
- lowrisc:prim:all
1313
- lowrisc:prim:clock_buf
1414
- lowrisc:prim:buf
15+
- lowrisc:prim:and2
1516
- lowrisc:prim:lc_dec
1617
- lowrisc:prim:lc_sync
1718
- lowrisc:prim:lc_sender

hw/ip_templates/pinmux/rtl/pinmux_jtag_buf.sv

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,29 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
module pinmux_jtag_buf (
6+
// Gates TCK/TRSTN below. Holds this TAP's clock off and reset asserted when deselected.
7+
input logic en_i,
68
input jtag_pkg::jtag_req_t req_i,
79
output jtag_pkg::jtag_req_t req_o,
810
input jtag_pkg::jtag_rsp_t rsp_i,
911
output jtag_pkg::jtag_rsp_t rsp_o
1012
);
1113

14+
logic tck_gated;
15+
prim_and2 #(.Width(1)) u_prim_and2_tck (
16+
.in0_i(en_i),
17+
.in1_i(req_i.tck),
18+
.out_o(tck_gated)
19+
);
20+
// This buffer is used in the constraints to define a generated clock
1221
prim_clock_buf prim_clock_buf_tck (
13-
.clk_i(req_i.tck),
22+
.clk_i(tck_gated),
1423
.clk_o(req_o.tck)
1524
);
16-
prim_buf prim_buf_trst_n (
17-
.in_i (req_i.trst_n),
25+
26+
prim_and2 #(.Width(1)) u_prim_and2_trst_n (
27+
.in0_i(en_i),
28+
.in1_i(req_i.trst_n),
1829
.out_o(req_o.trst_n)
1930
);
2031
prim_buf prim_buf_tms (

hw/ip_templates/pinmux/rtl/pinmux_strap_sampling.sv.tpl

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -331,34 +331,83 @@ module pinmux_strap_sampling
331331
assign tap_strap = tap_strap_t'(tap_strap_q);
332332
`ASSERT_KNOWN(TapStrapKnown_A, tap_strap)
333333

334+
// lc_tap_en_d/rv_tap_en_d/dft_tap_en_d are decoded from tap_strap in p_tap_mux below (qualified
335+
// by the live pinmux_hw_debug_en/lc_dft_en signals for the RV_DM and DFT taps) and registered
336+
// here into lc_tap_en_q/rv_tap_en_q/dft_tap_en_q. The registered, single-bit enables are what
337+
// actually gate the JTAG request lines further down, so those live lines are never a direct
338+
// combinational function of the 2-bit tap_strap register. tap_strap_q's two bits are separate
339+
// flops that are not guaranteed to switch at exactly the same instant post-synthesis, which
340+
// could otherwise glitch the JTAG request lines through an unintended TAP selection for a cycle.
341+
logic lc_tap_en_d, lc_tap_en_q;
342+
logic rv_tap_en_d, rv_tap_en_q;
343+
logic dft_tap_en_d, dft_tap_en_q;
344+
345+
always_ff @(posedge clk_i or negedge rst_ni) begin : p_tap_sel_reg
346+
if (!rst_ni) begin
347+
lc_tap_en_q <= 1'b0;
348+
rv_tap_en_q <= 1'b0;
349+
dft_tap_en_q <= 1'b0;
350+
end else begin
351+
lc_tap_en_q <= lc_tap_en_d;
352+
rv_tap_en_q <= rv_tap_en_d;
353+
dft_tap_en_q <= dft_tap_en_d;
354+
end
355+
end
356+
357+
// TCK and TRSTN are shared, ungated wires here: the actual gating with the registered,
358+
// single-bit tap enables happens inside pinmux_jtag_buf below (en_i), which holds each TAP's
359+
// clock off and reset asserted when it is not selected.
360+
assign lc_jtag_req.tck = jtag_req.tck;
361+
assign lc_jtag_req.trst_n = jtag_req.trst_n;
362+
assign rv_jtag_req.tck = jtag_req.tck;
363+
assign rv_jtag_req.trst_n = jtag_req.trst_n;
364+
assign dft_jtag_req.tck = jtag_req.tck;
365+
assign dft_jtag_req.trst_n = jtag_req.trst_n;
366+
367+
// Drives the immediate (unregistered) jtag_rsp/jtag_en response mux used for the TDO override
368+
// and pad tie-offs further below, and in the same case statement decodes the per-TAP request
369+
// enables (lc_tap_en_d/rv_tap_en_d/dft_tap_en_d) that get registered above. TMS and TDI - the
370+
// signals that actually determine what the selected TAP does with its data registers - are
371+
// generated here too, directly following tap_strap like jtag_rsp.
334372
always_comb begin : p_tap_mux
335-
jtag_rsp = '0;
336-
// Note that this holds the JTAGs in reset
337-
// when they are not selected.
338-
lc_jtag_req = '0;
339-
rv_jtag_req = '0;
340-
dft_jtag_req = '0;
373+
jtag_rsp = '0;
341374
// This activates the TDO override further below.
342-
jtag_en = 1'b0;
375+
jtag_en = 1'b0;
376+
377+
lc_tap_en_d = 1'b0;
378+
rv_tap_en_d = 1'b0;
379+
dft_tap_en_d = 1'b0;
380+
lc_jtag_req.tms = 1'b0;
381+
lc_jtag_req.tdi = 1'b0;
382+
rv_jtag_req.tms = 1'b0;
383+
rv_jtag_req.tdi = 1'b0;
384+
dft_jtag_req.tms = 1'b0;
385+
dft_jtag_req.tdi = 1'b0;
343386

344387
unique case (tap_strap)
345388
LcTapSel: begin
346-
lc_jtag_req = jtag_req;
347-
jtag_rsp = lc_jtag_rsp;
348-
jtag_en = 1'b1;
389+
jtag_rsp = lc_jtag_rsp;
390+
lc_tap_en_d = 1'b1;
391+
jtag_en = 1'b1;
392+
lc_jtag_req.tms = jtag_req.tms;
393+
lc_jtag_req.tdi = jtag_req.tdi;
349394
end
350395
RvTapSel: begin
351396
if (lc_tx_test_true_strict(pinmux_hw_debug_en[HwDebugEnTapSel])) begin
352-
rv_jtag_req = jtag_req;
353-
jtag_rsp = rv_jtag_rsp;
354-
jtag_en = 1'b1;
397+
jtag_rsp = rv_jtag_rsp;
398+
rv_tap_en_d = 1'b1;
399+
jtag_en = 1'b1;
400+
rv_jtag_req.tms = jtag_req.tms;
401+
rv_jtag_req.tdi = jtag_req.tdi;
355402
end
356403
end
357404
DftTapSel: begin
358405
if (lc_tx_test_true_strict(lc_dft_en[DftEnTapSel])) begin
359-
dft_jtag_req = jtag_req;
360-
jtag_rsp = dft_jtag_rsp;
361-
jtag_en = 1'b1;
406+
jtag_rsp = dft_jtag_rsp;
407+
jtag_en = 1'b1;
408+
dft_tap_en_d = 1'b1;
409+
dft_jtag_req.tms = jtag_req.tms;
410+
dft_jtag_req.tdi = jtag_req.tdi;
362411
end
363412
end
364413
default: ;
@@ -368,18 +417,21 @@ module pinmux_strap_sampling
368417
// Insert hand instantiated buffers for
369418
// these signals to prevent further optimization.
370419
pinmux_jtag_buf u_pinmux_jtag_buf_lc (
420+
.en_i(lc_tap_en_q),
371421
.req_i(lc_jtag_req),
372422
.req_o(lc_jtag_o),
373423
.rsp_i(lc_jtag_i),
374424
.rsp_o(lc_jtag_rsp)
375425
);
376426
pinmux_jtag_buf u_pinmux_jtag_buf_rv (
427+
.en_i(rv_tap_en_q),
377428
.req_i(rv_jtag_req),
378429
.req_o(rv_jtag_o),
379430
.rsp_i(rv_jtag_i),
380431
.rsp_o(rv_jtag_rsp)
381432
);
382433
pinmux_jtag_buf u_pinmux_jtag_buf_dft (
434+
.en_i(dft_tap_en_q),
383435
.req_i(dft_jtag_req),
384436
.req_o(dft_jtag_o),
385437
.rsp_i(dft_jtag_i),

hw/top_darjeeling/ip_autogen/pinmux/pinmux.core

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ filesets:
1212
- lowrisc:prim:all
1313
- lowrisc:prim:clock_buf
1414
- lowrisc:prim:buf
15+
- lowrisc:prim:and2
1516
- lowrisc:prim:lc_dec
1617
- lowrisc:prim:lc_sync
1718
- lowrisc:prim:lc_sender

hw/top_darjeeling/ip_autogen/pinmux/rtl/pinmux_jtag_buf.sv

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,29 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
module pinmux_jtag_buf (
6+
// Gates TCK/TRSTN below. Holds this TAP's clock off and reset asserted when deselected.
7+
input logic en_i,
68
input jtag_pkg::jtag_req_t req_i,
79
output jtag_pkg::jtag_req_t req_o,
810
input jtag_pkg::jtag_rsp_t rsp_i,
911
output jtag_pkg::jtag_rsp_t rsp_o
1012
);
1113

14+
logic tck_gated;
15+
prim_and2 #(.Width(1)) u_prim_and2_tck (
16+
.in0_i(en_i),
17+
.in1_i(req_i.tck),
18+
.out_o(tck_gated)
19+
);
20+
// This buffer is used in the constraints to define a generated clock
1221
prim_clock_buf prim_clock_buf_tck (
13-
.clk_i(req_i.tck),
22+
.clk_i(tck_gated),
1423
.clk_o(req_o.tck)
1524
);
16-
prim_buf prim_buf_trst_n (
17-
.in_i (req_i.trst_n),
25+
26+
prim_and2 #(.Width(1)) u_prim_and2_trst_n (
27+
.in0_i(en_i),
28+
.in1_i(req_i.trst_n),
1829
.out_o(req_o.trst_n)
1930
);
2031
prim_buf prim_buf_tms (

hw/top_darjeeling/ip_autogen/pinmux/rtl/pinmux_strap_sampling.sv

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -325,34 +325,83 @@ module pinmux_strap_sampling
325325
assign tap_strap = tap_strap_t'(tap_strap_q);
326326
`ASSERT_KNOWN(TapStrapKnown_A, tap_strap)
327327

328+
// lc_tap_en_d/rv_tap_en_d/dft_tap_en_d are decoded from tap_strap in p_tap_mux below (qualified
329+
// by the live pinmux_hw_debug_en/lc_dft_en signals for the RV_DM and DFT taps) and registered
330+
// here into lc_tap_en_q/rv_tap_en_q/dft_tap_en_q. The registered, single-bit enables are what
331+
// actually gate the JTAG request lines further down, so those live lines are never a direct
332+
// combinational function of the 2-bit tap_strap register. tap_strap_q's two bits are separate
333+
// flops that are not guaranteed to switch at exactly the same instant post-synthesis, which
334+
// could otherwise glitch the JTAG request lines through an unintended TAP selection for a cycle.
335+
logic lc_tap_en_d, lc_tap_en_q;
336+
logic rv_tap_en_d, rv_tap_en_q;
337+
logic dft_tap_en_d, dft_tap_en_q;
338+
339+
always_ff @(posedge clk_i or negedge rst_ni) begin : p_tap_sel_reg
340+
if (!rst_ni) begin
341+
lc_tap_en_q <= 1'b0;
342+
rv_tap_en_q <= 1'b0;
343+
dft_tap_en_q <= 1'b0;
344+
end else begin
345+
lc_tap_en_q <= lc_tap_en_d;
346+
rv_tap_en_q <= rv_tap_en_d;
347+
dft_tap_en_q <= dft_tap_en_d;
348+
end
349+
end
350+
351+
// TCK and TRSTN are shared, ungated wires here: the actual gating with the registered,
352+
// single-bit tap enables happens inside pinmux_jtag_buf below (en_i), which holds each TAP's
353+
// clock off and reset asserted when it is not selected.
354+
assign lc_jtag_req.tck = jtag_req.tck;
355+
assign lc_jtag_req.trst_n = jtag_req.trst_n;
356+
assign rv_jtag_req.tck = jtag_req.tck;
357+
assign rv_jtag_req.trst_n = jtag_req.trst_n;
358+
assign dft_jtag_req.tck = jtag_req.tck;
359+
assign dft_jtag_req.trst_n = jtag_req.trst_n;
360+
361+
// Drives the immediate (unregistered) jtag_rsp/jtag_en response mux used for the TDO override
362+
// and pad tie-offs further below, and in the same case statement decodes the per-TAP request
363+
// enables (lc_tap_en_d/rv_tap_en_d/dft_tap_en_d) that get registered above. TMS and TDI - the
364+
// signals that actually determine what the selected TAP does with its data registers - are
365+
// generated here too, directly following tap_strap like jtag_rsp.
328366
always_comb begin : p_tap_mux
329-
jtag_rsp = '0;
330-
// Note that this holds the JTAGs in reset
331-
// when they are not selected.
332-
lc_jtag_req = '0;
333-
rv_jtag_req = '0;
334-
dft_jtag_req = '0;
367+
jtag_rsp = '0;
335368
// This activates the TDO override further below.
336-
jtag_en = 1'b0;
369+
jtag_en = 1'b0;
370+
371+
lc_tap_en_d = 1'b0;
372+
rv_tap_en_d = 1'b0;
373+
dft_tap_en_d = 1'b0;
374+
lc_jtag_req.tms = 1'b0;
375+
lc_jtag_req.tdi = 1'b0;
376+
rv_jtag_req.tms = 1'b0;
377+
rv_jtag_req.tdi = 1'b0;
378+
dft_jtag_req.tms = 1'b0;
379+
dft_jtag_req.tdi = 1'b0;
337380

338381
unique case (tap_strap)
339382
LcTapSel: begin
340-
lc_jtag_req = jtag_req;
341-
jtag_rsp = lc_jtag_rsp;
342-
jtag_en = 1'b1;
383+
jtag_rsp = lc_jtag_rsp;
384+
lc_tap_en_d = 1'b1;
385+
jtag_en = 1'b1;
386+
lc_jtag_req.tms = jtag_req.tms;
387+
lc_jtag_req.tdi = jtag_req.tdi;
343388
end
344389
RvTapSel: begin
345390
if (lc_tx_test_true_strict(pinmux_hw_debug_en[HwDebugEnTapSel])) begin
346-
rv_jtag_req = jtag_req;
347-
jtag_rsp = rv_jtag_rsp;
348-
jtag_en = 1'b1;
391+
jtag_rsp = rv_jtag_rsp;
392+
rv_tap_en_d = 1'b1;
393+
jtag_en = 1'b1;
394+
rv_jtag_req.tms = jtag_req.tms;
395+
rv_jtag_req.tdi = jtag_req.tdi;
349396
end
350397
end
351398
DftTapSel: begin
352399
if (lc_tx_test_true_strict(lc_dft_en[DftEnTapSel])) begin
353-
dft_jtag_req = jtag_req;
354-
jtag_rsp = dft_jtag_rsp;
355-
jtag_en = 1'b1;
400+
jtag_rsp = dft_jtag_rsp;
401+
jtag_en = 1'b1;
402+
dft_tap_en_d = 1'b1;
403+
dft_jtag_req.tms = jtag_req.tms;
404+
dft_jtag_req.tdi = jtag_req.tdi;
356405
end
357406
end
358407
default: ;
@@ -362,18 +411,21 @@ module pinmux_strap_sampling
362411
// Insert hand instantiated buffers for
363412
// these signals to prevent further optimization.
364413
pinmux_jtag_buf u_pinmux_jtag_buf_lc (
414+
.en_i(lc_tap_en_q),
365415
.req_i(lc_jtag_req),
366416
.req_o(lc_jtag_o),
367417
.rsp_i(lc_jtag_i),
368418
.rsp_o(lc_jtag_rsp)
369419
);
370420
pinmux_jtag_buf u_pinmux_jtag_buf_rv (
421+
.en_i(rv_tap_en_q),
371422
.req_i(rv_jtag_req),
372423
.req_o(rv_jtag_o),
373424
.rsp_i(rv_jtag_i),
374425
.rsp_o(rv_jtag_rsp)
375426
);
376427
pinmux_jtag_buf u_pinmux_jtag_buf_dft (
428+
.en_i(dft_tap_en_q),
377429
.req_i(dft_jtag_req),
378430
.req_o(dft_jtag_o),
379431
.rsp_i(dft_jtag_i),

hw/top_earlgrey/ip_autogen/pinmux/lint/pinmux.waiver

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ waive -rules RESET_MUX -location {pinmux_strap_sampling.sv} -regexp {Asynchronou
2222
waive -rules {CLOCK_DRIVER CLOCK_MUX} -location {pinmux_strap_sampling.sv} -regexp {'(lc|rv)_jtag_req.tck' is driven( by a multiplexer)? here,( and)? used as a clock 'tck_i' at dmi_jtag_tap.sv} \
2323
-comment "These signals are muxed using the JTAG Selection Mux."
2424

25+
waive -rules {CLOCK_DRIVER CLOCK_MUX} -location {pinmux_jtag_buf.sv} -regexp {'tck_gated' is driven by instance 'u_prim_and2_tck' of module 'prim_and2', and used as a clock 'tck_i' at dmi_jtag_tap.sv} \
26+
-comment "TCK is gated per TAP by a hand-instantiated prim_and2 cell inside pinmux_jtag_buf, enabled by the TAP Selection Mux, before being re-buffered onto req_o.tck by prim_clock_buf."
27+
2528
waive -rules CLOCK_MUX -location {pinmux_strap_sampling.sv pinmux.sv} -regexp {Clock '(in_padring_i\[38\]|mio_in_i\[38\]|jtag_req.tck)' reaches a multiplexer here, used as a clock 'tck_i' at dmi_jtag_tap.sv} \
2629
-comment "The 'mio_in_i[TckPadIdx]' input signal is connected to 'jtag_req.tck' which eventually feeds into the JTAG Selection Mux."
2730

hw/top_earlgrey/ip_autogen/pinmux/pinmux.core

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ filesets:
1212
- lowrisc:prim:all
1313
- lowrisc:prim:clock_buf
1414
- lowrisc:prim:buf
15+
- lowrisc:prim:and2
1516
- lowrisc:prim:lc_dec
1617
- lowrisc:prim:lc_sync
1718
- lowrisc:prim:lc_sender

hw/top_earlgrey/ip_autogen/pinmux/rtl/pinmux_jtag_buf.sv

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,29 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
module pinmux_jtag_buf (
6+
// Gates TCK/TRSTN below. Holds this TAP's clock off and reset asserted when deselected.
7+
input logic en_i,
68
input jtag_pkg::jtag_req_t req_i,
79
output jtag_pkg::jtag_req_t req_o,
810
input jtag_pkg::jtag_rsp_t rsp_i,
911
output jtag_pkg::jtag_rsp_t rsp_o
1012
);
1113

14+
logic tck_gated;
15+
prim_and2 #(.Width(1)) u_prim_and2_tck (
16+
.in0_i(en_i),
17+
.in1_i(req_i.tck),
18+
.out_o(tck_gated)
19+
);
20+
// This buffer is used in the constraints to define a generated clock
1221
prim_clock_buf prim_clock_buf_tck (
13-
.clk_i(req_i.tck),
22+
.clk_i(tck_gated),
1423
.clk_o(req_o.tck)
1524
);
16-
prim_buf prim_buf_trst_n (
17-
.in_i (req_i.trst_n),
25+
26+
prim_and2 #(.Width(1)) u_prim_and2_trst_n (
27+
.in0_i(en_i),
28+
.in1_i(req_i.trst_n),
1829
.out_o(req_o.trst_n)
1930
);
2031
prim_buf prim_buf_tms (

0 commit comments

Comments
 (0)