Skip to content

Commit

Permalink
Add a basic mechanism for interrupt acknowledge. (#2502)
Browse files Browse the repository at this point in the history
  • Loading branch information
zchamski committed Sep 19, 2024
1 parent 8070feb commit f974e10
Show file tree
Hide file tree
Showing 13 changed files with 235 additions and 5 deletions.
28 changes: 28 additions & 0 deletions verif/docs/Protocols/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2024 Thales DIS France SAS
#
# Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0
# You may obtain a copy of the License at https://solderpad.org/licenses/
#
# Original Author: Zbigniew CHAMSKI - Thales

FIGDIR=figures
WAVEDIR=wavedrom

# Names of figure files, without directory prefix nor .svg suffix
FIGURES=interrupt-ack-uvm

SVG_FILES=$(patsubst %,$(FIGDIR)/%.svg,$(FIGURES))
WAVE_FILES=$(patsubst %,$(WAVEDIR)/%.wave,$(FIGURES))

all: $(SVG_FILES)

# wavedrom-cli requires a local installation and needs nodejs version >= 14.
# See https://github.com/wavedrom/cli.
$(FIGDIR)/%.svg: $(WAVEDIR)/%.wave
wavedrom-cli -i $^ -s $@

clean:
$(RM) $(SVG_FILES)

1 change: 1 addition & 0 deletions verif/docs/Protocols/figures/interrupt-ack-uvm.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions verif/docs/Protocols/interrupt-verification.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
////
Copyright 2024 Thales DIS France SAS

Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0
You may obtain a copy of the License at https://solderpad.org/licenses/

Original Author: Zbigniew CHAMSKI - Thales
////

= Interrupt Verification protocol

== Scope

In the rest of this section it is assumed that the design-under-test is limited to a hart (or 'core') with its internal CSRs. The hart supports only Machine mode, explicitly excluding User and Supervisor modes as not implemented.

Arbitration between multiple sources of external interrupts is under the responsibility of the "platform" and thus performed outside of the hart.

Therefore, the verification of interrupt handling at hart level covers two co-ordinated aspects:

* the detection of "interrupt present" state for all implemented interrupt types among those listed in MIP/MIE;
* the generation of the response to the "interrupt present" state according to the fixed interrupt priority (see https://cva6.readthedocs.io/en/latest/04_cv32a65x/riscv/priv.html#_machine_interrupt_mip_and_mie_registers[Machine Interrupt Registers, CV32A65X Privileged ISA spec, section 3.1.9]).

== Interfaces

The interface for raising interrupts (from the platform to the hart) is a set of bits in the `MIP` (Machine Interrupt Present) CSR. These bits are controlled by the verification platform by driving hardware inputs of the hart.
The interface for clearing pending interrupts (from the hart to the platform) can either rely on writable "interrupt pending" bits in `MIP`, or "the implementation must provide some other mechanism for clearing the pending interrupt."

On CV32A65X the "interrupt pending" bits in `MIP` are read-only and therefore, they cannot be used to acknowledge (clear) a pending interrupt. Hence, for the purpose of verification we introduce a 64-bit memory-mapped register `int_ack`. To acknowledge a pending interrupt represented by bit N in the `MIP` register, the hart performs a memory store at the address of `int_ack` with bit N set in the value being stored. All stores into `int_ack` shall be monitored by the platform and shall eventually result in clearing a pending interrupt if both the corresponding "interrupt pending" bit in MIP is set and the value written into `int_ack` has a bit set at the same bit position. The clearing of the corresponding pending interrupt by the platform may be immediate or delayed.

The `int_ack` memory location has no fixed address.

== The protocol

The basic operation of the interrupt verification protocol is shown in xref:fig-basic-raise-clear-protocol[xrefstyle=short].

.Basic interrupt raise-acknowledge protocol
[#fig-basic-raise-clear-protocol]
image::figures/interrupt-ack-uvm.svg[Basic interrupt verification protocol,800,opts=inline]

The platform/testbench notifies the hart of raising an interrupt by setting the corresponding hardware input of the hart. This results in the hart setting a bit at position 'N' in the `mip` CSR according to the hardware input being raised (marker `r` for "raise").
The hart detects the presence of a pending interrupt at position N in `mip`, and if that interrupt is enabled it starts executing the interrupt handler.

While executing the handler, the hart performs a memory store to the symbolic location `int_ack` with bit N set to acknowledge the servicing of interrupt at position N in `mip` (markers `s` for "store" and `w` for "write").

The platform responds by clearing the interrupt (marker `c` for "clear"): it clears bit N in `mip` *and* performs a memory store that clears bit N in `int_ack` (marker `z` for "zero the ACK"). Both operations shall occur in finite time but quickly enough to clear the interrupt pending bit in `mip` before the hart enables interrupts at the end of the interrupt handler.

== Software implementation

Test programs used for interrupt verification shall reserve the necessary memory storage located at symbol `int_ack` and shall make the value of that symbol available to verification software.
To reserve the storage, assembly program shall use storage type `.dword`. C/C++ programs shall use scalar type `unsigned long int` with `volatile` qualifier to prevent over-optimization of assignment operations.

The symbol shall be defined in section `.uvmif`. The linker scripts shall ensure that this section is aligned on a 64-byte multiple to reduce cache latency artefacts where applicable.

== Rationale

. The storage allocated for `int_ack` is 64 bits wide to support a single test program source for XLEN=32 and XLEN=64.
. The symbol `int_ack` has no fixed address to avoid hardcoded dependencies in the verification flow.
. In order to keep `int_ack` isolated from other symbols occurring in verification test programs, the symbol is placed in a dedicated section.
. The interrupt acknowledge mechanism abstracts from the Machine Timer Interrupt logic (cf. https://cva6.readthedocs.io/en/latest/04_cv32a65x/riscv/priv.html#_machine_timer_mtime_and_mtimecmp_registers[Machine Timer Registers, CV32A65X Privileged ISA spec, section 3.2.1]).
In particular, it relieves the verification platform from maintaining consistent `MTIME` and `MTIMECMP` values.
39 changes: 39 additions & 0 deletions verif/docs/Protocols/wavedrom/interrupt-ack-uvm.wave
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2024 Thales DIS France SAS
//
// Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0
// You may obtain a copy of the License at https://solderpad.org/licenses/
//
// Original Author: Zbigniew CHAMSKI - Thales

{signal: [
{ name: 'clk', wave: 'p..|..|...'},
['Platform',
['int active',
{name: 'int #0', wave: '0..|..|...'},
{name: 'int #n', wave: '01.|..|0..',
node: '.r.....c..' },
]
],
{},
['hart',
['mip',
{name: 'bit[0]', wave: 'x..|......'},
{name: 'bit[n]', wave: '01.|..|0..',
node: '..........'},
],
{name: 'exec', wave: '7.4|..|.7.', data: 'normal handler normal',
node: '....s.....'},
],
{ node: '.RE....ZX.'},
{},
['mem',
{name: '@int_ack[0]', wave: '...|..|...'},
{name: '@int_ack[n]', wave: '0..|.1|0..',
node: '.....w.z..'}
]
],
edge:
['R+E >0', 'Z+X >0', 's|->w ACK', 'r|R', 'c|z'],
}
64 changes: 63 additions & 1 deletion verif/regress/riscv-compliance.patch
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,21 @@ index 318b7498..4d6dd039 100644
.align 2; \
1:

diff --git a/riscv-test-env/p/link.ld b/riscv-test-env/p/link.ld
index 392e74f9..cc4a58e1 100644
--- a/riscv-test-env/p/link.ld
+++ b/riscv-test-env/p/link.ld
@@ -12,6 +12,8 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
+ .uvmif : { *(.uvmif) }
+ . = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
diff --git a/riscv-test-env/p/riscv_test.h b/riscv-test-env/p/riscv_test.h
index eaa67585..966e2ac0 100644
index eaa67585..91abcde0 100644
--- a/riscv-test-env/p/riscv_test.h
+++ b/riscv-test-env/p/riscv_test.h
@@ -63,10 +63,10 @@
Expand Down Expand Up @@ -74,6 +87,42 @@ index eaa67585..966e2ac0 100644

//-----------------------------------------------------------------------
// Data Section Macro
@@ -238,6 +238,9 @@ end_testcode: \
.align 8; .global tohost; tohost: .dword 0; \
.align 8; .global fromhost; fromhost: .dword 0; \
.popsection; \
+ .pushsection .uvmif,"aw",@progbits; \
+ .align 8; .global int_ack; int_ack: .dword 0; \
+ .popsection; \
.align 4; .global begin_signature; begin_signature:

#define RVTEST_DATA_END \
diff --git a/riscv-test-env/pm/link.ld b/riscv-test-env/pm/link.ld
index b3e315e7..5baa819e 100644
--- a/riscv-test-env/pm/link.ld
+++ b/riscv-test-env/pm/link.ld
@@ -8,6 +8,8 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
+ .uvmif : { *(.uvmif) }
+ . = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
diff --git a/riscv-test-env/pt/link.ld b/riscv-test-env/pt/link.ld
index b3e315e7..5baa819e 100644
--- a/riscv-test-env/pt/link.ld
+++ b/riscv-test-env/pt/link.ld
@@ -8,6 +8,8 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
+ .uvmif : { *(.uvmif) }
+ . = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
diff --git a/riscv-test-env/v/entry.S b/riscv-test-env/v/entry.S
index 97196620..37a68ba1 100644
--- a/riscv-test-env/v/entry.S
Expand All @@ -87,6 +136,19 @@ index 97196620..37a68ba1 100644
STORE t0,34*REGBYTES(sp)
csrr t0,scause
STORE t0,35*REGBYTES(sp)
diff --git a/riscv-test-env/v/link.ld b/riscv-test-env/v/link.ld
index b3e315e7..5baa819e 100644
--- a/riscv-test-env/v/link.ld
+++ b/riscv-test-env/v/link.ld
@@ -8,6 +8,8 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
+ .uvmif : { *(.uvmif) }
+ . = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
diff --git a/riscv-test-env/v/vm.c b/riscv-test-env/v/vm.c
index 6ab7fd15..1b365a9f 100644
--- a/riscv-test-env/v/vm.c
Expand Down
25 changes: 24 additions & 1 deletion verif/regress/riscv-tests-env.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
diff --git a/p/link.ld b/p/link.ld
index b3e315e..5baa819 100644
--- a/p/link.ld
+++ b/p/link.ld
@@ -8,6 +8,8 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
+ .uvmif : { *(.uvmif) }
+ . = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
diff --git a/p/riscv_test.h b/p/riscv_test.h
index 88ca6c1..b7eb1c2 100644
index 88ca6c1..def42af 100644
--- a/p/riscv_test.h
+++ b/p/riscv_test.h
@@ -110,7 +110,7 @@
Expand Down Expand Up @@ -35,6 +48,16 @@ index 88ca6c1..b7eb1c2 100644

//-----------------------------------------------------------------------
// Data Section Macro
@@ -262,6 +260,9 @@ reset_vector: \
.align 6; .global tohost; tohost: .dword 0; \
.align 6; .global fromhost; fromhost: .dword 0; \
.popsection; \
+ .pushsection .uvmif,"aw",@progbits; \
+ .align 6; .global int_ack; int_ack: .dword 0; \
+ .popsection; \
.align 4; .global begin_signature; begin_signature:

#define RVTEST_DATA_END .align 4; .global end_signature; end_signature:
diff --git a/v/entry.S b/v/entry.S
index fa492e6..49b2d3e 100644
--- a/v/entry.S
Expand Down
2 changes: 1 addition & 1 deletion verif/regress/smoke-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ if [[ "$DV_SIMULATORS" != *"uvm"* ]]; then
python3 cva6.py --testlist=../tests/testlist_riscv-tests-cv64a6_imafdc_sv39-p.yaml --test rv64ui-p-add --iss_yaml cva6.yaml --target cv64a6_imafdc_sv39 --iss=$DV_SIMULATORS $DV_OPTS
python3 cva6.py --testlist=../tests/testlist_riscv-arch-test-cv64a6_imafdc_sv39.yaml --test rv64i_m-add-01 --iss_yaml cva6.yaml --target cv64a6_imafdc_sv39 --iss=$DV_SIMULATORS $DV_OPTS --linker=../tests/riscv-arch-test/riscv-target/spike/link.ld
python3 cva6.py --testlist=../tests/testlist_custom.yaml --test custom_test_template --iss_yaml cva6.yaml --target cv64a6_imafdc_sv39 --iss=$DV_SIMULATORS $DV_OPTS
python3 cva6.py --c_tests ../tests/custom/hello_world/hello_world.c --iss_yaml cva6.yaml --target cv64a6_imafdc_sv39 --iss=$DV_SIMULATORS --gcc_opts="$CC_OPTS -T ../tests/custom/common/test.ld" $DV_OPTS
python3 cva6.py --c_tests ../tests/custom/hello_world/hello_world.c --iss_yaml cva6.yaml --target cv64a6_imafdc_sv39 --iss=$DV_SIMULATORS --gcc_opts="$CC_OPTS" $DV_OPTS --linker=../tests/custom/common/test.ld
make -C ../.. clean
make clean_all
python3 cva6.py --testlist=../tests/testlist_riscv-compliance-cv32a60x.yaml --test rv32i-I-ADD-01 --iss_yaml cva6.yaml --target cv32a6_imac_sv32 --iss=$DV_SIMULATORS $DV_OPTS
Expand Down
3 changes: 3 additions & 0 deletions verif/tests/custom/Zcmp/link.ld
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }

. = ALIGN(0x1000);
.uvmif : { *(.uvmif) }

. = ALIGN(0x1000);
.text : { *(.text) }

Expand Down
3 changes: 3 additions & 0 deletions verif/tests/custom/Zcmp/riscv_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ reset_vector: \
.align 6; .global tohost; tohost: .dword 0; \
.align 6; .global fromhost; fromhost: .dword 0; \
.popsection; \
.pushsection .uvmif,"aw",@progbits; \
.align 6; .global int_ack; int_ack: .dword 0; \
.popsection; \
.align 4; .global begin_signature; begin_signature:

#define RVTEST_DATA_END .align 4; .global end_signature; end_signature:
Expand Down
6 changes: 6 additions & 0 deletions verif/tests/custom/common/crt.S
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,9 @@ tohost: .dword 0
.align 6
.globl fromhost
fromhost: .dword 0

.section ".uvmif","aw",@progbits
# Alignment is 2**6 == 64 bytes
.align 6
.globl int_ack
int_ack: .dword 0
3 changes: 3 additions & 0 deletions verif/tests/custom/common/test.ld
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ SECTIONS
. = ALIGN(0x1000);
.tohost : { *(.tohost) }

. = ALIGN(0x1000);
.uvmif : { *(.uvmif) }

. = ALIGN(0x1000);
.text : { *(.text) }

Expand Down
2 changes: 1 addition & 1 deletion verif/tests/testlist_custom.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

common_test_config: &common_test_config
path_var: TESTS_PATH
gcc_opts: "-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common -T ../tests/custom/common/test.ld"
gcc_opts: "-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common"

testlist:
- test: custom_test_template
Expand Down

0 comments on commit f974e10

Please sign in to comment.