Working with existing Verilog IP is an integral part of many chip design flows. Fortunately, both Chisel and Chipyard provide extensive support for Verilog integration.
Here, we will examine the process of incorporating an MMIO peripheral (similar to the PWM example from the previous section) that uses a Verilog implementation of Greatest Common Denominator (GCD) algorithm. There are a few steps to adding a Verilog peripheral:
- Adding a Verilog resource file to the project
- Defining a Chisel
BlackBox
representing the Verilog module - Instantiating the
BlackBox
and interfacingRegField
entries - Setting up a chip
Top
andConfig
that use the peripheral
As before, it is possible to incorporate peripherals as part of your own generator project. However, Verilog resource files must go in a different directory from Chisel (Scala) sources.
generators/yourproject/
build.sbt
src/main/
scala/
resources/
vsrc/
YourFile.v
In addition to the steps outlined in the previous section on adding a project to the build.sbt
at the top level, it is also necessary to add any projects that contain Verilog IP as dependencies to the tapeout
project.
lazy val tapeout = conditionalDependsOn(project in file("./tools/barstools/tapeout/"))
.dependsOn(chisel_testers, example, yourproject)
.settings(commonSettings)
For this concrete GCD example, we will be using a GCDMMIOBlackBox
Verilog module that is defined in the example
project. The Scala and Verilog sources follow the prescribed directory layout.
generators/example/
build.sbt
src/main/
scala/
GCDMMIOBlackBox.scala
resources/
vsrc/
GCDMMIOBlackBox.v
A Chisel BlackBox
module provides a way of instantiating a module defined by an external Verilog source. The definition of the blackbox includes several aspects that allow it to be translated to an instance of the Verilog module:
- An
io
field: a bundle with fields corresponding to the portlist of the Verilog module. - A constructor parameter that takes a
Map
from Verilog parameter name to elaborated value - One or more resources added to indicate Verilog source dependencies
Of particular interest is the fact that parameterized Verilog modules can be passed the full space of possible parameter values. These values may depend on elaboration-time values in the Chisel generator, as the bitwidth of the GCD calculation does in this example.
Verilog GCD port list and parameters
../../generators/example/src/main/resources/vsrc/GCDMMIOBlackBox.v
Chisel BlackBox Definition
../../generators/example/src/main/scala/GCDMMIOBlackBox.scala
Next, we must instantiate the blackbox. In order to take advantage of diplomatic memory mapping on the system bus, we still have to integrate the peripheral at the Chisel level by mixing peripheral-specific traits into a TLRegisterRouter
. The params
member and HasRegMap
base trait should look familiar from the previous memory-mapped PWM device example.
../../generators/example/src/main/scala/GCDMMIOBlackBox.scala
One signficant difference from the PWM example is in the peripheral's memory map. RegField
exposes polymorphic r
and w
methods that allow read- and write-only memory-mapped registers to be interfaced to hardware in multiple ways.
RegField.r(2, status)
is used to create a 2-bit, read-only register that captures the current value of thestatus
signal when read.RegField.r(params.width, gcd)
"connects" the decoupled handshaking interfacegcd
to a read-only memory-mapped register. When this register is read via MMIO, theready
signal is asserted. This is in turn connected tooutput_ready
on the Verilog blackbox through the glue logic.RegField.w(params.width, x)
exposes a plain register (much like those in the PWM example) via MMIO, but makes it write-only.RegField.w(params.width, y)
associates the decoupled interface signaly
with a write-only memory-mapped register, causingy.valid
to be asserted when the register is written.
Since the ready/valid signals of y
are connected to the input_ready
and input_valid
signals of the blackbox, respectively, this register map and glue logic has the effect of triggering the GCD algorithm when y
is written. Therefore, the algorithm is set up by first writing x
and then performing a triggering write to y
. Polling can be used for status checks.
As with the PWM example, a few more pieces are needed to tie the system together.
Composing traits into a complete cake pattern peripheral
../../generators/example/src/main/scala/GCDMMIOBlackBox.scala
Note the differences arising due to the fact that this peripheral has no top-level IO. To build a complete system, a new Top
and new Config
objects are added in a manner exactly analogous to the PWM example.
The GCD module has a slightly more complex interface, so polling is used to check the status of the device before each triggering read or write.
../../tests/gcd.c