[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");
 }