New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Guidance on Building a Standalone RoCC for Unit Testing #450

Open
seldridge opened this Issue Nov 24, 2016 · 5 comments

Comments

Projects
None yet
3 participants
@seldridge
Member

seldridge commented Nov 24, 2016

I'd like to be able to build a standalone version of a RoCC and run unit tests on it. This is generally a problem of building anything that mixes in HasCoreParameters as this pulls in all sorts of parameters that are only really populated for a full version of Rocket Chip.

I had this working before with a simple top module that instantiated the chosen RoCC, attached a TileLinkTestRam to the autl line, defined a few missing parameters (NCoreplexExtClients), preloaded the RAM using DPI, and drove this with a top-level C++ testbench. (I wasn't using the other ports of the RoCC and tied them off to some "assert if accessed" modules). However, some very recent modifications now require TLCacheEdge to be defined and I'm struggling to get something to build while attempting to grok a lot of things I haven't seen before (LazyModule, diplomacy?).

Is there any guidance on the best approach here?

I'm currently trying to think about this from a "RoccPlex" approach and attempt to replicate the smallest subset of RocketTile / RocketPlex and see how far I can get.

There are a couple of alternatives approaches which I think are unsound:

  • Forgo unit testing and always rely on a Rocket + RoCC system. This may be fine, but only if you're running bare metal. Relying on the proxy kernel puts a user in an ~2 minute loop waiting for tests to run.
  • Define some wrapper that extends CoreModule and allow the actual RoCC to not extend CoreModule but use other parameters which are overridden later.

Alternative phrasing would be: "Does Hwacha have unit tests and how do they work?"

@colinschmidt

This comment has been minimized.

Show comment
Hide comment
@colinschmidt

colinschmidt Nov 24, 2016

Member

Hwacha does not have unit tests. The development loop uses bare-metal assembly tests for testing. In the past for classes where students develop RoCC accelerators we have used the advance tester to send RoCC commands and then expect results on the other interfaces. I haven't used that method in a long while and never on something as complicated as Hwacha.
Hwacha also hasn't been updated to the Laziness of the Core/Tile yet only the coreplex.
You could also consider looking at how groundtest does things as I know a lot of things outside the core can be tested that way so perhaps things inside the core could also be tested that way.

Member

colinschmidt commented Nov 24, 2016

Hwacha does not have unit tests. The development loop uses bare-metal assembly tests for testing. In the past for classes where students develop RoCC accelerators we have used the advance tester to send RoCC commands and then expect results on the other interfaces. I haven't used that method in a long while and never on something as complicated as Hwacha.
Hwacha also hasn't been updated to the Laziness of the Core/Tile yet only the coreplex.
You could also consider looking at how groundtest does things as I know a lot of things outside the core can be tested that way so perhaps things inside the core could also be tested that way.

@hcook

This comment has been minimized.

Show comment
Hide comment
@hcook

hcook Nov 29, 2016

Contributor

TileLink2 (and therefore diplomacy) has been extended deeply into the RocketTile, where it is now being used to replace use of GlobalAddressMap in the TLB and for L1D$ coherence. The intention was to avoid breaking backwards compatibility with RoCC for now by providing a TL1 Legacy converter in the Tile. However, it looks like the wide propagation of HasCoreParameters ended up leaking some assumptions about being instantiated inside a RocketTile into your code, sorry about that.

I can look at refactoring things to further isolate RoCC from this/future changes to HasCoreParameters. However, in the interest of future proofing you, here's a sketch of how I would make a unit test environment for a legacy RoCC (that could then be easily converted handle a TL2 version of RoCC once we get around to specifying what that will look like).

import Chisel._
import config._
import diplomacy._
import unittest._

class RoccUnitTester()(implicit p: Parameters)  extends LazyModule {
  val ram  = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)
  val legacy = LazyModule(new TLLegacy)
  ram.node := legacy.node

  val coreParams = p.alterPartial {
    case TLCacheEdge => legacy.node.edgesOut(0)
    //...other params you're supplying
  }

  lazy val module = new LazyModuleImp(this) with HasUnitTestIO {

    val rocc = Module(new MyRocc()(coreParams)
    // feed rocc input ports
    // tie off unused rocc output ports
    legacy.module.io.legacy <> rocc.io.autl

    io.finished := Bool(false) // or replace with however you signal this
}

class RoccUnitTest()(implicit p: Parameters) extends UnitTest(timeout = 500000) {
  io.finished := Module(LazyModule(new RoccUnitTester()(p)).module).io.finished
}
Contributor

hcook commented Nov 29, 2016

TileLink2 (and therefore diplomacy) has been extended deeply into the RocketTile, where it is now being used to replace use of GlobalAddressMap in the TLB and for L1D$ coherence. The intention was to avoid breaking backwards compatibility with RoCC for now by providing a TL1 Legacy converter in the Tile. However, it looks like the wide propagation of HasCoreParameters ended up leaking some assumptions about being instantiated inside a RocketTile into your code, sorry about that.

I can look at refactoring things to further isolate RoCC from this/future changes to HasCoreParameters. However, in the interest of future proofing you, here's a sketch of how I would make a unit test environment for a legacy RoCC (that could then be easily converted handle a TL2 version of RoCC once we get around to specifying what that will look like).

import Chisel._
import config._
import diplomacy._
import unittest._

class RoccUnitTester()(implicit p: Parameters)  extends LazyModule {
  val ram  = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff)
  val legacy = LazyModule(new TLLegacy)
  ram.node := legacy.node

  val coreParams = p.alterPartial {
    case TLCacheEdge => legacy.node.edgesOut(0)
    //...other params you're supplying
  }

  lazy val module = new LazyModuleImp(this) with HasUnitTestIO {

    val rocc = Module(new MyRocc()(coreParams)
    // feed rocc input ports
    // tie off unused rocc output ports
    legacy.module.io.legacy <> rocc.io.autl

    io.finished := Bool(false) // or replace with however you signal this
}

class RoccUnitTest()(implicit p: Parameters) extends UnitTest(timeout = 500000) {
  io.finished := Module(LazyModule(new RoccUnitTester()(p)).module).io.finished
}
@seldridge

This comment has been minimized.

Show comment
Hide comment
@seldridge

seldridge Nov 30, 2016

Member

Thanks @hcook and @colinschmidt. This is some great advice. I started to move in the direction of riscv-tests-like test cases, but that sketch seems to be exactly what I need.

However, I realized that there may actually be an issue here -- I'm failing to build RoccExampleConfig from master with a TLCacheEdge match error:

java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
	at util.HasGeneratorUtilities$$anonfun$2.apply(GeneratorUtils.scala:47)
	at util.HasGeneratorUtilities$$anonfun$2.apply(GeneratorUtils.scala:44)
	at chisel3.core.Module$.do_apply(Module.scala:35)
	at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:190)
	at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:190)
	at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:204)
	at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:202)
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
	at chisel3.internal.Builder$.build(Builder.scala:202)
	at chisel3.Driver$.elaborate(Driver.scala:190)
