Skip to content

Commit

Permalink
Add Sstc extension
Browse files Browse the repository at this point in the history
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: Ved Shanbhogue <[email protected]>
  • Loading branch information
Timmmm and ved-rivos authored Dec 4, 2024
1 parent a365bd2 commit f34a6cc
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 95 deletions.
5 changes: 5 additions & 0 deletions c_emulator/riscv_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions c_emulator/riscv_platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions c_emulator/riscv_platform_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions c_emulator/riscv_platform_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions c_emulator/riscv_sim.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ enum {
OPT_ENABLE_ZCB,
OPT_ENABLE_ZICBOM,
OPT_ENABLE_ZICBOZ,
OPT_ENABLE_SSTC,
OPT_CACHE_BLOCK_SIZE,
};

Expand Down Expand Up @@ -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));

Expand Down
33 changes: 19 additions & 14 deletions model/riscv_platform.sail
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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 = ()
20 changes: 20 additions & 0 deletions model/riscv_sstc.sail
Original file line number Diff line number Diff line change
@@ -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] }
15 changes: 13 additions & 2 deletions model/riscv_sys_control.sail
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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.
Expand Down
171 changes: 92 additions & 79 deletions model/riscv_sys_regs.sail
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,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. */
Expand Down Expand Up @@ -308,6 +315,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 = {
Expand All @@ -327,8 +415,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,
]
}

Expand Down Expand Up @@ -805,84 +896,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
Expand Down

0 comments on commit f34a6cc

Please sign in to comment.