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/Storage/RvLoadStoreRam.lvt
namespace Eccelerators.RiscV.Storage
using Eccelerators.RiscV.Sequential
using Livt.IO
/**
* Exclusive, synchronous little-endian RAM with one outstanding word transaction.
* Holds completion until request falls. Byte writes use read/modify/write and are
* atomic relative to this sole port. Out-of-range or unaligned requests fault
* before a RAM access. Reset retains payload; initialize every location read.
* The composition root may provision whole words only while the CPU is idle and
* request is low. Programming and execution must never overlap. No bus arbitration.
*/
component RvLoadStoreRam<WORDS: int = 1024, ADDRESS_BITS: int = 10,
STYLE: MemoryStyle = MemoryStyle.Block, BASE: uint = 0> : IRv32LoadStorePort
{
static assert (BASE & 3) == 0, "Memory base must be word aligned"
static assert ADDRESS_BITS > 0 && ADDRESS_BITS <= 28, "Memory address width must be 1..28"
static assert WORDS > 0 && WORDS <= (1 << ADDRESS_BITS), "Memory capacity exceeds address width"
static assert BASE <= 0xffffffff - ((WORDS as uint) * 4 - 1), "Memory range must not wrap"
// Host provisioning signals, sampled on an enabled edge with an idle bus.
public programWrite: in logic
public programIndex: in logic[ADDRESS_BITS]
public programData: in logic[32]
// Transaction phases and little-endian lane constants.
const IDLE: uint = 0
const CAPTURE: uint = 1
const WRITE_BACK: uint = 2
const RESPONSE: uint = 3
const ALL_LANES: uint = 15
const BYTE_MASK: uint = 255
// Owned native synchronous memory and transaction phase; no payload reset loop.
storage: SynchronousRam<logic[32], logic[ADDRESS_BITS], WORDS, STYLE>
phase: uint
responseError: logic
responseData: logic[32]
// Combinational address validity and expanded byte enables.
validAddress: bool
mask: uint
/**
* Creates exactly one synchronous payload memory.
*/
new()
{
storage = new SynchronousRam<logic[32], logic[ADDRESS_BITS], WORDS, STYLE>()
}
/**
* Maps load/store or provisioning operations onto the sole RAM port.
*/
process Drive[]()
{
validAddress = (address as uint & 3) == 0 && ((address as uint - BASE) >> 2) < (WORDS as uint)
mask = 0
if ((byteEnable as uint & 1) != 0) {
mask = mask | BYTE_MASK
}
if ((byteEnable as uint & 2) != 0) {
mask = mask | (BYTE_MASK << 8)
}
if ((byteEnable as uint & 4) != 0) {
mask = mask | (BYTE_MASK << 16)
}
if ((byteEnable as uint & 8) != 0) {
mask = mask | (BYTE_MASK << 24)
}
error = responseError
readData = responseData
ready = 0b0
if (phase == RESPONSE) {
ready = 0b1
}
storage.enable = false
storage.writeEnable = false
storage.address = ((address as uint - BASE) >> 2) as logic[ADDRESS_BITS]
storage.writeData = writeData
if (phase == IDLE && request == 0b1 && validAddress) {
storage.enable = true
storage.writeEnable = write == 0b1 && byteEnable as uint == ALL_LANES
} elif (phase == WRITE_BACK) {
storage.enable = true
storage.writeEnable = true
storage.writeData = (((storage.readData as uint) & ~mask) | ((writeData as uint) & mask)) as logic[32]
} elif (phase == IDLE && request == 0b0 && programWrite == 0b1) {
storage.enable = true
storage.writeEnable = true
storage.address = programIndex
storage.writeData = programData
}
}
/**
* Completes each bus request once, including errors and zero-byte-mask writes.
*/
process Advance()
{
state {
match (phase)
{
IDLE =>
{
if (request == 0b1) {
responseError = 0b0
if (!validAddress) {
responseError = 0b1
phase = RESPONSE
} elif (write == 0b1 && byteEnable as uint == ALL_LANES) {
phase = RESPONSE
} else {
phase = CAPTURE
}
}
}
CAPTURE =>
{
responseData = storage.readData
phase = RESPONSE
if (write == 0b1) {
phase = WRITE_BACK
}
}
WRITE_BACK =>
{
phase = RESPONSE
}
RESPONSE =>
{
if (request == 0b0) {
phase = IDLE
}
}
_ =>
{
phase = IDLE
}
}
}
}
}