import scala.collection.mutable.ArrayBuffer
import spinal.core._
import spinal.core.sim._
import spinal.lib._
import spinal.lib.bus.amba3.apb._
import spinal.lib.bus.amba4.axi._
import spinal.lib.bus.amba4.axilite.AxiLite4SpecRenamer
import spinal.lib.com.jtag.{Jtag, JtagTapInstructionCtrl}
import spinal.lib.com.jtag.sim.JtagTcp
import spinal.lib.com.uart._
import spinal.lib.com.uart.sim.{UartDecoder, UartEncoder}
import spinal.lib.misc.AxiLite4Clint
import spinal.lib.misc.HexTools
import spinal.lib.misc.plic.AxiLite4Plic
import vexriscv.demo.MuraxApb3Timer
import vexriscv.ip._
import vexriscv.ip.fpu.FpuParameter
import vexriscv.plugin._
import vexriscv.{Riscv, VexRiscv, VexRiscvConfig, plugin}
case class MeerkatSoCConfig(
coreFrequency: HertzNumber,
withNativeJtag: Boolean,
hardwareBreakpointCount: Int,
uartCtrlConfig: UartCtrlMemoryMappedConfig,
onChipRamHexFile: String,
onChipRamSize: BigInt,
cpuPlugins: ArrayBuffer[Plugin[VexRiscv]]
)
object MeerkatSoCConfig {
def default: MeerkatSoCConfig = MeerkatSoCConfig(
coreFrequency = 12 MHz,
withNativeJtag = false,
hardwareBreakpointCount = 0,
onChipRamHexFile = null,
onChipRamSize = 8000 kB,
uartCtrlConfig = UartCtrlMemoryMappedConfig(
uartCtrlConfig = UartCtrlGenerics(
dataWidthMax = 8,
clockDividerWidth = 20,
preSamplingSize = 1,
samplingSize = 3,
postSamplingSize = 1
),
initConfig = UartCtrlInitConfig(
baudrate = 115200,
dataLength = 7,
parity = UartParityType.NONE,
stop = UartStopType.ONE
),
busCanWriteClockDividerConfig = false,
busCanWriteFrameConfig = false,
txFifoDepth = 16,
rxFifoDepth = 16
),
cpuPlugins = ArrayBuffer(
new MmuPlugin(
ioRange = _(31 downto 28) === 0xF
),
new IBusCachedPlugin(
resetVector = 0x80000000L,
compressedGen = true,
prediction = DYNAMIC,
config = InstructionCacheConfig(
cacheSize = 4096,
bytePerLine = 32,
wayCount = 1,
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32,
catchIllegalAccess = true,
catchAccessFault = true,
asyncTagMemory = false,
twoCycleRam = false,
twoCycleCache = false
),
memoryTranslatorPortConfig = MmuPortConfig(
portTlbSize = 4
)
),
new DBusCachedPlugin(
dBusCmdMasterPipe = true,
dBusCmdSlavePipe = true,
dBusRspSlavePipe = true,
config = new DataCacheConfig(
cacheSize = 4096,
bytePerLine = 32,
wayCount = 1,
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32,
catchAccessError = true,
catchIllegal = true,
catchUnaligned = true,
withExclusive = false,
withInvalidate = false,
withLrSc = true,
withAmo = true
),
memoryTranslatorPortConfig = MmuPortConfig(
portTlbSize = 4
)
),
new DecoderSimplePlugin(
catchIllegalInstruction = true
),
new RegFilePlugin(
regFileReadyKind = plugin.SYNC,
zeroBoot = false
),
new IntAluPlugin,
new SrcPlugin(
separatedAddSub = false
),
new FullBarrelShifterPlugin,
new HazardSimplePlugin(
bypassExecute = true,
bypassMemory = true,
bypassWriteBack = true,
bypassWriteBackBuffer = true,
pessimisticUseSrc = false,
pessimisticWriteRegFile = false,
pessimisticAddressMatch = false
),
new MulPlugin,
new MulDivIterativePlugin(
genMul = false,
genDiv = true,
mulUnrollFactor = 32,
divUnrollFactor = 1
),
new CsrPlugin(
CsrPluginConfig.openSbi(mhartid = 0, misa = Riscv.misaToInt("imac")).copy(
mtvecInit = 0x80000000L,
ebreakGen = true,
marchid = 33,
utimeAccess = CsrAccess.READ_ONLY
)
),
new BranchPlugin(
earlyBranch = false
)
),
)
}
case class MeerkatSoC(config: MeerkatSoCConfig) extends Component {
import config._
val io = new Bundle {
val asyncReset = in Bool()
val mainClk = in Bool()
val jtag = ifGen(!withNativeJtag)(slave(Jtag()))
val uart = master(Uart())
}
val resetCtrlClockDomain = ClockDomain(
clock = io.mainClk,
config = ClockDomainConfig(resetKind = BOOT)
)
val resetCtrl = new ClockingArea(resetCtrlClockDomain) {
val mainClkResetUnbuffered = False
val systemClkResetCounter = Reg(UInt(6 bits)) init (0)
when(systemClkResetCounter =/= U(systemClkResetCounter.range -> true)) {
systemClkResetCounter := systemClkResetCounter + 1
mainClkResetUnbuffered := True
}
when(BufferCC(io.asyncReset)) {
systemClkResetCounter := 0
}
val mainClkReset = RegNext(mainClkResetUnbuffered)
val systemReset = RegNext(mainClkResetUnbuffered)
}
val systemClockDomain = ClockDomain(
clock = io.mainClk,
reset = resetCtrl.systemReset,
frequency = FixedFrequency(coreFrequency)
)
val debugClockDomain = ClockDomain(
clock = io.mainClk,
reset = resetCtrl.mainClkReset,
frequency = FixedFrequency(coreFrequency)
)
val jtagNative = withNativeJtag generate new ClockingArea(debugClockDomain) {
val jtagCtrl = JtagTapInstructionCtrl()
val tap = jtagCtrl.fromXilinxBscane2(userId = 2)
}
val system = new ClockingArea(systemClockDomain) {
val cpu = new VexRiscv(
VexRiscvConfig(
plugins = cpuPlugins += new DebugPlugin(debugClockDomain, hardwareBreakpointCount)
)
){
val clintCtrl = new AxiLite4Clint(1, bufferTime = false)
val plicCtrl = new AxiLite4Plic(
sourceCount = 31,
targetCount = 2
)
val clint = clintCtrl.io.bus.toIo()
val plic = plicCtrl.io.bus.toIo()
val plicInterrupts = in Bits(32 bits)
plicCtrl.io.sources := plicInterrupts >> 1
AxiLite4SpecRenamer(clint)
AxiLite4SpecRenamer(plic)
}
var iBus: Axi4ReadOnly = null
var dBus: Axi4Shared = null
for (plugin <- cpu.plugins) plugin match {
case p: IBusCachedPlugin =>
iBus = p.iBus.toAxi4ReadOnly()
case p: DBusCachedPlugin =>
dBus = p.dBus.toAxi4Shared(true)
case p: CsrPlugin =>
p.timerInterrupt.setAsDirectionLess() := cpu.clintCtrl.io.timerInterrupt(0)
p.softwareInterrupt.setAsDirectionLess() := cpu.clintCtrl.io.softwareInterrupt(0)
p.externalInterrupt.setAsDirectionLess() := cpu.plicCtrl.io.targets(0)
//p.externalInterruptS.setAsDirectionLess() := cpu.plicCtrl.io.targets(1)
p.utime.setAsDirectionLess() := cpu.clintCtrl.io.time
case p: DebugPlugin =>
p.debugClockDomain {
resetCtrl.systemReset setWhen RegNext(p.io.resetOut)
if (withNativeJtag) {
jtagNative.jtagCtrl <> p.io.bus.fromJtagInstructionCtrl(
ClockDomain(jtagNative.tap.TCK),
0
)
} else {
io.jtag <> p.io.bus.fromJtag()
}
}
case _ =>
}
assert(iBus != null, "IBusCachedPlugin missing from cpuPlugins")
assert(dBus != null, "DBusCachedPlugin missing from cpuPlugins")
val ram = Axi4SharedOnChipRam(
dataWidth = 32,
byteCount = onChipRamSize,
idWidth = 4,
)
if (onChipRamHexFile != null) {
val resetVector = cpu.plugins.collectFirst {
case p: IBusCachedPlugin => p.resetVector
}.getOrElse {
BigInt(0)
};
HexTools.initRam(ram.ram, onChipRamHexFile, resetVector)
}
val apbBridge = Axi4SharedToApb3Bridge(
addressWidth = 20,
dataWidth = 32,
idWidth = 4
)
val uartCtrl = Apb3UartCtrl(uartCtrlConfig)
uartCtrl.io.uart <> io.uart
val axiCrossbar = Axi4CrossbarFactory()
axiCrossbar.addSlaves(
ram.io.axi -> (0x80000000L, onChipRamSize),
apbBridge.io.axi -> (0xF0000000L, 1 MB)
)
axiCrossbar.addConnections(
iBus -> List(ram.io.axi),
dBus -> List(ram.io.axi, apbBridge.io.axi)
)
axiCrossbar.addPipelining(ram.io.axi) { (crossbar, ctrl) =>
crossbar.sharedCmd.halfPipe() >> ctrl.sharedCmd
crossbar.writeData >/-> ctrl.writeData
crossbar.writeRsp << ctrl.writeRsp
crossbar.readRsp << ctrl.readRsp
}
axiCrossbar.addPipelining(apbBridge.io.axi) { (crossbar, bridge) =>
crossbar.sharedCmd.halfPipe() >> bridge.sharedCmd
crossbar.writeData.halfPipe() >> bridge.writeData
crossbar.writeRsp << bridge.writeRsp
crossbar.readRsp << bridge.readRsp
}
axiCrossbar.addPipelining(dBus) { (cpuBus, crossbar) =>
cpuBus.sharedCmd >> crossbar.sharedCmd
cpuBus.writeData >> crossbar.writeData
cpuBus.writeRsp << crossbar.writeRsp
cpuBus.readRsp <-< crossbar.readRsp
}
axiCrossbar.build()
val apbDecoder = Apb3Decoder(
master = apbBridge.io.apb,
slaves = List(
uartCtrl.io.apb -> (0x10000, 4 kB),
)
)
}
}
object MeerkatSoC {
case class Args(
firmware: Option[String] = None,
yaml_out: Option[String] = None,
)
def parseArgs(args: Array[String]): Args = {
var firmware: Option[String] = None
var yaml_out: Option[String] = None
val it = args.iterator
while (it.hasNext) {
it.next() match {
case "--firmware" =>
if (!it.hasNext) {
sys.error("--firmware requires a path")
}
firmware = Some(it.next())
case "--yaml-out" =>
if (!it.hasNext) {
sys.error("--yaml-out requires a path")
}
yaml_out = Some(it.next())
case unknown =>
sys.error(s"Unknown argument: $unknown")
}
}
Args(firmware, yaml_out)
}
def main(args: Array[String]): Unit = {
val parsed = parseArgs(args)
val config = MeerkatSoCConfig.default.copy(
onChipRamHexFile = parsed.firmware.orNull
)
parsed.yaml_out.foreach { path =>
config.cpuPlugins += new YamlPlugin(path)
}
SpinalVerilog(MeerkatSoC(config))
}
}
object MeerkatSoCSim {
def main(args: Array[String]): Unit = {
val parsed = MeerkatSoC.parseArgs(args)
val config = MeerkatSoCConfig.default.copy(
onChipRamHexFile = parsed.firmware.orNull
)
parsed.yaml_out.foreach { path =>
config.cpuPlugins += new YamlPlugin(path)
}
SimConfig.allOptimisation.compile(MeerkatSoC(config)).doSimUntilVoid{dut =>
val mainClkPeriod = (1e12/dut.config.coreFrequency.toDouble).toLong
val jtagClkPeriod = mainClkPeriod*4
val uartBaudRate = 115200
val uartBaudPeriod = (1e12/uartBaudRate).toLong
val clockDomain = ClockDomain(dut.io.mainClk, dut.io.asyncReset)
clockDomain.forkStimulus(mainClkPeriod)
val tcpJtag = JtagTcp(
jtag = dut.io.jtag,
jtagClkPeriod = jtagClkPeriod
)
val uartTx = UartDecoder(
uartPin = dut.io.uart.txd,
baudPeriod = uartBaudPeriod
)
val uartRx = UartEncoder(
uartPin = dut.io.uart.rxd,
baudPeriod = uartBaudPeriod
)
}
}
}
[error] Exception in thread "main" java.lang.AssertionError: assertion failed
[error] at scala.Predef$.assert(Predef.scala:208)
[error] at spinal.core.package$.assert(core.scala:545)
[error] at vexriscv.plugin.DecoderSimplePlugin.addDefault(DecoderSimplePlugin.scala:67)
[error] at vexriscv.plugin.IBusCachedPlugin.setup(IBusCachedPlugin.scala:103)
[error] at vexriscv.plugin.IBusCachedPlugin.setup(IBusCachedPlugin.scala:28)
[error] at vexriscv.Pipeline.$anonfun$build$2(Pipeline.scala:47)
[error] at vexriscv.Pipeline.$anonfun$build$2$adapted(Pipeline.scala:47)
[error] at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
[error] at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
[error] at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
[error] at vexriscv.Pipeline.build(Pipeline.scala:47)
[error] at vexriscv.Pipeline.build$(Pipeline.scala:45)
[error] at vexriscv.VexRiscv.build(VexRiscv.scala:131)
[error] at vexriscv.Pipeline.$anonfun$$init$$1(Pipeline.scala:161)
[error] at spinal.core.Component.$anonfun$prePop$1(Component.scala:194)
[error] at spinal.core.Component.$anonfun$prePop$1$adapted(Component.scala:192)
[error] at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
[error] at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
[error] at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
[error] at spinal.core.Component.prePop(Component.scala:192)
[error] at spinal.core.Component.postInitCallback(Component.scala:201)
[error] at MeerkatSoC$$anon$4.<init>(MeerkatSoC.scala:214)
[error] at MeerkatSoC.<init>(MeerkatSoC.scala:213)
[error] at MeerkatSoCSim$.$anonfun$main$4(MeerkatSoC.scala:398)
[error] at spinal.core.internals.PhaseCreateComponent.$anonfun$impl$322(Phase.scala:3082)
[error] at spinal.core.fiber.Engine$.$anonfun$create$1(AsyncCtrl.scala:174)
[error] at spinal.core.fiber.AsyncThread.$anonfun$jvmThread$1(AsyncThread.scala:60)
[error] at spinal.core.fiber.EngineContext.$anonfun$newJvmThread$1(AsyncCtrl.scala:39)
[error] at spinal.sim.JvmThread.run(SimManager.scala:51)
[error] nonzero exit code returned from runner: 1
[error] (Compile / runMain) nonzero exit code returned from runner: 1
[error] Total time: 10 s, completed 2026/03/07 23:16:20
Everything I change causes the assert, idk what I am doing wrong. I've been changing things for an hour to resolve the issue but it doesn't work
Everything I change causes the assert, idk what I am doing wrong. I've been changing things for an hour to resolve the issue but it doesn't work