| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| |
| |
| `include "prim_assert.sv" |
| |
| module rv_timer import rv_timer_reg_pkg::*; |
| #( |
| parameter logic [NumAlerts-1:0] AlertAsyncOn = {NumAlerts{1'b1}} |
| ) ( |
| input clk_i, |
| input rst_ni, |
| |
| input tlul_pkg::tl_h2d_t tl_i, |
| output tlul_pkg::tl_d2h_t tl_o, |
| |
| 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, |
| |
| output logic intr_timer_expired_hart0_timer0_o |
| ); |
| |
| rv_timer_reg2hw_t reg2hw; |
| rv_timer_hw2reg_t hw2reg; |
| |
| logic [N_HARTS-1:0] active; |
| |
| logic [11:0] prescaler [N_HARTS]; |
| logic [7:0] step [N_HARTS]; |
| |
| logic [N_HARTS-1:0] tick; |
| |
| logic [63:0] mtime_d [N_HARTS]; |
| logic [63:0] mtime [N_HARTS]; |
| logic [63:0] mtimecmp [N_HARTS][N_TIMERS]; // Only [harts][0] is connected to mtimecmp CSRs |
| logic mtimecmp_update [N_HARTS][N_TIMERS]; |
| |
| logic [N_HARTS*N_TIMERS-1:0] intr_timer_set; |
| logic [N_HARTS*N_TIMERS-1:0] intr_timer_en; |
| logic [N_HARTS*N_TIMERS-1:0] intr_timer_test_q; |
| logic [N_HARTS-1:0] intr_timer_test_qe; |
| logic [N_HARTS*N_TIMERS-1:0] intr_timer_state_q; |
| logic [N_HARTS-1:0] intr_timer_state_de; |
| logic [N_HARTS*N_TIMERS-1:0] intr_timer_state_d; |
| |
| logic [N_HARTS*N_TIMERS-1:0] intr_out; |
| |
| ///////////////////////////////////////////////// |
| // Connecting register interface to the signal // |
| ///////////////////////////////////////////////// |
| |
| // Once reggen supports nested multireg, the following can be automated. For the moment, it must |
| // be connected manually. |
| assign active[0] = reg2hw.ctrl[0].q; |
| assign prescaler = '{reg2hw.cfg0.prescale.q}; |
| assign step = '{reg2hw.cfg0.step.q}; |
| |
| assign hw2reg.timer_v_upper0.de = tick[0]; |
| assign hw2reg.timer_v_lower0.de = tick[0]; |
| assign hw2reg.timer_v_upper0.d = mtime_d[0][63:32]; |
| assign hw2reg.timer_v_lower0.d = mtime_d[0][31: 0]; |
| assign mtime[0] = {reg2hw.timer_v_upper0.q, reg2hw.timer_v_lower0.q}; |
| assign mtimecmp = '{'{{reg2hw.compare_upper0_0.q,reg2hw.compare_lower0_0.q}}}; |
| assign mtimecmp_update[0][0] = reg2hw.compare_upper0_0.qe | reg2hw.compare_lower0_0.qe; |
| |
| assign intr_timer_expired_hart0_timer0_o = intr_out[0]; |
| assign intr_timer_en = reg2hw.intr_enable0[0].q; |
| assign intr_timer_state_q = reg2hw.intr_state0[0].q; |
| assign intr_timer_test_q = reg2hw.intr_test0[0].q; |
| assign intr_timer_test_qe = reg2hw.intr_test0[0].qe; |
| assign hw2reg.intr_state0[0].de = intr_timer_state_de | mtimecmp_update[0][0]; |
| assign hw2reg.intr_state0[0].d = intr_timer_state_d & ~mtimecmp_update[0][0]; |
| |
| |
| for (genvar h = 0 ; h < N_HARTS ; h++) begin : gen_harts |
| prim_intr_hw #( |
| .Width(N_TIMERS) |
| ) u_intr_hw ( |
| .clk_i, |
| .rst_ni, |
| .event_intr_i (intr_timer_set), |
| |
| .reg2hw_intr_enable_q_i (intr_timer_en[h*N_TIMERS+:N_TIMERS]), |
| .reg2hw_intr_test_q_i (intr_timer_test_q[h*N_TIMERS+:N_TIMERS]), |
| .reg2hw_intr_test_qe_i (intr_timer_test_qe[h]), |
| .reg2hw_intr_state_q_i (intr_timer_state_q[h*N_TIMERS+:N_TIMERS]), |
| .hw2reg_intr_state_de_o (intr_timer_state_de), |
| .hw2reg_intr_state_d_o (intr_timer_state_d[h*N_TIMERS+:N_TIMERS]), |
| |
| .intr_o (intr_out[h*N_TIMERS+:N_TIMERS]) |
| ); |
| |
| timer_core #( |
| .N (N_TIMERS) |
| ) u_core ( |
| .clk_i, |
| .rst_ni, |
| |
| .active (active[h]), |
| .prescaler (prescaler[h]), |
| .step (step[h]), |
| |
| .tick (tick[h]), |
| |
| .mtime_d (mtime_d[h]), |
| .mtime (mtime[h]), |
| .mtimecmp (mtimecmp[h]), |
| |
| .intr (intr_timer_set[h*N_TIMERS+:N_TIMERS]) |
| ); |
| end : gen_harts |
| |
| // Register module |
| logic [NumAlerts-1:0] alert_test, alerts; |
| rv_timer_reg_top u_reg ( |
| .clk_i, |
| .rst_ni, |
| |
| .tl_i, |
| .tl_o, |
| |
| .reg2hw, |
| .hw2reg, |
| |
| // SEC_CM: BUS.INTEGRITY |
| .intg_err_o (alerts[0]), |
| .devmode_i (1'b1) |
| ); |
| |
| // 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 |
| |
| //////////////// |
| // Assertions // |
| //////////////// |
| `ASSERT_KNOWN(TlODValidKnown, tl_o.d_valid) |
| `ASSERT_KNOWN(TlOAReadyKnown, tl_o.a_ready) |
| `ASSERT_KNOWN(AlertsKnown_A, alert_tx_o) |
| `ASSERT_KNOWN(IntrTimerExpiredHart0Timer0Known, intr_timer_expired_hart0_timer0_o) |
| |
| // Alert assertions for reg_we onehot check |
| `ASSERT_PRIM_REG_WE_ONEHOT_ERROR_TRIGGER_ALERT(RegWeOnehotCheck_A, u_reg, alert_tx_o[0]) |
| endmodule |