Skip to content

Commit

Permalink
New Cut in Chisel (#342)
Browse files Browse the repository at this point in the history
* New Cut

* scalafmt

* Update CustomOperator

* Update CustomOperators

* scalafmt

* Bug Fix (?)

* Remove comments
  • Loading branch information
IveanEx authored Sep 22, 2024
1 parent c802d7e commit cef3aa8
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class DataPathExtensionHost(
// Connect the data path
if (headCut) {
if (halfCut) {
io.data.in -/> extensions.head.io.data_i
io.data.in -\> extensions.head.io.data_i
} else {
io.data.in -||> extensions.head.io.data_i
}
Expand All @@ -113,7 +113,7 @@ class DataPathExtensionHost(

if (tailCut) {
if (halfCut) {
extensions.last.io.data_o -/> io.data.out
extensions.last.io.data_o -\> io.data.out
} else {
extensions.last.io.data_o -||> io.data.out
}
Expand All @@ -126,7 +126,7 @@ class DataPathExtensionHost(
extensions.zip(extensions.tail).foreach {
case (a, b) => {
if (halfCut) {
a.io.data_o -/> b.io.data_i
a.io.data_o -\> b.io.data_i
} else {
a.io.data_o -||> b.io.data_i
}
Expand Down
4 changes: 2 additions & 2 deletions hw/chisel/src/main/scala/snax/streamer/Streamer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,8 @@ class Streamer(
extensionList = param.dataPathExtensionParam,
dataWidth = param.fifoWidthReader(i),
headCut = false,
tailCut = true,
halfCut = true,
tailCut = false,
halfCut = false,
moduleNamePrefix = param.tagName
)
)
Expand Down
85 changes: 69 additions & 16 deletions hw/chisel/src/main/scala/snax/utils/CustomOperators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,40 @@ import chisel3.reflect.DataMirror
* signal (Flipped port); and insert one level of pipeline in between to avoid
* long combinatorial datapath
*/

class DataCut[T <: Data](gen: T, delay: Int) extends Module {
val io = IO(new Bundle {
val in = Flipped(Decoupled(gen))
val out = Decoupled(gen)
})

val in = Wire(ValidIO(gen))
val out = Wire(ValidIO(gen))
val shiftPermission = Wire(Bool())
val shiftSuggestion = Wire(Bool())
val shift =
shiftPermission && shiftSuggestion // shift is true when both shiftPermission and shiftSuggestion are true
in.bits := io.in.bits
in.valid := io.in.valid
io.in.ready := shiftPermission
io.out.valid := out.valid
io.out.bits := out.bits
out := ShiftRegister(in, delay, shift)

// shiftPermission is true when last item's valid is true and io.out.ready is true or last item's valid is false
shiftPermission := (out.valid && io.out.ready) || !out.valid

val dataInsideShiftRegister = Wire(Bool())

// shiftSuggestion is true when dataInsideShiftRegister is true or input.valid is true
shiftSuggestion := dataInsideShiftRegister || io.in.valid

// When the counter is abbout to overflow, data does not inside the shift register
val insideCounter = Counter(0 to delay, shift, io.in.valid)
dataInsideShiftRegister := insideCounter._1 =/= delay.U

}

object DecoupledCut {
implicit class BufferedDecoupledConnectionOp[T <: Data](
val left: DecoupledIO[T]
Expand All @@ -17,47 +51,66 @@ object DecoupledCut {

def -|>(
right: DecoupledIO[T]
)(implicit sourceInfo: chisel3.experimental.SourceInfo): Unit = {
)(implicit sourceInfo: chisel3.experimental.SourceInfo): DecoupledIO[T] = {
val buffer = Module(
new Queue(chiselTypeOf(left.bits), entries = 1, pipe = false)
)
buffer.suggestName("fullCut1")
buffer.suggestName("fullCutHalfBandwidth")

left <> buffer.io.enq
buffer.io.deq <> right
right
}

def -||>(
right: DecoupledIO[T]
)(implicit sourceInfo: chisel3.experimental.SourceInfo): Unit = {
)(implicit sourceInfo: chisel3.experimental.SourceInfo): DecoupledIO[T] = {
val buffer = Module(
new Queue(chiselTypeOf(left.bits), entries = 2, pipe = false)
)
buffer.suggestName("fullCut2")
buffer.suggestName("fullCutFullBandwidth")
left <> buffer.io.enq
buffer.io.deq <> right
right
}

def -|||>(
def -\>(
right: DecoupledIO[T]
)(implicit sourceInfo: chisel3.experimental.SourceInfo): Unit = {
)(implicit sourceInfo: chisel3.experimental.SourceInfo): DecoupledIO[T] = {
val buffer = Module(
new Queue(chiselTypeOf(left.bits), entries = 3, pipe = false)
new DataCut(chiselTypeOf(left.bits), delay = 1)
)
buffer.suggestName("fullCut3")
left <> buffer.io.enq
buffer.io.deq <> right
buffer.suggestName("dataCut1")

left <> buffer.io.in
buffer.io.out <> right
right
}

def -/>(
def -\\>(
right: DecoupledIO[T]
)(implicit sourceInfo: chisel3.experimental.SourceInfo): Unit = {
)(implicit sourceInfo: chisel3.experimental.SourceInfo): DecoupledIO[T] = {
val buffer = Module(
new Queue(chiselTypeOf(left.bits), entries = 1, pipe = true)
new DataCut(chiselTypeOf(left.bits), delay = 2)
)
buffer.suggestName("halfCut1")
left <> buffer.io.enq
buffer.io.deq <> right
buffer.suggestName("dataCut2")

left <> buffer.io.in
buffer.io.out <> right
right
}

def -\\\>(
right: DecoupledIO[T]
)(implicit sourceInfo: chisel3.experimental.SourceInfo): DecoupledIO[T] = {
val buffer = Module(
new DataCut(chiselTypeOf(left.bits), delay = 3)
)
buffer.suggestName("dataCut3")

left <> buffer.io.in
buffer.io.out <> right
right
}
}
}
Expand Down
113 changes: 113 additions & 0 deletions hw/chisel/src/test/scala/snax/utils/CustomOperatorsTester.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package snax.utils

import chisel3._
import chisel3.util._
import snax.utils.DecoupledCut._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import scala.util.Random
import scala.util.control.Breaks.{break, breakable}

class DataCutWrapper extends Module {
val io = IO(new Bundle {
val in = Flipped(Decoupled(UInt(8.W)))
val out = Decoupled(UInt(8.W))
})

val middle = Wire(Decoupled(UInt(8.W)))

io.in -||> middle -\\\> io.out
}

class CustomOperatorsTester extends AnyFlatSpec with ChiselScalatestTester {
"The test of DecoupledCut" should " pass" in {

val dataIn = collection.mutable.ListBuffer.fill(16384)(Random.nextInt(256))
val dataInCopy = dataIn.clone()
val dataOut = collection.mutable.ListBuffer[Int]()

var allowIn = false
var allowOut = false

test(new DataCutWrapper)
.withAnnotations(Seq(WriteVcdAnnotation, VerilatorBackendAnnotation)) {
dut =>
dut.clock.setTimeout(0)

var concurrent_threads =
new chiseltest.internal.TesterThreadList(Seq())
// Turn on and off the gate
concurrent_threads = concurrent_threads.fork {
breakable {
while (true) {
val delay = Random.between(50, 100)
dut.clock.step(delay)
allowIn = !allowIn
if (dataIn.isEmpty) {
allowIn = true
break()
}
}
}
}

concurrent_threads = concurrent_threads.fork {
breakable {
while (true) {
val delay = Random.between(50, 100)
dut.clock.step(delay)
allowOut = !allowOut
if (dataIn.isEmpty) {
allowOut = true
break()
}
}
}
}

concurrent_threads = concurrent_threads.fork {
var i = 0
breakable {
while (true) {
if (allowIn) {
dut.io.in.valid.poke(true.B)
dut.io.in.bits.poke(dataIn.head.U)
while (!dut.io.in.ready.peekBoolean()) {
dut.clock.step()
}
dut.clock.step()
dut.io.in.valid.poke(false.B)
dataIn.dropInPlace(1)
if (dataIn.isEmpty) break()
} else dut.clock.step()
}
}
}

concurrent_threads = concurrent_threads.fork {
var i = 0
breakable {
while (true) {
if (allowOut) {
dut.io.out.ready.poke(true.B)
while (!dut.io.out.valid.peekBoolean()) {
dut.clock.step()
}
dataOut.append(dut.io.out.bits.peekInt().toInt)
dut.clock.step()
dut.io.out.ready.poke(false.B)
if (dataOut.length == dataInCopy.length) break()
} else dut.clock.step()
}
}
}

concurrent_threads.joinAndStep()
if (dataInCopy == dataOut) {
println("Test passed")
} else {
throw new Exception("Test failed")
}
}
}
}

0 comments on commit cef3aa8

Please sign in to comment.