Skip to content

Commit

Permalink
[t1rocket] implement t1rocket Testbench
Browse files Browse the repository at this point in the history
  • Loading branch information
sequencer committed Aug 26, 2024
1 parent 439b2d6 commit 4eb745f
Show file tree
Hide file tree
Showing 3 changed files with 596 additions and 0 deletions.
205 changes: 205 additions & 0 deletions t1rocketemu/src/AXI4SlaveAgent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022-2024 Jiuyang Liu <[email protected]>

package org.chipsalliance.t1.t1rocketemu.dpi

// TODO: upstream to AMBA as VIP
import chisel3._
import chisel3.util.circt.dpi.{RawClockedVoidFunctionCall, RawUnclockedNonVoidFunctionCall}
import chisel3.util.{isPow2, log2Ceil}
import org.chipsalliance.amba.axi4.bundle.{ARChannel, ARFlowControl, AWChannel, AWFlowControl, AXI4BundleParameter, AXI4ROIrrevocableVerilog, AXI4RWIrrevocableVerilog, AXI4WOIrrevocableVerilog, BChannel, BFlowControl, RChannel, RFlowControl, WChannel, WFlowControl}

case class AXI4SlaveAgentParameter(name: String, axiParameter: AXI4BundleParameter, outstanding: Int, readPayloadSize: Int, writePayloadSize: Int)

class AXI4SlaveAgentInterface(parameter: AXI4SlaveAgentParameter) extends Bundle {
val clock: Clock = Input(Clock())
val reset: Reset = Input(Reset())
val channelId: UInt = Input(Const(UInt(64.W)))
// don't issue read DPI
val gateRead: Bool = Input(Bool())
// don't issue write DPI
val gateWrite: Bool = Input(Bool())
val channel = Flipped(
org.chipsalliance.amba.axi4.bundle.verilog.irrevocable(parameter.axiParameter)
)
}

class WritePayload(length: Int, dataWidth: Int) extends Bundle {
val data = Vec(length, UInt(dataWidth.W))
// For dataWidth <= 8, align strb to u8 for a simple C-API
val strb = Vec(length, UInt(math.max(8, dataWidth / 8).W))
}

class ReadPayload(length: Int,dataWidth: Int) extends Bundle {
val data = Vec(length, UInt(dataWidth.W))
}

// consume transaction from DPI, drive RTL signal
class AXI4SlaveAgent(parameter: AXI4SlaveAgentParameter)
extends FixedIORawModule[AXI4SlaveAgentInterface](new AXI4SlaveAgentInterface(parameter)) {
dontTouch(io)
io.channel match {
case channel: AXI4RWIrrevocableVerilog =>
new WriteManager(channel)
new ReadManager(channel)
case channel: AXI4ROIrrevocableVerilog =>
new ReadManager(channel)
case channel: AXI4WOIrrevocableVerilog =>
new WriteManager(channel)
}

private class WriteManager(
channel: AWChannel with AWFlowControl with WChannel with WFlowControl with BChannel with BFlowControl) {
withClockAndReset(io.clock, io.reset) {
/** There is an aw in the register. */
val awIssued = RegInit(false.B)
/** There is a w in the register. */
val last = RegInit(false.B)

/** memory to store the write payload
* @todo limit the payload size based on the RTL configuration.
*/
val writePayload = RegInit(0.U.asTypeOf(new WritePayload(parameter.writePayloadSize, parameter.axiParameter.dataWidth)))
/** AWID, latch at AW fire, used at B fire. */
val awid = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWID)))
val awaddr = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWADDR)))
val awlen = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWLEN)))
val awsize = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWSIZE)))
val awburst = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWBURST)))
val awlock = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWLOCK)))
val awcache = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWCACHE)))
val awprot = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWPROT)))
val awqos = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWQOS)))
val awregion = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWREGION)))
val awuser = RegInit(0.U.asTypeOf(chiselTypeOf(channel.AWUSER)))

/** index the payload, used to write [[writePayload]] */
val writeIdx = RegInit(0.U.asTypeOf(UInt(8.W)))
val bFire = channel.BREADY && channel.BVALID
val awFire = channel.AWREADY && channel.AWVALID
val wLastFire = channel.WVALID && channel.WREADY && channel.WLAST
val awExist = channel.AWVALID || awIssued
val wExist = channel.WVALID && channel.WLAST || last

