Commit fae43067 authored by Jens Korinth's avatar Jens Korinth
Browse files

Implement DataWidthConverter and unit tests

* basic Decoupled-based data width converter
* currently only downsizing implemented with endianess selector
* works, but performance is lackluster yet
* unit tests cover several interesting combinations in both endianesses
parent 5a1f9945
import Chisel._
/**
* DataWidthConverter converts the data width of a Queue.
* Output is provided via a Queue, with increased or decreased
* data rate, depending on the direction of the conversion.
* Note: This would be much more useful, if the two Queues
* could use different clocks, but multi-clock support
* in Chisel is currently unstable.
* @param inWidth Data width of input Decoupled (bits).
* @param outWidth Data width of output Decoupled (bits); must
* be integer multiples of each other.
* @param littleEndian if inWidth > outWidth, determines
* the order of the nibbles (low to high)
**/
class DataWidthConverter(
val inWidth: Int,
val outWidth: Int,
val littleEndian: Boolean = true
) extends Module {
require (inWidth > 0, "inWidth must be > 0")
require (outWidth > 0, "inWidth must be > 0")
require (inWidth != outWidth, "inWidth (%d) must be different from outWidth (%d)"
.format(inWidth, outWidth))
require (inWidth % outWidth == 0 || outWidth % inWidth == 0,
"inWidth (%d) and outWidth (%d) must be integer multiples of each other"
.format(inWidth, outWidth))
val io = new Bundle {
val inq = Decoupled(UInt(width = inWidth)).flip()
val deq = Decoupled(UInt(width = outWidth))
}
val ratio: Int = if (inWidth > outWidth) inWidth / outWidth else outWidth / inWidth
val d = Reg(UInt(width = inWidth)) // current value
val d_hs = Reg(Bool()) // handshake input
val i = Reg(UInt(width = log2Up(ratio))) // current byte index
val inq_ready = Reg(Bool()) // input data ready
val inq_valid = RegNext(io.inq.valid) // input data valid?
val deq_ready = RegNext(io.deq.ready) // output data ready?
val deq_valid = Reg(Bool()) // output data valid
inq_ready := !reset && !d_hs
deq_valid := !reset && d_hs
io.inq.ready := inq_ready
io.deq.valid := deq_valid
when (reset) {
d := UInt(0)
d_hs := Bool(false)
i := UInt(if (littleEndian) 0 else ratio - 1)
inq_ready := Bool(false)
deq_valid := Bool(false)
}
if (inWidth > outWidth) {
val outFifo = Module(new Queue(UInt(width = outWidth), ratio))
outFifo.io.deq <> io.deq
outFifo.io.enq.bits := d(i * UInt(outWidth) + UInt(outWidth), i * UInt(outWidth))
outFifo.io.enq.valid := d_hs
val out_ready = RegNext(outFifo.io.enq.ready)
when (reset) {}
.otherwise {
when (inq_ready && inq_valid) {
d := io.inq.bits
d_hs := Bool(true)
inq_ready := Bool(false)
}
when (d_hs) {
when (out_ready) {
if (littleEndian) {
i := Mux(i === UInt(ratio - 1), UInt(0), i + UInt(1))
when (i === UInt(ratio - 1)) { d_hs := Bool(false) }
} else {
i := Mux(i === UInt(0), UInt(ratio - 1), i - UInt(1))
when (i === UInt(0)) { d_hs := Bool(false) }
}
}
}
}
} else {
assert(Bool(false), "NOT IMPLEMENTED YET")
}
}
import Chisel._
import org.scalatest.junit.JUnitSuite
import org.junit.Test
import org.junit.Assert._
import scala.math._
/**
* DataWidthConverterHarness: Attaches data source to DWC.
**/
class DataWidthConverterHarness(inWidth: Int, outWidth: Int, littleEndian: Boolean) extends Module {
val io = new Bundle
val dwc = Module(new DataWidthConverter(inWidth, outWidth, false))
val dsrc = Module(new DecoupledDataSource(UInt(width = inWidth),
Seq(pow(2, inWidth).toInt, 100000).min,
n => UInt(n, width = inWidth),
littleEndian))
dwc.io.inq <> dsrc.io.out
dwc.io.deq.ready := !reset
}
/**
* Generic tester for DataWidthConverterHarness:
* Checks that the output matches the input.
**/
class DataWidthConverter_OutputCheck[T <: UInt](m: DataWidthConverterHarness) extends Tester(m, false) {
import scala.util.Properties.{lineSeparator => NL}
var errors: List[String] = List()
var curr_val = 0
var curr_byt = if (m.dwc.littleEndian) 0 else m.dwc.ratio - 1
var curr_idx = 0
while (curr_idx < m.dsrc.size) {
if (peek(m.dwc.io.deq.valid) > 0) {
val v = m.dwc.io.deq.bits
//println("read %d (0b%s)".format(peek(v).toInt, Integer.toBinaryString(peek(v).toInt)))
curr_val += peek(v).toInt << (curr_byt * m.dwc.outWidth)
if (m.dwc.littleEndian)
curr_byt += 1
else
curr_byt -= 1
}
if (curr_byt == m.dwc.ratio || curr_byt == -1) {
if (curr_val != curr_idx) {
errors = "element #%d should be %d (%d, 0b%s)".format(
curr_idx,
curr_idx,
curr_val,
Integer.toBinaryString(curr_val)
) :: errors
}
//println("read whole: %d (0b%s)".format(curr_val, Integer.toBinaryString(curr_val)))
curr_idx += 1
curr_val = 0
if (m.dwc.littleEndian)
curr_byt = 0
else
curr_byt = m.dwc.ratio - 1
}
step(1)
}
step (20)
assertTrue (("all elements should match, errors: " :: errors).mkString(NL), errors.length == 0)
}
class DataWidthConverterSuite extends JUnitSuite {
def downsize(inWidth: Int, outWidth: Int, littleEndian: Boolean = true) = {
println("testing conversion of %d bit to %d bit, %s ...".format(inWidth, outWidth, if (littleEndian) "little-endian" else "big-endian"))
chiselMainTest(Array("--genHarness", "--backend", "c", "--vcd", "--targetDir", "test", "--compile", "--test"),
() => Module(new DataWidthConverterHarness(inWidth, outWidth, littleEndian)))
{ m => new DataWidthConverter_OutputCheck(m) }
}
@Test def check2to1le { downsize(2, 1, true) }
@Test def check2to1be { downsize(2, 1, false) }
@Test def check8to1le { downsize(8, 1, true) }
@Test def check8to1be { downsize(8, 1, false) }
@Test def check16to4le { downsize(16, 4, true) }
@Test def check16to4be { downsize(16, 4, false) }
@Test def check16to8le { downsize(16, 8, true) }
@Test def check16to8be { downsize(16, 8, false) }
@Test def check32to8le { downsize(32, 8, true) }
@Test def check32to8be { downsize(32, 8, false) }
@Test def check64ot8le { downsize(64, 8, true) }
@Test def check64to8be { downsize(64, 8, false) }
@Test def check64ot32le { downsize(64, 32, true) }
@Test def check64to32be { downsize(64, 32, false) }
//@Test def check2to4le { downsize(2, 4, true) }
//@Test def check2to4be { downsize(2, 4, false) }
/*@Test def check2to1le { downsize(2, 1, true) }
@Test def check2to1be { downsize(2, 1, false) }
@Test def check8to1le { downsize(8, 1, true) }
@Test def check8to1be { downsize(8, 1, false) }
@Test def check16to4le { downsize(16, 4, true) }
@Test def check16to4be { downsize(16, 4, false) }
@Test def check16to8le { downsize(16, 8, true) }
@Test def check16to8be { downsize(16, 8, false) }
@Test def check32to8le { downsize(32, 8, true) }
@Test def check32to8be { downsize(32, 8, false) }
@Test def check64ot8le { downsize(64, 8, true) }
@Test def check64to8be { downsize(64, 8, false) }
@Test def check64ot32le { downsize(64, 32, true) }
@Test def check64to32be { downsize(64, 32, false) }*/
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment