Commit 56210a05 authored by Jens Korinth's avatar Jens Korinth
Browse files

Add 'common/ip/tapasco_status/' from commit 'e209f949'

git-subtree-dir: common/ip/tapasco_status
git-subtree-mainline: 225cfa31
git-subtree-split: e209f949
parents 225cfa31 e209f949
target/
project/
*.pyc
/chisel3/
/test/
TaPaSCo Status Core
===================
Starting from version 1.2, the TaPaSCo status IP core will be built from Chisel.
Maintaining the growing demand for new registers, different kinds of information
etc. made it unwieldy to use Verilog IP. Instead, a custom IP core is generated
for each composition.
*.log
*.jou
.Xil
/target/
/project/
/test/
/ip/
/packaging/target/
/miscutils/target/
name := "chisel-axiutils"
organization := "esa.cs.tu-darmstadt.de"
version := "0.4-SNAPSHOT"
scalaVersion := "2.11.12"
resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases")
)
// Provide a managed dependency on X if -DXVersion="" is supplied on the command line.
val defaultVersions = Map("chisel3" -> "3.1-SNAPSHOT",
"chisel-iotesters" -> "1.2-SNAPSHOT")
libraryDependencies ++= (Seq("chisel3","chisel-iotesters").map {
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) })
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.0.4" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.5" % "test",
"com.typesafe.play" %% "play-json" % "2.6.8",
"org.scalactic" %% "scalactic" % "3.0.4"
)
scalacOptions ++= Seq("-language:implicitConversions", "-language:reflectiveCalls", "-deprecation", "-feature")
// project structure
lazy val packaging = project.in(file("packaging"))
lazy val miscutils = project.in(file("miscutils"))
lazy val axiutils = (project in file(".")).dependsOn(packaging, miscutils, miscutils % "test->test").aggregate(packaging, miscutils)
cleanFiles += (baseDirectory.value / "test")
aggregate in test := false
*.log
*.jou
.Xil
/target/
/project/
/test/
/ip/
# Miscellaneous Chisel IP
Some very basic IP for everyday use, written in Chisel 3.0. Currently contains:
* [DataWidthConverter][1]
Converts `UInt` of arbitrary bit width to `UInt` of different bit width.
Zero delay, handshaked channels using Chisels `Decoupled` interface.
* [DecoupledDataSource][2]
Generic data provider with fixed (compile-time) data; uses handshakes via
Chisels `Decoupled` interface.
* [SignalGenerator][3]
Primitive 1-bit signal generator: Specify via change list, can cycle.
These were basically warm-up exercises with Chisel, but can be useful now and
then. For usage examples see the [unit test suites][4].
[1]: src/main/scala/DataWidthConverter.scala
[2]: src/main/scala/DecoupledDataSource.scala
[3]: src/main/scala/SignalGenerator.scala
[4]: src/test/scala/
name := "chisel-miscutils"
organization := "esa.cs.tu-darmstadt.de"
version := "0.4-SNAPSHOT"
scalaVersion := "2.11.11"
resolvers ++= Seq(
Resolver.sonatypeRepo("snapshots"),
Resolver.sonatypeRepo("releases")
)
// Provide a managed dependency on X if -DXVersion="" is supplied on the command line.
val defaultVersions = Map("chisel3" -> "3.1-SNAPSHOT",
"chisel-iotesters" -> "1.2-SNAPSHOT")
libraryDependencies ++= (Seq("chisel3","chisel-iotesters").map {
dep: String => "edu.berkeley.cs" %% dep % sys.props.getOrElse(dep + "Version", defaultVersions(dep)) })
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.0.4" % "test",
"org.scalacheck" %% "scalacheck" % "1.13.5" % "test",
"com.typesafe.play" %% "play-json" % "2.6.3"
)
// no parallel tests
parallelExecution in Test := false
testForkedParallel in Test := false
scalacOptions ++= Seq("-language:implicitConversions", "-language:reflectiveCalls", "-deprecation", "-feature")
cleanFiles += baseDirectory.value / "test"
package chisel.miscutils
import chisel3._
import chisel3.util._
object DataWidthConverter {
class IO(inWidth: Int, outWidth: Int) extends Bundle {
val inq = Flipped(Decoupled(UInt(inWidth.W)))
val deq = Decoupled(UInt(outWidth.W))
}
}
/**
* 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 DecoupledIO (bits).
* @param outWidth Data width of output DecoupledIO (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)
(implicit logLevel: Logging.Level) extends Module with Logging {
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))
cinfo(s"inWidth = $inWidth, outWidth = $outWidth, littleEndian = $littleEndian")
val io = IO(new DataWidthConverter.IO(inWidth, outWidth))
val ratio: Int = if (inWidth > outWidth) inWidth / outWidth else outWidth / inWidth
val d_w = if (inWidth > outWidth) inWidth else outWidth // data register width
if (inWidth > outWidth)
downsize()
else
upsize()
private def upsize() = {
val i = RegInit(UInt(log2Ceil(ratio + 1).W), init = ratio.U)
val d = RegInit(UInt(outWidth.W), 0.U)
io.inq.ready := i =/= 0.U || (io.inq.valid && io.deq.ready)
io.deq.bits := d
io.deq.valid := i === 0.U
when (io.inq.ready && io.inq.valid) {
if (littleEndian)
d := Cat(io.inq.bits, d) >> inWidth.U
else
d := (d << inWidth.U) | io.inq.bits
i := i - 1.U
}
when (io.deq.valid && io.deq.ready) {
i := Mux(io.inq.valid, (ratio - 1).U, ratio.U)
}
}
private def downsize() = {
val i = RegInit(UInt(log2Ceil(ratio + 1).W), init = 0.U)
val d = RegInit(UInt(inWidth.W), init = 0.U)
io.inq.ready := i === 0.U || (i === 1.U && io.deq.ready)
io.deq.valid := i > 0.U
if (littleEndian)
io.deq.bits := d(outWidth - 1, 0)
else
io.deq.bits := d(inWidth - 1, inWidth - outWidth)
when (i > 0.U && io.deq.ready) {
if (littleEndian)
d := d >> outWidth.U
else
d := d << outWidth.U
i := i - 1.U
}
when (io.inq.ready && io.inq.valid) {
d := io.inq.bits
i := ratio.U
}
}
}
package chisel.miscutils
import chisel3._
import chisel3.util._
object DecoupledDataSource {
/** Interface for DecoupledDataSource. */
class IO[T <: Data](gen: T) extends Bundle {
val out = Decoupled(gen.cloneType)
}
}
/** Data source providing fixed data via Decoupled interface.
* Provides the data given via Decoupled handshakes; if repeat
* is true, data is wrapped around.
* @param gen Type.
* @param size Total number of elements.
* @param data Function providing data for each index
* @param repeat If true, will always have data via wrap-around,
* otherwise valid will go low after data was
* consumed.
**/
class DecoupledDataSource[T <: Data](gen: T,
val size : Int,
val data: (Int) => T,
val repeat: Boolean = true)
(implicit l: Logging.Level) extends Module with Logging {
cinfo("size = %d, repeat = %s, addrWidth = %d".format(size,
if (repeat) "true" else "false", log2Ceil(if (repeat) size else size + 1)))
val ds = for (i <- 0 until size) yield data(i) // evaluate data to array
val io = IO(new DecoupledDataSource.IO(gen)) // interface
val i = RegInit(UInt(log2Ceil(if (repeat) size else size + 1).W), 0.U) // index
val rom = Vec.tabulate(size)(n => ds(n)) // ROM with data
io.out.bits := rom(i) // current index data
io.out.valid := repeat.B | i < size.U // valid until exceeded
when (io.out.fire()) {
val next = if (repeat) {
if (math.pow(2, log2Ceil(size)).toInt == size)
i + 1.U
else
Mux((i + 1.U) < size.U, i + 1.U, 0.U)
} else {
Mux(i < size.U, i + 1.U, i)
}
info(p"i = $i -> $next, bits = 0x${Hexadecimal(io.out.bits.asUInt())}")
i := next
}
}
package chisel.miscutils
import chisel3._
import Logging._
import scala.util.Properties.{lineSeparator => NL}
trait Logging {
self: Module =>
def info(msg: => core.Printable)(implicit level: Level) { log(Level.Info, msg) }
def warn(msg: => core.Printable)(implicit level: Level) { log(Level.Warn, msg) }
def error(msg: => core.Printable)(implicit level: Level) { log(Level.None, msg) }
def log(msgLevel: Level, msg: => core.Printable)(implicit l: Level): Unit = msgLevel match {
case Level.Info if (l == Level.Info) => printf(p"[INFO] $className: $msg$NL")
case Level.Warn if (l == Level.Info || l == Level.Warn) => printf(p"[WARN] $className: $msg$NL")
case Level.None => printf(p"[ERROR] $className: $msg$NL")
case _ => ()
}
def cinfo(msg: => String)(implicit level: Level) { clog(Level.Info, msg) }
def cwarn(msg: => String)(implicit level: Level) { clog(Level.Warn, msg) }
def cerror(msg: => String)(implicit level: Level) { clog(Level.None, msg) }
def clog(msgLevel: Level, msg: => String)(implicit l: Level): Unit = msgLevel match {
case Level.Info if (l == Level.Info) => println(s"[INFO] $className: $msg")
case Level.Warn if (l == Level.Info || l == Level.Warn) => println(s"[WARN] $className: $msg")
case Level.None => println(s"[ERROR] $className: $msg")
case _ => ()
}
private[this] final lazy val className = self.getClass.getSimpleName
}
object Logging {
sealed trait Level
object Level {
final case object Info extends Level
final case object Warn extends Level
final case object None extends Level
}
}
package chisel.miscutils
import chisel3._
import chisel3.util._
object SignalGenerator {
sealed case class Signal(value: Boolean, periods: Int = 1) extends Ordered[Signal] {
import scala.math.Ordered.orderingToOrdered
def compare(that: Signal): Int = periods compare that.periods
def unary_! = this.copy(value = !value)
}
final case class Waveform(signals: Seq[Signal]) {
require (signals.length > 0, "waveform must not be empty")
}
implicit def waveformToSeq(w: Waveform): Seq[Signal] = w.signals
implicit def seqToWaveform(s: Seq[Signal]): Waveform = Waveform.apply(s)
implicit def makeSignal(sd: (Boolean, Int)): Signal = Signal(sd._1, sd._2)
implicit def makeWaveform(ls: List[(Boolean, Int)]): Waveform = ls map makeSignal
class IO extends Bundle {
val v = Output(Bool())
val in = Input(Bool())
}
}
class SignalGenerator(val signals: SignalGenerator.Waveform,
val useInputAsClock: Boolean = false) extends Module {
require (signals.length > 0, "Waveform must not be empty.")
require (signals map (_.periods > 1) reduce (_&&_),
"All signals must have at least two clock cycles length.")
val io = IO(new SignalGenerator.IO)
val cnts_rom = Vec(signals map (n => (n.periods - 1).U))
val vals_rom = Vec(signals map (n => (n.value).B))
val cnt = RegInit(UInt(log2Ceil(signals.max.periods).W), init = cnts_rom(0))
val curr_idx = RegInit(UInt(log2Ceil(signals.length).W), init = 0.U)
val vreg = RegInit(Bool(), init = vals_rom(0))
io.v := vreg
vreg := vals_rom(curr_idx)
// trigger on either clock or pos input edge
when (if (useInputAsClock) io.in && !RegNext(io.in) else true.B) {
when (cnt === 0.U) {
val next_idx = Mux(curr_idx < (signals.length - 1).U, curr_idx + 1.U, 0.U)
curr_idx := next_idx
cnt := cnts_rom(next_idx)
}
.otherwise {
cnt := cnt - 1.U
}
}
}
package chisel.miscutils
import chisel3._
import chisel3.util._
/** A slow queue which delays each element by a configurable delay. */
class SlowQueue(width: Int, val delay: Int = 10) extends Module {
val io = IO(new Bundle {
val enq = Flipped(Decoupled(UInt(width.W)))
val deq = Decoupled(UInt(width.W))
val dly = Input(UInt(log2Ceil(delay).W))
})
val waiting :: ready :: Nil = Enum(2)
val state = RegInit(ready)
val wr = RegInit(UInt(log2Ceil(delay).W), io.dly)
io.deq.bits := io.enq.bits
io.enq.ready := io.deq.ready && state === ready
io.deq.valid := io.enq.valid && state === ready
when (state === ready && io.enq.ready && io.deq.valid) {
state := waiting
wr := io.dly
}
when (state === waiting) {
wr := wr - 1.U
when (wr === 0.U) { state := ready }
}
}
package chisel.miscutils.datawidthconverter
import chisel.miscutils._
import chisel3._
import chisel3.util._
import math.pow
/** Correctness test harness for DataWidthConverter:
* A DecoupledDataSource with random data is connected to a pair
* of data width converters with inverted params. This circuit
* must behave exactly like a delay on the input stream (where
* the length of the delay is 2 * in/out-width-ratio).
* There's a slow queue in-between to simulate receivers with
* varying speed of consumption.
* @param inWidth Bit width of input data.
* @param outWidth Bit width of output data (in and out must be
* integer multiples/fractions of each other)
* @param littleEndian Byte-endianess.
* @param delay Clock cycle delay in [[SlowQueue]].
*/
class CorrectnessHarness(inWidth: Int,
outWidth: Int,
littleEndian: Boolean,
delay: Int = 10)
(implicit logLevel: Logging.Level) extends Module {
require (delay > 0, "delay bitwidth must be > 0")
val io = IO(new Bundle {
val dly = Input(UInt(Seq(log2Ceil(delay), 1).max.W))
val dsrc_out_valid = Output(Bool())
val dsrc_out_bits = Output(UInt())
val dwc_inq_valid = Output(Bool())
val dwc_inq_ready = Output(Bool())
val dwc_deq_valid = Output(Bool())
val dwc_deq_ready = Output(Bool())
val dwc2_inq_valid = Output(Bool())
val dwc2_deq_valid = Output(Bool())
val dwc2_deq_ready = Output(Bool())
val dwc2_deq_bits = Output(UInt())
})
val dwc = Module(new DataWidthConverter(inWidth, outWidth, littleEndian))
val dsrc = Module(new DecoupledDataSource(UInt(inWidth.W),
Seq(Seq(pow(2, inWidth).toLong, dwc.ratio).max, 500.toLong).min.toInt,
n => (scala.math.random * pow(2, inWidth)).toLong.U,
repeat = false))
val dwc2 = Module(new DataWidthConverter(outWidth, inWidth, littleEndian))
val slq = Module(new SlowQueue(outWidth, delay))
dwc.io.inq <> dsrc.io.out
slq.io.enq <> dwc.io.deq
slq.io.dly := io.dly
dwc2.io.inq <> slq.io.deq
dwc2.io.deq.ready := true.B
// internal peek-and-poke does not work, need to wire as outputs:
io.dsrc_out_valid := dsrc.io.out.valid
io.dsrc_out_bits := dsrc.io.out.bits
io.dwc_inq_valid := dwc.io.inq.valid
io.dwc_inq_ready := dwc.io.inq.ready
io.dwc_deq_valid := dwc.io.deq.valid
io.dwc_deq_ready := dwc.io.deq.ready
io.dwc2_inq_valid := dwc2.io.inq.valid
io.dwc2_deq_valid := dwc2.io.deq.valid
io.dwc2_deq_ready := dwc2.io.deq.ready
io.dwc2_deq_bits := dwc2.io.deq.bits
}
package chisel.miscutils.datawidthconverter
import chisel.miscutils._
import chisel3._, chisel3.util._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
/** Generic tester for [[CorrectnessHarness]]:
* Uses DataWidthConverterHarness class to check output correctness.
* Tracks incoming data from the data source in expecteds list.
* Whenever output is valid, it is compared to the expecteds,
* mismatches are reported accordingly.
* Does NOT check timing, only correctness of the output values.
**/
class CorrectnessTester[T <: UInt](m: CorrectnessHarness) extends PeekPokeTester(m) {
import scala.util.Properties.{lineSeparator => NL}
// returns binary string for Int, e.g., 0011 for 3, width 4
private def toBinaryString(n: BigInt, width: Int) =
"%%%ds".format(width).format(n.toString(2)).replace(' ', '0')
/** Performs data correctness check. **/
def check() = {
var i = 0
var delay = m.slq.delay - 1
poke (m.io.dly, delay)
var expecteds: List[BigInt] = List()
def running = peek(m.io.dsrc_out_valid) > 0 ||
peek(m.io.dwc_inq_valid) > 0 ||
peek(m.io.dwc_deq_valid) > 0 ||
peek(m.io.dwc2_inq_valid) > 0 ||
peek(m.io.dwc2_deq_valid) > 0
while (running) {
// scan output element and add to end of expected list
if (peek(m.io.dsrc_out_valid) > 0 && peek(m.io.dwc_inq_ready) > 0) {
val e = peek(m.io.dsrc_out_bits)
expecteds = expecteds :+ e
//println ("adding expected value: %d (%s)".format(e, toBinaryString(e, m.dwc.inWidth)))
}
// check output element: must match head of expecteds
if (peek(m.io.dwc2_deq_valid) > 0 && peek(m.io.dwc2_deq_ready) > 0) {
// update delay (decreasing with each output)
delay = if (delay == 0) m.slq.delay - 1 else delay - 1
poke(m.io.dly, delay)
// check output
val v = peek(m.io.dwc2_deq_bits)
if (expecteds.isEmpty) {
val errmsg = "received value output value %d (%s), but none expected yet"
.format(v, toBinaryString(v, m.dwc.inWidth))
println (errmsg)
expect(false, errmsg)
} else {
if (v == expecteds.head) {
// println ("element #%d ok!".format(i))
} else {
val errmsg = "element #%d wrong: expected %d (%s), found %d (%s)".format(
i, expecteds.head, toBinaryString(expecteds.head, m.dwc.inWidth),
v, toBinaryString(v, m.dwc.inWidth))
println (errmsg)
expect(v == expecteds.head, errmsg)
}
expecteds = expecteds.tail
}
i += 1
}
// advance sim
step (1)
}
}
reset(10) // reset for 10 cycles
check()
step (20) // settle output
}
package chisel.miscutils.datawidthconverter
import chisel.miscutils._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
import org.scalacheck._, org.scalacheck.Prop._
import org.scalatest.prop.Checkers
import generators._
class DataWidthConverterSpec extends ChiselFlatSpec with Checkers {
implicit val logLevel = Logging.Level.Warn