Giter Site home page Giter Site logo

ucb-bar / chiseltest Goto Github PK

View Code? Open in Web Editor NEW
201.0 24.0 69.0 1.44 MB

The batteries-included testing and formal verification library for Chisel-based RTL designs.

License: Other

Scala 98.50% C++ 1.41% Verilog 0.10%
testing verification formal chisel

chiseltest's Introduction

chiseltest

Chiseltest is the batteries-included testing and formal verification library for Chisel-based RTL designs. Chiseltest emphasizes tests that are lightweight (minimizes boilerplate code), easy to read and write (understandability), and compose (for better test code reuse).

Installation

To use chisel-testers as a managed dependency, add this in your build.sbt:

libraryDependencies += "edu.berkeley.cs" %% "chiseltest" % "5.0-SNAPSHOT"

Starting with chisel5, please make sure to pick a matching major version, to avoid linking errors. For older versions, if you are also directly depending on the chisel3 library, please make sure that your chisel3 and chiseltest versions match to avoid linking errors.

Writing a Test

ChiselTest integrates with the ScalaTest framework, which provides good IDE and continuous integration support for launching unit tests.

Assuming a typical Chisel project with MyModule defined in src/main/scala/MyModule.scala:

class MyModule extend Module {
    val io = IO(new Bundle {
        val in = Input(UInt(16.W))
        val out = Output(UInt(16.W))
    })

    io.out := RegNext(io.in)
}

Create a new file in src/test/scala/, for example, BasicTest.scala.

In this file:

  1. Add the necessary imports:
    import chisel3._
    import chiseltest._
    import org.scalatest.flatspec.AnyFlatSpec
  2. Create a test class:
    class BasicTest extends AnyFlatSpec with ChiselScalatestTester {
      behavior of "MyModule"
      // test class body here
    }
    • AnyFlatSpec is the default and recommended ScalaTest style for unit testing.
    • ChiselScalatestTester provides testdriver functionality and integration (like signal value assertions) within the context of a ScalaTest environment.
    • For those interested in additional ScalaTest assertion expressibility, Matchers provides additional assertion syntax options. Matchers is optional as it's mainly for Scala-land assertions and does not inter-operate with circuit operations.
  3. In the test class, define a test case:
    it should "do something" in {
      // test case body here
    }
    There can be multiple test cases per test class, and we recommend one test class per Module being tested, and one test case per individual test.
  4. In the test case, define the module being tested:
    test(new MyModule) { c =>
      // test body here
    }
    test automatically runs the default simulator (which is treadle), and runs the test stimulus in the block. The argument to the test stimulus block (c in this case) is a handle to the module under test.
  5. In the test body, use poke, step, and expect operations to write the test:
    c.io.in.poke(0.U)
    c.clock.step()
    c.io.out.expect(0.U)
    c.io.in.poke(42.U)
    c.clock.step()
    c.io.out.expect(42.U)
    println("Last output value :" + c.io.out.peek().litValue)
  6. With your test case complete, you can run all the test cases in your project by invoking ScalaTest. If you're using sbt, you can either run sbt test from the command line, or test from the sbt console. testOnly can also be used to run specific tests.

Usage References

See the test cases for examples:

  • BasicTest shows basic peek, poke, and step functionality
  • QueueTest shows example uses of the DecoupledDriver library, providing functions like enqueueNow, expectDequeueNow, their sequence variants, expectPeek, and expectInvalid. Also, check out the DecoupledDriver implementation, and note that it is not a special case, but code that any user can write.
  • BundleLiteralsSpec shows examples of using bundle literals to poke and expect bundle wires.
    • Note: Bundle literals are still an experimental chisel3 feature and need to be explicitly imported:
      import chisel3.experimental.BundleLiterals._
  • AlutTest shows an example of re-using the same test for different data
  • ShiftRegisterTest shows an example of using fork/join to define a test helper function, where multiple invocations of it are pipelined using fork.

New Constructs

  • fork to spawn threads, and join to block (wait) on a thread. Pokes and peeks/expects to wires from threads are checked during runtime to ensure no collisions or unexpected behavior.
    • forked threads provide a concurrency abstraction for writing testbenches only, without real parallelism. The test infrastructure schedules threads one at a time, with threads running once per simulation cycle.
    • Thread order is deterministic, and attempts to follow lexical order (as it would appear from the code text): forked (child) threads run immediately, then return to the spawning (parent) thread. On future cycles, child threads run before their parent, in the order they were spawned.
    • Only cross-thread operations that round-trip through the simulator (eg, peek-after-poke) are checked. You can do cross-thread operations in Scala (eg, using shared variables) that aren't checked, but it is up to you to make sure they are correct and intuitive. This is not recommended. In the future, we may provide checked mechanisms for communicating between test threads.
  • Regions can be associated with a thread, with fork.withRegion(...), which act as a synchronization barrier within simulator time steps. This can be used to create monitors that run after other main testdriver threads have been run, and can read wires those threads have poked.

Simulator Backends

One of our goals is to keep your tests independent of the underlying simulator as much as possible. Thus, in most cases you should be able to choose from one of our four supported backends and get the exact same test results albeit with differences in execution speed and wave dump quality.

We provide full bindings to two popular open-source simulator:

  • treadle: default, fast startup times, slow execution for larger circuits, supports only VCD
  • verilator: enable with VerilatorBackendAnnotation, slow startup, fast execution, supports VCD and FST

We also provide bindings with some feature limitations to:

  • iverilog: open-source, enable with IcarusBackendAnnotation, supports VCD, FST and LXT
  • vcs: commercial, enable with VcsBackendAnnotation, supports VCD and FSDB

