aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala
diff options
context:
space:
mode:
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala')
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala357
1 files changed, 357 insertions, 0 deletions
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala
new file mode 100644
index 0000000..d343640
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala
@@ -0,0 +1,357 @@
+package vexriscv.plugin
+
+import vexriscv.{DecoderService, ExceptionCause, ExceptionService, Stage, Stageable, VexRiscv}
+import spinal.core._
+import spinal.lib._
+import spinal.lib.bus.bmb.WeakConnector
+import spinal.lib.bus.misc.{AddressMapping, DefaultMapping}
+import vexriscv.Riscv.IMM
+
+case class CfuPluginParameter(
+ CFU_VERSION : Int,
+ CFU_INTERFACE_ID_W : Int,
+ CFU_FUNCTION_ID_W : Int,
+ CFU_REORDER_ID_W : Int,
+ CFU_REQ_RESP_ID_W : Int,
+ CFU_INPUTS : Int,
+ CFU_INPUT_DATA_W : Int,
+ CFU_OUTPUTS : Int,
+ CFU_OUTPUT_DATA_W : Int,
+ CFU_FLOW_REQ_READY_ALWAYS : Boolean,
+ CFU_FLOW_RESP_READY_ALWAYS : Boolean)
+
+case class CfuBusParameter(CFU_VERSION : Int = 0,
+ CFU_INTERFACE_ID_W : Int = 0,
+ CFU_FUNCTION_ID_W : Int,
+ CFU_CFU_ID_W : Int = 0,
+ CFU_REORDER_ID_W : Int = 0,
+ CFU_REQ_RESP_ID_W : Int = 0,
+ CFU_STATE_INDEX_NUM : Int = 0,
+ CFU_INPUTS : Int,
+ CFU_INPUT_DATA_W : Int,
+ CFU_OUTPUTS : Int,
+ CFU_OUTPUT_DATA_W : Int,
+ CFU_FLOW_REQ_READY_ALWAYS : Boolean,
+ CFU_FLOW_RESP_READY_ALWAYS : Boolean,
+ CFU_WITH_STATUS : Boolean = false,
+ CFU_RAW_INSN_W : Int = 0)
+
+case class CfuCmd( p : CfuBusParameter ) extends Bundle{
+ val function_id = UInt(p.CFU_FUNCTION_ID_W bits)
+ val reorder_id = UInt(p.CFU_REORDER_ID_W bits)
+ val request_id = UInt(p.CFU_REQ_RESP_ID_W bits)
+ val inputs = Vec(Bits(p.CFU_INPUT_DATA_W bits), p.CFU_INPUTS)
+ val state_index = UInt(log2Up(p.CFU_STATE_INDEX_NUM) bits)
+ val cfu_index = UInt(p.CFU_CFU_ID_W bits)
+ val raw_insn = Bits(p.CFU_RAW_INSN_W bits)
+ def weakAssignFrom(m : CfuCmd): Unit ={
+ def s = this
+ WeakConnector(m, s, m.function_id, s.function_id, defaultValue = null, allowUpSize = false, allowDownSize = true , allowDrop = true)
+ WeakConnector(m, s, m.reorder_id, s.reorder_id, defaultValue = null, allowUpSize = false , allowDownSize = false, allowDrop = false)
+ WeakConnector(m, s, m.request_id, s.request_id, defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
+ s.inputs := m.inputs
+ }
+}
+
+case class CfuRsp(p : CfuBusParameter) extends Bundle{
+ val response_id = UInt(p.CFU_REQ_RESP_ID_W bits)
+ val outputs = Vec(Bits(p.CFU_OUTPUT_DATA_W bits), p.CFU_OUTPUTS)
+ val status = p.CFU_WITH_STATUS generate Bits(3 bits)
+
+ def weakAssignFrom(m : CfuRsp): Unit ={
+ def s = this
+ s.response_id := m.response_id
+ s.outputs := m.outputs
+ }
+}
+
+case class CfuBus(p : CfuBusParameter) extends Bundle with IMasterSlave{
+ val cmd = Stream(CfuCmd(p))
+ val rsp = Stream(CfuRsp(p))
+
+ def <<(m : CfuBus) : Unit = {
+ val s = this
+ s.cmd.arbitrationFrom(m.cmd)
+ m.rsp.arbitrationFrom(s.rsp)
+
+ s.cmd.weakAssignFrom(m.cmd)
+ m.rsp.weakAssignFrom(s.rsp)
+ }
+
+ override def asMaster(): Unit = {
+ master(cmd)
+ slave(rsp)
+ }
+}
+
+object CfuPlugin{
+ object Input2Kind extends SpinalEnum{
+ val RS, IMM_I = newElement()
+ }
+}
+
+case class CfuPluginEncoding(instruction : MaskedLiteral,
+ functionId : List[Range],
+ input2Kind : CfuPlugin.Input2Kind.E){
+ val functionIdWidth = functionId.map(_.size).sum
+}
+
+class CfuPlugin(val stageCount : Int,
+ val allowZeroLatency : Boolean,
+ val busParameter : CfuBusParameter,
+ val encodings : List[CfuPluginEncoding] = null,
+ val stateAndIndexCsrOffset : Int = 0xBC0,
+ val statusCsrOffset : Int = 0x801,
+ val withEnable : Boolean = true) extends Plugin[VexRiscv]{
+ def p = busParameter
+
+ assert(p.CFU_INPUTS <= 2)
+ assert(p.CFU_OUTPUTS == 1)
+// assert(p.CFU_FUNCTION_ID_W == 3)
+
+ var bus : CfuBus = null
+
+ lazy val forkStage = pipeline.execute
+ lazy val joinStage = pipeline.stages(Math.min(pipeline.stages.length - 1, pipeline.indexOf(forkStage) + stageCount))
+
+
+ val CFU_ENABLE = new Stageable(Bool()).setCompositeName(this, "CFU_ENABLE")
+ val CFU_IN_FLIGHT = new Stageable(Bool()).setCompositeName(this, "CFU_IN_FLIGHT")
+ val CFU_ENCODING = new Stageable(UInt(log2Up(encodings.size) bits)).setCompositeName(this, "CFU_ENCODING")
+ val CFU_INPUT_2_KIND = new Stageable(CfuPlugin.Input2Kind()).setCompositeName(this, "CFU_INPUT_2_KIND")
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ bus = master(CfuBus(p))
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+ decoderService.addDefault(CFU_ENABLE, False)
+
+ for((encoding, id) <- encodings.zipWithIndex){
+ var actions = List(
+ CFU_ENABLE -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> Bool(stageCount == 0),
+ BYPASSABLE_MEMORY_STAGE -> Bool(stageCount <= 1),
+ RS1_USE -> True,
+ CFU_ENCODING -> U(id),
+ CFU_INPUT_2_KIND -> encoding.input2Kind()
+ )
+
+ encoding.input2Kind match {
+ case CfuPlugin.Input2Kind.RS =>
+ actions :+= RS2_USE -> True
+ case CfuPlugin.Input2Kind.IMM_I =>
+ }
+
+ decoderService.add(
+ key = encoding.instruction,
+ values = actions
+ )
+ }
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+
+ val csr = pipeline plug new Area{
+ val factory = pipeline.service(classOf[CsrInterface])
+ val en = withEnable generate (Reg(Bool()) init(False))
+ if(withEnable) factory.rw(stateAndIndexCsrOffset, 31, en)
+
+ val stateId = Reg(UInt(log2Up(p.CFU_STATE_INDEX_NUM) bits)) init(0)
+ if(p.CFU_STATE_INDEX_NUM > 1) {
+ assert(stateAndIndexCsrOffset != -1, "CfuPlugin stateCsrIndex need to be set in the parameters")
+ factory.rw(stateAndIndexCsrOffset, 16, stateId)
+ }
+
+ val cfuIndex = Reg(UInt(p.CFU_CFU_ID_W bits)) init(0)
+ if(p.CFU_CFU_ID_W != 0){
+ factory.rw(stateAndIndexCsrOffset, 0, cfuIndex)
+ }
+ val status = p.CFU_WITH_STATUS generate new Area{
+ val CU, OP, FI, OF, SI, CI = RegInit(False)
+ val flags = List(CU, OP, FI, OF, SI, CI).reverse
+ factory.rw(statusCsrOffset, flags.zipWithIndex.map(_.swap) :_*)
+ factory.duringWrite(statusCsrOffset){
+ decode.arbitration.haltByOther := True //Handle CSRW to decode
+ }
+ }
+ }
+
+ if(withEnable) when(decode.input(CFU_ENABLE) && !csr.en){
+ pipeline.service(classOf[DecoderService]).forceIllegal()
+ }
+
+ forkStage plug new Area{
+ import forkStage._
+ val hazard = stages.dropWhile(_ != forkStage).tail.map(s => s.arbitration.isValid && s.input(HAS_SIDE_EFFECT)).orR
+ val scheduleWish = arbitration.isValid && input(CFU_ENABLE)
+ val schedule = scheduleWish && !hazard
+ arbitration.haltItself setWhen(scheduleWish && hazard)
+
+ val hold = RegInit(False) setWhen(schedule) clearWhen(bus.cmd.ready)
+ val fired = RegInit(False) setWhen(bus.cmd.fire) clearWhen(!arbitration.isStuck)
+ insert(CFU_IN_FLIGHT) := schedule || hold || fired
+
+ bus.cmd.valid := (schedule || hold) && !fired
+ arbitration.haltItself setWhen(bus.cmd.valid && !bus.cmd.ready)
+
+// bus.cmd.function_id := U(input(INSTRUCTION)(14 downto 12)).resized
+ val functionIdFromInstructinoWidth = encodings.map(_.functionIdWidth).max
+ val functionsIds = encodings.map(e => U(Cat(e.functionId.map(r => input(INSTRUCTION)(r))), functionIdFromInstructinoWidth bits))
+ bus.cmd.cfu_index := csr.cfuIndex
+ bus.cmd.state_index := csr.stateId
+ bus.cmd.function_id := functionsIds.read(input(CFU_ENCODING))
+ bus.cmd.reorder_id := 0
+ bus.cmd.request_id := 0
+ bus.cmd.raw_insn := input(INSTRUCTION).resized
+ if(p.CFU_INPUTS >= 1) bus.cmd.inputs(0) := input(RS1)
+ if(p.CFU_INPUTS >= 2) bus.cmd.inputs(1) := input(CFU_INPUT_2_KIND).mux(
+ CfuPlugin.Input2Kind.RS -> input(RS2),
+ CfuPlugin.Input2Kind.IMM_I -> IMM(input(INSTRUCTION)).h_sext
+ )
+ }
+
+ joinStage plug new Area{
+ import joinStage._
+
+ //If the CFU interface can produce a result combinatorialy and the fork stage isn't the same than the join stage
+ //Then it is required to add a buffer on rsp to not propagate the fork stage ready := False in the CPU pipeline.
+ val rsp = if(p.CFU_FLOW_RESP_READY_ALWAYS){
+ bus.rsp.toFlow.toStream.queueLowLatency(
+ size = stageCount + 1,
+ latency = 0
+ )
+ } else if(forkStage != joinStage && allowZeroLatency) {
+ bus.rsp.s2mPipe()
+ } else {
+ bus.rsp.combStage()
+ }
+
+ rsp.ready := False
+ when(input(CFU_IN_FLIGHT)){
+ arbitration.haltItself setWhen(!rsp.valid)
+ rsp.ready := !arbitration.isStuckByOthers
+ output(REGFILE_WRITE_DATA) := rsp.outputs(0)
+ if(p.CFU_WITH_STATUS) when(arbitration.isFiring){
+ switch(rsp.status) {
+ for (i <- 1 to 6) is(i) {
+ csr.status.flags(i-1) := True
+ }
+ }
+ }
+ }
+ }
+
+ pipeline.stages.drop(1).foreach(s => s.output(CFU_IN_FLIGHT) clearWhen(s.arbitration.isStuck))
+ addPrePopTask(() => stages.dropWhile(_ != memory).reverse.dropWhile(_ != joinStage).foreach(s => s.input(CFU_IN_FLIGHT).init(False)))
+ }
+}
+
+
+object CfuTest{
+
+// stageCount = 0,
+// allowZeroLatency = true,
+ def getCfuParameter() = CfuBusParameter(
+ CFU_VERSION = 0,
+ CFU_INTERFACE_ID_W = 0,
+ CFU_FUNCTION_ID_W = 3,
+ CFU_REORDER_ID_W = 0,
+ CFU_REQ_RESP_ID_W = 0,
+ CFU_INPUTS = 2,
+ CFU_INPUT_DATA_W = 32,
+ CFU_OUTPUTS = 1,
+ CFU_OUTPUT_DATA_W = 32,
+ CFU_FLOW_REQ_READY_ALWAYS = false,
+ CFU_FLOW_RESP_READY_ALWAYS = false
+ )
+}
+case class CfuTest() extends Component{
+ val io = new Bundle {
+ val bus = slave(CfuBus(CfuTest.getCfuParameter()))
+ }
+ io.bus.rsp.arbitrationFrom(io.bus.cmd)
+ io.bus.rsp.response_id := io.bus.cmd.request_id
+ io.bus.rsp.outputs(0) := ~(io.bus.cmd.inputs(0) & io.bus.cmd.inputs(1))
+}
+
+
+case class CfuBb(p : CfuBusParameter) extends BlackBox{
+ val io = new Bundle {
+ val clk, reset = in Bool()
+ val bus = slave(CfuBus(p))
+ }
+
+ mapCurrentClockDomain(io.clk, io.reset)
+}
+
+//case class CfuGray(p : CfuBusParameter) extends BlackBox{
+// val req_function_id = in Bits(p.CFU_FUNCTION_ID_W)
+// val req_data = in Bits(p.CFU_REQ_INPUTS)
+// val resp_data = in Bits(p.CFU_FUNCTION_ID_W)
+// input `CFU_FUNCTION_ID req_function_id,
+// input [CFU_REQ_INPUTS-1:0]`CFU_REQ_DATA req_data,
+// output [CFU_RESP_OUTPUTS-1:0]`CFU_RESP_DATA resp_data
+// io.bus.rsp.arbitrationFrom(io.bus.cmd)
+// io.bus.rsp.response_ok := True
+// io.bus.rsp.response_id := io.bus.cmd.request_id
+// io.bus.rsp.outputs(0) := ~(io.bus.cmd.inputs(0) & io.bus.cmd.inputs(1))
+//}
+
+
+case class CfuDecoder(p : CfuBusParameter,
+ mappings : Seq[AddressMapping],
+ pendingMax : Int = 3) extends Component{
+ val io = new Bundle {
+ val input = slave(CfuBus(p))
+ val outputs = Vec(master(CfuBus(p)), mappings.size)
+ }
+ val hasDefault = mappings.contains(DefaultMapping)
+ val logic = if(hasDefault && mappings.size == 1){
+ io.outputs(0) << io.input
+ } else new Area {
+ val hits = Vec(Bool, mappings.size)
+ for (portId <- 0 until mappings.length) yield {
+ val slaveBus = io.outputs(portId)
+ val memorySpace = mappings(portId)
+ val hit = hits(portId)
+ hit := (memorySpace match {
+ case DefaultMapping => !hits.filterNot(_ == hit).orR
+ case _ => memorySpace.hit(io.input.cmd.function_id)
+ })
+ slaveBus.cmd.valid := io.input.cmd.valid && hit
+ slaveBus.cmd.payload := io.input.cmd.payload.resized
+ }
+ val noHit = if (!hasDefault) !hits.orR else False
+ io.input.cmd.ready := (hits, io.outputs).zipped.map(_ && _.cmd.ready).orR || noHit
+
+ val rspPendingCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init(0)
+ rspPendingCounter := rspPendingCounter + U(io.input.cmd.fire) - U(io.input.rsp.fire)
+ val rspHits = RegNextWhen(hits, io.input.cmd.fire)
+ val rspPending = rspPendingCounter =/= 0
+ val rspNoHitValid = if (!hasDefault) !rspHits.orR else False
+ val rspNoHit = !hasDefault generate new Area{
+ val doIt = RegInit(False) clearWhen(io.input.rsp.fire) setWhen(io.input.cmd.fire && noHit)
+ val response_id = RegNextWhen(io.input.cmd.request_id, io.input.cmd.fire)
+ }
+
+ io.input.rsp.valid := io.outputs.map(_.rsp.valid).orR || (rspPending && rspNoHitValid)
+ io.input.rsp.payload := io.outputs.map(_.rsp.payload).read(OHToUInt(rspHits))
+ if(!hasDefault) when(rspNoHit.doIt) {
+ io.input.rsp.valid := True
+ io.input.rsp.response_id := rspNoHit.response_id
+ }
+ for(output <- io.outputs) output.rsp.ready := io.input.rsp.ready
+
+ val cmdWait = (rspPending && (hits =/= rspHits || rspNoHitValid)) || rspPendingCounter === pendingMax
+ when(cmdWait) {
+ io.input.cmd.ready := False
+ io.outputs.foreach(_.cmd.valid := False)
+ }
+ }
+}