<ktemkin>
I finally got bitten by the "assert defs[sig] is self" one too many times and decided maybe it was worth replacing that assert
<ktemkin>
(I don't know what that message should read, really; I'll admit I didn't stare at the codebase for long enough to get a good feeling for what it should say)
<whitequark>
ktemkin: that part of the code is tricky and i never spent time to figure out if something like your patch would work well
<whitequark>
it's possible that it is; i just don't know yet
<ktemkin>
mm; that's part of why I asked instead of PR'ing
<ktemkin>
but even having a very ambiguous/not-detailed message that contains the signal name seems a lot more useful than just an AssertionError
<ktemkin>
and experientially, I pretty much only see that assertion if I've done something stupid, like forgotten a .o on an inout signal
<ktemkin>
(which immediately becomes obvious if it even prints out prints out "TypeError: something bad happened with 'hitech_fmc_pipe_0__rx_termination__i' )
<FL4SHK>
lkcl: so it's too late!
<FL4SHK>
I've constructed the priority encoder
<FL4SHK>
lkcl: so it's a priority encoder that searches for how many leading zeros there are
<FL4SHK>
sort of searches for how many leading zeros there are
chipmuenk has quit [Ping timeout: 244 seconds]
chipmuenk1 is now known as chipmuenk
<whitequark>
ktemkin: i can do some nmigen work in near future, so what do you say about just fixing it properly?
<ktemkin>
I mean, I'd think an exception as intermediary scaffolding wouldn't hurt; but my preference about it is so weak that I'll 100% defer to whatever seems best to you
<whitequark>
i don't like turning assertions (i.e. indicators of a bug) into user-visible errors (i.e. indicators of a user error)
<whitequark>
but i agree that balancing false positives with false negatives is very much personal preference
hitomi2509 has quit [Quit: Nettalk6 - www.ntalk.de]
<ktemkin>
I mean, personally, I think I'd prefer a more useful AssertionError("Internal error processing `<signal name>`") just to give a breadcrumb for the time being
<ktemkin>
considering my go-to action when I see that and don't immediately realize the issue is to open ir.py and add a print
<ktemkin>
but again, my preferences aren't strong at all, so that's mostly an aesthetic/theoretical comment =P
<whitequark>
oh, we can totally add an assertion message
<whitequark>
assert defs[sig] is self, "<blah>"
<FL4SHK>
is something like `while bus.busy: yield Delay()` a thing?
<FL4SHK>
will that do what I expect?
<ktemkin>
whitequark: is it in violation of your aesthetic to add a parenthetical, like `assert <...>, f"unexpected use of {sig.name} [possible assignment to input]?"`
emeb_mac has joined #nmigen
<lkcl>
FL4SHK: that looks like a combination of a priority-encoder with a... urr! :)
<FL4SHK>
so I'm having trouble running a simulation...
<vup>
FL4SHK: you probably want something more like `while (yield bus.busy): yield Tick("the_clock_domain")`, atleast if you have a synchronous design
<vup>
ok, is your design synchronous or combinatorial?
<FL4SHK>
synchronous
<FL4SHK>
can you take a peek at my paste.ee?
<vup>
then you should use Tick("domain_name") instead of Delay(), Delay() (with no argument) is the same as Settle() and just propagates combinatorial changes
<FL4SHK>
I see
<lkcl>
FL4SHK: so the simulator is basically a generator. ideally therefore you need to understand python generators
<FL4SHK>
I've used them, but it's been a while.
<lkcl>
line 18 and 19 you want
<lkcl>
bus_quot = int (yield bus.quot)
<FL4SHK>
It complained when I did that
<vup>
you need double (()) I think
<lkcl>
definitely don't try to convert nmigen signals to integers
<FL4SHK>
I have no other way to do my testing
<lkcl>
or ... hang on... you don't really want to be converting to int at all because the result should already be int
<FL4SHK>
vup: so what does double `(())` mean?
<vup>
int((yield bus.quot))
<FL4SHK>
no, I mean
<FL4SHK>
semantically
<lkcl>
if you mean literally (()) then that's a tuple with one item it it, which is a tuple
<FL4SHK>
no
<FL4SHK>
I don't mean literally (())
<FL4SHK>
I mean `int((yield bus.quot))`
<lkcl>
actually i'm wrong about that, it's just "brackets round a tuple"
<FL4SHK>
why do I need to pass a tuple to `int()`?
<vup>
the yield eats the brackets, there is probably a deeper reason for that, but not sure which
<lkcl>
the first brackets is not a tuple, it's likely to be some grammatical way to separate the yield from the thing-receiving-the-yield
<lkcl>
vup: yeah i know you need it but not why
<ktemkin>
(yield) marks yield as an expression rather than a statement
<lkcl>
the int conversion really should not be needed because the result from a yield from nmigen sims is, as best i know, always an int
<ktemkin>
which I assume was a decision made to avoid semantic ambiguity in cases like my_func(yield a, b)
<lkcl>
ktemkin: interesting, thank you, that makes a lot of sense
<ktemkin>
and yes, the int() isn't necessary, there
<FL4SHK>
I see
<lkcl>
having played with python AST a *lot* it confirms some things that puzzled me.
<vup>
oh yeah I forgot about the `yield a,b` syntax
<gkelly>
For Elaboratables, if the class expects a resource from the platform should the Elaboratable itself request that resource, or is it better to let the top-level request that resource and pass it to the elaboratable that consumes it? I think for testing it seems like the latter, but I wasn't sure if I was missing something.
<gkelly>
(Or maybe even instead of passing the resource just .eq it up when instantiating the module...)
<whitequark>
granted, pysim *is* slow
<lkcl>
whitequark: perhaps it's the 4.8ghz i9 processor, however we're still able to use pysim for libresoc unit test instruction simulations. admittedly they're like... only 1-5 instructions each, but it's not "intolerable"
<lkcl>
and it's a heck of a lot easier to interact with.
<whitequark>
it depends on the specific kind of design
<lkcl>
at one point, for a DIV FSM, i was doing things like value = dut.issuer.core.div.pipe0.alu.input.state
<lkcl>
and printing that out for debug purposes :)
<whitequark>
if you have a lot of delta cycles pysim is going to be a lot worse
<lkcl>
delta cycles?
* lkcl
intrigued to know what that means
<whitequark>
so, a HDL simulator has to schedule a parallel circuit as a serial execution sequence
<whitequark>
this can be done statically (in cxxrtl) or dynamically (in pysim and in rare cases cxxrtl)
emeb has joined #nmigen
<whitequark>
you're never supposed to have a true combinatorial loop, but there are lots of false combinatorial loops, which are caused by the simulation nets not being fine-grained enough, or simulation hierarchy not being flattened
<whitequark>
false combinatorial loops require evaluation until the circuit converges. each evaluation step is called a "delta cycle"
* lkcl
did an event-driven simulation (time-based) so kinda knows what you mean
<whitequark>
with cxxrtl you ideally get one delta cycle per step
<lkcl>
ok i think i understand
<whitequark>
with pysim you can get ... an arbitrary amount of delta cycles, each of which has varying cost (!)
<whitequark>
so pysim performance can be really hard to predict from simple circuit statistics
<lkcl>
interesting
<whitequark>
it's entirely possible (although by no means certain) that FL4SHK just hit a really bad case and you didn't
<whitequark>
or maybe your CPU is faster. or both
<whitequark>
the bottom line is that pysim is optimized for startup latency, not throughput
<whitequark>
so complaining about pysim's throughput is eminently reasonable! it's not trying to excel there
<whitequark>
or, rather, i picked the low-hanging fruit and stopped
<whitequark>
it's still faster than nmigen's simulator of course
<lkcl>
yes, that's particularly why i've stuck with it for the unit tests because they're running for only.... 2-6 cycles maximum.
<whitequark>
*migens
<whitequark>
yup, that sounds reasonable
<whitequark>
if your unit tests use the exact same gateware each time, cxxsim would still be a win
<lkcl>
ahh... in the "big" one (which combines all "individual Function Unit tests") yes it's the same dut
<whitequark>
so that's the one you should eventually migrate to cxxsim
<whitequark>
the others probably are better with pysim
<lkcl>
oh, which reminds me (sorry been so busy, haven't checked) is everything good with cesar[m]'s MCVEs for cxxsim, now?
<lkcl>
the Signal(64) or Signal(256) eq(1) was the last one i remember he found
<lkcl>
i did wonder what would happen there if we'd done x.eq(Const(1, 256))
<whitequark>
it doesn't have any particular need to live in-tree, right?
<lkcl>
ok. no not really, it's just that if it's in nmutil it'll likely remain low on peoples' radar
* cr1901_modern
has nothing to report. Next week I'll prob have something to look at tho.
<whitequark>
cool!
emily has quit [Quit: killed]
jfng has quit [Quit: killed]
gkelly has quit [Quit: killed]
cesar[m] has quit [Quit: killed]
cesar[m] has joined #nmigen
emily has joined #nmigen
jfng has joined #nmigen
gkelly has joined #nmigen
XgF has quit [Remote host closed the connection]
XgF has joined #nmigen
alanvgreen has joined #nmigen
alanvgreen has quit [Client Quit]
alanvgreen has joined #nmigen
chipmuenk has quit [Quit: chipmuenk]
<carlomaragno>
Hi there, can someone give me an high level overview on how to design an application with nmigen? I've made the simple stuff work (blinking an led, making a pwm) both on a simulation and on a real board, but now I'm kinda stuck. How do multiple clock domain work? How can I combine multiple modules using different clock domains?
<carlomaragno>
I've also tried reading LUNA, https://github.com/greatscottgadgets/luna trying to get some info on how to structure the code, but I find understanding high level design patterns very difficult
<carlomaragno>
I know that this is not an easy question, if you have any pointers, code, similar, feel free tell me
<Lofty>
Do you have any previous HDL experience?
<carlomaragno>
Yeha, verilog
<carlomaragno>
I'm not super familiar with the toolchain
<gkelly>
Oh thanks for that LUNA link I think that has a bunch of practices I've been trying to figure out.
<carlomaragno>
Yhea, it's a lot to take in, and reading the code i feel it's double the complexity: understanding what it's supposed to do, and understanding the high level pattern
<Lofty>
Well, I guess the main thing is that nMigen feels maybe a small step back from Verilog
<carlomaragno>
I can't say that yet. Not enough experience
<carlomaragno>
Why do you think that's the case?
<Lofty>
For example, instead of building a bunch of `always @(*)` blocks, you have that this is a combinational statement
<carlomaragno>
Yhea, but that's fairly simple to adjust to
<Lofty>
I just reread your message and I missed your questions, I'm sorry, carlomaragno
<carlomaragno>
a, chill
<Lofty>
I presume you're thinking of designs that really *do* need multiple clock domains that you can't use clock-enables for?
<lkcl>
carlomaragno: no not at all, no need to apologise: it's what i do. ignore the documentation (because initially it makes no sense no matter how well-written)
<lkcl>
later - retrospectively - i "get it" :)
<Lofty>
A lot of the time you don't really need `ClockSignal`. Clocks are generally implied in nMigen code, I think
<lkcl>
ok, so there's by default 2 domains established for you:
<Lofty>
Imagine all the sync code being inside an `always @(posedge <clk>)`
<lkcl>
m.d.comb and
<lkcl>
m.d.sync
<lkcl>
and just as Lofty says, m.d.sync is set up to be @always posedge clk
<lkcl>
but that's set up *by default* for you.
<carlomaragno>
Lofty:
<carlomaragno>
Lofty: bonelesss cpu seems pretty nice
<Lofty>
And nMigen modules are generally written to be synchronous to one clock
<lkcl>
here's the thing: you *can* add more ClockDomains. you can add one that's sync negedge for example
<Lofty>
Which is the implied one in the `sync` domain
<lkcl>
right
<lkcl>
here's the thing though
<lkcl>
you might decide "argh i don't want this code to be in the default clock sync domain" and you might think "argh i have to rewrite all that code, changing its domain, argh!"
<lkcl>
no you don't
<lkcl>
you use the "DomainRenamer" pattern
<carlomaragno>
lkcl:
<carlomaragno>
lkcl: thanks
<Lofty>
lkcl: I suppose we work in very different areas
<carlomaragno>
That's what i was wondering
<lkcl>
Lofty: :) i learn by doing rather than "reading manuals". it means there's huge gaps in my knowledge (which i can't explain to others) that get filled retrospectively
<Lofty>
But in nMigen "transformers" (they don't have an official name, but they transform HDL fragments) are pretty common
<Lofty>
Basically, "modules that operate on modules"
<carlomaragno>
lkcl: and if i got everything right i can drive a clock signal from another module, eg a clock divider
<Lofty>
carlomaragno: yep
<lkcl>
yyep
<Lofty>
Another common one is EnableInserter, which adds a clock enable to the `sync` domain
<lkcl>
the most likely source of these is (as examples) might be the nmigen-boards git repo
<carlomaragno>
So for example, i initialize a clock divider module as a submodule and then i can use the new clock domain defined inside it?
<miek>
well.. i know in verilog land you generally don't want to drive a clock signal from logic (as it won't use the proper clock routing), i don't know if nmigen does anything special here
<Lofty>
miek: nMigen leaves it up to the platform to define the right way to get a clock
<Lofty>
carlomaragno: your clock divider module would give its clock output, and then you'd use DomainRenamer to name the submodule's `sync` domain use that clock
<Lofty>
Although for dividing a clock you should really use a clock enable instead
<carlomaragno>
Lofty: clock enable is EnableInserter
<miek>
it's for a software-defined radio device. it sets up a "radio" domain to receive samples from another IC, does a bit of DSP, then puts it all in a FIFO to transfer to the "usb" domain for transfer
<carlomaragno>
lkcl: yes, +1 on the code clarity, even I can follow
* carlomaragno
somewhere in luna maybe?
<miek>
carlomaragno: that's done within the LUNA stack, by the clock_domain_generator
<carlomaragno>
Cool
<miek>
that's a good point to note actually: you don't necessarily need to setup your domains at the top level. even if it's down 20 submodules deep, nMigen will propagate them globally during elaboration
<carlomaragno>
So it's as if clock domains were globals?
<miek>
yep
<ktemkin>
IIRC, if they're not declared with local=True, yep
<miek>
oh, thanks - didn't know that was a thing :)
<ktemkin>
it's still weird to see LUNA being actually useful for other people
<ktemkin>
usually everyone runs from USB~ =P
<carlomaragno>
ktemkin: it is
<FL4SHK>
vup: needing to add a clock sounds like the problem
<carlomaragno>
I'm super sorry to bother you people, what if i need two different clock domains in the same module?
<FL4SHK>
miek: "It sounds like you need to do some smaller sanity checking..." yes, that's true
<FL4SHK>
carlomaragno: so that's possible, and it's something I've almost done
<FL4SHK>
make sure to check out `DomainRenamier`
<miek>
ktemkin: *someone* made it so easy that you just hook up a fifo to it and it works :)
<ktemkin>
miek: I try ^^
<ktemkin>
it'll be there with SuperSpeed soon too, hopefully
<Lofty>
FL4SHK: we already mentioned DomainRenamer
<ktemkin>
at least as much as something can look like itself
<Lofty>
nMigen ouroburos
<Lofty>
Wow.
<Lofty>
Like...
<Lofty>
This is the kind of code quality I aspire to one day achieve in my unearthly *mess* of functions
<d1b2>
<Qyriad> Her code really is inspirational
<ktemkin>
>.>
<whitequark>
ktemkin writes phenomenally good code
<whitequark>
i couldn't do better if i tried
<Lofty>
I've never actually looked at LUNA
<ktemkin>
what
<whitequark>
completely serious
<Lofty>
I'm *utterly* doomed then
<whitequark>
like, i could perhaps do a few things *differently* because i have a different big-picture view
<d1b2>
<Qyriad> The more code I write, the more I try to emulate her style, especially because it's just so damn readable
<whitequark>
and some subjective preferences
<whitequark>
but i know i could just rubberstamp her pull requests in review and it'll be fine
<Lofty>
I admit, I write kinda awful nMigen code
<ktemkin>
aww; thanks ^^
<Lofty>
(which was why you rewrote vendor.intel after I'd hacked the pieces together)
<whitequark>
don't worry, a lot of nmigen is kinda hacked together
<whitequark>
for example, we discussed the assert defs[sig] earlier
<whitequark>
that uh... really does need a rewrite
Asu has quit [Remote host closed the connection]
<whitequark>
in particular it's completely fine to add hacked together things when we just don't know how to do them nicely
<whitequark>
the only important thing is to make sure the hacks stay localized and don't become a long-term burden
<ktemkin>
mm, scaffolding is good
<whitequark>
i.e. that they can be rewritten later
<Lofty>
I suppose one of my axioms is to first get something working, then get something working well. I just...never focus long enough on something to achieve the latter.
<d1b2>
<DX-MON> I agree about Kate's work - that's some really nice easy-reading code
<Qyriad>
The last PR I submitted to ktemkin I was like "…are you sure it doesn't need more comments. maybe I should put more comments."
<lkcl>
Lofty, you've heard the adage, "plan to write 3 versions because that's what you;ll be doing anyway"? :)
<Lofty>
lkcl: mignite, my logic optimiser, is on rewrite number 5
<lkcl>
1st version: you don't know what you want, and don't know how to do it. second version, you know what you want, but not how to do it. finally you know both :)
<ktemkin>
Qyriad: if anything, I comment overmuch; I just write everything as if they were examples I was going to give to students
<Qyriad>
Lofty: _unlike_ Kate (seemingly) I do the same thing of "get it working first", because doing that is often the only way that I can understand the thing I'm working with to begin with. But I really really do try to make it clean before I commit it. At least I try to comment it, and I find that sometimes the process of commenting my code induces clean-up
<whitequark>
sometimes i implement a thing and then go back and split it into a series of commits introduing it piece-by-piece
<whitequark>
even though that's not how i wrote it
<Qyriad>
ktemkin: your comments aren't the stereotypical `int x = 5; // assign 5 to x` or anything. Yeah there's a lot of them, but they're all _meaningful_
<lkcl>
ktemkin: interesting to hear that the primary intent behind what you do is not to "code" but to "communicate"
<Qyriad>
Yeah I do that all the time, whitequark
<whitequark>
it's also a good time to refactor stuff
<d1b2>
<DX-MON> I aim for my code to communicate too, though I don't use comments nearly as often as I should
<ktemkin>
I refactor constantly; it freaks Qyriad out
<d1b2>
<DX-MON> it helps once you get something to work to stop and step back a moment and try to figure out why, as that too can sometimes net you a nice "ohh" moment and some cleanup
<ktemkin>
I'll write several paragraphs of code, ponder for a second, and then just delete them all
<Qyriad>
No no, the refactoring doesn't freak me out, it's that you refactor by _deleting_ instead of commenting out, and _then_ deleting once the refactored version works
<lkcl>
whitequark: ngggh i got asked to do that on something that had critical circular dependencies.
<Qyriad>
I always comment out rather than delete until it's clean-up time
<d1b2>
<DX-MON> Qyriad: that's totally what Git and branches are for though, right?
<ktemkin>
I dunno; for me it's less about refining the code and more about refining my mental picture
<d1b2>
<DX-MON> I like to remove my old way of thinking about the problem as I go as it helps me emerge a better description of what I'm trying to get done and helps present to me better ways to restructure the code
<Qyriad>
DX-MON: generally yeah, but if there's multiple moving parts you're refactoring at once and want to switch out individually, using Git to manage that during refactoring becomes really clumsy
<d1b2>
<DX-MON> and reduces my mental baggage around it
<Qyriad>
Of course, you could argue you _shouldn't_ refactor multiple things at once, and that's probably true, but refactoring one thing often brings your attention to other things too
<Qyriad>
ktemkin: I lose the mental picture without a reference. If I delete the code I'll have to figure out how I did it all over again lol
<whitequark>
ktemkin: oh i do that too
<whitequark>
in my view, software is first about building a model of some system you care about, then communicating that model through code, comments, documentation, and so on to other humans, and only thirdly about actually running something in an interpreter or w/e
<carlomaragno>
Lofty: what did you mean with >your clock divider module would give its clock output
<Qyriad>
Kate'll have me watch her as she does some nmigen stuff sometimes, and then all of the sudden she'll just delete an entire function and I'll be like "!! What did that even do I don't even remember now!"
<d1b2>
<Attie> hi everyone... is there a way to get a value to saturate (and not overflow / wrap)?
<d1b2>
<Attie> or is the best option to test for this before incrementing
<Qyriad>
whitequark: I admire your and Kate's ability to think about it that way / in that order
<d1b2>
<Attie> (and if the latter, then is there a generic "is the maximum value" test?)
<whitequark>
the order in which i think about it is actually mixed
<Lofty>
carlomaragno: basically exactly that. DomainRenamer needs a clock signal, and the clock divider module would take in a clock signal and produce a different clock signal which you feed to DomainRenamer
<whitequark>
i care about having a nice model that can be communicated well, but i also care (perhaps too much) about it corresponding to nice code
<whitequark>
or maybe not too much, people seem to like the way i write code even if i didn't document it
<carlomaragno>
Lofty: so in the clock divider i need to define a new clock domain and add it to the module domains and than somehow drive the clk line, right?
<Lofty>
While I could go through something like Boneless and point out all the things I find useful about how you wrote things, I'm not sure if that would be particularly useful :P
<Lofty>
carlomaragno: could you write some Verilog code as an example? Maybe that's the clearest way of comparing
<carlomaragno>
sure, wait a sec
<ktemkin>
carlomaragno: ClockSignal("<domain name>") gets a reference to the clock signal for a given domain; you can assign to it like any other signal
<ktemkin>
so if you have a domain called clk48, and you want to create a clk12 domain, you can do something akin to `m.domains.clk12 = ClockDomain()` / `m.d.comb += ClockSignal("clk12").eq(my_12mhz_divided_clk)`
<ktemkin>
and then work with it like any other domain; e.g. `m.d.clk12 += a.eq(b)`
<Lofty>
...Should I feel bad that my personal approach is to avoid multiple domains in a module?
<ktemkin>
nope
<Lofty>
Except for like a top-level module
<ktemkin>
that's pretty reasonable, typically
<Lofty>
*top-level platform-specific module
<ktemkin>
generically, avoiding multiple clock domains whenever you can avoids a lot of footguns
<Lofty>
It also makes meeting timing a bit painful in some situations, but eh
<ktemkin>
whitequark: I think I only realized recently how much nMigen's use of context-managers feels Ruby-inspired
<whitequark>
strangely, it's not actually Ruby-inspired
<whitequark>
but yeah
<whitequark>
I *wish* I could run a `with` body multiple (or zero) times
<ktemkin>
specifically, I found myself doing something similar, and only realized afterwards that it was likely the time I spent writing Ruby that left me with the feeling that I should be able to design APIs that get the host language the hell out of the way
<carlomaragno>
Lofty, lkcl, ktemkin it works
<carlomaragno>
I wrote it in verilog and found some bugs in my nmigen code, than I took kate's clock domain example and it works magically