Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 205 additions & 39 deletions core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/main/scala-2/chisel3/choice/ModuleChoiceIntf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ private[chisel3] trait ModuleChoiceObjIntf extends SourceInfoDoc { self: ModuleC
*
* @throws java.lang.IllegalArgumentException if the cases do not belong to the same option.
*/
def apply[T <: Data](
def apply[T](
default: => FixedIOBaseModule[T]
)(choices: => Seq[(Case, () => FixedIOBaseModule[T])]): T =
macro InstChoiceTransform.apply[T]

/** @group SourceInfoTransformMacro */
def do_apply[T <: Data](
def do_apply[T](
default: => FixedIOBaseModule[T],
choices: Seq[(Case, () => FixedIOBaseModule[T])]
)(
Expand Down
59 changes: 59 additions & 0 deletions src/main/scala-3/chisel3/FixedIOModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3

import chisel3.experimental.hierarchy.{instantiable, public}
import chisel3.experimental.{BaseModule, ExtModule, Param}
import chisel3.experimental.hierarchy.core.Lookupable

/** A module or external module whose IO is generated from a specific generator.
* This module may have no additional IO created other than what is specified
* by its `ioGenerator` abstract member.
*/
@instantiable
sealed trait FixedIOBaseModule[A](using lookupable: Lookupable[A]) extends BaseModule {

/** A generator of IO */
protected def ioGenerator: A

private[chisel3] def _lookupable: Lookupable[A] = lookupable

@public final val io: A = {
val dataElems = lookupable.in(ioGenerator)
val names = LazyList.from(0).map(i => ('a' + i).toChar.toString)
val ports = dataElems.zip(names).map { case (d, name) =>
val p = IO(d)
p.suggestName(name)
p
}
lookupable.out(ioGenerator, ports.iterator)
}
endIOCreation()

}

/** A Chisel module whose IO is determined by an IO generator. This module
* cannot have additional IO created by modules that extend it.
*
* @param ioGenerator
*/
class FixedIORawModule[A](final val ioGenerator: A)(using Lookupable[A]) extends RawModule with FixedIOBaseModule[A]

/** A Chisel module whose IO (in addition to [[clock]] and [[reset]]) is determined
* by an IO generator. This module cannot have additional IO created by modules that
* extend it.
*
* @param ioGenerator
*/
class FixedIOModule[A](final val ioGenerator: A)(using Lookupable[A]) extends Module with FixedIOBaseModule[A]

/** A Chisel blackbox whose IO is determined by an IO generator. This module
* cannot have additional IO created by modules that extend it.
*
* @param ioGenerator
* @param params
*/
class FixedIOExtModule[A](final val ioGenerator: A, params: Map[String, Param] = Map.empty[String, Param])(
using Lookupable[A]
) extends ExtModule(params)
with FixedIOBaseModule[A]
4 changes: 2 additions & 2 deletions src/main/scala-3/chisel3/choice/ModuleChoiceIntf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

package chisel3.choice

import chisel3.{Data, FixedIOBaseModule}
import chisel3.FixedIOBaseModule
import chisel3.experimental.SourceInfo

private[chisel3] trait ModuleChoiceObjIntf { self: ModuleChoice.type =>
Expand All @@ -26,7 +26,7 @@ private[chisel3] trait ModuleChoiceObjIntf { self: ModuleChoice.type =>
*
* @throws java.lang.IllegalArgumentException if the cases do not belong to the same option.
*/
def apply[T <: Data](
def apply[T](
default: => FixedIOBaseModule[T]
)(
choices: => Seq[(Case, () => FixedIOBaseModule[T])]
Expand Down
72 changes: 65 additions & 7 deletions src/main/scala/chisel3/FixedIOModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,103 @@
package chisel3

import chisel3.experimental.hierarchy.{instantiable, public}
import chisel3.experimental.hierarchy.core.{Definition, Instance, Lookupable}
import chisel3.experimental.{BaseModule, UnlocatableSourceInfo}
import chisel3.experimental.dataview.DataProduct

/** A module or external module whose IO is generated from a specific generator.
* This module may have no additional IO created other than what is specified
* by its `ioGenerator` abstract member.
*/
@instantiable
sealed trait FixedIOBaseModule[A <: Data] extends BaseModule {
sealed trait FixedIOBaseModule[A] extends BaseModule {

/** A generator of IO */
protected def ioGenerator: A

@public
final val io = FlatIO(ioGenerator)(UnlocatableSourceInfo)
// Concrete subclasses implement this via an implicit constructor parameter.
protected implicit def lookupable: Lookupable[A]

protected implicit def dataProduct: DataProduct[A]

private[chisel3] def _lookupable: Lookupable[A] = lookupable

// No @public, lookups implemented manually in companion object.
final val io: A = {
val a = ioGenerator
val dataElems = lookupable.in(a)
// TODO add check for name collisions
val nameLookup = dataProduct.dataIterator(a, "").toMap
val io = dataElems.map { d =>
val p = FlatIO(d)(UnlocatableSourceInfo)
val name = {
val n = nameLookup(d)
if (n == "") "io" else n
}
p.suggestName(name)
p
}
lookupable.out(ioGenerator, io.iterator)
}
endIOCreation()

}

object FixedIOBaseModule {

// @public val io: A on the trait would generate these, but the @instantiable macro produces an
// external implicit class with no access to the trait's `lookupable` member. We replicate the
// generated lookup by passing proto.lookupable explicitly instead.
implicit class DefinitionOps[A](___module: Definition[FixedIOBaseModule[A]]) {
def io: A = {
implicit val _mg: chisel3.internal.MacroGenerated = new chisel3.internal.MacroGenerated {}
implicit val _l: Lookupable.Aux[A, A] =
___module.proto.lookupable.asInstanceOf[Lookupable.Aux[A, A]]
___module._lookup(_.io)
}
}

implicit class InstanceOps[A](___module: Instance[FixedIOBaseModule[A]]) {
def io: A = {
implicit val _mg: chisel3.internal.MacroGenerated = new chisel3.internal.MacroGenerated {}
implicit val _l: Lookupable.Aux[A, A] =
___module.proto.lookupable.asInstanceOf[Lookupable.Aux[A, A]]
___module._lookup(_.io)
}
}
}

/** A Chisel module whose IO is determined by an IO generator. This module
* cannot have additional IO created by modules that extend it.
*
* @param ioGenerator
*/
class FixedIORawModule[A <: Data](final val ioGenerator: A) extends RawModule with FixedIOBaseModule[A]
class FixedIORawModule[A](final val ioGenerator: A)(
implicit val lookupable: Lookupable[A],
val dataProduct: DataProduct[A]
) extends RawModule
with FixedIOBaseModule[A]

/** A Chisel module whose IO (in addition to [[clock]] and [[reset]]) is determined
* by an IO generator. This module cannot have additional IO created by modules that
* extend it.
*
* @param ioGenerator
*/
class FixedIOModule[A <: Data](final val ioGenerator: A) extends Module with FixedIOBaseModule[A]
class FixedIOModule[A](final val ioGenerator: A)(
implicit val lookupable: Lookupable[A],
val dataProduct: DataProduct[A]
) extends Module
with FixedIOBaseModule[A]

/** A Chisel blackbox whose IO is determined by an IO generator. This module
* cannot have additional IO created by modules that extend it.
*
* @param ioGenerator
* @param params
*/
class FixedIOExtModule[A <: Data](final val ioGenerator: A, params: Map[String, Param] = Map.empty[String, Param])
extends ExtModule(params)
class FixedIOExtModule[A](final val ioGenerator: A, params: Map[String, Param] = Map.empty[String, Param])(
implicit val lookupable: Lookupable[A],
val dataProduct: DataProduct[A]
) extends ExtModule(params)
with FixedIOBaseModule[A]
16 changes: 11 additions & 5 deletions src/main/scala/chisel3/choice/ModuleChoice.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,29 @@ import scala.collection.immutable.ListMap

import chisel3.{Data, FixedIOBaseModule, Module, SourceInfoDoc}
import chisel3.experimental.{BaseModule, SourceInfo}
import chisel3.experimental.hierarchy.core.Lookupable
import chisel3.internal.{groupByIntoSeq, Builder}
import chisel3.internal.binding.InstanceChoiceBinding
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl.ir.DefInstanceChoice

object ModuleChoice extends ModuleChoiceObjIntf {

protected def _applyImpl[T <: Data](
protected def _applyImpl[T](
default: => FixedIOBaseModule[T],
choices: Seq[(Case, () => FixedIOBaseModule[T])]
)(
implicit sourceInfo: SourceInfo
): T = {
val instDefaultModule = Module.evaluate(default)
val lk: Lookupable[T] = instDefaultModule._lookupable

val defaultLeaves = lk.in(instDefaultModule.io)

val choiceModules = choices.map { case (choice, module) =>
val instModule = Module.evaluate(module())
if (!instModule.io.typeEquivalent(instDefaultModule.io)) {
val choiceLeaves = lk.in(instModule.io)
if (!defaultLeaves.zip(choiceLeaves).forall { case (a, b) => b.typeEquivalent(a) }) {
Builder.error("Error: choice module IO bundles are not type equivalent")
}
Builder.options += choice
Expand All @@ -46,13 +51,14 @@ object ModuleChoice extends ModuleChoiceObjIntf {
}
val group = groupedChoices.head.name

val binding = instDefaultModule.io.cloneTypeFull
binding.bind(InstanceChoiceBinding(Builder.forcedUserModule, Builder.currentBlock))
val bindingLeaves = defaultLeaves.map(_.cloneTypeFull)
bindingLeaves.foreach(_.bind(InstanceChoiceBinding(Builder.forcedUserModule, Builder.currentBlock)))
val binding = lk.out(instDefaultModule.io, bindingLeaves.iterator)

pushCommand(
DefInstanceChoice(
sourceInfo,
binding,
bindingLeaves.head,
instDefaultModule,
group,
choiceModules.map { case (choice, instModule) =>
Expand Down
22 changes: 22 additions & 0 deletions src/test/scala/chiselTests/ModuleChoiceSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,26 @@ class ModuleChoiceSpec extends AnyFlatSpec with Matchers with FileCheck {
|CHECK: connect inst.in, io.in""".stripMargin
)
}

it should "generate correct FIRRTL when using tuple ports" in {

class Bar extends FixedIORawModule[(UInt, UInt)]((UInt(1.W), UInt(2.W)))

class Foo extends Module {
val a = IO(Output(UInt(1.W)))
val b = IO(Output(UInt(2.W)))
private val bar = ModuleChoice(new Bar)(Seq(Platform.FPGA -> new Bar))
a :<= bar._1
b :<= bar._2
}

ChiselStage.emitCHIRRTL(new Foo)
.fileCheck()(
"""|CHECK: instchoice bar of Bar
|CHECK: connect a, bar._1
|CHECK-NEXT: connect b, bar._2
|"""
)

}
}
Loading