blob: 1b5db985a7dfb329af5c86a8776e77bfccc1411d [file] [log] [blame] [edit]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// This module is the overall reset manager wrapper
`include "prim_assert.sv"
<%
from topgen.lib import Name
%>\
// This top level controller is fairly hardcoded right now, but will be switched to a template
module rstmgr
import rstmgr_pkg::*;
import rstmgr_reg_pkg::*;
import prim_mubi_pkg::mubi4_t;
#(
parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}},
parameter bit SecCheck = 1,
parameter int SecMaxSyncDelay = 2
) (
// Primary module clocks
input clk_i,
input rst_ni,
% for clk in reset_obj.get_clocks():
input clk_${clk}_i,
% endfor
input clk_por_i,
input rst_por_ni,
// POR input
input [PowerDomains-1:0] por_n_i,
// Bus Interface
input tlul_pkg::tl_h2d_t tl_i,
output tlul_pkg::tl_d2h_t tl_o,
// 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,
// pwrmgr interface
input pwrmgr_pkg::pwr_rst_req_t pwr_i,
output pwrmgr_pkg::pwr_rst_rsp_t pwr_o,
// software initiated reset request
output mubi4_t sw_rst_req_o,
// Interface to alert handler
input alert_pkg::alert_crashdump_t alert_dump_i,
// Interface to cpu crash dump
input rv_core_ibex_pkg::cpu_crash_dump_t cpu_dump_i,
// dft bypass
input scan_rst_ni,
// SEC_CM: SCAN.INTERSIG.MUBI
input prim_mubi_pkg::mubi4_t scanmode_i,
// Reset asserted indications going to alert handler
output rstmgr_rst_en_t rst_en_o,
// reset outputs
% for intf in export_rsts:
output rstmgr_${intf}_out_t resets_${intf}_o,
% endfor
output rstmgr_out_t resets_o
);
import prim_mubi_pkg::MuBi4False;
import prim_mubi_pkg::MuBi4True;
// receive POR and stretch
// The por is at first stretched and synced on clk_aon
// The rst_ni and pok_i input will be changed once AST is integrated
logic [PowerDomains-1:0] rst_por_aon_n;
for (genvar i = 0; i < PowerDomains; i++) begin : gen_rst_por_aon
// Declared as size 1 packed array to avoid FPV warning.
prim_mubi_pkg::mubi4_t [0:0] por_scanmode;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(0)
) u_por_scanmode_sync (
.clk_i,
.rst_ni,
.mubi_i(scanmode_i),
.mubi_o(por_scanmode)
);
if (i == DomainAonSel) begin : gen_rst_por_aon_normal
rstmgr_por u_rst_por_aon (
.clk_i(clk_aon_i),
.rst_ni(por_n_i[i]),
.scan_rst_ni,
.scanmode_i(prim_mubi_pkg::mubi4_test_true_strict(por_scanmode[0])),
.rst_no(rst_por_aon_n[i])
);
// reset asserted indication for alert handler
prim_mubi4_sender #(
.ResetValue(MuBi4True)
) u_prim_mubi4_sender (
.clk_i(clk_aon_i),
.rst_ni(rst_por_aon_n[i]),
.mubi_i(MuBi4False),
.mubi_o(rst_en_o.por_aon[i])
);
end else begin : gen_rst_por_domain
logic rst_por_aon_premux;
prim_flop_2sync #(
.Width(1),
.ResetValue('0)
) u_por_domain_sync (
.clk_i(clk_aon_i),
// do not release from reset if aon has not
.rst_ni(rst_por_aon_n[DomainAonSel] & por_n_i[i]),
.d_i(1'b1),
.q_o(rst_por_aon_premux)
);
prim_clock_mux2 #(
.NoFpgaBufG(1'b1)
) u_por_domain_mux (
.clk0_i(rst_por_aon_premux),
.clk1_i(scan_rst_ni),
.sel_i(prim_mubi_pkg::mubi4_test_true_strict(por_scanmode[0])),
.clk_o(rst_por_aon_n[i])
);
// reset asserted indication for alert handler
prim_mubi4_sender #(
.ResetValue(MuBi4True)
) u_prim_mubi4_sender (
.clk_i(clk_aon_i),
.rst_ni(rst_por_aon_n[i]),
.mubi_i(MuBi4False),
.mubi_o(rst_en_o.por_aon[i])
);
end
end
assign resets_o.rst_por_aon_n = rst_por_aon_n;
logic clk_por;
logic rst_por_n;
prim_clock_buf #(
.NoFpgaBuf(1'b1)
) u_por_clk_buf (
.clk_i(clk_por_i),
.clk_o(clk_por)
);
prim_clock_buf #(
.NoFpgaBuf(1'b1)
) u_por_rst_buf (
.clk_i(rst_por_ni),
.clk_o(rst_por_n)
);
////////////////////////////////////////////////////
// Register Interface //
////////////////////////////////////////////////////
rstmgr_reg_pkg::rstmgr_reg2hw_t reg2hw;
rstmgr_reg_pkg::rstmgr_hw2reg_t hw2reg;
logic reg_intg_err;
// SEC_CM: BUS.INTEGRITY
// SEC_CM: SW_RST.CONFIG.REGWEN, DUMP_CTRL.CONFIG.REGWEN
rstmgr_reg_top u_reg (
.clk_i,
.rst_ni,
.clk_por_i (clk_por),
.rst_por_ni (rst_por_n),
.tl_i,
.tl_o,
.reg2hw,
.hw2reg,
.intg_err_o(reg_intg_err),
.devmode_i(1'b1)
);
////////////////////////////////////////////////////
// Errors //
////////////////////////////////////////////////////
// consistency check errors
logic [${len(leaf_rsts)-1}:0][PowerDomains-1:0] cnsty_chk_errs;
logic [${len(leaf_rsts)-1}:0][PowerDomains-1:0] shadow_cnsty_chk_errs;
// consistency sparse fsm errors
logic [${len(leaf_rsts)-1}:0][PowerDomains-1:0] fsm_errs;
logic [${len(leaf_rsts)-1}:0][PowerDomains-1:0] shadow_fsm_errs;
assign hw2reg.err_code.reg_intg_err.d = 1'b1;
assign hw2reg.err_code.reg_intg_err.de = reg_intg_err;
assign hw2reg.err_code.reset_consistency_err.d = 1'b1;
assign hw2reg.err_code.reset_consistency_err.de = |cnsty_chk_errs ||
|shadow_cnsty_chk_errs;
assign hw2reg.err_code.fsm_err.d = 1'b1;
assign hw2reg.err_code.fsm_err.de = |fsm_errs || |shadow_fsm_errs;
////////////////////////////////////////////////////
// Alerts //
////////////////////////////////////////////////////
logic [NumAlerts-1:0] alert_test, alerts;
// All of these are fatal alerts
assign alerts[0] = reg2hw.err_code.reg_intg_err.q |
(|reg2hw.err_code.fsm_err.q);
assign alerts[1] = reg2hw.err_code.reset_consistency_err.q;
assign alert_test = {
reg2hw.alert_test.fatal_cnsty_fault.q & reg2hw.alert_test.fatal_cnsty_fault.qe,
reg2hw.alert_test.fatal_fault.q & reg2hw.alert_test.fatal_fault.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[i] ),
.alert_ack_o ( ),
.alert_state_o ( ),
.alert_rx_i ( alert_rx_i[i] ),
.alert_tx_o ( alert_tx_o[i] )
);
end
////////////////////////////////////////////////////
// Source resets in the system //
// These are hardcoded and not directly used. //
// Instead they act as async reset roots. //
////////////////////////////////////////////////////
// The two source reset modules are chained together. The output of one is fed into the
// the second. This ensures that if upstream resets for any reason, the associated downstream
// reset will also reset.
logic [PowerDomains-1:0] rst_lc_src_n;
logic [PowerDomains-1:0] rst_sys_src_n;
// Declared as size 1 packed array to avoid FPV warning.
prim_mubi_pkg::mubi4_t [0:0] rst_ctrl_scanmode;
prim_mubi4_sync #(
.NumCopies(1),
.AsyncOn(0)
) u_ctrl_scanmode_sync (
.clk_i (clk_por),
.rst_ni (rst_por_n),
.mubi_i(scanmode_i),
.mubi_o(rst_ctrl_scanmode)
);
// lc reset sources
rstmgr_ctrl u_lc_src (
.clk_i (clk_por),
.scanmode_i(prim_mubi_pkg::mubi4_test_true_strict(rst_ctrl_scanmode[0])),
.scan_rst_ni,
.rst_req_i(pwr_i.rst_lc_req),
.rst_parent_ni(rst_por_aon_n),
.rst_no(rst_lc_src_n)
);
// sys reset sources
rstmgr_ctrl u_sys_src (
.clk_i (clk_por),
.scanmode_i(prim_mubi_pkg::mubi4_test_true_strict(rst_ctrl_scanmode[0])),
.scan_rst_ni,
.rst_req_i(pwr_i.rst_sys_req),
.rst_parent_ni(rst_por_aon_n),
.rst_no(rst_sys_src_n)
);
assign pwr_o.rst_lc_src_n = rst_lc_src_n;
assign pwr_o.rst_sys_src_n = rst_sys_src_n;
////////////////////////////////////////////////////
// leaf reset in the system //
// These should all be generated //
////////////////////////////////////////////////////
// To simplify generation, each reset generates all associated power domain outputs.
// If a reset does not support a particular power domain, that reset is always hard-wired to 0.
% for i, rst in enumerate(leaf_rsts):
<%
names = [rst.name]
err_prefix = [""]
if rst.shadowed:
names.append(f'{rst.name}_shadowed')
err_prefix.append('shadow_')
%>\
// Generating resets for ${rst.name}
// Power Domains: ${rst.domains}
// Shadowed: ${rst.shadowed}
% for j, name in enumerate(names):
<%rst_name = Name.from_snake_case(name)
%>\
% for domain in power_domains:
% if domain in rst.domains:
rstmgr_leaf_rst #(
% if rst.name==rst_ni:
.SecCheck(0),
% else:
.SecCheck(SecCheck),
% endif
.SecMaxSyncDelay(SecMaxSyncDelay),
% if rst.sw:
.SwRstReq(1'b1)
% else:
.SwRstReq(1'b0)
% endif
) u_d${domain.lower()}_${name} (
.clk_i,
.rst_ni,
.leaf_clk_i(clk_${rst.clock.name}_i),
.parent_rst_ni(rst_${rst.parent}_n[Domain${domain}Sel]),
% if rst.sw:
.sw_rst_req_ni(reg2hw.sw_rst_ctrl_n[${rst.name.upper()}].q),
% else:
.sw_rst_req_ni(1'b1),
% endif
.scan_rst_ni,
.scanmode_i,
.rst_en_o(rst_en_o.${name}[Domain${domain}Sel]),
.leaf_rst_o(resets_o.rst_${name}_n[Domain${domain}Sel]),
.err_o(${err_prefix[j]}cnsty_chk_errs[${i}][Domain${domain}Sel]),
.fsm_err_o(${err_prefix[j]}fsm_errs[${i}][Domain${domain}Sel])
);
% if rst.name!=rst_ni:
if (SecCheck) begin : gen_d${domain.lower()}_${name}_assert
`ASSERT_PRIM_FSM_ERROR_TRIGGER_ALERT(
D${domain.capitalize()}${rst_name.as_camel_case()}FsmCheck_A,
u_d${domain.lower()}_${name}.gen_rst_chk.u_rst_chk.u_state_regs,
alert_tx_o[0])
end
% endif
% else:
assign resets_o.rst_${name}_n[Domain${domain}Sel] = '0;
assign ${err_prefix[j]}cnsty_chk_errs[${i}][Domain${domain}Sel] = '0;
assign ${err_prefix[j]}fsm_errs[${i}][Domain${domain}Sel] = '0;
assign rst_en_o.${name}[Domain${domain}Sel] = MuBi4True;
% endif
% endfor
% if len(names) == 1:
assign shadow_cnsty_chk_errs[${i}] = '0;
assign shadow_fsm_errs[${i}] = '0;
% endif
% endfor
% endfor
////////////////////////////////////////////////////
// Reset info construction //
////////////////////////////////////////////////////
logic rst_hw_req;
logic rst_low_power;
logic pwrmgr_rst_req;
// there is a valid reset request from pwrmgr
assign pwrmgr_rst_req = |pwr_i.rst_lc_req || |pwr_i.rst_sys_req;
// a reset reason is only valid if the related processing element is also reset.
// In the future, if ever there are multiple processing elements, this code here
// must be updated to account for each individual core.
assign rst_hw_req = pwrmgr_rst_req &
(pwr_i.reset_cause == pwrmgr_pkg::HwReq);
assign rst_low_power = pwrmgr_rst_req &
(pwr_i.reset_cause == pwrmgr_pkg::LowPwrEntry);
// software initiated reset request
assign sw_rst_req_o = prim_mubi_pkg::mubi4_t'(reg2hw.reset_req.q);
// when pwrmgr reset request is received (reset is imminent), clear software
// request so we are not in an infinite reset loop.
assign hw2reg.reset_req.de = pwrmgr_rst_req;
assign hw2reg.reset_req.d = prim_mubi_pkg::MuBi4False;
// Only sw is allowed to clear a reset reason, hw is only allowed to set it.
assign hw2reg.reset_info.low_power_exit.d = 1'b1;
assign hw2reg.reset_info.low_power_exit.de = rst_low_power;
// software issued request triggers the same response as hardware, although it is
// accounted for differently.
assign hw2reg.reset_info.sw_reset.d = prim_mubi_pkg::mubi4_test_true_strict(sw_rst_req_o) |
reg2hw.reset_info.sw_reset.q;
assign hw2reg.reset_info.sw_reset.de = rst_hw_req;
// HW reset requests most likely will be multi-bit, so OR in whatever reasons
// that are already set.
assign hw2reg.reset_info.hw_req.d = pwr_i.rstreqs |
reg2hw.reset_info.hw_req.q;
assign hw2reg.reset_info.hw_req.de = rst_hw_req;
////////////////////////////////////////////////////
// Crash info capture //
////////////////////////////////////////////////////
logic dump_capture;
assign dump_capture = rst_hw_req | rst_low_power;
// halt dump capture once we hit particular conditions
logic dump_capture_halt;
assign dump_capture_halt = rst_hw_req;
rstmgr_crash_info #(
.CrashDumpWidth($bits(alert_pkg::alert_crashdump_t))
) u_alert_info (
.clk_i(clk_por_i),
.rst_ni(rst_por_ni),
.dump_i(alert_dump_i),
.dump_capture_i(dump_capture & reg2hw.alert_info_ctrl.en.q),
.slot_sel_i(reg2hw.alert_info_ctrl.index.q),
.slots_cnt_o(hw2reg.alert_info_attr.d),
.slot_o(hw2reg.alert_info.d)
);
rstmgr_crash_info #(
.CrashDumpWidth($bits(rv_core_ibex_pkg::cpu_crash_dump_t))
) u_cpu_info (
.clk_i(clk_por_i),
.rst_ni(rst_por_ni),
.dump_i(cpu_dump_i),
.dump_capture_i(dump_capture & reg2hw.cpu_info_ctrl.en.q),
.slot_sel_i(reg2hw.cpu_info_ctrl.index.q),
.slots_cnt_o(hw2reg.cpu_info_attr.d),
.slot_o(hw2reg.cpu_info.d)
);
// once dump is captured, no more information is captured until
// re-eanbled by software.
assign hw2reg.alert_info_ctrl.en.d = 1'b0;
assign hw2reg.alert_info_ctrl.en.de = dump_capture_halt;
assign hw2reg.cpu_info_ctrl.en.d = 1'b0;
assign hw2reg.cpu_info_ctrl.en.de = dump_capture_halt;
////////////////////////////////////////////////////
// Exported resets //
////////////////////////////////////////////////////
% for intf, eps in export_rsts.items():
% for ep, rsts in eps.items():
% for rst in rsts:
assign resets_${intf}_o.rst_${intf}_${ep}_${rst['name']}_n = resets_o.rst_${rst['name']}_n;
% endfor
% endfor
% endfor
////////////////////////////////////////////////////
// Assertions //
////////////////////////////////////////////////////
`ASSERT_INIT(ParameterMatch_A, NumHwResets == pwrmgr_pkg::HwResetWidth)
// when upstream resets, downstream must also reset
// output known asserts
`ASSERT_KNOWN(TlDValidKnownO_A, tl_o.d_valid )
`ASSERT_KNOWN(TlAReadyKnownO_A, tl_o.a_ready )
`ASSERT_KNOWN(AlertsKnownO_A, alert_tx_o )
`ASSERT_KNOWN(PwrKnownO_A, pwr_o )
`ASSERT_KNOWN(ResetsKnownO_A, resets_o )
`ASSERT_KNOWN(RstEnKnownO_A, rst_en_o )
% for intf in export_rsts:
`ASSERT_KNOWN(${intf.capitalize()}ResetsKnownO_A, resets_${intf}_o )
% endfor
// Alert assertions for reg_we onehot check
`ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0])
endmodule // rstmgr