Skip to content

Latest commit

 

History

History
165 lines (124 loc) · 6.99 KB

Bridge-Walkthrough.rst

File metadata and controls

165 lines (124 loc) · 6.99 KB

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:

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 BlackBox1 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:

../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala

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:

../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala

Finally, we define the actual target-side module (specifically, a BlackBox):

../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala

To make it easier to instantiate our target-side module, we've also defined an optional companion object:

../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala

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:

../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala

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.

../../sim/firesim-lib/src/main/scala/bridges/UARTBridge.scala

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:

../../sim/midas/src/main/cc/core/bridge_driver.h

The declaration of the Uart bridge driver lives at sim/firesim-lib/src/main/cc/bridges/uart.h. It is inlined below:

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.

Registering the Driver

With the Bridge Driver implemented, we now have to register it in the main simulator simulator class defined in sim/midas/src/main/cc/core/constructor.h. Here, we rely on the C preprocessor macros to instantiate the bridge driver only when the corresponding BridgeModule is present:

../../sim/midas/src/main/cc/core/constructor.h

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: 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:

../../sim/src/main/makefrag/firesim/driver.mk

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.


  1. 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.