From f974e105bf88cf81c32f55789b0baab4fe4d16c9 Mon Sep 17 00:00:00 2001 From: Zbigniew Chamski <107464696+zchamski@users.noreply.github.com> Date: Thu, 19 Sep 2024 18:31:42 +0200 Subject: [PATCH] Add a basic mechanism for interrupt acknowledge. (#2502) --- verif/core-v-verif | 2 +- verif/docs/Protocols/Makefile | 28 ++++++++ .../Protocols/figures/interrupt-ack-uvm.svg | 1 + .../Protocols/interrupt-verification.adoc | 62 ++++++++++++++++++ .../Protocols/wavedrom/interrupt-ack-uvm.wave | 39 +++++++++++ verif/regress/riscv-compliance.patch | 64 ++++++++++++++++++- verif/regress/riscv-tests-env.patch | 25 +++++++- verif/regress/smoke-tests.sh | 2 +- verif/tests/custom/Zcmp/link.ld | 3 + verif/tests/custom/Zcmp/riscv_test.h | 3 + verif/tests/custom/common/crt.S | 6 ++ verif/tests/custom/common/test.ld | 3 + verif/tests/testlist_custom.yaml | 2 +- 13 files changed, 235 insertions(+), 5 deletions(-) create mode 100644 verif/docs/Protocols/Makefile create mode 100644 verif/docs/Protocols/figures/interrupt-ack-uvm.svg create mode 100644 verif/docs/Protocols/interrupt-verification.adoc create mode 100644 verif/docs/Protocols/wavedrom/interrupt-ack-uvm.wave diff --git a/verif/core-v-verif b/verif/core-v-verif index 2e5fbaed36..e2b58bede9 160000 --- a/verif/core-v-verif +++ b/verif/core-v-verif @@ -1 +1 @@ -Subproject commit 2e5fbaed368aa25e5bfc2a5502457f430fb95e7d +Subproject commit e2b58bede9abb52a3f2e770cc64bd4485fee9d29 diff --git a/verif/docs/Protocols/Makefile b/verif/docs/Protocols/Makefile new file mode 100644 index 0000000000..1cd221ea49 --- /dev/null +++ b/verif/docs/Protocols/Makefile @@ -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) + diff --git a/verif/docs/Protocols/figures/interrupt-ack-uvm.svg b/verif/docs/Protocols/figures/interrupt-ack-uvm.svg new file mode 100644 index 0000000000..b081d5eee3 --- /dev/null +++ b/verif/docs/Protocols/figures/interrupt-ack-uvm.svg @@ -0,0 +1 @@ +clkint #0int #nbit[0]bit[n]execnormalhandlernormal@int_ack[0]@int_ack[n]>0>0ACKrcswzint activePlatformmiphartmem \ No newline at end of file diff --git a/verif/docs/Protocols/interrupt-verification.adoc b/verif/docs/Protocols/interrupt-verification.adoc new file mode 100644 index 0000000000..4a14ef477e --- /dev/null +++ b/verif/docs/Protocols/interrupt-verification.adoc @@ -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. diff --git a/verif/docs/Protocols/wavedrom/interrupt-ack-uvm.wave b/verif/docs/Protocols/wavedrom/interrupt-ack-uvm.wave new file mode 100644 index 0000000000..a842979eb4 --- /dev/null +++ b/verif/docs/Protocols/wavedrom/interrupt-ack-uvm.wave @@ -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'], +} diff --git a/verif/regress/riscv-compliance.patch b/verif/regress/riscv-compliance.patch index 240cd52a0d..45eab76981 100644 --- a/verif/regress/riscv-compliance.patch +++ b/verif/regress/riscv-compliance.patch @@ -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 @@ @@ -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 @@ -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 diff --git a/verif/regress/riscv-tests-env.patch b/verif/regress/riscv-tests-env.patch index 7af4257b87..cbe3f38bb4 100644 --- a/verif/regress/riscv-tests-env.patch +++ b/verif/regress/riscv-tests-env.patch @@ -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 @@ @@ -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 diff --git a/verif/regress/smoke-tests.sh b/verif/regress/smoke-tests.sh index 555a7323e6..f8bcf2689b 100644 --- a/verif/regress/smoke-tests.sh +++ b/verif/regress/smoke-tests.sh @@ -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 diff --git a/verif/tests/custom/Zcmp/link.ld b/verif/tests/custom/Zcmp/link.ld index 6135e79df0..67e7d6cf05 100644 --- a/verif/tests/custom/Zcmp/link.ld +++ b/verif/tests/custom/Zcmp/link.ld @@ -28,6 +28,9 @@ SECTIONS . = ALIGN(0x1000); .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .uvmif : { *(.uvmif) } + . = ALIGN(0x1000); .text : { *(.text) } diff --git a/verif/tests/custom/Zcmp/riscv_test.h b/verif/tests/custom/Zcmp/riscv_test.h index 38f5cb36cf..6fe83e415b 100644 --- a/verif/tests/custom/Zcmp/riscv_test.h +++ b/verif/tests/custom/Zcmp/riscv_test.h @@ -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: diff --git a/verif/tests/custom/common/crt.S b/verif/tests/custom/common/crt.S index 799b82e6c8..d808114d21 100644 --- a/verif/tests/custom/common/crt.S +++ b/verif/tests/custom/common/crt.S @@ -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 diff --git a/verif/tests/custom/common/test.ld b/verif/tests/custom/common/test.ld index a50b017e81..9c4c8efba7 100644 --- a/verif/tests/custom/common/test.ld +++ b/verif/tests/custom/common/test.ld @@ -28,6 +28,9 @@ SECTIONS . = ALIGN(0x1000); .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .uvmif : { *(.uvmif) } + . = ALIGN(0x1000); .text : { *(.text) } diff --git a/verif/tests/testlist_custom.yaml b/verif/tests/testlist_custom.yaml index c326a286a2..7f3c125cbc 100644 --- a/verif/tests/testlist_custom.yaml +++ b/verif/tests/testlist_custom.yaml @@ -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