+
+ +

+ + + You're reading an pre-release version of this documentation.
+ For the latest stable release version, please have a look at master. + +
+

+
+ +
+
+

+
+ + + + +
+

Slots

+
+

Introduction

+

Let’s say you have some hardware which has to keep track of multiple similar ongoing activities, you may want to implement an array of “slots” to do so. This example show how to do it using Area, OHMasking.first, onMask and reader.

+
+

Implementation

+

This implementation avoid the use of Vec. Instead, it use Area which allow to mix signal, registers and logic definitions in each slot.

+

Note that the reader API is for SpinalHDL version comming after 1.9.1

+
package spinaldoc.examples.advanced
+
+import spinal.core._
+import spinal.lib._
+import spinal.lib.misc.{InterruptCtrl, Prescaler}
+import spinal.lib.bus.misc.BusSlaveFactory
+import spinal.lib.bus.amba3.apb.{Apb3, Apb3Config, Apb3SlaveFactory}
+
+import scala.language.postfixOps
+
+case class Timer(width : Int) extends Component {
+  val io = new Bundle {
+    val tick  = in Bool()
+    val clear = in Bool()
+    val limit = in UInt(width bits)
+
+    val full  = out Bool()
+    val value = out UInt(width bits)
+  }
+
+  val counter = Reg(UInt(width bits))
+  when(io.tick && !io.full) {
+    counter := counter + 1
+  }
+  when(io.clear) {
+    counter := 0
+  }
+
+  io.full := counter === io.limit && io.tick
+  io.value := counter
+
+  // Timer bus interface
+  // The function prototype uses Scala currying funcName(arg1,arg2)(arg3,arg3)
+  // which allow to call the function with a nice syntax later
+  // This function also returns an area, which allows to keep names of inner signals in the generated VHDL/Verilog.
+  def driveFrom(busCtrl: BusSlaveFactory, baseAddress: BigInt)(ticks: Seq[Bool], clears: Seq[Bool]) = new Area {
+    // Offset 0 => clear/tick masks + bus
+    val ticksEnable = busCtrl.createReadAndWrite(Bits(ticks.length bits), baseAddress + 0,0) init(0)
+    val clearsEnable = busCtrl.createReadAndWrite(Bits(clears.length bits), baseAddress + 0,16) init(0)
+    val busClearing = False
+
+    io.clear := (clearsEnable & clears.asBits).orR | busClearing
+    io.tick := (ticksEnable  & ticks.asBits ).orR
+
+    // Offset 4 => read/write limit (+ auto clear)
+    busCtrl.driveAndRead(io.limit, baseAddress + 4)
+    busClearing.setWhen(busCtrl.isWriting(baseAddress + 4))
+
+    // Offset 8 => read timer value / write => clear timer value
+    busCtrl.read(io.value, baseAddress + 8)
+    busClearing.setWhen(busCtrl.isWriting(baseAddress + 8))
+  }
+}
+// end case class Timer
+
+case class ApbTimer() extends Component {
+  val io = new Bundle {
+    val apb = slave(Apb3(Apb3Config(addressWidth=8, dataWidth=32)))
+    val interrupt = out Bool()
+    val external = new Bundle {
+      val tick = in Bool()
+      val clear = in Bool()
+    }
+  }
+
+  //Prescaler is very similar to the timer, it mainly integrates a piece of auto reload logic.
+  val prescaler = Prescaler(width = 16)
+
+  val timerA = Timer(width = 32)
+  val timerB,timerC,timerD = Timer(width = 16)
+
+  val busCtrl = Apb3SlaveFactory(io.apb)
+  
+  prescaler.driveFrom(busCtrl, 0x00)
+
+  timerA.driveFrom(busCtrl, 0x40)(
+    ticks=List(True, prescaler.io.overflow),
+    clears=List(timerA.io.full)
+  )
+  timerB.driveFrom(busCtrl, 0x50)(
+    ticks=List(True, prescaler.io.overflow, io.external.tick),
+    clears=List(timerB.io.full, io.external.clear)
+  )
+  timerC.driveFrom(busCtrl, 0x60)(
+    ticks=List(True, prescaler.io.overflow, io.external.tick),
+    clears=List(timerC.io.full, io.external.clear)
+  )
+  timerD.driveFrom(busCtrl, 0x70)(
+    ticks=List(True, prescaler.io.overflow, io.external.tick),
+    clears=List(timerD.io.full, io.external.clear)
+  )
+
+  val interruptCtrl = InterruptCtrl(4)
+  interruptCtrl.driveFrom(busCtrl, 0x10)
+  interruptCtrl.io.inputs(0) := timerA.io.full
+  interruptCtrl.io.inputs(1) := timerB.io.full
+  interruptCtrl.io.inputs(2) := timerC.io.full
+  interruptCtrl.io.inputs(3) := timerD.io.full
+  io.interrupt := interruptCtrl.io.pendings.orR
+}
+// end case class ApbTimer
+
+object ApbTimer extends App {
+  SpinalVerilog(ApbTimer())
+}
+
+
+
+
+

In practice

+

For instance, this kind of slot pattern is used in Tilelink coherency hub to keep track of all ongoing memory probes in flight:

+

https://github.com/SpinalHDL/SpinalHDL/blob/008c73f1ce18e294f137efe7a1442bd3f8fa2ee0/lib/src/main/scala/spinal/lib/bus/tilelink/coherent/Hub.scala#L376

+

As well in the DRAM / SDR / DDR memory controller to implement the handeling of multiple memory transactions at once (having multiple precharge / active / read / write running at the same time to improve performances) :

+

https://github.com/SpinalHDL/SpinalHDL/blob/1edba1890b5f629b28e5171b3c449155337d2548/lib/src/main/scala/spinal/lib/memory/sdram/xdr/Tasker.scala#L202

+

As well in the NaxRiscv (out of order CPU) load-store-unit to handle the store-queue / load-queue hardware (a bit too scary to show here in the doc XD)

+
+
+
+ + + + +
+