add additional example code as literalincludes

This commit is contained in:
Howard Mao
2019-09-12 18:08:45 -07:00
parent 6ae60b94c6
commit d5bccc0455
3 changed files with 54 additions and 170 deletions

View File

@@ -229,51 +229,33 @@ To add RoCC instructions in your program, use the RoCC C macros provided in ``te
Adding a DMA port Adding a DMA port
------------------- -------------------
IO devices or accelerators (like a disk or network driver), we may want to have the device write directly to the coherent memory system instead. For IO devices or accelerators (like a disk or network driver), instead of
To add a device like that, you would do the following. having the CPU poll data from the device, we may want to have the device write
directly to the coherent memory system instead. For example, here is a device
that writes zeros to the memory at a configured address.
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/InitZero.scala
:language: scala
class DMADevice(implicit p: Parameters) extends LazyModule { .. literalinclude:: ../../generators/example/src/main/scala/Top.scala
val node = TLHelper.makeClientNode( :language: scala
name = "dma-device", sourceId = IdRange(0, 1)) :start-after: DOC include start: TopWithInitZero
:end-before: DOC include end: TopWithInitZero
lazy val module = new DMADeviceModule(this) We use ``TLHelper.makeClientNode`` to create a TileLink client node for us.
} We then connect the client node to the memory system through the front bus (fbus).
For more info on creating TileLink client nodes, take a look at :ref:`Client Node`.
class DMADeviceModule(outer: DMADevice) extends LazyModuleImp(outer) { Once we've created our top-level module including the DMA widget, we can create a configuration for it as we did before.
val io = IO(new Bundle {
val ext = new ExtBundle
})
val (mem, edge) = outer.node.out(0) .. literalinclude:: ../../generators/example/src/main/scala/ConfigMixins.scala
:language: scala
:start-after: DOC include start: WithInitZero
:end-before: DOC include end: WithInitZero
// ... rest of the code ... .. literalinclude:: ../../generators/example/src/main/scala/RocketConfigs.scala
} :language: scala
:start-after: DOC include start: InitZeroRocketConfig
trait HasPeripheryDMA { this: BaseSubsystem => :end-before: DOC include end: InitZeroRocketConfig
implicit val p: Parameters
val dma = LazyModule(new DMADevice)
fbus.fromPort(Some(portName))() := dma.node
}
trait HasPeripheryDMAModuleImp extends LazyModuleImp {
val ext = IO(new ExtBundle)
ext <> outer.dma.module.io.ext
}
class TopWithDMA(implicit p: Parameters) extends Top
with HasPeripheryDMA {
override lazy val module = new TopWithDMAModule
}
class TopWithDMAModule(l: TopWithDMA) extends TopModule(l)
with HasPeripheryDMAModuleImp
The ``ExtBundle`` contains the signals we connect off-chip that we get data from.
The DMADevice also has a Tilelink client port that we connect into the L1-L2 crossbar through the frontend bus (fbus).
The sourceId variable given in the ``TLClientNode`` instantiation determines the range of ids that can be used in acquire messages from this device.
Since we specified [0, 1) as our range, only the ID 0 can be used.

View File

