add additional example code as literalincludes
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user