blob: 1cd3da1bccf6a2ce0a5b72da15ee1aa407a377b4 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
// Top-level debug module (DM)
//
// This module implements the RISC-V debug specification version 0.13,
//
// This toplevel wraps the PULP debug module available from
// https://github.com/pulp-platform/riscv-dbg to match the needs of
// the TL-UL-based lowRISC chip design.
`include "prim_assert.sv"
module rv_dm
import rv_dm_reg_pkg::*;
#(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
parameter logic [31:0] IdcodeValue = 32'h 0000_0001
) (
input logic clk_i, // clock
input logic rst_ni, // asynchronous reset active low, connect PoR
// here, not the system reset
// SEC_CM: LC_HW_DEBUG_EN.INTERSIG.MUBI
// HW Debug lifecycle enable signal (live version from the life cycle controller)
input lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i,
// HW Debug lifecycle enable signal (latched version from pinmux, only used for JTAG/TAP gating)
input lc_ctrl_pkg::lc_tx_t pinmux_hw_debug_en_i,
input prim_mubi_pkg::mubi4_t scanmode_i,
input scan_rst_ni,
output logic ndmreset_req_o, // non-debug module reset
output logic dmactive_o, // debug module is active
output logic [NrHarts-1:0] debug_req_o, // async debug request
input logic [NrHarts-1:0] unavailable_i, // communicate whether the hart is unavailable
// (e.g.: power down)
// bus device for comportable CSR access
input tlul_pkg::tl_h2d_t regs_tl_d_i,
output tlul_pkg::tl_d2h_t regs_tl_d_o,
// bus device with debug memory, for an execution based technique
input tlul_pkg::tl_h2d_t mem_tl_d_i,
output tlul_pkg::tl_d2h_t mem_tl_d_o,
// bus host, for system bus accesses
output tlul_pkg::tl_h2d_t sba_tl_h_o,
input tlul_pkg::tl_d2h_t sba_tl_h_i,
// Alerts
input prim_alert_pkg::alert_rx_t [NumAlerts-1:0] alert_rx_i,
output prim_alert_pkg::alert_tx_t [NumAlerts-1:0] alert_tx_o,
input jtag_pkg::jtag_req_t jtag_i,
output jtag_pkg::jtag_rsp_t jtag_o
);
///////////////////////////
// Parameter Definitions //
///////////////////////////
import prim_mubi_pkg::mubi4_bool_to_mubi;
import prim_mubi_pkg::mubi4_test_true_strict;
import lc_ctrl_pkg::lc_tx_test_true_strict;
`ASSERT_INIT(paramCheckNrHarts, NrHarts > 0)
// static debug hartinfo
localparam dm::hartinfo_t DebugHartInfo = '{
zero1: '0,
nscratch: 2, // Debug module needs at least two scratch regs
zero0: 0,
dataaccess: 1'b1, // data registers are memory mapped in the debugger
datasize: dm::DataCount,
dataaddr: dm::DataAddr
};
dm::hartinfo_t [NrHarts-1:0] hartinfo;
for (genvar i = 0; i < NrHarts; i++) begin : gen_dm_hart_ctrl
assign hartinfo[i] = DebugHartInfo;
end
// Currently only 32 bit busses are supported by our TL-UL IP
localparam int BusWidth = 32;
// all harts have contiguous IDs
localparam logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}};
///////////////
// CSR Nodes //
///////////////
tlul_pkg::tl_h2d_t mem_tl_win_h2d;
tlul_pkg::tl_d2h_t mem_tl_win_d2h;
rv_dm_reg_pkg::rv_dm_regs_reg2hw_t regs_reg2hw;
logic regs_intg_error, rom_intg_error;
logic sba_gate_intg_error, rom_gate_intg_error;
rv_dm_regs_reg_top u_reg_regs (
.clk_i,
.rst_ni,
.tl_i (regs_tl_d_i ),
.tl_o (regs_tl_d_o ),
.reg2hw (regs_reg2hw ),
// SEC_CM: BUS.INTEGRITY
.intg_err_o(regs_intg_error),
.devmode_i (1'b1 )
);
// We are not instantiating the generated rv_dm_mem_reg_top, since the registers are actually
// implemented inside the vendored-in rv_dm module from the PULP project.
assign mem_tl_win_h2d = mem_tl_d_i;
assign mem_tl_d_o = mem_tl_win_d2h;
// Alerts
logic [NumAlerts-1:0] alert_test, alerts;
assign alerts[0] = regs_intg_error | rom_intg_error |
sba_gate_intg_error | rom_gate_intg_error;
assign alert_test = {
regs_reg2hw.alert_test.q &
regs_reg2hw.alert_test.qe
};
for (genvar i = 0; i < NumAlerts; i++) begin : gen_alert_tx
prim_alert_sender #(
.AsyncOn(AlertAsyncOn[i]),
.IsFatal(1'b1)
) u_prim_alert_sender (
.clk_i,
.rst_ni,
.alert_test_i ( alert_test[i] ),
.alert_req_i ( alerts[0] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end
// Decode multibit scanmode enable
logic testmode;
assign testmode = mubi4_test_true_strict(scanmode_i);
///////////////////////
// Life Cycle Gating //
///////////////////////
// Debug enable gating.
localparam int LcEnDebugReqVal = 4 - 1;
localparam int LcEnResetReqVal = LcEnDebugReqVal + NrHarts;
// +1 to get number of bits and another +1 because LcEnLastPos is one more than LcEnResetReq.
localparam int RvDmLcEnSize = $clog2(LcEnResetReqVal + 2);
typedef enum logic [RvDmLcEnSize-1:0] {
LcEnFetch,
LcEnRom,
LcEnSba,
// LcEnDebugReq[NrHarts], <= this unfortunately does not work - SV-LRM mandates the use of
// integral numbers. Parameters are not allowed in this context.
LcEnDebugReq,
// The above literal accommodates NrHarts number of debug requests - so we number the next
// literal accordingly.
LcEnResetReq = RvDmLcEnSize'(LcEnResetReqVal),
// LcEnLastPos must immediately follow LcEnResetReq to calculate RvDmLcEnSize.
LcEnLastPos
} rv_dm_lc_en_e;
// These must be equal so that the difference between LcEnResetReq and LcEnDebugReq is NrHarts.
`ASSERT(RvDmLcEnDebugVal_A, int'(LcEnDebugReq) == LcEnDebugReqVal)
// debug enable gating
typedef enum logic [3:0] {
PmEnDmiReq,
PmEnJtagIn,
PmEnJtagOut,
PmEnLastPos
} rv_dm_pm_en_e;
lc_ctrl_pkg::lc_tx_t [LcEnLastPos-1:0] lc_hw_debug_en;
prim_lc_sync #(
.NumCopies(int'(LcEnLastPos))
) u_lc_en_sync (
.clk_i,
.rst_ni,
.lc_en_i(lc_hw_debug_en_i),
.lc_en_o(lc_hw_debug_en)
);
lc_ctrl_pkg::lc_tx_t [PmEnLastPos-1:0] pinmux_hw_debug_en;
prim_lc_sync #(
.NumCopies(int'(PmEnLastPos))
) u_pm_en_sync (
.clk_i,
.rst_ni,
.lc_en_i(pinmux_hw_debug_en_i),
.lc_en_o(pinmux_hw_debug_en)
);
dm::dmi_req_t dmi_req;
dm::dmi_resp_t dmi_rsp;
logic dmi_req_valid, dmi_req_ready;
logic dmi_rsp_valid, dmi_rsp_ready;
logic dmi_rst_n;
logic reset_req_en;
logic ndmreset_req;
logic ndmreset_req_qual;
// SEC_CM: DM_EN.CTRL.LC_GATED
assign reset_req_en = lc_tx_test_true_strict(lc_hw_debug_en[LcEnResetReq]);
assign ndmreset_req_o = ndmreset_req_qual & reset_req_en;
logic dmi_en;
// SEC_CM: DM_EN.CTRL.LC_GATED
assign dmi_en = lc_tx_test_true_strict(pinmux_hw_debug_en[PmEnDmiReq]);
/////////////////////////////////////////
// System Bus Access Port (TL-UL Host) //
/////////////////////////////////////////
logic host_req;
logic [BusWidth-1:0] host_add;
logic host_we;
logic [BusWidth-1:0] host_wdata;
logic [BusWidth/8-1:0] host_be;
logic host_gnt;
logic host_r_valid;
logic [BusWidth-1:0] host_r_rdata;
logic host_r_err;
logic host_r_other_err;
// SEC_CM: DM_EN.CTRL.LC_GATED
// SEC_CM: SBA_TL_LC_GATE.FSM.SPARSE
tlul_pkg::tl_h2d_t sba_tl_h_o_int;
tlul_pkg::tl_d2h_t sba_tl_h_i_int;
tlul_lc_gate #(
.NumGatesPerDirection(2)
) u_tlul_lc_gate_sba (
.clk_i,
.rst_ni,
.tl_h2d_i(sba_tl_h_o_int),
.tl_d2h_o(sba_tl_h_i_int),
.tl_h2d_o(sba_tl_h_o),
.tl_d2h_i(sba_tl_h_i),
.lc_en_i (lc_hw_debug_en[LcEnSba]),
.err_o (sba_gate_intg_error),
.flush_req_i('0),
.flush_ack_o(),
.resp_pending_o()
);
tlul_adapter_host #(
.MAX_REQS(1),
.EnableDataIntgGen(1),
.EnableRspDataIntgCheck(1)
) tl_adapter_host_sba (
.clk_i,
.rst_ni,
.req_i (host_req),
.instr_type_i (prim_mubi_pkg::MuBi4False),
.gnt_o (host_gnt),
.addr_i (host_add),
.we_i (host_we),
.wdata_i (host_wdata),
.wdata_intg_i ('0),
.be_i (host_be),
.valid_o (host_r_valid),
.rdata_o (host_r_rdata),
.rdata_intg_o (),
.err_o (host_r_err),
// Note: This bus integrity error is not connected to the alert due to a few reasons:
// 1) the SBA module is not active in production life cycle states.
// 2) there is value in being able to accept incoming transactions with integrity
// errors during test / debug life cycle states so that the system can be debugged
// without triggering alerts.
// 3) the error condition is hooked up to an error CSR that can be read out by the debugger
// via JTAG so that bus integrity errors can be told appart from regular bus errors.
.intg_err_o (host_r_other_err),
.tl_o (sba_tl_h_o_int),
.tl_i (sba_tl_h_i_int)
);
//////////////////////////////////////
// Debug Memory Port (TL-UL Device) //
//////////////////////////////////////
logic device_req;
logic device_we;
logic device_re;
logic [BusWidth/8-1:0] device_be;
logic [BusWidth-1:0] device_wdata;
logic [BusWidth-1:0] device_rdata;
logic device_err;
logic [BusWidth-1:0] device_addr_aligned;
logic [MemAw-1:0] device_addr;
assign device_addr_aligned = BusWidth'(device_addr);
logic [NrHarts-1:0] debug_req_en;
logic [NrHarts-1:0] debug_req;
for (genvar i = 0; i < NrHarts; i++) begin : gen_debug_req_hart
// SEC_CM: DM_EN.CTRL.LC_GATED
assign debug_req_en[i] = lc_tx_test_true_strict(lc_hw_debug_en[LcEnDebugReq + i]);
end
assign debug_req_o = debug_req & debug_req_en;
// Gating of JTAG signals
jtag_pkg::jtag_req_t jtag_in_int;
jtag_pkg::jtag_rsp_t jtag_out_int;
assign jtag_in_int = (lc_tx_test_true_strict(pinmux_hw_debug_en[PmEnJtagIn])) ? jtag_i : '0;
assign jtag_o = (lc_tx_test_true_strict(pinmux_hw_debug_en[PmEnJtagOut])) ? jtag_out_int : '0;
// Bound-in DPI module replaces the TAP
`ifndef DMIDirectTAP
logic tck_muxed;
logic trst_n_muxed;
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_prim_clock_mux2 (
.clk0_i(jtag_in_int.tck),
.clk1_i(clk_i),
.sel_i (testmode),
.clk_o (tck_muxed)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_prim_rst_n_mux2 (
.clk0_i(jtag_in_int.trst_n),
.clk1_i(scan_rst_ni),
.sel_i (testmode),
.clk_o (trst_n_muxed)
);
// JTAG TAP
dmi_jtag #(
.IdcodeValue (IdcodeValue)
) dap (
.clk_i (clk_i),
.rst_ni (rst_ni),
.testmode_i (testmode),
.test_rst_ni (scan_rst_ni),
.dmi_rst_no (dmi_rst_n),
.dmi_req_o (dmi_req),
.dmi_req_valid_o (dmi_req_valid),
.dmi_req_ready_i (dmi_req_ready & dmi_en),
.dmi_resp_i (dmi_rsp ),
.dmi_resp_ready_o (dmi_rsp_ready),
.dmi_resp_valid_i (dmi_rsp_valid & dmi_en),
//JTAG
.tck_i (tck_muxed),
.tms_i (jtag_in_int.tms),
.trst_ni (trst_n_muxed),
.td_i (jtag_in_int.tdi),
.td_o (jtag_out_int.tdo),
.tdo_oe_o (jtag_out_int.tdo_oe)
);
`endif
// SEC_CM: DM_EN.CTRL.LC_GATED
// SEC_CM: MEM_TL_LC_GATE.FSM.SPARSE
tlul_pkg::tl_h2d_t mem_tl_win_h2d_gated;
tlul_pkg::tl_d2h_t mem_tl_win_d2h_gated;
tlul_lc_gate #(
.NumGatesPerDirection(2)
) u_tlul_lc_gate_rom (
.clk_i,
.rst_ni,
.tl_h2d_i(mem_tl_win_h2d),
.tl_d2h_o(mem_tl_win_d2h),
.tl_h2d_o(mem_tl_win_h2d_gated),
.tl_d2h_i(mem_tl_win_d2h_gated),
.flush_req_i(ndmreset_req),
.flush_ack_o(ndmreset_req_qual),
.resp_pending_o(),
.lc_en_i (lc_hw_debug_en[LcEnRom]),
.err_o (rom_gate_intg_error)
);
prim_mubi_pkg::mubi4_t en_ifetch;
// SEC_CM: DM_EN.CTRL.LC_GATED, EXEC.CTRL.MUBI
assign en_ifetch = mubi4_bool_to_mubi(lc_tx_test_true_strict(lc_hw_debug_en[LcEnFetch]));
tlul_adapter_reg #(
.CmdIntgCheck (1),
.EnableRspIntgGen (1),
.EnableDataIntgGen(1),
.RegAw (MemAw),
.RegDw (BusWidth),
.AccessLatency (1)
) i_tlul_adapter_reg (
.clk_i,
.rst_ni,
.tl_i (mem_tl_win_h2d_gated),
.tl_o (mem_tl_win_d2h_gated),
// SEC_CM: EXEC.CTRL.MUBI
.en_ifetch_i (en_ifetch),
// SEC_CM: BUS.INTEGRITY
.intg_error_o(rom_intg_error),
.re_o (device_re),
.we_o (device_we),
.addr_o (device_addr),
.wdata_o (device_wdata),
.be_o (device_be),
.busy_i (1'b0),
.rdata_i (device_rdata),
.error_i (device_err)
);
assign device_req = device_we || device_re;
///////////////////////////
// Debug Module Instance //
///////////////////////////
dm_top #(
.NrHarts (NrHarts),
.BusWidth (BusWidth),
.SelectableHarts(SelectableHarts),
// The debug module provides a simplified ROM for systems that map the debug ROM to offset 0x0
// on the system bus. In that case, only one scratch register has to be implemented in the core.
// However, we require that the DM can be placed at arbitrary offsets in the system, which
// requires the generalized debug ROM implementation and two scratch registers. We hence set
// this parameter to a non-zero value (inside dm_mem, this just feeds into a comparison with 0).
.DmBaseAddress (1)
) u_dm_top (
.clk_i,
.rst_ni,
.testmode_i (testmode ),
.ndmreset_o (ndmreset_req ),
.dmactive_o,
.debug_req_o (debug_req ),
.unavailable_i,
.hartinfo_i (hartinfo ),
.slave_req_i (device_req ),
.slave_we_i (device_we ),
.slave_addr_i (device_addr_aligned ),
.slave_be_i (device_be ),
.slave_wdata_i (device_wdata ),
.slave_rdata_o (device_rdata ),
.slave_err_o (device_err ),
.master_req_o (host_req ),
.master_add_o (host_add ),
.master_we_o (host_we ),
.master_wdata_o (host_wdata ),
.master_be_o (host_be ),
.master_gnt_i (host_gnt ),
.master_r_valid_i (host_r_valid ),
.master_r_err_i (host_r_err ),
.master_r_other_err_i (host_r_other_err ),
.master_r_rdata_i (host_r_rdata ),
.dmi_rst_ni (dmi_rst_n ),
.dmi_req_valid_i (dmi_req_valid & dmi_en),
.dmi_req_ready_o (dmi_req_ready ),
.dmi_req_i (dmi_req ),
.dmi_resp_valid_o (dmi_rsp_valid ),
.dmi_resp_ready_i (dmi_rsp_ready & dmi_en),
.dmi_resp_o (dmi_rsp )
);
////////////////
// Assertions //
////////////////
`ASSERT_KNOWN(TlRegsDValidKnown_A, regs_tl_d_o.d_valid)
`ASSERT_KNOWN(TlRegsAReadyKnown_A, regs_tl_d_o.a_ready)
`ASSERT_KNOWN(TlMemDValidKnown_A, mem_tl_d_o.d_valid)
`ASSERT_KNOWN(TlMemAReadyKnown_A, mem_tl_d_o.a_ready)
`ASSERT_KNOWN(TlSbaAValidKnown_A, sba_tl_h_o.a_valid)
`ASSERT_KNOWN(TlSbaDReadyKnown_A, sba_tl_h_o.d_ready)
`ASSERT_KNOWN(NdmresetOKnown_A, ndmreset_req_o)
`ASSERT_KNOWN(DmactiveOKnown_A, dmactive_o)
`ASSERT_KNOWN(DebugReqOKnown_A, debug_req_o)
// JTAG TDO is driven by an inverted TCK in dmi_jtag_tap.sv
`ASSERT_KNOWN(JtagRspOTdoKnown_A, jtag_o.tdo, !jtag_i.tck, !jtag_i.trst_n)
`ASSERT_KNOWN(JtagRspOTdoOeKnown_A, jtag_o.tdo_oe, !jtag_i.tck, !jtag_i.trst_n)
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(SbaTlLcGateFsm_A,
u_tlul_lc_gate_sba.u_state_regs, alert_tx_o[0])
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(RomTlLcGateFsm_A,
u_tlul_lc_gate_rom.u_state_regs, alert_tx_o[0])
// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg_regs, alert_tx_o[0])
endmodule