Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sstc extension #570

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but wouldn't this be cleaner as a scattered function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one shouldn't be a scattered function because check_CSR_access() is always the same, but the other functions should be moved into is_CSR_defined(), something like this:

function check_CSR(csr : csreg, p : Privilege, isWrite : bool) -> bool =
   check_CSR_access(csrAccess(csr), csrPriv(csr), p, isWrite) & is_CSR_defined(csr, p, isWrite)

Or we could potentially leave isWrite just for the seed CSR. It's the only one that needs it and unfortunately it is actually specified that way:

Attempts to access the seed CSR using a read-only CSR-access instruction (CSRRS/CSRRC with rs1=x0 or CSRRSI/CSRRCI with uimm=0) raise an illegal instruction exception

I'll make a PR to clean this up after this one.

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 @@ -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. */
Expand Down Expand Up @@ -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 = {
Expand All @@ -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,
]
}

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