Skip to content

Commit

Permalink
Merge pull request #1757 from ucb-bar/clean-gcd
Browse files Browse the repository at this point in the history
Improve GCD example
  • Loading branch information
jerryz123 authored Jan 26, 2024
2 parents b6ba10a + aef7cda commit 50d9770
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 87 deletions.
31 changes: 18 additions & 13 deletions docs/Customization/MMIO-Peripherals.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ To create a RegisterRouter-based peripheral, you will need to specify a paramete
For this example, we will show how to connect a MMIO peripheral which computes the GCD.
The full code can be found in ``generators/chipyard/src/main/scala/example/GCD.scala``.

In this case we use a submodule ``GCDMMIOChiselModule`` to actually perform the GCD. The ``GCDModule`` class only creates the registers and hooks them up using ``regmap``.
In this case we use a submodule ``GCDMMIOChiselModule`` to actually perform the GCD. The ``GCDTL`` and ``GCDAXI4`` classes are the ``LazyModule`` classes which construct the TileLink or AXI4 ports, wrapping the inner ``GCDMMIOChiselModule``.
The ``node`` object is a Diplomacy node, which connects the peripheral to the Diplomacy interconnect graph.

.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
:language: scala
Expand All @@ -19,8 +20,9 @@ In this case we use a submodule ``GCDMMIOChiselModule`` to actually perform the

.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
:language: scala
:start-after: DOC include start: GCD instance regmap
:end-before: DOC include end: GCD instance regmap
:start-after: DOC include start: GCD router
:end-before: DOC include end: GCD router


Advanced Features of RegField Entries
-------------------------------------
Expand All @@ -41,15 +43,21 @@ 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.

.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
:language: scala
:start-after: DOC include start: GCD instance regmap
:end-before: DOC include end: GCD instance regmap


Connecting by TileLink
----------------------

Once you have these classes, you can construct the final peripheral by extending the ``TLRegisterRouter`` and passing the proper arguments.
The first set of arguments determines where the register router will be placed in the global address map and what information will be put in its device tree entry.
The second set of arguments is the IO bundle constructor, which we create by extending ``TLRegBundle`` with our bundle trait.
The final set of arguments is the module constructor, which we create by extends ``TLRegModule`` with our module trait.
Notice how we can create an analogous AXI4 version of our peripheral.
The key to connecting to the TileLink Diplomatic graph is the construction of the TileLink node for this peripheral.
In this case, since the peripheral acts as a manager of some register-mapped address space, it uses the ``TLRegisterNode`` object.
The parameters to the ``TLRegisterNode`` object specify the size of the managed space, the base address, and the port width.

Within the register-mapped peripheral, the control registers can be mapped using the ``node.regmap`` function, as described above.
A similar procedure is followed for both AXI4 and TileLin peripherals.

.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
:language: scala
Expand All @@ -62,12 +70,8 @@ Top-level Traits
----------------

After creating the module, we need to hook it up to our SoC.
Rocket Chip accomplishes this using the cake pattern.
This basically involves placing code inside traits.
In the Rocket Chip cake, there are two kinds of traits: a ``LazyModule`` trait and a module implementation trait.

The ``LazyModule`` trait runs setup code that must execute before all the hardware gets elaborated.
For a simple memory-mapped peripheral, this just involves connecting the peripheral's TileLink node to the MMIO crossbar.
For a simple memory-mapped peripheral, this just involves connecting the peripheral's TileLink node to the relevant bus.

.. literalinclude:: ../../generators/chipyard/src/main/scala/example/GCD.scala
:language: scala
Expand All @@ -80,6 +84,7 @@ This will automatically add address map and device tree entries for the peripher
Also observe how we have to place additional AXI4 buffers and converters for the AXI4 version of this peripheral.

Peripherals which expose I/O can use `InModuleBody` to punch their I/O to the `DigitalTop` module.
In this example, the GCD module's ``gcd_busy`` signal is exposed as a I/O of DigitalTop.

Constructing the DigitalTop and Config
--------------------------------------
Expand Down
179 changes: 105 additions & 74 deletions generators/chipyard/src/main/scala/example/GCD.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chisel3._
import chisel3.util._
import chisel3.experimental.{IntParam, BaseModule}
import freechips.rocketchip.amba.axi4._
import freechips.rocketchip.prci._
import freechips.rocketchip.subsystem.BaseSubsystem
import org.chipsalliance.cde.config.{Parameters, Field, Config}
import freechips.rocketchip.diplomacy._
Expand Down Expand Up @@ -36,27 +37,24 @@ class GCDIO(val w: Int) extends Bundle {
val busy = Output(Bool())
}