Verilator Versions

We currently support the following versions of the verilator simulator:

Frequently Asked Questions

How do I rerun with --full-stacktrace?

Whereas Chisel accepts command-line arguments, chiseltest exposes the underlying annotation interface. You can pass annotations to a test by using .withAnnotations(...), for example:

// Top of file
import chisel3.stage.PrintFullStackTraceAnnotation

// ...

    // Inside your test spec
    test(new MyModule).withChiselAnnotations(Seq(PrintFullStackTraceAnnotation)) { c =>
      // test body here
    }

This will remove the chisel3 stacktrace suppression (ie. at ... ()). However, if you are using ScalaTest, you may notice a shortened stack trace with ... at the end. You can tell ScalaTest to stop suppressing the stack trace by passing -oF to it. For example (using SBT):

$ sbt
> testOnly <spec name> -- -oF

Any arguments after -- pass to ScalaTest directly instead of being interpreted by SBT.

Stability

Most APIs that can be accessed through import chiseltest._ are going to remain stable. We are also trying to keep the API provided through import chiseltest.formal._ relatively stable. All other packages are considered internal and thus might change at any time.

Migrating from chisel-testers / iotesters

Port to new API

The core abstractions (poke, expect, step) are similar to chisel-testers, but the syntax is inverted: instead of doing tester.poke(wire, value) with a Scala number value, in ChiselTest you would write wire.poke(value) with a Chisel literal value. Furthermore, as no reference to the tester context is needed, test helper functions can be defined outside a test class and written as libraries.

PeekPokeTester compatibility

chiseltest now provides a compatibility layer that makes it possible to re-use old PeekPokeTester based tests with little to no changes to the code. We ported the majority of tests from the chisel-testers repository to our new compatibility layer. While the test itself can mostly remain unchanged, the old Driver is removed and instead tests are launched with the new test syntax.

Hardware testers

Hardware testers are synthesizeable tests, most often extending the BasicTester class provided by chisel3. You can now directly use these tests with chiseltest through the runUntilStop function.

License

Contributions submitted on behalf of the Regents of the University of California, are licensed under the 3-Clause BSD License. Contributions submitted by developers on behalf of themselves or any other organization or employer, are licensed under the Apache License, Version 2.0.

chiseltest's People

Contributors

albert-magyar avatar carlosedp avatar chick avatar ducky64 avatar edwardcwang avatar ekiwi avatar felixonmars avatar gallagator avatar jackkoenig avatar jiegec avatar kammoh avatar konda-x1 avatar liuyic00 avatar ljwljwljwljw avatar lsteveol avatar martoni avatar michael-etzkorn avatar oharboe avatar ryan-lund avatar scala-steward avatar schoeberl avatar sequencer avatar shuyunjia avatar sihaoliu avatar singularitykchen avatar terpstra avatar tsaianson avatar ucbjrl avatar yuchangyuan avatar zhongzc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

chiseltest's Issues

Rename timescope

This was briefly discussed today, with epoch being the proposed replacement. While the dictionary definition matches what we want, there is a possible conflict with the Unix epoch time starting at Jan 1, 1970.

timescope will go through a (probably lengthy) deprecation cycle, since it's pretty long-lived and a fundamental testers2 primitive.

Other possibilities discussed at this and previous meetings:

  • span, timespan
  • period (overloaded with clocks in terms of digital circuits), timePeriod
  • interval (conflicts with interval feature for width analysis)
  • era (implies a very long amount of time)
  • moment (implies a very short amount of time)

Test configurations

This arose out of support for -DwriteVcd=1 in ScalaTest being broken, and whether that breakage was intentional or not. At the chisel-dev meeting, this turned into a longer discussion about configurations, in particular addressing #65.

There seems to be agreement about using annotations as the internal mechanism for representing configurations, including (as currently supported) dumping VCDs and simulator backend.

The overall design requirements are:

  • Should apply for multiple tests (or all tests implicitly?)
  • Parameters are visible prior to test execution (?)
  • Repeatable and deterministic
  • Usable interface
  • Familiar interface (ideally reusing existing infrastructure)

That being said, there was a debate on what the interface should look like.

Proposal 1: universal configurations in a separate file

Under this proposal, there would be a configuration file, probably in the resources folder, formatted as annotations command line syntax, that would be read in to provide default annotations for all tests.

Pros:

  • No test modification needed to change existing tests. If you wanted all tests to behave a certain way without changing each test individually, this would do it.
  • Single underlying mechanism. I guess aside from the per-test .withAnnotations(...).
  • Independent of test infrasructure (eg, ScalaTest), since it's handled at the ChiselTest infrastructure level

Cons:

  • The configuration file isn't obvious, unless you read the docs (which I wouldn't assume). Kind of mutually exclusive with no test changes needed to add the global configuration, since otherwise you would need to add some kind of reference to the additional annotations in each test. But you don't get the reference that tells you that a non-default configuration is being loaded.
  • Additional state separate from your .scala files.
  • No separation of temporary (eg, waveform dumping) and permanent (eg, use Verilator) configurations.
  • Only one global configuration.

I personally think the cons outweigh the pros here, especially since the configuration is somewhat implicit.

Proposal 2: two mechanisms for two purposes

