From 1642e8340d011a0828928eb0d153ac2b82535c1f Mon Sep 17 00:00:00 2001 From: Wiktoria Kuna Date: Mon, 7 Oct 2024 11:01:59 +0200 Subject: [PATCH] Docs: Add DCLS documentation draft Internal-tag: [#66891] Signed-off-by: Wiktoria Kuna --- docs/source/dual-core-lock-step.md | 222 +++++++++++++++++++++++++ docs/source/img/dcls_block_diagram.png | Bin 0 -> 34469 bytes docs/source/index.md | 1 + testbench/tests/dcls/crt0.s | 1 + testbench/tests/dcls/dcls.c | 24 +++ testbench/tests/dcls/dcls.ld | 12 ++ testbench/tests/dcls/dcls.mki | 2 + testbench/tests/dcls/printf.c | 1 + verification/block/dcls/Makefile | 62 +++++++ verification/block/dcls/test_reset.py | 27 +++ verification/block/dcls/testbench.py | 82 +++++++++ verification/block/noxfile.py | 13 ++ 12 files changed, 447 insertions(+) create mode 100644 docs/source/dual-core-lock-step.md create mode 100644 docs/source/img/dcls_block_diagram.png create mode 120000 testbench/tests/dcls/crt0.s create mode 100644 testbench/tests/dcls/dcls.c create mode 100644 testbench/tests/dcls/dcls.ld create mode 100644 testbench/tests/dcls/dcls.mki create mode 120000 testbench/tests/dcls/printf.c create mode 100644 verification/block/dcls/Makefile create mode 100644 verification/block/dcls/test_reset.py create mode 100644 verification/block/dcls/testbench.py 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 0000000000000000000000000000000000000000..cd2185039003896523379fe1a66e3b27ca3f6e92 GIT binary patch literal 34469 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 literal 0 HcmV?d00001 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(