402 lines
17 KiB
Scala
402 lines
17 KiB
Scala
package chipyard.harness
|
|
|
|
import chisel3._
|
|
import chisel3.util._
|
|
import chisel3.experimental.{Analog, BaseModule, DataMirror, Direction}
|
|
|
|
import org.chipsalliance.cde.config.{Field, Config, Parameters}
|
|
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImpLike}
|
|
import freechips.rocketchip.amba.axi4.{AXI4Bundle, AXI4SlaveNode, AXI4MasterNode, AXI4EdgeParameters}
|
|
import freechips.rocketchip.devices.debug._
|
|
import freechips.rocketchip.jtag.{JTAGIO}
|
|
import freechips.rocketchip.system.{SimAXIMem}
|
|
import freechips.rocketchip.subsystem._
|
|
import freechips.rocketchip.util._
|
|
|
|
import sifive.blocks.devices.gpio._
|
|
import sifive.blocks.devices.uart._
|
|
import sifive.blocks.devices.spi._
|
|
|
|
import barstools.iocell.chisel._
|
|
|
|
import testchipip._
|
|
|
|
import chipyard._
|
|
import chipyard.clocking.{HasChipyardPRCI, ClockWithFreq}
|
|
import chipyard.iobinders.{GetSystemParameters, JTAGChipIO}
|
|
|
|
import tracegen.{TraceGenSystemModuleImp}
|
|
import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly}
|
|
|
|
import scala.reflect.{ClassTag}
|
|
|
|
case object HarnessBinders extends Field[HarnessBinderMap](HarnessBinderMapDefault)
|
|
|
|
object ApplyHarnessBinders {
|
|
def apply(th: HasHarnessInstantiators, sys: LazyModule, portMap: Map[String, Seq[Data]])(implicit p: Parameters): Unit = {
|
|
val pm = portMap.withDefaultValue(Nil)
|
|
p(HarnessBinders).foreach { case (s, f) =>
|
|
f(sys, th, pm(s))
|
|
f(sys.module, th, pm(s))
|
|
}
|
|
}
|
|
}
|
|
|
|
// The ClassTags here are necessary to overcome issues arising from type erasure
|
|
class HarnessBinder[T, S <: HasHarnessInstantiators, U <: Data](composer: ((T, S, Seq[U]) => Unit) => (T, S, Seq[U]) => Unit)(implicit systemTag: ClassTag[T], harnessTag: ClassTag[S], portTag: ClassTag[U]) extends Config((site, here, up) => {
|
|
case HarnessBinders => up(HarnessBinders, site) + (systemTag.runtimeClass.toString ->
|
|
((t: Any, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
|
val pts = ports.collect({case p: U => p})
|
|
require (pts.length == ports.length, s"Port type mismatch between IOBinder and HarnessBinder: ${portTag}")
|
|
val upfn = up(HarnessBinders, site)(systemTag.runtimeClass.toString)
|
|
(th, t) match {
|
|
case (th: S, system: T) => composer(upfn)(system, th, pts)
|
|
case _ =>
|
|
}
|
|
})
|
|
)
|
|
})
|
|
|
|
class OverrideHarnessBinder[T, S <: HasHarnessInstantiators, U <: Data](fn: => (T, S, Seq[U]) => Unit)
|
|
(implicit tag: ClassTag[T], thtag: ClassTag[S], ptag: ClassTag[U])
|
|
extends HarnessBinder[T, S, U]((upfn: (T, S, Seq[U]) => Unit) => fn)
|
|
|
|
class ComposeHarnessBinder[T, S <: HasHarnessInstantiators, U <: Data](fn: => (T, S, Seq[U]) => Unit)
|
|
(implicit tag: ClassTag[T], thtag: ClassTag[S], ptag: ClassTag[U])
|
|
extends HarnessBinder[T, S, U]((upfn: (T, S, Seq[U]) => Unit) => (t, th, p) => {
|
|
upfn(t, th, p)
|
|
fn(t, th, p)
|
|
})
|
|
|
|
|
|
class WithGPIOTiedOff extends OverrideHarnessBinder({
|
|
(system: HasPeripheryGPIOModuleImp, th: HasHarnessInstantiators, ports: Seq[Analog]) => {
|
|
ports.foreach { _ <> AnalogConst(0) }
|
|
}
|
|
})
|
|
|
|
// DOC include start: WithUARTAdapter
|
|
class WithUARTAdapter extends OverrideHarnessBinder({
|
|
(system: HasPeripheryUARTModuleImp, th: HasHarnessInstantiators, ports: Seq[UARTPortIO]) => {
|
|
UARTAdapter.connect(ports)(system.p)
|
|
}
|
|
})
|
|
// DOC include end: WithUARTAdapter
|
|
|
|
class WithSimSPIFlashModel(rdOnly: Boolean = true) extends OverrideHarnessBinder({
|
|
(system: HasPeripherySPIFlashModuleImp, th: HasHarnessInstantiators, ports: Seq[SPIChipIO]) => {
|
|
SimSPIFlashModel.connect(ports, th.harnessBinderReset, rdOnly)(system.p)
|
|
}
|
|
})
|
|
|
|
class WithSimBlockDevice extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryBlockDevice, th: HasHarnessInstantiators, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
|
implicit val p: Parameters = GetSystemParameters(system)
|
|
ports.map { b => SimBlockDevice.connect(b.clock, th.harnessBinderReset.asBool, Some(b.bits)) }
|
|
}
|
|
})
|
|
|
|
class WithBlockDeviceModel extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryBlockDevice, th: HasHarnessInstantiators, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
|
implicit val p: Parameters = GetSystemParameters(system)
|
|
ports.map { b => BlockDeviceModel.connect(Some(b.bits)) }
|
|
}
|
|
})
|
|
|
|
class WithLoopbackNIC extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryIceNIC, th: HasHarnessInstantiators, ports: Seq[ClockedIO[NICIOvonly]]) => {
|
|
implicit val p: Parameters = GetSystemParameters(system)
|
|
ports.map { n => NicLoopback.connect(Some(n.bits), p(NICKey)) }
|
|
}
|
|
})
|
|
|
|
class WithSimNetwork extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryIceNIC, th: BaseModule with HasHarnessInstantiators, ports: Seq[ClockedIO[NICIOvonly]]) => {
|
|
implicit val p: Parameters = GetSystemParameters(system)
|
|
ports.map { n => SimNetwork.connect(Some(n.bits), n.clock, th.harnessBinderReset.asBool) }
|
|
}
|
|
})
|
|
|
|
class WithSimAXIMem extends OverrideHarnessBinder({
|
|
(system: CanHaveMasterAXI4MemPort, th: HasHarnessInstantiators, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
|
val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
|
|
(ports zip system.memAXI4Node.edges.in).map { case (port, edge) =>
|
|
val mem = LazyModule(new SimAXIMem(edge, size=p(ExtMem).get.master.size)(p))
|
|
Module(mem.module).suggestName("mem")
|
|
mem.io_axi4.head <> port.bits
|
|
}
|
|
}
|
|
})
|
|
|
|
class WithSimAXIMemOverSerialTL extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryTLSerial, th: HasHarnessInstantiators, ports: Seq[ClockedIO[SerialIO]]) => {
|
|
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
|
|
|
p(SerialTLKey).map({ sVal =>
|
|
val serialTLManagerParams = sVal.serialTLManagerParams.get
|
|
val axiDomainParams = serialTLManagerParams.axiMemOverSerialTLParams.get
|
|
require(serialTLManagerParams.isMemoryDevice)
|
|
|
|
val memFreq = axiDomainParams.getMemFrequency(system.asInstanceOf[HasTileLinkLocations])
|
|
|
|
ports.map({ port =>
|
|
// DOC include start: HarnessClockInstantiatorEx
|
|
val memOverSerialTLClock = th.harnessClockInstantiator.requestClockHz("mem_over_serial_tl_clock", memFreq)
|
|
val serial_bits = port.bits
|
|
port.clock := th.harnessBinderClock
|
|
val harnessMultiClockAXIRAM = TSIHarness.connectMultiClockAXIRAM(
|
|
system.serdesser.get,
|
|
serial_bits,
|
|
memOverSerialTLClock,
|
|
th.harnessBinderReset)
|
|
// DOC include end: HarnessClockInstantiatorEx
|
|
val success = SimTSI.connect(Some(harnessMultiClockAXIRAM.module.io.tsi), th.harnessBinderClock, th.harnessBinderReset.asBool)
|
|
when (success) { th.success := true.B }
|
|
|
|
// connect SimDRAM from the AXI port coming from the harness multi clock axi ram
|
|
(harnessMultiClockAXIRAM.mem_axi4.get zip harnessMultiClockAXIRAM.memNode.get.edges.in).map { case (axi_port, edge) =>
|
|
val memSize = serialTLManagerParams.memParams.size
|
|
val memBase = serialTLManagerParams.memParams.base
|
|
val lineSize = p(CacheBlockBytes)
|
|
val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), memBase, edge.bundle)).suggestName("simdram")
|
|
mem.io.axi <> axi_port.bits
|
|
mem.io.clock := axi_port.clock
|
|
mem.io.reset := axi_port.reset
|
|
}
|
|
})
|
|
})
|
|
}
|
|
})
|
|
|
|
class WithBlackBoxSimMem(additionalLatency: Int = 0) extends OverrideHarnessBinder({
|
|
(system: CanHaveMasterAXI4MemPort, th: HasHarnessInstantiators, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
|
val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
|
|
(ports zip system.memAXI4Node.edges.in).map { case (port, edge) =>
|
|
// TODO FIX: This currently makes each SimDRAM contain the entire memory space
|
|
val memSize = p(ExtMem).get.master.size
|
|
val memBase = p(ExtMem).get.master.base
|
|
val lineSize = p(CacheBlockBytes)
|
|
val clockFreq = p(MemoryBusKey).dtsFrequency.get
|
|
val mem = Module(new SimDRAM(memSize, lineSize, clockFreq, memBase, edge.bundle)).suggestName("simdram")
|
|
mem.io.axi <> port.bits
|
|
// Bug in Chisel implementation. See https://github.com/chipsalliance/chisel3/pull/1781
|
|
def Decoupled[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
|
|
require(DataMirror.directionOf(irr.bits) == Direction.Output, "Only safe to cast produced Irrevocable bits to Decoupled.")
|
|
val d = Wire(new DecoupledIO(chiselTypeOf(irr.bits)))
|
|
d.bits := irr.bits
|
|
d.valid := irr.valid
|
|
irr.ready := d.ready
|
|
d
|
|
}
|
|
if (additionalLatency > 0) {
|
|
withClockAndReset (port.clock, port.reset) {
|
|
mem.io.axi.aw <> (0 until additionalLatency).foldLeft(Decoupled(port.bits.aw))((t, _) => Queue(t, 1, pipe=true))
|
|
mem.io.axi.w <> (0 until additionalLatency).foldLeft(Decoupled(port.bits.w ))((t, _) => Queue(t, 1, pipe=true))
|
|
port.bits.b <> (0 until additionalLatency).foldLeft(Decoupled(mem.io.axi.b))((t, _) => Queue(t, 1, pipe=true))
|
|
mem.io.axi.ar <> (0 until additionalLatency).foldLeft(Decoupled(port.bits.ar))((t, _) => Queue(t, 1, pipe=true))
|
|
port.bits.r <> (0 until additionalLatency).foldLeft(Decoupled(mem.io.axi.r))((t, _) => Queue(t, 1, pipe=true))
|
|
}
|
|
}
|
|
mem.io.clock := port.clock
|
|
mem.io.reset := port.reset
|
|
}
|
|
}
|
|
})
|
|
|
|
class WithSimAXIMMIO extends OverrideHarnessBinder({
|
|
(system: CanHaveMasterAXI4MMIOPort, th: HasHarnessInstantiators, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
|
val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
|
|
(ports zip system.mmioAXI4Node.edges.in).map { case (port, edge) =>
|
|
val mmio_mem = LazyModule(new SimAXIMem(edge, size = p(ExtBus).get.size)(p))
|
|
withClockAndReset(port.clock, port.reset) {
|
|
Module(mmio_mem.module).suggestName("mmio_mem")
|
|
}
|
|
mmio_mem.io_axi4.head <> port.bits
|
|
}
|
|
}
|
|
})
|
|
|
|
class WithTieOffInterrupts extends OverrideHarnessBinder({
|
|
(system: HasExtInterruptsModuleImp, th: HasHarnessInstantiators, ports: Seq[UInt]) => {
|
|
ports.foreach { _ := 0.U }
|
|
}
|
|
})
|
|
|
|
class WithTieOffL2FBusAXI extends OverrideHarnessBinder({
|
|
(system: CanHaveSlaveAXI4Port, th: HasHarnessInstantiators, ports: Seq[ClockedIO[AXI4Bundle]]) => {
|
|
ports.foreach({ p =>
|
|
p.bits := DontCare
|
|
p.bits.aw.valid := false.B
|
|
p.bits.w.valid := false.B
|
|
p.bits.b.ready := false.B
|
|
p.bits.ar.valid := false.B
|
|
p.bits.r.ready := false.B
|
|
})
|
|
}
|
|
})
|
|
|
|
class WithSimDebug extends OverrideHarnessBinder({
|
|
(system: HasPeripheryDebug, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
|
implicit val p: Parameters = GetSystemParameters(system)
|
|
ports.map {
|
|
case d: ClockedDMIIO =>
|
|
val dtm_success = WireInit(false.B)
|
|
when (dtm_success) { th.success := true.B }
|
|
val dtm = Module(new TestchipSimDTM).connect(th.harnessBinderClock, th.harnessBinderReset.asBool, d, dtm_success)
|
|
case j: JTAGChipIO =>
|
|
val dtm_success = WireInit(false.B)
|
|
when (dtm_success) { th.success := true.B }
|
|
val jtag_wire = Wire(new JTAGIO)
|
|
jtag_wire.TDO.data := j.TDO
|
|
jtag_wire.TDO.driven := true.B
|
|
j.TCK := jtag_wire.TCK
|
|
j.TMS := jtag_wire.TMS
|
|
j.TDI := jtag_wire.TDI
|
|
val jtag = Module(new SimJTAG(tickDelay=3))
|
|
jtag.connect(jtag_wire, th.harnessBinderClock, th.harnessBinderReset.asBool, ~(th.harnessBinderReset.asBool), dtm_success)
|
|
}
|
|
}
|
|
})
|
|
|
|
class WithTiedOffDebug extends OverrideHarnessBinder({
|
|
(system: HasPeripheryDebug, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
|
ports.map {
|
|
case j: JTAGChipIO =>
|
|
j.TCK := true.B.asClock
|
|
j.TMS := true.B
|
|
j.TDI := true.B
|
|
case d: ClockedDMIIO =>
|
|
d.dmi.req.valid := false.B
|
|
d.dmi.req.bits := DontCare
|
|
d.dmi.resp.ready := true.B
|
|
d.dmiClock := false.B.asClock
|
|
d.dmiReset := true.B
|
|
case a: ClockedAPBBundle =>
|
|
a.pready := false.B
|
|
a.pslverr := false.B
|
|
a.prdata := 0.U
|
|
a.pduser := DontCare
|
|
a.clock := false.B.asClock
|
|
a.reset := true.B.asAsyncReset
|
|
a.psel := false.B
|
|
a.penable := false.B
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
class WithSerialTLTiedOff extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryTLSerial, th: HasHarnessInstantiators, ports: Seq[ClockedIO[SerialIO]]) => {
|
|
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
|
ports.map({ port =>
|
|
val bits = port.bits
|
|
if (DataMirror.directionOf(port.clock) == Direction.Input) {
|
|
port.clock := false.B.asClock
|
|
}
|
|
port.bits.out.ready := false.B
|
|
port.bits.in.valid := false.B
|
|
port.bits.in.bits := DontCare
|
|
})
|
|
}
|
|
})
|
|
|
|
class WithSimTSIOverSerialTL extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryTLSerial, th: HasHarnessInstantiators, ports: Seq[ClockedIO[SerialIO]]) => {
|
|
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
|
ports.map({ port =>
|
|
val bits = port.bits
|
|
port.clock := th.harnessBinderClock
|
|
val ram = TSIHarness.connectRAM(system.serdesser.get, bits, th.harnessBinderReset)
|
|
val success = SimTSI.connect(Some(ram.module.io.tsi), th.harnessBinderClock, th.harnessBinderReset.asBool)
|
|
when (success) { th.success := true.B }
|
|
})
|
|
}
|
|
})
|
|
|
|
class WithSimUARTToUARTTSI extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryUARTTSI, th: HasHarnessInstantiators, ports: Seq[UARTTSIIO]) => {
|
|
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
|
require(ports.size <= 1)
|
|
ports.map { port => {
|
|
UARTAdapter.connect(Seq(port.uart),
|
|
baudrate=port.uartParams.initBaudRate,
|
|
clockFrequency=th.getHarnessBinderClockFreqHz.toInt,
|
|
forcePty=true)
|
|
assert(!port.dropped)
|
|
}}
|
|
}
|
|
})
|
|
|
|
class WithSimTSIToUARTTSI extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryUARTTSI, th: HasHarnessInstantiators, ports: Seq[UARTTSIIO]) => {
|
|
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
|
require(ports.size <= 1)
|
|
ports.map({ port =>
|
|
val freq = th.getHarnessBinderClockFreqHz.toInt
|
|
val uart_to_serial = Module(new UARTToSerial(freq, port.uartParams))
|
|
val serial_width_adapter = Module(new SerialWidthAdapter(8, TSI.WIDTH))
|
|
val success = SimTSI.connect(Some(TSIIO(serial_width_adapter.io.wide)), th.harnessBinderClock, th.harnessBinderReset)
|
|
when (success) { th.success := true.B }
|
|
assert(!uart_to_serial.io.dropped)
|
|
serial_width_adapter.io.narrow.flipConnect(uart_to_serial.io.serial)
|
|
uart_to_serial.io.uart.rxd := port.uart.txd
|
|
port.uart.rxd := uart_to_serial.io.uart.txd
|
|
})
|
|
}
|
|
})
|
|
|
|
|
|
class WithTraceGenSuccess extends OverrideHarnessBinder({
|
|
(system: TraceGenSystemModuleImp, th: HasHarnessInstantiators, ports: Seq[Bool]) => {
|
|
ports.map { p => when (p) { th.success := true.B } }
|
|
}
|
|
})
|
|
|
|
class WithSimDromajoBridge extends ComposeHarnessBinder({
|
|
(system: CanHaveTraceIOModuleImp, th: HasHarnessInstantiators, ports: Seq[TraceOutputTop]) => {
|
|
ports.map { p => p.traces.map(tileTrace => SimDromajoBridge(tileTrace)(system.p)) }
|
|
}
|
|
})
|
|
|
|
class WithCospike extends ComposeHarnessBinder({
|
|
(system: CanHaveTraceIOModuleImp, th: HasHarnessInstantiators, ports: Seq[TraceOutputTop]) => {
|
|
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
|
val chipyardSystem = system.asInstanceOf[ChipyardSystemModule[_]].outer.asInstanceOf[ChipyardSystem]
|
|
val tiles = chipyardSystem.tiles
|
|
val cfg = SpikeCosimConfig(
|
|
isa = tiles.headOption.map(_.isaDTS).getOrElse(""),
|
|
priv = tiles.headOption.map(t => if (t.usingUser) "MSU" else if (t.usingSupervisor) "MS" else "M").getOrElse(""),
|
|
mem0_base = p(ExtMem).map(_.master.base).getOrElse(BigInt(0)),
|
|
mem0_size = p(ExtMem).map(_.master.size).getOrElse(BigInt(0)),
|
|
pmpregions = tiles.headOption.map(_.tileParams.core.nPMPs).getOrElse(0),
|
|
nharts = tiles.size,
|
|
bootrom = chipyardSystem.bootROM.map(_.module.contents.toArray.mkString(" ")).getOrElse(""),
|
|
has_dtm = p(ExportDebug).protocols.contains(DMI) // assume that exposing clockeddmi means we will connect SimDTM
|
|
)
|
|
ports.map { p => p.traces.zipWithIndex.map(t => SpikeCosim(t._1, t._2, cfg)) }
|
|
}
|
|
})
|
|
|
|
|
|
class WithCustomBootPinPlusArg extends OverrideHarnessBinder({
|
|
(system: CanHavePeripheryCustomBootPin, th: HasHarnessInstantiators, ports: Seq[Bool]) => {
|
|
val pin = PlusArg("custom_boot_pin", width=1)
|
|
ports.foreach(_ := pin)
|
|
}
|
|
})
|
|
|
|
|
|
class WithClockAndResetFromHarness extends OverrideHarnessBinder({
|
|
(system: HasChipyardPRCI, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
|
implicit val p = GetSystemParameters(system)
|
|
val clocks = ports.collect { case c: ClockWithFreq => c }
|
|
ports.map ({
|
|
case c: ClockWithFreq => {
|
|
val clock = th.harnessClockInstantiator.requestClockMHz(s"clock_${c.freqMHz.toInt}MHz", c.freqMHz)
|
|
c.clock := clock
|
|
}
|
|
case r: AsyncReset => r := th.referenceReset.asAsyncReset
|
|
})
|
|
}
|
|
})
|