SpinalHDL / VexRiscv

A FPGA friendly 32 bit RISC-V CPU implementation
MIT License
2.52k stars 420 forks source link

Assertion when tracing the elaboration issue #371

Closed eruanno123 closed 1 year ago

eruanno123 commented 1 year ago

Hello, I am having some issues that make debugging my design quite tedious. I have no doubts I have mistakes like missing connections to ports. My understanding is that SpinalHDL executes the "second pass" to identify what fails the elaboration.

Unfortunately, during this second pass, I am getting a long assertion trace from a seemingly unrelated component. Normally, I should get a list of things that fail elaboration, which would make correcting code easy.

I am relatively new to VexRiscv and SpinalHDL in general, so I am having difficulty following up and understanding what's going on in the cache plugin. Do you have any idea what could cause the assertion in the DecoderSimplePlugin.addDefault function, but only when Spinal restarts with a trace to identify the elaboration problem?

Meanwhile, I am working on a minimum working example, which I will try to provide soon.

[info] [Warning] Elaboration failed (6 errors).
[info]           Spinal will restart with scala trace to help you to find the problem.
[info] **********************************************************************************************
[info] [Progress] at 1.991 : Elaborate components
[error] Exception in thread "main" java.lang.AssertionError: assertion failed
[error]         at scala.Predef$.assert(Predef.scala:208)
[error]         at spinal.core.package$.assert(core.scala:497)
[error]         at vexriscv.plugin.DecoderSimplePlugin.addDefault(DecoderSimplePlugin.scala:67)
[error]         at vexriscv.plugin.IBusCachedPlugin.setup(IBusCachedPlugin.scala:103)
[error]         at vexriscv.plugin.IBusCachedPlugin.setup(IBusCachedPlugin.scala:28)
[error]         at vexriscv.Pipeline.$anonfun$build$2(Pipeline.scala:47)
[error]         at vexriscv.Pipeline.$anonfun$build$2$adapted(Pipeline.scala:47)
[error]         at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
[error]         at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
[error]         at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
[error]         at vexriscv.Pipeline.build(Pipeline.scala:47)
[error]         at vexriscv.Pipeline.build$(Pipeline.scala:45)
[error]         at vexriscv.VexRiscv.build(VexRiscv.scala:126)
[error]         at vexriscv.Pipeline.$anonfun$$init$$1(Pipeline.scala:161)
[error]         at spinal.core.Component.$anonfun$prePop$1(Component.scala:180)
[error]         at spinal.core.Component.$anonfun$prePop$1$adapted(Component.scala:178)
[error]         at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
[error]         at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
[error]         at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
[error]         at spinal.core.Component.prePop(Component.scala:178)
[error]         at spinal.core.Component.postInitCallback(Component.scala:187)
[error]         at vexriscv.custom.Risky$$anon$3$$anon$4.<init>(Risky.scala:328)
[error]         at vexriscv.custom.Risky$$anon$3.<init>(Risky.scala:319)
[error]         at vexriscv.custom.Risky.<init>(Risky.scala:271)
[error]         at vexriscv.custom.RiskyDe0Nano$.$anonfun$main$1(Risky.scala:550)
[error]         at spinal.core.internals.PhaseCreateComponent.$anonfun$impl$274(Phase.scala:2606)
[error]         at spinal.core.fiber.Engine$.$anonfun$create$1(AsyncCtrl.scala:147)
[error]         at spinal.core.fiber.AsyncThread.$anonfun$jvmThread$1(AsyncThread.scala:59)
[error]         at spinal.core.fiber.EngineContext.$anonfun$newJvmThread$1(AsyncCtrl.scala:39)
[error]         at spinal.sim.JvmThread.run(SimManager.scala:51)
[error] Nonzero exit code returned from runner: 1
[error] (Compile / runMain) Nonzero exit code returned from runner: 1
[error] Total time: 11 s, completed Oct 10, 2023, 11:21:36 AM
eruanno123 commented 1 year ago

Btw. "vexriscv.custom.Risky" is a copy of "Briey" with my customization to the peripherals. As I mentioned, I will try to provide an example of the issue, once I strip it down from many things unrelated to the main issue.

eruanno123 commented 1 year ago

The reasonable workaround I just found is enabling the traces during the first pass by providing debugComponents to the SpinalConfig:

