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)
- fixtures
- DelayedWordDevice.lvt
- PipelineFetchMemory.lvt
- PipelineWordDevice.lvt
- README.md
- Rv32BusCoreTest.lvt
- Rv32BusProtocolTest.lvt
- Rv32CsrEngineTest.lvt
- Rv32CsrRetirement.lvt
- Rv32DspControlTest.lvt
- Rv32DspEngineTest.lvt
- Rv32DspMultiplierTest.lvt
- Rv32DualReadDistributedTest.lvt
- Rv32DualReadRegisterBankTest.lvt
- Rv32ExternalInterruptTest.lvt
- Rv32FetchBusTest.lvt
- Rv32iConstantsTest.lvt
- Rv32iDecodeTest.lvt
- Rv32InterruptCsrTest.lvt
- Rv32iPackageApiTest.lvt
- Rv32iSemanticsTest.lvt
- Rv32IterativeMultiplierTest.lvt
- Rv32LoadStoreBusTest.lvt
- Rv32MachineCsrsTest.lvt
- Rv32MachineDecoderTest.lvt
- Rv32MultiplyRetirement.lvt
- Rv32MultiplyVectors.lvt
- Rv32PipelineControlTest.lvt
- Rv32PipelineCsrEngineTest.lvt
- Rv32PipelineDspEngineTest.lvt
- Rv32PipelineEngineTest.lvt
- Rv32PipelineExternalInterruptTest.lvt
- Rv32PipelineTimerInterruptTest.lvt
- Rv32PipelineTrapEngineTest.lvt
- Rv32PipelineZmmulEngineTest.lvt
- Rv32RegisterBankTest.lvt
- Rv32SequentialControlTest.lvt
- Rv32SequentialEngineTest.lvt
- Rv32TimerInterruptTest.lvt
- Rv32TrapEngineTest.lvt
- Rv32TrapPolicyTest.lvt
- Rv32ZicsrAccessTest.lvt
- Rv32ZicsrDecoderTest.lvt
- Rv32ZmmulControlTest.lvt
- Rv32ZmmulDecodeTest.lvt
- Rv32ZmmulEngineTest.lvt
- RvExternalInterruptsTest.lvt
- RvMachineSystemTest.lvt
- RvMachineTimerTest.lvt
- RvPipelineSystemTest.lvt
- RvSystemTest.lvt
- RvUartDeviceTest.lvt
- RvUartSystemTest.lvt
tests/Rv32PipelineControlTest.lvt
namespace Eccelerators.RiscV.Tests
using Eccelerators.RiscV.Sequential
using Eccelerators.RiscV.Storage
using Eccelerators.RiscV.Presets
/**
* Control and MMIO tests cover delayed completion, pause, stepping and restart drain.
*/
@Test
component Rv32PipelineControlTest
{
// Owned instruction storage, delayable device, core and retirement observations.
fetch: PipelineFetchMemory
loadStore: PipelineWordDevice
core: Rv32iPipelineCore
clearObservation: bool
count: uint
lastValue: uint
/**
* Creates exclusively owned providers in one reset context.
*/
new()
{
fetch = new PipelineFetchMemory()
loadStore = new PipelineWordDevice()
core = new Rv32iPipelineCore(fetch, loadStore)
}
/**
* Observes successful commits independently of stepping acknowledgements.
*/
process Observe()
{
state {
if (clearObservation) {
count = 0
lastValue = 0
} elif (core.retired == 0b1) {
count++
lastValue = core.retiredValue as uint
}
}
}
/**
* Restarts the core and independently clears the test device and observations.
*/
fn Prepare()
{
core.run = 0b0
fetch.hold = 0b0
fetch.fail = 0b0
WaitIdle()
var ticket = ~core.restartAck
core.restartRequest = ticket
Simulation.Wait(50)
assert core.restartAck == ticket
clearObservation = true
loadStore.clear = 0b1
loadStore.releaseResponse = 0b0
Simulation.Wait(4)
loadStore.clear = 0b0
clearObservation = false
for (var index: uint = 0; index < 32; index++) {
Load(index, 0x00100073)
}
}
/**
* Provisions one instruction while paused.
*/
fn Load(index: uint, word: uint)
{
state {
fetch.programWrite = 0b1
fetch.programIndex = index as logic[8]
fetch.programData = word as logic[32]
}
state {
fetch.programWrite = 0b0
}
}
/**
* Bounds initialization or instruction completion while run is low.
*/
fn WaitIdle()
{
var elapsed: uint = 0
while (core.idle == 0b0 && elapsed < 200) {
state {
elapsed++
}
}
assert core.idle == 0b1
}
/**
* Toggles exactly one pending step and waits for its completion acknowledgement.
*/
fn Step()
{
core.stepRequest = ~core.stepRequest
var elapsed: uint = 0
while (core.stepAck != core.stepRequest && elapsed < 200) {
state {
elapsed++
}
}
assert core.stepAck == core.stepRequest
}
/**
* A held step request executes once; restart clears registers and restarts at zero.
*/
@Test
fn TestStepPauseRestart()
{
Prepare()
Load(0, 0x00700093)
Load(1, 0x00108113)
Load(2, 0x00100073)
WaitIdle()
Step()
assert count == 1 && lastValue == 7
assert core.pc == 4
Simulation.Wait(20)
assert count == 1
Step()
assert count == 2 && lastValue == 8
Step()
assert core.halted == 0b1 && core.faultCause == 3
assert count == 2
var ticket = ~core.restartAck
core.restartRequest = ticket
Simulation.Wait(50)
assert core.restartAck == ticket && core.halted == 0b0
assert core.pc == 0
Load(0, 0x00008193)
Step()
assert count == 3 && lastValue == 0
}
/**
* Software restart drains a pending store exactly once before clearing CPU state.
*/
@Test
fn TestRestartDrainsDelayedStore()
{
Prepare()
Load(0, 0x02a00093)
Load(1, 0x00102023)
Load(2, 0x00002103)
Load(3, 0x00100073)
core.run = 0b1
var elapsed: uint = 0
while (loadStore.accepted == 0b0 && elapsed < 300) {
state {
elapsed++
}
}
assert loadStore.accepted == 0b1
core.run = 0b0
var ticket = ~core.restartAck
core.restartRequest = ticket
Simulation.Wait(20)
assert core.restartAck != ticket
assert loadStore.writes == 0 && count == 1
loadStore.releaseResponse = 0b1
Simulation.Wait(130)
assert core.restartAck == ticket && core.idle == 0b1
assert core.pc == 0
assert loadStore.writes == 1 && loadStore.reads == 1
assert loadStore.payload == 42 && count == 3
Load(0, 0x00002183)
Step()
assert lastValue == 42 && loadStore.reads == 2
}
/**
* An error response prevents retirement and MMIO effects, including a load to x0.
*/
@Test
fn TestDelayedAccessFault()
{
Prepare()
Load(0, 0x00402003)
loadStore.releaseResponse = 0b1
WaitIdle()
Step()
assert core.halted == 0b1 && core.faultCause == 5
assert core.faultValue == 4 && core.pc == 0
assert count == 0 && loadStore.reads == 0 && loadStore.writes == 0
}
/**
* A taken branch drains an uncancellable wrong-path fetch and discards its error.
*/
@Test
fn TestRedirectDrainsFailedFetch()
{
Prepare()
Load(0, 0x0100006f) // jal x0, 16
Load(1, 0x00002003) // wrong-path MMIO read
Load(4, 0x00700093)
fetch.holdAddress = 4
fetch.hold = 0b1
fetch.failAddress = 4
fetch.fail = 0b1
core.run = 0b1
var elapsed: uint = 0
while ((fetch.blocked == 0b0 || core.pc != 16) && elapsed < 300) {
state {
elapsed++
}
}
assert fetch.blocked == 0b1 && core.pc == 16
core.run = 0b0
Simulation.Wait(20)
assert core.idle == 0b0 && core.halted == 0b0
assert count == 1 && loadStore.reads == 0 && loadStore.writes == 0
fetch.hold = 0b0
WaitIdle()
assert core.pc == 16 && core.halted == 0b0
Step()
assert count == 2 && lastValue == 7 && core.pc == 20
}
/**
* The earliest fault blocks younger stores and side-effecting loads.
*/
@Test
fn TestOlderFaultBlocksMmio()
{
Prepare()
Load(0, 0x02a00093)
Load(1, 0x00402103) // failing load
Load(2, 0x00102023) // younger store must not escape
Load(3, 0x00002003) // younger load to x0 must not escape
core.run = 0b1
var elapsed: uint = 0
while (loadStore.accepted == 0b0 && elapsed < 300) {
state {
elapsed++
}
}
assert loadStore.accepted == 0b1
Simulation.Wait(60)
assert count == 1 && loadStore.reads == 0 && loadStore.writes == 0
loadStore.releaseResponse = 0b1
elapsed = 0
while (core.halted == 0b0 && elapsed < 100) {
state {
elapsed++
}
}
core.run = 0b0
WaitIdle()
assert core.halted == 0b1 && core.faultCause == 5 && core.pc == 4
assert count == 1 && loadStore.reads == 0 && loadStore.writes == 0
}
/**
* Taken branches suppress both kinds of wrong-path device access.
*/
@Test
fn TestBranchBlocksMmio()
{
Prepare()
Load(0, 0x00c0006f)
Load(1, 0x00002003)
Load(2, 0x00002023)
Load(3, 0x00900093)
loadStore.releaseResponse = 0b1
core.run = 0b1
var elapsed: uint = 0
while (core.halted == 0b0 && elapsed < 300) {
state {
elapsed++
}
}
core.run = 0b0
WaitIdle()
assert core.halted == 0b1 && core.faultCause == 3 && core.pc == 16
assert count == 2 && lastValue == 9
assert loadStore.reads == 0 && loadStore.writes == 0
}
/**
* Vary the pause edge across fetch/read/execute/commit occupancy. Draining
* leaves an architectural PC matching exactly the admitted instruction prefix.
*/
@Test
fn TestPauseAtEveryOffset()
{
for (var offset: uint = 1; offset <= 30; offset++) {
Prepare()
// Scheduled loop overhead allows more clocks than the offset itself.
// Keep the final step inside the arithmetic stream at every pause offset.
for (var index: uint = 0; index < 64; index++) {
Load(index, 0x00108093) // addi x1, x1, 1
}
core.run = 0b1
for (var tick: uint = 0; tick < offset; tick++) {
state {
}
}
core.run = 0b0
WaitIdle()
assert core.halted == 0b0
assert core.pc as uint == count * 4
assert lastValue == count
var pausedCount = count
Simulation.Wait(8)
assert count == pausedCount
Step()
assert count == pausedCount + 1 && lastValue == count
}
}
/**
* Diagnostic halt can precede completion of a speculative fetch; idle cannot.
*/
@Test
fn TestFaultDrainsFetchBeforeIdle()
{
Prepare()
fetch.holdAddress = 4
fetch.hold = 0b1
core.run = 0b1
var elapsed: uint = 0
while (core.halted == 0b0 && elapsed < 300) {
state {
elapsed++
}
}
assert core.halted == 0b1 && core.pc == 0 && count == 0
core.run = 0b0
assert core.idle == 0b0
fetch.hold = 0b0
WaitIdle()
assert count == 0
}
}