-
Notifications
You must be signed in to change notification settings - Fork 615
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
[RFC] [Proposal] Programmatic Port Creation #833
Changes from 10 commits
82fe01d
cce3378
92e2d95
7ce2562
cbb25e8
6ec7c7d
24ef51c
64ddbab
0b28183
fa7e1ab
10e9227
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,17 +38,30 @@ abstract class UserModule(implicit moduleCompileOptions: CompileOptions) | |
|
||
val compileOptions = moduleCompileOptions | ||
|
||
private[chisel3] def namePorts(names: HashMap[HasId, String]): Unit = { | ||
for (port <- getModulePorts) { | ||
port.suggestedName.orElse(names.get(port)) match { | ||
case Some(name) => | ||
if (_namespace.contains(name)) { | ||
Builder.error(s"""Unable to name port $port to "$name" in $this,""" + | ||
" name is already taken by another port!") | ||
} | ||
port.setRef(ModuleIO(this, _namespace.name(name))) | ||
case None => Builder.error(s"Unable to name port $port in $this, " + | ||
"try making it a public field of the Module") | ||
} | ||
} | ||
} | ||
|
||
|
||
private[core] override def generateComponent(): Component = { | ||
require(!_closed, "Can't generate module more than once") | ||
_closed = true | ||
|
||
val names = nameIds(classOf[UserModule]) | ||
|
||
// Ports get first naming priority, since they are part of a Module's IO spec | ||
for (port <- getModulePorts) { | ||
require(names.contains(port), s"Unable to name port $port in $this") | ||
port.setRef(ModuleIO(this, _namespace.name(names(port)))) | ||
} | ||
namePorts(names) | ||
|
||
// Then everything else gets named | ||
for ((node, name) <- names) { | ||
|
@@ -170,6 +183,15 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) | |
names | ||
} | ||
|
||
private[chisel3] override def namePorts(names: HashMap[HasId, String]): Unit = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does LegacyModule really needs its own specialized code path? I think LegacyModule already checks for extraneous IOs as part of existing code, so suggestName-based IOs should already be rejected. In the case of a suggestName("io") conflict, hopefully the name conflict mechanism should cause it to error out? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It probably doesn't, I'll look into it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It actually does, the regular UserModule flow involves checking suggestedName and |
||
for (port <- getModulePorts) { | ||
// This shoudl already have been caught | ||
if (!names.contains(port)) throwException(s"Unable to name port $port in $this") | ||
val name = names(port) | ||
port.setRef(ModuleIO(this, _namespace.name(name))) | ||
} | ||
} | ||
|
||
private[core] override def generateComponent(): Component = { | ||
_compatAutoWrapPorts() // pre-IO(...) compatibility hack | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// See LICENSE for license details. | ||
|
||
package chiselTests | ||
package experimental | ||
|
||
import chisel3._ | ||
import chisel3.experimental.MultiIOModule | ||
|
||
// NOTE This is currently an experimental API and subject to change | ||
// Example using a private port | ||
class PrivatePort extends NamedModuleTester { | ||
private val port = expectName(IO(Input(UInt(8.W))), "foo") | ||
port.suggestName("foo") | ||
} | ||
|
||
// Example of using composition to add ports to a Module | ||
class CompositionalPort(module: NamedModuleTester, name: String) { | ||
import chisel3.experimental.IO | ||
val foo = module.expectName(IO(Output(Bool())), name) | ||
foo.suggestName(name) | ||
foo := true.B | ||
} | ||
|
||
class CompositionalPortTester extends NamedModuleTester { | ||
val a = new CompositionalPort(this, "cheese") | ||
val b = new CompositionalPort(this, "tart") | ||
} | ||
|
||
class PortsWinTester extends NamedModuleTester { | ||
val wire = expectName(Wire(UInt()), "wire_1") | ||
val foo = expectName(Wire(UInt()).suggestName("wire"), "wire_2") | ||
val output = expectName(IO(Output(UInt())).suggestName("wire"), "wire") | ||
} | ||
|
||
class ProgrammaticPortsSpec extends ChiselFlatSpec { | ||
|
||
private def doTest(testMod: => NamedModuleTester): Unit = { | ||
var module: NamedModuleTester = null | ||
elaborate { module = testMod; module } | ||
assert(module.getNameFailures() == Nil) | ||
} | ||
|
||
"Programmatic port creation" should "be supported" in { | ||
doTest(new PrivatePort) | ||
} | ||
|
||
"Calling IO outside of a Module definition" should "be supported" in { | ||
doTest(new CompositionalPortTester) | ||
} | ||
|
||
"Ports" should "always win over internal components in naming" in { | ||
doTest(new PortsWinTester) | ||
} | ||
|
||
"LegacyModule" should "ignore suggestName on ports" in { | ||
doTest(new Module with NamedModuleTester { | ||
val io = IO(new Bundle { | ||
val foo = Output(UInt(8.W)) | ||
}) | ||
expectName(io.suggestName("cheese"), "io") | ||
expectName(clock.suggestName("tart"), "clock") | ||
expectName(reset.suggestName("teser"), "reset") | ||
}) | ||
} | ||
|
||
"SuggestName collisions on ports" should "be illegal" in { | ||
a [ChiselException] should be thrownBy { | ||
elaborate(new MultiIOModule { | ||
val foo = IO(UInt(8.W)).suggestName("apple") | ||
val bar = IO(UInt(8.W)).suggestName("apple") | ||
}) | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this detect naming conflicts? Previously we didn't need to worry about this because (presumably) you can't get a conflict with member accessors, but given arbitrary strings, I think this is where you want to error out where you have two wires fighting for the same name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently you can get conflicts using suggestName:
gives
This PR doesn't change anything about that.
That being said, there definitely should be some tests illustrating how conflicts are resolved. TLDR, ports always win over things like wires, but _# is introduced when ports conflict as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense to check for conflicts in namePorts and error out if one is detected? I don't know if it makes sense to refactor namespace to do that checking, but that's definitely out of scope for this,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a stricter
.Name
API could error. I don't think we should change.suggestName
to error since it's name suggests it won't, but maybe I can make it error just for ports? I'll look into how hard that is to doThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think it should error for ports. We don't have to worry about wires until suggestName is replaced, but I think that a particularly nasty case could be a conflict between a suggested name and a val name, and one of them (probably based on call order) will unexpectedly turn into the wrong thing in the generated verilog.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It now errors for ports getting suggested (or otherwise named) to the same thing