// AW
channel.AWREADY := !awIssued || (wExist && channel.BREADY)
when(channel.AWREADY && channel.AWVALID) {
awid := channel.AWID
awaddr := channel.AWADDR
awlen := channel.AWLEN
awsize := channel.AWSIZE
awburst := channel.AWBURST
awlock := channel.AWLOCK
awcache := channel.AWCACHE
awprot := channel.AWPROT
awqos := channel.AWQOS
awregion := channel.AWREGION
awuser := channel.AWUSER
}
when(awFire ^ bFire) {
awIssued := awFire
}

// W
val writePayloadUpdate = WireDefault(writePayload)
channel.WREADY := !last || (awExist && channel.BREADY)
when(channel.WVALID && channel.WREADY) {
writePayload.data(writeIdx) := channel.WDATA
writePayloadUpdate.data(writeIdx) := channel.WDATA
writePayload.strb(writeIdx) := channel.WSTRB.pad(writePayload.strb.getWidth)
writePayloadUpdate.strb(writeIdx) := channel.WSTRB.pad(writePayload.strb.getWidth)
writeIdx := writeIdx + 1.U
when(channel.WLAST) {
writeIdx := 0.U
}
}
when(wLastFire ^ bFire) {
last := wLastFire
}

// B
channel.BVALID := awExist && wExist
channel.BID := Mux(awIssued, awid, channel.AWID)
channel.BRESP := 0.U(2.W) // OK
channel.BUSER := Mux(awIssued, awuser, channel.AWUSER)
when(channel.BVALID && channel.BREADY) {
RawClockedVoidFunctionCall(s"axi_write_${parameter.name}")(
io.clock,
when.cond && !io.gateWrite,
io.channelId,
// handle AW and W at same beat.
Mux(awIssued, awid.asTypeOf(UInt(64.W)), channel.AWID),
Mux(awIssued, awaddr.asTypeOf(UInt(64.W)), channel.AWADDR),
Mux(awIssued, awlen.asTypeOf(UInt(64.W)), channel.AWLEN),
Mux(awIssued, awsize.asTypeOf(UInt(64.W)), channel.AWSIZE),
Mux(awIssued, awburst.asTypeOf(UInt(64.W)), channel.AWBURST),
Mux(awIssued, awlock.asTypeOf(UInt(64.W)), channel.AWLOCK),
Mux(awIssued, awcache.asTypeOf(UInt(64.W)), channel.AWCACHE),
Mux(awIssued, awprot.asTypeOf(UInt(64.W)), channel.AWPROT),
Mux(awIssued, awqos.asTypeOf(UInt(64.W)), channel.AWQOS),
Mux(awIssued, awregion.asTypeOf(UInt(64.W)), channel.AWREGION),
writePayloadUpdate
)
}
}
}

