diff --git a/.github/scripts/defaults.sh b/.github/scripts/defaults.sh index 1028960e..08637428 100755 --- a/.github/scripts/defaults.sh +++ b/.github/scripts/defaults.sh @@ -34,7 +34,7 @@ grouping["group-accels"]="chipyard-mempress chipyard-sha3 chipyard-hwacha chipya grouping["group-constellation"]="chipyard-constellation" grouping["group-tracegen"]="tracegen tracegen-boom" grouping["group-other"]="icenet testchipip constellation rocketchip-amba rocketchip-tlsimple rocketchip-tlwidth rocketchip-tlxbar" -grouping["group-fpga"]="arty vcu118 vc707 arty100t" +grouping["group-fpga"]="arty arty100t nexysvideo vc707 vcu118" # key value store to get the build strings declare -A mapping @@ -79,6 +79,7 @@ mapping["rocketchip-tlwidth"]="SUB_PROJECT=rocketchip CONFIG=TLWidthUnitTestConf mapping["rocketchip-tlxbar"]="SUB_PROJECT=rocketchip CONFIG=TLXbarUnitTestConfig" mapping["arty"]="SUB_PROJECT=arty verilog" -mapping["vcu118"]="SUB_PROJECT=vcu118 verilog" -mapping["vc707"]="SUB_PROJECT=vc707 verilog" mapping["arty100t"]="SUB_PROJECT=arty100t verilog" +mapping["nexysvideo"]="SUB_PROJECT=nexysvideo verilog" +mapping["vc707"]="SUB_PROJECT=vc707 verilog" +mapping["vcu118"]="SUB_PROJECT=vcu118 verilog" diff --git a/docs/Customization/IOBinders.rst b/docs/Customization/IOBinders.rst index d3626a03..d7e65223 100644 --- a/docs/Customization/IOBinders.rst +++ b/docs/Customization/IOBinders.rst @@ -13,7 +13,7 @@ The ``IOBinder`` functions are responsible for instantiating IO cells and IOPort For example, the ``WithUARTIOCells`` IOBinder will, for any ``System`` that might have UART ports (``HasPeripheryUARTModuleImp``, generate ports within the ``ChipTop`` (``ports``) as well as IOCells with the appropriate type and direction (``cells2d``). This function returns a the list of generated ports, and the list of generated IOCells. The list of generated ports is passed to the ``HarnessBinders`` such that they can be connected to ``TestHarness`` devices. -.. literalinclude:: ../../generators/chipyard/src/main/scala/IOBinders.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/iobinders/IOBinders.scala :language: scala :start-after: DOC include start: WithUARTIOCells :end-before: DOC include end: WithUARTIOCells diff --git a/docs/Prototyping/Arty.rst b/docs/Prototyping/Arty.rst index 15347cf8..0575d811 100644 --- a/docs/Prototyping/Arty.rst +++ b/docs/Prototyping/Arty.rst @@ -2,18 +2,18 @@ Running a Design on Arty ======================== Arty100T Instructions ----------------------- +--------------------- -The default Xilinx Arty 100T harness uses a TSI-over-UART adapter to bringup the FPGA. -A user can connect to the Arty 100T target using a special ``uart_tsi`` program that opens a UART TTY. +The default Digilent Arty A7-100T harness uses a TSI-over-UART adapter to bringup the FPGA. +A user can connect to the Arty A7-100T target using a special ``uart_tsi`` program that opens a UART TTY. The interface for the ``uart_tsi`` program provides unique functionality that is useful for bringing up test chips. -To build the design, run: +To build the design (Vivado should be added to the ``PATH``), run: .. code-block:: shell cd fpga/ - make SUB_PROJECT=arty100t + make SUB_PROJECT=arty100t bitstream To build the UART-based frontend server, run: @@ -58,7 +58,7 @@ Run a design at a higher baud rate than default (For example, if ``CONFIG=UART92 Arty35T Legacy Instructions --------------------------- -The default Xilinx Arty 35T harness is setup to have JTAG available over the board's PMOD pins, and UART available over its FTDI serial USB adapter. The pin mappings for JTAG signals are identical to those described in the `SiFive Freedom E310 Arty 35T Getting Started Guide `__. +The default Digilent Arty A7-35T harness is setup to have JTAG available over the board's PMOD pins, and UART available over its FTDI serial USB adapter. The pin mappings for JTAG signals are identical to those described in the `SiFive Freedom E310 Arty 35T Getting Started Guide `__. The JTAG interface allows a user to connect to the core via OpenOCD, run bare-metal applications, and debug these applications with gdb. UART allows a user to communicate with the core over a USB connection and serial console running on a PC. To extend this design, a user may create their own Chipyard configuration and add the ``WithArtyTweaks`` located in ``fpga/src/main/scala/arty/Configs.scala``. Adding this config. fragment will enable and connect the JTAG and UART interfaces to your Chipyard design. @@ -68,13 +68,13 @@ Adding this config. fragment will enable and connect the JTAG and UART interface :start-after: DOC include start: AbstractArty and Rocket :end-before: DOC include end: AbstractArty and Rocket -Future peripherals to be supported include the Arty 35T SPI Flash EEPROM, and I2C/PWM/SPI over the Arty 35T GPIO pins. These peripherals are available as part of sifive-blocks. +Future peripherals to be supported include the Arty A7-35T SPI Flash EEPROM, and I2C/PWM/SPI over the Arty A7-35T GPIO pins. These peripherals are available as part of sifive-blocks. Brief Implementation Description and Guidance for Adding/Changing Xilinx Collateral ----------------------------------------------------------------------------------- -Like the VCU118, the basis for the Arty 35T design is the creation of a special test harness that connects the external IO (which exist as Xilinx IP blackboxes) to the Chipyard design. -This is done with the ``ArtyTestHarness`` in the basic default Arty 35T target. However, unlike the ``VCU118TestHarness``, the ``ArtyTestHarness`` uses no ``Overlays``, and instead directly connects chip top IO to the ports of the external IO blackboxes, using functions such as ``IOBUF`` provided by ``fpga-shells``. -Unlike the VCU118 and other more complicated test harnesses, the Arty 35T Vivado collateral is not generated by ``Overlays``, but rather are a static collection of ``create_ip`` and ``set_properties`` statements located in the files within ``fpga/fpga-shells/xilinx/arty/tcl`` and ``fpga/fpga-shells/xilinx/arty/constraints``. +Like the VCU118, the basis for the Arty A7-35T design is the creation of a special test harness that connects the external IO (which exist as Xilinx IP blackboxes) to the Chipyard design. +This is done with the ``ArtyTestHarness`` in the basic default Arty A7-35T target. However, unlike the ``VCU118TestHarness``, the ``ArtyTestHarness`` uses no ``Overlays``, and instead directly connects chip top IO to the ports of the external IO blackboxes, using functions such as ``IOBUF`` provided by ``fpga-shells``. +Unlike the VCU118 and other more complicated test harnesses, the Arty A7-35T Vivado collateral is not generated by ``Overlays``, but rather are a static collection of ``create_ip`` and ``set_properties`` statements located in the files within ``fpga/fpga-shells/xilinx/arty/tcl`` and ``fpga/fpga-shells/xilinx/arty/constraints``. If the user wishes to re-map FPGA package pins to different harness-level IO, this may be changed within ``fpga/fpga-shells/xilinx/arty/constraints/arty-master.xdc``. The addition of new Xilinx IP blocks may be done in ``fpga-shells/xilinx/arty/tcl/ip.tcl``, mapped to harness-level IOs in ``arty-master.xdc``, and wired through from the test harness to the chip top using ``HarnessBinders`` and ``IOBinders``. Examples of a simple ``IOBinder`` and ``HarnessBinder`` for routing signals (in this case the debug and JTAG resets) from the core to the test harness are the ``WithResetPassthrough`` and ``WithArtyResetHarnessBinder``. diff --git a/docs/Prototyping/General.rst b/docs/Prototyping/General.rst index 2b7a7332..051cbcf8 100644 --- a/docs/Prototyping/General.rst +++ b/docs/Prototyping/General.rst @@ -2,7 +2,7 @@ General Setup and Usage ============================== Sources ---------------------------- +------- All FPGA prototyping-related collateral and sources are located in the ``fpga`` top-level Chipyard directory. This includes the ``fpga-shells`` submodule and the ``src`` directory that hold both Scala, TCL and other collateral. diff --git a/docs/Prototyping/NexysVideo.rst b/docs/Prototyping/NexysVideo.rst new file mode 100644 index 00000000..773084b9 --- /dev/null +++ b/docs/Prototyping/NexysVideo.rst @@ -0,0 +1,49 @@ +Running a Design on Nexys Video +=============================== + +Nexys Video Instructions +------------------------ + +The default Digilent Nexys Video harness uses a TSI-over-UART adapter to bringup the FPGA. +A user can connect to the Nexys Video target using a special ``uart_tsi`` program that opens a UART TTY. +The interface for the ``uart_tsi`` program provides unique functionality that is useful for bringing up test chips. + +To build the design (Vivado should be added to the ``PATH``), run: + +.. code-block:: shell + + cd fpga/ + make SUB_PROJECT=nexysvideo bitstream + +To build the UART-based frontend server, run: + +.. code-block:: shell + + cd generators/testchipip/uart_tsi + make + +After programming the bitstream, and connecting the Nexys Video's UART to a host PC via the USB cable, the ``uart_tsi`` program can be run to interact with the target. + +Running a program: + +.. code-block:: shell + + ./uart_tsi +tty=/dev/ttyUSBX dhrystone.riscv + +Probe an address on the target system: + +.. code-block:: shell + + ./uart_tsi +tty=/dev/ttyUSBX +init_read=0x10040 none + +Write some address before running a program: + +.. code-block:: shell + + ./uart_tsi +tty=/dev/ttyUSBX +init_write=0x80000000:0xdeadbeef none + +Self-check that binary loading proceeded correctly: + +.. code-block:: shell + + ./uart_tsi +tty=/dev/ttyUSBX +selfcheck dhrystone.riscv diff --git a/docs/Prototyping/index.rst b/docs/Prototyping/index.rst index ba0dff49..a2332a25 100644 --- a/docs/Prototyping/index.rst +++ b/docs/Prototyping/index.rst @@ -2,10 +2,10 @@ Prototyping Flow ================ Chipyard supports FPGA prototyping for local FPGAs supported by `fpga-shells `__. -This includes popular FPGAs such as the Xilinx VCU118 and the Xilinx Arty 35T board. +This includes popular FPGAs such as the Xilinx VCU118 and the Digilent Arty A7-35T/A7-100T board. -.. Note:: While ``fpga-shells`` provides harnesses for other FPGA development boards such as the Xilinx VC707 and some MicroSemi PolarFire, only harnesses for the Xilinx VCU118 and Xilinx Arty 35T boards are currently supported in Chipyard. - However, the VCU118 and Arty 35T examples demonstrate how a user may implement support for other harnesses provided by fpga-shells. +.. Note:: While ``fpga-shells`` provides harnesses for other FPGA development boards such as the Xilinx VC707 and some MicroSemi PolarFire, only harnesses for the Xilinx VCU118 and Digilent Arty A7-35T/A7-100T boards are currently supported in Chipyard. + However, the VCU118 and Arty A7-35T/A7-100T examples demonstrate how a user may implement support for other harnesses provided by fpga-shells. .. toctree:: :maxdepth: 2 @@ -14,3 +14,4 @@ This includes popular FPGAs such as the Xilinx VCU118 and the Xilinx Arty 35T bo General VCU118 Arty + NexysVideo diff --git a/fpga/Makefile b/fpga/Makefile index 7521b1ed..3d3caf1c 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -57,6 +57,21 @@ ifeq ($(SUB_PROJECT),bringup) BOARD ?= vcu118 FPGA_BRAND ?= xilinx endif + +ifeq ($(SUB_PROJECT),nexysvideo) + SBT_PROJECT ?= fpga_platforms + MODEL ?= NexysVideoHarness + VLOG_MODEL ?= NexysVideoHarness + MODEL_PACKAGE ?= chipyard.fpga.nexysvideo + CONFIG ?= RocketNexysVideoConfig + CONFIG_PACKAGE ?= chipyard.fpga.nexysvideo + GENERATOR_PACKAGE ?= chipyard + TB ?= none # unused + TOP ?= ChipTop + BOARD ?= nexys_video + FPGA_BRAND ?= xilinx +endif + ifeq ($(SUB_PROJECT),arty) # TODO: Fix with Arty SBT_PROJECT ?= fpga_platforms diff --git a/fpga/fpga-shells b/fpga/fpga-shells index 7d0b79f8..2ce3e6f3 160000 --- a/fpga/fpga-shells +++ b/fpga/fpga-shells @@ -1 +1 @@ -Subproject commit 7d0b79f8559b9bcea1bde8d0293576a502a7a896 +Subproject commit 2ce3e6f3df06d64c858bc1073ba1c75e7eb71a07 diff --git a/fpga/src/main/scala/arty/Configs.scala b/fpga/src/main/scala/arty/Configs.scala index 1cf7bfce..3e208060 100644 --- a/fpga/src/main/scala/arty/Configs.scala +++ b/fpga/src/main/scala/arty/Configs.scala @@ -17,7 +17,8 @@ import chipyard.{BuildSystem} // DOC include start: AbstractArty and Rocket class WithArtyTweaks extends Config( - new WithArtyResetHarnessBinder ++ + new WithArtyDebugResetHarnessBinder ++ + new WithArtyJTAGResetHarnessBinder ++ new WithArtyJTAGHarnessBinder ++ new WithArtyUARTHarnessBinder ++ new WithDebugResetPassthrough ++ diff --git a/fpga/src/main/scala/arty/HarnessBinders.scala b/fpga/src/main/scala/arty/HarnessBinders.scala index 1d96cecf..5cef5f80 100644 --- a/fpga/src/main/scala/arty/HarnessBinders.scala +++ b/fpga/src/main/scala/arty/HarnessBinders.scala @@ -11,68 +11,61 @@ import sifive.blocks.devices.pinctrl.{BasePin} import sifive.fpgashells.ip.xilinx.{IBUFG, IOBUF, PULLUP, PowerOnResetFPGAOnly} -import chipyard.harness.{ComposeHarnessBinder, OverrideHarnessBinder} -import chipyard.iobinders.JTAGChipIO +import chipyard.harness.{HarnessBinder} +import chipyard.iobinders._ -class WithArtyResetHarnessBinder extends ComposeHarnessBinder({ - (system: HasPeripheryDebug, th: ArtyFPGATestHarness, ports: Seq[Data]) => { - val resetPorts = ports.collect { case b: Bool => b } - require(resetPorts.size == 2) +class WithArtyDebugResetHarnessBinder extends HarnessBinder({ + case (th: ArtyFPGATestHarness, port: DebugResetPort) => { + th.dut_ndreset := port.io // Debug module reset + } +}) + +class WithArtyJTAGResetHarnessBinder extends HarnessBinder({ + case (th: ArtyFPGATestHarness, port: JTAGResetPort) => { + port.io := PowerOnResetFPGAOnly(th.clock_32MHz) // JTAG module reset + } +}) + +class WithArtyJTAGHarnessBinder extends HarnessBinder({ + case (th: ArtyFPGATestHarness, port: JTAGPort) => { + val jtag_wire = Wire(new JTAGIO) + jtag_wire.TDO.data := port.io.TDO + jtag_wire.TDO.driven := true.B + port.io.TCK := jtag_wire.TCK + port.io.TMS := jtag_wire.TMS + port.io.TDI := jtag_wire.TDI + + val io_jtag = Wire(new JTAGPins(() => new BasePin(), false)).suggestName("jtag") + + JTAGPinsFromPort(io_jtag, jtag_wire) + + io_jtag.TCK.i.ival := IBUFG(IOBUF(th.jd_2).asClock).asBool + + IOBUF(th.jd_5, io_jtag.TMS) + PULLUP(th.jd_5) + + IOBUF(th.jd_4, io_jtag.TDI) + PULLUP(th.jd_4) + + IOBUF(th.jd_0, io_jtag.TDO) + + // mimic putting a pullup on this line (part of reset vote) + th.SRST_n := IOBUF(th.jd_6) + PULLUP(th.jd_6) + + // ignore the po input + io_jtag.TCK.i.po.map(_ := DontCare) + io_jtag.TDI.i.po.map(_ := DontCare) + io_jtag.TMS.i.po.map(_ := DontCare) + io_jtag.TDO.i.po.map(_ := DontCare) + } +}) + +class WithArtyUARTHarnessBinder extends HarnessBinder({ + case (th: ArtyFPGATestHarness, port: UARTPort) => { withClockAndReset(th.clock_32MHz, th.ck_rst) { - // Debug module reset - th.dut_ndreset := resetPorts(0) - - // JTAG reset - resetPorts(1) := PowerOnResetFPGAOnly(th.clock_32MHz) - } - } -}) - -class WithArtyJTAGHarnessBinder extends OverrideHarnessBinder({ - (system: HasPeripheryDebug, th: ArtyFPGATestHarness, ports: Seq[Data]) => { - ports.map { - case j: JTAGChipIO => { - 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 io_jtag = Wire(new JTAGPins(() => new BasePin(), false)).suggestName("jtag") - - JTAGPinsFromPort(io_jtag, jtag_wire) - - io_jtag.TCK.i.ival := IBUFG(IOBUF(th.jd_2).asClock).asBool - - IOBUF(th.jd_5, io_jtag.TMS) - PULLUP(th.jd_5) - - IOBUF(th.jd_4, io_jtag.TDI) - PULLUP(th.jd_4) - - IOBUF(th.jd_0, io_jtag.TDO) - - // mimic putting a pullup on this line (part of reset vote) - th.SRST_n := IOBUF(th.jd_6) - PULLUP(th.jd_6) - - // ignore the po input - io_jtag.TCK.i.po.map(_ := DontCare) - io_jtag.TDI.i.po.map(_ := DontCare) - io_jtag.TMS.i.po.map(_ := DontCare) - io_jtag.TDO.i.po.map(_ := DontCare) - } - case b: Bool => - } - } -}) - -class WithArtyUARTHarnessBinder extends OverrideHarnessBinder({ - (system: HasPeripheryUARTModuleImp, th: ArtyFPGATestHarness, ports: Seq[UARTPortIO]) => { - withClockAndReset(th.clock_32MHz, th.ck_rst) { - IOBUF(th.uart_rxd_out, ports.head.txd) - ports.head.rxd := IOBUF(th.uart_txd_in) + IOBUF(th.uart_rxd_out, port.io.txd) + port.io.rxd := IOBUF(th.uart_txd_in) } } }) diff --git a/fpga/src/main/scala/arty/IOBinders.scala b/fpga/src/main/scala/arty/IOBinders.scala index da6c7318..c3929262 100644 --- a/fpga/src/main/scala/arty/IOBinders.scala +++ b/fpga/src/main/scala/arty/IOBinders.scala @@ -5,7 +5,7 @@ import chisel3.experimental.{IO} import freechips.rocketchip.devices.debug.{HasPeripheryDebug} -import chipyard.iobinders.{ComposeIOBinder} +import chipyard.iobinders.{ComposeIOBinder, DebugResetPort, JTAGResetPort} class WithDebugResetPassthrough extends ComposeIOBinder({ (system: HasPeripheryDebug) => { @@ -18,6 +18,6 @@ class WithDebugResetPassthrough extends ComposeIOBinder({ val io_sjtag_reset: Bool = IO(Input(Bool())).suggestName("sjtag_reset") sjtag.reset := io_sjtag_reset - (Seq(io_ndreset, io_sjtag_reset), Nil) + (Seq(DebugResetPort(io_ndreset), JTAGResetPort(io_sjtag_reset)), Nil) } }) diff --git a/fpga/src/main/scala/arty100t/HarnessBinders.scala b/fpga/src/main/scala/arty100t/HarnessBinders.scala index 060e3d8d..e313c6e1 100644 --- a/fpga/src/main/scala/arty100t/HarnessBinders.scala +++ b/fpga/src/main/scala/arty100t/HarnessBinders.scala @@ -16,33 +16,28 @@ import sifive.fpgashells.ip.xilinx.{IBUFG, IOBUF, PULLUP, PowerOnResetFPGAOnly} import chipyard._ import chipyard.harness._ -import chipyard.iobinders.JTAGChipIO +import chipyard.iobinders._ import testchipip._ -class WithArty100TUARTTSI(uartBaudRate: BigInt = 115200) extends OverrideHarnessBinder({ - (system: CanHavePeripheryUARTTSI, th: HasHarnessInstantiators, ports: Seq[UARTTSIIO]) => { - implicit val p = chipyard.iobinders.GetSystemParameters(system) - require(ports.size <= 1) +class WithArty100TUARTTSI(uartBaudRate: BigInt = 115200) extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: UARTTSIPort) => { val ath = th.asInstanceOf[LazyRawModuleImp].wrapper.asInstanceOf[Arty100THarness] - ports.map({ port => - ath.io_uart_bb.bundle <> port.uart - ath.other_leds(1) := port.dropped - ath.other_leds(9) := port.tsi2tl_state(0) - ath.other_leds(10) := port.tsi2tl_state(1) - ath.other_leds(11) := port.tsi2tl_state(2) - ath.other_leds(12) := port.tsi2tl_state(3) - }) + ath.io_uart_bb.bundle <> port.io.uart + ath.other_leds(1) := port.io.dropped + ath.other_leds(9) := port.io.tsi2tl_state(0) + ath.other_leds(10) := port.io.tsi2tl_state(1) + ath.other_leds(11) := port.io.tsi2tl_state(2) + ath.other_leds(12) := port.io.tsi2tl_state(3) } }) -class WithArty100TDDRTL extends OverrideHarnessBinder({ - (system: CanHaveMasterTLMemPort, th: HasHarnessInstantiators, ports: Seq[HeterogeneousBag[TLBundle]]) => { - require(ports.size == 1) +class WithArty100TDDRTL extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: TLMemPort) => { val artyTh = th.asInstanceOf[LazyRawModuleImp].wrapper.asInstanceOf[Arty100THarness] val bundles = artyTh.ddrClient.out.map(_._1) val ddrClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) bundles.zip(ddrClientBundle).foreach { case (bundle, io) => bundle <> io } - ddrClientBundle <> ports.head + ddrClientBundle <> port.io } }) diff --git a/fpga/src/main/scala/nexysvideo/Configs.scala b/fpga/src/main/scala/nexysvideo/Configs.scala new file mode 100644 index 00000000..f31e38d1 --- /dev/null +++ b/fpga/src/main/scala/nexysvideo/Configs.scala @@ -0,0 +1,72 @@ +// See LICENSE for license details. +package chipyard.fpga.nexysvideo + +import org.chipsalliance.cde.config._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.devices.debug._ +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.system._ +import freechips.rocketchip.tile._ + +import sifive.blocks.devices.uart._ +import sifive.fpgashells.shell.{DesignKey} + +import testchipip.{SerialTLKey} + +import chipyard.{BuildSystem} + +// don't use FPGAShell's DesignKey +class WithNoDesignKey extends Config((site, here, up) => { + case DesignKey => (p: Parameters) => new SimpleLazyModule()(p) +}) + +// DOC include start: WithNexysVideoTweaks and Rocket +class WithNexysVideoTweaks extends Config( + new WithNexysVideoUARTTSI ++ + new WithNexysVideoDDRTL ++ + new WithNoDesignKey ++ + new testchipip.WithUARTTSIClient ++ + new chipyard.harness.WithSerialTLTiedOff ++ + new chipyard.harness.WithHarnessBinderClockFreqMHz(50) ++ + new chipyard.config.WithMemoryBusFrequency(50.0) ++ + new chipyard.config.WithFrontBusFrequency(50.0) ++ + new chipyard.config.WithSystemBusFrequency(50.0) ++ + new chipyard.config.WithPeripheryBusFrequency(50.0) ++ + new chipyard.harness.WithAllClocksFromHarnessClockInstantiator ++ + new chipyard.clocking.WithPassthroughClockGenerator ++ + new chipyard.config.WithNoDebug ++ // no jtag + new chipyard.config.WithNoUART ++ // use UART for the UART-TSI thing instad + new chipyard.config.WithTLBackingMemory ++ // FPGA-shells converts the AXI to TL for us + new freechips.rocketchip.subsystem.WithExtMemSize(BigInt(512) << 20) ++ // 512mb on Nexys Video + new freechips.rocketchip.subsystem.WithoutTLMonitors) + +class RocketNexysVideoConfig extends Config( + new WithNexysVideoTweaks ++ + new chipyard.config.WithBroadcastManager ++ // no l2 + new chipyard.RocketConfig) +// DOC include end: WithNexysVideoTweaks and Rocket + +// DOC include start: WithTinyNexysVideoTweaks and Rocket +class WithTinyNexysVideoTweaks extends Config( + new WithNexysVideoUARTTSI ++ + new WithNoDesignKey ++ + new sifive.fpgashells.shell.xilinx.WithNoNexysVideoShellDDR ++ // no DDR + new testchipip.WithUARTTSIClient ++ + new chipyard.harness.WithSerialTLTiedOff ++ + new chipyard.harness.WithHarnessBinderClockFreqMHz(50) ++ + new chipyard.config.WithMemoryBusFrequency(50.0) ++ + new chipyard.config.WithFrontBusFrequency(50.0) ++ + new chipyard.config.WithSystemBusFrequency(50.0) ++ + new chipyard.config.WithPeripheryBusFrequency(50.0) ++ + new chipyard.harness.WithAllClocksFromHarnessClockInstantiator ++ + new chipyard.clocking.WithPassthroughClockGenerator ++ + new chipyard.config.WithNoDebug ++ // no jtag + new chipyard.config.WithNoUART ++ // use UART for the UART-TSI thing instad + new freechips.rocketchip.subsystem.WithoutTLMonitors) + +class TinyRocketNexysVideoConfig extends Config( + new WithTinyNexysVideoTweaks ++ + new chipyard.config.WithBroadcastManager ++ // no l2 + new chipyard.TinyRocketConfig) + // DOC include end: WithTinyNexysVideoTweaks and Rocket \ No newline at end of file diff --git a/fpga/src/main/scala/nexysvideo/Harness.scala b/fpga/src/main/scala/nexysvideo/Harness.scala new file mode 100644 index 00000000..0cfb7110 --- /dev/null +++ b/fpga/src/main/scala/nexysvideo/Harness.scala @@ -0,0 +1,92 @@ +// See LICENSE for license details. +package chipyard.fpga.nexysvideo + +import chisel3._ +import chisel3.util._ +import freechips.rocketchip.diplomacy._ +import org.chipsalliance.cde.config.{Parameters} +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem.{SystemBusKey} + +import sifive.fpgashells.shell.xilinx._ +import sifive.fpgashells.shell._ +import sifive.fpgashells.clocks.{ClockGroup, ClockSinkNode, PLLFactoryKey, ResetWrangler} + +import sifive.blocks.devices.uart._ + +import chipyard._ +import chipyard.harness._ +import chipyard.iobinders.{HasIOBinders} + +class NexysVideoHarness(override implicit val p: Parameters) extends NexysVideoShell { + def dp = designParameters + + val clockOverlay = dp(ClockInputOverlayKey).map(_.place(ClockInputDesignInput())).head + val harnessSysPLL = dp(PLLFactoryKey) + val harnessSysPLLNode = harnessSysPLL() + val dutFreqMHz = (dp(SystemBusKey).dtsFrequency.get / (1000 * 1000)).toInt + val dutClock = ClockSinkNode(freqMHz = dutFreqMHz) + println(s"NexysVideo FPGA Base Clock Freq: ${dutFreqMHz} MHz") + val dutWrangler = LazyModule(new ResetWrangler()) + val dutGroup = ClockGroup() + dutClock := dutWrangler.node := dutGroup := harnessSysPLLNode + + harnessSysPLLNode := clockOverlay.overlayOutput.node + + val io_uart_bb = BundleBridgeSource(() => new UARTPortIO(dp(PeripheryUARTKey).headOption.getOrElse(UARTParams(0)))) + val uartOverlay = dp(UARTOverlayKey).head.place(UARTDesignInput(io_uart_bb)) + + // Optional DDR + val ddrOverlay = if (p(NexysVideoShellDDR)) Some(dp(DDROverlayKey).head.place(DDRDesignInput(dp(ExtTLMem).get.master.base, dutWrangler.node, harnessSysPLLNode)).asInstanceOf[DDRNexysVideoPlacedOverlay]) else None + val ddrClient = if (p(NexysVideoShellDDR)) Some(TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLMasterParameters.v1( + name = "chip_ddr", + sourceId = IdRange(0, 1 << dp(ExtTLMem).get.master.idBits) + )))))) else None + val ddrBlockDuringReset = if (p(NexysVideoShellDDR)) Some(LazyModule(new TLBlockDuringReset(4))) else None + if (p(NexysVideoShellDDR)) { ddrOverlay.get.overlayOutput.ddr := ddrBlockDuringReset.get.node := ddrClient.get } + + val ledOverlays = dp(LEDOverlayKey).map(_.place(LEDDesignInput())) + val all_leds = ledOverlays.map(_.overlayOutput.led) + val status_leds = all_leds.take(2) + val other_leds = all_leds.drop(2) + + + override lazy val module = new HarnessLikeImpl + + class HarnessLikeImpl extends Impl with HasHarnessInstantiators { + all_leds.foreach(_ := DontCare) + clockOverlay.overlayOutput.node.out(0)._1.reset := ~resetPin + + val clk_100mhz = clockOverlay.overlayOutput.node.out.head._1.clock + + // Blink the status LEDs for sanity + withClockAndReset(clk_100mhz, dutClock.in.head._1.reset) { + val period = (BigInt(100) << 20) / status_leds.size + val counter = RegInit(0.U(log2Ceil(period).W)) + val on = RegInit(0.U(log2Ceil(status_leds.size).W)) + status_leds.zipWithIndex.map { case (o,s) => o := on === s.U } + counter := Mux(counter === (period-1).U, 0.U, counter + 1.U) + when (counter === 0.U) { + on := Mux(on === (status_leds.size-1).U, 0.U, on + 1.U) + } + } + + other_leds(0) := resetPin + + harnessSysPLL.plls.foreach(_._1.getReset.get := pllReset) + + def referenceClockFreqMHz = dutFreqMHz + def referenceClock = dutClock.in.head._1.clock + def referenceReset = dutClock.in.head._1.reset + def success = { require(false, "Unused"); false.B } + + if (p(NexysVideoShellDDR)) { + ddrOverlay.get.mig.module.clock := harnessBinderClock + ddrOverlay.get.mig.module.reset := harnessBinderReset + ddrBlockDuringReset.get.module.clock := harnessBinderClock + ddrBlockDuringReset.get.module.reset := harnessBinderReset.asBool || !ddrOverlay.get.mig.module.io.port.init_calib_complete + } + + instantiateChipTops() + } +} diff --git a/fpga/src/main/scala/nexysvideo/HarnessBinders.scala b/fpga/src/main/scala/nexysvideo/HarnessBinders.scala new file mode 100644 index 00000000..4027cbcf --- /dev/null +++ b/fpga/src/main/scala/nexysvideo/HarnessBinders.scala @@ -0,0 +1,39 @@ +// See LICENSE for license details. +package chipyard.fpga.nexysvideo + +import chisel3._ + +import freechips.rocketchip.subsystem.{PeripheryBusKey} +import freechips.rocketchip.tilelink.{TLBundle} +import freechips.rocketchip.util.{HeterogeneousBag} +import freechips.rocketchip.diplomacy.{LazyRawModuleImp} + +import sifive.blocks.devices.uart.{UARTParams} + +import chipyard._ +import chipyard.harness._ + +import testchipip._ +import chipyard.iobinders._ + +class WithNexysVideoUARTTSI(uartBaudRate: BigInt = 115200) extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: UARTTSIPort) => { + val nexysvideoth = th.asInstanceOf[LazyRawModuleImp].wrapper.asInstanceOf[NexysVideoHarness] + nexysvideoth.io_uart_bb.bundle <> port.io.uart + nexysvideoth.other_leds(1) := port.io.dropped + nexysvideoth.other_leds(2) := port.io.tsi2tl_state(0) + nexysvideoth.other_leds(3) := port.io.tsi2tl_state(1) + nexysvideoth.other_leds(4) := port.io.tsi2tl_state(2) + nexysvideoth.other_leds(5) := port.io.tsi2tl_state(3) + } +}) + +class WithNexysVideoDDRTL extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: TLMemPort) => { + val nexysTh = th.asInstanceOf[LazyRawModuleImp].wrapper.asInstanceOf[NexysVideoHarness] + val bundles = nexysTh.ddrClient.get.out.map(_._1) + val ddrClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) + bundles.zip(ddrClientBundle).foreach { case (bundle, io) => bundle <> io } + ddrClientBundle <> port.io + } +}) diff --git a/fpga/src/main/scala/vc707/Configs.scala b/fpga/src/main/scala/vc707/Configs.scala index 55ce8e06..b37064ac 100644 --- a/fpga/src/main/scala/vc707/Configs.scala +++ b/fpga/src/main/scala/vc707/Configs.scala @@ -53,10 +53,6 @@ class WithVC707Tweaks extends Config ( new WithVC707UARTHarnessBinder ++ new WithVC707SPISDCardHarnessBinder ++ new WithVC707DDRMemHarnessBinder ++ - // io binders - new WithUARTIOPassthrough ++ - new WithSPIIOPassthrough ++ - new WithTLIOPassthrough ++ // other configuration new WithDefaultPeripherals ++ new chipyard.config.WithTLBackingMemory ++ // use TL backing memory diff --git a/fpga/src/main/scala/vc707/HarnessBinders.scala b/fpga/src/main/scala/vc707/HarnessBinders.scala index cf5bbb5e..25224d57 100644 --- a/fpga/src/main/scala/vc707/HarnessBinders.scala +++ b/fpga/src/main/scala/vc707/HarnessBinders.scala @@ -11,36 +11,29 @@ import sifive.blocks.devices.spi.{HasPeripherySPI, SPIPortIO} import sifive.fpgashells.devices.xilinx.xilinxvc707pciex1.{HasSystemXilinxVC707PCIeX1ModuleImp, XilinxVC707PCIeX1IO} import chipyard.{CanHaveMasterTLMemPort} -import chipyard.harness.{OverrideHarnessBinder} +import chipyard.harness.{HarnessBinder} +import chipyard.iobinders._ /*** UART ***/ -class WithVC707UARTHarnessBinder extends OverrideHarnessBinder({ - (system: HasPeripheryUARTModuleImp, th: BaseModule, ports: Seq[UARTPortIO]) => { - th match { case vc707th: VC707FPGATestHarnessImp => { - vc707th.vc707Outer.io_uart_bb.bundle <> ports.head - }} +class WithVC707UARTHarnessBinder extends HarnessBinder({ + case (th: VC707FPGATestHarnessImp, port: UARTPort) => { + th.vc707Outer.io_uart_bb.bundle <> port.io } }) /*** SPI ***/ -class WithVC707SPISDCardHarnessBinder extends OverrideHarnessBinder({ - (system: HasPeripherySPI, th: BaseModule, ports: Seq[SPIPortIO]) => { - th match { case vc707th: VC707FPGATestHarnessImp => { - vc707th.vc707Outer.io_spi_bb.bundle <> ports.head - }} +class WithVC707SPISDCardHarnessBinder extends HarnessBinder({ + case (th: VC707FPGATestHarnessImp, port: SPIPort) => { + th.vc707Outer.io_spi_bb.bundle <> port.io } }) /*** Experimental DDR ***/ -class WithVC707DDRMemHarnessBinder extends OverrideHarnessBinder({ - (system: CanHaveMasterTLMemPort, th: BaseModule, ports: Seq[HeterogeneousBag[TLBundle]]) => { - th match { case vc707th: VC707FPGATestHarnessImp => { - require(ports.size == 1) - - val bundles = vc707th.vc707Outer.ddrClient.out.map(_._1) - val ddrClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) - bundles.zip(ddrClientBundle).foreach { case (bundle, io) => bundle <> io } - ddrClientBundle <> ports.head - }} +class WithVC707DDRMemHarnessBinder extends HarnessBinder({ + case (th: VC707FPGATestHarnessImp, port: TLMemPort) => { + val bundles = th.vc707Outer.ddrClient.out.map(_._1) + val ddrClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) + bundles.zip(ddrClientBundle).foreach { case (bundle, io) => bundle <> io } + ddrClientBundle <> port.io } }) diff --git a/fpga/src/main/scala/vc707/IOBinders.scala b/fpga/src/main/scala/vc707/IOBinders.scala deleted file mode 100644 index ad485571..00000000 --- a/fpga/src/main/scala/vc707/IOBinders.scala +++ /dev/null @@ -1,53 +0,0 @@ -package chipyard.fpga.vc707 - -import chisel3._ -import chisel3.experimental.{IO, DataMirror} - -import freechips.rocketchip.diplomacy.{ResourceBinding, Resource, ResourceAddress, InModuleBody} -import freechips.rocketchip.subsystem.{BaseSubsystem} -import freechips.rocketchip.util.{HeterogeneousBag} -import freechips.rocketchip.tilelink.{TLBundle} - -import sifive.blocks.devices.uart.{HasPeripheryUARTModuleImp} -import sifive.blocks.devices.spi.{HasPeripherySPI, HasPeripherySPIModuleImp, MMCDevice} -import sifive.fpgashells.devices.xilinx.xilinxvc707pciex1.{HasSystemXilinxVC707PCIeX1ModuleImp} - -import chipyard.{CanHaveMasterTLMemPort} -import chipyard.iobinders.{OverrideIOBinder, OverrideLazyIOBinder} - -class WithUARTIOPassthrough extends OverrideIOBinder({ - (system: HasPeripheryUARTModuleImp) => { - val io_uart_pins_temp = system.uart.zipWithIndex.map { case (dio, i) => IO(dio.cloneType).suggestName(s"uart_$i") } - (io_uart_pins_temp zip system.uart).map { case (io, sysio) => - io <> sysio - } - (io_uart_pins_temp, Nil) - } -}) - -class WithSPIIOPassthrough extends OverrideLazyIOBinder({ - (system: HasPeripherySPI) => { - // attach resource to 1st SPI - ResourceBinding { - Resource(new MMCDevice(system.tlSpiNodes.head.device, 1), "reg").bind(ResourceAddress(0)) - } - - InModuleBody { - system.asInstanceOf[BaseSubsystem].module match { case system: HasPeripherySPIModuleImp => { - val io_spi_pins_temp = system.spi.zipWithIndex.map { case (dio, i) => IO(dio.cloneType).suggestName(s"spi_$i") } - (io_spi_pins_temp zip system.spi).map { case (io, sysio) => - io <> sysio - } - (io_spi_pins_temp, Nil) - } } - } - } -}) - -class WithTLIOPassthrough extends OverrideIOBinder({ - (system: CanHaveMasterTLMemPort) => { - val io_tl_mem_pins_temp = IO(DataMirror.internal.chiselTypeClone[HeterogeneousBag[TLBundle]](system.mem_tl)).suggestName("tl_slave") - io_tl_mem_pins_temp <> system.mem_tl - (Seq(io_tl_mem_pins_temp), Nil) - } -}) diff --git a/fpga/src/main/scala/vcu118/Configs.scala b/fpga/src/main/scala/vcu118/Configs.scala index bd48fb4e..35632961 100644 --- a/fpga/src/main/scala/vcu118/Configs.scala +++ b/fpga/src/main/scala/vcu118/Configs.scala @@ -52,9 +52,6 @@ class WithVCU118Tweaks extends Config( new WithUART ++ new WithSPISDCard ++ new WithDDRMem ++ - // io binders - new WithUARTIOPassthrough ++ - new WithSPIIOPassthrough ++ // other configuration new WithDefaultPeripherals ++ new chipyard.config.WithTLBackingMemory ++ // use TL backing memory diff --git a/fpga/src/main/scala/vcu118/HarnessBinders.scala b/fpga/src/main/scala/vcu118/HarnessBinders.scala index f17b654f..769e5b42 100644 --- a/fpga/src/main/scala/vcu118/HarnessBinders.scala +++ b/fpga/src/main/scala/vcu118/HarnessBinders.scala @@ -11,35 +11,28 @@ import sifive.blocks.devices.spi.{HasPeripherySPI, SPIPortIO} import chipyard._ import chipyard.harness._ +import chipyard.iobinders._ /*** UART ***/ -class WithUART extends OverrideHarnessBinder({ - (system: HasPeripheryUARTModuleImp, th: BaseModule with HasHarnessInstantiators, ports: Seq[UARTPortIO]) => { - th match { case vcu118th: VCU118FPGATestHarnessImp => { - vcu118th.vcu118Outer.io_uart_bb.bundle <> ports.head - } } +class WithUART extends HarnessBinder({ + case (th: VCU118FPGATestHarnessImp, port: UARTPort) => { + th.vcu118Outer.io_uart_bb.bundle <> port.io } }) /*** SPI ***/ -class WithSPISDCard extends OverrideHarnessBinder({ - (system: HasPeripherySPI, th: BaseModule with HasHarnessInstantiators, ports: Seq[SPIPortIO]) => { - th match { case vcu118th: VCU118FPGATestHarnessImp => { - vcu118th.vcu118Outer.io_spi_bb.bundle <> ports.head - } } +class WithSPISDCard extends HarnessBinder({ + case (th: VCU118FPGATestHarnessImp, port: SPIPort) => { + th.vcu118Outer.io_spi_bb.bundle <> port.io } }) /*** Experimental DDR ***/ -class WithDDRMem extends OverrideHarnessBinder({ - (system: CanHaveMasterTLMemPort, th: BaseModule with HasHarnessInstantiators, ports: Seq[HeterogeneousBag[TLBundle]]) => { - th match { case vcu118th: VCU118FPGATestHarnessImp => { - require(ports.size == 1) - - val bundles = vcu118th.vcu118Outer.ddrClient.out.map(_._1) - val ddrClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) - bundles.zip(ddrClientBundle).foreach { case (bundle, io) => bundle <> io } - ddrClientBundle <> ports.head - } } +class WithDDRMem extends HarnessBinder({ + case (th: VCU118FPGATestHarnessImp, port: TLMemPort) => { + val bundles = th.vcu118Outer.ddrClient.out.map(_._1) + val ddrClientBundle = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) + bundles.zip(ddrClientBundle).foreach { case (bundle, io) => bundle <> io } + ddrClientBundle <> port.io } }) diff --git a/fpga/src/main/scala/vcu118/IOBinders.scala b/fpga/src/main/scala/vcu118/IOBinders.scala deleted file mode 100644 index 279f9865..00000000 --- a/fpga/src/main/scala/vcu118/IOBinders.scala +++ /dev/null @@ -1,44 +0,0 @@ -package chipyard.fpga.vcu118 - -import chisel3._ -import chisel3.experimental.{IO, DataMirror} - -import freechips.rocketchip.diplomacy.{ResourceBinding, Resource, ResourceAddress, InModuleBody} -import freechips.rocketchip.subsystem.{BaseSubsystem} -import freechips.rocketchip.util.{HeterogeneousBag} -import freechips.rocketchip.tilelink.{TLBundle} - -import sifive.blocks.devices.uart.{HasPeripheryUARTModuleImp} -import sifive.blocks.devices.spi.{HasPeripherySPI, HasPeripherySPIModuleImp, MMCDevice} - -import chipyard.{CanHaveMasterTLMemPort} -import chipyard.iobinders.{OverrideIOBinder, OverrideLazyIOBinder} - -class WithUARTIOPassthrough extends OverrideIOBinder({ - (system: HasPeripheryUARTModuleImp) => { - val io_uart_pins_temp = system.uart.zipWithIndex.map { case (dio, i) => IO(dio.cloneType).suggestName(s"uart_$i") } - (io_uart_pins_temp zip system.uart).map { case (io, sysio) => - io <> sysio - } - (io_uart_pins_temp, Nil) - } -}) - -class WithSPIIOPassthrough extends OverrideLazyIOBinder({ - (system: HasPeripherySPI) => { - // attach resource to 1st SPI - ResourceBinding { - Resource(new MMCDevice(system.tlSpiNodes.head.device, 1), "reg").bind(ResourceAddress(0)) - } - - InModuleBody { - system.asInstanceOf[BaseSubsystem].module match { case system: HasPeripherySPIModuleImp => { - val io_spi_pins_temp = system.spi.zipWithIndex.map { case (dio, i) => IO(dio.cloneType).suggestName(s"spi_$i") } - (io_spi_pins_temp zip system.spi).map { case (io, sysio) => - io <> sysio - } - (io_spi_pins_temp, Nil) - } } - } - } -}) diff --git a/fpga/src/main/scala/vcu118/bringup/Configs.scala b/fpga/src/main/scala/vcu118/bringup/Configs.scala index bff715dc..65a23a1f 100644 --- a/fpga/src/main/scala/vcu118/bringup/Configs.scala +++ b/fpga/src/main/scala/vcu118/bringup/Configs.scala @@ -20,6 +20,7 @@ import testchipip.{PeripheryTSIHostKey, TSIHostParams, TSIHostSerdesParams} import chipyard.{BuildSystem} import chipyard.fpga.vcu118.{WithVCU118Tweaks, WithFPGAFrequency, VCU118DDR2Size} +import chipyard.iobinders.{WithGPIOPunchthrough} class WithBringupPeripherals extends Config((site, here, up) => { case PeripheryUARTKey => up(PeripheryUARTKey, site) ++ List(UARTParams(address = BigInt(0x64003000L))) @@ -80,8 +81,7 @@ class WithBringupAdditions extends Config( new WithBringupGPIO ++ new WithBringupTSIHost ++ new WithTSITLIOPassthrough ++ - new WithI2CIOPassthrough ++ - new WithGPIOIOPassthrough ++ + new WithGPIOPunchthrough ++ new WithBringupPeripherals ++ new WithBringupVCU118System) diff --git a/fpga/src/main/scala/vcu118/bringup/HarnessBinders.scala b/fpga/src/main/scala/vcu118/bringup/HarnessBinders.scala index 97d62ae2..d60de620 100644 --- a/fpga/src/main/scala/vcu118/bringup/HarnessBinders.scala +++ b/fpga/src/main/scala/vcu118/bringup/HarnessBinders.scala @@ -14,56 +14,38 @@ import sifive.blocks.devices.gpio.{HasPeripheryGPIOModuleImp, GPIOPortIO} import testchipip.{HasPeripheryTSIHostWidget, TSIHostWidgetIO} import chipyard.harness._ +import chipyard.iobinders._ /*** UART ***/ -class WithBringupUART extends ComposeHarnessBinder({ - (system: HasPeripheryUARTModuleImp, th: BaseModule with HasHarnessInstantiators, ports: Seq[UARTPortIO]) => { - th match { case vcu118th: BringupVCU118FPGATestHarnessImp => { - require(ports.size == 2) - - vcu118th.bringupOuter.io_fmc_uart_bb.bundle <> ports.last - } } +class WithBringupUART extends HarnessBinder({ + case (th: BringupVCU118FPGATestHarnessImp, port: UARTPort) => { + th.bringupOuter.io_fmc_uart_bb.bundle <> port.io } }) /*** I2C ***/ -class WithBringupI2C extends OverrideHarnessBinder({ - (system: HasPeripheryI2CModuleImp, th: BaseModule with HasHarnessInstantiators, ports: Seq[I2CPort]) => { - th match { case vcu118th: BringupVCU118FPGATestHarnessImp => { - require(ports.size == 1) - - vcu118th.bringupOuter.io_i2c_bb.bundle <> ports.head - } } +class WithBringupI2C extends HarnessBinder({ + case (th: BringupVCU118FPGATestHarnessImp, port: chipyard.iobinders.I2CPort) => { + th.bringupOuter.io_i2c_bb.bundle <> port.io } }) /*** GPIO ***/ -class WithBringupGPIO extends OverrideHarnessBinder({ - (system: HasPeripheryGPIOModuleImp, th: BaseModule with HasHarnessInstantiators, ports: Seq[GPIOPortIO]) => { - th match { case vcu118th: BringupVCU118FPGATestHarnessImp => { - (vcu118th.bringupOuter.io_gpio_bb zip ports).map { case (bb_io, dut_io) => - bb_io.bundle <> dut_io - } - } } +class WithBringupGPIO extends HarnessBinder({ + case (th: BringupVCU118FPGATestHarnessImp, port: GPIOPort) => { + th.bringupOuter.io_gpio_bb(port.pinId).bundle <> port.io } }) /*** TSI Host Widget ***/ -class WithBringupTSIHost extends OverrideHarnessBinder({ - (system: HasPeripheryTSIHostWidget, th: BaseModule with HasHarnessInstantiators, ports: Seq[Data]) => { - th match { case vcu118th: BringupVCU118FPGATestHarnessImp => { - require(ports.size == 2) // 1st goes to the TL mem, 2nd goes to the serial link - - ports.head match { case tlPort: HeterogeneousBag[TLBundle] => - val tsiBundles = vcu118th.bringupOuter.tsiDdrClient.out.map(_._1) - val tsiDdrClientBundle = Wire(new HeterogeneousBag(tsiBundles.map(_.cloneType))) - tsiBundles.zip(tsiDdrClientBundle).foreach { case (bundle, io) => bundle <> io } - tsiDdrClientBundle <> tlPort - } - - ports.last match { case serialPort: TSIHostWidgetIO => - vcu118th.bringupOuter.io_tsi_serial_bb.bundle <> serialPort - } - } } +class WithBringupTSIHost extends HarnessBinder({ + case (th: BringupVCU118FPGATestHarnessImp, port: TLMemPort) => { + val tsiBundles = th.bringupOuter.tsiDdrClient.out.map(_._1) + val tsiDdrClientBundle = Wire(new HeterogeneousBag(tsiBundles.map(_.cloneType))) + tsiBundles.zip(tsiDdrClientBundle).foreach { case (bundle, io) => bundle <> io } + tsiDdrClientBundle <> port.io + } + case (th: BringupVCU118FPGATestHarnessImp, port: TSIHostWidgetPort) => { + th.bringupOuter.io_tsi_serial_bb.bundle <> port.io } }) diff --git a/fpga/src/main/scala/vcu118/bringup/IOBinders.scala b/fpga/src/main/scala/vcu118/bringup/IOBinders.scala index 87763cde..69dfbce9 100644 --- a/fpga/src/main/scala/vcu118/bringup/IOBinders.scala +++ b/fpga/src/main/scala/vcu118/bringup/IOBinders.scala @@ -11,27 +11,10 @@ import sifive.blocks.devices.i2c.{HasPeripheryI2CModuleImp} import testchipip.{HasPeripheryTSIHostWidget, TSIHostWidgetIO} -import chipyard.iobinders.{OverrideIOBinder} +import chipyard.iobinders.{OverrideIOBinder, Port, TLMemPort} -class WithGPIOIOPassthrough extends OverrideIOBinder({ - (system: HasPeripheryGPIOModuleImp) => { - val io_gpio_pins_temp = system.gpio.zipWithIndex.map { case (dio, i) => IO(dio.cloneType).suggestName(s"gpio_$i") } - (io_gpio_pins_temp zip system.gpio).map { case (io, sysio) => - io <> sysio - } - (io_gpio_pins_temp, Nil) - } -}) - -class WithI2CIOPassthrough extends OverrideIOBinder({ - (system: HasPeripheryI2CModuleImp) => { - val io_i2c_pins_temp = system.i2c.zipWithIndex.map { case (dio, i) => IO(dio.cloneType).suggestName(s"i2c_$i") } - (io_i2c_pins_temp zip system.i2c).map { case (io, sysio) => - io <> sysio - } - (io_i2c_pins_temp, Nil) - } -}) +case class TSIHostWidgetPort(val io: TSIHostWidgetIO) + extends Port[TSIHostWidgetIO] class WithTSITLIOPassthrough extends OverrideIOBinder({ (system: HasPeripheryTSIHostWidget) => { @@ -42,6 +25,6 @@ class WithTSITLIOPassthrough extends OverrideIOBinder({ require(system.tsiSerial.size == 1) val io_tsi_serial_pins_temp = IO(DataMirror.internal.chiselTypeClone[TSIHostWidgetIO](system.tsiSerial.head)).suggestName("tsi_serial") io_tsi_serial_pins_temp <> system.tsiSerial.head - (Seq(io_tsi_tl_mem_pins_temp, io_tsi_serial_pins_temp), Nil) + (Seq(TLMemPort(io_tsi_tl_mem_pins_temp), TSIHostWidgetPort(io_tsi_serial_pins_temp)), Nil) } }) diff --git a/generators/chipyard/src/main/scala/clocking/ClockBinders.scala b/generators/chipyard/src/main/scala/clocking/ClockBinders.scala index 412dd43d..3ef8a61b 100644 --- a/generators/chipyard/src/main/scala/clocking/ClockBinders.scala +++ b/generators/chipyard/src/main/scala/clocking/ClockBinders.scala @@ -2,17 +2,13 @@ package chipyard.clocking import chisel3._ import chisel3.util._ -import chipyard.iobinders.{OverrideLazyIOBinder, GetSystemParameters, IOCellKey} +import chipyard.iobinders.{OverrideLazyIOBinder, GetSystemParameters, IOCellKey, ClockPort, ResetPort} import freechips.rocketchip.prci._ import freechips.rocketchip.diplomacy._ import freechips.rocketchip.subsystem._ import freechips.rocketchip.tilelink._ import barstools.iocell.chisel._ -class ClockWithFreq(val freqMHz: Double) extends Bundle { - val clock = Clock() -} - // This uses the FakePLL, which uses a ClockAtFreq Verilog blackbox to generate // the requested clocks. This also adds TileLink ClockDivider and ClockSelector // blocks, which allow memory-mapped control of clock division, and clock muxing @@ -58,13 +54,13 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({ pllCtrlSink := pllCtrl.ctrlNode InModuleBody { - val clock_wire = Wire(Input(new ClockWithFreq(100))) + val clock_wire = Wire(Input(Clock())) val reset_wire = Wire(Input(AsyncReset())) val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey)) val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey)) slowClockSource.out.unzip._1.map { o => - o.clock := clock_wire.clock + o.clock := clock_wire o.reset := reset_wire } @@ -79,7 +75,7 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({ o.reset := reset_wire } - (Seq(clock_io, reset_io), clockIOCell ++ resetIOCell) + (Seq(ClockPort(clock_io, 100), ResetPort(reset_io)), clockIOCell ++ resetIOCell) } } }) @@ -114,12 +110,12 @@ class WithPassthroughClockGenerator extends OverrideLazyIOBinder({ require(m.take.isDefined, s"""Clock ${m.name.get} has no requested frequency |Clocks: ${edge.sink.members.map(_.name.get)}""".stripMargin) val freq = m.take.get.freqMHz - val clock_io = IO(Input(new ClockWithFreq(freq))).suggestName(s"clock_${m.name.get}") - b.clock := clock_io.clock + val clock_io = IO(Input(Clock())).suggestName(s"clock_${m.name.get}") + b.clock := clock_io b.reset := reset_io - clock_io + ClockPort(clock_io, freq) }.toSeq - ((clock_ios :+ reset_io), Nil) + ((clock_ios :+ ResetPort(reset_io)), Nil) } } }) diff --git a/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala b/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala index e52ec674..42c45db6 100644 --- a/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala +++ b/generators/chipyard/src/main/scala/clocking/DividerOnlyClockGenerator.scala @@ -51,7 +51,7 @@ object FrequencyUtils { require(!requestedOutputs.contains(0.0)) val requestedFreqs = requestedOutputs.map(_.freqMHz) val fastestFreq = requestedFreqs.max - require(fastestFreq <= maximumAllowableFreqMHz) + require(fastestFreq <= maximumAllowableFreqMHz, s"Fastest Freq $fastestFreq > Max Freq $maximumAllowableFreqMHz") val candidateFreqs = Seq.tabulate(Math.ceil(maximumAllowableFreqMHz / fastestFreq).toInt)(i => (i + 1) * fastestFreq) diff --git a/generators/chipyard/src/main/scala/config/AbstractConfig.scala b/generators/chipyard/src/main/scala/config/AbstractConfig.scala index 44235cbd..a08abb6d 100644 --- a/generators/chipyard/src/main/scala/config/AbstractConfig.scala +++ b/generators/chipyard/src/main/scala/config/AbstractConfig.scala @@ -15,7 +15,8 @@ class AbstractConfig extends Config( new chipyard.harness.WithUARTAdapter ++ // add UART adapter to display UART on stdout, if uart is present new chipyard.harness.WithBlackBoxSimMem ++ // add SimDRAM DRAM model for axi4 backing memory, if axi4 mem is enabled new chipyard.harness.WithSimTSIOverSerialTL ++ // add external serial-adapter and RAM - new chipyard.harness.WithSimDebug ++ // add SimJTAG or SimDTM adapters if debug module is enabled + new chipyard.harness.WithSimJTAGDebug ++ // add SimJTAG if JTAG for debug exposed + new chipyard.harness.WithSimDMI ++ // add SimJTAG if DMI exposed new chipyard.harness.WithGPIOTiedOff ++ // tie-off chiptop GPIOs, if GPIOs are present new chipyard.harness.WithSimSPIFlashModel ++ // add simulated SPI flash memory, if SPI is enabled new chipyard.harness.WithSimAXIMMIO ++ // add SimAXIMem for axi4 mmio port, if enabled @@ -23,7 +24,8 @@ class AbstractConfig extends Config( new chipyard.harness.WithTieOffL2FBusAXI ++ // tie-off external AXI4 master, if present new chipyard.harness.WithCustomBootPinPlusArg ++ // drive custom-boot pin with a plusarg, if custom-boot-pin is present new chipyard.harness.WithSimUARTToUARTTSI ++ // connect a SimUART to the UART-TSI port - new chipyard.harness.WithClockAndResetFromHarness ++ // all Clock/Reset I/O in ChipTop should be driven by harnessClockInstantiator + new chipyard.harness.WithClockFromHarness ++ // all Clock I/O in ChipTop should be driven by harnessClockInstantiator + new chipyard.harness.WithResetFromHarness ++ // reset controlled by harness new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ // generate clocks in harness with unsynthesizable ClockSourceAtFreqMHz // The IOBinders instantiate ChipTop IOs to match desired digital IOs @@ -32,11 +34,13 @@ class AbstractConfig extends Config( new chipyard.iobinders.WithDebugIOCells ++ new chipyard.iobinders.WithUARTIOCells ++ new chipyard.iobinders.WithGPIOCells ++ - new chipyard.iobinders.WithSPIIOCells ++ + new chipyard.iobinders.WithSPIFlashIOCells ++ new chipyard.iobinders.WithExtInterruptIOCells ++ new chipyard.iobinders.WithCustomBootPin ++ // The "punchthrough" IOBInders below don't generate IOCells, as these interfaces shouldn't really be mapped to ASIC IO // Instead, they directly pass through the DigitalTop ports to ports in the ChipTop + new chipyard.iobinders.WithI2CPunchthrough ++ + new chipyard.iobinders.WithSPIIOPunchthrough ++ new chipyard.iobinders.WithAXI4MemPunchthrough ++ new chipyard.iobinders.WithAXI4MMIOPunchthrough ++ new chipyard.iobinders.WithTLMemPunchthrough ++ diff --git a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala index ab00ad24..55cce1b8 100644 --- a/generators/chipyard/src/main/scala/config/TracegenConfigs.scala +++ b/generators/chipyard/src/main/scala/config/TracegenConfigs.scala @@ -7,7 +7,8 @@ class AbstractTraceGenConfig extends Config( new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ new chipyard.harness.WithBlackBoxSimMem ++ new chipyard.harness.WithTraceGenSuccess ++ - new chipyard.harness.WithClockAndResetFromHarness ++ + new chipyard.harness.WithClockFromHarness ++ + new chipyard.harness.WithResetFromHarness ++ new chipyard.iobinders.WithAXI4MemPunchthrough ++ new chipyard.iobinders.WithTraceGenSuccessPunchthrough ++ new chipyard.clocking.WithPassthroughClockGenerator ++ diff --git a/generators/chipyard/src/main/scala/example/FlatChipTop.scala b/generators/chipyard/src/main/scala/example/FlatChipTop.scala index 96e21611..a1a1aeaa 100644 --- a/generators/chipyard/src/main/scala/example/FlatChipTop.scala +++ b/generators/chipyard/src/main/scala/example/FlatChipTop.scala @@ -68,13 +68,13 @@ class FlatChipTop(implicit p: Parameters) extends LazyModule { l.reset := implicit_reset }} - val clock_wire = Wire(Input(new ClockWithFreq(80))) + val clock_wire = Wire(Input(Clock())) 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.clock := clock_wire o.reset := reset_wire } diff --git a/generators/chipyard/src/main/scala/example/FlatTestHarness.scala b/generators/chipyard/src/main/scala/example/FlatTestHarness.scala index 56bc9a78..5b3168b3 100644 --- a/generators/chipyard/src/main/scala/example/FlatTestHarness.scala +++ b/generators/chipyard/src/main/scala/example/FlatTestHarness.scala @@ -30,7 +30,7 @@ class FlatTestHarness(implicit val p: Parameters) extends Module { 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 + dut.clock_pad := clock_source.io.clk // Reset dut.reset_pad := reset.asAsyncReset diff --git a/generators/chipyard/src/main/scala/harness/HarnessBinders.scala b/generators/chipyard/src/main/scala/harness/HarnessBinders.scala index 2b522884..8530bb33 100644 --- a/generators/chipyard/src/main/scala/harness/HarnessBinders.scala +++ b/generators/chipyard/src/main/scala/harness/HarnessBinders.scala @@ -6,359 +6,284 @@ 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 freechips.rocketchip.jtag.{JTAGIO} +import freechips.rocketchip.devices.debug.{SimJTAG} import barstools.iocell.chisel._ - import testchipip._ - +import icenet.{NicLoopback, SimNetwork} import chipyard._ -import chipyard.clocking.{HasChipyardPRCI, ClockWithFreq} -import chipyard.iobinders.{GetSystemParameters, JTAGChipIO} +import chipyard.clocking.{HasChipyardPRCI} +import chipyard.iobinders._ -import tracegen.{TraceGenSystemModuleImp} -import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly} - -import scala.reflect.{ClassTag} - -case object HarnessBinders extends Field[HarnessBinderMap](HarnessBinderMapDefault) +case object HarnessBinders extends Field[HarnessBinderFunction]({case _ => }) 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)) - } + def apply(th: HasHarnessInstantiators, ports: Seq[Port[_]])(implicit p: Parameters): Unit = { + ports.foreach(port => p(HarnessBinders)(th, port)) } } -// 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 HarnessBinder[T <: HasHarnessInstantiators, S <: Port[_]]( + fn: => HarnessBinderFunction +) extends Config((site, here, up) => { + case HarnessBinders => fn orElse up(HarnessBinders) }) -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) } +class WithGPIOTiedOff extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: GPIOPort) => { + port.io <> AnalogConst(0) } }) // DOC include start: WithUARTAdapter -class WithUARTAdapter extends OverrideHarnessBinder({ - (system: HasPeripheryUARTModuleImp, th: HasHarnessInstantiators, ports: Seq[UARTPortIO]) => { - UARTAdapter.connect(ports)(system.p) +class WithUARTAdapter extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: UARTPort) => { + val div = (th.getHarnessBinderClockFreqMHz.toDouble * 1000000 / port.io.c.initBaudRate.toDouble).toInt + val uart_sim = Module(new UARTAdapter(port.uartNo, div, false)).suggestName(s"uart_sim_uartno${port.uartNo}") + uart_sim.io.uart.txd := port.io.txd + port.io.rxd := uart_sim.io.uart.rxd } }) // 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 WithSimSPIFlashModel(rdOnly: Boolean = true) extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: SPIFlashPort) => { + val spi_mem = Module(new SimSPIFlashModel(port.params.fSize, port.spiId, rdOnly)).suggestName(s"spi_mem${port.spiId}") + spi_mem.io.sck := port.io.sck + require(port.params.csWidth == 1, "I don't know what to do with your extra CS bits. Fix me please.") + spi_mem.io.cs(0) := port.io.cs(0) + spi_mem.io.dq.zip(port.io.dq).foreach { case (x, y) => x <> y } + spi_mem.io.reset := th.harnessBinderReset } }) -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 WithSimBlockDevice extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: BlockDevicePort) => { + val sim_blkdev = Module(new SimBlockDevice(port.params)) + sim_blkdev.io.bdev <> port.io.bits + sim_blkdev.io.clock := port.io.clock + sim_blkdev.io.reset := th.harnessBinderReset } }) -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 WithBlockDeviceModel extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: BlockDevicePort) => { + val blkdev_model = Module(new BlockDeviceModel(16, port.params)) + blkdev_model.io <> port.io.bits + blkdev_model.clock := port.io.clock + blkdev_model.reset := th.harnessBinderReset } }) -class WithLoopbackNIC extends OverrideHarnessBinder({ - (system: CanHavePeripheryIceNIC, th: HasHarnessInstantiators, ports: Seq[ClockedIO[NICIOvonly]]) => { - implicit val p: Parameters = GetSystemParameters(system) - ports.map { n => - withClockAndReset(n.clock, th.harnessBinderReset.asBool) { - NicLoopback.connect(Some(n.bits), p(NICKey)) +class WithLoopbackNIC extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: NICPort) => { + withClock(port.io.clock) { NicLoopback.connect(port.io.bits, port.params) } + } +}) + +class WithSimNetwork extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: NICPort) => { + withClock(port.io.clock) { SimNetwork.connect(Some(port.io.bits), port.io.clock, th.harnessBinderReset.asBool) } + } +}) + +class WithSimAXIMem extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: AXI4MemPort) => { + val mem = LazyModule(new SimAXIMem(port.edge, size=port.params.master.size)(Parameters.empty)) + withClock(port.io.clock) { Module(mem.module) } + mem.io_axi4.head <> port.io + } +}) + +class WithBlackBoxSimMem(additionalLatency: Int = 0) extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: AXI4MemPort) => { + // TODO FIX: This currently makes each SimDRAM contain the entire memory space + val memSize = port.params.master.size + val memBase = port.params.master.base + val lineSize = 64 // cache block size + val clockFreq = port.clockFreqMHz + val mem = Module(new SimDRAM(memSize, lineSize, clockFreq, memBase, port.edge.bundle)).suggestName("simdram") + + mem.io.clock := port.io.clock + mem.io.reset := th.harnessBinderReset.asAsyncReset + mem.io.axi <> port.io.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) { + withClock (port.io.clock) { + mem.io.axi.aw <> (0 until additionalLatency).foldLeft(Decoupled(port.io.bits.aw))((t, _) => Queue(t, 1, pipe=true)) + mem.io.axi.w <> (0 until additionalLatency).foldLeft(Decoupled(port.io.bits.w ))((t, _) => Queue(t, 1, pipe=true)) + port.io.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.io.bits.ar))((t, _) => Queue(t, 1, pipe=true)) + port.io.bits.r <> (0 until additionalLatency).foldLeft(Decoupled(mem.io.axi.r ))((t, _) => Queue(t, 1, pipe=true)) } } } }) -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 WithSimAXIMMIO extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: AXI4MMIOPort) => { + val mmio_mem = LazyModule(new SimAXIMem(port.edge, size = port.params.size)(Parameters.empty)) + withClock(port.io.clock) { Module(mmio_mem.module).suggestName("mmio_mem") } + mmio_mem.io_axi4.head <> port.io.bits } }) -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 WithTieOffInterrupts extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: ExtIntPort) => { + port.io := 0.U + } +}) + +class WithTieOffL2FBusAXI extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: AXI4InPort) => { + port.io := DontCare + port.io.bits.aw.valid := false.B + port.io.bits.w.valid := false.B + port.io.bits.b.ready := false.B + port.io.bits.ar.valid := false.B + port.io.bits.r.ready := false.B + } +}) + +class WithSimJTAGDebug extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: JTAGPort) => { + val dtm_success = WireInit(false.B) + when (dtm_success) { th.success := true.B } + val jtag_wire = Wire(new JTAGIO) + jtag_wire.TDO.data := port.io.TDO + jtag_wire.TDO.driven := true.B + port.io.TCK := jtag_wire.TCK + port.io.TMS := jtag_wire.TMS + port.io.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 WithSimDMI extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: DMIPort) => { + val dtm_success = WireInit(false.B) + when (dtm_success) { th.success := true.B } + val dtm = Module(new TestchipSimDTM()(Parameters.empty)).connect(th.harnessBinderClock, th.harnessBinderReset.asBool, port.io, dtm_success) + } +}) + +class WithTiedOffJTAG extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: JTAGPort) => { + port.io.TCK := true.B.asClock + port.io.TMS := true.B + port.io.TDI := true.B + } +}) + +class WithTiedOffDMI extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: DMIPort) => { + port.io.dmi.req.valid := false.B + port.io.dmi.req.bits := DontCare + port.io.dmi.resp.ready := true.B + port.io.dmiClock := false.B.asClock + port.io.dmiReset := true.B + } +}) + +class WithSerialTLTiedOff extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: SerialTLPort) => { + if (DataMirror.directionOf(port.io.clock) == Direction.Input) { + port.io.clock := false.B.asClock } + port.io.bits.out.ready := false.B + port.io.bits.in.valid := false.B + port.io.bits.in.bits := DontCare } }) -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 WithSimTSIOverSerialTL extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: SerialTLPort) => { + val bits = port.io.bits + if (DataMirror.directionOf(port.io.clock) == Direction.Input) { + port.io.clock := th.harnessBinderClock } + val ram = LazyModule(new SerialRAM(port.serdesser)(port.serdesser.p)) + Module(ram.module) + ram.module.io.ser <> port.io.bits + val tsi = Module(new SimTSI) + tsi.io.clock := th.harnessBinderClock + tsi.io.reset := th.harnessBinderReset + tsi.io.tsi <> ram.module.io.tsi + val exit = tsi.io.exit + val success = exit === 1.U + val error = exit >= 2.U + assert(!error, "*** FAILED *** (exit code = %d)\n", exit >> 1.U) + when (success) { th.success := true.B } } }) -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 WithSimUARTToUARTTSI extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: UARTPort) => { + UARTAdapter.connect(Seq(port.io), + baudrate=port.io.c.initBaudRate, + clockFrequency=th.getHarnessBinderClockFreqHz.toInt, + forcePty=true) } }) -class WithTieOffInterrupts extends OverrideHarnessBinder({ - (system: HasExtInterruptsModuleImp, th: HasHarnessInstantiators, ports: Seq[UInt]) => { - ports.foreach { _ := 0.U } +class WithSimTSIToUARTTSI extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: UARTTSIPort) => { + val freq = th.getHarnessBinderClockFreqHz.toInt + val uart_to_serial = Module(new UARTToSerial(freq, port.io.uart.c)) + 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.io.uart.txd + port.io.uart.rxd := uart_to_serial.io.uart.txd } }) -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 WithTraceGenSuccess extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: SuccessPort) => { + when (port.io) { th.success := true.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 WithCospike extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: TracePort) => { + port.io.traces.zipWithIndex.map(t => SpikeCosim(t._1, t._2, port.cosimCfg)) } }) -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 - if (DataMirror.directionOf(port.clock) == Direction.Input) { - 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 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(""), - vlen = tiles.headOption.map(_.tileParams.core.vLen).getOrElse(0), - 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]) => { +class WithCustomBootPinPlusArg extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: CustomBootPort) => { val pin = PlusArg("custom_boot_pin", width=1) - ports.foreach(_ := pin) + port.io := 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 } +class WithClockFromHarness extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: ClockPort) => { // DOC include start: HarnessClockInstantiatorEx - 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 - }) + port.io := th.harnessClockInstantiator.requestClockMHz(s"clock_${port.freqMHz}MHz", port.freqMHz) // DOC include end: HarnessClockInstantiatorEx } }) + +class WithResetFromHarness extends HarnessBinder({ + case (th: HasHarnessInstantiators, port: ResetPort) => { + port.io := th.referenceReset.asAsyncReset + } +}) + diff --git a/generators/chipyard/src/main/scala/harness/HasHarnessInstantiators.scala b/generators/chipyard/src/main/scala/harness/HasHarnessInstantiators.scala index e79b6c4e..48f63e04 100644 --- a/generators/chipyard/src/main/scala/harness/HasHarnessInstantiators.scala +++ b/generators/chipyard/src/main/scala/harness/HasHarnessInstantiators.scala @@ -63,7 +63,7 @@ trait HasHarnessInstantiators { val supportsMultiChip: Boolean = false - private val chipParameters = p(MultiChipNChips) match { + val chipParameters = p(MultiChipNChips) match { case Some(n) => (0 until n).map { i => p(MultiChipParameters(i)).alterPartial { case TargetDirKey => p(TargetDirKey) // hacky fix case MultiChipIdx => i @@ -83,7 +83,7 @@ trait HasHarnessInstantiators { withClockAndReset (harnessBinderClock, harnessBinderReset) { lazyDuts.zipWithIndex.foreach { - case (d: HasIOBinders, i: Int) => ApplyHarnessBinders(this, d.lazySystem, d.portMap)(chipParameters(i)) + case (d: HasIOBinders, i: Int) => ApplyHarnessBinders(this, d.portMap.values.flatten.toSeq)(chipParameters(i)) case _ => } ApplyMultiHarnessBinders(this, lazyDuts) diff --git a/generators/chipyard/src/main/scala/harness/MultiHarnessBinders.scala b/generators/chipyard/src/main/scala/harness/MultiHarnessBinders.scala index 1c057dff..399e64be 100644 --- a/generators/chipyard/src/main/scala/harness/MultiHarnessBinders.scala +++ b/generators/chipyard/src/main/scala/harness/MultiHarnessBinders.scala @@ -2,6 +2,7 @@ package chipyard.harness import chisel3._ import chisel3.util._ +import chisel3.experimental.{DataMirror, Direction} import org.chipsalliance.cde.config.{Field, Config, Parameters} import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImpLike} @@ -12,67 +13,58 @@ import freechips.rocketchip.util._ import testchipip._ import chipyard._ -import chipyard.clocking.{HasChipyardPRCI, ClockWithFreq} -import chipyard.iobinders.{GetSystemParameters, JTAGChipIO, HasIOBinders} +import chipyard.iobinders.{GetSystemParameters, JTAGChipIO, HasIOBinders, Port, SerialTLPort} import scala.reflect.{ClassTag} -case class MultiHarnessBinders(c0: Int, c1: Int) extends Field[MultiHarnessBinderMap](MultiHarnessBinderMapDefault) - -class MultiHarnessBinder[T0, T1, S <: HasHarnessInstantiators, U0 <: Data, U1 <: Data] - (chip0: Int, chip1: Int, fn: => (T0, T1, S, Seq[U0], Seq[U1]) => Unit) - (implicit tag0: ClassTag[T0], tag1: ClassTag[T1], thtag: ClassTag[S], ptag0: ClassTag[U0], ptag1: ClassTag[U1]) - extends Config((site, here, up) => { - // Override any HarnessBinders for chip0/chip1 - case MultiChipParameters(`chip0`) => new Config( - new OverrideHarnessBinder[T0, S, U0]((system: T0, th: S, ports: Seq[U0]) => Nil) ++ - up(MultiChipParameters(chip0)) - ) - case MultiChipParameters(`chip1`) => new Config( - new OverrideHarnessBinder[T1, S, U1]((system: T1, th: S, ports: Seq[U1]) => Nil) ++ - up(MultiChipParameters(chip1)) - ) - // Set the multiharnessbinder key - case MultiHarnessBinders(`chip0`, `chip1`) => up(MultiHarnessBinders(chip0, chip1)) + - ((tag0.runtimeClass.toString, tag1.runtimeClass.toString) -> - ((c0: Any, c1: Any, th: HasHarnessInstantiators, ports0: Seq[Data], ports1: Seq[Data]) => { - val pts0 = ports0.map(_.asInstanceOf[U0]) - val pts1 = ports1.map(_.asInstanceOf[U1]) - require(pts0.size == pts1.size) - (c0, c1, th) match { - case (c0: T0, c1: T1, th: S) => fn(c0, c1, th, pts0, pts1) - case _ => - } - }) - ) - }) +case class MultiHarnessBinders(chip0: Int, chip1: Int) extends Field[Seq[MultiHarnessBinderFunction]](Nil) object ApplyMultiHarnessBinders { def apply(th: HasHarnessInstantiators, chips: Seq[LazyModule])(implicit p: Parameters): Unit = { Seq.tabulate(chips.size, chips.size) { case (i, j) => if (i != j) { (chips(i), chips(j)) match { - case (l0: HasIOBinders, l1: HasIOBinders) => p(MultiHarnessBinders(i, j)).foreach { - case ((s0, s1), f) => { - f(l0.lazySystem , l1.lazySystem , th, l0.portMap(s0), l1.portMap(s1)) - f(l0.lazySystem.module, l1.lazySystem.module, th, l0.portMap(s0), l1.portMap(s1)) - } + case (l0: HasIOBinders, l1: HasIOBinders) => p(MultiHarnessBinders(i, j)).foreach { f => + f(l0.portMap.values.flatten.toSeq, l1.portMap.values.flatten.toSeq) } - case _ => } }} } } -class WithMultiChipSerialTL(chip0: Int, chip1: Int) extends MultiHarnessBinder(chip0, chip1, ( - (system0: CanHavePeripheryTLSerial, system1: CanHavePeripheryTLSerial, - th: HasHarnessInstantiators, - ports0: Seq[ClockedIO[SerialIO]], ports1: Seq[ClockedIO[SerialIO]] - ) => { - require(ports0.size == ports1.size) - (ports0 zip ports1).map { case (l, r) => - l.clock <> r.clock - require(l.bits.w == r.bits.w) - l.bits.flipConnect(r.bits) +class MultiHarnessBinder[T <: Port[_]]( + chip0: Int, chip1: Int, + chip0portFn: T => Boolean, chip1portFn: T => Boolean, + connectFn: (T, T) => Unit +)(implicit tag: ClassTag[T]) extends Config((site, here, up) => { + // Override any HarnessBinders for chip0/chip1 + case MultiChipParameters(`chip0`) => new Config( + new HarnessBinder({case (th, port: T) if chip0portFn(port) => }) ++ up(MultiChipParameters(chip0)) + ) + case MultiChipParameters(`chip1`) => new Config( + new HarnessBinder({case (th, port: T) if chip1portFn(port) => }) ++ up(MultiChipParameters(chip1)) + ) + // Set the multiharnessbinder key + case MultiHarnessBinders(`chip0`, `chip1`) => up(MultiHarnessBinders(chip0, chip1)) :+ { + ((chip0Ports: Seq[Port[_]], chip1Ports: Seq[Port[_]]) => { + val chip0Port: Seq[T] = chip0Ports.collect { case (p: T) if chip0portFn(p) => p } + val chip1Port: Seq[T] = chip1Ports.collect { case (p: T) if chip1portFn(p) => p } + require(chip0Port.size == 1 && chip1Port.size == 1) + connectFn(chip0Port(0), chip1Port(0)) + }) } + }) + + +class WithMultiChipSerialTL(chip0: Int, chip1: Int, chip0portId: Int = 0, chip1portId: Int = 0) extends MultiHarnessBinder[SerialTLPort]( + chip0, chip1, + (p0: SerialTLPort) => p0.portId == chip0portId, + (p1: SerialTLPort) => p1.portId == chip1portId, + (p0: SerialTLPort, p1: SerialTLPort) => { + (DataMirror.directionOf(p0.io.clock), DataMirror.directionOf(p1.io.clock)) match { + case (Direction.Input, Direction.Output) => p0.io.clock := p1.io.clock + case (Direction.Output, Direction.Input) => p1.io.clock := p0.io.clock + } + p0.io.bits.in <> p1.io.bits.out + p1.io.bits.in <> p0.io.bits.out } -)) +) diff --git a/generators/chipyard/src/main/scala/harness/package.scala b/generators/chipyard/src/main/scala/harness/package.scala index 789d1595..258655a7 100644 --- a/generators/chipyard/src/main/scala/harness/package.scala +++ b/generators/chipyard/src/main/scala/harness/package.scala @@ -5,13 +5,7 @@ import scala.collection.immutable.ListMap package object harness { - type HarnessBinderFunction = (Any, HasHarnessInstantiators, Seq[Data]) => Unit - type HarnessBinderMap = Map[String, HarnessBinderFunction] - def HarnessBinderMapDefault: HarnessBinderMap = (new ListMap[String, HarnessBinderFunction]) - .withDefaultValue((t: Any, th: HasHarnessInstantiators, d: Seq[Data]) => ()) - - type MultiHarnessBinderFunction = (Any, Any, HasHarnessInstantiators, Seq[Data], Seq[Data]) => Unit - type MultiHarnessBinderMap = Map[(String, String), MultiHarnessBinderFunction] - def MultiHarnessBinderMapDefault: MultiHarnessBinderMap = (new ListMap[(String, String), MultiHarnessBinderFunction]) - .withDefaultValue((_: Any, _: Any, _: HasHarnessInstantiators, _: Seq[Data], _: Seq[Data]) => ()) + import chipyard.iobinders.Port + type HarnessBinderFunction = PartialFunction[(HasHarnessInstantiators, Port[_]), Unit] + type MultiHarnessBinderFunction = (Seq[Port[_]], Seq[Port[_]]) => Unit } diff --git a/generators/chipyard/src/main/scala/IOBinders.scala b/generators/chipyard/src/main/scala/iobinders/IOBinders.scala similarity index 70% rename from generators/chipyard/src/main/scala/IOBinders.scala rename to generators/chipyard/src/main/scala/iobinders/IOBinders.scala index b74d8f0e..773f3d39 100644 --- a/generators/chipyard/src/main/scala/IOBinders.scala +++ b/generators/chipyard/src/main/scala/iobinders/IOBinders.scala @@ -18,18 +18,19 @@ import freechips.rocketchip.tilelink.{TLBundle} import sifive.blocks.devices.gpio._ import sifive.blocks.devices.uart._ import sifive.blocks.devices.spi._ +import sifive.blocks.devices.i2c._ import tracegen.{TraceGenSystemModuleImp} import barstools.iocell.chisel._ import testchipip._ import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly} -import chipyard.{CanHaveMasterTLMemPort} +import chipyard.{CanHaveMasterTLMemPort, ChipyardSystem, ChipyardSystemModule} import scala.reflect.{ClassTag} object IOBinderTypes { - type IOBinderTuple = (Seq[Data], Seq[IOCell]) + type IOBinderTuple = (Seq[Port[_]], Seq[IOCell]) type IOBinderFunction = (Boolean, => Any) => ModuleValue[IOBinderTuple] } import IOBinderTypes._ @@ -82,7 +83,7 @@ abstract trait HasIOBinders { this: LazyModule => InModuleBody { if (p(DontTouchIOBindersPorts)) { - portMap.values.foreach(_.foreach(dontTouch(_))) + portMap.values.flatten.foreach { case (port: Port[Data]) => dontTouch(port.io) } } println("IOCells generated by IOBinders:") @@ -171,29 +172,72 @@ class WithGPIOCells extends OverrideIOBinder({ iocell.io.ie := pin.o.ie pin.i.ival := iocell.io.i iocell.io.pad <> g - (g, iocell) + (GPIOPort(g, i, j), iocell) }).unzip }).unzip - val ports: Seq[Analog] = ports2d.flatten - (ports, cells2d.flatten) + (ports2d.flatten, cells2d.flatten) + } +}) + +class WithGPIOPunchthrough extends OverrideIOBinder({ + (system: HasPeripheryGPIOModuleImp) => { + val ports = system.gpio.zipWithIndex.map { case (gpio, i) => + val io_gpio = IO(gpio.cloneType).suggestName(s"gpio_$i") + io_gpio <> gpio + GPIOPinsPort(io_gpio, i) + } + (ports, Nil) + } +}) + +class WithI2CPunchthrough extends OverrideIOBinder({ + (system: HasPeripheryI2CModuleImp) => { + val ports = system.i2c.zipWithIndex.map { case (i2c, i) => + val io_i2c = IO(i2c.cloneType).suggestName(s"i2c_$i") + io_i2c <> i2c + I2CPort(i2c) + } + (ports, Nil) } }) // DOC include start: WithUARTIOCells class WithUARTIOCells extends OverrideIOBinder({ (system: HasPeripheryUARTModuleImp) => { - val (ports: Seq[UARTPortIO], cells2d) = system.uart.zipWithIndex.map({ case (u, i) => + val (ports: Seq[UARTPort], cells2d) = system.uart.zipWithIndex.map({ case (u, i) => val (port, ios) = IOCell.generateIOFromSignal(u, s"uart_${i}", system.p(IOCellKey), abstractResetAsAsync = true) - (port, ios) + val where = PBUS // TODO fix + val bus = system.outer.asInstanceOf[HasTileLinkLocations].locateTLBusWrapper(where) + val freqMHz = bus.dtsFrequency.get / 1000000 + (UARTPort(port, i, freqMHz.toInt), ios) }).unzip (ports, cells2d.flatten) } }) // DOC include end: WithUARTIOCells -class WithSPIIOCells extends OverrideIOBinder({ +class WithSPIIOPunchthrough extends OverrideLazyIOBinder({ + (system: HasPeripherySPI) => { + // attach resource to 1st SPI + if (system.tlSpiNodes.size > 0) ResourceBinding { + Resource(new MMCDevice(system.tlSpiNodes.head.device, 1), "reg").bind(ResourceAddress(0)) + } + InModuleBody { + val spi = system.asInstanceOf[BaseSubsystem].module.asInstanceOf[HasPeripherySPIBundle].spi + val ports = spi.zipWithIndex.map({ case (s, i) => + val io_spi = IO(s.cloneType).suggestName(s"spi_$i") + io_spi <> s + SPIPort(io_spi) + }) + (ports, Nil) + } + } +}) + +class WithSPIFlashIOCells extends OverrideIOBinder({ (system: HasPeripherySPIFlashModuleImp) => { - val (ports: Seq[SPIChipIO], cells2d) = system.qspi.zipWithIndex.map({ case (s, i) => + val (ports: Seq[SPIFlashPort], cells2d) = system.qspi.zipWithIndex.map({ case (s, i) => + val name = s"spi_${i}" val port = IO(new SPIChipIO(s.c.csWidth)).suggestName(name) val iocellBase = s"iocell_${name}" @@ -213,7 +257,7 @@ class WithSPIIOCells extends OverrideIOBinder({ iocell } - (port, dqIOs ++ csIOs ++ sckIOs) + (SPIFlashPort(port, system.p(PeripherySPIFlashKey)(i), i), dqIOs ++ csIOs ++ sckIOs) }).unzip (ports, cells2d.flatten) } @@ -223,7 +267,7 @@ class WithExtInterruptIOCells extends OverrideIOBinder({ (system: HasExtInterruptsModuleImp) => { if (system.outer.nExtInterrupts > 0) { val (port: UInt, cells) = IOCell.generateIOFromSignal(system.interrupts, "ext_interrupts", system.p(IOCellKey), abstractResetAsAsync = true) - (Seq(port), cells) + (Seq(ExtIntPort(port)), cells) } else { system.interrupts := DontCare // why do I have to drive this 0-wide wire??? (Nil, Nil) @@ -273,7 +317,8 @@ class WithDebugIOCells extends OverrideLazyIOBinder({ // Add IOCells for the DMI/JTAG/APB ports val dmiTuple = debug.clockeddmi.map { d => - IOCell.generateIOFromSignal(d, "dmi", p(IOCellKey), abstractResetAsAsync = true) + val (port, cells) = IOCell.generateIOFromSignal(d, "dmi", p(IOCellKey), abstractResetAsAsync = true) + (DMIPort(port), cells) } val jtagTuple = debug.systemjtag.map { j => @@ -282,14 +327,13 @@ class WithDebugIOCells extends OverrideLazyIOBinder({ 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) + val (port, cells) = IOCell.generateIOFromSignal(jtag_wire, "jtag", p(IOCellKey), abstractResetAsAsync = true) + (JTAGPort(port), cells) } - val apbTuple = debug.apb.map { a => - IOCell.generateIOFromSignal(a, "apb", p(IOCellKey), abstractResetAsAsync = true) - } + require(!debug.apb.isDefined) - val allTuples = (dmiTuple ++ jtagTuple ++ apbTuple).toSeq + val allTuples = (dmiTuple ++ jtagTuple).toSeq (allTuples.map(_._1).toSeq, allTuples.flatMap(_._2).toSeq) }).getOrElse((Nil, Nil)) }}} @@ -297,20 +341,26 @@ class WithDebugIOCells extends OverrideLazyIOBinder({ }) class WithSerialTLIOCells extends OverrideIOBinder({ - (system: CanHavePeripheryTLSerial) => system.serial_tl.map({ s => - val sys = system.asInstanceOf[BaseSubsystem] - val (port, cells) = IOCell.generateIOFromSignal(s.getWrappedValue, "serial_tl", sys.p(IOCellKey), abstractResetAsAsync = true) - (Seq(port), cells) - }).getOrElse((Nil, Nil)) + (system: CanHavePeripheryTLSerial) => { + val (ports, cells) = system.serial_tl.zipWithIndex.map({ case (s, id) => + val sys = system.asInstanceOf[BaseSubsystem] + val (port, cells) = IOCell.generateIOFromSignal(s.getWrappedValue, "serial_tl", sys.p(IOCellKey), abstractResetAsAsync = true) + (SerialTLPort(port, sys.p(SerialTLKey).get, system.serdesser.get, id), cells) + }).unzip + (ports.toSeq, cells.flatten.toSeq) + } }) class WithSerialTLPunchthrough extends OverrideIOBinder({ - (system: CanHavePeripheryTLSerial) => system.serial_tl.map({ s => - val sys = system.asInstanceOf[BaseSubsystem] - val port = IO(s.getWrappedValue.cloneType) - port <> s.getWrappedValue - (Seq(port), Nil) - }).getOrElse((Nil, Nil)) + (system: CanHavePeripheryTLSerial) => { + val (ports, cells) = system.serial_tl.zipWithIndex.map({ case (s, id) => + val sys = system.asInstanceOf[BaseSubsystem] + val port = IO(s.getWrappedValue.cloneType) + port <> s.getWrappedValue + (SerialTLPort(port, sys.p(SerialTLKey).get, system.serdesser.get, id), Nil) + }).unzip + (ports.toSeq, cells.flatten.toSeq) + } }) class WithAXI4MemPunchthrough extends OverrideLazyIOBinder({ @@ -321,12 +371,11 @@ class WithAXI4MemPunchthrough extends OverrideLazyIOBinder({ def clockBundle = clockSinkNode.get.in.head._1 InModuleBody { - val ports: Seq[ClockedAndResetIO[AXI4Bundle]] = system.mem_axi4.zipWithIndex.map({ case (m, i) => - val p = IO(new ClockedAndResetIO(DataMirror.internal.chiselTypeClone[AXI4Bundle](m))).suggestName(s"axi4_mem_${i}") - p.bits <> m - p.clock := clockBundle.clock - p.reset := clockBundle.reset - p + val ports: Seq[AXI4MemPort] = system.mem_axi4.zipWithIndex.map({ case (m, i) => + val port = IO(new ClockedIO(DataMirror.internal.chiselTypeClone[AXI4Bundle](m))).suggestName(s"axi4_mem_${i}") + port.bits <> m + port.clock := clockBundle.clock + AXI4MemPort(port, p(ExtMem).get, system.memAXI4Node.edges.in(i), p(MemoryBusKey).dtsFrequency.get.toInt) }).toSeq (ports, Nil) } @@ -341,12 +390,11 @@ class WithAXI4MMIOPunchthrough extends OverrideLazyIOBinder({ def clockBundle = clockSinkNode.get.in.head._1 InModuleBody { - val ports: Seq[ClockedAndResetIO[AXI4Bundle]] = system.mmio_axi4.zipWithIndex.map({ case (m, i) => - val p = IO(new ClockedAndResetIO(DataMirror.internal.chiselTypeClone[AXI4Bundle](m))).suggestName(s"axi4_mmio_${i}") - p.bits <> m - p.clock := clockBundle.clock - p.reset := clockBundle.reset - p + val ports: Seq[AXI4MMIOPort] = system.mmio_axi4.zipWithIndex.map({ case (m, i) => + val port = IO(new ClockedIO(DataMirror.internal.chiselTypeClone[AXI4Bundle](m))).suggestName(s"axi4_mmio_${i}") + port.bits <> m + port.clock := clockBundle.clock + AXI4MMIOPort(port, p(ExtBus).get, system.mmioAXI4Node.edges.in(i)) }).toSeq (ports, Nil) } @@ -361,11 +409,11 @@ class WithL2FBusAXI4Punchthrough extends OverrideLazyIOBinder({ def clockBundle = clockSinkNode.get.in.head._1 InModuleBody { - val ports: Seq[ClockedIO[AXI4Bundle]] = system.l2_frontend_bus_axi4.zipWithIndex.map({ case (m, i) => - val p = IO(new ClockedIO(Flipped(DataMirror.internal.chiselTypeClone[AXI4Bundle](m)))).suggestName(s"axi4_fbus_${i}") - m <> p.bits - p.clock := clockBundle.clock - p + val ports: Seq[AXI4InPort] = system.l2_frontend_bus_axi4.zipWithIndex.map({ case (m, i) => + val port = IO(new ClockedIO(Flipped(DataMirror.internal.chiselTypeClone[AXI4Bundle](m)))).suggestName(s"axi4_fbus_${i}") + m <> port.bits + port.clock := clockBundle.clock + AXI4InPort(port, p(ExtIn).get) }).toSeq (ports, Nil) } @@ -374,10 +422,12 @@ class WithL2FBusAXI4Punchthrough extends OverrideLazyIOBinder({ class WithBlockDeviceIOPunchthrough extends OverrideIOBinder({ (system: CanHavePeripheryBlockDevice) => { - val ports: Seq[ClockedIO[BlockDeviceIO]] = system.bdev.map({ bdev => - val p = IO(new ClockedIO(new BlockDeviceIO()(GetSystemParameters(system)))).suggestName("blockdev") - p <> bdev - p + val ports: Seq[BlockDevicePort] = system.bdev.map({ bdev => + val p = GetSystemParameters(system) + val bdParams = p(BlockDeviceKey).get + val port = IO(new ClockedIO(new BlockDeviceIO(bdParams))).suggestName("blockdev") + port <> bdev + BlockDevicePort(port, bdParams) }).toSeq (ports, Nil) } @@ -385,10 +435,11 @@ class WithBlockDeviceIOPunchthrough extends OverrideIOBinder({ class WithNICIOPunchthrough extends OverrideIOBinder({ (system: CanHavePeripheryIceNIC) => { - val ports: Seq[ClockedIO[NICIOvonly]] = system.icenicOpt.map({ n => - val p = IO(new ClockedIO(new NICIOvonly)).suggestName("nic") - p <> n - p + val ports: Seq[NICPort] = system.icenicOpt.map({ n => + val p = GetSystemParameters(system) + val port = IO(new ClockedIO(new NICIOvonly)).suggestName("nic") + port <> n + NICPort(port, p(NICKey).get) }).toSeq (ports, Nil) } @@ -398,16 +449,30 @@ class WithTraceGenSuccessPunchthrough extends OverrideIOBinder({ (system: TraceGenSystemModuleImp) => { val success: Bool = IO(Output(Bool())).suggestName("success") success := system.success - (Seq(success), Nil) + (Seq(SuccessPort(success)), Nil) } }) class WithTraceIOPunchthrough extends OverrideIOBinder({ (system: CanHaveTraceIOModuleImp) => { - val ports: Option[TraceOutputTop] = system.traceIO.map { t => + val ports: Option[TracePort] = system.traceIO.map { t => val trace = IO(DataMirror.internal.chiselTypeClone[TraceOutputTop](t)).suggestName("trace") trace <> t - trace + val p = GetSystemParameters(system) + val chipyardSystem = system.asInstanceOf[ChipyardSystemModule[_]].outer.asInstanceOf[ChipyardSystem] + val tiles = chipyardSystem.tiles + val cfg = SpikeCosimConfig( + isa = tiles.headOption.map(_.isaDTS).getOrElse(""), + vlen = tiles.headOption.map(_.tileParams.core.vLen).getOrElse(0), + 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 + ) + TracePort(trace, cfg) } (ports.toSeq, Nil) } @@ -417,7 +482,7 @@ class WithCustomBootPin extends OverrideIOBinder({ (system: CanHavePeripheryCustomBootPin) => system.custom_boot_pin.map({ p => val sys = system.asInstanceOf[BaseSubsystem] val (port, cells) = IOCell.generateIOFromSignal(p.getWrappedValue, "custom_boot", sys.p(IOCellKey), abstractResetAsAsync = true) - (Seq(port), cells) + (Seq(CustomBootPort(port)), cells) }).getOrElse((Nil, Nil)) }) @@ -426,7 +491,7 @@ class WithUARTTSIPunchthrough extends OverrideIOBinder({ val sys = system.asInstanceOf[BaseSubsystem] val uart_tsi = IO(new UARTTSIIO(p.uartParams)) uart_tsi <> p - (Seq(uart_tsi), Nil) + (Seq(UARTTSIPort(uart_tsi)), Nil) }).getOrElse((Nil, Nil)) }) @@ -434,7 +499,7 @@ class WithTLMemPunchthrough extends OverrideIOBinder({ (system: CanHaveMasterTLMemPort) => { val io_tl_mem_pins_temp = IO(DataMirror.internal.chiselTypeClone[HeterogeneousBag[TLBundle]](system.mem_tl)).suggestName("tl_slave") io_tl_mem_pins_temp <> system.mem_tl - (Seq(io_tl_mem_pins_temp), Nil) + (Seq(TLMemPort(io_tl_mem_pins_temp)), Nil) } }) diff --git a/generators/chipyard/src/main/scala/iobinders/Ports.scala b/generators/chipyard/src/main/scala/iobinders/Ports.scala new file mode 100644 index 00000000..61c25a36 --- /dev/null +++ b/generators/chipyard/src/main/scala/iobinders/Ports.scala @@ -0,0 +1,94 @@ +package chipyard.iobinders + +import chisel3._ +import chisel3.experimental.{Analog} +import sifive.blocks.devices.uart.{UARTPortIO} +import sifive.blocks.devices.spi.{SPIFlashParams, SPIPortIO} +import sifive.blocks.devices.i2c.{I2CPort} +import sifive.blocks.devices.gpio.{GPIOPortIO} +import testchipip._ +import icenet.{NICIOvonly, NICConfig} +import org.chipsalliance.cde.config.{Parameters} +import freechips.rocketchip.amba.axi4.{AXI4Bundle, AXI4EdgeParameters} +import freechips.rocketchip.subsystem.{MemoryPortParams, MasterPortParams, SlavePortParams} +import freechips.rocketchip.devices.debug.{ClockedDMIIO} +import freechips.rocketchip.util.{HeterogeneousBag} +import freechips.rocketchip.tilelink.{TLBundle} + +trait Port[T <: Data] { + val io: T +} + +// These case classes are generated by IOBinders, and interpreted by HarnessBinders +case class GPIOPort (val io: Analog, val gpioId: Int, val pinId: Int) + extends Port[Analog] + +case class GPIOPinsPort (val io: GPIOPortIO, val gpioId: Int) + extends Port[GPIOPortIO] + +case class I2CPort (val io: sifive.blocks.devices.i2c.I2CPort) + extends Port[sifive.blocks.devices.i2c.I2CPort] + +case class UARTPort (val io: UARTPortIO, val uartNo: Int, val freqMHz: Int) + extends Port[UARTPortIO] + +case class SPIFlashPort (val io: SPIChipIO, val params: SPIFlashParams, val spiId: Int) + extends Port[SPIChipIO] + +case class SPIPort (val io: SPIPortIO) + extends Port[SPIPortIO] + +case class BlockDevicePort (val io: ClockedIO[BlockDeviceIO], val params: BlockDeviceConfig) + extends Port[ClockedIO[BlockDeviceIO]] + +case class NICPort (val io: ClockedIO[NICIOvonly], val params: NICConfig) + extends Port[ClockedIO[NICIOvonly]] + +case class AXI4MemPort (val io: ClockedIO[AXI4Bundle], val params: MemoryPortParams, val edge: AXI4EdgeParameters, val clockFreqMHz: Int) + extends Port[ClockedIO[AXI4Bundle]] + +case class AXI4MMIOPort (val io: ClockedIO[AXI4Bundle], val params: MasterPortParams, val edge: AXI4EdgeParameters) + extends Port[ClockedIO[AXI4Bundle]] + +case class AXI4InPort (val io: ClockedIO[AXI4Bundle], val params: SlavePortParams) + extends Port[ClockedIO[AXI4Bundle]] + +case class ExtIntPort (val io: UInt) + extends Port[UInt] + +case class DMIPort (val io: ClockedDMIIO) + extends Port[ClockedDMIIO] + +case class JTAGPort (val io: JTAGChipIO) + extends Port[JTAGChipIO] + +case class SerialTLPort (val io: ClockedIO[SerialIO], val params: SerialTLParams, val serdesser: TLSerdesser, val portId: Int) + extends Port[ClockedIO[SerialIO]] + +case class UARTTSIPort (val io: UARTTSIIO) + extends Port[UARTTSIIO] + +case class SuccessPort (val io: Bool) + extends Port[Bool] + +case class TracePort (val io: TraceOutputTop, val cosimCfg: SpikeCosimConfig) + extends Port[TraceOutputTop] + +case class CustomBootPort (val io: Bool) + extends Port[Bool] + +case class ClockPort (val io: Clock, val freqMHz: Double) + extends Port[Clock] + +case class ResetPort (val io: AsyncReset) + extends Port[Reset] + +case class DebugResetPort (val io: Reset) + extends Port[Reset] + +case class JTAGResetPort (val io: Reset) + extends Port[Reset] + +case class TLMemPort (val io: HeterogeneousBag[TLBundle]) + extends Port[HeterogeneousBag[TLBundle]] + diff --git a/generators/firechip/src/main/scala/BridgeBinders.scala b/generators/firechip/src/main/scala/BridgeBinders.scala index db6ec2fb..e4169ec2 100644 --- a/generators/firechip/src/main/scala/BridgeBinders.scala +++ b/generators/firechip/src/main/scala/BridgeBinders.scala @@ -3,7 +3,6 @@ package firesim.firesim import chisel3._ -import chisel3.experimental.annotate import chisel3.experimental.{DataMirror, Direction} import chisel3.util.experimental.BoringUtils @@ -12,7 +11,6 @@ import freechips.rocketchip.diplomacy.{LazyModule} import freechips.rocketchip.devices.debug.{Debug, HasPeripheryDebug, ExportDebug, DMI} import freechips.rocketchip.amba.axi4.{AXI4Bundle} import freechips.rocketchip.subsystem._ -import freechips.rocketchip.tile.{RocketTile} import freechips.rocketchip.prci.{ClockBundle, ClockBundleParameters} import freechips.rocketchip.util.{ResetCatchAndSync} import sifive.blocks.devices.uart._ @@ -22,21 +20,19 @@ import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvon import junctions.{NastiKey, NastiParameters} import midas.models.{FASEDBridge, AXI4EdgeSummary, CompleteConfig} -import midas.targetutils.{MemModelAnnotation, EnableModelMultiThreadingAnnotation} import firesim.bridges._ import firesim.configs.MemModelKey import tracegen.{TraceGenSystemModuleImp} import cva6.CVA6Tile -import boom.common.{BoomTile} import barstools.iocell.chisel._ -import chipyard.iobinders.{IOBinders, OverrideIOBinder, ComposeIOBinder, GetSystemParameters, IOCellKey} +import chipyard.iobinders._ import chipyard._ import chipyard.harness._ object MainMemoryConsts { val regionNamePrefix = "MainMemory" - def globalName()(implicit p: Parameters) = s"${regionNamePrefix}_${p(MultiChipIdx)}" + def globalName(chipId: Int) = s"${regionNamePrefix}_$chipId" } trait Unsupported { @@ -69,130 +65,70 @@ class WithFireSimIOCellModels extends Config((site, here, up) => { case IOCellKey => FireSimIOCellParams() }) -class WithTSIBridgeAndHarnessRAMOverSerialTL extends OverrideHarnessBinder({ - (system: CanHavePeripheryTLSerial, th: FireSim, ports: Seq[ClockedIO[SerialIO]]) => { - ports.map { port => - implicit val p = GetSystemParameters(system) - val bits = port.bits - port.clock := th.harnessBinderClock - val ram = TSIHarness.connectRAM(system.serdesser.get, bits, th.harnessBinderReset) - TSIBridge(th.harnessBinderClock, ram.module.io.tsi, p(ExtMem).map(_ => MainMemoryConsts.globalName), th.harnessBinderReset.asBool) - } - Nil +class WithTSIBridgeAndHarnessRAMOverSerialTL extends HarnessBinder({ + case (th: FireSim, port: SerialTLPort) => { + val bits = port.io.bits + port.io.clock := th.harnessBinderClock + val ram = LazyModule(new SerialRAM(port.serdesser)(Parameters.empty)) + Module(ram.module) + ram.module.io.ser <> port.io.bits + + // This assumes that: + // If ExtMem for the target is defined, then FASED bridge will be attached + // If FASED bridge is attached, loadmem widget is present + val hasMainMemory = th.chipParameters(th.p(MultiChipIdx))(ExtMem).isDefined + val mainMemoryName = Option.when(hasMainMemory)(MainMemoryConsts.globalName(th.p(MultiChipIdx))) + TSIBridge(th.harnessBinderClock, ram.module.io.tsi, mainMemoryName, th.harnessBinderReset.asBool)(th.p) } }) -class WithNICBridge extends OverrideHarnessBinder({ - (system: CanHavePeripheryIceNIC, th: FireSim, ports: Seq[ClockedIO[NICIOvonly]]) => { - val p: Parameters = GetSystemParameters(system) - ports.map { n => NICBridge(n.clock, n.bits)(p) } - Nil +class WithNICBridge extends HarnessBinder({ + case (th: FireSim, port: NICPort) => { + NICBridge(port.io.clock, port.io.bits)(th.p) } }) -class WithUARTBridge extends OverrideHarnessBinder({ - (system: HasPeripheryUARTModuleImp, th: FireSim, ports: Seq[UARTPortIO]) => - val uartSyncClock = Wire(Clock()) - uartSyncClock := false.B.asClock - val pbusClockNode = system.outer.asInstanceOf[HasTileLinkLocations].locateTLBusWrapper(PBUS).fixedClockNode - val pbusClock = pbusClockNode.in.head._1.clock - BoringUtils.bore(pbusClock, Seq(uartSyncClock)) - ports.map { p => UARTBridge(uartSyncClock, p, th.harnessBinderReset.asBool)(system.p) }; Nil +class WithUARTBridge extends HarnessBinder({ + case (th: FireSim, port: UARTPort) => + val uartSyncClock = th.harnessClockInstantiator.requestClockMHz("uart_clock", port.freqMHz) + UARTBridge(uartSyncClock, port.io, th.harnessBinderReset.asBool, port.freqMHz)(th.p) }) -class WithBlockDeviceBridge extends OverrideHarnessBinder({ - (system: CanHavePeripheryBlockDevice, th: FireSim, ports: Seq[ClockedIO[BlockDeviceIO]]) => { - implicit val p: Parameters = GetSystemParameters(system) - ports.map { b => BlockDevBridge(b.clock, b.bits, th.harnessBinderReset.asBool) } - Nil +class WithBlockDeviceBridge extends HarnessBinder({ + case (th: FireSim, port: BlockDevicePort) => { + BlockDevBridge(port.io.clock, port.io.bits, th.harnessBinderReset.asBool) } }) -class WithFASEDBridge extends OverrideHarnessBinder({ - (system: CanHaveMasterAXI4MemPort, th: FireSim, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => { - implicit val p: Parameters = GetSystemParameters(system) - (ports zip system.memAXI4Node.edges.in).map { case (axi4, edge) => - val nastiKey = NastiParameters(axi4.bits.r.bits.data.getWidth, - axi4.bits.ar.bits.addr.getWidth, - axi4.bits.ar.bits.id.getWidth) - system match { - case s: BaseSubsystem => FASEDBridge(axi4.clock, axi4.bits, axi4.reset.asBool, - CompleteConfig(p(firesim.configs.MemModelKey), - nastiKey, - Some(AXI4EdgeSummary(edge)), - Some(MainMemoryConsts.globalName))) - case _ => throw new Exception("Attempting to attach FASED Bridge to misconfigured design") - } - } - Nil +class WithFASEDBridge extends HarnessBinder({ + case (th: FireSim, port: AXI4MemPort) => { + val nastiKey = NastiParameters(port.io.bits.r.bits.data.getWidth, + port.io.bits.ar.bits.addr.getWidth, + port.io.bits.ar.bits.id.getWidth) + FASEDBridge(port.io.clock, port.io.bits, th.harnessBinderReset.asBool, + CompleteConfig(th.p(firesim.configs.MemModelKey), + nastiKey, + Some(AXI4EdgeSummary(port.edge)), + Some(MainMemoryConsts.globalName(th.p(MultiChipIdx)))))(th.p) } }) -class WithTracerVBridge extends ComposeHarnessBinder({ - (system: CanHaveTraceIOModuleImp, th: FireSim, ports: Seq[TraceOutputTop]) => { - ports.map { p => p.traces.map(tileTrace => TracerVBridge(tileTrace)(system.p)) } - Nil +class WithTracerVBridge extends HarnessBinder({ + case (th: FireSim, port: TracePort) => { + port.io.traces.map(tileTrace => TracerVBridge(tileTrace)(th.p)) } }) -class WithCospikeBridge extends ComposeHarnessBinder({ - (system: CanHaveTraceIOModuleImp, th: FireSim, 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(""), - vlen = tiles.headOption.map(_.tileParams.core.vLen).getOrElse(0), - 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 => CospikeBridge(t._1, t._2, cfg)) } +class WithCospikeBridge extends HarnessBinder({ + case (th: FireSim, port: TracePort) => { + port.io.traces.zipWithIndex.map(t => CospikeBridge(t._1, t._2, port.cosimCfg)) } }) -class WithTraceGenBridge extends OverrideHarnessBinder({ - (system: TraceGenSystemModuleImp, th: FireSim, ports: Seq[Bool]) => - ports.map { p => GroundTestBridge(th.harnessBinderClock, p)(system.p) }; Nil -}) - -class WithFireSimMultiCycleRegfile extends ComposeIOBinder({ - (system: HasTilesModuleImp) => { - system.outer.tiles.map { - case r: RocketTile => { - annotate(MemModelAnnotation(r.module.core.rocketImpl.rf.rf)) - r.module.fpuOpt.foreach(fpu => annotate(MemModelAnnotation(fpu.fpuImpl.regfile))) - } - case b: BoomTile => { - val core = b.module.core - core.iregfile match { - case irf: boom.exu.RegisterFileSynthesizable => annotate(MemModelAnnotation(irf.regfile)) - } - if (core.fp_pipeline != null) core.fp_pipeline.fregfile match { - case frf: boom.exu.RegisterFileSynthesizable => annotate(MemModelAnnotation(frf.regfile)) - } - } - case _ => - } - (Nil, Nil) - } -}) - -class WithFireSimFAME5 extends ComposeIOBinder({ - (system: HasTilesModuleImp) => { - system.outer.tiles.map { - case b: BoomTile => - annotate(EnableModelMultiThreadingAnnotation(b.module)) - case r: RocketTile => - annotate(EnableModelMultiThreadingAnnotation(r.module)) - case _ => Nil - } - (Nil, Nil) +class WithSuccessBridge extends HarnessBinder({ + case (th: FireSim, port: SuccessPort) => { + GroundTestBridge(th.harnessBinderClock, port.io)(th.p) } }) diff --git a/generators/firechip/src/main/scala/FireSim.scala b/generators/firechip/src/main/scala/FireSim.scala index c20abca1..4cca7557 100644 --- a/generators/firechip/src/main/scala/FireSim.scala +++ b/generators/firechip/src/main/scala/FireSim.scala @@ -5,21 +5,26 @@ package firesim.firesim import scala.collection.mutable.{LinkedHashMap} import chisel3._ -import chisel3.experimental.{IO} +import chisel3.experimental.{IO, annotate} import freechips.rocketchip.prci._ -import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey} +import freechips.rocketchip.subsystem.{BaseSubsystem, SubsystemDriveAsyncClockGroupsKey, HasTiles} import org.chipsalliance.cde.config.{Field, Config, Parameters} import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, InModuleBody, ValName} import freechips.rocketchip.util.{ResetCatchAndSync, RecordMap} +import freechips.rocketchip.tile.{RocketTile} +import boom.common.{BoomTile} import midas.widgets.{Bridge, PeekPokeBridge, RationalClockBridge, RationalClock, ResetPulseBridge, ResetPulseBridgeParameters} - +import midas.targetutils.{MemModelAnnotation, EnableModelMultiThreadingAnnotation} import chipyard._ import chipyard.harness._ import chipyard.iobinders._ import chipyard.clocking._ +case object FireSimMultiCycleRegFile extends Field[Boolean](false) +case object FireSimFAME5 extends Field[Boolean](false) + /** * Under FireSim's current multiclock implementation there can be only a * single clock bridge. This requires, therefore, that it be instantiated in @@ -85,11 +90,46 @@ class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessInsta override val supportsMultiChip = true - instantiateChipTops() + val chiptops = instantiateChipTops() // Ensures FireSim-synthesized assertions and instrumentation is disabled // while resetBridge.io.reset is asserted. This ensures assertions do not fire at // time zero in the event their local reset is delayed (typically because it // has been pipelined) midas.targetutils.GlobalResetCondition(resetBridge.io.reset) + + + // FireSim multi-cycle regfile optimization + // FireSim ModelMultithreading + chiptops.foreach { + case c: ChipTop => c.lazySystem match { + case ls: HasTiles => { + if (p(FireSimMultiCycleRegFile)) ls.tiles.map { + case r: RocketTile => { + annotate(MemModelAnnotation(r.module.core.rocketImpl.rf.rf)) + r.module.fpuOpt.foreach(fpu => annotate(MemModelAnnotation(fpu.fpuImpl.regfile))) + } + case b: BoomTile => { + val core = b.module.core + core.iregfile match { + case irf: boom.exu.RegisterFileSynthesizable => annotate(MemModelAnnotation(irf.regfile)) + } + if (core.fp_pipeline != null) core.fp_pipeline.fregfile match { + case frf: boom.exu.RegisterFileSynthesizable => annotate(MemModelAnnotation(frf.regfile)) + } + } + case _ => + } + if (p(FireSimFAME5)) ls.tiles.map { + case b: BoomTile => + annotate(EnableModelMultiThreadingAnnotation(b.module)) + case r: RocketTile => + annotate(EnableModelMultiThreadingAnnotation(r.module)) + case _ => Nil + } + } + case _ => + } + case _ => + } } diff --git a/generators/firechip/src/main/scala/TargetConfigs.scala b/generators/firechip/src/main/scala/TargetConfigs.scala index c08a028d..6f2a0291 100644 --- a/generators/firechip/src/main/scala/TargetConfigs.scala +++ b/generators/firechip/src/main/scala/TargetConfigs.scala @@ -55,6 +55,16 @@ class WithScalaTestFeatures extends Config((site, here, up) => { case TracePortKey => up(TracePortKey, site).map(_.copy(print = true)) }) +// Multi-cycle regfile for rocket+boom +class WithFireSimMultiCycleRegfile extends Config((site, here, up) => { + case FireSimMultiCycleRegFile => true +}) + +// Model multithreading optimization +class WithFireSimFAME5 extends Config((site, here, up) => { + case FireSimFAME5 => true +}) + // FASED Config Aliases. This to enable config generation via "_" concatenation // which requires that all config classes be defined in the same package class DDR3FCFS extends FCFS16GBQuadRank @@ -72,7 +82,8 @@ class WithMinimalFireSimDesignTweaks extends Config( // Required*: Punch all clocks to FireSim's harness clock instantiator new WithFireSimHarnessClockBridgeInstantiator ++ new chipyard.harness.WithHarnessBinderClockFreqMHz(1000.0) ++ - new chipyard.harness.WithClockAndResetFromHarness ++ + new chipyard.harness.WithClockFromHarness ++ + new chipyard.harness.WithResetFromHarness ++ new chipyard.clocking.WithPassthroughClockGenerator ++ // Required*: When using FireSim-as-top to provide a correct path to the target bootrom source new WithBootROM ++ @@ -289,6 +300,7 @@ class FireSimLeanGemminiPrintfRocketConfig extends Config( //********************************************************************************** class SupernodeFireSimRocketConfig extends Config( new WithFireSimHarnessClockBridgeInstantiator ++ + new WithDefaultMemModel ++ // this is a global for all the multi-chip configs new chipyard.harness.WithHomogeneousMultiChip(n=4, new Config( new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 8L) ++ // 8GB DRAM per node new FireSimRocketConfig))) diff --git a/generators/icenet b/generators/icenet index 68b4c7f3..18e88b57 160000 --- a/generators/icenet +++ b/generators/icenet @@ -1 +1 @@ -Subproject commit 68b4c7f30f0119fe5cfab7ea99fb6927a563e112 +Subproject commit 18e88b5779ffdd7d75ca62cf9909f0ffc6fda95b diff --git a/generators/testchipip b/generators/testchipip index 177e3071..6436959d 160000 --- a/generators/testchipip +++ b/generators/testchipip @@ -1 +1 @@ -Subproject commit 177e3071991421bdb042fec8411c51463477c7fc +Subproject commit 6436959d997d0bb578790d95078648b478ca049b diff --git a/scripts/build-setup.sh b/scripts/build-setup.sh index c95056db..3624b5e7 100755 --- a/scripts/build-setup.sh +++ b/scripts/build-setup.sh @@ -109,8 +109,31 @@ if [ $TOOLCHAIN_TYPE == "esp-tools" ]; then done fi + +####################################### +###### BEGIN STEP-BY-STEP SETUP ####### +####################################### + +# In order to run code on error, we must handle errors manually +set +e; + +function begin_step +{ + thisStepNum=$1; + thisStepDesc=$2; + echo " ========== BEGINNING STEP $thisStepNum: $thisStepDesc ==========" +} +function exit_if_last_command_failed +{ + local exitcode=$?; + if [ $exitcode -ne 0 ]; then + die "Build script failed with exit code $exitcode at step $thisStepNum: $thisStepDesc" $exitcode; + fi +} + # setup and install conda environment if run_step "1"; then + begin_step "1" "Conda environment setup" # note: lock file must end in .conda-lock.yml - see https://github.com/conda-incubator/conda-lock/issues/154 CONDA_REQS=$CYDIR/conda-reqs CONDA_LOCK_REQS=$CONDA_REQS/conda-lock-reqs @@ -120,13 +143,15 @@ if run_step "1"; then if [ "$USE_UNPINNED_DEPS" = true ]; then # auto-gen the lockfiles $CYDIR/scripts/generate-conda-lockfiles.sh + exit_if_last_command_failed fi # use conda-lock to create env - conda-lock install --conda $(which conda) -p $CYDIR/.conda-env $LOCKFILE + conda-lock install --conda $(which conda) -p $CYDIR/.conda-env $LOCKFILE && - source $CYDIR/.conda-env/etc/profile.d/conda.sh + source $CYDIR/.conda-env/etc/profile.d/conda.sh && conda activate $CYDIR/.conda-env + exit_if_last_command_failed fi if [ -z "$FORCE_FLAG" ]; then @@ -138,11 +163,14 @@ fi # initialize all submodules (without the toolchain submodules) if run_step "2"; then + begin_step "2" "Initializing Chipyard submodules" $CYDIR/scripts/init-submodules-no-riscv-tools.sh $FORCE_FLAG + exit_if_last_command_failed fi # build extra toolchain collateral (i.e. spike, pk, riscv-tests, libgloss) if run_step "3"; then + begin_step "3" "Building toolchain collateral" if run_step "1"; then PREFIX=$CONDA_PREFIX/$TOOLCHAIN_TYPE else @@ -153,57 +181,73 @@ if run_step "3"; then PREFIX=$RISCV fi $CYDIR/scripts/build-toolchain-extra.sh $TOOLCHAIN_TYPE -p $PREFIX + exit_if_last_command_failed fi # run ctags for code navigation if run_step "4"; then + begin_step "4" "Running ctags for code navigation" $CYDIR/scripts/gen-tags.sh + exit_if_last_command_failed fi # precompile chipyard scala sources if run_step "5"; then - pushd $CYDIR/sims/verilator - make launch-sbt SBT_COMMAND=";project chipyard; compile" - make launch-sbt SBT_COMMAND=";project tapeout; compile" + begin_step "5" "Pre-compiling Chipyard Scala sources" + pushd $CYDIR/sims/verilator && + make launch-sbt SBT_COMMAND=";project chipyard; compile" && + make launch-sbt SBT_COMMAND=";project tapeout; compile" && popd + exit_if_last_command_failed fi # setup firesim if run_step "6"; then - $CYDIR/scripts/firesim-setup.sh + begin_step "6" "Setting up FireSim" + $CYDIR/scripts/firesim-setup.sh && $CYDIR/sims/firesim/gen-tags.sh + exit_if_last_command_failed # precompile firesim scala sources if run_step "7"; then - pushd $CYDIR/sims/firesim + begin_step "7" "Pre-compiling Firesim Scala sources" + pushd $CYDIR/sims/firesim && ( + set -e # Subshells un-set "set -e" so it must be re enabled echo $CYDIR source sourceme-manager.sh --skip-ssh-setup pushd sim make sbt SBT_COMMAND="project {file:$CYDIR}firechip; compile" TARGET_PROJECT=firesim popd ) + exit_if_last_command_failed popd fi fi # setup firemarshal if run_step "8"; then - pushd $CYDIR/software/firemarshal + begin_step "8" "Setting up FireMarshal" + pushd $CYDIR/software/firemarshal && ./init-submodules.sh + exit_if_last_command_failed # precompile firemarshal buildroot sources if run_step "9"; then - source $CYDIR/scripts/fix-open-files.sh - ./marshal $VERBOSE_FLAG build br-base.json + begin_step "9" "Pre-compiling FireMarshal buildroot sources" + source $CYDIR/scripts/fix-open-files.sh && + ./marshal $VERBOSE_FLAG build br-base.json && ./marshal $VERBOSE_FLAG clean br-base.json + exit_if_last_command_failed fi popd fi # do misc. cleanup for a "clean" git status if run_step "10"; then + begin_step "10" "Cleaning up repository" $CYDIR/scripts/repo-clean.sh + exit_if_last_command_failed fi cat <> env.sh diff --git a/sims/firesim b/sims/firesim index 08b565c8..73fe6a51 160000 --- a/sims/firesim +++ b/sims/firesim @@ -1 +1 @@ -Subproject commit 08b565c8c69f125a1de292eb8efe03ee08408d2e +Subproject commit 73fe6a51b28a2dbbe3f307bdbc6ba2407b311a27 diff --git a/software/firemarshal b/software/firemarshal index 8b8eea35..97826983 160000 --- a/software/firemarshal +++ b/software/firemarshal @@ -1 +1 @@ -Subproject commit 8b8eea35f618b9418c850df7ed809a2285ee5d1c +Subproject commit 97826983438427b8fc40d9b66af2674ce7046bac diff --git a/tools/dsptools b/tools/dsptools index 7bd039fb..8f433663 160000 --- a/tools/dsptools +++ b/tools/dsptools @@ -1 +1 @@ -Subproject commit 7bd039fb5f28ce2f31ed4420deb9a2220542838d +Subproject commit 8f4336639578d2e2dbaf61e542ee1f0fa8d79e63 diff --git a/tools/fixedpoint b/tools/fixedpoint index 35dda166..36ce43c9 160000 --- a/tools/fixedpoint +++ b/tools/fixedpoint @@ -1 +1 @@ -Subproject commit 35dda166f58f021cc32d00a2e76a5a33691c2b20 +Subproject commit 36ce43c90ce9cfc63e7698fa1e27fd122c878e9e diff --git a/tools/rocket-dsp-utils b/tools/rocket-dsp-utils index 341e9198..19445522 160000 --- a/tools/rocket-dsp-utils +++ b/tools/rocket-dsp-utils @@ -1 +1 @@ -Subproject commit 341e91985fdda7cce7eb30566fe58482a6f5aa40 +Subproject commit 194455223aa75f400d2ac76bfd71e61e3c2a9533