Add MachineCsr (untested)

This commit is contained in:
Charles Papon 2017-03-22 18:29:34 +01:00
parent e9d3977737
commit 94770f8e0b
6 changed files with 274 additions and 64 deletions

View File

@ -6,17 +6,68 @@ import SpinalRiscv._
import SpinalRiscv.Riscv._ import SpinalRiscv.Riscv._
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable
/** /**
* Created by spinalvm on 21.03.17. * Created by spinalvm on 21.03.17.
*/ */
trait CsrAccess
object CsrAccess {
object WRITE_ONLY extends CsrAccess
object READ_ONLY extends CsrAccess
object READ_WRITE extends CsrAccess
object NONE extends CsrAccess
}
case class ExceptionPortInfo(port : Flow[UInt],stage : Stage) case class ExceptionPortInfo(port : Flow[UInt],stage : Stage)
case class MachineCsrConfig() case class MachineCsrConfig(
mvendorid : BigInt,
marchid : BigInt,
mimpid : BigInt,
mhartid : BigInt,
misaExtensions : Int,
misaAccess : CsrAccess,
mtvecAccess : CsrAccess,
mtvecInit : BigInt,
mepcAccess : CsrAccess,
mscratchGen : Boolean,
mcauseAccess : CsrAccess,
mbadaddrAccess : CsrAccess
class MachineCsr extends Plugin[VexRiscv] with ExceptionService{ )
case class CsrWrite(that : Data, bitOffset : Int)
case class CsrRead(that : Data , bitOffset : Int)
case class CsrMapping(){
val mapping = mutable.HashMap[Int,ArrayBuffer[Any]]()
def addMappingAt(address : Int,that : Any) = mapping.getOrElseUpdate(address,new ArrayBuffer[Any]) += that
def r(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrRead(that,bitOffset))
def w(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrWrite(that,bitOffset))
def rw(csrAddress : Int, bitOffset : Int,that : Data): Unit ={
r(csrAddress,bitOffset,that)
w(csrAddress,bitOffset,that)
}
def rw(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) rw(csrAddress,that._1, that._2)
def r [T <: Data](csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) r(csrAddress,that._1, that._2)
def rw[T <: Data](csrAddress : Int, that : T): Unit = rw(csrAddress,0,that)
def r [T <: Data](csrAddress : Int, that : T): Unit = r(csrAddress,0,that)
def rx [T <: Data](csrAddress : Int, thats : (Int, Data)*)(writable : Boolean) : Unit =
if(writable)
for(that <- thats) rw(csrAddress,that._1, that._2)
else
for(that <- thats) r(csrAddress,that._1, that._2)
}
class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with ExceptionService {
import config._
import CsrAccess._
//Mannage ExceptionService calls
val exceptionPortsInfos = ArrayBuffer[ExceptionPortInfo]() val exceptionPortsInfos = ArrayBuffer[ExceptionPortInfo]()
def exceptionCodeWidth = 4 def exceptionCodeWidth = 4
override def newExceptionPort(stage : Stage) = { override def newExceptionPort(stage : Stage) = {
@ -26,6 +77,7 @@ class MachineCsr extends Plugin[VexRiscv] with ExceptionService{
} }
var jumpInterface : Flow[UInt] = null var jumpInterface : Flow[UInt] = null
var pluginExceptionPort : Flow[UInt] = null
object EnvCtrlEnum extends SpinalEnum(binarySequential){ object EnvCtrlEnum extends SpinalEnum(binarySequential){
val NONE, EBREAK, ECALL, MRET = newElement() val NONE, EBREAK, ECALL, MRET = newElement()
@ -33,6 +85,7 @@ class MachineCsr extends Plugin[VexRiscv] with ExceptionService{
object ENV_CTRL extends Stageable(EnvCtrlEnum()) object ENV_CTRL extends Stageable(EnvCtrlEnum())
object EXCEPTION extends Stageable(Bool) object EXCEPTION extends Stageable(Bool)
object IS_CSR extends Stageable(Bool)
override def setup(pipeline: VexRiscv): Unit = { override def setup(pipeline: VexRiscv): Unit = {
import pipeline.config._ import pipeline.config._
@ -41,22 +94,25 @@ class MachineCsr extends Plugin[VexRiscv] with ExceptionService{
LEGAL_INSTRUCTION -> True LEGAL_INSTRUCTION -> True
) )
val defaultActions = List[(Stageable[_ <: BaseType],Any)]( val defaultCsrActions = List[(Stageable[_ <: BaseType],Any)](
LEGAL_INSTRUCTION -> True, LEGAL_INSTRUCTION -> True,
IS_CSR -> True,
REGFILE_WRITE_VALID -> True, REGFILE_WRITE_VALID -> True,
BYPASSABLE_EXECUTE_STAGE -> False, BYPASSABLE_EXECUTE_STAGE -> False,
BYPASSABLE_MEMORY_STAGE -> False BYPASSABLE_MEMORY_STAGE -> False
) )
val nonImmediatActions = defaultActions ++ List( val nonImmediatActions = defaultCsrActions ++ List(
SRC1_CTRL -> Src1CtrlEnum.RS SRC1_CTRL -> Src1CtrlEnum.RS,
REG1_USE -> True
) )
val immediatActions = defaultActions val immediatActions = defaultCsrActions
val decoderService = pipeline.service(classOf[DecoderService]) val decoderService = pipeline.service(classOf[DecoderService])
decoderService.addDefault(ENV_CTRL, EnvCtrlEnum.NONE) decoderService.addDefault(ENV_CTRL, EnvCtrlEnum.NONE)
decoderService.addDefault(IS_CSR, False)
decoderService.add(List( decoderService.add(List(
CSRRW -> nonImmediatActions, CSRRW -> nonImmediatActions,
CSRRS -> nonImmediatActions, CSRRS -> nonImmediatActions,
@ -69,80 +125,188 @@ class MachineCsr extends Plugin[VexRiscv] with ExceptionService{
MRET -> (defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.MRET)) MRET -> (defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.MRET))
)) ))
pipeline.fetch.insert(EXCEPTION) := False
val pcManagerService = pipeline.service(classOf[JumpService]) val pcManagerService = pipeline.service(classOf[JumpService])
jumpInterface = pcManagerService.createJumpInterface(pipeline.execute) jumpInterface = pcManagerService.createJumpInterface(pipeline.execute)
jumpInterface.valid := False jumpInterface.valid := False
jumpInterface.payload.assignDontCare() jumpInterface.payload.assignDontCare()
}
pluginExceptionPort = newExceptionPort(pipeline.execute)
pluginExceptionPort.valid := False
pluginExceptionPort.payload.assignDontCare()
}
def xlen = 32
override def build(pipeline: VexRiscv): Unit = { override def build(pipeline: VexRiscv): Unit = {
import pipeline._ import pipeline._
import pipeline.config._ import pipeline.config._
//Manage ECALL instructions
when(execute.arbitration.isValid && execute.input(ENV_CTRL) === EnvCtrlEnum.ECALL){
val mtvec = UInt(32 bits) pluginExceptionPort.valid := True
val mepc = Reg(UInt(32 bits)) pluginExceptionPort.payload := 3
val mstatus = new Area{
val MIE, MPIE = Reg(Bool) init(False)
} }
val pipelineLiberator = new Area{ pipeline plug new Area{
val enable = False //Define CSR registers
decode.arbitration.haltIt setWhen(enable) val csrMapping = new CsrMapping()
val done = ! List(fetch, decode, execute, memory, writeBack).map(_.arbitration.isValid).orR implicit class CsrAccessPimper(csrAccess : CsrAccess){
} def apply(csrAddress : Int, thats : (Int, Data)*) : Unit = csrAccess match{
case `WRITE_ONLY` | `READ_WRITE` => for(that <- thats) csrMapping.w(csrAddress,that._1, that._2)
case `READ_ONLY` | `READ_WRITE` => for(that <- thats) csrMapping.r(csrAddress,that._1, that._2)
}
def apply(csrAddress : Int, that : Data) : Unit = csrAccess match{
case `WRITE_ONLY` | `READ_WRITE` => csrMapping.w(csrAddress, 0, that)
case `READ_ONLY` | `READ_WRITE` => csrMapping.r(csrAddress, 0, that)
}
}
val exceptionPortCtrl = new Area{ //Define CSR registers
val pipelineHasException = List(fetch, decode, execute, memory, writeBack).map(s => s.arbitration.isValid && s.input(EXCEPTION)).orR val mtvec = RegInit(U(mtvecInit,xlen bits))
decode.arbitration.haltIt setWhen(pipelineHasException) val mepc = Reg(UInt(xlen bits))
val mstatus = new Area{
val MIE, MPIE = RegInit(False)
}
val mip = new Area{
val MEIP, MTIP, MSIP = False //TODO
}
val mie = new Area{
val MEIE, MTIE, MSIE = RegInit(False)
}
val mscratch = if(mscratchGen) Reg(Bits(xlen bits)) else null
val mcause = new Area{
val interrupt = Reg(Bool)
val exceptionCode = Reg(UInt(exceptionCodeWidth bits))
}
val mbadaddr = Reg(UInt(xlen bits))
val groupedByStage = exceptionPortsInfos.map(_.stage).distinct.map(s => { //Define CSR registers accessibility
val stagePortsInfos = exceptionPortsInfos.filter(_.stage == s) if(mvendorid != null) READ_ONLY(CSR.MVENDORID, U(mvendorid))
val stagePort = stagePortsInfos.length match{ if(marchid != null) READ_ONLY(CSR.MARCHID , U(marchid ))
case 1 => stagePortsInfos.head.port if(mimpid != null) READ_ONLY(CSR.MIMPID , U(mimpid ))
case _ => { if(mhartid != null) READ_ONLY(CSR.MHARTID , U(mhartid ))
val groupedPort = Flow(UInt(exceptionCodeWidth bits))
val valids = stagePortsInfos.map(_.port.valid) misaAccess(CSR.MISA, xlen-2 -> U"01" , 0 -> U(misaExtensions))
val codes = stagePortsInfos.map(_.port.payload) READ_ONLY(CSR.MIP, 11 -> mip.MEIP, 7 -> mip.MTIP, 3 -> mip.MSIP)
groupedPort.valid := valids.orR READ_WRITE(CSR.MIE, 11 -> mie.MEIE, 7 -> mie.MTIE, 3 -> mie.MSIE)
groupedPort.payload := MuxOH(stagePortsInfos.map(_.port.valid), codes)
groupedPort mtvecAccess(CSR.MTVEC , mtvec)
mepcAccess(CSR.MEPC , mepc)
READ_ONLY(CSR.MSTATUS, 7 -> mstatus.MPIE, 3 -> mstatus.MIE)
if(mscratchGen) READ_WRITE(CSR.MSCRATCH, mscratch)
mcauseAccess(CSR.MCAUSE, xlen-1 -> mcause.interrupt, 0 -> mcause.exceptionCode)
mbadaddrAccess(CSR.MBADADDR, mbadaddr)
//Used to make the pipeline empty softly (for interrupts)
val pipelineLiberator = new Area{
val enable = False
decode.arbitration.haltIt setWhen(enable)
val done = ! List(fetch, decode, execute, memory, writeBack).map(_.arbitration.isValid).orR
}
//Aggregate all exception port and remove required instructions
val exceptionPortCtrl = if(exceptionPortsInfos.nonEmpty) new Area{
val firstStageIndexWithExceptionPort = exceptionPortsInfos.map(i => indexOf(i.stage)).min
val pipelineHasException = stages.drop(firstStageIndexWithExceptionPort).map(s => s.arbitration.isValid && s.input(EXCEPTION)).orR
decode.arbitration.haltIt setWhen(pipelineHasException)
val groupedByStage = exceptionPortsInfos.map(_.stage).distinct.map(s => {
val stagePortsInfos = exceptionPortsInfos.filter(_.stage == s)
val stagePort = stagePortsInfos.length match{
case 1 => stagePortsInfos.head.port
case _ => {
val groupedPort = Flow(UInt(exceptionCodeWidth bits))
val valids = stagePortsInfos.map(_.port.valid)
val codes = stagePortsInfos.map(_.port.payload)
groupedPort.valid := valids.orR
groupedPort.payload := MuxOH(stagePortsInfos.map(_.port.valid), codes)
groupedPort
}
}
ExceptionPortInfo(stagePort,s)
})
val sortedByStage = groupedByStage.sortWith((a, b) => pipeline.indexOf(a.stage) > pipeline.indexOf(b.stage))
sortedByStage.head.stage.insert(EXCEPTION) := False
for(portInfo <- sortedByStage; port = portInfo.port ; stage = portInfo.stage){
when(port.valid){
stages(indexOf(stage) - 1).arbitration.flushIt := True
stage.input(EXCEPTION) := True
} }
} }
ExceptionPortInfo(stagePort,s) } else null
})
val sortedByStage = groupedByStage.sortWith((a, b) => pipeline.indexOf(a.stage) > pipeline.indexOf(b.stage))
for(portInfo <- sortedByStage; port = portInfo.port ; stage = portInfo.stage){ val interrupt = False
when(port.valid){ val exception = if(exceptionPortsInfos.nonEmpty)
stages(indexOf(stage)).arbitration.flushIt := True writeBack.arbitration.isValid && writeBack.input(EXCEPTION)
stage.input(EXCEPTION) := True else
False
when(mstatus.MIE){
pipelineLiberator.enable := interrupt
when(exception || (interrupt && pipelineLiberator.done)){
jumpInterface.valid := True
jumpInterface.payload := mtvec
mstatus.MIE := False
mstatus.MPIE := mstatus.MIE
mepc := exception ? writeBack.input(PC) | prefetch.input(PC_CALC_WITHOUT_JUMP)
} }
} }
}
val interrupt = False when(memory.arbitration.isFiring && memory.input(ENV_CTRL) === EnvCtrlEnum.MRET){
val exception = writeBack.arbitration.isValid && writeBack.input(EXCEPTION)
when(mstatus.MIE){
pipelineLiberator.enable := True
when(exception || (interrupt && pipelineLiberator.done)){
jumpInterface.valid := True jumpInterface.valid := True
jumpInterface.payload := mtvec jumpInterface.payload := mepc
mstatus.MIE := False mstatus.MIE := mstatus.MPIE
mstatus.MPIE := mstatus.MIE
mepc := exception ? writeBack.input(PC) | prefetch.input(PC_CALC_WITHOUT_JUMP)
} }
}
when(memory.arbitration.isFiring && memory.input(ENV_CTRL) === EnvCtrlEnum.MRET){
jumpInterface.valid := True
jumpInterface.payload := mepc execute plug new Area {
mstatus.MIE := mstatus.MPIE
import execute._
val imm = IMM(input(INSTRUCTION))
val writeEnable = !((input(INSTRUCTION)(14 downto 13) === "01" && input(INSTRUCTION)(rs1Range) === 0)
|| (input(INSTRUCTION)(14 downto 13) === "10" && imm.z === 0))
val readEnable = input(INSTRUCTION)(rdRange) =/= 0
val writeSrc = input(INSTRUCTION)(14) ? imm.z.asBits.resized | input(SRC1)
val readData = B(0, 32 bits)
val writeData = input(INSTRUCTION)(12).mux(
False -> writeSrc,
True -> Mux(input(INSTRUCTION)(13), readData & ~writeSrc, readData | writeSrc)
)
when(arbitration.isValid && input(IS_CSR)) {
when(writeEnable) {
output(REGFILE_WRITE_DATA) := writeData
}
}
val csrAddress = input(INSTRUCTION)(csrRange)
when(arbitration.isValid && input(IS_CSR)) {
for ((address, jobs) <- csrMapping.mapping) {
when(csrAddress === address) {
when(writeEnable) {
for (element <- jobs) element match {
case element: CsrWrite => element.that.assignFromBits(writeData(element.bitOffset, element.that.getBitsWidth bits))
// case element: BusSlaveFactoryOnWriteAtAddress => element.doThat()
case _ =>
}
}
for (element <- jobs) element match {
case element: CsrRead => readData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
// case element: BusSlaveFactoryOnReadAtAddress => when(readEnable) { element.doThat() }
case _ =>
}
}
}
}
}
} }
} }
} }

View File

@ -151,7 +151,7 @@ class LightShifterPlugin extends Plugin[VexRiscv]{
when(arbitration.isValid && isShift && input(SRC2)(4 downto 0) =/= 0){ when(arbitration.isValid && isShift && input(SRC2)(4 downto 0) =/= 0){
insert(REGFILE_WRITE_DATA) := input(SHIFT_CTRL).mux( output(REGFILE_WRITE_DATA) := input(SHIFT_CTRL).mux(
ShiftCtrlEnum.SLL -> (shiftInput |<< 1), ShiftCtrlEnum.SLL -> (shiftInput |<< 1),
default -> (((input(SHIFT_CTRL) === ShiftCtrlEnum.SRA && shiftInput.msb) ## shiftInput).asSInt >> 1).asBits //ALU.SRL,ALU.SRA default -> (((input(SHIFT_CTRL) === ShiftCtrlEnum.SRA && shiftInput.msb) ## shiftInput).asSInt >> 1).asBits //ALU.SRL,ALU.SRA
) )

View File

@ -9,6 +9,7 @@ object Riscv{
def funct3Range = 14 downto 12 def funct3Range = 14 downto 12
def rs1Range = 19 downto 15 def rs1Range = 19 downto 15
def rs2Range = 24 downto 20 def rs2Range = 24 downto 20
def csrRange = 31 downto 20
case class IMM(instruction : Bits) extends Area{ case class IMM(instruction : Bits) extends Area{
// immediates // immediates
@ -82,4 +83,22 @@ object Riscv{
def ECALL = M"00000000000000000000000001110011" def ECALL = M"00000000000000000000000001110011"
def EBREAK = M"00000000000100000000000001110011" def EBREAK = M"00000000000100000000000001110011"
def MRET = M"00110000001000000000000001110011" def MRET = M"00110000001000000000000001110011"
object CSR{
def MVENDORID = 0xF11 // MRO Vendor ID.
def MARCHID = 0xF12 // MRO Architecture ID.
def MIMPID = 0xF13 // MRO Implementation ID.
def MHARTID = 0xF14 // MRO Hardware thread ID.Machine Trap Setup
def MSTATUS = 0x300 // MRW Machine status register.
def MISA = 0x301 // MRW ISA and extensions
def MEDELEG = 0x302 // MRW Machine exception delegation register.
def MIDELEG = 0x303 // MRW Machine interrupt delegation register.
def MIE = 0x304 // MRW Machine interrupt-enable register.
def MTVEC = 0x305 // MRW Machine trap-handler base address. Machine Trap Handling
def MSCRATCH = 0x340 // MRW Scratch register for machine trap handlers.
def MEPC = 0x341 // MRW Machine exception program counter.
def MCAUSE = 0x342 // MRW Machine trap cause.
def MBADADDR = 0x343 // MRW Machine bad address.
def MIP = 0x344 // MRW Machine interrupt pending.
}
} }

View File

@ -30,8 +30,25 @@ object TopLevel {
pcWidth = 32 pcWidth = 32
) )
import CsrAccess._
val csrConfig = MachineCsrConfig(
mvendorid = 11,
marchid = 22,
mimpid = 33,
mhartid = 44,
misaExtensions = 66,
misaAccess = READ_WRITE,
mtvecAccess = READ_WRITE,
mtvecInit = 0x80000004l,
mepcAccess = READ_WRITE,
mscratchGen = true,
mcauseAccess = READ_WRITE,
mbadaddrAccess = READ_WRITE
)
config.plugins ++= List( config.plugins ++= List(
new PcManagerSimplePlugin(0, false), new PcManagerSimplePlugin(0x00000000l, false),
new IBusSimplePlugin(true), new IBusSimplePlugin(true),
new DecoderSimplePlugin, new DecoderSimplePlugin,
new RegFilePlugin(Plugin.SYNC), new RegFilePlugin(Plugin.SYNC),
@ -45,6 +62,7 @@ object TopLevel {
// new HazardSimplePlugin(false, false, false, false), // new HazardSimplePlugin(false, false, false, false),
new MulPlugin, new MulPlugin,
new DivPlugin, new DivPlugin,
new MachineCsr(csrConfig),
new BranchPlugin(false, DYNAMIC) new BranchPlugin(false, DYNAMIC)
) )

View File

@ -502,23 +502,25 @@ int main(int argc, char **argv, char **env) {
#ifndef REF #ifndef REF
TestA().run(); TestA().run();
for(const string &name : riscvTestMain){ for(const string &name : riscvTestMain){
redo(5,RiscvTest(name).run();) redo(REDO,RiscvTest(name).run();)
} }
for(const string &name : riscvTestMemory){ for(const string &name : riscvTestMemory){
redo(5,RiscvTest(name).run();) redo(REDO,RiscvTest(name).run();)
} }
for(const string &name : riscvTestMul){ for(const string &name : riscvTestMul){
redo(5,RiscvTest(name).run();) redo(REDO,RiscvTest(name).run();)
} }
for(const string &name : riscvTestDiv){ for(const string &name : riscvTestDiv){
redo(5,RiscvTest(name).run();) redo(REDO,RiscvTest(name).run();)
} }
#endif #endif
#ifdef DHRYSTONE
Dhrystone("dhrystoneO3",true,true).run(1e6); Dhrystone("dhrystoneO3",true,true).run(1e6);
Dhrystone("dhrystoneO3M",true,true).run(0.8e6); Dhrystone("dhrystoneO3M",true,true).run(0.8e6);
Dhrystone("dhrystoneO3M",false,false).run(0.8e6); Dhrystone("dhrystoneO3M",false,false).run(0.8e6);
// Dhrystone("dhrystoneO3ML",false,false).run(8e6); // Dhrystone("dhrystoneO3ML",false,false).run(8e6);
// Dhrystone("dhrystoneO3MLL",false,false).run(80e6); // Dhrystone("dhrystoneO3MLL",false,false).run(80e6);
#endif
} }
uint64_t duration = timer_end(startedAt); uint64_t duration = timer_end(startedAt);

View File

@ -1,4 +1,11 @@
TRACE=no TRACE=no
DHRYSTONE=yes
REDO=5
ADDCFLAGS += -CFLAGS -DREDO=${REDO}
ifeq ($(DHRYSTONE),yes)
ADDCFLAGS += -CFLAGS -DDHRYSTONE
endif
ifeq ($(TRACE),yes) ifeq ($(TRACE),yes)
VERILATOR_ARGS += --trace VERILATOR_ARGS += --trace