Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions hw/spinal/futurecore/Globals.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package futurecore

import futurecore.riscv.{Rv32i, Zicsr, Privileged}

object Globals {
val SupportIsas = List(Rv32i, Zicsr, Privileged)
val AllInstructions = SupportIsas.collect(_.instructions).flatten
}
9 changes: 8 additions & 1 deletion hw/spinal/futurecore/debug/DebugPlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import spinal.lib.misc.plugin._

import futurecore.fetch.FetchPlugin
import futurecore.decode.DecodePlugin
import futurecore.execute.ExecutePlugin

class DebugPlugin extends FiberPlugin {
val setup = during setup new Area {
val fp = host[FetchPlugin]
val dp = host[DecodePlugin]
val ep = host[ExecutePlugin]
}

val logic = during build new Area {
Expand All @@ -19,10 +21,15 @@ class DebugPlugin extends FiberPlugin {
val interconnect = during build new Area {
val fp = setup.get.fp
val dp = setup.get.dp
val ep = setup.get.ep
val l = logic.get

l.probe.io.inPc := fp.getPc()
l.probe.io.inInst := fp.getInstruction()
l.probe.io.inGprs := dp.getDbgRegfile()
l.probe.io.inGprs := dp.getDbgIntRegs()
l.probe.io.inMstatus := ep.getDbgMstatus()
l.probe.io.inMtvec := ep.getDbgMtvec()
l.probe.io.inMepc := ep.getDbgMepc()
l.probe.io.inMcause := ep.getDbgMcause()
}
}
8 changes: 8 additions & 0 deletions hw/spinal/futurecore/debug/DebugProbe.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ class DebugProbe extends Component {
val inPc = in port UInt(32 bits)
val inInst = in port Bits(32 bits)
val inGprs = in port Vec(Bits(32 bits), 32)
val inMstatus = in port Bits(32 bits)
val inMtvec = in port Bits(32 bits)
val inMepc = in port Bits(32 bits)
val inMcause = in port Bits(32 bits)
}

val dbg = new debug_dpi(gprNum = 32, gprWidth = 32)
Expand All @@ -17,4 +21,8 @@ class DebugProbe extends Component {
dbg.io.s.inst_i := io.inInst
// Reverse registers to make the unified signal little-endian to pass legally
dbg.io.s.gprs_i := io.inGprs.reverse.reduce(_ ## _)
dbg.io.s.mstatus_i := io.inMstatus
dbg.io.s.mtvec_i := io.inMtvec
dbg.io.s.mepc_i := io.inMepc
dbg.io.s.mcause_i := io.inMcause
}
46 changes: 26 additions & 20 deletions hw/spinal/futurecore/decode/DecodePlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import spinal.lib.misc.plugin.FiberPlugin

import scala.collection.mutable.Map

import futurecore.riscv.Rvi
import futurecore.Globals.AllInstructions
import futurecore.riscv.{Rv32i, Zicsr, Privileged}
import futurecore.fetch.FetchPlugin
import futurecore.writeback.WritebackPlugin

Expand All @@ -25,56 +26,59 @@ class DecodePlugin extends FiberPlugin with CtrlService {
val regfile = new IntRegfile

val immSelDef = CtrlDef(ImmMode(), ImmMode.I)
.setWhen(ImmMode.S, Rvi.instructions.filter(_.fields.contains(Rvi.SImm)))
.setWhen(ImmMode.B, Rvi.instructions.filter(_.fields.contains(Rvi.BImm)))
.setWhen(ImmMode.U, Rvi.instructions.filter(_.fields.contains(Rvi.UImm)))
.setWhen(ImmMode.J, Rvi.instructions.filter(_.fields.contains(Rvi.JImm)))
.setWhen(ImmMode.S, Rv32i.instructions.filter(_.fields.contains(Rv32i.SImm)))
.setWhen(ImmMode.B, Rv32i.instructions.filter(_.fields.contains(Rv32i.BImm)))
.setWhen(ImmMode.U, Rv32i.instructions.filter(_.fields.contains(Rv32i.UImm)))
.setWhen(ImmMode.J, Rv32i.instructions.filter(_.fields.contains(Rv32i.JImm)))
registerCtrlSignal(immSelDef)

val rfWriteEnableDef = CtrlDef(Bool(), False)
.setWhen(
True,
Rvi.instructions
.filter(_.fields.contains(Rvi.Rd))
.filterNot(_ == Rvi.Ebreak)
Rv32i.instructions
.filter(_.fields.contains(Rv32i.Rd))
.filterNot(_ == Rv32i.Ebreak)
)
.setWhen(True, Zicsr.instructions.filter(_.fields.contains(Zicsr.Rd)))
registerCtrlSignal(rfWriteEnableDef)

ctrlLock.await()

val ctrlArea = new Area {
val instruction = Bits(32 bits)
val inst = Bits(32 bits)

val map = ctrlSignals.map { ctrlDef =>
val spec = ctrlDef.toDecodingSpec
val signal = spec.build(instruction, Rvi.instructions.map(_.toMasked))
val signal = spec.build(inst, AllInstructions.map(_.toMasked))
ctrlDef -> signal
}.toMap
}

val instruction = Bits(32 bits)
val writebackData = Bits(32 bits)
val inst = Bits(32 bits)
val immSel = ImmMode()
val writebackData = Bits(32 bits)
val rfWriteEnable = Bool()

ctrlArea.instruction := instruction
ctrlArea.inst := inst

immGen.io.inInst := instruction
immGen.io.inInst := inst
immGen.io.inSelMode := immSel

regfile.io.inAddrReadA := Rvi.Rs1.extract(instruction).asUInt
regfile.io.inAddrReadB := Rvi.Rs2.extract(instruction).asUInt
regfile.io.inAddrWrite := Rvi.Rd.extract(instruction).asUInt
regfile.io.inDataWrite := writebackData
regfile.io.inAddrReadA := Rv32i.Rs1.extract(inst).asUInt
regfile.io.inAddrReadB := Rv32i.Rs2.extract(inst).asUInt
regfile.io.inAddrWrite := Rv32i.Rd.extract(inst).asUInt
regfile.io.inEnableWrite := rfWriteEnable
regfile.io.inDataWrite := writebackData

val csrAddr = Zicsr.Csr.extract(inst).asUInt
}

val interconnect = during build new Area {
val fp = setup.get.fp
val wp = setup.get.wp
val l = logic.get

l.instruction := fp.getInstruction()
l.inst := fp.getInstruction()
l.writebackData := wp.getWriteback()
l.immSel := getCtrlSignal(l.immSelDef)
l.rfWriteEnable := getCtrlSignal(l.rfWriteEnableDef)
Expand All @@ -93,7 +97,9 @@ class DecodePlugin extends FiberPlugin with CtrlService {

def getImm(): SInt = logic.get.immGen.io.outImm

def getCsrAddr(): UInt = logic.get.csrAddr

def getReturnStatus(): SInt = logic.get.regfile.io.outSpecialRet.asSInt

def getDbgRegfile(): Vec[Bits] = logic.get.regfile.io.outDbgRegisters
def getDbgIntRegs(): Vec[Bits] = logic.get.regfile.io.dbgIntRegisters
}
15 changes: 11 additions & 4 deletions hw/spinal/futurecore/decode/ImmGenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package futurecore.decode

import spinal.core._

import futurecore.riscv.Rvi.{IImm, SImm, BImm, UImm, JImm}
import futurecore.misc.TypeExtensions.BitsSextExtension
import futurecore.riscv.Rv32i.{IImm, SImm, BImm, UImm, JImm}
import futurecore.riscv.Zicsr.{MicroImm => CsrMicroImm}
import futurecore.misc.TypeExtensions.{BitsSextExtension, BitsZeroExtension}

object ImmGenerator {
object ImmMode extends SpinalEnum {
val I, S, B, U, J = newElement()
val I, S, B, U, J, CsrU = newElement()
}
}

Expand All @@ -28,12 +29,18 @@ class ImmGenerator extends Component {
val uImm = (UImm.extract(inst) ## U"12'b0").sext
val jImm = (JImm.extract(inst) ## U"1'b0").sext

val zicsrMicroImm = (CsrMicroImm.extract(inst)).zext

// Select immediate based on mode
io.outImm := io.inSelMode.mux(
ImmMode.I -> iImm,
ImmMode.S -> sImm,
ImmMode.B -> bImm,
ImmMode.U -> uImm,
ImmMode.J -> jImm
ImmMode.J -> jImm,
// Although uImm is UInt and zero-extended,to make type system happy,
// still needs to convert to SInt. This will not cause any problem as
// binary representation keep untouched.
ImmMode.CsrU -> zicsrMicroImm.asSInt
)
}
4 changes: 2 additions & 2 deletions hw/spinal/futurecore/decode/IntRegfile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class IntRegfile extends Component {
val inDataWrite = in port Bits(32 bits)

val outSpecialRet = out port Bits(32 bits)
val outDbgRegisters = out port Vec(Bits(32 bits), 32)
val dbgIntRegisters = out port Vec(Bits(32 bits), 32)
}

val regfile = Mem(Bits(32 bits), 32)
Expand All @@ -32,5 +32,5 @@ class IntRegfile extends Component {
val returnIndex = U(10, 5 bits)
io.outSpecialRet := regfile(returnIndex.resized)

io.outDbgRegisters.zipWithIndex.foreach { case (b, i) => b := regfile(U(i).resized) }
io.dbgIntRegisters.zipWithIndex.foreach { case (b, i) => b := regfile(U(i).resized) }
}
150 changes: 150 additions & 0 deletions hw/spinal/futurecore/execute/CsrHandler.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package futurecore.execute

import spinal.core._

object CsrHandler {
object CsrMode extends SpinalEnum {
val Write, Set = newElement()
}

object ExceptionType extends SpinalEnum {
val EcallM = newElement() // ! Triggers SpinalHDL bug (see below)
}
}

class CsrHandler extends Component {
import CsrHandler._

val io = new Bundle {
val inEnableCsr = in port Bool()
val inSelCsrMode = in port CsrMode()
val inCsrAddr = in port UInt(12 bits)
val inNewVal = in port Bits(32 bits)
val outOldVal = out port Bits(32 bits)

val inEnableTrap = in port Bool()
val inEnableTrapReturn = in port Bool()
val inSelException = in port ExceptionType()
val inExceptionPc = in port UInt(32 bits)
val outTrapVector = out port UInt(32 bits)
val outReturnPc = out port UInt(32 bits)

val dbgMstatus = out port Bits(32 bits)
val dbgMtvec = out port Bits(32 bits)
val dbgMepc = out port Bits(32 bits)
val dbgMcause = out port Bits(32 bits)
}

val csrRegfile = new Area {
val enable = Bool()
val mode = CsrMode()
val addr = UInt(12 bits)
val newVal = Bits(32 bits)

case class CsrReg(number: Int, init: Int) {
val index = U(number, 12 bits)
val reg = RegInit(B(init, 32 bits))
}

// ! WARNING: Hardcoded mstatus initial value here to pass difftest!
// ! Actually do nothing on mstatus except for direct access.
val mstatus = CsrReg(0x300, 0x1800)
val mtvec = CsrReg(0x305, 0)
val mepc = CsrReg(0x341, 0)
val mcause = CsrReg(0x342, 0)

// ! WARNING: To simplify implementation,
// ! ccessibility and lowestLevel not checked!
// ! Just list here for hinting of future implementation.
// read/write: 00,01 read-only: 11
val accessibility = addr(11 downto 10)
val isReadOnly = accessibility.andR
// privilege levels: user: 00 supervisor: 01 machine: 11
val lowestLevel = addr(9 downto 8)

// Atomic read write
// ! WARNING: Also, to simplify implementation,
// ! field constrains are ignored!
// ! Moreover, here uses ALWAYS READ policy!
// ! This VIOLATES the rule that if rd=x0,
// ! ReadWrite should not cause any read side effect!
// For now, just use plain mux or switch to handle addressing
val oldVal = addr.mux(
mstatus.index -> mstatus.reg,
mtvec.index -> mtvec.reg,
mepc.index -> mepc.reg,
mcause.index -> mcause.reg,
default -> B(0).resized
)

val writeVal = isReadOnly ? oldVal | mode.mux(
CsrMode.Write -> newVal,
// use or to set unmasked bits and remain masked bits
CsrMode.Set -> (newVal | oldVal)
)

when(enable) {
switch(addr) {
is(mstatus.index) { mstatus.reg := writeVal }
is(mtvec.index) { mtvec.reg := writeVal }
is(mepc.index) { mepc.reg := writeVal }
is(mcause.index) { mcause.reg := writeVal }
}
}
}

csrRegfile.enable := io.inEnableCsr
csrRegfile.mode := io.inSelCsrMode
csrRegfile.addr := io.inCsrAddr
csrRegfile.newVal := io.inNewVal
io.outOldVal := io.inEnableCsr ? csrRegfile.oldVal | B(0).resized

// Trap handling logic
/*
! SpinalHDL bug: enum.mux() causes NullPointerException when enum has only 1 element.
! Workaround: Ensure enum has at least 2 elements.
! Using constant here since only Ecall is supported.
! https://github.com/SpinalHDL/SpinalHDL/issues/1884
*/
val TrapCauseId = B"32'hb"

// ! WARNING: Hardcoded mstatus trap value here to pass difftest!
// ! Actually do nothing on mstatus except for direct access.
val MstatusAfterTrap = B"32'h1800"

// Only accept trap when not csr ops, as csr should be atomic
// ? Actually this the false combination should never happen !
when(io.inEnableTrap & !io.inEnableCsr) {
csrRegfile.mstatus.reg := MstatusAfterTrap
csrRegfile.mcause.reg := TrapCauseId
csrRegfile.mepc.reg := io.inExceptionPc.asBits
}

// Trap return handling logic
// ! WARNING: Hardcoded mstatus return value here to pass difftest!
// ! Actually do nothing on mstatus except for direct access.
val MstatusAfterTrapReturn = B"32'h80"
// Only accept trap return when not csr ops nor trap
// ? Actually this the false combination should never happen !
when(io.inEnableTrapReturn & !io.inEnableTrap & !io.inEnableCsr) {
csrRegfile.mstatus.reg := MstatusAfterTrapReturn
}

// ! WARNING: the trap vector and epc have been hard wared and may not
// ! comfy spec, which may cause security problems!
val trapTarget = (csrRegfile.mtvec.reg(31 downto 2) ## U"2'b0").asUInt
val returnTarget = csrRegfile.mepc.reg.asUInt

io.outTrapVector := trapTarget
io.outReturnPc := returnTarget

// Debug outputs for CSR registers
io.dbgMstatus := csrRegfile.mstatus.reg
io.dbgMtvec := csrRegfile.mtvec.reg
io.dbgMepc := csrRegfile.mepc.reg
io.dbgMcause := csrRegfile.mcause.reg
}

object CsrHandlerTest extends App {
SpinalConfig().generateSystemVerilog(new CsrHandler)
}
Loading