val debugComponents = HashSet[Class[_]]()
debugComponents += this.getClass()

val config =
  SpinalConfig(
    /*... */,
    debugComponents = debugComponents
  )

config.generateVerilog({
  val toplevel = new Risky(riskyConfig.copy(sdramLayout = IS42x160G.layout))
  toplevel
})

Now, I get correct elaboration failure results

[info] [Progress] at 1.721 : Checks and transforms
[error] Exception in thread "main" spinal.core.SpinalExit: 
[error]  Error detected in phase PhaseCheck_noLatchNoOverride
[error] ********************************************************************************
[error] ********************************************************************************
[error] NO DRIVER ON (toplevel/axi_bootManager/aclk : in Bool), defined at
[error] ???
[error] ********************************************************************************
[error] ********************************************************************************
[error] NO DRIVER ON (toplevel/axi_bootManager/aresetn : in Bool), defined at
[error] ???
Dolu1990 commented 1 year ago

Hi,

The reasonable workaround I just found is enabling the traces during the first pass by providing debugComponents to the SpinalConfig:

How was your scala main which was generating the hardware before ? The issue you had will happen if you define the VexRiscv config outside the generateVerilog block.

Was it the case ?

eruanno123 commented 1 year ago

Thanks for the quick response.

I think this might be the case, although I am not sure if making a copy of the configuration counts. I am building the configuration starting from defaults and 'injecting' modifications passed through the command line. I will check if it helps to refactor the code, so that things happen under the generateVerilog block.

class CommandLineConf(arguments: Seq[String]) extends ScallopConf(arguments) {
  val outputDir = opt[String]("output", short = 'O', default = Some("_build"))
  val hwJtag = toggle("hw-jtag")
  val coreFrequency = opt[Int](
    "core-frequency",
    default = Some(100000000),
    validate = (0 to 300000000).contains
  )

  val makeIntRangeArg =
    (name: String, default: Some[Int], minVal: Int, maxVal: Int) =>
      opt[Int](name, default = default, validate = (minVal to maxVal).contains)

  val makeApbAddressArg = (id: Int) =>
    makeIntRangeArg(s"apb${id}-address-width", Some(21), 12, 32)

  val apb1AddressWidth = makeApbAddressArg(1)
  val apb2AddressWidth = makeApbAddressArg(2)

  val noDma = toggle("no-dma")

  verify()

  def propagateRiskyConfig(baseConfig: RiskyConfig): RiskyConfig = {
    baseConfig.copy(
      useHardwareJtag = hwJtag.toOption.getOrElse(false),
      axiFrequency = coreFrequency.toOption.getOrElse(100000000) Hz,
      apb1BusConfig = RiskyConfig.default.apb1BusConfig
        .copy(addressWidth = apb1AddressWidth.toOption.getOrElse(21)),
      apb2BusConfig = RiskyConfig.default.apb2BusConfig
        .copy(addressWidth = apb2AddressWidth.toOption.getOrElse(21)),
      saxi1BusPresent = !noDma.toOption.getOrElse(false)
    )
  }

  def getOutputBuildDirectory(): String = outputDir.toOption.get
}

// Implementation for Terasic DE0-Nano board
object RiskyDe0Nano {
  def main(args: Array[String]) {
    val cmdLineConfig = new CommandLineConf(args)
    val riskyConfig = cmdLineConfig.propagateRiskyConfig(RiskyConfig.default)

   // ... here is some irrelevant SDRAM stuff

    val debugComponents = HashSet[Class[_]]()
    debugComponents += this.getClass()

    val config =
      SpinalConfig(
        targetDirectory = cmdLineConfig.getOutputBuildDirectory(),
        debugComponents = debugComponents
      )

    config.generateVerilog({
      val toplevel =
        new Risky(riskyConfig.copy(sdramLayout = IS42x160G.layout))
      toplevel
    })
  }
}
eruanno123 commented 1 year ago

I am confirming this is indeed the reason I am observing the issue. The following modification works as intended:

    config.generateVerilog({
      // moved riskyConfig here:
      val riskyConfig = cmdLineConfig.propagateRiskyConfig(RiskyConfig.default)
      val toplevel =
        new Risky(riskyConfig.copy(sdramLayout = IS42x160G.layout))
      toplevel
    })

Many thanks for help!