[rv_timer] Int clr by mtimecmp update
RISC-V Previliged Spec mentioned that the interrupt line is dropped by
writing values to `mtimecmp`. @pfmooney raised the issue that the
RV_TIMER in OpenTitan doesn't follow this (see issue #1404 )
This commit is to change the rv_timer behavior to support clearing the
interrupt by updating `mtimecmp` CSRs.
Please note that the writing 1 to the interrupt status register also
clears it to support current VIP and the firmware.
Signed-off-by: Eunchan Kim <eunchan@opentitan.org>
diff --git a/hw/ip/rv_timer/data/rv_timer.hjson b/hw/ip/rv_timer/data/rv_timer.hjson
index 9c10792..f470d15 100644
--- a/hw/ip/rv_timer/data/rv_timer.hjson
+++ b/hw/ip/rv_timer/data/rv_timer.hjson
@@ -72,6 +72,7 @@
desc: "Timer value Lower",
swaccess: "rw",
hwaccess: "hro",
+ hwqe: "true",
fields: [
{ bits: "31:0", name: "v", resval: "0xffffffff", desc: "Timer compare value [31:0]" },
],
@@ -80,6 +81,7 @@
desc: "Timer value Upper",
swaccess: "rw",
hwaccess: "hro",
+ hwqe: "true",
fields: [
{ bits: "31:0", name: "v", resval: "0xffffffff", desc: "Timer compare value [63:32]" },
],
@@ -124,3 +126,4 @@
},
],
}
+
diff --git a/hw/ip/rv_timer/data/rv_timer.hjson.tpl b/hw/ip/rv_timer/data/rv_timer.hjson.tpl
index fa07de8..46b919a 100644
--- a/hw/ip/rv_timer/data/rv_timer.hjson.tpl
+++ b/hw/ip/rv_timer/data/rv_timer.hjson.tpl
@@ -6,7 +6,7 @@
## - harts: number of HART in timer module
## - timers: number of timers in each hart
{ name: "rv_timer",
- clock_primary: "clk_fixed",
+ clock_primary: "clk_i",
bus_device: "tlul",
bus_host: "none",
available_input_list: [
@@ -81,6 +81,7 @@
desc: "Timer value Lower",
swaccess: "rw",
hwaccess: "hro",
+ hwqe: "true",
fields: [
{ bits: "31:0", name: "v", resval: "0xffffffff", desc: "Timer compare value [31:0]" },
],
@@ -89,6 +90,7 @@
desc: "Timer value Upper",
swaccess: "rw",
hwaccess: "hro",
+ hwqe: "true",
fields: [
{ bits: "31:0", name: "v", resval: "0xffffffff", desc: "Timer compare value [63:32]" },
],
diff --git a/hw/ip/rv_timer/doc/_index.md b/hw/ip/rv_timer/doc/_index.md
index 984ff6c..0b7c91d 100644
--- a/hw/ip/rv_timer/doc/_index.md
+++ b/hw/ip/rv_timer/doc/_index.md
@@ -234,7 +234,11 @@
## Interrupt Handling
-TBD
+If `mtime` is greater than or equal to the value of `mtimecmp`, the interrupt is generated from the RV_TIMER module.
+If the core enables the timer interrupt in `MIE` CSR, it jumps into the timer interupt service routine.
+Clearing the interrupt can be done by writing 1 into the Interrupt Status register {{<regref "INTR_STATE0">}}.
+The RV_TIMER module also follows RISC-V Previliged spec that requires the interrupt to be cleared by updating `mtimecmp` memory-mapped CSRs.
+In this case both {{<regref "COMPARE_LOWER0_0">}} and {{<regref "COMPARE_UPPER0_0">}} can clear the interrupt source.
## Register Table
diff --git a/hw/ip/rv_timer/rtl/rv_timer.sv b/hw/ip/rv_timer/rtl/rv_timer.sv
index 7602960..9b939ee 100644
--- a/hw/ip/rv_timer/rtl/rv_timer.sv
+++ b/hw/ip/rv_timer/rtl/rv_timer.sv
@@ -34,6 +34,7 @@
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;
@@ -60,15 +61,16 @@
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,reg2hw.compare_lower0_0}}};
+ 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_0_0_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;
- assign hw2reg.intr_state0[0].d = intr_timer_state_d;
+ 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
diff --git a/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv b/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv
index 1de1fb1..2addad6 100644
--- a/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv
+++ b/hw/ip/rv_timer/rtl/rv_timer_reg_pkg.sv
@@ -36,10 +36,12 @@
typedef struct packed {
logic [31:0] q;
+ logic qe;
} rv_timer_reg2hw_compare_lower0_0_reg_t;
typedef struct packed {
logic [31:0] q;
+ logic qe;
} rv_timer_reg2hw_compare_upper0_0_reg_t;
typedef struct packed {
@@ -76,12 +78,12 @@
// Register to internal design logic //
///////////////////////////////////////
typedef struct packed {
- rv_timer_reg2hw_ctrl_mreg_t [0:0] ctrl; // [152:152]
- rv_timer_reg2hw_cfg0_reg_t cfg0; // [151:132]
- rv_timer_reg2hw_timer_v_lower0_reg_t timer_v_lower0; // [131:100]
- rv_timer_reg2hw_timer_v_upper0_reg_t timer_v_upper0; // [99:68]
- rv_timer_reg2hw_compare_lower0_0_reg_t compare_lower0_0; // [67:36]
- rv_timer_reg2hw_compare_upper0_0_reg_t compare_upper0_0; // [35:4]
+ rv_timer_reg2hw_ctrl_mreg_t [0:0] ctrl; // [154:154]
+ rv_timer_reg2hw_cfg0_reg_t cfg0; // [153:134]
+ rv_timer_reg2hw_timer_v_lower0_reg_t timer_v_lower0; // [133:102]
+ rv_timer_reg2hw_timer_v_upper0_reg_t timer_v_upper0; // [101:70]
+ rv_timer_reg2hw_compare_lower0_0_reg_t compare_lower0_0; // [69:37]
+ rv_timer_reg2hw_compare_upper0_0_reg_t compare_upper0_0; // [36:4]
rv_timer_reg2hw_intr_enable0_mreg_t [0:0] intr_enable0; // [3:3]
rv_timer_reg2hw_intr_state0_mreg_t [0:0] intr_state0; // [2:2]
rv_timer_reg2hw_intr_test0_mreg_t [0:0] intr_test0; // [1:0]
diff --git a/hw/ip/rv_timer/rtl/rv_timer_reg_top.sv b/hw/ip/rv_timer/rtl/rv_timer_reg_top.sv
index de5c20e..b7bd923 100644
--- a/hw/ip/rv_timer/rtl/rv_timer_reg_top.sv
+++ b/hw/ip/rv_timer/rtl/rv_timer_reg_top.sv
@@ -258,7 +258,7 @@
.d ('0 ),
// to internal hardware
- .qe (),
+ .qe (reg2hw.compare_lower0_0.qe),
.q (reg2hw.compare_lower0_0.q ),
// to register interface (read)
@@ -285,7 +285,7 @@
.d ('0 ),
// to internal hardware
- .qe (),
+ .qe (reg2hw.compare_upper0_0.qe),
.q (reg2hw.compare_upper0_0.q ),
// to register interface (read)
diff --git a/sw/device/tests/rv_timer/meson.build b/sw/device/tests/rv_timer/meson.build
index a843a9c..d4ad549 100644
--- a/sw/device/tests/rv_timer/meson.build
+++ b/sw/device/tests/rv_timer/meson.build
@@ -10,6 +10,8 @@
sw_lib_irq,
sw_lib_rv_timer,
sw_lib_uart,
+ sw_lib_gpio,
+ sw_lib_pinmux,
riscv_crt,
sw_lib_irq_handlers,
],
diff --git a/sw/device/tests/rv_timer/rv_timer_test.c b/sw/device/tests/rv_timer/rv_timer_test.c
index e339ba0..9e65a9b 100644
--- a/sw/device/tests/rv_timer/rv_timer_test.c
+++ b/sw/device/tests/rv_timer/rv_timer_test.c
@@ -5,6 +5,8 @@
#include "sw/device/lib/common.h"
#include "sw/device/lib/irq.h"
#include "sw/device/lib/rv_timer.h"
+#include "sw/device/lib/gpio.h"
+#include "sw/device/lib/pinmux.h"
#include "sw/device/lib/uart.h"
static uint32_t intr_handling_success = 0;
@@ -15,6 +17,10 @@
uart_init(UART_BAUD_RATE);
+ pinmux_init();
+ // Enable GPIO: 0-7 and 16 is input, 8-15 is output
+ gpio_init(0xFF00);
+
irq_global_ctrl(true);
irq_timer_ctrl(true);
rv_timer_set_us_tick(hart);
@@ -22,12 +28,16 @@
rv_timer_ctrl(hart, true);
rv_timer_intr_enable(hart, true);
+ gpio_write_all(0xFF00); // all LEDs on
+
while (1) {
if (intr_handling_success) {
break;
}
}
+ gpio_write_all(0xAA00); // Test Completed
+
uart_send_str("PASS!\r\n");
}