Comments (24)
Comment by whitequark
Sunday Jan 13, 2019 at 08:53 GMT
@jordens Any ideas on naming here?
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 12:21 GMT
IMO .synthesize()
would also colide with what the lower toolchain layers claim to do.
.elaborate()
maybe?
One other thing that I noticed when trying to port the CORDIC in misoc to nmigen, that may tie into this refactoring: Subclassing a core in nmigen seems hard or impossible. Previously in migen, I'd just implement a core wrapping some functionality of another core by inheriting and then intercepting Signal()
s as needed (see TwoQuadrantCORDIC
vs the full CORDIC
that remaps the quadrants).
This is now hard because I was unable to figure out a workable way to intercept/override/remap Signals in __init__
and at the same time properly overload and handle get_fragment()
from the two classes. If this explanation is too convoluted I'll come up with a minimal example that shows what I'd like to do.
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 12:23 GMT
I don't like .elaborate()
but I don't have any options I like so it's probably fine.
If this explanation is too convoluted I'll come up with a minimal example that shows what I'd like to do.
I think I understand what you mean but I'd appreciate an example as it would let me see a solution more clearly. I definitely acknowledge that subclassing may be more difficult in nmigen.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 13:12 GMT
class And(Module):
def __init__(self):
self.i0 = Signal()
self.i1 = SIgnal()
self.o = Signal()
###
self.sync += self.o.eq(self.a & self.b)
class Nand(And):
def __init__(self):
super().__init__()
###
self.o, o_inner = Signal(), self.o
self.comb += self.o.eq(~o_inner)
This could be done in a different way here. But the basic pattern of providing modified functionality through a similar interface by intercepting inputs and outputs is what I am looking for.
In nmigen I would have to own the inner And
in submodules and then add i0
, i1
as attributes to Nand
(or forward them with jet more signals).
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 13:16 GMT
(I assume you meant to use super()
above.)
Can you explain why this pattern is preferable to having a submodule? Is it just because you have to forward some of the signals in the wrapper, or is there a deeper reason?
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 13:32 GMT
No need to forward. It's less code, shorter and easier to read verilog. But more importantly Nand
doesn't need to know anything about the input interface (how many and where the inputs are and how they are named). All those also apply to the original CORDIC case.
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 13:40 GMT
shorter and easier to read verilog
You can set f.flatten
on a fragment to get the same Verilog, at least.
But more importantly
Nand
doesn't need to know anything about the input interface (how many and where the inputs are and how they are named). All those also apply to the original CORDIC case.
Is subclassing like that really the most appropriate way to solve this? What about having a single base class that computes an intermediate result, and two subclasses that bring out different final results? (Or a single class with an argument to the constructor, which is effectively equivalent, but arugably more unwieldy, especially with a larger amount of variants than two.)
That's how I would write this code in the first place in oMigen as well--using this pattern would not really occur to me, as it is IME not common in Python and it violates LSP.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 14:12 GMT
If one of the subclasses is the identity (as in these two examples) that's a lot of low density mapping code again. And while LSP is maybe violated by the example above, it isn't in the CORDIC example where the four quadrant cordic strictly extends the applicability of the two quadrant cordic. But anyway LSP violation doesn't seem relevant to the question how to subclass here.
In addition to the signal interception question: How do you overload get_fragment()
? Can I add more logic to whatever (Fragment or Module) I obtain from super().get_fragment()
? Is .lower()
idempotent?
I think there is a functional problem with the semantics of the Fragment/Module split. Can I add a fragment as a submodule?
def get_fragment(self, platform):
m = Module()
m.submodules += super().get_fragment(platform)
return m.lower()
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 14:16 GMT
that's a lot of low density mapping code again
Is it? You have something like:
class And(Common):
def __init__(self):
super().__init__(self)
self.o = self.intermediate
which does not seem to me like a lot of code. I.e. only the things that would be changed in the other subclass would need to be brought out.
I think there is a functional problem with the semantics of the Fragment/Module split. Can I add a fragment as a submodule?
Yes, fragments can be added as submodules.
In addition to the signal interception question: How do you overload
get_fragment()
? Can I add more logic to whatever (Fragment or Module) I obtain fromsuper().get_fragment()
? Is.lower()
idempotent?
After this discussion I think that get_fragment()
should return a Module
i.e. not call lower
. (This is actually easier than the current behavior.) So yes, you could extend the returned module in a subclass.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 14:16 GMT
On the name, there are also .emit()
, .refine()
, .specialize()
, .generate()
.
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 14:17 GMT
I think I like elaborate
the most here, because it is clearly HDL-specific, even if the meaning is somewhat different from what Verilog has.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 14:27 GMT
that's a lot of low density mapping code again
Is it? You have something like:
[...]
which does not seem to me like a lot of code. I.e. only the things that would be changed in the other subclass would need to be brought out.
I think it's super().__init__()
without the self
.
Yes. I meant those four lines of extra code. That could arguably be made smaller for complicated interfaces with many Signal
s by the use of Record
s. But to force the use of a Record
just because it makes implementing an identity wrapper easier seems backwards.
I think there is a functional problem with the semantics of the Fragment/Module split. Can I add a fragment as a submodule?
Yes, fragments can be added as submodules.
That begs the question: Are fragments also modules? Functionally and/or semantically?
After this discussion I think that
get_fragment()
should return aModule
i.e. not calllower
. (This is actually easier than the current behavior.) So yes, you could extend the returned module in a subclass.
Yes. Getting access to the base class Module()
in subclasses generally seems desirable.
Then if get_fragment()
returns a Module
, who should call lower()
?
.modularize()
or even .get_module()
?
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 14:31 GMT
Yes. I meant those four lines of extra code.
I'm not convinced it's actually bad. If nothing else, it makes the signals that are being overridden explicit. I would like to see that written out when working on such code.
That begs the question: Are fragments also modules? Functionally and/or semantically?
Anything that is "fragment-compatible" may be added as a submodule. That is, anything with get_fragment()
function. The only constraint on get_fragment()
is that it should return another fragment-compatible object; during lowering, this is iterated to fixed point. That is how a get_fragment()
function can delegate to platform.get_primitive()
which might want to return a Module
or even another fragment-compatible object.
This answers your other question as well, I believe.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 14:38 GMT
Ok. Module
s are fragment-compatible. But the naming of submodule
is at least confusing as it indicates the reverse is also true (Fragment
being added as a submodule).
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 14:39 GMT
Point. Suggestions for better naming?
from amaranth.
Comment by adamgreig
Tuesday Jan 15, 2019 at 14:44 GMT
Why do we need both Module
and Fragment
? Given as you can always turn a Module
into a Fragment
, and you can use a Fragment
anywhere you currently use a Module
, could we just combine the two classes?
This would also solve the nuisance of currently being able to add ports to a Fragment
but not a Module
(so you need to frag = m.lower(); frag.add_ports(...); return frag
, which would break just always returning a module from get_fragment()
proposed above), and being able to set .flatten
on Fragment
but not Module
, etc.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 14:44 GMT
The naive .subfragments
, .parts
, .children
. But let's take a step back.
Now we have all three (bare fragment-compatible "cores", Fragment
, Module
) being fragment-compatible but get_fragment()
not returning Fragment
in most cases. Yet, the return value is always submodule-compatible.
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 14:46 GMT
Why do we need both
Module
andFragment
? Given as you can always turn aModule
into aFragment
, and you can use aFragment
anywhere you currently use aModule
, could we just combine the two classes?
Module
holds additional state that Fragment
does not need and that cannot be correctly updated e.g. when merging Fragment
s during hierarchy flattening.
This would also solve the nuisance of currently being able to add ports to a
Fragment
but not aModule
(so you need tofrag = m.lower(); frag.add_ports(...); return frag
, which would break just always returning a module fromget_fragment()
proposed above), and being able to set.flatten
onFragment
but notModule
, etc.
You should never manually call add_ports()
. If you need that it is a bug elsewhere.
Now we have all three (bare fragment-compatible "cores",
Fragment
,Module
) being fragment-compatible butget_fragment()
not returningFragment
in most cases. Yet, the return value is always submodule-compatible.
We agreed to rename get_fragment()
to elaborate()
, which works well with this context.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 14:53 GMT
We agreed to rename
get_fragment()
toelaborate()
, which works well with this context.
Ok. The new terminology is then: Implementing elaborate(platform)
makes something elaborate-compatible. Fragment
, Module
and typical "cores" are elaborate-compatible. Everything elaborate-compatible can be added to Module().xxx
, where xxx
is TBD.
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 14:57 GMT
SGTM.
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 15:02 GMT
Then I have the usual components
, parts
, children
, pieces
as group noun for the elaborate-compatible things, i.e. xxx
.
from amaranth.
Comment by whitequark
Tuesday Jan 15, 2019 at 15:04 GMT
components
sounds best to me of all these options.
from amaranth.
Comment by adamgreig
Tuesday Jan 15, 2019 at 15:05 GMT
It's a bit longer, but perhaps subcomponents
?
from amaranth.
Comment by jordens
Tuesday Jan 15, 2019 at 15:16 GMT
Or just the shorter and slightly idiosyncratic sub
.
from amaranth.
Related Issues (20)
- Unfriendly "recursion depth exceeded" error message when forgetting `In` and `Out` on a `Component`'s signature HOT 2
- Infer Verilog ports on Interface objects HOT 1
- Memory generates an empty Verilog wrport module that Vivado considers a black box HOT 6
- Use something better than `path=` in `Interface()`/`Signature.create()`
- `amaranth.lib.enum` does not support functional syntax
- Signal reset cannot be set to a const value
- Possible inconsistency in FlippedSignature HOT 2
- `lib.wiring.connect` does not take custom `Signature.__eq__` overrides into account HOT 2
- agregate Signal() HOT 1
- Issue with `Memory` simulation HOT 2
- `UnusedElaboratable` message still appearing in `pytest` tests when `xfail(run=False)` marker is used in conjunction with Simulator and warning is suppressed.
- Failure of sign-extension for bitwise operations HOT 3
- Transparent memory using Python simulator reading from incorrect address when write occurred on previous cycle also HOT 1
- Amaranth emitted invalid Verilog (Amaranth conditionals containing only comb assignments results in Verilog "empty case" error) HOT 12
- Allow `isinstance()`/`issubclass()` to work between Interface and FlippedInterface HOT 2
- `Signature.is_compliant()` should ensure that its argument is an interface object HOT 4
- Invalid verilog generated for 0-bit Signals HOT 4
- AttributeError when requesting resource with subsignals and direction "-"
- iCE40 BRAM errata workaround silently disappears once a clock domain named "sync" is defined HOT 2
- SyncFIFOBuffered raise a TypeError HOT 4
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from amaranth.