diff --git a/docs/source/dual-core-lock-step.md b/docs/source/dual-core-lock-step.md new file mode 100644 index 00000000000..cda85573819 --- /dev/null +++ b/docs/source/dual-core-lock-step.md @@ -0,0 +1,222 @@ +# Dual-Core Lockstep (DCLS) + +This chapter describes the proposed Dual-Core Lockstep functionality and its future implementation in the VeeR EL2 Core, as required by Caliptra 2.0 for side-channel mitigation scenarios, although it may be useful for other applications like rad-hardening or other safety related-scenarios which DCLS is often also used for. + +## VeeR EL2 DCLS Overview + +The lockstep feature will be added as an optional feature of VeeR EL2, disabled by default. +If enabled, another copy of the VeeR EL2 CPU core will be additionally instantiated in the design. +This second core will be referred to as a Shadow Core in this chapter. + +The Shadow Core is delayed by a constant, configurable `DELAY` number of clock cycles with regards to the main core. + +The `DCCM` and `ICCM` memories are not duplicated, and only the main VeeR EL2 CPU core has access to them. +The Shadow Core is only supplied with the delayed inputs of the main core, including the relevant `DCCM` and `ICCM` data, without any ability to read from or write to those memories by itself. + +Similarly, `Icache` is not duplicated with only the main VeeR EL2 CPU core having direct access. +The Shadow Core will receive a delayed copy of main core's `Icache` inputs. +The copy of main core's `Icache` outputs will be passed into the `Equivalency Checker` to be validated against the Shadow Core's `Icache` outputs. + +Both cores operate on separate register files. + +The Shadow Core's register file can be monitored via the exposed `Register File Interface`. + +The diagram below outlines the architecture of the proposed solution. + +![VeeR DCLS Overview](img/dcls_block_diagram.png) + +Outputs and the register file from the main core are delayed by `DELAY` cycles and passed to the `Equivalency Checker` for verification against the outputs and the register file of the Shadow Core. + +If the `Equivalency Checker` detects a mismatch between the cores, the logic will assert a panic signal. + +It is up to the integrator to provide the error handling for the corruption detection. + +[Monitored registers](#monitored-registers) are exposed for comparison purposes from the [el2_dec_tlu_ctl](https://github.com/chipsalliance/Cores-VeeR-EL2/blob/795eb588e34b6815033b769d54fcf7cfac4aae3a/design/dec/el2_dec_tlu_ctl.sv) and [el2_dec_gpr_ctl](https://github.com/chipsalliance/Cores-VeeR-EL2/blob/795eb588e34b6815033b769d54fcf7cfac4aae3a/design/dec/el2_dec_gpr_ctl.sv) modules through the [el2_dec](https://github.com/chipsalliance/Cores-VeeR-EL2/blob/795eb588e34b6815033b769d54fcf7cfac4aae3a/design/dec/el2_dec.sv) instantiated in [el2_veer](https://github.com/chipsalliance/Cores-VeeR-EL2/blob/795eb588e34b6815033b769d54fcf7cfac4aae3a/design/el2_veer.sv#L924) module. + +### Error Policy + +Depending on the application and its security requirements, one of the following error policies can be configured: + +* Detected error will be reported using the detection bit and the cores' execution flow will proceed. +* Detected error will trigger an interrupt and halt the execution until the interrupt is handled. +* Detected error will escalate the problem to the external controller and await the reset of the system. + +It is up to the integrator to choose the error policy and provide a handler logic (e.g. external reset block). + +### Monitored Registers + +To determine whether a discrepancy has occurred, the outputs from both cores will be compared taking into account a reasonable subset of the VeeR EL2 registers, as defined in the table below: + +:::{list-table} Monitored VeeR EL2 Registers +:header-rows: 0 +:name: tab-dcls-monitored-veer-el2-registers +:align: center + +* - **Name** + - **Description** +* - x1 (ra) + - Return address +* - x2 (sp) + - Stack pointer +* - x8 (s0/fp) + - Saved register / frame pointer +* - x10-x11 (a0-a1) + - Function arguments / return values +* - x12-17 (a2-7) + - Function arguments +* - pc + - Program Counter +* - npc + - Next Program Counter +* - mstatus + - Machine status +* - [mie](adaptations.md#machine-interrupt-enable-mie-and-machine-interrupt-pending-mip-registers) + - Machine interrupt enable +* - mtvec + - Machine trap-handler base address +* - mscratch + - Scratch register for machine trap handlers +* - mepc + - Machine exception program counter +* - [mcause](adaptations.md#machine-cause-register-mcause) + - Machine trap cause +* - mtval + - Machine bad address or instruction +* - [mip](adaptations.md#machine-interrupt-enable-mie-and-machine-interrupt-pending-mip-registers) + - Machine interrupt pending +* - [mcycle](performance.md#standard-risc-v-registers) + - Machine cycle counter +* - [minstret](performance.md#standard-risc-v-registers) + - Machine instructions-retired counter +* - [mrac](memory-map.md#region-access-control-register-mrac) + - Region access control +::: + +```{note} +Should the monitored registers be dependent on the VeeR configuration? +``` + +## Configuration + +```{warning} +The DCLS feature is not supported in Debug Mode. +Entering Debug Mode with DCLS enabled will disable DCLS until the next reset. +``` + +The DCLS feature can be enabled via: `--set=dcls_enable` option. + +The delay can be specified with `--set=dcls_delay = { 2, 3, 4 }`, with the delay between 2 and 4 cycles. + +```{note} +The range of allowed delays can be discussed in detail and adjusted later. +``` + +## Validation Plan + +The DCLS feature will be tested within: + +* Software DCLS [smoke test](https://github.com/chipsalliance/Cores-VeeR-EL2/tree/main/testbench/tests/dcls/dcls.c) - covers VeeR CPU core with the Shadow Core execution flow. +* RTL `el2_veer_lockstep` [module tests](https://github.com/chipsalliance/Cores-VeeR-EL2/tree/main/verification/block/dcls) - covers the Shadow Core by itself. + +:::{list-table} Validation Plan +:name: vp-block-name-list-table +:header-rows: 0 +:align: center + +* - **Function** + - **VeeR EL2 CPU core input corruption detection** +* - Reference Document + - +* - Check description + - Verify the panic signal is raised only upon core states' mismatch. Introduce corruption via VeeR EL2 CPU core inputs directed to the Shadow Core. +* - Coverage groups + - Each output of the VeeR EL2 CPU Core is reached when detecting the mismatch by `Equivalence Checker`. All bounds of configurable delay are reached. +* - Assertions + - Detection bit is asserted upon encountered corruption. Error behavior follows the error handling policy. No action is taken if no corruption was introduced. +* - Comments + - +* - Test Name + - +* - + - +* - **Function** + - **VeeR EL2 CPU core output corruption detection** +* - Reference Document + - +* - Check description + - Verify the panic signal is raised only upon core states' mismatch. Introduce corruption via the outputs of the main VeeR CPU core directed to `Equivalence Checker` in the Shadow Core. +* - Coverage groups + - Each output of the VeeR EL2 CPU Core is reached when detecting the mismatch by `Equivalence Checker`. All bounds of configurable delay are reached. +* - Assertions + - Detection bit is asserted upon encountered corruption. Error behavior follows the relevant error handling policy. No action is taken if no corruption was introduced. +* - Comments + - +* - Test Name + - +* - + - +* - **Function** + - **Internal state corruption detection** +* - Reference Document + - +* - Check description + - Verify the panic signal is raised only upon core states' mismatch. Introduce corruption via exposed registers of the Shadow Core. +* - Coverage groups + - Each [monitored register](#monitored-registers) is detected by the `Equivalence Checker`. All bounds of configurable delay are reached. +* - Assertions + - Detection bit is asserted upon encountered corruption. Error behavior follows the relevant error handling policy. No action is taken if no corruption was introduced. +* - Comments + - The default path will likely be more easily testable with the help of the software testbench. It should be possible to simulate a fault injection via mailbox see: [top_tb.sv](https://github.com/chipsalliance/Cores-VeeR-EL2/blob/795eb588e34b6815033b769d54fcf7cfac4aae3a/testbench/tb_top.sv#L727). +* - Test Name + - +* - + - +* - **Function** + - **DCLS default execution** +* - Reference Document + - +* - Check description + - Verify the DCLS feature behavior during non-obstructed execution. +* - Coverage groups + - +* - Assertions + - Detection bit is not raised. Detection interrupt is not asserted. The test provides the same results as the VeeR EL2 CPU core without the DCLS feature enabled. +* - Comments + - It might be beneficial to use a software test with a program that will produce a result that can by easily compared to an alternative flow and also engage the VeeR EL2 core. Consider matrix multiplication. +* - Test Name + - +* - + - +* - **Function** + - **Error reporting** +* - Reference Document + - +* - Check description + - Verify error reporting policy upon detected corruption. +* - Coverage groups + - Each error policy is covered. +* - Assertions + - +* - Comments + - +* - Test Name + - +* - + - +* - **Function** + - **Reset** +* - Reference Document + - +* - Check description + - Verify the behavior in reset. Ensure normal execution upon leaving reset. +* - Coverage groups + - +* - Assertions + - Shadow Core enters reset at the same time the main VeeR core does. Shadow Core exits reset after a predefined delay following the main core. Detected corruption and interrupts are deasserted upon entering the reset. +* - Comments + - +* - Test Name + - +* - + - +::: diff --git a/docs/source/img/dcls_block_diagram.png b/docs/source/img/dcls_block_diagram.png new file mode 100644 index 00000000000..cd218503900 Binary files /dev/null and b/docs/source/img/dcls_block_diagram.png differ diff --git a/docs/source/index.md b/docs/source/index.md index 80e61f1e9cf..fadc52259c7 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -8,6 +8,7 @@ intro overview memory-map error-protection +dual-core-lock-step timers power interrupts diff --git a/testbench/tests/dcls/crt0.s b/testbench/tests/dcls/crt0.s new file mode 120000 index 00000000000..d09de58fb6a --- /dev/null +++ b/testbench/tests/dcls/crt0.s @@ -0,0 +1 @@ +../../asm/crt0.s \ No newline at end of file diff --git a/testbench/tests/dcls/dcls.c b/testbench/tests/dcls/dcls.c new file mode 100644 index 00000000000..b0d406bbe2b --- /dev/null +++ b/testbench/tests/dcls/dcls.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2024 Antmicro, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +int main(int argc, char *argv[]) +{ + printf("Hello, world!\n"); + return 0; +} diff --git a/testbench/tests/dcls/dcls.ld b/testbench/tests/dcls/dcls.ld new file mode 100644 index 00000000000..0f92eb31bb2 --- /dev/null +++ b/testbench/tests/dcls/dcls.ld @@ -0,0 +1,12 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS { + . = 0x80000000; + .text : { *(.text*) } + _end = .; + .data : { *(.*data) *(.rodata*) *(.sbss) STACK = ALIGN(16) + 0x1000;} + .bss : { *(.bss) } + . = 0xd0580000; + .data.io . : { *(.data.io) } +} \ No newline at end of file diff --git a/testbench/tests/dcls/dcls.mki b/testbench/tests/dcls/dcls.mki new file mode 100644 index 00000000000..4b34b4d3f41 --- /dev/null +++ b/testbench/tests/dcls/dcls.mki @@ -0,0 +1,2 @@ +OFILES = crt0.o dcls.o printf.o +TEST_CFLAGS = -g -O3 \ No newline at end of file diff --git a/testbench/tests/dcls/printf.c b/testbench/tests/dcls/printf.c new file mode 120000 index 00000000000..430ba5df62c --- /dev/null +++ b/testbench/tests/dcls/printf.c @@ -0,0 +1 @@ +../../asm/printf.c \ No newline at end of file diff --git a/verification/block/dcls/Makefile b/verification/block/dcls/Makefile new file mode 100644 index 00000000000..94eb749f8c4 --- /dev/null +++ b/verification/block/dcls/Makefile @@ -0,0 +1,62 @@ +null := +space := $(null) # +comma := , + +CURDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +SRCDIR := $(abspath $(CURDIR)../../../../design) + +TEST_FILES = $(sort $(wildcard test_*.py)) + +MODULE ?= $(subst $(space),$(comma),$(subst .py,,$(TEST_FILES))) +TOPLEVEL = el2_veer_lockstep + +VERILOG_INCLUDE_DIRS += \ + ${RV_ROOT}/testbench \ + ${RV_ROOT}/design/include + +VERILOG_SOURCES = \ + ${SRCDIR}/el2_veer_wrapper.sv \ + ${SRCDIR}/el2_mem.sv \ + ${SRCDIR}/el2_pic_ctrl.sv \ + ${SRCDIR}/el2_veer.sv \ + ${SRCDIR}/el2_dma_ctrl.sv \ + ${SRCDIR}/el2_pmp.sv \ + ${SRCDIR}/ifu/el2_ifu_aln_ctl.sv \ + ${SRCDIR}/ifu/el2_ifu_compress_ctl.sv \ + ${SRCDIR}/ifu/el2_ifu_ifc_ctl.sv \ + ${SRCDIR}/ifu/el2_ifu_bp_ctl.sv \ + ${SRCDIR}/ifu/el2_ifu_ic_mem.sv \ + ${SRCDIR}/ifu/el2_ifu_mem_ctl.sv \ + ${SRCDIR}/ifu/el2_ifu_iccm_mem.sv \ + ${SRCDIR}/ifu/el2_ifu.sv \ + ${SRCDIR}/dec/el2_dec_decode_ctl.sv \ + ${SRCDIR}/dec/el2_dec_gpr_ctl.sv \ + ${SRCDIR}/dec/el2_dec_ib_ctl.sv \ + ${SRCDIR}/dec/el2_dec_pmp_ctl.sv \ + ${SRCDIR}/dec/el2_dec_tlu_ctl.sv \ + ${SRCDIR}/dec/el2_dec_trigger.sv \ + ${SRCDIR}/dec/el2_dec.sv \ + ${SRCDIR}/exu/el2_exu_alu_ctl.sv \ + ${SRCDIR}/exu/el2_exu_mul_ctl.sv \ + ${SRCDIR}/exu/el2_exu_div_ctl.sv \ + ${SRCDIR}/exu/el2_exu.sv \ + ${SRCDIR}/lsu/el2_lsu.sv \ + ${SRCDIR}/lsu/el2_lsu_clkdomain.sv \ + ${SRCDIR}/lsu/el2_lsu_addrcheck.sv \ + ${SRCDIR}/lsu/el2_lsu_lsc_ctl.sv \ + ${SRCDIR}/lsu/el2_lsu_stbuf.sv \ + ${SRCDIR}/lsu/el2_lsu_bus_buffer.sv \ + ${SRCDIR}/lsu/el2_lsu_bus_intf.sv \ + ${SRCDIR}/lsu/el2_lsu_ecc.sv \ + ${SRCDIR}/lsu/el2_lsu_dccm_mem.sv \ + ${SRCDIR}/lsu/el2_lsu_dccm_ctl.sv \ + ${SRCDIR}/lsu/el2_lsu_trigger.sv \ + ${SRCDIR}/dbg/el2_dbg.sv \ + ${SRCDIR}/dmi/dmi_mux.v \ + ${SRCDIR}/dmi/dmi_wrapper.v \ + ${SRCDIR}/dmi/dmi_jtag_to_core_sync.v \ + ${SRCDIR}/dmi/rvjtag_tap.v \ + ${SRCDIR}/lib/el2_lib.sv \ + $(SRCDIR)/el2_veer_lockstep.sv + +include $(CURDIR)/../common.mk diff --git a/verification/block/dcls/test_reset.py b/verification/block/dcls/test_reset.py new file mode 100644 index 00000000000..d78f790eb8f --- /dev/null +++ b/verification/block/dcls/test_reset.py @@ -0,0 +1,27 @@ +# Copyright (c) 2024 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +import cocotb +import pyuvm +from cocotb.triggers import ClockCycles +from testbench import BaseTest + +# ============================================================================= + + +@pyuvm.test() +class TestReset(BaseTest): + """ + A basic test that resets the DUT + """ + + # TODO: Change me once `el2_veer_lockstep` outputs only `panic` signal + async def run(self): + # The shadow core should go into the reset regardless of the delay + state = { + "corruption_detected": 0, + } + + for name, value in state.items(): + signal = getattr(cocotb.top, name) + assert signal.value == value, "{}={}, should be {}".format(name, signal.value, value) diff --git a/verification/block/dcls/testbench.py b/verification/block/dcls/testbench.py new file mode 100644 index 00000000000..6f0b3a6cf2b --- /dev/null +++ b/verification/block/dcls/testbench.py @@ -0,0 +1,82 @@ +import logging +import os + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import ClockCycles, FallingEdge +from pyuvm import ConfigDB, uvm_env, uvm_report_object, uvm_test + + +class BaseEnv(uvm_env): + """ + Base PyUVM test environment + """ + + def build_phase(self): + # Config + pmp_entries = 16 + ConfigDB().set(None, "*", "PMP_ENTRIES", pmp_entries) + ConfigDB().set(None, "*", "PMP_CHANNELS", 3) + ConfigDB().set(None, "*", "PMP_GRANULARITY", 0) + + ConfigDB().set(None, "*", "TEST_CLK_PERIOD", 1) + ConfigDB().set(None, "*", "TEST_ITERATIONS", 100) + + def connect_phase(self): + pass + + +# ============================================================================== + + +class BaseTest(uvm_test): + """ + Base test for the module + """ + + def __init__(self, name, parent, env_class=BaseEnv): + super().__init__(name, parent) + self.env_class = env_class + + # Synchronize pyuvm logging level with cocotb logging level. Unclear + # why it does not happen automatically. + level = logging.getLevelName(os.environ.get("COCOTB_LOG_LEVEL", "INFO")) + uvm_report_object.set_default_logging_level(level) + + def build_phase(self): + self.env = self.env_class("env", self) + + def start_clock(self, name): + period = ConfigDB().get(None, "", "TEST_CLK_PERIOD") + sig = getattr(cocotb.top, name) + clock = Clock(sig, period, units="ns") + cocotb.start_soon(clock.start(start_high=False)) + + async def do_reset(self): + cocotb.top.rst_l.value = 0 + await ClockCycles(cocotb.top.clk, 2) + await FallingEdge(cocotb.top.clk) + cocotb.top.rst_l.value = 1 + + async def run_phase(self): + self.raise_objection() + + # Start clocks + self.start_clock("clk") + + # Issue reset + await self.do_reset() + + # Wait some cycles + await ClockCycles(cocotb.top.clk, 2) + + # Run the actual test + await self.run() + + # Wait some cycles + await ClockCycles(cocotb.top.clk, 10) + + self.drop_objection() + + async def run(self): + raise NotImplementedError() diff --git a/verification/block/noxfile.py b/verification/block/noxfile.py index f5cf61ab122..d375cbcb472 100644 --- a/verification/block/noxfile.py +++ b/verification/block/noxfile.py @@ -270,6 +270,19 @@ def dccm_verify(session, blockName, testName, coverage): verify_block(session, blockName, testName, coverage) +@nox.session(tags=["tests"]) +@nox.parametrize("blockName", ["dcls"]) +@nox.parametrize( + "testName", + [ + "test_reset", + ], +) +@nox.parametrize("coverage", coverageTypes) +def dcls_verify(session, blockName, testName, coverage): + verify_block(session, blockName, testName, coverage) + + @nox.session(tags=["tests"]) @nox.parametrize("blockName", ["lib_axi4_to_ahb"]) @nox.parametrize(