blob: 3b6684ebd66fb708fc1d9eec6c00b4752772d8dd [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
module pinmux_strap_sampling
import pinmux_pkg::*;
import pinmux_reg_pkg::*;
import prim_pad_wrapper_pkg::*;
import lc_ctrl_pkg::*;
#(
// Taget-specific pinmux configuration passed down from the
// target-specific top-level.
parameter target_cfg_t TargetCfg = DefaultTargetCfg
) (
input clk_i,
input rst_ni,
input prim_mubi_pkg::mubi4_t scanmode_i,
// To padring side
output pad_attr_t [NumIOs-1:0] attr_padring_o,
output logic [NumIOs-1:0] out_padring_o,
output logic [NumIOs-1:0] oe_padring_o,
input logic [NumIOs-1:0] in_padring_i,
// To core side
input pad_attr_t [NumIOs-1:0] attr_core_i,
input logic [NumIOs-1:0] out_core_i,
input logic [NumIOs-1:0] oe_core_i,
output logic [NumIOs-1:0] in_core_o,
// Used for TAP qualification
input logic strap_en_i,
input lc_tx_t lc_dft_en_i,
input lc_tx_t lc_hw_debug_en_i,
input lc_tx_t lc_check_byp_en_i,
input lc_tx_t lc_escalate_en_i,
output lc_tx_t pinmux_hw_debug_en_o,
// Sampled values for DFT straps
output dft_strap_test_req_t dft_strap_test_o,
// Hold tap strap select
input dft_hold_tap_sel_i,
// Qualified JTAG signals for TAPs
output jtag_pkg::jtag_req_t lc_jtag_o,
input jtag_pkg::jtag_rsp_t lc_jtag_i,
output jtag_pkg::jtag_req_t rv_jtag_o,
input jtag_pkg::jtag_rsp_t rv_jtag_i,
output jtag_pkg::jtag_req_t dft_jtag_o,
input jtag_pkg::jtag_rsp_t dft_jtag_i
);
/////////////////////////////////////
// Life cycle signal synchronizers //
/////////////////////////////////////
prim_mubi_pkg::mubi4_t [0:0] scanmode;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(0) // clock/reset below is only used for SVAs.
) u_por_scanmode_sync (
.clk_i,
.rst_ni,
.mubi_i(scanmode_i),
.mubi_o(scanmode)
);
typedef enum logic [1:0] {
DftEnSample,
DftEnTapSel,
DftEnLast
} lc_dft_en_e;
lc_tx_t [DftEnLast-1:0] lc_dft_en;
prim_lc_sync #(
.NumCopies(int'(DftEnLast))
) u_prim_lc_sync_lc_dft_en (
.clk_i,
.rst_ni,
.lc_en_i(lc_dft_en_i),
.lc_en_o(lc_dft_en)
);
lc_tx_t [0:0] lc_hw_debug_en;
prim_lc_sync #(
.NumCopies(1)
) u_prim_lc_sync_lc_hw_debug_en (
.clk_i,
.rst_ni,
.lc_en_i(lc_hw_debug_en_i),
.lc_en_o(lc_hw_debug_en)
);
lc_tx_t [0:0] lc_check_byp_en;
prim_lc_sync #(
.NumCopies(1)
) u_prim_lc_sync_lc_check_byp_en (
.clk_i,
.rst_ni,
.lc_en_i(lc_check_byp_en_i),
.lc_en_o(lc_check_byp_en)
);
lc_tx_t [0:0] lc_escalate_en;
prim_lc_sync #(
.NumCopies(1)
) u_prim_lc_sync_lc_escalate_en (
.clk_i,
.rst_ni,
.lc_en_i(lc_escalate_en_i),
.lc_en_o(lc_escalate_en)
);
/////////////////////////////
// LC_HW_DEBUG_EN Latching //
/////////////////////////////
// In order to keep a RV_DM JTAG debug session alive during NDM reset, we need to memorize the
// state of lc_hw_debug_en, since OTP/LC will be reset as part of NDM reset (the only parts not
// reset are in the pwr/rst/clkmgrs, RV_DM and this strap sampling module). We sample the life
// cycle signal state when the strap sampling pulse is asserted bu the PWRMGR. This pulse is
// asserted once during boot (and not after an NDM reset).
//
// Note that DFT TAP selection is not affected by this since we always consume the life value for
// lc_dft_en. We also make sure to invalidate the sampled lc_hw_debug_en whenever lc_check_byp_en
// or lc_escalate_en are not OFF. lc_escalate_en is asserted as part of an escalation, and
// lc_check_byp_en is asserted whenever a life cycle transition is initiated (it causes the OTP
// controller to skip background checks on the life cycle partition as it undergoes
// modification). This makes sure that the sampled value here does not survive a life cycle
// transition.
//
// Finally, note that there is secondary gating on the RV_DM and DFT TAPs that is always consuming
// live lc_hw_debug_en and lc_dft_en signals for added protection.
// Convert the strap enable pulse to a mubi signal and mask lc_hw_debug_en with it.
lc_tx_t lc_strap_en, lc_hw_debug_en_masked;
assign lc_strap_en = lc_tx_bool_to_lc_tx(strap_en_i);
assign lc_hw_debug_en_masked = lc_tx_and_hi(lc_strap_en, lc_hw_debug_en[0]);
// Output ON if
// - If the strap sampling pulse is asserted and lc_hw_debug_en is ON
// - If the pinmux_hw_debug_en_q is already set to ON (this is the latching feedback loop)
// Note: make sure we use a hardened, rectifying OR function since otherwise two non-strict
// values may produce a strict ON value.
lc_tx_t hw_debug_en_set, pinmux_hw_debug_en_q;
prim_lc_or_hardened #(
.ActVal(On)
) u_prim_lc_or_hardened (
.clk_i,
.rst_ni,
.lc_en_a_i(lc_hw_debug_en_masked),
.lc_en_b_i(pinmux_hw_debug_en_q),
.lc_en_o (hw_debug_en_set)
);
// Output ON if both lc_check_byp_en and lc_escalate_en are set to OFF.
lc_tx_t hw_debug_en_gating;
assign hw_debug_en_gating = lc_tx_inv(lc_tx_and_lo(lc_check_byp_en[0], lc_escalate_en[0]));
// Gate the hw_debug_en_set signal and feed it into the latching flop.
lc_tx_t pinmux_hw_debug_en_d;
assign pinmux_hw_debug_en_d = lc_tx_and_hi(hw_debug_en_set, hw_debug_en_gating);
prim_lc_sender u_prim_lc_sender_pinmux_hw_debug_en (
.clk_i,
.rst_ni,
.lc_en_i(pinmux_hw_debug_en_d),
.lc_en_o(pinmux_hw_debug_en_q)
);
typedef enum logic [1:0] {
HwDebugEnSample,
HwDebugEnTapSel,
HwDebugEnRvDmOut,
HwDebugEnLast
} pinmux_hw_debug_en_e;
lc_tx_t [HwDebugEnLast-1:0] pinmux_hw_debug_en;
prim_lc_sync #(
.NumCopies(int'(HwDebugEnLast)),
.AsyncOn(0) // no sync needed
) u_prim_lc_sync_pinmux_hw_debug_en (
.clk_i,
.rst_ni,
.lc_en_i(pinmux_hw_debug_en_q),
.lc_en_o(pinmux_hw_debug_en)
);
// We send this latched version over to the RV_DM in order to gate the JTAG signals and TAP side.
// Note that the bus side will remain gated with the live lc_hw_debug_en value inside RV_DM.
assign pinmux_hw_debug_en_o = pinmux_hw_debug_en[HwDebugEnRvDmOut];
// Check that we can correctly latch upon strap_en_i
`ASSERT(LcHwDebugEnSet_A,
(lc_tx_test_true_strict(lc_hw_debug_en[0]) ||
lc_tx_test_true_strict(pinmux_hw_debug_en_q)) &&
lc_tx_test_false_strict(lc_check_byp_en[0]) &&
lc_tx_test_false_strict(lc_escalate_en[0]) &&
strap_en_i
|=>
lc_tx_test_true_strict(pinmux_hw_debug_en_q))
// Check that latching ON can only occur if lc_hw_debug_en_i is set.
`ASSERT(LcHwDebugEnSetRev0_A,
lc_tx_test_false_loose(pinmux_hw_debug_en_q) ##1
lc_tx_test_true_strict(pinmux_hw_debug_en_q)
|->
$past(lc_tx_test_true_strict(lc_hw_debug_en[0])))
// Check that latching ON can only occur if strap_en_i is set.
`ASSERT(LcHwDebugEnSetRev1_A,
lc_tx_test_false_loose(pinmux_hw_debug_en_q) ##1
lc_tx_test_true_strict(pinmux_hw_debug_en_q)
|->
$past(strap_en_i))
// Check that any non-OFF value on lc_check_byp_en_i and
// lc_escalate_en_i clears the latched value.
`ASSERT(LcHwDebugEnClear_A,
lc_tx_test_true_loose(lc_check_byp_en[0]) ||
lc_tx_test_true_loose(lc_escalate_en[0])
|=>
lc_tx_test_false_loose(pinmux_hw_debug_en_q))
//////////////////////////
// Strap Sampling Logic //
//////////////////////////
logic dft_strap_valid_d, dft_strap_valid_q;
logic lc_strap_sample_en, rv_strap_sample_en, dft_strap_sample_en;
logic [NTapStraps-1:0] tap_strap_d, tap_strap_q;
logic [NDFTStraps-1:0] dft_strap_d, dft_strap_q;
// The LC strap at index 0 has a slightly different
// enable condition than the DFT strap at index 1.
assign tap_strap_d[0] = (lc_strap_sample_en) ? in_padring_i[TargetCfg.tap_strap0_idx] :
tap_strap_q[0];
assign tap_strap_d[1] = (rv_strap_sample_en) ? in_padring_i[TargetCfg.tap_strap1_idx] :
tap_strap_q[1];
// We're always using the DFT strap sample enable for the DFT straps.
assign dft_strap_d = (dft_strap_sample_en) ? {in_padring_i[TargetCfg.dft_strap1_idx],
in_padring_i[TargetCfg.dft_strap0_idx]} :
dft_strap_q;
assign dft_strap_valid_d = dft_strap_sample_en | dft_strap_valid_q;
assign dft_strap_test_o.valid = dft_strap_valid_q;
assign dft_strap_test_o.straps = dft_strap_q;
// During dft enabled states, we continously sample all straps unless
// told not to do so by external dft logic
logic tap_sampling_en;
logic dft_hold_tap_sel;
// Delay the strap sampling pulse by one cycle so that the pinmux_hw_debug_en above can
// propagate through the pinmux_hw_debug_en_q flop.
logic strap_en_q;
prim_buf #(
.Width(1)
) u_buf_hold_tap (
.in_i(dft_hold_tap_sel_i),
.out_o(dft_hold_tap_sel)
);
assign tap_sampling_en = lc_tx_test_true_strict(lc_dft_en[DftEnSample]) & ~dft_hold_tap_sel;
always_comb begin : p_strap_sampling
lc_strap_sample_en = 1'b0;
rv_strap_sample_en = 1'b0;
dft_strap_sample_en = 1'b0;
// Initial strap sampling pulse from pwrmgr,
// qualified by life cycle signals.
// The DFT-mode straps are always sampled only once.
if (strap_en_q && tap_sampling_en) begin
dft_strap_sample_en = 1'b1;
end
// In DFT-enabled life cycle states we continously
// sample the TAP straps to be able to switch back and
// forth between different TAPs.
if (strap_en_q || tap_sampling_en) begin
lc_strap_sample_en = 1'b1;
if (lc_tx_test_true_strict(pinmux_hw_debug_en[HwDebugEnSample])) begin
rv_strap_sample_en = 1'b1;
end
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin : p_strap_sample
if (!rst_ni) begin
tap_strap_q <= '0;
dft_strap_q <= '0;
dft_strap_valid_q <= 1'b0;
strap_en_q <= 1'b0;
end else begin
tap_strap_q <= tap_strap_d;
dft_strap_q <= dft_strap_d;
dft_strap_valid_q <= dft_strap_valid_d;
strap_en_q <= strap_en_i;
end
end
///////////////////////
// TAP Selection Mux //
///////////////////////
logic jtag_en;
tap_strap_t tap_strap;
jtag_pkg::jtag_req_t jtag_req, lc_jtag_req, rv_jtag_req, dft_jtag_req;
jtag_pkg::jtag_rsp_t jtag_rsp, lc_jtag_rsp, rv_jtag_rsp, dft_jtag_rsp;
// This muxes the JTAG signals to the correct TAP, based on the
// sampled straps. Further, the individual JTAG signals are gated
// using the corresponding life cycle signal.
assign tap_strap = tap_strap_t'(tap_strap_q);
`ASSERT_KNOWN(TapStrapKnown_A, tap_strap)
always_comb begin : p_tap_mux
jtag_rsp = '0;
// Note that this holds the JTAGs in reset
// when they are not selected.
lc_jtag_req = '0;
rv_jtag_req = '0;
dft_jtag_req = '0;
// This activates the TDO override further below.
jtag_en = 1'b0;
unique case (tap_strap)
LcTapSel: begin
lc_jtag_req = jtag_req;
jtag_rsp = lc_jtag_rsp;
jtag_en = 1'b1;
end
RvTapSel: begin
if (lc_tx_test_true_strict(pinmux_hw_debug_en[HwDebugEnTapSel])) begin
rv_jtag_req = jtag_req;
jtag_rsp = rv_jtag_rsp;
jtag_en = 1'b1;
end
end
DftTapSel: begin
if (lc_tx_test_true_strict(lc_dft_en[DftEnTapSel])) begin
dft_jtag_req = jtag_req;
jtag_rsp = dft_jtag_rsp;
jtag_en = 1'b1;
end
end
default: ;
endcase // tap_strap_t'(tap_strap_q)
end
// Insert hand instantiated buffers for
// these signals to prevent further optimization.
pinmux_jtag_buf u_pinmux_jtag_buf_lc (
.req_i(lc_jtag_req),
.req_o(lc_jtag_o),
.rsp_i(lc_jtag_i),
.rsp_o(lc_jtag_rsp)
);
pinmux_jtag_buf u_pinmux_jtag_buf_rv (
.req_i(rv_jtag_req),
.req_o(rv_jtag_o),
.rsp_i(rv_jtag_i),
.rsp_o(rv_jtag_rsp)
);
pinmux_jtag_buf u_pinmux_jtag_buf_dft (
.req_i(dft_jtag_req),
.req_o(dft_jtag_o),
.rsp_i(dft_jtag_i),
.rsp_o(dft_jtag_rsp)
);
//////////////////////
// TAP Input Muxes //
//////////////////////
// Inputs connections
assign jtag_req.tck = in_padring_i[TargetCfg.tck_idx];
assign jtag_req.tms = in_padring_i[TargetCfg.tms_idx];
assign jtag_req.tdi = in_padring_i[TargetCfg.tdi_idx];
// Note that this resets the selected TAP controller in
// scanmode. If the TAP controller needs to be active during
// reset, this reset bypass needs to be adapted accordingly.
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_rst_por_aon_n_mux (
.clk0_i(in_padring_i[TargetCfg.trst_idx]),
.clk1_i(rst_ni),
.sel_i(prim_mubi_pkg::mubi4_test_true_strict(scanmode[0])),
.clk_o(jtag_req.trst_n)
);
// Input tie-off muxes and output overrides
for (genvar k = 0; k < NumIOs; k++) begin : gen_input_tie_off
if (k == TargetCfg.tck_idx ||
k == TargetCfg.tms_idx ||
k == TargetCfg.trst_idx ||
k == TargetCfg.tdi_idx ||
k == TargetCfg.tdo_idx) begin : gen_jtag_signal
// Tie off inputs.
assign in_core_o[k] = (jtag_en) ? 1'b0 : in_padring_i[k];
if (k == TargetCfg.tdo_idx) begin : gen_output_mux
// Override TDO output.
assign out_padring_o[k] = (jtag_en) ? jtag_rsp.tdo : out_core_i[k];
assign oe_padring_o[k] = (jtag_en) ? jtag_rsp.tdo_oe : oe_core_i[k];
end else begin : gen_output_tie_off
// Make sure these pads are set to high-z.
assign out_padring_o[k] = (jtag_en) ? 1'b0 : out_core_i[k];
assign oe_padring_o[k] = (jtag_en) ? 1'b0 : oe_core_i[k];
end
// Also reset all corresponding pad attributes to the default ('0) when JTAG is enabled.
// This disables functional pad features that may have been set, e.g., pull-up/pull-down.
assign attr_padring_o[k] = (jtag_en) ? '0 : attr_core_i[k];
end else begin : gen_other_inputs
assign attr_padring_o[k] = attr_core_i[k];
assign in_core_o[k] = in_padring_i[k];
assign out_padring_o[k] = out_core_i[k];
assign oe_padring_o[k] = oe_core_i[k];
end
end
////////////////
// Assertions //
////////////////
`ASSERT_INIT(tck_idxRange_A, TargetCfg.tck_idx >= 0 && TargetCfg.tck_idx < NumIOs)
`ASSERT_INIT(tms_idxRange_A, TargetCfg.tms_idx >= 0 && TargetCfg.tms_idx < NumIOs)
`ASSERT_INIT(trst_idxRange_A, TargetCfg.trst_idx >= 0 && TargetCfg.trst_idx < NumIOs)
`ASSERT_INIT(tdi_idxRange_A, TargetCfg.tdi_idx >= 0 && TargetCfg.tdi_idx < NumIOs)
`ASSERT_INIT(tdo_idxRange_A, TargetCfg.tdo_idx >= 0 && TargetCfg.tdo_idx < NumIOs)
`ASSERT_INIT(tap_strap0_idxRange_A, TargetCfg.tap_strap0_idx >= 0 &&
TargetCfg.tap_strap0_idx < NumIOs)
`ASSERT_INIT(tap_strap1_idxRange_A, TargetCfg.tap_strap1_idx >= 0 &&
TargetCfg.tap_strap1_idx < NumIOs)
`ASSERT_INIT(dft_strap0_idxRange_A, TargetCfg.dft_strap0_idx >= 0 &&
TargetCfg.dft_strap0_idx < NumIOs)
`ASSERT_INIT(dft_strap1_idxRange_A, TargetCfg.dft_strap1_idx >= 0 &&
TargetCfg.dft_strap1_idx < NumIOs)
// The strap sampling enable input shall be pulsed high for exactly one cycle after cold boot.
`ASSUME(PwrMgrStrapSampleOnce0_A, strap_en_i |=> !strap_en_i)
`ASSUME(PwrMgrStrapSampleOnce1_A, $fell(strap_en_i) |-> always !strap_en_i)
`ASSERT(RvTapOff0_A, lc_hw_debug_en_i == Off ##2 strap_en_i |=> rv_jtag_o == '0)
`ASSERT(RvTapOff1_A, pinmux_hw_debug_en[0] == Off |-> rv_jtag_o == '0)
`ASSERT(DftTapOff0_A, lc_dft_en_i == Off |-> ##2 dft_jtag_o == '0)
// These assumptions are only used in FPV. They will cause failures in simulations.
`ASSUME_FPV(RvTapOff2_A, lc_hw_debug_en_i == Off ##2 strap_en_i |=> rv_jtag_i == '0)
`ASSUME_FPV(RvTapOff3_A, pinmux_hw_debug_en[0] == Off |-> rv_jtag_i == '0)
`ASSUME_FPV(DftTapOff1_A, lc_dft_en_i == Off |-> ##2 dft_jtag_i == '0)
endmodule : pinmux_strap_sampling