| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| // | |
| // sysrst_ctrl module | |
| `include "prim_assert.sv" | |
| module sysrst_ctrl | |
| import sysrst_ctrl_reg_pkg::*; | |
| #( | |
| parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} | |
| ) ( | |
| input clk_i, // Always-on 24MHz clock(config) | |
| input clk_aon_i, // Always-on 200KHz clock(logic) | |
| input rst_ni, // power-on reset for the 24MHz clock(config) | |
| input rst_aon_ni, // power-on reset for the 200KHz clock(logic) | |
| // Register 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, | |
| // Wake, reset and interrupt requests | |
| output logic wkup_req_o, // OT wake to pwrmgr, on AON clock | |
| output logic rst_req_o, // OT reset to rstmgr, on AON clock | |
| output logic intr_event_detected_o, // event interrupt to PLIC, on bus clock | |
| // IOs | |
| input cio_ac_present_i, // AC power is present | |
| input cio_ec_rst_l_i, // EC reset is asserted by some other system agent | |
| input cio_key0_in_i, // VolUp button in tablet; column output from the EC in a laptop | |
| input cio_key1_in_i, // VolDown button in tablet; row input from keyboard matrix in a laptop | |
| input cio_key2_in_i, // TBD button in tablet; row input from keyboard matrix in a laptop | |
| input cio_pwrb_in_i, // Power button in both tablet and laptop | |
| input cio_lid_open_i, // lid is open from GMR | |
| input cio_flash_wp_l_i, // read back of the flash_wp_l output pad | |
| output logic cio_bat_disable_o, // Battery is disconnected | |
| output logic cio_flash_wp_l_o,//Flash write protect is asserted by sysrst_ctrl | |
| output logic cio_ec_rst_l_o, // EC reset is asserted by sysrst_ctrl | |
| output logic cio_key0_out_o, // Passthrough from key0_in, can be configured to invert | |
| output logic cio_key1_out_o, // Passthrough from key1_in, can be configured to invert | |
| output logic cio_key2_out_o, // Passthrough from key2_in, can be configured to invert | |
| output logic cio_pwrb_out_o, // Passthrough from pwrb_in, can be configured to invert | |
| output logic cio_z3_wakeup_o, // Exit from Z4 sleep mode and enter Z3 mode | |
| output logic cio_bat_disable_en_o, | |
| output logic cio_flash_wp_l_en_o, | |
| output logic cio_ec_rst_l_en_o, | |
| output logic cio_key0_out_en_o, | |
| output logic cio_key1_out_en_o, | |
| output logic cio_key2_out_en_o, | |
| output logic cio_pwrb_out_en_o, | |
| output logic cio_z3_wakeup_en_o | |
| ); | |
| ///////////////////////// | |
| // Alerts and CSR Node // | |
| ///////////////////////// | |
| sysrst_ctrl_reg2hw_t reg2hw; | |
| sysrst_ctrl_hw2reg_t hw2reg; | |
| logic [NumAlerts-1:0] alert_test, alerts; | |
| assign alert_test = {reg2hw.alert_test.q & 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 | |
| sysrst_ctrl_reg_top u_reg ( | |
| .clk_i, | |
| .rst_ni, | |
| .clk_aon_i, | |
| .rst_aon_ni, | |
| .tl_i, | |
| .tl_o, | |
| .reg2hw, | |
| .hw2reg, | |
| // SEC_CM: BUS.INTEGRITY | |
| .intg_err_o(alerts[0]), | |
| .devmode_i (1'b1) | |
| ); | |
| /////////////////////////////////////// | |
| // Input inversion and Synchronizers // | |
| /////////////////////////////////////// | |
| // Optionally invert some of the input signals | |
| logic pwrb_int, key0_int, key1_int, key2_int, ac_present_int, lid_open_int; | |
| logic ec_rst_l_int, flash_wp_l_int; | |
| assign pwrb_int = reg2hw.key_invert_ctl.pwrb_in.q ^ cio_pwrb_in_i; | |
| assign key0_int = reg2hw.key_invert_ctl.key0_in.q ^ cio_key0_in_i; | |
| assign key1_int = reg2hw.key_invert_ctl.key1_in.q ^ cio_key1_in_i; | |
| assign key2_int = reg2hw.key_invert_ctl.key2_in.q ^ cio_key2_in_i; | |
| assign ac_present_int = reg2hw.key_invert_ctl.ac_present.q ^ cio_ac_present_i; | |
| assign lid_open_int = reg2hw.key_invert_ctl.lid_open.q ^ cio_lid_open_i; | |
| // Uninverted inputs | |
| assign ec_rst_l_int = cio_ec_rst_l_i; | |
| assign flash_wp_l_int = cio_flash_wp_l_i; | |
| // Synchronize input signals to AON clock | |
| logic aon_pwrb_int, aon_key0_int, aon_key1_int, aon_key2_int; | |
| logic aon_ac_present_int, aon_lid_open_int, aon_ec_rst_l_int, aon_flash_wp_l_int; | |
| prim_flop_2sync #( | |
| .Width(8) | |
| ) u_prim_flop_2sync_input ( | |
| .clk_i(clk_aon_i), | |
| .rst_ni(rst_aon_ni), | |
| .d_i({pwrb_int, | |
| key0_int, | |
| key1_int, | |
| key2_int, | |
| ac_present_int, | |
| lid_open_int, | |
| ec_rst_l_int, | |
| flash_wp_l_int}), | |
| .q_o({ | |
| aon_pwrb_int, | |
| aon_key0_int, | |
| aon_key1_int, | |
| aon_key2_int, | |
| aon_ac_present_int, | |
| aon_lid_open_int, | |
| aon_ec_rst_l_int, | |
| aon_flash_wp_l_int | |
| }) | |
| ); | |
| /////////////// | |
| // Autoblock // | |
| /////////////// | |
| // The registers in this module run on the AON clock. | |
| // However, the passthrough signals are NOT synchronous to the AON clock. | |
| logic pwrb_out_hw, key0_out_hw, key1_out_hw, key2_out_hw; | |
| sysrst_ctrl_autoblock u_sysrst_ctrl_autoblock ( | |
| .clk_i(clk_aon_i), | |
| .rst_ni(rst_aon_ni), | |
| // (Optionally) inverted input signals on AON clock | |
| .aon_pwrb_int_i(aon_pwrb_int), | |
| // (Optionally) inverted input signals (not synced to AON clock) | |
| .pwrb_int_i(pwrb_int), | |
| .key0_int_i(key0_int), | |
| .key1_int_i(key1_int), | |
| .key2_int_i(key2_int), | |
| // CSRs synced to AON clock | |
| .aon_auto_block_debounce_ctl_i(reg2hw.auto_block_debounce_ctl), | |
| .aon_auto_block_out_ctl_i(reg2hw.auto_block_out_ctl), | |
| // Output signals to pin override logic (not synced to AON clock) | |
| .pwrb_out_hw_o(pwrb_out_hw), | |
| .key0_out_hw_o(key0_out_hw), | |
| .key1_out_hw_o(key1_out_hw), | |
| .key2_out_hw_o(key2_out_hw) | |
| ); | |
| ///////// | |
| // ULP // | |
| ///////// | |
| // This module runs on the AON clock entirely. | |
| // Hence, its local signals are not prefixed with aon_*. | |
| logic aon_z3_wakeup_hw; | |
| logic aon_ulp_wakeup_pulse_int; | |
| sysrst_ctrl_ulp u_sysrst_ctrl_ulp ( | |
| .clk_i(clk_aon_i), | |
| .rst_ni(rst_aon_ni), | |
| // (Optionally) inverted input signals on AON clock | |
| .pwrb_int_i(aon_pwrb_int), | |
| .lid_open_int_i(aon_lid_open_int), | |
| .ac_present_int_i(aon_ac_present_int), | |
| // CSRs synced to AON clock | |
| .ulp_ac_debounce_ctl_i(reg2hw.ulp_ac_debounce_ctl), | |
| .ulp_lid_debounce_ctl_i(reg2hw.ulp_lid_debounce_ctl), | |
| .ulp_pwrb_debounce_ctl_i(reg2hw.ulp_pwrb_debounce_ctl), | |
| .ulp_ctl_i(reg2hw.ulp_ctl), | |
| .ulp_status_o(hw2reg.ulp_status), | |
| // wakeup pulses on AON clock | |
| .ulp_wakeup_pulse_o(aon_ulp_wakeup_pulse_int), | |
| .z3_wakeup_hw_o(aon_z3_wakeup_hw) | |
| ); | |
| ///////////////////////////// | |
| // Key triggered interrups // | |
| ///////////////////////////// | |
| // This module runs on the AON clock entirely. | |
| // Hence, its local signals are not prefixed with aon_*. | |
| logic aon_sysrst_ctrl_key_intr; | |
| sysrst_ctrl_keyintr u_sysrst_ctrl_keyintr ( | |
| .clk_i(clk_aon_i), | |
| .rst_ni(rst_aon_ni), | |
| // (Optionally) inverted input signals on AON clock | |
| .pwrb_int_i(aon_pwrb_int), | |
| .key0_int_i(aon_key0_int), | |
| .key1_int_i(aon_key1_int), | |
| .key2_int_i(aon_key2_int), | |
| .ac_present_int_i(aon_ac_present_int), | |
| .ec_rst_l_int_i(aon_ec_rst_l_int), | |
| .flash_wp_l_int_i(aon_flash_wp_l_int), | |
| // CSRs synced to AON clock | |
| .key_intr_ctl_i(reg2hw.key_intr_ctl), | |
| .key_intr_debounce_ctl_i(reg2hw.key_intr_debounce_ctl), | |
| .key_intr_status_o(hw2reg.key_intr_status), | |
| // IRQ running on AON clock | |
| .sysrst_ctrl_key_intr_o(aon_sysrst_ctrl_key_intr) | |
| ); | |
| ///////////////////// | |
| // Combo detection // | |
| ///////////////////// | |
| // This module runs on the AON clock entirely. | |
| // Hence, its local signals are not prefixed with aon_*. | |
| logic aon_sysrst_ctrl_combo_intr, aon_bat_disable_hw, aon_ec_rst_l_hw; | |
| sysrst_ctrl_combo u_sysrst_ctrl_combo ( | |
| .clk_i(clk_aon_i), | |
| .rst_ni(rst_aon_ni), | |
| // (Optionally) inverted input signals on AON clock | |
| .pwrb_int_i(aon_pwrb_int), | |
| .key0_int_i(aon_key0_int), | |
| .key1_int_i(aon_key1_int), | |
| .key2_int_i(aon_key2_int), | |
| .ac_present_int_i(aon_ac_present_int), | |
| .ec_rst_l_int_i(aon_ec_rst_l_int), | |
| // CSRs synced to AON clock | |
| .ec_rst_ctl_i(reg2hw.ec_rst_ctl), | |
| .key_intr_debounce_ctl_i(reg2hw.key_intr_debounce_ctl), | |
| .com_pre_sel_ctl_i(reg2hw.com_pre_sel_ctl), | |
| .com_pre_det_ctl_i(reg2hw.com_pre_det_ctl), | |
| .com_sel_ctl_i(reg2hw.com_sel_ctl), | |
| .com_det_ctl_i(reg2hw.com_det_ctl), | |
| .com_out_ctl_i(reg2hw.com_out_ctl), | |
| .combo_intr_status_o(hw2reg.combo_intr_status), | |
| // Output signals on AON clock | |
| .sysrst_ctrl_combo_intr_o(aon_sysrst_ctrl_combo_intr), | |
| .bat_disable_hw_o(aon_bat_disable_hw), | |
| .rst_req_o(rst_req_o), | |
| .ec_rst_l_hw_o(aon_ec_rst_l_hw) | |
| ); | |
| /////////////////////////////// | |
| // Pin visibility / override // | |
| /////////////////////////////// | |
| // This module operates on both synchronized and unsynchronized signals. | |
| // I.e., the passthrough signals are NOT synchronnous to the AON clock. | |
| logic pwrb_out_int, key0_out_int, key1_out_int, key2_out_int, aon_bat_disable_out_int; | |
| logic aon_z3_wakeup_out_int, aon_ec_rst_out_int_l, aon_flash_wp_out_int_l; | |
| sysrst_ctrl_pin u_sysrst_ctrl_pin ( | |
| .clk_i, | |
| .rst_ni, | |
| // Raw input signals (not synced to AON clock) | |
| .cio_pwrb_in_i, | |
| .cio_key0_in_i, | |
| .cio_key1_in_i, | |
| .cio_key2_in_i, | |
| .cio_ac_present_i, | |
| .cio_ec_rst_l_i, | |
| .cio_flash_wp_l_i, | |
| .cio_lid_open_i, | |
| // Signals from autoblock (not synced to AON clock) | |
| .pwrb_out_hw_i(pwrb_out_hw), | |
| .key0_out_hw_i(key0_out_hw), | |
| .key1_out_hw_i(key1_out_hw), | |
| .key2_out_hw_i(key2_out_hw), | |
| // Generated signals, running on AON clock | |
| .aon_bat_disable_hw_i(aon_bat_disable_hw), | |
| .aon_ec_rst_l_hw_i(aon_ec_rst_l_hw), | |
| .aon_z3_wakeup_hw_i(aon_z3_wakeup_hw), | |
| // CSRs synced to AON clock | |
| .aon_pin_allowed_ctl_i(reg2hw.pin_allowed_ctl), | |
| .aon_pin_out_ctl_i(reg2hw.pin_out_ctl), | |
| .aon_pin_out_value_i(reg2hw.pin_out_value), | |
| // CSRs synced to bus clock | |
| .pin_in_value_o(hw2reg.pin_in_value), | |
| // Output signals (not synced to AON clock) | |
| .pwrb_out_int_o(pwrb_out_int), | |
| .key0_out_int_o(key0_out_int), | |
| .key1_out_int_o(key1_out_int), | |
| .key2_out_int_o(key2_out_int), | |
| // Output signals running on AON clock | |
| .aon_bat_disable_out_int_o(aon_bat_disable_out_int), | |
| .aon_z3_wakeup_out_int_o(aon_z3_wakeup_out_int), | |
| .aon_ec_rst_out_int_l_o(aon_ec_rst_out_int_l), | |
| .aon_flash_wp_out_int_l_o(aon_flash_wp_out_int_l) | |
| ); | |
| // Optionally invert some of the output signals | |
| assign cio_pwrb_out_o = reg2hw.key_invert_ctl.pwrb_out.q ^ pwrb_out_int; | |
| assign cio_key0_out_o = reg2hw.key_invert_ctl.key0_out.q ^ key0_out_int; | |
| assign cio_key1_out_o = reg2hw.key_invert_ctl.key1_out.q ^ key1_out_int; | |
| assign cio_key2_out_o = reg2hw.key_invert_ctl.key2_out.q ^ key2_out_int; | |
| assign cio_bat_disable_o = reg2hw.key_invert_ctl.bat_disable.q ^ aon_bat_disable_out_int; | |
| assign cio_z3_wakeup_o = reg2hw.key_invert_ctl.z3_wakeup.q ^ aon_z3_wakeup_out_int; | |
| // uninverted outputs | |
| assign cio_ec_rst_l_o = aon_ec_rst_out_int_l; | |
| assign cio_flash_wp_l_o = aon_flash_wp_out_int_l; | |
| // These outputs are always enabled | |
| assign cio_pwrb_out_en_o = 1'b1; | |
| assign cio_key0_out_en_o = 1'b1; | |
| assign cio_key1_out_en_o = 1'b1; | |
| assign cio_key2_out_en_o = 1'b1; | |
| assign cio_bat_disable_en_o = 1'b1; | |
| assign cio_z3_wakeup_en_o = 1'b1; | |
| assign cio_ec_rst_l_en_o = 1'b1; | |
| assign cio_flash_wp_l_en_o = 1'b1; | |
| /////////////////////////// | |
| // Interrupt agreggation // | |
| /////////////////////////// | |
| // OT wakeup signal to pwrmgr, CSRs and signals on AON domain (see #6323) | |
| logic aon_intr_event_pulse; | |
| assign aon_intr_event_pulse = aon_ulp_wakeup_pulse_int || | |
| aon_sysrst_ctrl_combo_intr || | |
| aon_sysrst_ctrl_key_intr; | |
| assign hw2reg.wkup_status.de = aon_intr_event_pulse; | |
| assign hw2reg.wkup_status.d = 1'b1; | |
| assign wkup_req_o = reg2hw.wkup_status.q; | |
| logic aon_intr_req, aon_intr_ack; | |
| always_ff @(posedge clk_aon_i or negedge rst_aon_ni) begin : p_intr_req_hold_reg | |
| if(~rst_aon_ni) begin | |
| aon_intr_req <= 1'b0; | |
| end else begin | |
| // A new interrupt event has priority over the acknowledge pulse. | |
| aon_intr_req <= (aon_intr_req && !aon_intr_ack) || aon_intr_event_pulse; | |
| end | |
| end | |
| // This synchronizes over a pulse if there is a pending request. | |
| // If the main bus clock is not active, this will stall the synchronization until | |
| // the clock becomes live again so that no interrupt requests are missed. | |
| logic intr_event_pulse; | |
| prim_sync_reqack u_prim_sync_reqack ( | |
| .clk_src_i(clk_aon_i), | |
| .rst_src_ni(rst_aon_ni), | |
| .clk_dst_i(clk_i), | |
| .rst_dst_ni(rst_ni), | |
| .req_chk_i(1'b1), | |
| .src_req_i(aon_intr_req), | |
| .src_ack_o(aon_intr_ack), | |
| .dst_req_o(intr_event_pulse), | |
| .dst_ack_i(intr_event_pulse) | |
| ); | |
| // Instantiate the interrupt module | |
| prim_intr_hw #( | |
| .Width(1) | |
| ) u_prim_intr_hw ( | |
| .clk_i, | |
| .rst_ni, | |
| .event_intr_i (intr_event_pulse), | |
| .reg2hw_intr_enable_q_i(reg2hw.intr_enable.q), | |
| .reg2hw_intr_test_q_i (reg2hw.intr_test.q), | |
| .reg2hw_intr_test_qe_i (reg2hw.intr_test.qe), | |
| .reg2hw_intr_state_q_i (reg2hw.intr_state.q), | |
| .hw2reg_intr_state_de_o(hw2reg.intr_state.de), | |
| .hw2reg_intr_state_d_o (hw2reg.intr_state.d), | |
| .intr_o (intr_event_detected_o) | |
| ); | |
| //////////////// | |
| // Assertions // | |
| //////////////// | |
| // All outputs should be known value after reset | |
| `ASSERT_KNOWN(IntrEventOKnown_A, intr_event_detected_o) | |
| `ASSERT_KNOWN(OTWkOKnown_A, wkup_req_o) | |
| `ASSERT_KNOWN(OTRstOKnown_A, rst_req_o) | |
| `ASSERT_KNOWN(TlODValidKnown_A, tl_o.d_valid) | |
| `ASSERT_KNOWN(TlOAReadyKnown_A, tl_o.a_ready) | |
| `ASSERT_KNOWN(AlertKnown_A, alert_tx_o) | |
| `ASSERT_KNOWN(BatOKnown_A, cio_bat_disable_o) | |
| `ASSERT_KNOWN(ECRSTOKnown_A, cio_ec_rst_l_o) | |
| `ASSERT_KNOWN(PwrbOKnown_A, cio_pwrb_out_o) | |
| `ASSERT_KNOWN(Key0OKnown_A, cio_key0_out_o) | |
| `ASSERT_KNOWN(Key1OKnown_A, cio_key1_out_o) | |
| `ASSERT_KNOWN(Key2OKnown_A, cio_key2_out_o) | |
| `ASSERT_KNOWN(Z3WwakupOKnown_A, cio_z3_wakeup_o) | |
| `ASSERT_KNOWN(FlashWpOKnown_A, cio_flash_wp_l_o) | |
| // In this IP, all output enables are constantly set to 1 | |
| `ASSERT(BatOEnIsOne_A, cio_bat_disable_en_o === 1'b1) | |
| `ASSERT(ECRSTOEnIsOne_A, cio_ec_rst_l_en_o === 1'b1) | |
| `ASSERT(PwrbOEnIsOne_A, cio_pwrb_out_en_o === 1'b1) | |
| `ASSERT(Key0OEnIsOne_A, cio_key0_out_en_o === 1'b1) | |
| `ASSERT(Key1OEnIsOne_A, cio_key1_out_en_o === 1'b1) | |
| `ASSERT(Key2OEnIsOne_A, cio_key2_out_en_o === 1'b1) | |
| `ASSERT(Z3WakeupOEnIsOne_A, cio_z3_wakeup_en_o === 1'b1) | |
| `ASSERT(FlashWpOEnIsOne_A, cio_flash_wp_l_en_o === 1'b1) | |
| // Alert assertions for reg_we onehot check | |
| `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0]) | |
| endmodule |