trait GCDTopIO extends Bundle {
class GCDTopIO extends Bundle {
val gcd_busy = Output(Bool())
}

trait HasGCDIO extends BaseModule {
val w: Int
val io = IO(new GCDIO(w))
trait HasGCDTopIO {
def io: GCDTopIO
}

// DOC include start: GCD blackbox
class GCDMMIOBlackBox(val w: Int) extends BlackBox(Map("WIDTH" -> IntParam(w))) with HasBlackBoxResource
with HasGCDIO
{
class GCDMMIOBlackBox(val w: Int) extends BlackBox(Map("WIDTH" -> IntParam(w))) with HasBlackBoxResource {
val io = IO(new GCDIO(w))
addResource("/vsrc/GCDMMIOBlackBox.v")
}
// DOC include end: GCD blackbox

// DOC include start: GCD chisel
class GCDMMIOChiselModule(val w: Int) extends Module
with HasGCDIO
{
class GCDMMIOChiselModule(val w: Int) extends Module {
val io = IO(new GCDIO(w))
val s_idle :: s_run :: s_done :: Nil = Enum(3)

val state = RegInit(s_idle)
Expand Down Expand Up @@ -90,70 +88,106 @@ class GCDMMIOChiselModule(val w: Int) extends Module
}
// DOC include end: GCD chisel

// DOC include start: GCD instance regmap
// DOC include start: GCD router
class GCDTL(params: GCDParams, beatBytes: Int)(implicit p: Parameters) extends ClockSinkDomain(ClockSinkParameters())(p) {
val device = new SimpleDevice("gcd", Seq("ucbbar,gcd"))
val node = TLRegisterNode(Seq(AddressSet(params.address, 4096-1)), device, "reg/control", beatBytes=beatBytes)

override lazy val module = new GCDImpl
class GCDImpl extends Impl with HasGCDTopIO {
val io = IO(new GCDTopIO)
withClockAndReset(clock, reset) {
// How many clock cycles in a PWM cycle?
val x = Reg(UInt(params.width.W))
val y = Wire(new DecoupledIO(UInt(params.width.W)))
val gcd = Wire(new DecoupledIO(UInt(params.width.W)))
val status = Wire(UInt(2.W))

val impl_io = if (params.useBlackBox) {
val impl = Module(new GCDMMIOBlackBox(params.width))
impl.io
} else {
val impl = Module(new GCDMMIOChiselModule(params.width))
impl.io
}

trait GCDModule extends HasRegMap {
val io: GCDTopIO
impl_io.clock := clock
impl_io.reset := reset.asBool

implicit val p: Parameters
def params: GCDParams
val clock: Clock
val reset: Reset
impl_io.x := x
impl_io.y := y.bits
impl_io.input_valid := y.valid
y.ready := impl_io.input_ready

gcd.bits := impl_io.gcd
gcd.valid := impl_io.output_valid
impl_io.output_ready := gcd.ready

// How many clock cycles in a PWM cycle?
val x = Reg(UInt(params.width.W))
val y = Wire(new DecoupledIO(UInt(params.width.W)))
val gcd = Wire(new DecoupledIO(UInt(params.width.W)))
val status = Wire(UInt(2.W))
status := Cat(impl_io.input_ready, impl_io.output_valid)
io.gcd_busy := impl_io.busy

val impl = if (params.useBlackBox) {
Module(new GCDMMIOBlackBox(params.width))
} else {
Module(new GCDMMIOChiselModule(params.width))
// DOC include start: GCD instance regmap
node.regmap(
0x00 -> Seq(
RegField.r(2, status)), // a read-only register capturing current status
0x04 -> Seq(
RegField.w(params.width, x)), // a plain, write-only register
0x08 -> Seq(
RegField.w(params.width, y)), // write-only, y.valid is set on write
0x0C -> Seq(
RegField.r(params.width, gcd))) // read-only, gcd.ready is set on read
// DOC include end: GCD instance regmap
}
}

impl.io.clock := clock
impl.io.reset := reset.asBool

impl.io.x := x
impl.io.y := y.bits
impl.io.input_valid := y.valid
y.ready := impl.io.input_ready

gcd.bits := impl.io.gcd
gcd.valid := impl.io.output_valid
impl.io.output_ready := gcd.ready

status := Cat(impl.io.input_ready, impl.io.output_valid)
io.gcd_busy := impl.io.busy

regmap(
0x00 -> Seq(
RegField.r(2, status)), // a read-only register capturing current status
0x04 -> Seq(
RegField.w(params.width, x)), // a plain, write-only register
0x08 -> Seq(
RegField.w(params.width, y)), // write-only, y.valid is set on write
0x0C -> Seq(
RegField.r(params.width, gcd))) // read-only, gcd.ready is set on read
}
// DOC include end: GCD instance regmap

// DOC include start: GCD router
class GCDTL(params: GCDParams, beatBytes: Int)(implicit p: Parameters)
extends TLRegisterRouter(
params.address, "gcd", Seq("ucbbar,gcd"),
beatBytes = beatBytes)(
new TLRegBundle(params, _) with GCDTopIO)(
new TLRegModule(params, _, _) with GCDModule)

class GCDAXI4(params: GCDParams, beatBytes: Int)(implicit p: Parameters)
extends AXI4RegisterRouter(
params.address,
beatBytes=beatBytes)(
new AXI4RegBundle(params, _) with GCDTopIO)(
new AXI4RegModule(params, _, _) with GCDModule)
class GCDAXI4(params: GCDParams, beatBytes: Int)(implicit p: Parameters) extends ClockSinkDomain(ClockSinkParameters())(p) {
val node = AXI4RegisterNode(AddressSet(params.address, 4096-1), beatBytes=beatBytes)
override lazy val module = new GCDImpl
class GCDImpl extends Impl with HasGCDTopIO {
val io = IO(new GCDTopIO)
withClockAndReset(clock, reset) {
// How many clock cycles in a PWM cycle?
val x = Reg(UInt(params.width.W))
val y = Wire(new DecoupledIO(UInt(params.width.W)))
val gcd = Wire(new DecoupledIO(UInt(params.width.W)))
val status = Wire(UInt(2.W))

val impl_io = if (params.useBlackBox) {
val impl = Module(new GCDMMIOBlackBox(params.width))
impl.io
} else {
val impl = Module(new GCDMMIOChiselModule(params.width))
impl.io
}

impl_io.clock := clock
impl_io.reset := reset.asBool

impl_io.x := x
impl_io.y := y.bits
impl_io.input_valid := y.valid
y.ready := impl_io.input_ready

gcd.bits := impl_io.gcd
gcd.valid := impl_io.output_valid
impl_io.output_ready := gcd.ready

status := Cat(impl_io.input_ready, impl_io.output_valid)
io.gcd_busy := impl_io.busy

node.regmap(
0x00 -> Seq(
RegField.r(2, status)), // a read-only register capturing current status
0x04 -> Seq(
RegField.w(params.width, x)), // a plain, write-only register
0x08 -> Seq(
RegField.w(params.width, y)), // write-only, y.valid is set on write
0x0C -> Seq(
RegField.r(params.width, gcd))) // read-only, gcd.ready is set on read
}
}
}
// DOC include end: GCD router

// DOC include start: GCD lazy trait
Expand All @@ -164,7 +198,8 @@ trait CanHavePeripheryGCD { this: BaseSubsystem =>
val gcd_busy = p(GCDKey) match {
case Some(params) => {
val gcd = if (params.useAXI4) {
val gcd = pbus { LazyModule(new GCDAXI4(params, pbus.beatBytes)(p)) }
val gcd = LazyModule(new GCDAXI4(params, pbus.beatBytes)(p))
gcd.clockNode := pbus.fixedClockNode
pbus.coupleTo(portName) {
gcd.node :=
AXI4Buffer () :=
Expand All @@ -174,18 +209,14 @@ trait CanHavePeripheryGCD { this: BaseSubsystem =>
}
gcd
} else {
val gcd = pbus { LazyModule(new GCDTL(params, pbus.beatBytes)(p)) }
val gcd = LazyModule(new GCDTL(params, pbus.beatBytes)(p))
gcd.clockNode := pbus.fixedClockNode
pbus.coupleTo(portName) { gcd.node := TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _ }
gcd
}
val pbus_io = pbus { InModuleBody {
val busy = IO(Output(Bool()))
busy := gcd.module.io.gcd_busy
busy
}}
val gcd_busy = InModuleBody {
val busy = IO(Output(Bool())).suggestName("gcd_busy")
busy := pbus_io
busy := gcd.module.io.gcd_busy
busy
}
Some(gcd_busy)
Expand Down

0 comments on commit 50d9770

Please sign in to comment.