From f1b17b533bf1a9e30c2b18e4d290e18a625fdf8a Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Wed, 5 Apr 2023 18:37:35 -0700 Subject: [PATCH] Add examples of ChipLikeRocketConfig/FlatChipTop/FlatTestHarness --- .../main/scala/clocking/ClockBinders.scala | 11 +- .../src/main/scala/config/ChipConfigs.scala | 44 ++++++ .../src/main/scala/example/FlatChipTop.scala | 143 ++++++++++++++++++ .../main/scala/example/FlatTestHarness.scala | 83 ++++++++++ 4 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 generators/chipyard/src/main/scala/config/ChipConfigs.scala create mode 100644 generators/chipyard/src/main/scala/example/FlatChipTop.scala create mode 100644 generators/chipyard/src/main/scala/example/FlatTestHarness.scala diff --git a/generators/chipyard/src/main/scala/clocking/ClockBinders.scala b/generators/chipyard/src/main/scala/clocking/ClockBinders.scala index 01eb261c..6d0ace69 100644 --- a/generators/chipyard/src/main/scala/clocking/ClockBinders.scala +++ b/generators/chipyard/src/main/scala/clocking/ClockBinders.scala @@ -103,9 +103,14 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({ // For a real chip you should replace this ClockSourceAtFreqFromPlusArg // with a blackbox of whatever PLL is being integrated - val fakeClockSource = Module(new ClockSourceAtFreqFromPlusArg("pll_freq_mhz")) - fakeClockSource.io.power := pllCtrlSink.in(0)._1.power - fakeClockSource.io.gate := pllCtrlSink.in(0)._1.gate + val fake_pll = Module(new ClockSourceAtFreqFromPlusArg("pll_freq_mhz")) + fake_pll.io.power := pllCtrlSink.in(0)._1.power + fake_pll.io.gate := pllCtrlSink.in(0)._1.gate + + pllClockSource.out.unzip._1.map { o => + o.clock := fake_pll.io.clk + o.reset := reset_wire + } (Seq(clock_io, reset_io), clockIOCell ++ resetIOCell) } diff --git a/generators/chipyard/src/main/scala/config/ChipConfigs.scala b/generators/chipyard/src/main/scala/config/ChipConfigs.scala new file mode 100644 index 00000000..1ed215f6 --- /dev/null +++ b/generators/chipyard/src/main/scala/config/ChipConfigs.scala @@ -0,0 +1,44 @@ +package chipyard + +import freechips.rocketchip.config.{Config} +import freechips.rocketchip.diplomacy._ + +// A simple config demonstrating how to set up a basic chip in Chipyard +class ChipLikeRocketConfig extends Config( + //================================== + // Set up TestHarness + //================================== + new chipyard.WithAbsoluteFreqHarnessClockInstantiator ++ // use absolute frequencies for simulations in the harness + // NOTE: This only simulates properly in VCS + + //================================== + // Set up tiles + //================================== + new freechips.rocketchip.subsystem.WithAsynchronousRocketTiles(3, 3) ++ // Add rational crossings between RocketTile and uncore + new freechips.rocketchip.subsystem.WithNBigCores(4) ++ // quad-core (4 RocketTiles) + + //================================== + // Set up I/O + //================================== + new testchipip.WithSerialTLWidth(4) ++ + new chipyard.harness.WithSimAXIMemOverSerialTL ++ // Attach fast SimDRAM to TestHarness + new chipyard.config.WithSerialTLBackingMemory ++ // Backing memory is over serial TL protocol + new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 4L) ++ // 4GB max external memory + + //================================== + // Set up clock./reset + //================================== + new chipyard.clocking.WithPLLSelectorDividerClockGenerator ++ // Use a PLL-based clock selector/divider generator structure + + // Create two clock groups, uncore and fbus, in addition to the tile clock groups + new chipyard.clocking.WithClockGroupsCombinedByName("uncore", "implicit", "sbus", "mbus", "cbus", "system_bus") ++ + new chipyard.clocking.WithClockGroupsCombinedByName("fbus", "fbus", "pbus") ++ + + // Set up the crossings + new chipyard.config.WithCbusToPbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between PBUS and CBUS + new chipyard.config.WithSbusToMbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossings between backside of L2 and MBUS + new testchipip.WithAsynchronousSerialSlaveCrossing ++ // Add Async crossing between serial and MBUS. Its master-side is tied to the FBUS + new testchipip.WithSerialTLAsyncResetQueue ++ // Add Async reset queue to block ready while in reset + + new chipyard.config.AbstractConfig) + diff --git a/generators/chipyard/src/main/scala/example/FlatChipTop.scala b/generators/chipyard/src/main/scala/example/FlatChipTop.scala new file mode 100644 index 00000000..97ac5032 --- /dev/null +++ b/generators/chipyard/src/main/scala/example/FlatChipTop.scala @@ -0,0 +1,143 @@ +package chipyard.example + + +import chisel3._ +import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.prci._ +import freechips.rocketchip.util._ +import freechips.rocketchip.devices.debug.{ExportDebug, JtagDTMKey, Debug} +import freechips.rocketchip.tilelink.{TLBuffer} +import chipyard.{BuildSystem, DigitalTop} +import chipyard.clocking._ +import chipyard.iobinders.{IOCellKey, JTAGChipIO} +import barstools.iocell.chisel._ + + +// This "FlatChipTop" uses no IOBinders, so all the IO have +// to be explicitly constructed. +// This only supports the base "DigitalTop" +class FlatChipTop(implicit p: Parameters) extends LazyModule { + override lazy val desiredName = "ChipTop" + val system = LazyModule(p(BuildSystem)(p)).suggestName("system").asInstanceOf[DigitalTop] + + //======================== + // Diplomatic clock stuff + //======================== + val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock")))) + system.connectImplicitClockSinkNode(implicitClockSinkNode) + + val tlbus = system.locateTLBusWrapper(system.prciParams.slaveWhere) + val baseAddress = system.prciParams.baseAddress + val clockDivider = system.prci_ctrl_domain { LazyModule(new TLClockDivider (baseAddress + 0x20000, tlbus.beatBytes)) } + val clockSelector = system.prci_ctrl_domain { LazyModule(new TLClockSelector(baseAddress + 0x30000, tlbus.beatBytes)) } + val pllCtrl = system.prci_ctrl_domain { LazyModule(new FakePLLCtrl (baseAddress + 0x40000, tlbus.beatBytes)) } + + tlbus.toVariableWidthSlave(Some("clock-div-ctrl")) { clockDivider.tlNode := TLBuffer() } + tlbus.toVariableWidthSlave(Some("clock-sel-ctrl")) { clockSelector.tlNode := TLBuffer() } + tlbus.toVariableWidthSlave(Some("pll-ctrl")) { pllCtrl.tlNode := TLBuffer() } + + system.allClockGroupsNode := clockDivider.clockNode := clockSelector.clockNode + + // Connect all other requested clocks + val slowClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + val pllClockSource = ClockSourceNode(Seq(ClockSourceParameters())) + + // The order of the connections to clockSelector.clockNode configures what + clockSelector.clockNode := slowClockSource + clockSelector.clockNode := pllClockSource + + val pllCtrlSink = BundleBridgeSink[FakePLLCtrlBundle]() + pllCtrlSink := pllCtrl.ctrlNode + + val debugClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters())) + debugClockSinkNode := system.locateTLBusWrapper(p(ExportDebug).slaveWhere).fixedClockNode + def debugClockBundle = debugClockSinkNode.in.head._1 + + override lazy val module = new FlatChipTopImpl + class FlatChipTopImpl extends LazyRawModuleImp(this) { + //========================= + // Clock/reset + //========================= + val implicit_clock = implicitClockSinkNode.in.head._1.clock + val implicit_reset = implicitClockSinkNode.in.head._1.reset + system.module match { case l: LazyModuleImp => { + l.clock := implicit_clock + l.reset := implicit_reset + }} + + val clock_wire = Wire(Input(new ClockWithFreq(80))) + val reset_wire = Wire(Input(AsyncReset())) + val (clock_pad, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey)) + val (reset_pad, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey)) + + slowClockSource.out.unzip._1.map { o => + o.clock := clock_wire.clock + o.reset := reset_wire + } + + // For a real chip you should replace this ClockSourceAtFreqFromPlusArg + // with a blackbox of whatever PLL is being integrated + val fake_pll = Module(new ClockSourceAtFreqFromPlusArg("pll_freq_mhz")) + fake_pll.io.power := pllCtrlSink.in(0)._1.power + fake_pll.io.gate := pllCtrlSink.in(0)._1.gate + + pllClockSource.out.unzip._1.map { o => + o.clock := fake_pll.io.clk + o.reset := reset_wire + } + + //========================= + // Custom Boot + //========================= + val (custom_boot_pad, customBootIOCell) = IOCell.generateIOFromSignal(system.custom_boot_pin.get.getWrappedValue, "custom_boot", p(IOCellKey)) + + //========================= + // Serialized TileLink + //========================= + val (serial_tl_pad, serialTLIOCells) = IOCell.generateIOFromSignal(system.serial_tl.get.getWrappedValue, "serial_tl", p(IOCellKey)) + + //========================= + // JTAG/Debug + //========================= + val debug = system.module.debug.get + // We never use the PSDIO, so tie it off on-chip + system.module.psd.psd.foreach { _ <> 0.U.asTypeOf(new PSDTestMode) } + system.module.resetctrl.map { rcio => rcio.hartIsInReset.map { _ := false.B } } + + // Tie off extTrigger + debug.extTrigger.foreach { t => + t.in.req := false.B + t.out.ack := t.out.req + } + // Tie off disableDebug + debug.disableDebug.foreach { d => d := false.B } + // Drive JTAG on-chip IOs + debug.systemjtag.map { j => + j.reset := ResetCatchAndSync(j.jtag.TCK, debugClockBundle.reset.asBool) + j.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W) + j.part_number := p(JtagDTMKey).idcodePartNum.U(16.W) + j.version := p(JtagDTMKey).idcodeVersion.U(4.W) + } + + Debug.connectDebugClockAndReset(Some(debug), debugClockBundle.clock) + + // Add IOCells for the DMI/JTAG/APB ports + require(!debug.clockeddmi.isDefined) + require(!debug.apb.isDefined) + val (jtag_pad, jtagIOCells) = debug.systemjtag.map { j => + val jtag_wire = Wire(new JTAGChipIO) + j.jtag.TCK := jtag_wire.TCK + j.jtag.TMS := jtag_wire.TMS + j.jtag.TDI := jtag_wire.TDI + jtag_wire.TDO := j.jtag.TDO.data + IOCell.generateIOFromSignal(jtag_wire, "jtag", p(IOCellKey), abstractResetAsAsync = true) + }.get + + //========================== + // UART + //========================== + require(system.uarts.size == 1) + val (uart_pad, uartIOCells) = IOCell.generateIOFromSignal(system.module.uart.head, "uart_0", p(IOCellKey)) + } +} diff --git a/generators/chipyard/src/main/scala/example/FlatTestHarness.scala b/generators/chipyard/src/main/scala/example/FlatTestHarness.scala new file mode 100644 index 00000000..a45a4a3b --- /dev/null +++ b/generators/chipyard/src/main/scala/example/FlatTestHarness.scala @@ -0,0 +1,83 @@ +package chipyard.example + +import chisel3._ + +import scala.collection.mutable.{ArrayBuffer, LinkedHashMap} + +import freechips.rocketchip.config.{Field, Parameters} +import freechips.rocketchip.diplomacy.{LazyModule} +import freechips.rocketchip.prci.{ClockSourceAtFreqFromPlusArg, ClockBundle, ClockBundleParameters} +import freechips.rocketchip.util.{PlusArg} +import freechips.rocketchip.subsystem.{CacheBlockBytes} +import freechips.rocketchip.devices.debug.{SimJTAG} +import freechips.rocketchip.jtag.{JTAGIO} +import testchipip.{SerialTLKey, SerialAdapter, UARTAdapter, SimDRAM} +import chipyard.{BuildTop} + +// A "flat" TestHarness that doesn't use IOBinders +// use with caution. +// This example is hard-coded to work only for FlatChipTop, and the ChipLikeRocketConfig +class FlatTestHarness(implicit val p: Parameters) extends Module { + val io = IO(new Bundle { + val success = Output(Bool()) + }) + + // This only works with FlatChipTop + val lazyDut = LazyModule(new FlatChipTop).suggestName("chiptop") + val dut = Module(lazyDut.module) + + // Clock + val clock_source = Module(new ClockSourceAtFreqFromPlusArg("slow_clk_freq_mhz")) + clock_source.io.power := true.B + clock_source.io.gate := false.B + dut.clock_pad.clock := clock_source.io.clk + + // Reset + dut.reset_pad := reset.asAsyncReset + + // Custom boot + dut.custom_boot_pad := PlusArg("custom_boot_pin", width=1) + + // Serialized TL + val sVal = p(SerialTLKey).get + require(sVal.axiMemOverSerialTLParams.isDefined) + require(sVal.isMemoryDevice) + val axiDomainParams = sVal.axiMemOverSerialTLParams.get + val memFreq = axiDomainParams.getMemFrequency(lazyDut.system) + + withClockAndReset(clock, reset) { + val memOverSerialTLClockBundle = Wire(new ClockBundle(ClockBundleParameters())) + memOverSerialTLClockBundle.clock := clock + memOverSerialTLClockBundle.reset := reset + val serial_bits = SerialAdapter.asyncQueue(dut.serial_tl_pad, clock, reset) + val harnessMultiClockAXIRAM = SerialAdapter.connectHarnessMultiClockAXIRAM( + lazyDut.system.serdesser.get, + serial_bits, + memOverSerialTLClockBundle, + reset) + io.success := SerialAdapter.connectSimSerial(harnessMultiClockAXIRAM.module.io.tsi_ser, clock, reset) + + // connect SimDRAM from the AXI port coming from the harness multi clock axi ram + (harnessMultiClockAXIRAM.mem_axi4 zip harnessMultiClockAXIRAM.memNode.edges.in).map { case (axi_port, edge) => + val memSize = sVal.memParams.size + val lineSize = p(CacheBlockBytes) + val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), edge.bundle)).suggestName("simdram") + mem.io.axi <> axi_port.bits + mem.io.clock := axi_port.clock + mem.io.reset := axi_port.reset + } + } + + // JTAG + val jtag_wire = Wire(new JTAGIO) + jtag_wire.TDO.data := dut.jtag_pad.TDO + jtag_wire.TDO.driven := true.B + dut.jtag_pad.TCK := jtag_wire.TCK + dut.jtag_pad.TMS := jtag_wire.TMS + dut.jtag_pad.TDI := jtag_wire.TDI + val dtm_success = WireInit(false.B) + val jtag = Module(new SimJTAG(tickDelay=3)).connect(jtag_wire, clock, reset.asBool, ~(reset.asBool), dtm_success) + + // UART + UARTAdapter.connect(Seq(dut.uart_pad)) +}