Skip to content

Commit 3e4abd8

Browse files
committed
rio: add reconfigurable pifo hardware implementation
1 parent 7ec7e9a commit 3e4abd8

File tree

20 files changed

+2288
-0
lines changed

20 files changed

+2288
-0
lines changed

pifo-hardware/.gitignore

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
*.class
2+
*.log
3+
*.bak
4+
5+
# sbt specific
6+
.cache/
7+
.history/
8+
.lib/
9+
dist/*
10+
target
11+
lib_managed/
12+
src_managed/
13+
project/boot/
14+
project/project
15+
project/plugins/project/
16+
17+
# Scala-IDE specific
18+
.scala_dependencies
19+
.worksheet
20+
.bloop
21+
22+
.idea
23+
out
24+
25+
# Metals
26+
.metals
27+
project/metals.sbt
28+
29+
# Eclipse
30+
bin/
31+
.classpath
32+
.project
33+
.settings
34+
.cache-main
35+
36+
#User
37+
/*.vhd
38+
/*.v
39+
*.cf
40+
*.json
41+
*.vcd
42+
!tester/src/test/resources/*.vhd
43+
44+
45+
simWorkspace/
46+
tmp/
47+
null

pifo-hardware/.mill-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.11.11

pifo-hardware/.scalafmt.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version = 3.6.0
2+
runner.dialect = scala212
3+
align.preset = some
4+
maxColumn = 120
5+
docstrings.wrap = no

pifo-hardware/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Runtime reconfigurable pifo hardware
2+
3+
```bash
4+
sbt "runMain rio.sim.BasicPifoSim"
5+
```
6+
7+
8+

pifo-hardware/build.sbt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
ThisBuild / version := "1.0"
2+
ThisBuild / scalaVersion := "2.13.14"
3+
ThisBuild / organization := "org.example"
4+
5+
val spinalVersion = "1.12.3"
6+
val spinalCore = "com.github.spinalhdl" %% "spinalhdl-core" % spinalVersion
7+
val spinalLib = "com.github.spinalhdl" %% "spinalhdl-lib" % spinalVersion
8+
val spinalIdslPlugin = compilerPlugin("com.github.spinalhdl" %% "spinalhdl-idsl-plugin" % spinalVersion)
9+
10+
lazy val rio = (project in file("."))
11+
.settings(
12+
name := "rio",
13+
Compile / scalaSource := baseDirectory.value / "hw" / "spinal",
14+
libraryDependencies ++= Seq(spinalCore, spinalLib, spinalIdslPlugin)
15+
)
16+
17+
fork := true

pifo-hardware/build.sc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import mill._, scalalib._
2+
3+
val spinalVersion = "1.12.3"
4+
5+
object rio extends SbtModule {
6+
def scalaVersion = "2.13.14"
7+
override def millSourcePath = os.pwd
8+
def sources = T.sources(
9+
millSourcePath / "hw" / "spinal"
10+
)
11+
def ivyDeps = Agg(
12+
ivy"com.github.spinalhdl::spinalhdl-core:$spinalVersion",
13+
ivy"com.github.spinalhdl::spinalhdl-lib:$spinalVersion"
14+
)
15+
def scalacPluginIvyDeps = Agg(ivy"com.github.spinalhdl::spinalhdl-idsl-plugin:$spinalVersion")
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package rio
2+
3+
import spinal.core._
4+
import spinal.core.sim._
5+
6+
object Config {
7+
def spinal = SpinalConfig(
8+
targetDirectory = "hw/gen",
9+
defaultConfigForClockDomains = ClockDomainConfig(
10+
resetActiveLevel = HIGH
11+
),
12+
onlyStdLogicVectorAtTopLevelIo = false
13+
)
14+
15+
def sim = SimConfig.withConfig(spinal).withFstWave
16+
}
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
package rio
2+
3+
import scala.collection.mutable.ArrayBuffer
4+
5+
import spinal.core._
6+
import spinal.lib._
7+
import scala.collection.mutable
8+
9+
// Configuration case class to match the Verilog PIFO parameters
10+
case class PifoConfig(
11+
numPifo: Int = 64,
12+
bitPort: Int = 8,
13+
bitPrio: Int = 16,
14+
bitData: Int = 32,
15+
) {
16+
val bitPifo = log2Up(numPifo)
17+
}
18+
19+
// Data type for PIFO entries
20+
case class PifoData(config: PifoConfig) extends Bundle {
21+
val priority = UInt(config.bitPrio bits)
22+
val data = UInt(config.bitData bits)
23+
}
24+
25+
// Pop interface bundle
26+
case class PifoPopInterface(config: PifoConfig) extends Bundle {
27+
val port = UInt(config.bitPort bits)
28+
}
29+
30+
// Pop response bundle
31+
case class PifoPopResponse(config: PifoConfig) extends Bundle {
32+
val exist = Bool()
33+
val priority = UInt(config.bitPrio bits)
34+
val data = UInt(config.bitData bits)
35+
}
36+
37+
// Push interface bundle
38+
case class PifoEntry(config: PifoConfig) extends Bundle {
39+
val priority = UInt(config.bitPrio bits)
40+
val data = UInt(config.bitData bits)
41+
val port = UInt(config.bitPort bits)
42+
}
43+
44+
// SpinalHDL blackbox wrapper for the pifo Verilog module
45+
case class PifoBlackbox(config: PifoConfig = PifoConfig()) extends BlackBox {
46+
47+
// Define the IO bundle to match the Verilog interface exactly
48+
val io = new Bundle {
49+
// Pop interface (single pop port)
50+
val pop_0 = in Bool()
51+
val oprt_0 = in UInt(config.bitPort bits)
52+
val ovld_0 = out Bool()
53+
val opri_0 = out UInt(config.bitPrio bits)
54+
val odout_0 = out UInt(config.bitData bits)
55+
56+
// Push interface 1
57+
val push_1 = in Bool()
58+
val uprt_1 = in UInt(config.bitPort bits)
59+
val upri_1 = in UInt(config.bitPrio bits)
60+
val udin_1 = in UInt(config.bitData bits)
61+
62+
// Push interface 2
63+
val push_2 = in Bool()
64+
val uprt_2 = in UInt(config.bitPort bits)
65+
val upri_2 = in UInt(config.bitPrio bits)
66+
val udin_2 = in UInt(config.bitData bits)
67+
}
68+
69+
// Ensure port names match exactly with the Verilog module
70+
noIoPrefix()
71+
72+
// Clock and reset mapping
73+
val clk = in Bool()
74+
val rst = in Bool()
75+
mapCurrentClockDomain(clk, rst)
76+
77+
// Set the Verilog module name
78+
setDefinitionName("pifo")
79+
80+
// Add Verilog parameters
81+
addGeneric("NUMPIFO", config.numPifo)
82+
addGeneric("BITPORT", config.bitPort)
83+
addGeneric("BITPRIO", config.bitPrio)
84+
addGeneric("BITDATA", config.bitData)
85+
86+
// Add the appropriate Verilog file based on useDropFeature flag
87+
addRTLPath("hw/verilog/pifo_orig.v")
88+
}
89+
90+
// Wrapper class with Flow interfaces as requested
91+
class Pifo(config: PifoConfig = PifoConfig()) extends Component {
92+
93+
val io = new Bundle {
94+
// Pop interface - single flow
95+
val popRequest = slave(Flow(PifoPopInterface(config)))
96+
val popResponse = master(Flow(PifoPopResponse(config)))
97+
98+
// Push interfaces - 1 flow each as requested
99+
val push1 = slave(Flow(PifoEntry(config)))
100+
val push2 = slave(Flow(PifoEntry(config)))
101+
}
102+
103+
// Instantiate the blackbox
104+
val pifo = PifoBlackbox(config)
105+
106+
// Connect pop interface directly
107+
pifo.io.pop_0 := io.popRequest.valid
108+
pifo.io.oprt_0 := io.popRequest.port
109+
110+
val popValid = Reg(Bool()) init(False)
111+
popValid := pifo.io.pop_0
112+
113+
io.popResponse.valid := popValid
114+
io.popResponse.exist := pifo.io.ovld_0
115+
io.popResponse.priority := pifo.io.opri_0
116+
io.popResponse.data := pifo.io.odout_0
117+
118+
// Connect push interfaces directly (1 flow each)
119+
pifo.io.push_1 := io.push1.valid
120+
pifo.io.uprt_1 := io.push1.port
121+
pifo.io.upri_1 := io.push1.priority
122+
pifo.io.udin_1 := io.push1.data
123+
124+
pifo.io.push_2 := io.push2.valid
125+
pifo.io.uprt_2 := io.push2.port
126+
pifo.io.upri_2 := io.push2.priority
127+
pifo.io.udin_2 := io.push2.data
128+
}
129+
130+
// Helper case class for PIFO entries
131+
132+
// SpinalHDL RTL implementation of PIFO
133+
class PifoRTL(config: PifoConfig) extends Component {
134+
135+
val io = new Bundle {
136+
// Push interfaces - 2 input ports
137+
val push1 = slave(Flow(PifoEntry(config)))
138+
val push2 = slave(Flow(PifoEntry(config)))
139+
140+
// Pop request interface - specifies which port to pop from
141+
val popRequest = slave(Flow(PifoPopInterface(config)))
142+
val popResponse = master(Flow(PifoPopResponse(config)))
143+
}
144+
145+
// Internal PIFO storage
146+
val pifoArray = Vec(Reg(PifoEntry(config)), config.numPifo)
147+
val pifoCount = Reg(UInt(config.bitPifo + 1 bits)) init(0)
148+
149+
// Find insertion position for a packet based on priority
150+
def findFirstPosition(defaultValue : Bool = False)(F : PifoEntry => Bool): (Bool, UInt) = {
151+
val bits = Vec(Bool(), config.numPifo)
152+
(pifoArray zip bits).zipWithIndex.foreach { case ((idx, bit), i) =>
153+
// TODO(zhiyuang): check this condition
154+
bit := Mux(U(i) < pifoCount, F(idx), defaultValue)
155+
}
156+
val encoder = PriorityEncoderLogBlackbox(config.numPifo)
157+
encoder.io.decode := bits.asBits
158+
(encoder.io.valid, encoder.io.encode)
159+
}
160+
161+
// Array update logic
162+
var nextArray = ArrayBuffer.fill(config.numPifo)(UInt(2 bits))
163+
for (i <- 0 until config.numPifo) { nextArray(i) := 0 }
164+
var nextCount = CombInit(pifoCount)
165+
166+
def prepareShift(start : UInt, offset : UInt) = {
167+
for(i <- 0 until config.numPifo) {
168+
when(U(i) >= start && U(i) < config.numPifo) {
169+
nextArray(i) \= nextArray(i) + offset
170+
}
171+
}
172+
}
173+
174+
// Find pop position based on requested port
175+
val (popExists, popPosition) = findFirstPosition()(_.port === io.popRequest.port)
176+
177+
when (io.popRequest.valid && popExists) {
178+
prepareShift(popPosition, 3)
179+
nextCount \= nextCount - 1
180+
}
181+
182+
val (_, pos1) = findFirstPosition(True) { _.priority < io.push1.priority }
183+
val (_, pos2) = findFirstPosition(True) { _.priority < io.push2.priority }
184+
185+
var adjustedPos1 = CombInit(pos1)
186+
when(popPosition < pos1 && io.popRequest.valid) { adjustedPos1 \= adjustedPos1 - 1 }
187+
when(pos2 < pos1 && io.push2.valid) { adjustedPos1 \= adjustedPos1 + 1 }
188+
189+
var adjustedPos2 = CombInit(pos2)
190+
when(popPosition < pos2 && io.popRequest.valid) { adjustedPos2 \= adjustedPos2 - 1 }
191+
when(pos1 < pos2 && io.push1.valid) { adjustedPos2 \= adjustedPos2 + 1 }
192+
when(pos1 === pos2 && io.push1.valid) { adjustedPos2 \= adjustedPos2 + 1 }
193+
194+
when (io.push1.valid && nextCount < config.numPifo) {
195+
prepareShift(adjustedPos1 + 1, 1)
196+
nextCount \= nextCount + 1
197+
}
198+
when (io.push2.valid && nextCount < config.numPifo) {
199+
prepareShift(adjustedPos2 + 1, 1)
200+
nextCount \= nextCount + 1
201+
}
202+
203+
// TODO(zhiyuang): convert this to a mux on shift regs
204+
for (i <- 0 until config.numPifo) {
205+
var orig = CombInit(pifoArray(i))
206+
when (adjustedPos1 === U(i) && io.push1.valid) {
207+
orig := io.push1.payload
208+
} elsewhen (adjustedPos2 === U(i) && io.push2.valid) {
209+
orig := io.push2.payload
210+
}
211+
pifoArray(i) := orig
212+
213+
if (i > 0) {
214+
when (nextArray(i) === 1) {
215+
pifoArray(i) := pifoArray(i - 1)
216+
}
217+
}
218+
219+
if (i > 1) {
220+
when (nextArray(i) === 2) {
221+
pifoArray(i) := pifoArray(i - 2)
222+
}
223+
}
224+
225+
if (i < config.numPifo - 1) {
226+
when (nextArray(i) === 3) {
227+
pifoArray(i) := pifoArray(i + 1)
228+
}
229+
}
230+
}
231+
232+
// output next cycle
233+
io.popResponse.valid := RegNext(io.popRequest.valid)
234+
io.popResponse.exist := RegNext(popExists)
235+
io.popResponse.data := RegNext(pifoArray(popPosition).data)
236+
io.popResponse.priority := RegNext(pifoArray(popPosition).priority)
237+
238+
pifoCount := nextCount
239+
}
240+
241+
// SpinalHDL blackbox wrapper for priority_encode_log.v
242+
case class PriorityEncoderLogBlackbox(width: Int) extends BlackBox {
243+
assert(isPow2(width), "Width must be a power of 2")
244+
val logWidth = log2Up(width)
245+
246+
addGeneric("width", width)
247+
addGeneric("log_width", logWidth)
248+
249+
val io = new Bundle {
250+
val clk = in Bool()
251+
val rst = in Bool()
252+
val decode = in Bits(width bits)
253+
val encode = out UInt(logWidth bits)
254+
val valid = out Bool()
255+
}
256+
257+
noIoPrefix()
258+
259+
// Clock and reset mapping
260+
mapCurrentClockDomain(io.clk, io.rst)
261+
262+
// Set the Verilog module name
263+
setDefinitionName("priority_encode_log")
264+
265+
// Add the Verilog file path
266+
addRTLPath("hw/verilog/priority_encode_log.v")
267+
}
268+

0 commit comments

Comments
 (0)