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)
- IRv32ControlPort.lvt
- IRv32DecodePort.lvt
- IRv32FetchPort.lvt
- IRv32LoadStorePort.lvt
- IRv32MultiplyPort.lvt
- IRv32RegisterPort.lvt
- Rv32DspMultiplier.lvt
- Rv32iConstants.lvt
- Rv32iDecode.lvt
- Rv32iDecoder.lvt
- Rv32iIntegerUnit.lvt
- Rv32iOperations.lvt
- Rv32iSemantics.lvt
- Rv32IterativeMultiplier.lvt
- Rv32iZmmulDecoder.lvt
- Rv32MultiplyOperations.lvt
- Rv32NoMultiplier.lvt
- Rv32SequentialEngine.lvt
src/Sequential/Rv32SequentialEngine.lvt
namespace Eccelerators.RiscV.Sequential
using Eccelerators.RiscV.Contracts
using Eccelerators.RiscV.Machine
/**
* In-order RV32I engine with one instruction and load/store transaction outstanding.
* Borrows all units exclusively in one clock/reset context; the composition root
* owns their lifetime. RAM reads have one-edge latency. Decoder/integer ports are
* combinational. One clocked controller owns PC, faults and architectural commit.
* Diagnostic policy stops on exceptions. MACHINE_TRAPS enters direct mtvec and
* enables MRET through the shared CSR owner; interrupt delivery samples the CSR
* provider at a drained instruction boundary before the next fetch (a NoCsrs
* provider ties interruptEligible low, making delivery inert).
*/
component Rv32SequentialEngine<RESET_PC: uint = 0, MACHINE_TRAPS: bool = false> : IRv32ControlPort
{
static assert (RESET_PC & 3) == 0, "Reset PC must be word aligned"
// Semantic operation families owned by the controller, separate from integer execution.
public const CAPABILITIES: uint = Rv32iOperations.MEMORY_CAPABILITY | Rv32iOperations.CONTROL_CAPABILITY
// Controller phases. Boot clears the bank; stopped waits for an explicit restart.
const BOOT: uint = 0
const IDLE: uint = 1
const FETCH: uint = 2
const DECODE: uint = 3
const READ_FIRST: uint = 4
const CAPTURE_FIRST: uint = 5
const CAPTURE_SECOND: uint = 6
const EXECUTE: uint = 7
const MEMORY: uint = 8
const COMMIT: uint = 9
const MULTIPLY_WAIT: uint = 11
const STOPPED: uint = 10
const EXCEPTION: uint = 12
// Exclusive borrowed implementation instances, with substitutable decode/multiply ports.
registers: IRv32RegisterPort
fetch: IRv32FetchPort
loadStore: IRv32LoadStorePort
decoder: IRv32DecodePort
integer: Rv32iIntegerUnit
multiplier: IRv32MultiplyPort
csrs: IRv32CsrPort
// Owned sequencing, instruction identity, operands and pending completion.
phase: uint
clearIndex: uint
programCounter: uint
instruction: uint
leftValue: uint
rightValue: uint
resultValue: uint
nextPc: uint
memoryAddress: uint
activeStep: logic
stepCompleted: logic
restartCompleted: logic
stopCause: logic[32]
stopValue: logic[32]
trapPulse: logic
retirementPulse: logic
commitPc: logic[32]
commitRd: logic[5]
commitValue: logic[32]
// Combinational decode views, never additional scheduled calls or retained copies.
control: uint
kind: uint
funct3: uint
commitData: uint
/**
* Binds caller-owned resources. Hardware reset starts bank initialization;
* wait for idle before requesting a single step or provisioning shared resources.
*/
new(registers: flip IRv32RegisterPort, fetch: flip IRv32FetchPort, loadStore: flip IRv32LoadStorePort,
decoder: flip IRv32DecodePort, integer: Rv32iIntegerUnit, multiplier: flip IRv32MultiplyPort, csrs: flip IRv32CsrPort)
{
this.registers = registers
this.fetch = fetch
this.loadStore = loadStore
this.decoder = decoder
this.integer = integer
this.multiplier = multiplier
this.csrs = csrs
}
/**
* Drives all borrowed ports from the current phase. Memory request remains stable
* until completion; there are no speculative or repeated MMIO accesses.
*/
process Drive[]()
{
stepAck = stepCompleted
restartAck = restartCompleted
faultCause = stopCause
faultValue = stopValue
if (MACHINE_TRAPS) {
faultCause = 0
faultValue = 0
}
trapped = trapPulse
trapPc = commitPc
trapCause = stopCause
trapValue = stopValue
retired = retirementPulse
retiredPc = commitPc
retiredRd = commitRd
retiredValue = commitValue
decoder.instruction = instruction as logic[32]
control = decoder.control as uint
kind = RvDecoded.Operation(control) >> Rv32iOperations.KIND_SHIFT
funct3 = (instruction >> 12) & 7
pc = programCounter as logic[32]
retiredInstruction = instruction as logic[32]
idle = 0b0
halted = 0b0
if (phase == IDLE || phase == STOPPED) {
idle = 0b1
}
if (phase == STOPPED) {
halted = 0b1
}
fetch.request = 0b0
fetch.address = programCounter as logic[32]
if (phase == FETCH) {
fetch.request = 0b1
}
// Copy values before pure calls so state registers keep direct publication.
var addressValue = memoryAddress
var width = funct3
var csrInstruction = instruction
var csrOperand = leftValue
var csrDestination = RvDecoded.Rd(control)
loadStore.request = 0b0
loadStore.address = (memoryAddress & 0xfffffffc) as logic[32]
loadStore.write = 0b0
loadStore.byteEnable = Rv32iSemantics.StoreMask(addressValue, width) as logic[4]
loadStore.writeData = (rightValue << (((memoryAddress & 3) * 8) as int)) as logic[32]
if (phase == MEMORY) {
loadStore.request = 0b1
}
if (kind == Rv32iOperations.STORE) {
loadStore.write = 0b1
}
registers.enable = 0b0
registers.write = 0b0
registers.index = RvDecoded.Rs1(control) as logic[5]
commitData = resultValue
if (kind == Rv32CsrOperations.KIND) {
commitData = csrs.readData as uint
}
registers.writeData = commitData as logic[32]
if (phase == BOOT) {
registers.enable = 0b1
registers.write = 0b1
registers.index = clearIndex as logic[5]
registers.writeData = 0
} elif (phase == READ_FIRST && RvDecoded.HasSource1(control)) {
registers.enable = 0b1
} elif (phase == CAPTURE_FIRST && RvDecoded.HasSource2(control)) {
registers.enable = 0b1
registers.index = RvDecoded.Rs2(control) as logic[5]
} elif (phase == COMMIT && RvDecoded.HasDestinationWrite(control)) {
registers.enable = 0b1
registers.write = 0b1
registers.index = RvDecoded.Rd(control) as logic[5]
}
multiplier.request = 0b0
multiplier.operation = funct3 as logic[2]
multiplier.left = leftValue as logic[32]
multiplier.right = rightValue as logic[32]
if (kind == Rv32MultiplyOperations.KIND && (phase == EXECUTE || phase == MULTIPLY_WAIT)) {
multiplier.request = 0b1
}
csrs.enterTrap = 0b0
csrs.returnTrap = 0b0
csrs.entryPc = programCounter as logic[32]
csrs.entryCause = stopCause
csrs.entryValue = stopValue
if (MACHINE_TRAPS) {
if (phase == EXCEPTION) {
csrs.enterTrap = 0b1
}
if (phase == COMMIT && kind == Rv32TrapOperations.RETURN_KIND) {
csrs.returnTrap = 0b1
}
}
csrs.address = (instruction >> 20) as logic[12]
csrs.operation = (funct3 & 3) as logic[2]
csrs.operand = leftValue as logic[32]
csrs.readEnable = 0b0
csrs.writeEnable = 0b0
csrs.commit = 0b0
csrs.retire = 0b0
csrs.clear = 0b0
if (phase == BOOT) {
csrs.clear = 0b1
}
if (phase == COMMIT) {
csrs.retire = 0b1
}
if (kind == Rv32CsrOperations.KIND) {
csrs.operand = Rv32ZicsrAccess.SelectOperand(width, csrInstruction, csrOperand) as logic[32]
csrs.readEnable = Rv32ZicsrAccess.RequiresRead(width, csrDestination) as logic
csrs.writeEnable = Rv32ZicsrAccess.RequiresWrite(width, csrInstruction) as logic
if (phase == COMMIT) {
csrs.commit = 0b1
}
}
integer.operation = (RvDecoded.Operation(control) & Rv32iOperations.ALU_MASK) as logic[4]
integer.left = leftValue as logic[32]
integer.right = decoder.immediate
if (kind == Rv32iOperations.REGISTER) {
integer.right = rightValue as logic[32]
}
if (kind == Rv32iOperations.LUI) {
integer.left = 0
} elif (kind == Rv32iOperations.AUIPC || kind == Rv32iOperations.JAL || kind == Rv32iOperations.BRANCH) {
integer.left = programCounter as logic[32]
}
}
/**
* Advances one controller edge. This process contains no scheduled calls.
* Keep state updates in this clocked process: scheduled helpers would add
* call phases and change the sampling and publication edges.
*/
process Advance()
{
state {
// Locals preserve the sampled values without passing state fields by reference.
var operand1 = leftValue
var operand2 = rightValue
var addressValue = memoryAddress
trapPulse = 0b0
retirementPulse = 0b0
match (phase)
{
BOOT =>
{
programCounter = RESET_PC
stopCause = 0
stopValue = 0
if (clearIndex == 31) {
phase = IDLE
restartCompleted = restartRequest
stepCompleted = stepRequest
} else {
clearIndex++
}
}
IDLE, STOPPED =>
{
if (restartRequest != restartCompleted) {
clearIndex = 0
phase = BOOT
} elif (phase == IDLE && (run == 0b1 || stepRequest != stepCompleted)) {
activeStep = stepRequest
if (csrs.interruptEligible == 0b1) {
stopCause = csrs.interruptCause
stopValue = 0
phase = EXCEPTION
} else {
phase = FETCH
}
}
}
FETCH =>
{
if (fetch.ready == 0b1) {
if (fetch.error == 0b1) {
stopCause = Rv32iOperations.INSTRUCTION_ACCESS as logic[32]
stopValue = programCounter as logic[32]
phase = EXCEPTION
} else {
instruction = fetch.instruction as uint
phase = DECODE
}
}
}
DECODE =>
{
if (!RvDecoded.IsLegal(control) || (!MACHINE_TRAPS && kind == Rv32TrapOperations.RETURN_KIND)) {
stopCause = Rv32iOperations.ILLEGAL_INSTRUCTION as logic[32]
stopValue = instruction as logic[32]
phase = EXCEPTION
} else {
phase = READ_FIRST
}
}
READ_FIRST =>
{
phase = CAPTURE_FIRST
}
CAPTURE_FIRST =>
{
leftValue = 0
if (RvDecoded.HasSource1(control)) {
leftValue = registers.readData as uint
}
phase = CAPTURE_SECOND
}
CAPTURE_SECOND =>
{
rightValue = 0
if (RvDecoded.HasSource2(control)) {
rightValue = registers.readData as uint
}
phase = EXECUTE
}
EXECUTE =>
{
var arithmetic = integer.result as uint
nextPc = programCounter + 4
resultValue = arithmetic
phase = COMMIT
match (kind)
{
Rv32TrapOperations.RETURN_KIND =>
{
if (MACHINE_TRAPS) {
nextPc = csrs.returnPc as uint
}
}
Rv32CsrOperations.KIND =>
{
if (csrs.illegal == 0b1) {
stopCause = Rv32iOperations.ILLEGAL_INSTRUCTION as logic[32]
stopValue = instruction as logic[32]
phase = EXCEPTION
}
}
Rv32MultiplyOperations.KIND =>
{
phase = MULTIPLY_WAIT
}
Rv32iOperations.JAL, Rv32iOperations.JALR =>
{
var target = arithmetic
if (kind == Rv32iOperations.JALR) {
target = target & 0xfffffffe
}
if ((target & 3) != 0) {
stopCause = Rv32iOperations.INSTRUCTION_MISALIGNED as logic[32]
stopValue = target as logic[32]
phase = EXCEPTION
} else {
nextPc = target
resultValue = programCounter + 4
}
}
Rv32iOperations.BRANCH =>
{
if (Rv32iSemantics.Branch(funct3, operand1, operand2)) {
if ((arithmetic & 3) != 0) {
stopCause = Rv32iOperations.INSTRUCTION_MISALIGNED as logic[32]
stopValue = arithmetic as logic[32]
phase = EXCEPTION
} else {
nextPc = arithmetic
}
}
}
Rv32iOperations.LOAD, Rv32iOperations.STORE =>
{
memoryAddress = arithmetic
if (Rv32iSemantics.Misaligned(arithmetic, funct3)) {
if (kind == Rv32iOperations.LOAD) {
stopCause = Rv32iOperations.LOAD_MISALIGNED as logic[32]
stopValue = arithmetic as logic[32]
phase = EXCEPTION
} else {
stopCause = Rv32iOperations.STORE_MISALIGNED as logic[32]
stopValue = arithmetic as logic[32]
phase = EXCEPTION
}
} else {
phase = MEMORY
}
}
Rv32iOperations.ECALL =>
{
stopCause = Rv32iOperations.ENVIRONMENT_CALL as logic[32]
stopValue = 0 as logic[32]
phase = EXCEPTION
}
Rv32iOperations.EBREAK =>
{
stopCause = Rv32iOperations.BREAKPOINT as logic[32]
stopValue = programCounter as logic[32]
phase = EXCEPTION
}
_ => {
// Other legal instructions complete through the common commit edge.
}
}
}
MULTIPLY_WAIT =>
{
if (multiplier.ready == 0b1) {
resultValue = multiplier.result as uint
phase = COMMIT
}
}
MEMORY =>
{
if (loadStore.ready == 0b1) {
if (loadStore.error == 0b1) {
if (kind == Rv32iOperations.LOAD) {
stopCause = Rv32iOperations.LOAD_ACCESS as logic[32]
stopValue = memoryAddress as logic[32]
phase = EXCEPTION
} else {
stopCause = Rv32iOperations.STORE_ACCESS as logic[32]
stopValue = memoryAddress as logic[32]
phase = EXCEPTION
}
} else {
resultValue = Rv32iSemantics.Load(loadStore.readData as uint, addressValue, funct3)
phase = COMMIT
}
}
}
EXCEPTION =>
{
stepCompleted = activeStep
if (MACHINE_TRAPS) {
commitPc = programCounter as logic[32]
programCounter = csrs.trapVector as uint
trapPulse = 0b1
phase = IDLE
} else {
phase = STOPPED
}
}
COMMIT =>
{
retirementPulse = 0b1
commitPc = programCounter as logic[32]
commitRd = 0
commitValue = 0
if (RvDecoded.HasDestinationWrite(control) && RvDecoded.Rd(control) != 0) {
commitRd = RvDecoded.Rd(control) as logic[5]
commitValue = commitData as logic[32]
}
programCounter = nextPc
stepCompleted = activeStep
phase = IDLE
}
_ =>
{
stopCause = Rv32iOperations.ILLEGAL_INSTRUCTION as logic[32]
stopValue = instruction as logic[32]
phase = EXCEPTION
}
}
}
}
}