private class ReadManager(channel: ARChannel with ARFlowControl with RChannel with RFlowControl) {
withClockAndReset(io.clock, io.reset) {
class CAMValue extends Bundle {
val arid = UInt(16.W)
val arlen = UInt(8.W)
val readPayload = new ReadPayload(parameter.readPayloadSize, parameter.axiParameter.dataWidth)
val readPayloadIndex = UInt(8.W)
val valid = Bool()
val user: UInt = UInt(channel.ARUSER.getWidth.W)
}
/** CAM to maintain order of read requests. This is maintained as FIFO. */
val cam: Vec[CAMValue] = RegInit(0.U.asTypeOf(Vec(parameter.outstanding, new CAMValue)))
require(isPow2(parameter.outstanding), "Need to handle pointers")
val arPtr = RegInit(0.U.asTypeOf(UInt(log2Ceil(parameter.outstanding).W)))
val rPtr = RegInit(0.U.asTypeOf(UInt(log2Ceil(parameter.outstanding).W)))

// AR
channel.ARREADY := !cam(arPtr).valid
when(channel.ARREADY && channel.ARVALID) {
cam(arPtr).arid := channel.ARID
cam(arPtr).arlen := channel.ARLEN
cam(arPtr).user := channel.ARUSER
cam(arPtr).readPayload := RawUnclockedNonVoidFunctionCall(s"axi_read_${parameter.name}", new ReadPayload(parameter.readPayloadSize, parameter.axiParameter.dataWidth))(
when.cond && !io.gateRead,
io.channelId,
channel.ARID.asTypeOf(UInt(64.W)),
channel.ARADDR.asTypeOf(UInt(64.W)),
channel.ARLEN.asTypeOf(UInt(64.W)),
channel.ARSIZE.asTypeOf(UInt(64.W)),
channel.ARBURST.asTypeOf(UInt(64.W)),
channel.ARLOCK.asTypeOf(UInt(64.W)),
channel.ARCACHE.asTypeOf(UInt(64.W)),
channel.ARPROT.asTypeOf(UInt(64.W)),
channel.ARQOS.asTypeOf(UInt(64.W)),
channel.ARREGION.asTypeOf(UInt(64.W))
)
cam(arPtr).readPayloadIndex := 0.U
cam(arPtr).valid := true.B
arPtr := arPtr + 1.U
}

// R
channel.RVALID := cam(rPtr).valid
channel.RID := cam(rPtr).arid
channel.RDATA := cam(rPtr).readPayload.data(cam(rPtr).readPayloadIndex)
channel.RRESP := 0.U // OK
channel.RLAST := (cam(rPtr).arlen === cam(rPtr).readPayloadIndex) && cam(rPtr).valid
channel.RUSER := cam(rPtr).user
when(channel.RREADY && channel.RVALID) {
// increase index
cam(rPtr).readPayloadIndex := cam(rPtr).readPayloadIndex + 1.U
when(channel.RLAST) {
cam(rPtr).valid := false.B
rPtr := rPtr + 1.U
}
}
}
}
}
74 changes: 74 additions & 0 deletions t1rocketemu/src/FPToIEEE.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2012-2014 The Regents of the University of California
// SPDX-FileCopyrightText: 2016-2017 SiFive, Inc
// SPDX-FileCopyrightText: 2024 Jiuyang Liu <[email protected]>
package org.chipsalliance.t1.t1rocketemu

import chisel3._
import chisel3.experimental.hierarchy.instantiable
import chisel3.experimental.{SerializableModule, SerializableModuleParameter}
import chisel3.util._
import org.chipsalliance.rocketv.{FPUHelper, FType}

object FPToIEEEParameter {
implicit def rwP: upickle.default.ReadWriter[FPToIEEEParameter] = upickle.default.macroRW[FPToIEEEParameter]
}

class FPToIEEEInput(fLen: Int) extends Bundle {
val typeTag = UInt(2.W)
val data = UInt((fLen + 1).W)
}

case class FPToIEEEParameter(
useAsyncReset: Boolean,
xLen: Int,
fLen: Int,
minFLen: Int)
extends SerializableModuleParameter
class FPToIEEEInterface(parameter: FPToIEEEParameter) extends Bundle {
val clock = Input(Clock())
val reset = Input(if (parameter.useAsyncReset) AsyncReset() else Bool())
val in = Flipped(Valid(new FPToIEEEInput(parameter.fLen)))
val out = Valid(UInt(parameter.fLen.W))
}

@instantiable
class FPToIEEE(val parameter: FPToIEEEParameter)
extends FixedIORawModule(new FPToIEEEInterface(parameter))
with SerializableModule[FPToIEEEParameter]
with ImplicitClock
with ImplicitReset {
override protected def implicitClock: Clock = io.clock
override protected def implicitReset: Reset = io.reset

val minFLen: Int = parameter.minFLen
val fLen: Int = parameter.fLen
val xLen: Int = parameter.xLen
val helper = new FPUHelper(minFLen, fLen, xLen)
val maxExpWidth = helper.maxExpWidth
val maxSigWidth = helper.maxSigWidth
val floatTypes = helper.floatTypes
val maxType = helper.maxType
val minXLen = helper.minXLen
val nIntTypes = helper.nIntTypes
def ieee(x: UInt, t: FType = maxType) = helper.ieee(x, t)

val in = io.in.bits
val valid = io.in.valid

def sextTo(x: UInt, n: Int): UInt = {
require(x.getWidth <= n)
if (x.getWidth == n) x
else Cat(Fill(n - x.getWidth, x(x.getWidth - 1)), x)
}

val store = VecInit(
floatTypes.map(t =>
if (t == FType.H) Fill(maxType.ieeeWidth / minXLen, sextTo(ieee(in.data)(15, 0), minXLen))
else Fill(maxType.ieeeWidth / t.ieeeWidth, ieee(in.data)(t.ieeeWidth - 1, 0))
)
)(in.typeTag)

io.out.valid := valid
io.out.bits := store
}
Loading

0 comments on commit 4eb745f

Please sign in to comment.