Caused by: scala.MatchError: TLCacheEdge (of class rocket.TLCacheEdge$)
	at config.TerminalView.find(Config.scala:54)
	at config.EmptyParameters.chain(Config.scala:66)
	at config.ChainView.find(Config.scala:58)
	at config.FunctionParameters.chain(Config.scala:71)
	at config.Config.chain(Config.scala:43)
	at config.ChainView.find(Config.scala:58)
	at config.FunctionParameters.chain(Config.scala:71)
	at config.Config.chain(Config.scala:43)
	at config.ChainParameters.chain(Config.scala:62)
	at config.Config.chain(Config.scala:43)
	at config.ChainView.find(Config.scala:58)
	at config.FunctionParameters.chain(Config.scala:71)
	at config.Config.chain(Config.scala:43)
	at config.ChainParameters.chain(Config.scala:62)
java.lang.RuntimeException: Nonzero exit code: 1
	at scala.sys.package$.error(package.scala:27)
	at sbt.BuildCommon$$anonfun$toError$1.apply(Defaults.scala:2077)
	at sbt.BuildCommon$$anonfun$toError$1.apply(Defaults.scala:2077)
	at scala.Option.foreach(Option.scala:236)
	at sbt.BuildCommon$class.toError(Defaults.scala:2077)
	at sbt.Defaults$.toError(Defaults.scala:39)
	at sbt.Defaults$$anonfun$runMainTask$1$$anonfun$apply$36$$anonfun$apply$37.apply(Defaults.scala:740)
	at sbt.Defaults$$anonfun$runMainTask$1$$anonfun$apply$36$$anonfun$apply$37.apply(Defaults.scala:738)
	at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
	at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
	at sbt.std.Transform$$anon$4.work(System.scala:63)
	at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
	at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
	at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)

