From 373d3cfcb5566c448dcad6b679dee43bf66f878a Mon Sep 17 00:00:00 2001 From: jackkoenig Date: Mon, 14 Mar 2016 22:35:06 -0700 Subject: Revamp string literal handling --- src/test/resources/features/Printf.fir | 16 ++++ src/test/resources/top.cpp | 88 ++++++++++++++++++++++ src/test/scala/firrtlTests/FirrtlSpec.scala | 6 +- src/test/scala/firrtlTests/StringSpec.scala | 113 ++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 src/test/resources/features/Printf.fir create mode 100644 src/test/resources/top.cpp create mode 100644 src/test/scala/firrtlTests/StringSpec.scala (limited to 'src/test') diff --git a/src/test/resources/features/Printf.fir b/src/test/resources/features/Printf.fir new file mode 100644 index 00000000..d0c1c775 --- /dev/null +++ b/src/test/resources/features/Printf.fir @@ -0,0 +1,16 @@ +circuit Top : + module Top : + input clk : Clock + input reset : UInt<1> + + reg count : UInt<10>, clk with : + reset => (reset, UInt<6>(0)) + reg const : UInt<32> clk with : + reset => (reset, UInt(123456)) + + node notReset = not(reset) + count <= add(count, UInt(1)) + printf(clk, notReset, "\tcount = %d 0x%x b%b\\\'%d%%\'\n", count, count, count, const) + + when eq(count, UInt(255)) : + stop(clk, UInt(1), 0) diff --git a/src/test/resources/top.cpp b/src/test/resources/top.cpp new file mode 100644 index 00000000..8bfe2a99 --- /dev/null +++ b/src/test/resources/top.cpp @@ -0,0 +1,88 @@ +#include +#include + +#if VM_TRACE +# include // Trace file format header +#endif + +using namespace std; + +//VGCDTester *top; +TOP_TYPE *top; + +vluint64_t main_time = 0; // Current simulation time + // This is a 64-bit integer to reduce wrap over issues and + // allow modulus. You can also use a double, if you wish. + +double sc_time_stamp () { // Called by $time in Verilog + return main_time; // converts to double, to match + // what SystemC does +} + +// TODO Provide command-line options like vcd filename, timeout count, etc. +const long timeout = 100000000L; + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); // Remember args + top = new TOP_TYPE; + +#if VM_TRACE // If verilator was invoked with --trace + Verilated::traceEverOn(true); // Verilator must compute traced signals + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + top->trace (tfp, 99); // Trace 99 levels of hierarchy + tfp->open ("dump.vcd"); // Open the dump file +#endif + + + top->reset = 1; + + cout << "Starting simulation!\n"; + + while (!Verilated::gotFinish() && main_time < timeout) { + if (main_time > 10) { + top->reset = 0; // Deassert reset + } + if ((main_time % 10) == 1) { + top->clk = 1; // Toggle clock + } + if ((main_time % 10) == 6) { + top->clk = 0; + } + top->eval(); // Evaluate model +#if VM_TRACE + if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp +#endif + main_time++; // Time passes... + } + + if (main_time >= timeout) { + cout << "Simulation terminated by timeout at time " << main_time << + " (cycle " << main_time / 10 << ")"<< endl; + return -1; + } else { + cout << "Simulation completed at time " << main_time << + " (cycle " << main_time / 10 << ")"<< endl; + } + + // Run for 10 more clocks + vluint64_t end_time = main_time + 100; + while (main_time < end_time) { + if ((main_time % 10) == 1) { + top->clk = 1; // Toggle clock + } + if ((main_time % 10) == 6) { + top->clk = 0; + } + top->eval(); // Evaluate model +#if VM_TRACE + if (tfp) tfp->dump (main_time); // Create waveform trace for this timestamp +#endif + main_time++; // Time passes... + } + +#if VM_TRACE + if (tfp) tfp->close(); +#endif +} + diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala index 438a5282..a8ccb0a9 100644 --- a/src/test/scala/firrtlTests/FirrtlSpec.scala +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -90,7 +90,7 @@ trait BackendCompilationUtilities { val e = Process(s"./V${prefix}", dir) ! ProcessLogger(line => { triggered = triggered || line.contains(assertionMsg) - System.out.println(line) + //System.out.println(line) }) triggered } @@ -101,7 +101,7 @@ trait BackendCompilationUtilities { } trait FirrtlRunners extends BackendCompilationUtilities { - lazy val cpp = new File(s"/integration/top.cpp") + lazy val cppHarness = new File(s"/top.cpp") def compileFirrtlTest(prefix: String, srcDir: String): File = { val testDir = createTempDirectory(prefix) copyResourceToFile(s"${srcDir}/${prefix}.fir", new File(testDir, s"${prefix}.fir")) @@ -112,7 +112,7 @@ trait FirrtlRunners extends BackendCompilationUtilities { def runFirrtlTest(prefix: String, srcDir: String) { val testDir = compileFirrtlTest(prefix, srcDir) val harness = new File(testDir, s"top.cpp") - copyResourceToFile(cpp.toString, harness) + copyResourceToFile(cppHarness.toString, harness) verilogToCpp(prefix, testDir, Seq(), harness).! cppToExe(prefix, testDir).! diff --git a/src/test/scala/firrtlTests/StringSpec.scala b/src/test/scala/firrtlTests/StringSpec.scala new file mode 100644 index 00000000..2278a147 --- /dev/null +++ b/src/test/scala/firrtlTests/StringSpec.scala @@ -0,0 +1,113 @@ + +package firrtlTests + +import firrtl._ + +import java.io._ + +import scala.sys.process._ +import org.scalatest._ +import org.scalatest.prop._ +import org.scalatest.Assertions._ +import org.scalacheck._ + +class PrintfSpec extends FirrtlPropSpec { + + property("Printf should correctly print values in each format %x, %d, %b") { + val prefix = "Printf" + val testDir = compileFirrtlTest(prefix, "/features") + val harness = new File(testDir, s"top.cpp") + copyResourceToFile(cppHarness.toString, harness) + + verilogToCpp(prefix, testDir, Seq(), harness).! + cppToExe(prefix, testDir).! + + // Check for correct Printf: + // Count up from 0, match decimal, hex, and binary + // see /features/Print.fir to see what we're matching + val regex = """\tcount\s+=\s+(\d+)\s+0x(\w+)\s+b([01]+).*""".r + var done = false + var expected = 0 + var error = false + val ret = Process(s"./V${prefix}", testDir) ! + ProcessLogger( line => { + line match { + case regex(dec, hex, bin) => { + if (!done) { + // Must mark error before assertion or sbt test will pass + if (Integer.parseInt(dec, 10) != expected) error = true + assert(Integer.parseInt(dec, 10) == expected) + if (Integer.parseInt(hex, 16) != expected) error = true + assert(Integer.parseInt(hex, 16) == expected) + if (Integer.parseInt(bin, 2) != expected) error = true + assert(Integer.parseInt(bin, 2) == expected) + expected += 1 + } + } + case _ => // Do Nothing + } + }) + if (error) fail() + } +} + +class StringSpec extends FirrtlPropSpec { + + // Whitelist is [0x20 - 0x7e] + val whitelist = + """ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ""" + + """[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~""" + val whitelistBA: Array[Byte] = Array.range(0x20, 0x7e) map (_.toByte) + + property(s"Character whitelist should be supported: [$whitelist] ") { + val lit = firrtl.FIRRTLStringLitHandler.unescape(whitelist) + // Check internal + lit.array zip whitelistBA foreach { case (b, e) => + assert(b == e, s"(${b.toChar} did not equal expected ${e.toChar})") + } + // Check result + assert(lit.serialize == whitelist) + } + + // Valid escapes = \n, \t, \\, \", \' + val esc = """\\\'\"\t\n""" + val validEsc = Seq('n', 't', '\\', '"', '\'') + property(s"Escape characters [$esc] should parse") { + val lit = firrtl.FIRRTLStringLitHandler.unescape(esc) + assert(lit.array(0) == 0x5c) + assert(lit.array(1) == 0x27) + assert(lit.array(2) == 0x22) + assert(lit.array(3) == 0x09) + assert(lit.array(4) == 0x0a) + assert(lit.array.length == 5) + } + + // Generators for random testing + val validChar = for (c <- Gen.choose(0x20.toChar, 0x7e.toChar) if c != '\\') yield c + val validCharSeq = Gen.containerOf[Seq, Char](validChar) + val invalidChar = Gen.oneOf(Gen.choose(0x00.toChar, 0x1f.toChar), + Gen.choose(0x7f.toChar, 0xff.toChar)) + val invalidEsc = for ( + c <- Gen.choose(0x00.toChar, 0xff.toChar + ) if (!validEsc.contains(c))) yield c + + property("Random invalid strings should fail") { + forAll(validCharSeq, invalidChar, validCharSeq) { + (head: Seq[Char], bad: Char, tail: Seq[Char]) => + val str = ((head :+ bad) ++ tail).mkString + intercept[InvalidStringLitException] { + firrtl.FIRRTLStringLitHandler.unescape(str) + } + } + } + + property(s"Invalid escape characters should fail") { + forAll(validCharSeq, invalidEsc, validCharSeq) { + (head: Seq[Char], badEsc: Char, tail: Seq[Char]) => + val str = (head ++ Seq('\\', badEsc) ++ tail).mkString + intercept[InvalidEscapeCharException] { + firrtl.FIRRTLStringLitHandler.unescape(str) + } + } + } +} -- cgit v1.2.3