This proposal provides both a temporary mechanism (akin to the debug printf - something that isn't pushed to version control) for changing global configurations, and a permanent mechanism (something that does get pushed to version control) for sharing annotations across multiple test classes while remaining in Scala.

Temporary (per-run) annotations through test infrastructure arguments

Basically, bring back (and possibly expand) the -DwriteVcd=1 option that can be passed to ScalaTest in sbt.

Pros:

  • Extremely lightweight. No mucking around creating files in your resources folder.
  • Temporary configurations are structurally temporary. Your source files don't get changed, there's nothing to revert before you commit, and you can't accidentally commit the equivalent of a debug printf.

Cons:

  • Uncertain how this works for non-sbt/ScalaTest environments - but you can always fall back to .withAnnotations(...).
    • In particular, it's unclear how this works with IntelliJ/ScalaTest. @chick apparently didn't have success configuring a default test configuration for ScalaTest that works across multiple unit tests. But I'd be kind of surprised if IntelliJ actually had no way of setting a project-wide default test configuration.

Permanent annotations in the class hierarchy

Currently, tests are largely written with

class BasicTest extends FlatSpec with ChiselScalatestTester { ...

This proposal would allow subclassing ChiselScalatestTester with annotations, for example:

class MyTestConfig extends ChiselScalatestTester(annos=Seq(writeVcdAnnotataion))
...
class BasicTest extends FlatSpec with MyTestConfig { ...

Potential solution for #65.

Pros:

  • The existence of eg MyTestConfig in the unittest class hierarchy provides a dead giveaway of some non-default configuration being applied, and IDEs can furthermore navigate to that link.
  • Allows multiple configurations per project, with all the features of inheritance.
  • Pure Scala, consistent with existing withAnnotations(...), managed by existing infrastructure like sbt.

Cons:

  • It's a bit harder to add a global config to all tests, since you have to update the inheritance hierarchy of each test. Though it's trivial to update the shared config class afterwards.

Misc

Still up for debate how to run multiple backends. Should this be a responsibility of the build system (and above the ChiselTest level)? Or should there be an annotation that runs a single test multiple times under different backends? Should all tests be written such that they can run multiple times in the same JVM invocation? How would this coexist with proposals to avoid multiple elaboration?

waitFor construct

This would have equivalent arguments to what is supported by expect. Could provide better debugging information on timeouts, and could be more efficient in simulation since threads can be put to sleep.

In the future this API could be expanded with BitPat matches and stuff.

Support peek/poking FIRRTL circuits/circuits with non-Chisel IOs

It would be great if the tester could peek/poke FIRRTL circuits. Also, it would be nice to use it to test IOs which a FIRRTL transform added, which doesn't have a Chisel equivalent.

I'd recommend looking at FIRRTL's CompleteTarget class hierarchy, and hopefully use them to represent signals. We should also probably have normal Chisel references be converted to these Targets.

Thoughts?

Test On All Backends

  • All tests should be run on all backends
  • Backends supported should be (open to debate):
    • Verilator
    • Treadle
    • Icarus Verilog (once supported)

VCS testing also has to be figured out.

Results reporting is not useful

The treadle backend calls the report method which has a field for number of tests (i.e. expects) that succeeded. But because testers2 does not use treadle's expect this number is always zero. This is confusing to new users. Simple fix would be to remove the report call, but I think it would be warmer and fuzzier to give some actual stats.
This would be nice for other backends, so it should probably be handled at the testers2 level and not within the specific backend.

Implement expectHold

This would check some signal holds for the duration of the enclosing timescope. Checks are made between timeslots (right before the clock edge) and handled by infrastructure. Motivating case is to check certain signals are held, specifying the held time with a timescope ("while this is happening, expect these signals to hold at x").

expectDuring (or similar) might also be a useful construct, where it expect some signal value during some period in time. Or perhaps more useful would be a wait-until-with-cap, which does the same but also advances time. Use case could be where a spec calls for some maximum latency without needing to be precise.

Random thoughts:

  • Alternate semantics could have checks be made between thread switches or between regions. But between timeslots (before the clock edge) is probably sufficiently robust since it ignores the effect of combinational testdriver logic.
  • It would be nice to be able to implement this as a library instead of in infrastructure, however there would need to be some way to detect the end of a timescope (not sure what that would look like, maybe getTimescope as a function for library developers that returns the current topmost timescope, which can be queried for open / closed status?). In this case, these checks would happen in the monitor region (or in a new, dedicated region).

Support ScalaTest printing inside TestBuilder

There are a lot of useful printing features inside ScalaTest that are currently not usable inside a TestBuilder environment:

  • Given/When/Then of GivenWhenThen
  • info of Informing
  • feature/scenario of FeatureSpec variants

This limits the ability of a user to write a descriptive test that provides ScalaTest-reported output about what the test is doing. Users can always rely on println (which does work). However, println isn't integrated with the ScalaTest environment in any way and doesn't expose at what point in a test something failed.

As one example, I'd like the following to be supported:

behavior of "Foo"

it should "bar" in {
  def test(new Foo) { c => 
    Given("a Foo initialized via reset for 4 cycles")
    c.doReset(4)

    When("a sequence is enqueued")
    c.in.enqueSeq(Seq(1, 2, 3))

    Then("the same sequence should be dequeued")
    c.out.expectDequeueSeq(Seq(1, 2, 3))
  }
}

Currently, this will just swallow the Given/When/Then and not print them.

See: http://doc.scalatest.org/3.1.0/org/scalatest/GivenWhenThen.html

This is related to some comments I made in #68, but is really a disjoint issue.

Out of metaspace

I'm able to run testOnly pkg.MyModuleSpec in SBT about five times before getting an OutOfMemoryError: Metaspace. Based on removing parts of the (very simple) test, it seems like there's a memory leak per test run irrespective of what happens in the test (this is to say that it doesn't depend on whether there are peeks/pokes/steps).

I can only imagine that I'm doing something wrong...

Chisel version: 3.2-SNAPSHOT
Scala version: 2.12.7
SBT version: 1.0

[error] java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Metaspace
[error] 	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122)
[error] 	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
[error] 	at sbt.ConcurrentRestrictions$$anon$4.take(ConcurrentRestrictions.scala:207)
[error] 	at sbt.Execute.next$1(Execute.scala:104)
[error] 	at sbt.Execute.processAll(Execute.scala:107)
[error] 	at sbt.Execute.runKeep(Execute.scala:84)
[error] 	at sbt.EvaluateTask$.liftedTree1$1(EvaluateTask.scala:387)
[error] 	at sbt.EvaluateTask$.run$1(EvaluateTask.scala:386)
[error] 	at sbt.EvaluateTask$.runTask(EvaluateTask.scala:405)
[error] 	at sbt.internal.Aggregation$.$anonfun$timedRun$4(Aggregation.scala:100)
[error] 	at sbt.EvaluateTask$.withStreams(EvaluateTask.scala:331)
[error] 	at sbt.internal.Aggregation$.timedRun(Aggregation.scala:98)
[error] 	at sbt.internal.Aggregation$.runTasks(Aggregation.scala:111)
[error] 	at sbt.internal.Aggregation$.$anonfun$applyDynamicTasks$4(Aggregation.scala:174)
[error] 	at sbt.Command$.$anonfun$applyEffect$2(Command.scala:130)
[error] 	at sbt.internal.Act$.$anonfun$actParser0$3(Act.scala:387)
[error] 	at sbt.MainLoop$.processCommand(MainLoop.scala:153)
[error] 	at sbt.MainLoop$.$anonfun$next$2(MainLoop.scala:136)
[error] 	at sbt.State$$anon$1.runCmd$1(State.scala:242)
[error] 	at sbt.State$$anon$1.process(State.scala:248)
[error] 	at sbt.MainLoop$.$anonfun$next$1(MainLoop.scala:136)
[error] 	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:16)
[error] 	at sbt.MainLoop$.next(MainLoop.scala:136)
[error] 	at sbt.MainLoop$.run(MainLoop.scala:129)
[error] 	at sbt.MainLoop$.$anonfun$runWithNewLog$1(MainLoop.scala:107)
[error] 	at sbt.io.Using.apply(Using.scala:22)
[error] 	at sbt.MainLoop$.runWithNewLog(MainLoop.scala:101)
[error] 	at sbt.MainLoop$.runAndClearLast(MainLoop.scala:57)
[error] 	at sbt.MainLoop$.runLoggedLoop(MainLoop.scala:42)
[error] 	at sbt.MainLoop$.runLogged(MainLoop.scala:34)
[error] 	at sbt.StandardMain$.runManaged(Main.scala:113)
[error] 	at sbt.xMain.run(Main.scala:76)
[error] 	at xsbt.boot.Launch$$anonfun$run$1.apply(Launch.scala:109)
[error] 	at xsbt.boot.Launch$.withContextLoader(Launch.scala:128)
[error] 	at xsbt.boot.Launch$.run(Launch.scala:109)
[error] 	at xsbt.boot.Launch$$anonfun$apply$1.apply(Launch.scala:35)
[error] 	at xsbt.boot.Launch$.launch(Launch.scala:117)
[error] 	at xsbt.boot.Launch$.apply(Launch.scala:18)
[error] 	at xsbt.boot.Boot$.runImpl(Boot.scala:56)
[error] 	at xsbt.boot.Boot$.main(Boot.scala:18)
[error] 	at xsbt.boot.Boot.main(Boot.scala)

Support for Testing Combinational Ready Valid Controllers

The ready valid controllers I use have combinational paths through them. The decision on whether to perform a transaction depends on the valid and ready signals supplied to the controller in that cycle.
To test such a controller, our test benches check the combinationally generated output of the controller to also decided which transactions are to be performed. So there is a sequence like this:

  1. Test bench pokes valid signals on DUT input channels and ready signals on DUT output channels.
  2. The DUT generates ready signals on DUT input channels and valid signals on DUT output channels.
  3. The testbench looks at these signals generated by the DUT and takes appropriate action.

What is the best way to write testbenches for these controllers?

Here is what I tried to do but fails because there are peeks in threads after there are pokes.
There are some more comments below the code.

package examples.testers2

import org.scalatest._

import chisel3.tester._

import chisel3._
import chisel3.util._

import scala.util.Random


class Join extends Module {
  val io = IO( new Bundle {
    val inp0 = Flipped( DecoupledIO( UInt(16.W)))
    val inp1 = Flipped( DecoupledIO( UInt(16.W)))
    val out = DecoupledIO( UInt(16.W))
  })

  io.inp0.nodeq
  io.inp1.nodeq
  io.out.noenq

  when ( io.inp0.valid && io.inp1.valid && io.out.ready) {
    io.out.bits := io.inp0.bits + io.inp1.bits
    io.inp0.ready := true.B
    io.inp1.ready := true.B
    io.out.valid := true.B
  }

}

class Tie extends Module {
  val io = IO( new Bundle {
    val inp = Flipped( DecoupledIO( UInt(16.W)))
    val out = DecoupledIO( UInt(16.W))
  })
 
  io.inp.nodeq
  io.out.noenq

  when ( io.inp.valid && io.out.ready) {
    io.out.bits := io.inp.bits
    io.inp.ready := true.B
    io.out.valid := true.B
  }

}

import chisel3.internal.firrtl.{LitArg, ULit, SLit}

object AlternativeTestAdapters {
  class RandomFlip( seed : Int, val num : Int, val den : Int) {
    val rnd = new Random( seed)  

    def nextBoolean() : Boolean = {
      val r = rnd.nextInt(den) < num
      println( s"Random flip: ${r}")
      r
    }
  }

  val rflip = new RandomFlip( 47, 12, 16)

  class BaseAdapter( clk: Clock, timeout : Int) {
    var nsteps = 0
    def step() : Unit = {
      if ( timeout > 0 && nsteps >= timeout) throw new Exception( s"Exceeded clock tick limit (${timeout}).")
      clk.step(1)
      nsteps += 1
    }
  }

  class ReadyValidSource[T <: Data](x: ReadyValidIO[T], clk: Clock, tossCoin : () => Boolean, timeout : Int = 0) extends BaseAdapter( clk, timeout) {

    x.valid.poke(false.B)

    def enqueue(data: T): Unit = timescope {
      while ( true) {
        if ( tossCoin()) {
          x.bits.poke(data)
          x.valid.poke(true.B)
          while ( x.ready.peek().litToBoolean == false) {
            step()
          }
          step()
          return
        }
        step()
      }
    }

    def enqueueSeq(data: Seq[T]): Unit = timescope {
      for (elt <- data) {
        enqueue(elt)
      }
    }
  }

  class ReadyValidSink[T <: Data](x: ReadyValidIO[T], clk: Clock, tossCoin : () => Boolean, timeout : Int = 0) extends BaseAdapter( clk, timeout) {

    x.ready.poke(false.B)

    def dequeueExpect(data : T): Unit = {
      while ( true) {
        if ( tossCoin()) {
          x.ready.poke(true.B)

          while ( x.valid.peek().litToBoolean == false) {
            step()
          }
          x.bits.expect(data)
          step()
          return
        }
        step()
      }
    }

    def dequeueExpectSeq(data: Seq[T]): Unit = timescope {
      for (elt <- data) {
        dequeueExpect(elt)
      }
    }

  }
}


import AlternativeTestAdapters._

class TieTestProbablyWrong extends FlatSpec with ChiselScalatestTester {
  val rnd = new Random()

  behavior of "Testers2 with Tie"

  it should "work with a tie" in {
    test( new Tie) { c =>
      val INP = IndexedSeq.fill( 100){ BigInt( 16, rnd)}
      val OUT = INP

      val source = new ReadyValidSource( c.io.inp, c.clock, rflip.nextBoolean)
      val sink = new ReadyValidSink( c.io.out, c.clock, rflip.nextBoolean, 2000)

      fork {
        source.enqueueSeq( INP map (_.U))
      }

      sink.dequeueExpectSeq( OUT map (_.U))
    }
  }
}

class JoinTestProbablyWrong extends FlatSpec with ChiselScalatestTester {
  val rnd = new Random()

  behavior of "Testers2 with Join"

  it should "work with a join" in {
    test( new Join) { c =>
      val INP0 = IndexedSeq.fill( 100){ BigInt( 16, rflip.rnd)}
      val INP1 = IndexedSeq.fill( 100){ BigInt( 16, rflip.rnd)}
      val OUT  = (INP0 zip INP1) map { case (x,y) => (x+y) & ((1<<16)-1)}

      val source0 = new ReadyValidSource( c.io.inp0, c.clock, rflip.nextBoolean)
      val source1 = new ReadyValidSource( c.io.inp1, c.clock, rflip.nextBoolean)

      val sink = new ReadyValidSink( c.io.out, c.clock, rflip.nextBoolean, 2000)

      fork {
        source0.enqueueSeq( INP0 map (_.U))
      }

      fork {
        source1.enqueueSeq( INP1 map (_.U))
      }

      sink.dequeueExpectSeq( OUT map (_.U))
    }
  }
}

I do like the way you can use implicit program counters in the threads so you don't need to write explicit state machines for random injection and extraction as well as waiting for the transaction to be performed.

So how do we make this work. Can we add a phase_step or some other action? Could it look like this:

object AlternativeTestAdapters {
...

  class ReadyValidSource[T <: Data](x: ReadyValidIO[T], clk: Clock, tossCoin : () => Boolean, timeout : Int = 0) extends BaseAdapter( clk, timeout) {

    x.valid.poke(false.B)

    def enqueue(data: T): Unit = timescope {
      while ( true) {
        if ( tossCoin()) {
          x.bits.poke(data)
          x.valid.poke(true.B)
          phase_step()
          while ( x.ready.peek().litToBoolean == false) {
            step()
          }
          step()
          return
        }
        step()
      }
    }

    def enqueueSeq(data: Seq[T]): Unit = timescope {
      for (elt <- data) {
        enqueue(elt)
      }
    }
  }

  class ReadyValidSink[T <: Data](x: ReadyValidIO[T], clk: Clock, tossCoin : () => Boolean, timeout : Int = 0) extends BaseAdapter( clk, timeout) {

    x.ready.poke(false.B)

    def dequeueExpect(data : T): Unit = {
      while ( true) {
        if ( tossCoin()) {
          x.ready.poke(true.B)
          phase_step()
          while ( x.valid.peek().litToBoolean == false) {
            step()
          }
          x.bits.expect(data)
          step()
          return
        }
        step()
      }
    }

    def dequeueExpectSeq(data: Seq[T]): Unit = timescope {
      for (elt <- data) {
        dequeueExpect(elt)
      }
    }

  }
}

Every tick is made up of two phases. "phase_step()" will pass the first phase. "step()" will pass the second phase (or both phases if the first hasn't been passed yet.)

implicit conver to DecoupledIO does not work

The TestAdapters suggest to use implicit conversion to DecoupledDriver
https://github.com/ucb-bar/chisel-testers2/blob/ea5e81f2e54f2df4d5cd34d8e7ece2a69fab0f86/src/main/scala/chisel3/tester/TestAdapters.scala#L9

It woks fine for DecoupledIO. However, it does not work for IrrevocableIO. I have to force the type conversion to DecoupledIO:

it should "run implicit convert" in {
    test(new Module{
      val io = IO( new Bundle{
        val in = Flipped(Irrevocable(UInt(8.W)))
        val out = Irrevocable(UInt(8.W))
      })
    }).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      Decoupled(c.io.in).initSource.setSourceClock(c.clock)
    }
  }

Access to a clock counter

For simulation, it would be convenient to have access to a clock cycle counter. Adding a counter into the design and then routing it to an output is not the best workaround.

Add dummy getSourceClock, getSinkClock in master

Implicit clock resolution works on a branch of firrtl and testers2 but code that depends on those features will fail to compile on master. Add a stub method so that tests will compile, though tests depending on that feature may exception out.

Statistics on simulation frequency dumped only for Treadle

There should be the same statistics printed for all simulators.

  • Startup time(here Treadle is expected to score much higher)
  • Simulation frequency
  • Total time
test ThingAMaBob Success: 0 tests passed in 1008 cycles in 34.515736 seconds 29.20 Hz

Fork on Region

Resolution from chisel-dev 2/8: get rid of the blocking Region concept, replace it with a forking version to avoid the weirdness.
Also, maybe fork creates a ThreadBuilder object so we could have fork.withName("lol").onRegion(Monitor) { ... }

Test Directory collisions may confuse users

A nice feature of Testers2 is that it bases the name of the work directory for a test from the scalaTest test name. This can lead to potential collisions if two different tests share the same tests name.
Proposed fix, incorporate class name of test into the test directory
Example

class X extends FreeSpec with ChiselScalatestTester {
  "basic compile should work" in {}
}
class Y extends FreeSpec with ChiselScalatestTester {
  "basic compile should work" in {}
}

The working directory for both these tests will be the same, i.e. "test_run_dir/basic_compile_should_work".
It might not matter but it can create weird errors if the the two tests are similar and have different backends.

[rfc] Waveform Viewing

This is a feature request.

It would be nice if testers2 was integrated with a waveform viewer in some way. My current workflow is something that I think could be automated and integrated with ChiselTest.

Overview

Current Workflow

  1. Run a test and generate an annotation file
test(new Foo).withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
  dut.io.bar.poke(1.U)
  dut.clock.step(1)
}
  1. Postprocess the VCD file to generate a GTKWave save file that (1) adds non-temporary signals and (2) groups signals by module. I currently do this by running GTKWave with a TCL script, but this could just as easily be done using Scala and likely more intelligently than processing the VCD header.

  2. Open GTKWave loading the save file to get something like the following:

(Signal names are from something I'm working on and don't match the code example above.)

2019-12-04-133343_901x538_scrot

  1. I then run the test, make modifications, and just reload the waveform inside my GTKWave instance (so long as no signals were added/removed or signal names changed).

Proposed Automated Workflow/API

I think this is entirely automatable behind some viewWaveform method that could be added to ChiselTest. Modifying the example above:

test(new Foo).withAnnotations(Seq(WriteVcdAnnotation)) { dut =>
  dut.io.bar.poke(1.U)
  dut.clock.step(1)

  dut.viewWaveform()
}

At minimum, this could blindly run through steps 1--3 above.

At best it could reload the waveform associated with that specific test or create one if it doesn't yet exist.

Implementation

For the minimum implementation, this is just encapsulating the current workflow above into a method. This shouldn't be hard to do and could, at further minimum, use the scripts I already have.

For the best approach, ChiselTest would need to maintain some global state associated with each test and some Option[Pipe] that connects to the TCL console of that test's waveform viewer. GTKWave supports a TCL console (that you can startup with -w/--wish) which can be used for things like reloading, etc. It would also be tractable to instead of using a script to setup the viewer, the TCL console could be used entirely.

Appendix E of the GTKWave manual documents the available TCL commands.

Discussion

The major benefit of something like this is that it doesn't involve rolling a custom waveform viewer like has been talked about in the past. GTKWave is totally open source (GPLv2), so there's no barrier to entry for using it with ChiselTest.

The one difficulty I've had is finding documentation of the save file format (outside of the source code). Granted, this isn't too complicated and the existing effort to script this out has discovered most of the interesting options that are needed.

0.1-SNAPSHOT not in sync with chisel3 3.2-SNAPSHOT

I think the SNAPSHOT of chisel-testers2 is not in sync with the other SNAPSHOTS

I cloned the master branch of https://github.com/freechipsproject/chisel-template/

I am running into the error below, and inspecting release dates of the SNAPSHOTS, it looks like chisel-testers2 was released 2 days before the latest chisel3 on https://oss.sonatype.org/content/repositories/snapshots/edu/berkeley/cs/.

It looks like MultiIOModule moved from experimental in this PR: chipsalliance/chisel#1162

Is the best course of action to just download all the repos and build/publish them locally?

[info] Updating ...
[warn] Choosing sonatype-snapshots for edu.berkeley.cs#chisel3_2.12;3.2-SNAPSHOT
[warn] Choosing sonatype-snapshots for edu.berkeley.cs#firrtl_2.12;1.2-SNAPSHOT
[warn] Choosing sonatype-snapshots for edu.berkeley.cs#chisel-iotesters_2.12;1.3-SNAPSHOT
[warn] Choosing sonatype-snapshots for edu.berkeley.cs#firrtl-interpreter_2.12;1.2-SNAPSHOT
[warn] Choosing sonatype-snapshots for edu.berkeley.cs#treadle_2.12;1.1-SNAPSHOT
[warn] Choosing sonatype-snapshots for edu.berkeley.cs#chisel-testers2_2.12;0.1-SNAPSHOT
[info] Done updating.
[info] Compiling 2 Scala sources to /k/tmp/chisel-test3/target/scala-2.12/classes ...
[warn] there were 5 feature warnings; re-run with -feature for details
[warn] one warning found
[info] Done compiling.
[info] Packaging /k/tmp/chisel-test3/target/scala-2.12/mytest_2.12-0.0.1.jar ...
[info] Done packaging.
[info] Compiling 3 Scala sources to /k/tmp/chisel-test3/target/scala-2.12/test-classes ...
[error] /k/tmp/chisel-test3/src/test/scala/gcd/GcdTesters2.scala:25:5: Symbol 'type chisel3.experimental.MultiIOModule' is missing from the classpath.
[error] This symbol is required by 'type chisel3.tester.ChiselScalatestTester.T'.
[error] Make sure that type MultiIOModule is in your classpath and check for conflicting dependencies with `-Ylog-classpath`.
[error] A full rebuild may help if 'ChiselScalatestTester.class' was compiled against an incompatible version of chisel3.experimental.
[error]     test(new DecoupledGcd(16)) { dut =>
[error]     ^
[error] /k/tmp/chisel-test3/src/test/scala/gcd/GcdTesters2.scala:25:10: type mismatch;
[error]  found   : gcd.DecoupledGcd
[error]  required: T
[error]     test(new DecoupledGcd(16)) { dut =>
[error]          ^
[error] two errors found
[error] (Test / compileIncremental) Compilation failed
[error] Total time: 18 s, completed Sep 30, 2019, 4:40:07 AM

Poking vecs of Bundles doesn't work

import chisel3._
import chisel3.tester._
import org.scalatest.FreeSpec


class VecIO extends Bundle {
  val x = UInt(5.W)
}

class UsesVec extends MultiIOModule {
  val in   = IO(Input(Vec(4, new VecIO)))
  val addr = IO(Input(UInt(8.W)))
  val out  = IO(Output(UInt(5.W)))

  out := in(addr)
}

class UsesVecSpec extends FreeSpec with ChiselScalatestTester {
  "run" in {
    test(new UsesVec) { c =>
      c.in(0).x.poke(5.U)
      c.in(1).x.poke(5.U)
      c.in(2).x.poke(4.U)
      c.addr.poke(2.U)
      c.clock.step()
      c.out.expect(4.U)
    }
  }
}

Generates error

Connection between sink (UInt<5>(IO in unelaborated UsesVec)) and source (VecIO(IO in unelaborated UsesVec)) failed @: Sink (UInt<5>(IO in unelaborated UsesVec)) and Source (VecIO(IO in unelaborated UsesVec)) have different types.
chisel3.internal.ChiselException: Connection between sink (UInt<5>(IO in unelaborated UsesVec)) and source (VecIO(IO in unelaborated UsesVec)) failed @: Sink (UInt<5>(IO in unelaborated UsesVec)) and Source (VecIO(IO in unelaborated UsesVec)) have different types.

how to install it

I git it but when run sbt "testOnly chisel3.tests.BasicTest -- -DwriteVcd=1" there are a lot of errors like
object stage is not a member of package chisel3
I think maybe i should install the latest chisel3 ,but in sbt is already "chisel3" -> "3.2-SNAPSHOT", so what's the problem

The line number of the timed out step() does not appear in exception

Java/Scala seems to cut the exception off before there's a line number of the step() printed, so I don't know which step() that timed out

[info] - should write to main memory *** FAILED *** (1 minute, 43 seconds)
[info]   chisel3.tester.TimeoutException: timeout on Clock(IO clock in ThingAMaBob) at 1000 idle cycles
[info]   at chisel3.tester.backends.treadle.TreadleBackend.$anonfun$run$5(TreadleBackend.scala:175)
[info]   at chisel3.tester.backends.treadle.TreadleBackend.$anonfun$run$5$adapted(TreadleBackend.scala:172)
[info]   at scala.collection.mutable.HashMap.$anonfun$foreach$1(HashMap.scala:149)
[info]   at scala.collection.mutable.HashTable.foreachEntry(HashTable.scala:237)
[info]   at scala.collection.mutable.HashTable.foreachEntry$(HashTable.scala:230)
[info]   at scala.collection.mutable.HashMap.foreachEntry(HashMap.scala:44)
[info]   at scala.collection.mutable.HashMap.foreach(HashMap.scala:149)
[info]   at chisel3.tester.backends.treadle.TreadleBackend.run(TreadleBackend.scala:172)
[info]   at chisel3.tester.internal.Context$.$anonfun$run$1(Testers2.scala:85)
[info]   at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
[info]   ...

chisel-testers2 elaborates twice when Verilator is enabled

Elboration can be quite time consuming, so this slows down Verilator as a backend significantly.

Here's a pull request with a test that reproduces the problem and fails if it happens: #98

Discussed here:

https://groups.google.com/forum/#!topic/chisel-users/31NHUTKm8gw

FIRRTL compile and Verilator only runs once though.

This test passes for Treadle, but fails for Verilator.

$ sbt "test:testOnly *SingleElaboration*"
[deleted]
[info] SingleElaborationTests:
[info] elaboration should happen once
[info] [0.004] Elaborating design...
[info] [0.124] Done elaborating.
[info] [0.000] Elaborating design...
[info] [0.009] Done elaborating.
HACK! new phases.WriteOutputAnnotations to save memory while waiting for Chisel 3.2.0 memory consumption fix
Total FIRRTL Compile Time: 631.7 ms
[deleted]

Peeking zero-width I/Os

While I know that zero-width I/Os don't really exist, it might be nice as syntactic sugar to be able to peek (or even dummy poke?) zero-width wires and have it return 0. This is especially useful in generators where parameters might be set in a way such that some wires end up with zero-width.

This might also be a treadle issue... @chick ?

Not super high priority since it can be worked around with some if statements.

Currently it fails with a slightly cryptic message:

java.lang.AssertionError: assertion failed: Error: getValue("zero_width_io_name") : argument is not an element of this circuit
  at scala.Predef$.assert(Predef.scala:219)
  at treadle.executable.ExecutionEngine.getValue(ExecutionEngine.scala:165)
  at treadle.TreadleTester.peek(TreadleTester.scala:245)
  at chisel3.tester.TreadleBackend.peekBits(TreadleBackend.scala:90)
  at chisel3.tester.TreadleBackend.expectBits(TreadleBackend.scala:99)
  at chisel3.tester.package$testableData.expectWithStale(package.scala:72)
  at chisel3.tester.package$testableData.expect(package.scala:91)

Testing single compilation units

One long-standing issue with the workflow with chisel-testers was that we always re-ran the chisel generator. Ideally, you'd be able to re-run all your unit tests on your top level design. We had some workarounds for this (e.g. saving the peeks/expects and generating a verilog testbench that checked the original behavior was precisely reproduced), but they weren't great.

I think the fundamental issue is that we want to be able to introspect on the module. Is there a good way to retain scala objects used to produce a circuit so it can be reused after a compilation run?

Improving threading performance

ZIO? Akka?

I've heard that Scala prefers Monads / etc for the problems that coroutines solve. Need to do more research into options to speed up the concurrency.

TimeoutException should dump better debug information

Is there a way to catch TimeoutException in user code? It seems like the exception gets thrown and there is no way to catch it (user code is not even part of the stacktrace) and process it in user code (e.g. to reformat the message, provide extra context, etc).

- should connect ports and work *** FAILED ***
  chisel3.tester.TimeoutException: timeout on Clock(IO clock in MyModule) at 1000 idle cycles
  at chisel3.tester.TreadleBackend.$anonfun$run$5(TreadleBackend.scala:188)
  at chisel3.tester.TreadleBackend.$anonfun$run$5$adapted(TreadleBackend.scala:185)
  at scala.collection.mutable.HashMap.$anonfun$foreach$1(HashMap.scala:145)
  at scala.collection.mutable.HashTable.foreachEntry(HashTable.scala:235)
  at scala.collection.mutable.HashTable.foreachEntry$(HashTable.scala:228)
  at scala.collection.mutable.HashMap.foreachEntry(HashMap.scala:40)
  at scala.collection.mutable.HashMap.foreach(HashMap.scala:145)
  at chisel3.tester.TreadleBackend.run(TreadleBackend.scala:185)
  at chisel3.tester.internal.Context$.$anonfun$run$1(Testers2.scala:25)
  at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)

Using Chisel types in peek

This is more a question, not an issue.

When returning a Chisel type on peek(), they are not directly usable in Scala testing code, right? Example:

    val x = port.rdData.peek
    println("compare Chisel type: " + (x == 1.U))
    println("compare with Scala conversion: " + (x.litValue() == 1))

Only the second compare works. Is this the only way (except expect()) to use the peek values?

Cheers,
Martin

Named timescopes

So that failures can give a better trace of where something went wrong. eg timescope.withName(s"bit $i") { ... }

Threading core refactor

Have checks happen on the poke, instead of in bulk at the end of the timestep.

And other cleanup.

Implicit initializers

Not even sure how this would be done, or even what the syntax would look like, but it might make sense to initialize things like boundary ReadyValid IOs implicitly.

chisel3 AOP changes break backends using chisel3.stage.ChiselGeneratorAnnotation(dutGen)

Recent chisel3 changes (chipsalliance/chisel#1077) cause compilation errors for testers2 backends that use chisel3.stage.ChiselGeneratorAnnotation():

[info] Compiling 29 Scala sources to .../testers2/target/scala-2.11/classes ...
[error] .../testers2/src/main/scala/chisel3/tester/backends/treadle/TreadleExecutive.scala:30:49: value circuit is not a member of firrtl.AnnotationSeq
[error]     val circuit = generatorAnnotation.elaborate.circuit
[error]                                                 ^

and

[error] .../testers2/src/main/scala/chisel3/tester/legacy/backends/verilator/VerilatorExecutive.scala:46:49: value circuit is not a member of firrtl.AnnotationSeq
[error]     val circuit = generatorAnnotation.elaborate.circuit
[error]                                                 ^

Support for post-syn testing

Apparently a requirement for practically building chips.

Ideas:

  • Support for using blackbox as the module passed into a test() function, so that we don't need to re-elaborate to Chisel circuit.
  • Separation of circuit interface (potentially the blackbox solution as above, or a full Chisel elaborated module that we discard the circuit for) and implementation (which you can plug in a separate Verilog module or something)
  • Potentially a blackbox where you can specify a FIRRTL file, or some other file that contains data like combinational paths for cross-thread-ops checking or implicit clock resolution.

@seldridge, @jackkoenig ?

Expose TesterOptions / getTestOptions to programmatically set options

Sometimes it is very useful to be able to set options (notably VCD dumping) programmatically as opposed to from the command line. Right now there isn't an easy way to programmatically set it since TesterOptions is private. It can be in an experimental or some other not-fully-supported API.

Allow peeking internal signals

Resolution from chisel-dev 2/8: should be able to peek internal signals, optionally needing an annotation to avoid DCE

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.