It looks to me like the coreParams in RocketTile aren't getting passed to the generator of the RoCCs.

Member

seldridge commented Nov 30, 2016

Thanks @hcook and @colinschmidt. This is some great advice. I started to move in the direction of riscv-tests-like test cases, but that sketch seems to be exactly what I need.

However, I realized that there may actually be an issue here -- I'm failing to build RoccExampleConfig from master with a TLCacheEdge match error:

java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
	at util.HasGeneratorUtilities$$anonfun$2.apply(GeneratorUtils.scala:47)
	at util.HasGeneratorUtilities$$anonfun$2.apply(GeneratorUtils.scala:44)
	at chisel3.core.Module$.do_apply(Module.scala:35)
	at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:190)
	at chisel3.Driver$$anonfun$elaborate$1.apply(Driver.scala:190)
	at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:204)
	at chisel3.internal.Builder$$anonfun$build$1.apply(Builder.scala:202)
	at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
	at chisel3.internal.Builder$.build(Builder.scala:202)
	at chisel3.Driver$.elaborate(Driver.scala:190)
Caused by: scala.MatchError: TLCacheEdge (of class rocket.TLCacheEdge$)
	at config.TerminalView.find(Config.scala:54)
	at config.EmptyParameters.chain(Config.scala:66)
	at config.ChainView.find(Config.scala:58)
	at config.FunctionParameters.chain(Config.scala:71)
	at config.Config.chain(Config.scala:43)
	at config.ChainView.find(Config.scala:58)
	at config.FunctionParameters.chain(Config.scala:71)
	at config.Config.chain(Config.scala:43)
	at config.ChainParameters.chain(Config.scala:62)
	at config.Config.chain(Config.scala:43)
	at config.ChainView.find(Config.scala:58)
	at config.FunctionParameters.chain(Config.scala:71)
	at config.Config.chain(Config.scala:43)
	at config.ChainParameters.chain(Config.scala:62)
java.lang.RuntimeException: Nonzero exit code: 1
	at scala.sys.package$.error(package.scala:27)
	at sbt.BuildCommon$$anonfun$toError$1.apply(Defaults.scala:2077)
	at sbt.BuildCommon$$anonfun$toError$1.apply(Defaults.scala:2077)
	at scala.Option.foreach(Option.scala:236)
	at sbt.BuildCommon$class.toError(Defaults.scala:2077)
	at sbt.Defaults$.toError(Defaults.scala:39)
	at sbt.Defaults$$anonfun$runMainTask$1$$anonfun$apply$36$$anonfun$apply$37.apply(Defaults.scala:740)
	at sbt.Defaults$$anonfun$runMainTask$1$$anonfun$apply$36$$anonfun$apply$37.apply(Defaults.scala:738)
	at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
	at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
	at sbt.std.Transform$$anon$4.work(System.scala:63)
	at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
	at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)
	at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)

It looks to me like the coreParams in RocketTile aren't getting passed to the generator of the RoCCs.

@hcook

This comment has been minimized.

Show comment
Hide comment
@hcook

hcook Nov 30, 2016

Contributor

I agree with your assessment of the issue and fix. We should make sure RoccExampleConfig at least gets built as part of our regressions...

Contributor

hcook commented Nov 30, 2016

I agree with your assessment of the issue and fix. We should make sure RoccExampleConfig at least gets built as part of our regressions...

@colinschmidt

This comment has been minimized.

Show comment
Hide comment
@colinschmidt

colinschmidt Nov 30, 2016

Member

We used to require that at my behest...

Member

colinschmidt commented Nov 30, 2016

We used to require that at my behest...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment