Livt packages
Eccelerators.RiscV
Version 0.3.0
Composable RV32I and Zmmul processors, machine-mode support, and reusable FPGA system components written in Livt.
Published files (120)
src/Machine/Rv32MachineCsrs.lvt
namespace Eccelerators.RiscV.Machine
/**
* Machine-only CSR and synchronous trap state, exclusively owned in the engine's clock/reset domain.
* Trap entry/return share the single writer; optional interrupt delivery samples
* same-domain levels. mcycle counts every non-clear edge even
* while paused/stopped, unless inhibited; minstret counts the retire input. Explicit
* writes suppress the targeted 64-bit counter's automatic increment for that edge.
* Counter half writes retain the other half. Software clear and hardware reset zero
* storage; MPP and ISA/identification constants are reconstructed on reads.
*/
component Rv32MachineCsrs<HART_ID: uint = 0, TIMER_INTERRUPTS: bool = false, EXTERNAL_INTERRUPTS: bool = false> : IRv32CsrPort
{
// Concrete Zicsr coverage for composition checks.
public const CAPABILITIES: uint = Rv32CsrOperations.CAPABILITY
public const TRAP_CAPABILITIES: uint = Rv32TrapOperations.CAPABILITY
// Platform-owned pending level; tie low when absent. CPU clear never resets the timer.
public timerPending: in logic
public externalPending: in logic
// Supported interrupt enable bits; disabled elaborations remove this storage.
interruptEnable: uint
// Machine state stores only writable status bits and aligned address payloads.
status: uint
vector: uint
scratch: uint
epc: uint
cause: uint
value: uint
// Two independent 64-bit counters, represented by 32-bit words.
cycleLow: uint
cycleHigh: uint
retireLow: uint
retireHigh: uint
inhibit: uint
// Pure query view and selected read/modify/write value.
oldValue: uint
newValue: uint
known: bool
writeAccepted: bool
/**
* Decodes legal addresses without side effects, including fixed-zero HPM ranges.
*/
process Query[]()
{
trapVector = vector as logic[32]
returnPc = epc as logic[32]
interruptEligible = 0b0
interruptCause = Rv32TrapOperations.TIMER_CAUSE as logic[32]
if ((status & Rv32TrapOperations.STATUS_MIE) != 0) {
if (EXTERNAL_INTERRUPTS && externalPending == 0b1
&& (interruptEnable & Rv32TrapOperations.EXTERNAL_MASK) != 0) {
interruptEligible = 0b1
interruptCause = Rv32TrapOperations.EXTERNAL_CAUSE as logic[32]
} elif (TIMER_INTERRUPTS && timerPending == 0b1
&& (interruptEnable & Rv32TrapOperations.TIMER_MASK) != 0) {
interruptEligible = 0b1
}
}
var index = address as uint
oldValue = 0
known = true
match (index)
{
Rv32MachineCsrMap.MSTATUS =>
{
oldValue = status | Rv32MachineCsrMap.MPP_MACHINE
}
Rv32MachineCsrMap.MISA =>
{
oldValue = Rv32MachineCsrMap.ISA
}
Rv32MachineCsrMap.MTVEC =>
{
oldValue = vector
}
Rv32MachineCsrMap.MSCRATCH =>
{
oldValue = scratch
}
Rv32MachineCsrMap.MEPC =>
{
oldValue = epc
}
Rv32MachineCsrMap.MCAUSE =>
{
oldValue = cause
}
Rv32MachineCsrMap.MTVAL =>
{
oldValue = value
}
Rv32MachineCsrMap.MCOUNTINHIBIT =>
{
oldValue = inhibit
}
Rv32MachineCsrMap.MCYCLE =>
{
oldValue = cycleLow
}
Rv32MachineCsrMap.MCYCLEH =>
{
oldValue = cycleHigh
}
Rv32MachineCsrMap.MINSTRET =>
{
oldValue = retireLow
}
Rv32MachineCsrMap.MINSTRETH =>
{
oldValue = retireHigh
}
Rv32MachineCsrMap.MHARTID =>
{
oldValue = HART_ID
}
Rv32MachineCsrMap.MIE =>
{
if (TIMER_INTERRUPTS || EXTERNAL_INTERRUPTS) {
oldValue = interruptEnable
}
}
Rv32MachineCsrMap.MIP =>
{
if (TIMER_INTERRUPTS && timerPending == 0b1) {
oldValue = Rv32TrapOperations.TIMER_MASK
}
if (EXTERNAL_INTERRUPTS && externalPending == 0b1) {
oldValue = oldValue | Rv32TrapOperations.EXTERNAL_MASK
}
}
Rv32MachineCsrMap.MSTATUSH,
Rv32MachineCsrMap.MCOUNTEREN, Rv32MachineCsrMap.MVENDORID,
Rv32MachineCsrMap.MARCHID, Rv32MachineCsrMap.MIMPID, Rv32MachineCsrMap.MCONFIGPTR =>
{
oldValue = 0
}
_ =>
{
known = (index >= Rv32MachineCsrMap.HPM_FIRST && index <= Rv32MachineCsrMap.HPM_LAST)
|| (index >= Rv32MachineCsrMap.HPM_HIGH_FIRST && index <= Rv32MachineCsrMap.HPM_HIGH_LAST)
|| (index >= Rv32MachineCsrMap.EVENT_FIRST && index <= Rv32MachineCsrMap.EVENT_LAST)
}
}
illegal = 0b0
if (!known || operation == 0
|| (writeEnable == 0b1 && (index & Rv32MachineCsrMap.READ_ONLY_ADDRESS) == Rv32MachineCsrMap.READ_ONLY_ADDRESS)) {
illegal = 0b1
}
readData = 0
if (readEnable == 0b1) {
readData = oldValue as logic[32]
}
newValue = operand as uint
if (operation as uint == Rv32CsrOperations.SET) {
newValue = oldValue | (operand as uint)
} elif (operation as uint == Rv32CsrOperations.CLEAR) {
newValue = oldValue & ~(operand as uint)
}
writeAccepted = commit == 0b1 && writeEnable == 0b1 && illegal == 0b0
&& enterTrap == 0b0 && returnTrap == 0b0
}
/**
* Applies clear, trap/return, counter increments and explicit writes with one state owner.
*/
process Advance()
{
state {
var index = address as uint
if (clear == 0b1) {
status = 0
interruptEnable = 0
vector = 0
scratch = 0
epc = 0
cause = 0
value = 0
cycleLow = 0
cycleHigh = 0
retireLow = 0
retireHigh = 0
inhibit = 0
} else {
if ((inhibit & Rv32MachineCsrMap.INHIBIT_CYCLE) == 0
&& !(writeAccepted && (index == Rv32MachineCsrMap.MCYCLE || index == Rv32MachineCsrMap.MCYCLEH))) {
if (cycleLow == 0xffffffff) {
cycleHigh++
}
cycleLow++
}
if (retire == 0b1 && enterTrap == 0b0 && (inhibit & Rv32MachineCsrMap.INHIBIT_RETIRE) == 0
&& !(writeAccepted && (index == Rv32MachineCsrMap.MINSTRET || index == Rv32MachineCsrMap.MINSTRETH))) {
if (retireLow == 0xffffffff) {
retireHigh++
}
retireLow++
}
if (enterTrap == 0b1) {
epc = (entryPc as uint) & Rv32MachineCsrMap.WORD_ALIGNED
cause = entryCause as uint
value = entryValue as uint
status = (status & Rv32TrapOperations.STATUS_MIE) << Rv32TrapOperations.STATUS_STACK_SHIFT
} elif (returnTrap == 0b1) {
status = ((status & Rv32TrapOperations.STATUS_MPIE) >> Rv32TrapOperations.STATUS_STACK_SHIFT)
| Rv32TrapOperations.STATUS_MPIE
} elif (writeAccepted) {
match (index)
{
Rv32MachineCsrMap.MSTATUS =>
{
status = newValue & Rv32MachineCsrMap.STATUS_WRITABLE
}
Rv32MachineCsrMap.MIE =>
{
var supported: uint = 0
if (TIMER_INTERRUPTS) {
supported = Rv32TrapOperations.TIMER_MASK
}
if (EXTERNAL_INTERRUPTS) {
supported = supported | Rv32TrapOperations.EXTERNAL_MASK
}
interruptEnable = newValue & supported
}
Rv32MachineCsrMap.MTVEC =>
{
vector = newValue & Rv32MachineCsrMap.WORD_ALIGNED
}
Rv32MachineCsrMap.MSCRATCH =>
{
scratch = newValue
}
Rv32MachineCsrMap.MEPC =>
{
epc = newValue & Rv32MachineCsrMap.WORD_ALIGNED
}
Rv32MachineCsrMap.MCAUSE =>
{
cause = newValue
}
Rv32MachineCsrMap.MTVAL =>
{
value = newValue
}
Rv32MachineCsrMap.MCOUNTINHIBIT =>
{
inhibit = newValue & Rv32MachineCsrMap.COUNTER_MASK
}
Rv32MachineCsrMap.MCYCLE =>
{
cycleLow = newValue
}
Rv32MachineCsrMap.MCYCLEH =>
{
cycleHigh = newValue
}
Rv32MachineCsrMap.MINSTRET =>
{
retireLow = newValue
}
Rv32MachineCsrMap.MINSTRETH =>
{
retireHigh = newValue
}
_ => {
// Legal fixed fields retain their documented values.
}
}
}
}
}
}
}