Skip to content

ModuleChoice does not connect implicit clock/reset to FixedIOModule targets #5383

Description

@uenoku

Type of issue: Bug Report

Please provide the steps to reproduce the problem:
Run scala-cli on the following scala code (assisted by claude code sonnet 4.6).

//> using repository https://central.sonatype.com/repository/maven-snapshots
//> using scala 2.13.18
//> using dep org.chipsalliance::chisel:7.12.0+49-61358ac3-SNAPSHOT
//> using plugin org.chipsalliance:::chisel-plugin:7.12.0+49-61358ac3-SNAPSHOT
//> using options -unchecked -deprecation -language:reflectiveCalls -feature -Xcheckinit -Ymacro-annotations

import chisel3._
import chisel3.choice.{DynamicCase, DynamicGroup, ModuleChoice}
import _root_.circt.stage.ChiselStage

object MyGroup extends DynamicGroup("MyGroup") {
  object Present    extends DynamicCase
  object NotPresent extends DynamicCase
}

// ---------------------------------------------------------------------------
// Case A: FixedIOModule — implicit clock/reset from Module
// ---------------------------------------------------------------------------

class SimpleIO extends Bundle {
  val in  = Input(UInt(8.W))
  val out = Output(UInt(8.W))
}

class RegisteredFixedIOModule extends FixedIOModule[SimpleIO](new SimpleIO) {
  val reg = RegInit(0.U(8.W))
  reg    := io.in
  io.out := reg
}

class PassthroughFixedIOModule extends FixedIOModule[SimpleIO](new SimpleIO) {
  io.out := io.in
}

// ModuleChoice with FixedIOModule — fails: "reset network never driven with concrete type"
class TopWithFixedIOModule extends Module {
  val in  = IO(Input(UInt(8.W)))
  val out = IO(Output(UInt(8.W)))
  val m   = ModuleChoice(new RegisteredFixedIOModule)(Seq(
    MyGroup.Present    -> new RegisteredFixedIOModule,
    MyGroup.NotPresent -> new PassthroughFixedIOModule,
  ))
  m.in := in
  out  := m.out
}

// ---------------------------------------------------------------------------
// Case B: FixedIORawModule — explicit clock/reset in IO bundle (workaround)
// ---------------------------------------------------------------------------

class SimpleIOWithClockReset extends Bundle {
  val clock = Input(Clock())
  val reset = Input(Reset())
  val in    = Input(UInt(8.W))
  val out   = Output(UInt(8.W))
}

class RegisteredFixedIORawModule extends FixedIORawModule[SimpleIOWithClockReset](new SimpleIOWithClockReset) {
  withClockAndReset(io.clock, io.reset) {
    val reg = RegInit(0.U(8.W))
    reg    := io.in
    io.out := reg
  }
}

class PassthroughFixedIORawModule extends FixedIORawModule[SimpleIOWithClockReset](new SimpleIOWithClockReset) {
  io.out := io.in
}

// ModuleChoice with FixedIORawModule — succeeds when clock/reset driven explicitly
class TopWithFixedIORawModule extends Module {
  val in  = IO(Input(UInt(8.W)))
  val out = IO(Output(UInt(8.W)))
  val m   = ModuleChoice(new RegisteredFixedIORawModule)(Seq(
    MyGroup.Present    -> new RegisteredFixedIORawModule,
    MyGroup.NotPresent -> new PassthroughFixedIORawModule,
  ))
  m.clock := clock
  m.reset := reset
  m.in    := in
  out     := m.out
}

// ---------------------------------------------------------------------------

object Main extends App {
  println("=== Case B: FixedIORawModule (should succeed) ===")
  println(ChiselStage.emitSystemVerilog(new TopWithFixedIORawModule))

  println("=== Case A: FixedIOModule (should fail: reset network never driven with concrete type) ===")
  println(ChiselStage.emitSystemVerilog(new TopWithFixedIOModule))
}

What is the current behavior?
firtool fails with:

  error: reset network never driven with concrete type
    val m = ModuleChoice(new RegisteredFixedIOModule)(Seq(

What is the expected behavior?

ModuleChoice should connect the enclosing module's implicit clock/reset to FixedIOModule targets, as Module(new Foo) does. I think implicit connection is missing for ModuleChoice(FixedIOModule)?

Please tell us about your environment:

Other Information

What is the use case for changing the behavior?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions