From 81287b2db6ef9c6052eadd39b078c3ddd8607a40 Mon Sep 17 00:00:00 2001 From: Ved Shanbhogue Date: Thu, 10 Oct 2024 17:20:20 +0100 Subject: [PATCH] Add Sstc extension This adds the stimecmp[h] CSRs. It is enabled by default. The code is slightly unfortunately structured to handle CSRs that don't fit the standard permission checks. I added a TODO to improve it. Co-authored-by: Tim Hutt --- c_emulator/riscv_platform.c | 5 + c_emulator/riscv_platform.h | 1 + c_emulator/riscv_platform_impl.c | 1 + c_emulator/riscv_platform_impl.h | 1 + c_emulator/riscv_sim.c | 5 + model/riscv_platform.sail | 33 +++--- model/riscv_sstc.sail | 20 ++++ model/riscv_sys_control.sail | 15 ++- model/riscv_sys_regs.sail | 171 +++++++++++++++++-------------- 9 files changed, 157 insertions(+), 95 deletions(-) create mode 100644 model/riscv_sstc.sail diff --git a/c_emulator/riscv_platform.c b/c_emulator/riscv_platform.c index ae67bac42..f467bbfb8 100644 --- a/c_emulator/riscv_platform.c +++ b/c_emulator/riscv_platform.c @@ -67,6 +67,11 @@ bool sys_enable_zicboz(unit u) return rv_enable_zicboz; } +bool sys_enable_sstc(unit u) +{ + return rv_enable_sstc; +} + uint64_t sys_pmp_count(unit u) { return rv_pmp_count; diff --git a/c_emulator/riscv_platform.h b/c_emulator/riscv_platform.h index fe81c901b..c8c740824 100644 --- a/c_emulator/riscv_platform.h +++ b/c_emulator/riscv_platform.h @@ -12,6 +12,7 @@ bool sys_enable_vext(unit); bool sys_enable_bext(unit); bool sys_enable_zicbom(unit); bool sys_enable_zicboz(unit); +bool sys_enable_sstc(unit); uint64_t sys_pmp_count(unit); uint64_t sys_pmp_grain(unit); diff --git a/c_emulator/riscv_platform_impl.c b/c_emulator/riscv_platform_impl.c index 2713715cc..1febe97d6 100644 --- a/c_emulator/riscv_platform_impl.c +++ b/c_emulator/riscv_platform_impl.c @@ -19,6 +19,7 @@ bool rv_enable_vext = true; bool rv_enable_bext = false; bool rv_enable_zicbom = false; bool rv_enable_zicboz = false; +bool rv_enable_sstc = false; bool rv_enable_dirty_update = false; bool rv_enable_misaligned = false; diff --git a/c_emulator/riscv_platform_impl.h b/c_emulator/riscv_platform_impl.h index 8bda5e624..bd0340566 100644 --- a/c_emulator/riscv_platform_impl.h +++ b/c_emulator/riscv_platform_impl.h @@ -23,6 +23,7 @@ extern bool rv_enable_vext; extern bool rv_enable_bext; extern bool rv_enable_zicbom; extern bool rv_enable_zicboz; +extern bool rv_enable_sstc; extern bool rv_enable_writable_misa; extern bool rv_enable_dirty_update; extern bool rv_enable_misaligned; diff --git a/c_emulator/riscv_sim.c b/c_emulator/riscv_sim.c index 78838462a..8ffe60026 100644 --- a/c_emulator/riscv_sim.c +++ b/c_emulator/riscv_sim.c @@ -58,6 +58,7 @@ enum { OPT_ENABLE_ZCB, OPT_ENABLE_ZICBOM, OPT_ENABLE_ZICBOZ, + OPT_ENABLE_SSTC, OPT_CACHE_BLOCK_SIZE, }; @@ -428,6 +429,10 @@ static int process_args(int argc, char **argv) fprintf(stderr, "enabling Zicboz extension.\n"); rv_enable_zicboz = true; break; + case OPT_ENABLE_SSTC: + fprintf(stderr, "enabling Sstc extension.\n"); + rv_enable_sstc = true; + break; case OPT_CACHE_BLOCK_SIZE: block_size_exp = ilog2(atol(optarg)); diff --git a/model/riscv_platform.sail b/model/riscv_platform.sail index b29eb8325..687ba3a34 100644 --- a/model/riscv_platform.sail +++ b/model/riscv_platform.sail @@ -121,8 +121,13 @@ function within_htif_readable forall 'n, 0 < 'n <= max_mem_access . (addr : xlen val plat_insns_per_tick = pure {interpreter: "Platform.insns_per_tick", c: "plat_insns_per_tick", lem: "plat_insns_per_tick"} : unit -> int -// assumes a single hart, since this typically is a vector of per-hart registers. -register mtimecmp : bits(64) // memory-mapped internal clint register. +// Each hart has a memory-mapped mtimecmp register. Typically these are +// exposed as an array in CLINT. The CLINT implementation here is currently +// hard-coded to use the mtimecmp for hart 0. +register mtimecmp : bits(64) + +// Unlike mtimecmp, stimecmp is a real CSR; not memory mapped. +register stimecmp : bits(64) /* CLINT memory-mapped IO */ @@ -211,8 +216,17 @@ function clint_dispatch() -> unit = { if mtimecmp <=_u mtime then { if get_config_print_platform() then print_platform(" clint timer pending at mtime " ^ BitStr(mtime)); - mip[MTI] = 0b1 - } + mip[MTI] = 0b1; + }; + /* Sstc - supervisor timer register */ + if extensionEnabled(Ext_Sstc) then { + mip[STI] = 0b0; + if stimecmp <=_u mtime then { + if get_config_print_platform() + then print_platform(" supervisor timer pending at mtime " ^ BitStr(mtime)); + mip[STI] = 0b1; + } + }; } /* The rreg effect is due to checking mtime. */ @@ -461,13 +475,4 @@ function handle_illegal() -> unit = { } /* Platform-specific wait-for-interrupt */ - -function platform_wfi() -> unit = { - /* speed execution by getting the timer to fire at the next instruction, - * since we currently don't have any other devices raising interrupts. - */ - if mtime <_u mtimecmp then { - mtime = mtimecmp; - mcycle = mtimecmp; - } -} +function platform_wfi() -> unit = () diff --git a/model/riscv_sstc.sail b/model/riscv_sstc.sail new file mode 100644 index 000000000..3e050ce91 --- /dev/null +++ b/model/riscv_sstc.sail @@ -0,0 +1,20 @@ +/*=======================================================================================*/ +/* This Sail RISC-V architecture model, comprising all files and */ +/* directories except where otherwise noted is subject the BSD */ +/* two-clause license in the LICENSE file. */ +/* */ +/* SPDX-License-Identifier: BSD-2-Clause */ +/*=======================================================================================*/ + +/* Sstc - supervisor time compare */ +mapping clause csr_name_map = 0x14D <-> "stimecmp" +mapping clause csr_name_map = 0x15D <-> "stimecmph" + +function clause is_CSR_defined(0x14D) = extensionEnabled(Ext_S) & extensionEnabled(Ext_Sstc) +function clause is_CSR_defined(0x15D) = extensionEnabled(Ext_S) & extensionEnabled(Ext_Sstc) & xlen == 32 + +function clause read_CSR(0x14D) = stimecmp[xlen - 1 .. 0] +function clause read_CSR(0x15D if xlen == 32) = stimecmp[63 .. 32] + +function clause write_CSR(0x14D, value) = { stimecmp[(xlen - 1) .. 0] = value; stimecmp[xlen - 1 ..0] } +function clause write_CSR((0x15D, value) if xlen == 32) = { stimecmp[63 ..32] = value; stimecmp[63 .. 32] } diff --git a/model/riscv_sys_control.sail b/model/riscv_sys_control.sail index 7890e4dbf..efa20fd18 100644 --- a/model/riscv_sys_control.sail +++ b/model/riscv_sys_control.sail @@ -32,14 +32,21 @@ function feature_enabled_for_priv(p : Privilege, machine_enable_bit : bit, super // Return true if the counter is enabled OR the CSR is not a counter. function check_Counteren(csr : csreg, p : Privilege) -> bool = { // Check if it is not a counter. - if csr <_u 0xC00 | 0xC1F <_u csr - then return true; + if csr <_u 0xC00 | 0xC1F <_u csr then return true; // Check the relevant bit in m/scounteren. let index = unsigned(csr[4 .. 0]); feature_enabled_for_priv(p, mcounteren.bits[index], scounteren.bits[index]) } +// Return true if the stimecmp[h] CSR is accessible OR the CSR is not stimecmp[h]. +function check_Stimecmp(csr : csreg, p : Privilege) -> bool = { + // Check if it is not stimecmp. + if csr != 0x14D & csr != 0x15D then return true; + + p == Machine | (p == Supervisor & mcounteren[TM] == 0b1 & menvcfg[STCE] == 0b1) +} + /* Seed may only be accessed if we are doing a write, and access has been * allowed in the current priv mode */ @@ -61,8 +68,12 @@ function check_seed_CSR (csr : csreg, p : Privilege, isWrite : bool) -> bool = { function check_CSR(csr : csreg, p : Privilege, isWrite : bool) -> bool = is_CSR_defined(csr) & check_CSR_access(csrAccess(csr), csrPriv(csr), p, isWrite) + // TODO: If we add `p` back to is_CSR_defined() we could move these three + // check_ functions back there. We should also rename is_CSR_defined() + // to is_CSR_accessible() or similar. & check_TVM_SATP(csr, p) & check_Counteren(csr, p) + & check_Stimecmp(csr, p) & check_seed_CSR(csr, p, isWrite) /* Reservation handling for LR/SC. diff --git a/model/riscv_sys_regs.sail b/model/riscv_sys_regs.sail index eec299b80..2e4a633b7 100644 --- a/model/riscv_sys_regs.sail +++ b/model/riscv_sys_regs.sail @@ -116,6 +116,13 @@ val sys_enable_bext = pure "sys_enable_bext" : unit -> bool val sys_enable_zicbom = pure "sys_enable_zicbom" : unit -> bool val sys_enable_zicboz = pure "sys_enable_zicboz" : unit -> bool +// Is the Sstc stimecmp extension supported. +val sys_enable_sstc = pure "sys_enable_sstc" : unit -> bool + +// Supervisor timecmp +enum clause extension = Ext_Sstc +function clause extensionEnabled(Ext_Sstc) = sys_enable_sstc() + /* This function allows an extension to veto a write to Misa if it would violate an alignment restriction on unsetting C. If it returns true the write will have no effect. */ @@ -310,6 +317,87 @@ function in32BitMode() -> bool = { cur_Architecture() == RV32 } +// envcfg Resisters +bitfield MEnvcfg : bits(64) = { + // Supervisor TimeCmp Extension + STCE : 63, + // Page Based Memory Types Extension + PBMTE : 62, + // Reserved WPRI bits. + wpri_1 : 61 .. 8, + // Cache Block Zero instruction Enable + CBZE : 7, + // Cache Block Clean and Flush instruction Enable + CBCFE : 6, + // Cache Block Invalidate instruction Enable + CBIE : 5 .. 4, + // Reserved WPRI bits. + wpri_0 : 3 .. 1, + // Fence of I/O implies Memory + FIOM : 0, +} + +bitfield SEnvcfg : xlenbits = { + // Cache Block Zero instruction Enable + CBZE : 7, + // Cache Block Clean and Flush instruction Enable + CBCFE : 6, + // Cache Block Invalidate instruction Enable + CBIE : 5 .. 4, + // Reserved WPRI bits. + wpri_0 : 3 .. 1, + // Fence of I/O implies Memory + FIOM : 0, +} + +function legalize_menvcfg(o : MEnvcfg, v : bits(64)) -> MEnvcfg = { + let v = Mk_MEnvcfg(v); + [o with + FIOM = if sys_enable_writable_fiom() then v[FIOM] else 0b0, + STCE = if extensionEnabled(Ext_Sstc) then v[STCE] else 0b0, + // Other extensions are not implemented yet so all other fields are read only zero. + ] +} + +function legalize_senvcfg(o : SEnvcfg, v : xlenbits) -> SEnvcfg = { + let v = Mk_SEnvcfg(v); + [o with + FIOM = if sys_enable_writable_fiom() then v[FIOM] else 0b0, + // Other extensions are not implemented yet so all other fields are read only zero. + ]; +} + +register menvcfg : MEnvcfg +register senvcfg : SEnvcfg + +mapping clause csr_name_map = 0x30A <-> "menvcfg" +mapping clause csr_name_map = 0x31A <-> "menvcfgh" +mapping clause csr_name_map = 0x10A <-> "senvcfg" + +function clause is_CSR_defined(0x30A) = extensionEnabled(Ext_U) // menvcfg +function clause is_CSR_defined(0x31A) = extensionEnabled(Ext_U) & (xlen == 32) // menvcfgh +function clause is_CSR_defined(0x10A) = extensionEnabled(Ext_S) // senvcfg + +function clause read_CSR(0x30A) = menvcfg.bits[xlen - 1 .. 0] +function clause read_CSR(0x31A if xlen == 32) = menvcfg.bits[63 .. 32] +function clause read_CSR(0x10A) = senvcfg.bits[xlen - 1 .. 0] + +function clause write_CSR((0x30A, value) if xlen == 32) = { menvcfg = legalize_menvcfg(menvcfg, menvcfg.bits[63 .. 32] @ value); menvcfg.bits[31 .. 0] } +function clause write_CSR((0x30A, value) if xlen == 64) = { menvcfg = legalize_menvcfg(menvcfg, value); menvcfg.bits } +function clause write_CSR((0x31A, value) if xlen == 32) = { menvcfg = legalize_menvcfg(menvcfg, value @ menvcfg.bits[31 .. 0]); menvcfg.bits[63 .. 32] } +function clause write_CSR(0x10A, value) = { senvcfg = legalize_senvcfg(senvcfg, zero_extend(value)); senvcfg.bits[xlen - 1 .. 0] } + +// Return whether or not FIOM is currently active, based on the current +// privilege and the menvcfg/senvcfg settings. This means that I/O fences +// imply memory fence. +function is_fiom_active() -> bool = { + match cur_privilege { + Machine => false, + Supervisor => menvcfg[FIOM] == 0b1, + User => (menvcfg[FIOM] | senvcfg[FIOM]) == 0b1, + } +} + /* interrupt processing state */ bitfield Minterrupts : xlenbits = { @@ -329,8 +417,11 @@ function legalize_mip(o : Minterrupts, v : xlenbits) -> Minterrupts = { let v = Mk_Minterrupts(v); [o with SEI = if extensionEnabled(Ext_S) then v[SEI] else 0b0, - STI = if extensionEnabled(Ext_S) then v[STI] else 0b0, SSI = if extensionEnabled(Ext_S) then v[SSI] else 0b0, + STI = if extensionEnabled(Ext_S) then ( + // STI is read only if Sstc is enabled and STCE is set (it is equal to stimecmp <= mtime). + if extensionEnabled(Ext_Sstc) & menvcfg[STCE] == 0b1 then o[STI] else v[STI] + ) else 0b0, ] } @@ -835,84 +926,6 @@ val get_16_random_bits = impure { lem: "plat_get_16_random_bits" } : unit -> bits(16) -// envcfg Resisters -bitfield MEnvcfg : bits(64) = { - // Supervisor TimeCmp Extension - STCE : 63, - // Page Based Memory Types Extension - PBMTE : 62, - // Reserved WPRI bits. - wpri_1 : 61 .. 8, - // Cache Block Zero instruction Enable - CBZE : 7, - // Cache Block Clean and Flush instruction Enable - CBCFE : 6, - // Cache Block Invalidate instruction Enable - CBIE : 5 .. 4, - // Reserved WPRI bits. - wpri_0 : 3 .. 1, - // Fence of I/O implies Memory - FIOM : 0, -} - -bitfield SEnvcfg : xlenbits = { - // Cache Block Zero instruction Enable - CBZE : 7, - // Cache Block Clean and Flush instruction Enable - CBCFE : 6, - // Cache Block Invalidate instruction Enable - CBIE : 5 .. 4, - // Reserved WPRI bits. - wpri_0 : 3 .. 1, - // Fence of I/O implies Memory - FIOM : 0, -} - -function legalize_menvcfg(o : MEnvcfg, v : bits(64)) -> MEnvcfg = { - let v = Mk_MEnvcfg(v); - let o = [o with FIOM = if sys_enable_writable_fiom() then v[FIOM] else 0b0]; - // Other extensions are not implemented yet so all other fields are read only zero. - o -} - -function legalize_senvcfg(o : SEnvcfg, v : xlenbits) -> SEnvcfg = { - let v = Mk_SEnvcfg(v); - let o = [o with FIOM = if sys_enable_writable_fiom() then v[FIOM] else 0b0]; - // Other extensions are not implemented yet so all other fields are read only zero. - o -} - -register menvcfg : MEnvcfg -register senvcfg : SEnvcfg - -mapping clause csr_name_map = 0x30A <-> "menvcfg" -mapping clause csr_name_map = 0x31A <-> "menvcfgh" -mapping clause csr_name_map = 0x10A <-> "senvcfg" - -function clause is_CSR_defined(0x30A) = extensionEnabled(Ext_U) // menvcfg -function clause is_CSR_defined(0x31A) = extensionEnabled(Ext_U) & (xlen == 32) // menvcfgh -function clause is_CSR_defined(0x10A) = extensionEnabled(Ext_S) // senvcfg - -function clause read_CSR(0x30A) = menvcfg.bits[xlen - 1 .. 0] -function clause read_CSR(0x31A if xlen == 32) = menvcfg.bits[63 .. 32] -function clause read_CSR(0x10A) = senvcfg.bits[xlen - 1 .. 0] - -function clause write_CSR((0x30A, value) if xlen == 32) = { menvcfg = legalize_menvcfg(menvcfg, menvcfg.bits[63 .. 32] @ value); menvcfg.bits[31 .. 0] } -function clause write_CSR((0x30A, value) if xlen == 64) = { menvcfg = legalize_menvcfg(menvcfg, value); menvcfg.bits } -function clause write_CSR((0x31A, value) if xlen == 32) = { menvcfg = legalize_menvcfg(menvcfg, value @ menvcfg.bits[31 .. 0]); menvcfg.bits[63 .. 32] } -function clause write_CSR(0x10A, value) = { senvcfg = legalize_senvcfg(senvcfg, zero_extend(value)); senvcfg.bits[xlen - 1 .. 0] } - -// Return whether or not FIOM is currently active, based on the current -// privilege and the menvcfg/senvcfg settings. This means that I/O fences -// imply memory fence. -function is_fiom_active() -> bool = { - match cur_privilege { - Machine => false, - Supervisor => menvcfg[FIOM] == 0b1, - User => (menvcfg[FIOM] | senvcfg[FIOM]) == 0b1, - } -} - /* vector csrs */ register vstart : bits(16) /* use the largest possible length of vstart */ register vl : xlenbits