插槽(Slots)

简介

假设您有一些硬件必须跟踪多个相似的正在进行的活动,您可能需要实现一组“插槽”来执行此操作。此示例展示如何使用 Area、OHMasking.first、onMask和reader来完成此操作。

实现

此实现避免使用Vec。相反,它使用允许在每个插槽中混合信号、寄存器和逻辑定义的逻辑区。

请注意, reader API 适用于1.9.1之后推出的SpinalHDL版本

package spinaldoc.examples.advanced

import spinal.core._
import spinal.lib._
import scala.language.postfixOps

case class SlotsDemo(slotsCount : Int) extends Component {
  //...
  
  
  //Create the hardware for each slot
  //Note each slot is an Area, not a Bundle
  val slots = for(i <- 0 until slotsCount) yield new Area{
    //Because the slot is an Area, we can define mix signal, registers, logic definitions
    //Here are the registers for each slots
    val valid = RegInit(False)
    val address = Reg(UInt(8 bits))
    val age = Reg(UInt(16 bits)) //Will count since how many cycles the slot is valid

    //Here is some hardware behaviour for each slots
    //Implement the age logic
    when(valid){
      age := age + 1
    }

    //removeIt will be used as a slot interface later on
    val removeIt = False
    when(removeIt){
      valid := False
    }
  }

  //Logic to allocate a new slot
  val insert = new Area{
    val cmd = Stream(UInt(8 bits)) //interface to issue requests
    val free = slots.map(!_.valid)
    val freeOh = OHMasking.first(free) //Get the first free slot (on hot mask)
    cmd.ready := free.orR //Only allow cmd when there is a free slot
    when(cmd.fire){
      //slots.onMask(freeOh)(code) will execute the code for each slot where the corresponding freeOh bit is set
      slots.onMask(freeOh){slot =>
        slot.valid := True
        slot.address := cmd.payload
        slot.age := 0
      }
    }
  }

  //Logic to remove the slots which match a given address (assuming there is not more than one match)
  val remove = new Area{
    val cmd = Flow(UInt(8 bits))//interface to issue requests
    val oh = slots.map(s => s.valid && s.address === cmd.payload) //oh meaning "one hot"
    when(cmd.fire){
      slots.onMask(oh){ slot =>
        slot.removeIt := True
      }
    }

    val reader = slots.reader(oh) //Create a facility to read the slots using "oh" as index
    val age = reader(_.age) //Age of the slot which is selected by "oh"
  }
  
  //...
}

object SlotsDemo extends App {
  SpinalVerilog(SlotsDemo(4))
}

应用

例如,在Tilelink总线(具有一致性机制)hub中,这种插槽模式用于跟踪所有正在进行的内存操作:

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

以及在 DRAM / SDR / DDR 内存控制器中实现同时处理多个内存事务(同时运行多个预充电/激活/读/写以提高性能):

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

以及在NaxRiscv(乱序 CPU)的加载存储单元中处理存储队列/加载队列的硬件(难度有些可怕,不宜在文档中展示XD)