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

@@ -18,39 +18,10 @@ This section will focus on the second method.
Basic Usage
-----------
.. code-block:: scala
import chisel3._
import chisel3.util._
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)))
}
}
.. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
:start-after: DOC include start: MyDeviceController
:end-before: DOC include end: MyDeviceController
The code example above shows a simple lazy module that uses the ``TLRegisterNode``
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
hardware FIFO like so.
.. code-block:: scala
// 4-entry 64-bit queue
val queue = Module(new Queue(UInt(64.W), 4))
node.regmap(
0x00 -> Seq(RegField(64, queue.io.deq, queue.io.enq)))
.. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
:start-after: DOC include start: MyQueueRegisters
:end-before: DOC include end: MyQueueRegisters
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
@@ -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
enqueue and dequeue to use different addresses, you could write the following.
.. code-block:: scala
node.regmap(
0x00 -> Seq(RegField.r(64, queue.io.deq)),
0x08 -> Seq(RegField.w(64, queue.io.enq)))
.. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
:start-after: DOC include start: MySeparateQueueRegisters
:end-before: DOC include end: MySeparateQueueRegisters
The read-only register function can also be used to read signals
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
a read.
.. code-block:: scala
val counter = RegInit(0.U(64.W))
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(_, _))))
.. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
:start-after: DOC include start: MyCounterRegisters
:end-before: DOC include end: MyCounterRegisters
The functions here are essentially the same as a decoupled interface.
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
output. So for instance, if we wanted to do this for the previous example.
.. code-block:: scala
val counter = RegInit(0.U(64.W))
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(_, _, _))))
.. literalinclude:: ../../generators/example/src/main/scala/RegisterNodeExample.scala
:language: scala
:start-after: DOC include start: MyCounterReqRespRegisters
:end-before: DOC include end: MyCounterReqRespRegisters
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
@@ -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
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._
import chisel3.util._
import freechips.rocketchip.config.Parameters
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.
Other than the fact that AXI4 nodes don't take a ``device`` argument, and can
only have a single AddressSet instead of multiple, everything else is
unchanged.