@@ -18,39 +18,10 @@ This section will focus on the second method.
Basic Usage Basic Usage
----------- -----------
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
import chisel3._ :start-after: DOC include start: MyDeviceController
import chisel3.util._ :end-before: DOC include end: MyDeviceController
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy.{SimpleDevice, AddressSet}
import freechips.rocketchip.tilelink.TLRegisterNode
class MyDeviceController(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice("my-device", Seq("tutorial,my-device0"))
val node = TLRegisterNode(
address = Seq(AddressSet(0x10019000, 0xfff)),
device = device,
beatBytes = 8,
concurrency = 1)
lazy val module = new LazyModuleImp(this) {
val bigReg = RegInit(0.U(64.W))
val mediumReg = RegInit(0.U(32.W))
val smallReg = RegInit(0.U(16.W))
val tinyReg0 = RegInit(0.U(4.W))
val tinyReg1 = RegInit(0.U(4.W))
node.regmap(
0x00 -> Seq(RegField(64, bigReg)),
0x08 -> Seq(RegField(32, mediumReg)),
0x0C -> Seq(RegField(16, smallReg)),
0x0E -> Seq(
RegField(4, tinyReg0),
RegField(4, tinyReg1)))
}
}
The code example above shows a simple lazy module that uses the ``TLRegisterNode`` The code example above shows a simple lazy module that uses the ``TLRegisterNode``
to memory map hardware registers of different sizes. The constructor has to memory map hardware registers of different sizes. The constructor has
@@ -85,13 +56,10 @@ register. The ``RegField`` interface also provides support for reading
and writing ``DecoupledIO`` interfaces. For instance, you can implement a and writing ``DecoupledIO`` interfaces. For instance, you can implement a
hardware FIFO like so. hardware FIFO like so.
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
// 4-entry 64-bit queue :start-after: DOC include start: MyQueueRegisters
val queue = Module(new Queue(UInt(64.W), 4)) :end-before: DOC include end: MyQueueRegisters
node.regmap(
0x00 -> Seq(RegField(64, queue.io.deq, queue.io.enq)))
This variant of the ``RegField`` constructor takes three arguments instead of This variant of the ``RegField`` constructor takes three arguments instead of
two. The first argument is still the bit width. The second is the decoupled two. The first argument is still the bit width. The second is the decoupled
@@ -103,11 +71,10 @@ You need not specify both read and write for a register. You can also create
read-only or write-only registers. So for the previous example, if you wanted read-only or write-only registers. So for the previous example, if you wanted
enqueue and dequeue to use different addresses, you could write the following. enqueue and dequeue to use different addresses, you could write the following.
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
node.regmap( :start-after: DOC include start: MySeparateQueueRegisters
0x00 -> Seq(RegField.r(64, queue.io.deq)), :end-before: DOC include end: MySeparateQueueRegisters
0x08 -> Seq(RegField.w(64, queue.io.enq)))
The read-only register function can also be used to read signals The read-only register function can also be used to read signals
that aren't registers. that aren't registers.
@@ -126,24 +93,10 @@ You can also create registers using functions. Say, for instance, that you
want to create a counter that gets incremented on a write and decremented on want to create a counter that gets incremented on a write and decremented on
a read. a read.
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
val counter = RegInit(0.U(64.W)) :start-after: DOC include start: MyCounterRegisters
:end-before: DOC include end: MyCounterRegisters
def readCounter(ready: Bool): (Bool, UInt) = {
when (ready) { counter := counter - 1.U }
(true.B, counter)
}
def writeCounter(valid: Bool, bits: UInt): Bool = {
when (valid) { counter := counter + 1.U }
// Ignore bits
true.B
}
node.regmap(
0x00 -> Seq(RegField.r(64, readCounter(_))),
0x08 -> Seq(RegField.w(64, writeCounter(_, _))))
The functions here are essentially the same as a decoupled interface. The functions here are essentially the same as a decoupled interface.
The read function gets passed the ``ready`` signal and returns the The read function gets passed the ``ready`` signal and returns the
@@ -154,39 +107,10 @@ You can also pass functions that decouple the read/write request and response.
The request will appear as a decoupled input and the response as a decoupled The request will appear as a decoupled input and the response as a decoupled
output. So for instance, if we wanted to do this for the previous example. output. So for instance, if we wanted to do this for the previous example.
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
val counter = RegInit(0.U(64.W)) :start-after: DOC include start: MyCounterReqRespRegisters
:end-before: DOC include end: MyCounterReqRespRegisters
def readCounter(ivalid: Bool, oready: Bool): (Bool, Bool, UInt) = {
val responding = RegInit(false.B)
when (ivalid && !responding) { responding := true.B }
when (responding && oready) {
counter := counter - 1.U
responding := false.B
}
(!responding, responding, counter)
}
def writeCounter(ivalid: Bool, bits: UInt, oready: Bool): (Bool, Bool) = {
val responding = RegInit(false.B)
when (ivalid && !responding) { responding := true.B }
when (responding && oready) {
counter := counter + 1.U
responding := false.B
}
(!responding, responding)
}
node.regmap(
0x00 -> Seq(RegField.r(64, readCounter(_, _))),
0x08 -> Seq(RegField.w(64, writeCounter(_, _, _))))
In each function, we set up a state variable ``responding``. The function In each function, we set up a state variable ``responding``. The function
is ready to take requests when this is false and is sending a response when is ready to take requests when this is false and is sending a response when
@@ -207,37 +131,11 @@ change the protocol being used. For instance, in the first example in
:ref:`Basic Usage`, you could simply change the ``TLRegisterNode`` to :ref:`Basic Usage`, you could simply change the ``TLRegisterNode`` to
and ``AXI4RegisterNode``. and ``AXI4RegisterNode``.
.. code-block:: scala .. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
:start-after: DOC include start: MyAXI4DeviceController
:end-before: DOC include end: MyAXI4DeviceController
import chisel3._ Other than the fact that AXI4 nodes don't take a ``device`` argument, and can
import chisel3.util._ only have a single AddressSet instead of multiple, everything else is
import freechips.rocketchip.config.Parameters unchanged.
import freechips.rocketchip.diplomacy.{SimpleDevice, AddressSet}
import freechips.rocketchip.amba.axi4.AXI4RegisterNode
class MyAXI4DeviceController(implicit p: Parameters) extends LazyModule {
val node = AXI4RegisterNode(
address = Seq(AddressSet(0x10019000, 0xfff)),
beatBytes = 8,
concurrency = 1)
lazy val module = new LazyModuleImp(this) {
val bigReg = RegInit(0.U(64.W))
val mediumReg = RegInit(0.U(32.W))
val smallReg = RegInit(0.U(16.W))
val tinyReg0 = RegInit(0.U(4.W))
val tinyReg1 = RegInit(0.U(4.W))
node.regmap(
0x00 -> Seq(RegField(64, bigReg)),
0x08 -> Seq(RegField(32, mediumReg)),
0x0C -> Seq(RegField(16, smallReg)),
0x0E -> Seq(
RegField(4, tinyReg0),
RegField(4, tinyReg1)))
}
}
Other than the fact that AXI4 nodes don't take a ``device`` argument,
everything else is unchanged.

