.. _bridge-walkthrough: Bridge Walkthrough ================== In this section, we'll walkthrough a simple Target-to-Host bridge, the UARTBridge, provided with FireSim to demonstrate how to integrate your own. The UARTBridge uses host-MMIO to model a UART device. Reading the Bridges section is a prerequisite to reading these sections. UART Bridge (Host-MMIO) ----------------------- Source code for the UART Bridge lives in the following directories: .. code-block:: text sim/ ├-firesim-lib/src/main/ │ ├-scala/bridges/UARTBridge.scala # Target-Side Bridge and BridgeModule Definitions │ ├-cc/brides/uart.cc # Bridge Driver source │ └-cc/brides/uart.h # Bridge Driver header ├-src/main/cc/firesim/firesim_top.cc # Driver instantiation in the main simulation driver └-src/main/makefrag/firesim/ # Target-specific build rules ├ build.mk # Definition of the Chisel elaboration step ├ config.mk # Target-specific configuration and path setup ├ driver.mk # Build rules for the driver └ run.mk # Custom run commands for meta-simulation Target Side +++++++++++ The first order of business when designing a new bridge is to implement its target side. In the case of UART we've defined a Chisel BlackBox [#]_ extending Bridge. We'll instantiate this BlackBox and connect it to UART IO in the top-level of our chip. We first define a class that captures the target-side interface of the Bridge: .. literalinclude:: ../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala :language: scala :start-after: DOC include start: UART Bridge Target-Side Interface :end-before: DOC include end: UART Bridge Target-Side Interface .. [#] You can also extend a non-BlackBox Chisel Module, but any Chisel source contained within will be removed by Golden Gate. You may wish to do this to enclose a synthesizable model of the Bridge for other simulation backends, or simply to wrap a larger chunk RTL you wish to model in the host-side of the Bridge. Here, we define a case class that carries additional metadata to the host-side BridgeModule. For UART, this is simply the clock-division required to produce the baudrate: .. literalinclude:: ../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala :language: scala :start-after: DOC include start: UART Bridge Constructor Arg :end-before: DOC include end: UART Bridge Constructor Arg Finally, we define the actual target-side module (specifically, a BlackBox): .. literalinclude:: ../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala :language: scala :start-after: DOC include start: UART Bridge Target-Side Module :end-before: DOC include end: UART Bridge Target-Side Module To make it easier to instantiate our target-side module, we've also defined an optional companion object: .. literalinclude:: ../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala :language: scala :start-after: DOC include start: UART Bridge Companion Object :end-before: DOC include end: UART Bridge Companion Object That completes the target-side definition. Host-Side BridgeModule ++++++++++++++++++++++ The remainder of the file is dedicated to the host-side BridgeModule definition. Here we have to process tokens generated by the target, and expose a memory-mapped interface to the bridge driver. Inspecting the top of the class: .. literalinclude:: ../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala :language: scala :start-after: DOC include start: UART Bridge Header :end-before: DOC include end: UART Bridge Header Most of what follows is responsible for modeling the timing of the UART. As a bridge designer, you're free to take as many host-cycles as you need to process tokens. In simpler models, like this one, it's often easiest to write logic that operates in a single cycle but gate state-updates using a "fire" signal that is asserted when the required tokens are available. Now, we'll skip to the end to see how to add registers to the simulator's memory map that can be accessed using MMIO from bridge driver. .. literalinclude:: ../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala :language: scala :start-after: DOC include start: UART Bridge Footer :end-before: DOC include end: UART Bridge Footer Host-Side Driver ++++++++++++++++ To complete our host-side definition, we need to define a CPU-hosted bridge driver. Bridge Drivers extend the ``bridge_driver_t`` interface, which declares 5 virtual methods a concrete bridge driver must implement: .. literalinclude:: ../../sim/midas/src/main/cc/core/bridge_driver.h :language: c++ :start-after: DOC include start: Bridge Driver Interface :end-before: DOC include end: Bridge Driver Interface The declaration of the Uart bridge driver lives at :gh-file-ref:`sim/firesim-lib/src/main/cc/bridges/uart.h`. It is inlined below: .. include:: ../../sim/firesim-lib/src/main/cc/bridges/uart.h :code: c++ The bulk of the driver's work is done in its ``tick()`` method. Here, the driver polls the BridgeModule and then does some work. Note: the name, ``tick`` is vestigial: one invocation of tick() may do work corresponding to an arbitrary number of target cycles. It's critical that tick be non-blocking, as waiting for work from the BridgeModule may deadlock the simulator. Build-System Modifications ++++++++++++++++++++++++++ The final consideration in adding your bridge concerns the build system. You should be able to host the Scala sources for your bridge with rest of your target RTL: SBT will make sure those classes are available on the runtime classpath. If you're hosting your bridge driver sources outside of the existing directories, you'll need to modify your target-project make fragments to include them. The default Chipyard/Rocket Chip-based one lives here: :gh-file-ref:`sim/src/main/makefrag/firesim`. Here the main order of business is to add header and source files to ``DRIVER_H`` and ``DRIVER_CC`` respectively in `driver.mk`, by modifying the lines below: .. literalinclude:: ../../sim/src/main/makefrag/firesim/driver.mk :language: make :start-after: DOC include start: Bridge Build System Changes :end-before: DOC include end: Bridge Build System Changes That's it! At this point you should be able to both test your bridge in software simulation using metasimulation, or deploy it to an FPGA.