diff options
author | Friedrich Beckmann <friedrich.beckmann@hs-augsburg.de> | 2022-07-25 17:55:39 +0200 |
---|---|---|
committer | Friedrich Beckmann <friedrich.beckmann@hs-augsburg.de> | 2022-07-25 17:55:39 +0200 |
commit | 3fff6023602822531efdae30bc8ebf862967f1ef (patch) | |
tree | 16028102b8d850f8ab3115d28a8539ca6bc5f51d /VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala |
Initial Commit
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala')
-rw-r--r-- | VexRiscv/src/main/scala/vexriscv/plugin/CfuPlugin.scala | 357 |
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) + } + } +} |