diff options
| author | Jim Lawson | 2017-01-20 08:52:15 -0800 |
|---|---|---|
| committer | GitHub | 2017-01-20 08:52:15 -0800 |
| commit | 58c1840c7db278417fcceaf035e9df7601233406 (patch) | |
| tree | 29c6c3e1d1ad26f06f72b45cfe4f2ead7f83aee9 /src | |
| parent | efc367e883ffd8c0a239f04943e4bda5ce356da4 (diff) | |
| parent | 51fde13c21825f87ee7fc854eb41215e02076bb5 (diff) | |
Merge branch 'master' into scaladocroot
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 1 | ||||
| -rw-r--r-- | src/main/scala/firrtl/ExecutionOptionsManager.scala | 8 | ||||
| -rw-r--r-- | src/main/scala/firrtl/LoweringCompilers.scala | 6 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/VerilogModulusCleanup.scala | 84 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/CompilerTests.scala | 37 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/DriverSpec.scala | 1 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/MultiThreadingSpec.scala | 1 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/VerilogEmitterTests.scala | 21 |
8 files changed, 156 insertions, 3 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index e40a18a4..a290ced4 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -562,6 +562,7 @@ class VerilogEmitter extends Emitter with PassBased { } def passSeq = Seq( + passes.VerilogModulusCleanup, passes.VerilogWrap, passes.VerilogRename, passes.VerilogPrep) diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala index ab900a36..62ecb1c9 100644 --- a/src/main/scala/firrtl/ExecutionOptionsManager.scala +++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala @@ -161,6 +161,7 @@ case class FirrtlExecutionOptions( compilerName match { case "high" => new HighFirrtlCompiler() case "low" => new LowFirrtlCompiler() + case "middle" => new MiddleFirrtlCompiler() case "verilog" => new VerilogCompiler() } } @@ -170,6 +171,7 @@ case class FirrtlExecutionOptions( case "verilog" => "v" case "low" => "lo.fir" case "high" => "hi.fir" + case "middle" => "mid.fir" case _ => throw new Exception(s"Illegal compiler name $compilerName") } @@ -247,12 +249,12 @@ trait HasFirrtlOptions { parser.opt[String]("compiler") .abbr("X") - .valueName ("<high|low|verilog>") + .valueName ("<high|middle|low|verilog>") .foreach { x => firrtlOptions = firrtlOptions.copy(compilerName = x) } .validate { x => - if (Array("high", "low", "verilog").contains(x.toLowerCase)) parser.success + if (Array("high", "middle", "low", "verilog").contains(x.toLowerCase)) parser.success else parser.failure(s"$x not a legal compiler") }.text { s"compiler to use, default is ${firrtlOptions.compilerName}" @@ -342,7 +344,7 @@ sealed trait FirrtlExecutionResult * Indicates a successful execution of the firrtl compiler, returning the compiled result and * the type of compile * - * @param emitType The name of the compiler used, currently "high", "low", or "verilog" + * @param emitType The name of the compiler used, currently "high", "middle", "low", or "verilog" * @param emitted The text result of the compilation, could be verilog or firrtl text. */ case class FirrtlExecutionSuccess(emitType: String, emitted: String) extends FirrtlExecutionResult diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala index c29ce01a..44d3a757 100644 --- a/src/main/scala/firrtl/LoweringCompilers.scala +++ b/src/main/scala/firrtl/LoweringCompilers.scala @@ -119,6 +119,12 @@ class HighFirrtlCompiler extends Compiler { def transforms: Seq[Transform] = getLoweringTransforms(ChirrtlForm, HighForm) } +/** Emits middle Firrtl input circuit */ +class MiddleFirrtlCompiler extends Compiler { + def emitter = new FirrtlEmitter + def transforms: Seq[Transform] = getLoweringTransforms(ChirrtlForm, MidForm) +} + /** Emits lowered input circuit */ class LowFirrtlCompiler extends Compiler { def emitter = new FirrtlEmitter diff --git a/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala b/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala new file mode 100644 index 00000000..b4df534f --- /dev/null +++ b/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala @@ -0,0 +1,84 @@ +// See LICENSE for license details. + +package firrtl +package passes + +import firrtl.ir._ +import firrtl.Mappers._ +import firrtl.PrimOps.{Bits, Rem} +import firrtl.Utils._ + +import scala.collection.mutable + +/** + * Verilog has the width of (a % b) = Max(W(a), W(b)) + * FIRRTL has the width of (a % b) = Min(W(a), W(b)), which makes more sense, + * but nevertheless is a problem when emitting verilog + * + * This pass finds every instance of (a % b) and: + * 1) adds a temporary node equal to (a % b) with width Max(W(a), W(b)) + * 2) replaces the reference to (a % b) with a bitslice of the temporary node + * to get back down to width Min(W(a), W(b)) + * + * This is technically incorrect firrtl, but allows the verilog emitter + * to emit correct verilog without needing to add temporary nodes + */ +object VerilogModulusCleanup extends Pass { + def name = "Add temporary nodes with verilog widths for modulus" + + private def onModule(m: Module): Module = { + val namespace = Namespace(m) + def onStmt(s: Statement): Statement = { + val v = mutable.ArrayBuffer[Statement]() + + def getWidth(e: Expression): Width = e.tpe match { + case t: GroundType => t.width + case t => UnknownWidth + } + + def maxWidth(ws: Seq[Width]): Width = ws reduceLeft { (x,y) => (x,y) match { + case (IntWidth(x), IntWidth(y)) => IntWidth(x max y) + case (x, y) => UnknownWidth + }} + + def verilogRemWidth(e: DoPrim)(tpe: Type): Type = { + val newWidth = maxWidth(e.args.map(exp => getWidth(exp))) + tpe mapWidth (w => newWidth) + } + + def removeRem(e: Expression): Expression = e match { + case e: DoPrim => e.op match { + case Rem => + val name = namespace.newTemp + val newType = e mapType verilogRemWidth(e) + v += DefNode(get_info(s), name, e mapType verilogRemWidth(e)) + val remRef = WRef(name, newType.tpe, kind(e), gender(e)) + val remWidth = bitWidth(e.tpe) + DoPrim(Bits, Seq(remRef), Seq(remWidth - 1, BigInt(0)), e.tpe) + case _ => e + } + case _ => e + } + + s map removeRem match { + case x: Block => x map onStmt + case EmptyStmt => EmptyStmt + case x => + v += x + v.size match { + case 1 => v.head + case _ => Block(v.toSeq) + } + } + } + Module(m.info, m.name, m.ports, onStmt(m.body)) + } + + def run(c: Circuit): Circuit = { + val modules = c.modules map { + case m: Module => onModule(m) + case m: ExtModule => m + } + Circuit(c.info, modules, c.main) + } +} diff --git a/src/test/scala/firrtlTests/CompilerTests.scala b/src/test/scala/firrtlTests/CompilerTests.scala index 2a5311f8..a9fce0c2 100644 --- a/src/test/scala/firrtlTests/CompilerTests.scala +++ b/src/test/scala/firrtlTests/CompilerTests.scala @@ -14,6 +14,7 @@ import firrtl.{ CircuitState, Compiler, HighFirrtlCompiler, + MiddleFirrtlCompiler, LowFirrtlCompiler, Parser, VerilogCompiler @@ -61,6 +62,42 @@ class HighFirrtlCompilerSpec extends CompilerSpec with Matchers { } /** + * An example test for testing the MiddleFirrtlCompiler. + * + * Given an input Firrtl circuit (expressed as a string), + * the compiler is executed. The output of the compiler is + * a lowered (to MidForm) version of the input circuit. The output is + * string compared to the correct lowered circuit. + */ +class MiddleFirrtlCompilerSpec extends CompilerSpec with Matchers { + val compiler = new MiddleFirrtlCompiler() + val input = + """ +circuit Top : + module Top : + input reset : UInt<1> + input a : UInt<1>[2] + wire b : UInt + b <= a[0] + when reset : + b <= UInt(0) +""" + // Verify that Vecs are retained, but widths are inferred and whens are expanded. + val check = Seq( + "circuit Top :", + " module Top :", + " input reset : UInt<1>", + " input a : UInt<1>[2]", + " wire b : UInt<1>", + " node _GEN_0 = mux(reset, UInt<1>(\"h0\"), a[0])", + " b <= _GEN_0\n\n" + ).reduce(_ + "\n" + _) + "A circuit" should "match exactly to its MidForm state" in { + (parse(getOutput)) should be (parse(check)) + } +} + +/** * An example test for testing the LoweringCompiler. * * Given an input Firrtl circuit (expressed as a string), diff --git a/src/test/scala/firrtlTests/DriverSpec.scala b/src/test/scala/firrtlTests/DriverSpec.scala index 26b32baa..9f29b918 100644 --- a/src/test/scala/firrtlTests/DriverSpec.scala +++ b/src/test/scala/firrtlTests/DriverSpec.scala @@ -156,6 +156,7 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities Seq( "low" -> "./Dummy.lo.fir", "high" -> "./Dummy.hi.fir", + "middle" -> "./Dummy.mid.fir", "verilog" -> "./Dummy.v" ).foreach { case (compilerName, expectedOutputFileName) => val manager = new ExecutionOptionsManager("test") with HasFirrtlOptions { diff --git a/src/test/scala/firrtlTests/MultiThreadingSpec.scala b/src/test/scala/firrtlTests/MultiThreadingSpec.scala index 169aa6b2..1698c462 100644 --- a/src/test/scala/firrtlTests/MultiThreadingSpec.scala +++ b/src/test/scala/firrtlTests/MultiThreadingSpec.scala @@ -21,6 +21,7 @@ class MultiThreadingSpec extends FirrtlPropSpec { // The parameters we're testing with val compilers = Seq( new firrtl.HighFirrtlCompiler, + new firrtl.MiddleFirrtlCompiler, new firrtl.LowFirrtlCompiler, new firrtl.VerilogCompiler) val inputFilePath = s"/integration/GCDTester.fir" // arbitrary diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala index 39592269..41fd6e41 100644 --- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala +++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala @@ -74,4 +74,25 @@ class DoPrimVerilog extends FirrtlFlatSpec { |""".stripMargin.split("\n") map normalized executeTest(input, check, compiler) } + "Rem" should "emit correctly" in { + val compiler = new VerilogCompiler + val input = + """circuit Test : + | module Test : + | input in : UInt<8> + | output out : UInt<1> + | out <= rem(in, UInt<1>("h1")) + |""".stripMargin + val check = + """module Test( + | input [7:0] in, + | output out + |); + | wire [7:0] _GEN_0; + | assign out = _GEN_0[0]; + | assign _GEN_0 = in % 8'h1; + |endmodule + |""".stripMargin.split("\n") map normalized + executeTest(input, check, compiler) + } } |
