diff options
| author | Jim Lawson | 2016-06-20 11:08:46 -0700 |
|---|---|---|
| committer | Jim Lawson | 2016-06-20 11:08:46 -0700 |
| commit | d408d73a171535bd7c2ba9d0037c194022b8a62f (patch) | |
| tree | 81885a99ec56e89532bc3fa338f22b163dcc4d1f /src/main/scala/chisel3 | |
| parent | b5a534914795d9d17f4dfe623525f1b804e4c60f (diff) | |
Rename chisel3 package.
Diffstat (limited to 'src/main/scala/chisel3')
28 files changed, 1659 insertions, 0 deletions
diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala new file mode 100644 index 00000000..ba2b1389 --- /dev/null +++ b/src/main/scala/chisel3/Driver.scala @@ -0,0 +1,132 @@ +// See LICENSE for license details. + +package chisel + +import scala.sys.process._ +import java.io._ + +import internal._ +import internal.firrtl._ + +trait BackendCompilationUtilities { + /** Create a temporary directory with the prefix name. Exists here because it doesn't in Java 6. + */ + def createTempDirectory(prefix: String): File = { + val temp = File.createTempFile(prefix, "") + if (!temp.delete()) { + throw new IOException(s"Unable to delete temp file '$temp'") + } + if (!temp.mkdir()) { + throw new IOException(s"Unable to create temp directory '$temp'") + } + temp + } + + def makeHarness(template: String => String, post: String)(f: File): File = { + val prefix = f.toString.split("/").last + val vf = new File(f.toString + post) + val w = new FileWriter(vf) + w.write(template(prefix)) + w.close() + vf + } + + def firrtlToVerilog(prefix: String, dir: File): ProcessBuilder = { + Process( + Seq("firrtl", + "-i", s"$prefix.fir", + "-o", s"$prefix.v", + "-X", "verilog"), + dir) + } + + /** Generates a Verilator invocation to convert Verilog sources to C++ + * simulation sources. + * + * The Verilator prefix will be V$dutFile, and running this will generate + * C++ sources and headers as well as a makefile to compile them. + * + * @param dutFile name of the DUT .v without the .v extension + * @param name of the top-level module in the design + * @param dir output directory + * @param vSources list of additional Verilog sources to compile + * @param cppHarness C++ testharness to compile/link against + */ + def verilogToCpp( + dutFile: String, + topModule: String, + dir: File, + vSources: Seq[File], + cppHarness: File + ): ProcessBuilder = { + val command = Seq("verilator", + "--cc", s"$dutFile.v") ++ + vSources.map(file => Seq("-v", file.toString)).flatten ++ + Seq("--assert", + "-Wno-fatal", + "-Wno-WIDTH", + "-Wno-STMTDLY", + "--trace", + "-O2", + "--top-module", topModule, + "+define+TOP_TYPE=V" + dutFile, + s"+define+PRINTF_COND=!$topModule.reset", + "-CFLAGS", + s"""-Wno-undefined-bool-conversion -O2 -DTOP_TYPE=V$dutFile -include V$dutFile.h""", + "-Mdir", dir.toString, + "--exe", cppHarness.toString) + System.out.println(s"${command.mkString(" ")}") // scalastyle:ignore regex + command + } + + def cppToExe(prefix: String, dir: File): ProcessBuilder = + Seq("make", "-C", dir.toString, "-j", "-f", s"V${prefix}.mk", s"V${prefix}") + + def executeExpectingFailure( + prefix: String, + dir: File, + assertionMsg: String = "Assertion failed"): Boolean = { + var triggered = false + val e = Process(s"./V${prefix}", dir) ! + ProcessLogger(line => { + triggered = triggered || line.contains(assertionMsg) + System.out.println(line) // scalastyle:ignore regex + }) + triggered + } + + def executeExpectingSuccess(prefix: String, dir: File): Boolean = { + !executeExpectingFailure(prefix, dir) + } +} + +object Driver extends BackendCompilationUtilities { + + /** Elaborates the Module specified in the gen function into a Circuit + * + * @param gen a function that creates a Module hierarchy + * @return the resulting Chisel IR in the form of a Circuit (TODO: Should be FIRRTL IR) + */ + def elaborate[T <: Module](gen: () => T): Circuit = Builder.build(Module(gen())) + + def emit[T <: Module](gen: () => T): String = Emitter.emit(elaborate(gen)) + + def dumpFirrtl(ir: Circuit, optName: Option[File]): File = { + val f = optName.getOrElse(new File(ir.name + ".fir")) + val w = new FileWriter(f) + w.write(Emitter.emit(ir)) + w.close() + f + } + + private var target_dir: Option[String] = None + def parseArgs(args: Array[String]): Unit = { + for (i <- 0 until args.size) { + if (args(i) == "--targetDir") { + target_dir = Some(args(i + 1)) + } + } + } + + def targetDir(): String = { target_dir getOrElse new File(".").getCanonicalPath } +} diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala new file mode 100644 index 00000000..56088562 --- /dev/null +++ b/src/main/scala/chisel3/compatibility.scala @@ -0,0 +1,150 @@ +// See LICENSE for license details. + +// Allows legacy users to continue using Chisel (capital C) package name while +// moving to the more standard package naming convention chisel (lowercase c). + +package object Chisel { + type Direction = chisel.core.Direction + val INPUT = chisel.core.INPUT + val OUTPUT = chisel.core.OUTPUT + val NO_DIR = chisel.core.NO_DIR + + type Flipped = chisel.core.Flipped + type Data = chisel.core.Data + val Wire = chisel.core.Wire + val Clock = chisel.core.Clock + type Clock = chisel.core.Clock + + type Aggregate = chisel.core.Aggregate + val Vec = chisel.core.Vec + type Vec[T <: Data] = chisel.core.Vec[T] + type VecLike[T <: Data] = chisel.core.VecLike[T] + type Bundle = chisel.core.Bundle + + val assert = chisel.core.assert + + type Element = chisel.core.Element + type Bits = chisel.core.Bits + val Bits = chisel.core.Bits + type Num[T <: Data] = chisel.core.Num[T] + type UInt = chisel.core.UInt + val UInt = chisel.core.UInt + type SInt = chisel.core.SInt + val SInt = chisel.core.SInt + type Bool = chisel.core.Bool + val Bool = chisel.core.Bool + val Mux = chisel.core.Mux + + type BlackBox = chisel.core.BlackBox + + val Mem = chisel.core.Mem + type MemBase[T <: Data] = chisel.core.MemBase[T] + type Mem[T <: Data] = chisel.core.Mem[T] + val SeqMem = chisel.core.SeqMem + type SeqMem[T <: Data] = chisel.core.SeqMem[T] + + val Module = chisel.core.Module + type Module = chisel.core.Module + + val printf = chisel.core.printf + + val Reg = chisel.core.Reg + + val when = chisel.core.when + type WhenContext = chisel.core.WhenContext + + + type BackendCompilationUtilities = chisel.BackendCompilationUtilities + val Driver = chisel.Driver + type FileSystemUtilities = chisel.compatibility.FileSystemUtilities + val ImplicitConversions = chisel.util.ImplicitConversions + val chiselMain = chisel.compatibility.chiselMain + val throwException = chisel.compatibility.throwException + val debug = chisel.compatibility.debug + + object testers { + type BasicTester = chisel.testers.BasicTester + val TesterDriver = chisel.testers.TesterDriver + } + + + val log2Up = chisel.util.log2Up + val log2Ceil = chisel.util.log2Ceil + val log2Down = chisel.util.log2Down + val log2Floor = chisel.util.log2Floor + val isPow2 = chisel.util.isPow2 + + val BitPat = chisel.util.BitPat + type BitPat = chisel.util.BitPat + + type ArbiterIO[T <: Data] = chisel.util.ArbiterIO[T] + type LockingArbiterLike[T <: Data] = chisel.util.LockingArbiterLike[T] + type LockingRRArbiter[T <: Data] = chisel.util.LockingRRArbiter[T] + type LockingArbiter[T <: Data] = chisel.util.LockingArbiter[T] + type RRArbiter[T <: Data] = chisel.util.RRArbiter[T] + type Arbiter[T <: Data] = chisel.util.Arbiter[T] + + val FillInterleaved = chisel.util.FillInterleaved + val PopCount = chisel.util.PopCount + val Fill = chisel.util.Fill + val Reverse = chisel.util.Reverse + + val Cat = chisel.util.Cat + + val Log2 = chisel.util.Log2 + + val unless = chisel.util.unless + type SwitchContext[T <: Bits] = chisel.util.SwitchContext[T] + val is = chisel.util.is + val switch = chisel.util.switch + + type Counter = chisel.util.Counter + val Counter = chisel.util.Counter + + type DecoupledIO[+T <: Data] = chisel.util.DecoupledIO[T] + val Decoupled = chisel.util.Decoupled + type EnqIO[T <: Data] = chisel.util.EnqIO[T] + type DeqIO[T <: Data] = chisel.util.DeqIO[T] + type DecoupledIOC[+T <: Data] = chisel.util.DecoupledIOC[T] + type QueueIO[T <: Data] = chisel.util.QueueIO[T] + type Queue[T <: Data] = chisel.util.Queue[T] + val Queue = chisel.util.Queue + + val Enum = chisel.util.Enum + + val LFSR16 = chisel.util.LFSR16 + + val ListLookup = chisel.util.ListLookup + val Lookup = chisel.util.Lookup + + val Mux1H = chisel.util.Mux1H + val PriorityMux = chisel.util.PriorityMux + val MuxLookup = chisel.util.MuxLookup + val MuxCase = chisel.util.MuxCase + + val OHToUInt = chisel.util.OHToUInt + val PriorityEncoder = chisel.util.PriorityEncoder + val UIntToOH = chisel.util.UIntToOH + val PriorityEncoderOH = chisel.util.PriorityEncoderOH + + val RegNext = chisel.util.RegNext + val RegInit = chisel.util.RegInit + val RegEnable = chisel.util.RegEnable + val ShiftRegister = chisel.util.ShiftRegister + + type ValidIO[+T <: Data] = chisel.util.ValidIO[T] + val Valid = chisel.util.Valid + val Pipe = chisel.util.Pipe + type Pipe[T <: Data] = chisel.util.Pipe[T] + + + import chisel.internal.firrtl.Width + implicit def fromBigIntToLiteral(x: BigInt): chisel.fromBigIntToLiteral = + new chisel.fromBigIntToLiteral(x) + implicit def fromIntToLiteral(x: Int): chisel.fromIntToLiteral= + new chisel.fromIntToLiteral(x) + implicit def fromStringToLiteral(x: String): chisel.fromStringToLiteral= + new chisel.fromStringToLiteral(x) + implicit def fromBooleanToLiteral(x: Boolean): chisel.fromBooleanToLiteral= + new chisel.fromBooleanToLiteral(x) +} diff --git a/src/main/scala/chisel3/compatibility/FileSystemUtilities.scala b/src/main/scala/chisel3/compatibility/FileSystemUtilities.scala new file mode 100644 index 00000000..d12e627d --- /dev/null +++ b/src/main/scala/chisel3/compatibility/FileSystemUtilities.scala @@ -0,0 +1,12 @@ +// See LICENSE for license details. + +package chisel.compatibility + +import chisel._ + +@deprecated("FileSystemUtilities doesn't exist in chisel3", "3.0.0") +trait FileSystemUtilities { + def createOutputFile(name: String): java.io.FileWriter = { + new java.io.FileWriter(Driver.targetDir + "/" + name) + } +} diff --git a/src/main/scala/chisel3/compatibility/Main.scala b/src/main/scala/chisel3/compatibility/Main.scala new file mode 100644 index 00000000..9072bfcf --- /dev/null +++ b/src/main/scala/chisel3/compatibility/Main.scala @@ -0,0 +1,19 @@ +// See LICENSE for license details. + +package chisel.compatibility + +import java.io.File + +import chisel._ + +@deprecated("chiselMain doesn't exist in Chisel3", "3.0") object chiselMain { + def apply[T <: Module](args: Array[String], gen: () => T): Unit = + Predef.assert(false, "No more chiselMain in Chisel3") + + def run[T <: Module] (args: Array[String], gen: () => T): Unit = { + val circuit = Driver.elaborate(gen) + Driver.parseArgs(args) + val output_file = new File(Driver.targetDir + "/" + circuit.name + ".fir") + Driver.dumpFirrtl(circuit, Option(output_file)) + } +} diff --git a/src/main/scala/chisel3/compatibility/debug.scala b/src/main/scala/chisel3/compatibility/debug.scala new file mode 100644 index 00000000..8850c76b --- /dev/null +++ b/src/main/scala/chisel3/compatibility/debug.scala @@ -0,0 +1,8 @@ +package chisel.compatibility + +import chisel.core._ + +@deprecated("debug doesn't do anything in Chisel3 as no pruning happens in the frontend", "chisel3") +object debug { // scalastyle:ignore object.name + def apply (arg: Data): Data = arg +} diff --git a/src/main/scala/chisel3/compatibility/throwException.scala b/src/main/scala/chisel3/compatibility/throwException.scala new file mode 100644 index 00000000..3b9fd06e --- /dev/null +++ b/src/main/scala/chisel3/compatibility/throwException.scala @@ -0,0 +1,14 @@ +// See LICENSE for license details. + +package chisel.compatibility + +import chisel._ + +@deprecated("throwException doesn't exist in Chisel3", "3.0.0") +@throws(classOf[Exception]) +object throwException { + def apply(s: String, t: Throwable = null) = { + val xcpt = new Exception(s, t) + throw xcpt + } +} diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala new file mode 100644 index 00000000..e48eb226 --- /dev/null +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -0,0 +1,112 @@ +// See LICENSE for license details. + +package chisel.internal.firrtl +import chisel._ +import chisel.internal.sourceinfo.{NoSourceInfo, SourceLine} + +private[chisel] object Emitter { + def emit(circuit: Circuit): String = new Emitter(circuit).toString +} + +private class Emitter(circuit: Circuit) { + override def toString: String = res.toString + + private def emitPort(e: Port): String = + s"${e.dir} ${e.id.getRef.name} : ${e.id.toType}" + private def emit(e: Command, ctx: Component): String = { + val firrtlLine = e match { + case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})" + case e: DefWire => s"wire ${e.name} : ${e.id.toType}" + case e: DefReg => s"reg ${e.name} : ${e.id.toType}, ${e.clock.fullName(ctx)}" + case e: DefRegInit => s"reg ${e.name} : ${e.id.toType}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" + case e: DefMemory => s"cmem ${e.name} : ${e.t.toType}[${e.size}]" + case e: DefSeqMemory => s"smem ${e.name} : ${e.t.toType}[${e.size}]" + case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}" + case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}" + case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}" + case e: Stop => s"stop(${e.clk.fullName(ctx)}, UInt<1>(1), ${e.ret})" + case e: Printf => s"""printf(${e.clk.fullName(ctx)}, UInt<1>(1), "${e.format}"${e.ids.map(_.fullName(ctx)).fold(""){_ + ", " + _}})""" + case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid" + case e: DefInstance => { + val modName = moduleMap.get(e.id.name).get + s"inst ${e.name} of $modName" + } + + case w: WhenBegin => + indent() + s"when ${w.pred.fullName(ctx)} :" + case _: WhenEnd => + unindent() + s"skip" + } + e.sourceInfo match { + case SourceLine(filename, line, col) => s"${firrtlLine} @[${filename} ${line}:${col}] " + case _: NoSourceInfo => firrtlLine + } + } + + // Map of Module FIRRTL definition to FIRRTL name, if it has been emitted already. + private val defnMap = collection.mutable.HashMap[String, String]() + // Map of Component name to FIRRTL id. + private val moduleMap = collection.mutable.HashMap[String, String]() + + /** Generates the FIRRTL module definition with a specified name. + */ + private def moduleDefn(m: Component, name: String): String = { + val body = new StringBuilder + m.id match { + case _: BlackBox => body ++= newline + s"extmodule $name : " + case _: Module => body ++= newline + s"module $name : " + } + withIndent { + for (p <- m.ports) + body ++= newline + emitPort(p) + body ++= newline + + m.id match { + case _: BlackBox => + // TODO: BlackBoxes should be empty, but funkiness in Module() means + // it's not for now. Eventually, this should assert out. + case _: Module => for (cmd <- m.commands) { + body ++= newline + emit(cmd, m) + } + } + body ++= newline + } + body.toString() + } + + /** Returns the FIRRTL declaration and body of a module, or nothing if it's a + * duplicate of something already emitted (on the basis of simple string + * matching). + */ + private def emit(m: Component): String = { + // Generate the body. + val moduleName = m.id.getClass.getName.split('.').last + val defn = moduleDefn(m, moduleName) + + defnMap get defn match { + case Some(deduplicatedName) => + moduleMap(m.name) = deduplicatedName + "" + case None => + require(!(moduleMap contains m.name), + "emitting module with same name but different contents") + + moduleMap(m.name) = m.name + defnMap(defn) = m.name + + moduleDefn(m, m.name) + } + } + + private var indentLevel = 0 + private def newline = "\n" + (" " * indentLevel) + private def indent(): Unit = indentLevel += 1 + private def unindent() { require(indentLevel > 0); indentLevel -= 1 } + private def withIndent(f: => Unit) { indent(); f; unindent() } + + private val res = new StringBuilder(s"circuit ${circuit.name} : ") + withIndent { circuit.components.foreach(c => res ++= emit(c)) } + res ++= newline +} diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala new file mode 100644 index 00000000..f7ed6b13 --- /dev/null +++ b/src/main/scala/chisel3/package.scala @@ -0,0 +1,82 @@ +package object chisel { + import scala.language.experimental.macros + + import internal.firrtl.Width + import internal.sourceinfo.{SourceInfo, SourceInfoTransform} + import util.BitPat + + + type Direction = chisel.core.Direction + val INPUT = chisel.core.INPUT + val OUTPUT = chisel.core.OUTPUT + val NO_DIR = chisel.core.NO_DIR + type Flipped = chisel.core.Flipped + type Data = chisel.core.Data + val Wire = chisel.core.Wire + val Clock = chisel.core.Clock + type Clock = chisel.core.Clock + + type Aggregate = chisel.core.Aggregate + val Vec = chisel.core.Vec + type Vec[T <: Data] = chisel.core.Vec[T] + type VecLike[T <: Data] = chisel.core.VecLike[T] + type Bundle = chisel.core.Bundle + + val assert = chisel.core.assert + + type Element = chisel.core.Element + type Bits = chisel.core.Bits + val Bits = chisel.core.Bits + type Num[T <: Data] = chisel.core.Num[T] + type UInt = chisel.core.UInt + val UInt = chisel.core.UInt + type SInt = chisel.core.SInt + val SInt = chisel.core.SInt + type Bool = chisel.core.Bool + val Bool = chisel.core.Bool + val Mux = chisel.core.Mux + + type BlackBox = chisel.core.BlackBox + + val Mem = chisel.core.Mem + type MemBase[T <: Data] = chisel.core.MemBase[T] + type Mem[T <: Data] = chisel.core.Mem[T] + val SeqMem = chisel.core.SeqMem + type SeqMem[T <: Data] = chisel.core.SeqMem[T] + + val Module = chisel.core.Module + type Module = chisel.core.Module + + val printf = chisel.core.printf + + val Reg = chisel.core.Reg + + val when = chisel.core.when + type WhenContext = chisel.core.WhenContext + + + implicit class fromBigIntToLiteral(val x: BigInt) extends AnyVal { + def U: UInt = UInt(x, Width()) + def S: SInt = SInt(x, Width()) + } + implicit class fromIntToLiteral(val x: Int) extends AnyVal { + def U: UInt = UInt(BigInt(x), Width()) + def S: SInt = SInt(BigInt(x), Width()) + } + implicit class fromStringToLiteral(val x: String) extends AnyVal { + def U: UInt = UInt(x) + } + implicit class fromBooleanToLiteral(val x: Boolean) extends AnyVal { + def B: Bool = Bool(x) + } + + implicit class fromUIntToBitPatComparable(val x: UInt) extends AnyVal { + final def === (that: BitPat): Bool = macro SourceInfoTransform.thatArg + final def != (that: BitPat): Bool = macro SourceInfoTransform.thatArg + final def =/= (that: BitPat): Bool = macro SourceInfoTransform.thatArg + + def do_=== (that: BitPat)(implicit sourceInfo: SourceInfo): Bool = that === x + def do_!= (that: BitPat)(implicit sourceInfo: SourceInfo): Bool = that != x + def do_=/= (that: BitPat)(implicit sourceInfo: SourceInfo): Bool = that =/= x + } +} diff --git a/src/main/scala/chisel3/testers/BasicTester.scala b/src/main/scala/chisel3/testers/BasicTester.scala new file mode 100644 index 00000000..36ff7c52 --- /dev/null +++ b/src/main/scala/chisel3/testers/BasicTester.scala @@ -0,0 +1,38 @@ +// See LICENSE for license details. + +package chisel.testers +import chisel._ + +import scala.language.experimental.macros + +import internal._ +import internal.Builder.pushCommand +import internal.firrtl._ +import internal.sourceinfo.SourceInfo + +class BasicTester extends Module { + // The testbench has no IOs, rather it should communicate using printf, assert, and stop. + val io = new Bundle() + + def popCount(n: Long): Int = n.toBinaryString.count(_=='1') + + /** Ends the test reporting success. + * + * Does not fire when in reset (defined as the encapsulating Module's + * reset). If your definition of reset is not the encapsulating Module's + * reset, you will need to gate this externally. + */ + def stop()(implicit sourceInfo: SourceInfo) { + // TODO: rewrite this using library-style SourceInfo passing. + when (!reset) { + pushCommand(Stop(sourceInfo, Node(clock), 0)) + } + } + + /** The finish method provides a hook that subclasses of BasicTester can use to + * alter a circuit after their constructor has been called. + * For example, a specialized tester subclassing BasicTester could override finish in order to + * add flow control logic for a decoupled io port of a device under test + */ + def finish(): Unit = {} +} diff --git a/src/main/scala/chisel3/testers/TesterDriver.scala b/src/main/scala/chisel3/testers/TesterDriver.scala new file mode 100644 index 00000000..5c0275e0 --- /dev/null +++ b/src/main/scala/chisel3/testers/TesterDriver.scala @@ -0,0 +1,69 @@ +// See LICENSE for license details. + +package chisel.testers + +import chisel._ +import scala.io.Source +import scala.sys.process._ +import java.io._ + +object TesterDriver extends BackendCompilationUtilities { + /** Copy the contents of a resource to a destination file. + */ + def copyResourceToFile(name: String, file: File) { + val in = getClass().getResourceAsStream(name) + if (in == null) { + throw new FileNotFoundException(s"Resource '$name'") + } + val out = new FileOutputStream(file) + Iterator.continually(in.read).takeWhile(-1 !=).foreach(out.write) + out.close() + } + + /** For use with modules that should successfully be elaborated by the + * frontend, and which can be turned into executables with assertions. */ + def execute(t: () => BasicTester, additionalVResources: Seq[String] = Seq()): Boolean = { + // Invoke the chisel compiler to get the circuit's IR + val circuit = Driver.elaborate(finishWrapper(t)) + + // Set up a bunch of file handlers based on a random temp filename, + // plus the quirks of Verilator's naming conventions + val target = circuit.name + + val path = createTempDirectory(target) + val fname = new File(path, target) + + // For now, dump the IR out to a file + Driver.dumpFirrtl(circuit, Some(new File(fname.toString + ".fir"))) + + // Copy CPP harness and other Verilog sources from resources into files + val cppHarness = new File(path, "top.cpp") + copyResourceToFile("/top.cpp", cppHarness) + val additionalVFiles = additionalVResources.map((name: String) => { + val mangledResourceName = name.replace("/", "_") + val out = new File(path, mangledResourceName) + copyResourceToFile(name, out) + out + }) + + // Use sys.Process to invoke a bunch of backend stuff, then run the resulting exe + if ((firrtlToVerilog(target, path) #&& + verilogToCpp(target, target, path, additionalVFiles, cppHarness) #&& + cppToExe(target, path)).! == 0) { + executeExpectingSuccess(target, path) + } else { + false + } + } + /** + * Calls the finish method of an BasicTester or a class that extends it. + * The finish method is a hook for code that augments the circuit built in the constructor. + */ + def finishWrapper(test: () => BasicTester): () => BasicTester = { + () => { + val tester = test() + tester.finish() + tester + } + } +} diff --git a/src/main/scala/chisel3/util/Arbiter.scala b/src/main/scala/chisel3/util/Arbiter.scala new file mode 100644 index 00000000..3723f2a9 --- /dev/null +++ b/src/main/scala/chisel3/util/Arbiter.scala @@ -0,0 +1,119 @@ +// See LICENSE for license details. + +/** Arbiters in all shapes and sizes. + */ + +package chisel.util + +import chisel._ + +/** An I/O bundle for the Arbiter */ +class ArbiterIO[T <: Data](gen: T, n: Int) extends Bundle { + val in = Vec(n, Decoupled(gen)).flip + val out = Decoupled(gen) + val chosen = UInt(OUTPUT, log2Up(n)) +} + +/** Arbiter Control determining which producer has access */ +private object ArbiterCtrl +{ + def apply(request: Seq[Bool]): Seq[Bool] = request.length match { + case 0 => Seq() + case 1 => Seq(Bool(true)) + case _ => Bool(true) +: request.tail.init.scanLeft(request.head)(_ || _).map(!_) + } +} + +abstract class LockingArbiterLike[T <: Data](gen: T, n: Int, count: Int, needsLock: Option[T => Bool]) extends Module { + def grant: Seq[Bool] + def choice: UInt + val io = new ArbiterIO(gen, n) + + io.chosen := choice + io.out.valid := io.in(io.chosen).valid + io.out.bits := io.in(io.chosen).bits + + if (count > 1) { + val lockCount = Counter(count) + val lockIdx = Reg(UInt()) + val locked = lockCount.value =/= UInt(0) + val wantsLock = needsLock.map(_(io.out.bits)).getOrElse(Bool(true)) + + when (io.out.fire() && wantsLock) { + lockIdx := io.chosen + lockCount.inc() + } + + when (locked) { io.chosen := lockIdx } + for ((in, (g, i)) <- io.in zip grant.zipWithIndex) + in.ready := Mux(locked, lockIdx === UInt(i), g) && io.out.ready + } else { + for ((in, g) <- io.in zip grant) + in.ready := g && io.out.ready + } +} + +class LockingRRArbiter[T <: Data](gen: T, n: Int, count: Int, needsLock: Option[T => Bool] = None) + extends LockingArbiterLike[T](gen, n, count, needsLock) { + lazy val lastGrant = RegEnable(io.chosen, io.out.fire()) + lazy val grantMask = (0 until n).map(UInt(_) > lastGrant) + lazy val validMask = io.in zip grantMask map { case (in, g) => in.valid && g } + + override def grant: Seq[Bool] = { + val ctrl = ArbiterCtrl((0 until n).map(i => validMask(i)) ++ io.in.map(_.valid)) + (0 until n).map(i => ctrl(i) && grantMask(i) || ctrl(i + n)) + } + + override lazy val choice = Wire(init=UInt(n-1)) + for (i <- n-2 to 0 by -1) + when (io.in(i).valid) { choice := UInt(i) } + for (i <- n-1 to 1 by -1) + when (validMask(i)) { choice := UInt(i) } +} + +class LockingArbiter[T <: Data](gen: T, n: Int, count: Int, needsLock: Option[T => Bool] = None) + extends LockingArbiterLike[T](gen, n, count, needsLock) { + def grant: Seq[Bool] = ArbiterCtrl(io.in.map(_.valid)) + + override lazy val choice = Wire(init=UInt(n-1)) + for (i <- n-2 to 0 by -1) + when (io.in(i).valid) { choice := UInt(i) } +} + +/** Hardware module that is used to sequence n producers into 1 consumer. + Producers are chosen in round robin order. + + Example usage: + val arb = new RRArbiter(2, UInt()) + arb.io.in(0) <> producer0.io.out + arb.io.in(1) <> producer1.io.out + consumer.io.in <> arb.io.out + */ +class RRArbiter[T <: Data](gen:T, n: Int) extends LockingRRArbiter[T](gen, n, 1) + +/** Hardware module that is used to sequence n producers into 1 consumer. + Priority is given to lower producer + + Example usage: + val arb = Module(new Arbiter(2, UInt())) + arb.io.in(0) <> producer0.io.out + arb.io.in(1) <> producer1.io.out + consumer.io.in <> arb.io.out + */ +class Arbiter[T <: Data](gen: T, n: Int) extends Module { + val io = new ArbiterIO(gen, n) + + io.chosen := UInt(n-1) + io.out.bits := io.in(n-1).bits + for (i <- n-2 to 0 by -1) { + when (io.in(i).valid) { + io.chosen := UInt(i) + io.out.bits := io.in(i).bits + } + } + + val grant = ArbiterCtrl(io.in.map(_.valid)) + for ((in, g) <- io.in zip grant) + in.ready := g && io.out.ready + io.out.valid := !grant.last || io.in.last.valid +} diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala new file mode 100644 index 00000000..13bbe1b0 --- /dev/null +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -0,0 +1,89 @@ +// See LICENSE for license details. + +package chisel.util + +import scala.language.experimental.macros + +import chisel._ +import chisel.internal.sourceinfo.{SourceInfo, SourceInfoTransform} + +object BitPat { + /** Parses a bit pattern string into (bits, mask, width). + * + * @return bits the literal value, with don't cares being 0 + * @return mask the mask bits, with don't cares being 0 and cares being 1 + * @return width the number of bits in the literal, including values and + * don't cares. + */ + private def parse(x: String): (BigInt, BigInt, Int) = { + // Notes: + // While Verilog Xs also handle octal and hex cases, there isn't a + // compelling argument and no one has asked for it. + // If ? parsing is to be exposed, the return API needs further scrutiny + // (especially with things like mask polarity). + require(x.head == 'b', "BitPats must be in binary and be prefixed with 'b'") + var bits = BigInt(0) + var mask = BigInt(0) + for (d <- x.tail) { + if (d != '_') { + require("01?".contains(d), "Literal: " + x + " contains illegal character: " + d) + mask = (mask << 1) + (if (d == '?') 0 else 1) + bits = (bits << 1) + (if (d == '1') 1 else 0) + } + } + (bits, mask, x.length - 1) + } + + /** Creates a [[BitPat]] literal from a string. + * + * @param n the literal value as a string, in binary, prefixed with 'b' + * @note legal characters are '0', '1', and '?', as well as '_' as white + * space (which are ignored) + */ + def apply(n: String): BitPat = { + val (bits, mask, width) = parse(n) + new BitPat(bits, mask, width) + } + + /** Creates a [[BitPat]] of all don't cares of the specified bitwidth. */ + def dontCare(width: Int): BitPat = BitPat("b" + ("?" * width)) + + @deprecated("Use BitPat.dontCare", "chisel3") + def DC(width: Int): BitPat = dontCare(width) // scalastyle:ignore method.name + + /** Allows BitPats to be used where a UInt is expected. + * + * @note the BitPat must not have don't care bits (will error out otherwise) + */ + def bitPatToUInt(x: BitPat): UInt = { + require(x.mask == (BigInt(1) << x.getWidth) - 1) + UInt(x.value, x.getWidth) + } + + /** Allows UInts to be used where a BitPat is expected, useful for when an + * interface is defined with BitPats but not all cases need the partial + * matching capability. + * + * @note the UInt must be a literal + */ + def apply(x: UInt): BitPat = { + require(x.isLit) + BitPat("b" + x.litValue.toString(2)) + } +} + +// TODO: Break out of Core? (this doesn't involve FIRRTL generation) +/** Bit patterns are literals with masks, used to represent values with don't + * cares. Equality comparisons will ignore don't care bits (for example, + * BitPat(0b10?1) === UInt(0b1001) and UInt(0b1011)). + */ +sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) { + def getWidth: Int = width + def === (that: UInt): Bool = macro SourceInfoTransform.thatArg + def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg + def != (that: UInt): Bool = macro SourceInfoTransform.thatArg + + def do_=== (that: UInt)(implicit sourceInfo: SourceInfo): Bool = UInt(value) === (that & UInt(mask)) + def do_=/= (that: UInt)(implicit sourceInfo: SourceInfo): Bool = !(this === that) + def do_!= (that: UInt)(implicit sourceInfo: SourceInfo): Bool = this =/= that +} diff --git a/src/main/scala/chisel3/util/Bitwise.scala b/src/main/scala/chisel3/util/Bitwise.scala new file mode 100644 index 00000000..d7d62ea3 --- /dev/null +++ b/src/main/scala/chisel3/util/Bitwise.scala @@ -0,0 +1,74 @@ +// See LICENSE for license details. + +/** Miscellaneous circuit generators operating on bits. + */ + +package chisel.util + +import chisel._ +import chisel.core.SeqUtils + +object FillInterleaved +{ + def apply(n: Int, in: UInt): UInt = apply(n, in.toBools) + def apply(n: Int, in: Seq[Bool]): UInt = Vec(in.map(Fill(n, _))).toBits +} + +/** Returns the number of bits set (i.e value is 1) in the input signal. + */ +object PopCount +{ + def apply(in: Iterable[Bool]): UInt = SeqUtils.count(in.toSeq) + def apply(in: Bits): UInt = apply((0 until in.getWidth).map(in(_))) +} + +/** Fill fans out a UInt to multiple copies */ +object Fill { + /** Fan out x n times */ + def apply(n: Int, x: UInt): UInt = { + n match { + case 0 => UInt(width=0) + case 1 => x + case y if n > 1 => + val p2 = Array.ofDim[UInt](log2Up(n + 1)) + p2(0) = x + for (i <- 1 until p2.length) + p2(i) = Cat(p2(i-1), p2(i-1)) + Cat((0 until log2Up(y + 1)).filter(i => (y & (1 << i)) != 0).map(p2(_))) + case _ => throw new IllegalArgumentException(s"n (=$n) must be nonnegative integer.") + } + } + /** Fan out x n times */ + def apply(n: Int, x: Bool): UInt = + if (n > 1) { + UInt(0,n) - x + } else { + apply(n, x: UInt) + } +} + +/** Litte/big bit endian convertion: reverse the order of the bits in a UInt. +*/ +object Reverse +{ + private def doit(in: UInt, length: Int): UInt = { + if (length == 1) { + in + } else if (isPow2(length) && length >= 8 && length <= 64) { + // This esoterica improves simulation performance + var res = in + var shift = length >> 1 + var mask = UInt((BigInt(1) << length) - 1, length) + do { + mask = mask ^ (mask(length-shift-1,0) << shift) + res = ((res >> shift) & mask) | ((res(length-shift-1,0) << shift) & ~mask) + shift = shift >> 1 + } while (shift > 0) + res + } else { + val half = (1 << log2Up(length))/2 + Cat(doit(in(half-1,0), half), doit(in(length-1,half), length-half)) + } + } + def apply(in: UInt): UInt = doit(in, in.getWidth) +} diff --git a/src/main/scala/chisel3/util/Cat.scala b/src/main/scala/chisel3/util/Cat.scala new file mode 100644 index 00000000..b47da706 --- /dev/null +++ b/src/main/scala/chisel3/util/Cat.scala @@ -0,0 +1,21 @@ +// See LICENSE for license details. + +package chisel.util + +import chisel._ +import chisel.core.SeqUtils + +object Cat { + /** Combine data elements together + * @param a Data to combine with + * @param r any number of other Data elements to be combined in order + * @return A UInt which is all of the bits combined together + */ + def apply[T <: Bits](a: T, r: T*): UInt = apply(a :: r.toList) + + /** Combine data elements together + * @param r any number of other Data elements to be combined in order + * @return A UInt which is all of the bits combined together + */ + def apply[T <: Bits](r: Seq[T]): UInt = SeqUtils.asUInt(r.reverse) +} diff --git a/src/main/scala/chisel3/util/CircuitMath.scala b/src/main/scala/chisel3/util/CircuitMath.scala new file mode 100644 index 00000000..c3b94fdb --- /dev/null +++ b/src/main/scala/chisel3/util/CircuitMath.scala @@ -0,0 +1,28 @@ +// See LICENSE for license details. + +/** Circuit-land math operations. + */ + +package chisel.util + +import chisel._ + +/** Compute Log2 with truncation of a UInt in hardware using a Mux Tree + * An alternative interpretation is it computes the minimum number of bits needed to represent x + * @example + * {{{ data_out := Log2(data_in) }}} + * @note Truncation is used so Log2(UInt(12412)) = 13*/ +object Log2 { + /** Compute the Log2 on the least significant n bits of x */ + def apply(x: Bits, width: Int): UInt = { + if (width < 2) { + UInt(0) + } else if (width == 2) { + x(1) + } else { + Mux(x(width-1), UInt(width-1), apply(x, width-1)) + } + } + + def apply(x: Bits): UInt = apply(x, x.getWidth) +} diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala new file mode 100644 index 00000000..01c12799 --- /dev/null +++ b/src/main/scala/chisel3/util/Conditional.scala @@ -0,0 +1,73 @@ +// See LICENSE for license details. + +/** Conditional blocks. + */ + +package chisel.util + +import scala.language.reflectiveCalls +import scala.language.experimental.macros +import scala.reflect.runtime.universe._ +import scala.reflect.macros.blackbox._ + +import chisel._ + +/** This is identical to [[Chisel.when when]] with the condition inverted */ +object unless { // scalastyle:ignore object.name + def apply(c: Bool)(block: => Unit) { + when (!c) { block } + } +} + +class SwitchContext[T <: Bits](cond: T) { + def is(v: Iterable[T])(block: => Unit) { + if (!v.isEmpty) when (v.map(_.asUInt === cond.asUInt).reduce(_||_)) { block } + } + def is(v: T)(block: => Unit) { is(Seq(v))(block) } + def is(v: T, vr: T*)(block: => Unit) { is(v :: vr.toList)(block) } +} + +/** An object for separate cases in [[Chisel.switch switch]] + * It is equivalent to a [[Chisel.when$ when]] block comparing to the condition + * Use outside of a switch statement is illegal */ +object is { // scalastyle:ignore object.name + // Begin deprecation of non-type-parameterized is statements. + def apply(v: Iterable[Bits])(block: => Unit) { + require(false, "The 'is' keyword may not be used outside of a switch.") + } + + def apply(v: Bits)(block: => Unit) { + require(false, "The 'is' keyword may not be used outside of a switch.") + } + + def apply(v: Bits, vr: Bits*)(block: => Unit) { + require(false, "The 'is' keyword may not be used outside of a switch.") + } +} + +/** Conditional logic to form a switch block + * @example + * {{{ ... // default values here + * switch ( myState ) { + * is( state1 ) { + * ... // some logic here + * } + * is( state2 ) { + * ... // some logic here + * } + * } }}}*/ +object switch { // scalastyle:ignore object.name + def apply[T <: Bits](cond: T)(x: => Unit): Unit = macro impl + def impl(c: Context)(cond: c.Tree)(x: c.Tree): c.Tree = { import c.universe._ + val sc = c.universe.internal.reificationSupport.freshTermName("sc") + def extractIsStatement(tree: Tree): List[c.universe.Tree] = tree match { + // TODO: remove when Chisel compatibility package is removed + case q"Chisel.`package`.is.apply( ..$params )( ..$body )" => List(q"$sc.is( ..$params )( ..$body )") + case q"chisel.util.is.apply( ..$params )( ..$body )" => List(q"$sc.is( ..$params )( ..$body )") + case b => throw new Exception(s"Cannot include blocks that do not begin with is() in switch.") + } + val q"..$body" = x + val ises = body.flatMap(extractIsStatement(_)) + q"""{ val $sc = new SwitchContext($cond); ..$ises }""" + } +} diff --git a/src/main/scala/chisel3/util/Counter.scala b/src/main/scala/chisel3/util/Counter.scala new file mode 100644 index 00000000..1c0b0203 --- /dev/null +++ b/src/main/scala/chisel3/util/Counter.scala @@ -0,0 +1,46 @@ +// See LICENSE for license details. + +package chisel.util + +import chisel._ + +/** A counter module + * @param n number of counts before the counter resets (or one more than the + * maximum output value of the counter), need not be a power of two + */ +class Counter(val n: Int) { + require(n >= 0) + val value = if (n > 1) Reg(init=UInt(0, log2Up(n))) else UInt(0) + /** Increment the counter, returning whether the counter currently is at the + * maximum and will wrap. The incremented value is registered and will be + * visible on the next cycle. + */ + def inc(): Bool = { + if (n > 1) { + val wrap = value === UInt(n-1) + value := value + UInt(1) + if (!isPow2(n)) { + when (wrap) { value := UInt(0) } + } + wrap + } else { + Bool(true) + } + } +} + +/** Counter Object + * Example Usage: + * {{{ val countOn = Bool(true) // increment counter every clock cycle + * val myCounter = Counter(countOn, n) + * when ( myCounter.value === UInt(3) ) { ... } }}}*/ +object Counter +{ + def apply(n: Int): Counter = new Counter(n) + def apply(cond: Bool, n: Int): (UInt, Bool) = { + val c = new Counter(n) + var wrap: Bool = null + when (cond) { wrap = c.inc() } + (c.value, cond && wrap) + } +} diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala new file mode 100644 index 00000000..89b0e39d --- /dev/null +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -0,0 +1,185 @@ +// See LICENSE for license details. + +/** Wrappers for ready-valid (Decoupled) interfaces and associated circuit generators using them. + */ + +package chisel.util + +import chisel._ + +/** An I/O Bundle with simple handshaking using valid and ready signals for data 'bits'*/ +class DecoupledIO[+T <: Data](gen: T) extends Bundle +{ + val ready = Bool(INPUT) + val valid = Bool(OUTPUT) + val bits = gen.cloneType.asOutput + def fire(dummy: Int = 0): Bool = ready && valid + override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type] +} + +/** Adds a ready-valid handshaking protocol to any interface. + * The standard used is that the consumer uses the flipped interface. + */ +object Decoupled { + def apply[T <: Data](gen: T): DecoupledIO[T] = new DecoupledIO(gen) +} + +/** An I/O bundle for enqueuing data with valid/ready handshaking + * Initialization must be handled, if necessary, by the parent circuit + */ +class EnqIO[T <: Data](gen: T) extends DecoupledIO(gen) +{ + /** push dat onto the output bits of this interface to let the consumer know it has happened. + * @param dat the values to assign to bits. + * @return dat. + */ + def enq(dat: T): T = { valid := Bool(true); bits := dat; dat } + + /** Initialize this Bundle. Valid is set to false, and all bits are set to zero. + * NOTE: This method of initialization is still being discussed and could change in the + * future. + */ + def init(): Unit = { + valid := Bool(false) + for (io <- bits.flatten) + io := UInt(0) + } + override def cloneType: this.type = { new EnqIO(gen).asInstanceOf[this.type]; } +} + +/** An I/O bundle for dequeuing data with valid/ready handshaking. + * Initialization must be handled, if necessary, by the parent circuit + */ +class DeqIO[T <: Data](gen: T) extends DecoupledIO(gen) with Flipped +{ + /** Assert ready on this port and return the associated data bits. + * This is typically used when valid has been asserted by the producer side. + * @param b ignored + * @return the data for this device, + */ + def deq(b: Boolean = false): T = { ready := Bool(true); bits } + + /** Initialize this Bundle. + * NOTE: This method of initialization is still being discussed and could change in the + * future. + */ + def init(): Unit = { + ready := Bool(false) + } + override def cloneType: this.type = { new DeqIO(gen).asInstanceOf[this.type]; } +} + +/** An I/O bundle for dequeuing data with valid/ready handshaking */ +class DecoupledIOC[+T <: Data](gen: T) extends Bundle +{ + val ready = Bool(INPUT) + val valid = Bool(OUTPUT) + val bits = gen.cloneType.asOutput +} + +/** An I/O Bundle for Queues + * @param gen The type of data to queue + * @param entries The max number of entries in the queue */ +class QueueIO[T <: Data](gen: T, entries: Int) extends Bundle +{ + /** I/O to enqueue data, is [[Chisel.DecoupledIO]] flipped */ + val enq = Decoupled(gen.cloneType).flip() + /** I/O to enqueue data, is [[Chisel.DecoupledIO]]*/ + val deq = Decoupled(gen.cloneType) + /** The current amount of data in the queue */ + val count = UInt(OUTPUT, log2Up(entries + 1)) +} + +/** A hardware module implementing a Queue + * @param gen The type of data to queue + * @param entries The max number of entries in the queue + * @param pipe True if a single entry queue can run at full throughput (like a pipeline). The ''ready'' signals are + * combinationally coupled. + * @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately). + * The ''valid'' signals are coupled. + * + * Example usage: + * {{{ val q = new Queue(UInt(), 16) + * q.io.enq <> producer.io.out + * consumer.io.in <> q.io.deq }}} + */ +class Queue[T <: Data](gen: T, val entries: Int, + pipe: Boolean = false, + flow: Boolean = false, + override_reset: Option[Bool] = None) +extends Module(override_reset=override_reset) { + def this(gen: T, entries: Int, pipe: Boolean, flow: Boolean, _reset: Bool) = + this(gen, entries, pipe, flow, Some(_reset)) + + val io = new QueueIO(gen, entries) + + val ram = Mem(entries, gen) + val enq_ptr = Counter(entries) + val deq_ptr = Counter(entries) + val maybe_full = Reg(init=Bool(false)) + + val ptr_match = enq_ptr.value === deq_ptr.value + val empty = ptr_match && !maybe_full + val full = ptr_match && maybe_full + val do_enq = Wire(init=io.enq.fire()) + val do_deq = Wire(init=io.deq.fire()) + + when (do_enq) { + ram(enq_ptr.value) := io.enq.bits + enq_ptr.inc() + } + when (do_deq) { + deq_ptr.inc() + } + when (do_enq != do_deq) { + maybe_full := do_enq + } + + io.deq.valid := !empty + io.enq.ready := !full + io.deq.bits := ram(deq_ptr.value) + + if (flow) { + when (io.enq.valid) { io.deq.valid := Bool(true) } + when (empty) { + io.deq.bits := io.enq.bits + do_deq := Bool(false) + when (io.deq.ready) { do_enq := Bool(false) } + } + } + + if (pipe) { + when (io.deq.ready) { io.enq.ready := Bool(true) } + } + + val ptr_diff = enq_ptr.value - deq_ptr.value + if (isPow2(entries)) { + io.count := Cat(maybe_full && ptr_match, ptr_diff) + } else { + io.count := Mux(ptr_match, + Mux(maybe_full, + UInt(entries), UInt(0)), + Mux(deq_ptr.value > enq_ptr.value, + UInt(entries) + ptr_diff, ptr_diff)) + } +} + +/** Generic hardware queue. Required parameter entries controls + the depth of the queues. The width of the queue is determined + from the inputs. + + Example usage: + {{{ val q = Queue(Decoupled(UInt()), 16) + q.io.enq <> producer.io.out + consumer.io.in <> q.io.deq }}} + */ +object Queue +{ + def apply[T <: Data](enq: DecoupledIO[T], entries: Int = 2, pipe: Boolean = false): DecoupledIO[T] = { + val q = Module(new Queue(enq.bits.cloneType, entries, pipe)) + q.io.enq.valid := enq.valid // not using <> so that override is allowed + q.io.enq.bits := enq.bits + enq.ready := q.io.enq.ready + TransitName(q.io.deq, q) + } +} diff --git a/src/main/scala/chisel3/util/Enum.scala b/src/main/scala/chisel3/util/Enum.scala new file mode 100644 index 00000000..8babcd23 --- /dev/null +++ b/src/main/scala/chisel3/util/Enum.scala @@ -0,0 +1,23 @@ +// See LICENSE for license details. + +/** Enum generators, allowing circuit constants to have more meaningful names. + */ + +package chisel.util + +import chisel._ + +object Enum { + /** Returns a sequence of Bits subtypes with values from 0 until n. Helper method. */ + private def createValues[T <: Bits](nodeType: T, n: Int): Seq[T] = + (0 until n).map(x => nodeType.fromInt(x, log2Up(n))) + + /** create n enum values of given type */ + def apply[T <: Bits](nodeType: T, n: Int): List[T] = createValues(nodeType, n).toList + + /** create enum values of given type and names */ + def apply[T <: Bits](nodeType: T, l: Symbol *): Map[Symbol, T] = (l zip createValues(nodeType, l.length)).toMap + + /** create enum values of given type and names */ + def apply[T <: Bits](nodeType: T, l: List[Symbol]): Map[Symbol, T] = (l zip createValues(nodeType, l.length)).toMap +} diff --git a/src/main/scala/chisel3/util/ImplicitConversions.scala b/src/main/scala/chisel3/util/ImplicitConversions.scala new file mode 100644 index 00000000..846c0cbd --- /dev/null +++ b/src/main/scala/chisel3/util/ImplicitConversions.scala @@ -0,0 +1,10 @@ +// See LICENSE for license details. + +package chisel.util + +import chisel._ + +object ImplicitConversions { + implicit def intToUInt(x: Int): UInt = UInt(x) + implicit def booleanToBool(x: Boolean): Bool = Bool(x) +} diff --git a/src/main/scala/chisel3/util/LFSR.scala b/src/main/scala/chisel3/util/LFSR.scala new file mode 100644 index 00000000..f70630bf --- /dev/null +++ b/src/main/scala/chisel3/util/LFSR.scala @@ -0,0 +1,24 @@ +// See LICENSE for license details. + +/** LFSRs in all shapes and sizes. + */ + +package chisel.util + +import chisel._ + +// scalastyle:off magic.number +/** linear feedback shift register + */ +object LFSR16 +{ + def apply(increment: Bool = Bool(true)): UInt = + { + val width = 16 + val lfsr = Reg(init=UInt(1, width)) + when (increment) { lfsr := Cat(lfsr(0)^lfsr(2)^lfsr(3)^lfsr(5), lfsr(width-1,1)) } + lfsr + } +} +// scalastyle:on magic.number + diff --git a/src/main/scala/chisel3/util/Lookup.scala b/src/main/scala/chisel3/util/Lookup.scala new file mode 100644 index 00000000..d32d9aec --- /dev/null +++ b/src/main/scala/chisel3/util/Lookup.scala @@ -0,0 +1,19 @@ +// See LICENSE for license details. + +package chisel.util + +import chisel._ + +object ListLookup { + def apply[T <: Data](addr: UInt, default: List[T], mapping: Array[(BitPat, List[T])]): List[T] = { + val map = mapping.map(m => (m._1 === addr, m._2)) + default.zipWithIndex map { case (d, i) => + map.foldRight(d)((m, n) => Mux(m._1, m._2(i), n)) + } + } +} + +object Lookup { + def apply[T <: Bits](addr: UInt, default: T, mapping: Seq[(BitPat, T)]): T = + ListLookup(addr, List(default), mapping.map(m => (m._1, List(m._2))).toArray).head +} diff --git a/src/main/scala/chisel3/util/Math.scala b/src/main/scala/chisel3/util/Math.scala new file mode 100644 index 00000000..69464d15 --- /dev/null +++ b/src/main/scala/chisel3/util/Math.scala @@ -0,0 +1,44 @@ +// See LICENSE for license details. + +/** Scala-land math helper functions, like logs. + */ + +package chisel.util + +import chisel._ + +/** Compute the log2 rounded up with min value of 1 */ +object log2Up { + def apply(in: BigInt): Int = { + require(in >= 0) + 1 max (in-1).bitLength + } + def apply(in: Int): Int = apply(BigInt(in)) +} + +/** Compute the log2 rounded up */ +object log2Ceil { + def apply(in: BigInt): Int = { + require(in > 0) + (in-1).bitLength + } + def apply(in: Int): Int = apply(BigInt(in)) +} + +/** Compute the log2 rounded down with min value of 1 */ +object log2Down { + def apply(in: BigInt): Int = log2Up(in) - (if (isPow2(in)) 0 else 1) + def apply(in: Int): Int = apply(BigInt(in)) +} + +/** Compute the log2 rounded down */ +object log2Floor { + def apply(in: BigInt): Int = log2Ceil(in) - (if (isPow2(in)) 0 else 1) + def apply(in: Int): Int = apply(BigInt(in)) +} + +/** Check if an Integer is a power of 2 */ +object isPow2 { + def apply(in: BigInt): Boolean = in > 0 && ((in & (in-1)) == 0) + def apply(in: Int): Boolean = apply(BigInt(in)) +} diff --git a/src/main/scala/chisel3/util/Mux.scala b/src/main/scala/chisel3/util/Mux.scala new file mode 100644 index 00000000..6f074a7e --- /dev/null +++ b/src/main/scala/chisel3/util/Mux.scala @@ -0,0 +1,64 @@ +// See LICENSE for license details. + +/** Mux circuit generators. + */ + +package chisel.util + +import chisel._ +import chisel.core.SeqUtils + +/** Builds a Mux tree out of the input signal vector using a one hot encoded + select signal. Returns the output of the Mux tree. + */ +object Mux1H +{ + def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = + apply(sel zip in) + def apply[T <: Data](in: Iterable[(Bool, T)]): T = SeqUtils.oneHotMux(in) + def apply[T <: Data](sel: UInt, in: Seq[T]): T = + apply((0 until in.size).map(sel(_)), in) + def apply(sel: UInt, in: UInt): Bool = (sel & in).orR +} + +/** Builds a Mux tree under the assumption that multiple select signals + can be enabled. Priority is given to the first select signal. + + Returns the output of the Mux tree. + */ +object PriorityMux +{ + def apply[T <: Data](in: Seq[(Bool, T)]): T = SeqUtils.priorityMux(in) + def apply[T <: Data](sel: Seq[Bool], in: Seq[T]): T = apply(sel zip in) + def apply[T <: Data](sel: Bits, in: Seq[T]): T = apply((0 until in.size).map(sel(_)), in) +} + +/** MuxLookup creates a cascade of n Muxs to search for a key value */ +object MuxLookup { + /** @param key a key to search for + * @param default a default value if nothing is found + * @param mapping a sequence to search of keys and values + * @return the value found or the default if not + */ + def apply[S <: UInt, T <: Bits] (key: S, default: T, mapping: Seq[(S, T)]): T = { + var res = default + for ((k, v) <- mapping.reverse) + res = Mux(k === key, v, res) + res + } + +} + +/** MuxCase returns the first value that is enabled in a map of values */ +object MuxCase { + /** @param default the default value if none are enabled + * @param mapping a set of data values with associated enables + * @return the first value in mapping that is enabled */ + def apply[T <: Bits] (default: T, mapping: Seq[(Bool, T)]): T = { + var res = default + for ((t, v) <- mapping.reverse){ + res = Mux(t, v, res) + } + res + } +} diff --git a/src/main/scala/chisel3/util/OneHot.scala b/src/main/scala/chisel3/util/OneHot.scala new file mode 100644 index 00000000..ef21c65d --- /dev/null +++ b/src/main/scala/chisel3/util/OneHot.scala @@ -0,0 +1,64 @@ +// See LICENSE for license details. + +/** Circuit generators for working with one-hot representations. + */ + +package chisel.util + +import chisel._ + +/** Converts from One Hot Encoding to a UInt indicating which bit is active + * This is the inverse of [[Chisel.UIntToOH UIntToOH]]*/ +object OHToUInt { + def apply(in: Seq[Bool]): UInt = apply(Vec(in)) + def apply(in: Vec[Bool]): UInt = apply(in.toBits, in.size) + def apply(in: Bits): UInt = apply(in, in.getWidth) + + def apply(in: Bits, width: Int): UInt = { + if (width <= 2) { + Log2(in, width) + } else { + val mid = 1 << (log2Up(width)-1) + val hi = in(width-1, mid) + val lo = in(mid-1, 0) + Cat(hi.orR, apply(hi | lo, mid)) + } + } +} + +/** @return the bit position of the trailing 1 in the input vector + * with the assumption that multiple bits of the input bit vector can be set + * @example {{{ data_out := PriorityEncoder(data_in) }}} + */ +object PriorityEncoder { + def apply(in: Seq[Bool]): UInt = PriorityMux(in, (0 until in.size).map(UInt(_))) + def apply(in: Bits): UInt = apply(in.toBools) +} + +/** Returns the one hot encoding of the input UInt. + */ +object UIntToOH +{ + def apply(in: UInt, width: Int = -1): UInt = + if (width == -1) { + UInt(1) << in + } else { + (UInt(1) << in(log2Up(width)-1,0))(width-1,0) + } +} + +/** Returns a bit vector in which only the least-significant 1 bit in + the input vector, if any, is set. + */ +object PriorityEncoderOH +{ + private def encode(in: Seq[Bool]): UInt = { + val outs = Seq.tabulate(in.size)(i => UInt(BigInt(1) << i, in.size)) + PriorityMux(in :+ Bool(true), outs :+ UInt(0, in.size)) + } + def apply(in: Seq[Bool]): Seq[Bool] = { + val enc = encode(in) + Seq.tabulate(in.size)(enc(_)) + } + def apply(in: Bits): UInt = encode((0 until in.getWidth).map(i => in(i))) +} diff --git a/src/main/scala/chisel3/util/Reg.scala b/src/main/scala/chisel3/util/Reg.scala new file mode 100644 index 00000000..1b40646d --- /dev/null +++ b/src/main/scala/chisel3/util/Reg.scala @@ -0,0 +1,57 @@ +// See LICENSE for license details. + +/** Variations and helpers for registers. + */ + +package chisel.util + +import chisel._ + +object RegNext { + + def apply[T <: Data](next: T): T = Reg[T](null.asInstanceOf[T], next, null.asInstanceOf[T]) + + def apply[T <: Data](next: T, init: T): T = Reg[T](null.asInstanceOf[T], next, init) + +} + +object RegInit { + + def apply[T <: Data](init: T): T = Reg[T](null.asInstanceOf[T], null.asInstanceOf[T], init) + +} + +/** A register with an Enable signal */ +object RegEnable +{ + def apply[T <: Data](updateData: T, enable: Bool): T = { + val r = Reg(updateData) + when (enable) { r := updateData } + r + } + def apply[T <: Data](updateData: T, resetData: T, enable: Bool): T = { + val r = RegInit(resetData) + when (enable) { r := updateData } + r + } +} + +/** Returns the n-cycle delayed version of the input signal. + */ +object ShiftRegister +{ + /** @param in input to delay + * @param n number of cycles to delay + * @param en enable the shift */ + def apply[T <: Data](in: T, n: Int, en: Bool = Bool(true)): T = + { + // The order of tests reflects the expected use cases. + if (n == 1) { + RegEnable(in, en) + } else if (n != 0) { + RegNext(apply(in, n-1, en)) + } else { + in + } + } +} diff --git a/src/main/scala/chisel3/util/TransitName.scala b/src/main/scala/chisel3/util/TransitName.scala new file mode 100644 index 00000000..04e1995b --- /dev/null +++ b/src/main/scala/chisel3/util/TransitName.scala @@ -0,0 +1,22 @@ +package chisel.util + +import chisel._ +import internal.HasId + +object TransitName { + // The purpose of this is to allow a library to 'move' a name call to a more + // appropriate place. + // For example, a library factory function may create a module and return + // the io. The only user-exposed field is that given IO, which can't use + // any name supplied by the user. This can add a hook so that the supplied + // name then names the Module. + // See Queue companion object for working example + def apply[T<:HasId](from: T, to: HasId): T = { + from.addPostnameHook((given_name: String) => {to.suggestName(given_name)}) + from + } + def withSuffix[T<:HasId](suffix: String)(from: T, to: HasId): T = { + from.addPostnameHook((given_name: String) => {to.suggestName(given_name+suffix)}) + from + } +} diff --git a/src/main/scala/chisel3/util/Valid.scala b/src/main/scala/chisel3/util/Valid.scala new file mode 100644 index 00000000..56ac9abb --- /dev/null +++ b/src/main/scala/chisel3/util/Valid.scala @@ -0,0 +1,61 @@ +// See LICENSE for license details. + +/** Wrappers for valid interfaces and associated circuit generators using them. + */ + +package chisel.util + +import chisel._ + +/** An I/O Bundle containing data and a signal determining if it is valid */ +class ValidIO[+T <: Data](gen2: T) extends Bundle +{ + val valid = Bool(OUTPUT) + val bits = gen2.cloneType.asOutput + def fire(dummy: Int = 0): Bool = valid + override def cloneType: this.type = new ValidIO(gen2).asInstanceOf[this.type] +} + +/** Adds a valid protocol to any interface. The standard used is + that the consumer uses the flipped interface. +*/ +object Valid { + def apply[T <: Data](gen: T): ValidIO[T] = new ValidIO(gen) +} + +/** A hardware module that delays data coming down the pipeline + by the number of cycles set by the latency parameter. Functionality + is similar to ShiftRegister but this exposes a Pipe interface. + + Example usage: + val pipe = new Pipe(UInt()) + pipe.io.enq <> produce.io.out + consumer.io.in <> pipe.io.deq + */ +object Pipe +{ + def apply[T <: Data](enqValid: Bool, enqBits: T, latency: Int): ValidIO[T] = { + if (latency == 0) { + val out = Wire(Valid(enqBits)) + out.valid <> enqValid + out.bits <> enqBits + out + } else { + val v = Reg(Bool(), next=enqValid, init=Bool(false)) + val b = RegEnable(enqBits, enqValid) + apply(v, b, latency-1) + } + } + def apply[T <: Data](enqValid: Bool, enqBits: T): ValidIO[T] = apply(enqValid, enqBits, 1) + def apply[T <: Data](enq: ValidIO[T], latency: Int = 1): ValidIO[T] = apply(enq.valid, enq.bits, latency) +} + +class Pipe[T <: Data](gen: T, latency: Int = 1) extends Module +{ + val io = new Bundle { + val enq = Valid(gen).flip + val deq = Valid(gen) + } + + io.deq <> Pipe(io.enq, latency) +} |