View File

@@ -115,12 +115,14 @@ class MyCounterRegisters(implicit p: Parameters) extends LazyModule {
def readCounter(ready: Bool): (Bool, UInt) = { def readCounter(ready: Bool): (Bool, UInt) = {
when (ready) { counter := counter - 1.U } when (ready) { counter := counter - 1.U }
// (ready, bits)
(true.B, counter) (true.B, counter)
} }
def writeCounter(valid: Bool, bits: UInt): Bool = { def writeCounter(valid: Bool, bits: UInt): Bool = {
when (valid) { counter := counter + 1.U } when (valid) { counter := counter + 1.U }
// Ignore bits // Ignore bits
// Return ready
true.B true.B
} }
@@ -153,10 +155,11 @@ class MyCounterReqRespRegisters(implicit p: Parameters) extends LazyModule {
responding := false.B responding := false.B
} }
// (iready, ovalid, obits)
(!responding, responding, counter) (!responding, responding, counter)
} }
def writeCounter(ivalid: Bool, oready: Bool, bits: UInt): (Bool, Bool) = { def writeCounter(ivalid: Bool, oready: Bool, ibits: UInt): (Bool, Bool) = {
val responding = RegInit(false.B) val responding = RegInit(false.B)
when (ivalid && !responding) { responding := true.B } when (ivalid && !responding) { responding := true.B }
@@ -166,6 +169,7 @@ class MyCounterReqRespRegisters(implicit p: Parameters) extends LazyModule {
responding := false.B responding := false.B
} }
// (iready, ovalid)
(!responding, responding) (!responding, responding)
} }