ChanServ changed the topic of #nmigen to: nMigen hardware description language · code at · logs at
daveshah has quit [Ping timeout: 240 seconds]
Degi_ has joined #nmigen
rohitksingh has joined #nmigen
Degi has quit [Ping timeout: 256 seconds]
Degi_ is now known as Degi
daveshah has joined #nmigen
ktemkin has quit [Ping timeout: 256 seconds]
ktemkin has joined #nmigen
rohitksingh has quit [Ping timeout: 260 seconds]
rohitksingh has joined #nmigen
rohitksingh has quit [Ping timeout: 260 seconds]
rohitksingh has joined #nmigen
futarisIRCcloud has quit [Quit: Connection closed for inactivity]
rohitksingh has quit [Ping timeout: 260 seconds]
rohitksingh has joined #nmigen
Asu has joined #nmigen
<whitequark> awygle: yes, but it's not very elegant
<whitequark> take a look at test_FSM_basic
rohitksingh has quit [Ping timeout: 260 seconds]
lkcl_ has quit [Quit: Leaving]
lkcl_ has joined #nmigen
nengel has quit [Ping timeout: 240 seconds]
chipmuenk has joined #nmigen
chipmuenk has quit [Quit: chipmuenk]
proteus-guy has quit [Ping timeout: 256 seconds]
chipmuenk has joined #nmigen
<mithro> ktemkin: Have you looked at lambdaconcepts USB2.0 core at ?
nengel has joined #nmigen
<ktemkin> mithro: yep, I have
<mithro> What is your thoughts on it?
<ktemkin> it has a relatively small codebase; but I found it more difficult to follow than it needs to be; and found many of its components are relatively tightly coupled together
<ktemkin> and it's not particularly complete
tnt has joined #nmigen
<ktemkin> I didn't get to the point of actually trying it on a board; but when I read the codebase, it looked like it'd been stuck in an "isn't complete enough to do a full enumeration" state for a while
<ktemkin> in short: it looks neat, but it also looks like more of an experiment than an application-targeted library; and a few months ago I decided it'd be easier/cleaner to write a from-scratch stack than to try to adapt it
<whitequark> I thought it has a few interesting approaches, personally, but my perspective is more that of a language designer than USB designer, here
<whitequark> it seems like something that may be considered when designing stream operations
<ktemkin> whitequark: is there anything in particular that you think should be taken away from it? I'm not above stealing good ideas in my own implementations :)
<whitequark> I have almost no recollection of looking at it, beyond what I mentioned above
<whitequark> I trust that you'll recognize any steal-able ideas; my opinion on what is good might not be that useful as I never implemented USB in hardware
<ktemkin> mm
<ktemkin> by the way -- at some point, when I'm a bit further on the LUNA stack, I might ask for your opinions on how I'm using nMigen; if you wind up having the time/drive/spoons to take a look
<whitequark> yup, I can absolutely do that
rohitksingh has joined #nmigen
<whitequark> it's important for me as well, since language design needs to be driven by feedback
<ktemkin> yeah, and given the relative dearth of nMigen projects, any significant projects that do exist are inevitably going to wind up referenced as example code
<ktemkin> so it's important to me not to set awkward or ill-advised idioms :)
<whitequark> indeed
<ZirconiumX> TFW you only have insignificant projects :( (/s)
<ktemkin> in a cosmic sense, aren't all of our projects insignificant?~
<whitequark> dust in the wind
<ZirconiumX> To be fair, I'm pretty happy with what I *do* have
<ZirconiumX> I suppose one of the biggest hurdles I've had is pipelining. When stuff gets sufficiently big it becomes pretty painful to pipeline stuff.
<ZirconiumX> ~150 lines of nothing but connecting pipeline stages
<whitequark> yes, this is the kind of thing #213 is supposed to improve
<ZirconiumX> Ah, I see
<ZirconiumX> Minerva used Record.connect IIRC
<whitequark> yep
<ktemkin> connecting signals from place to place has always been the bane of sanity
<awygle> ugh yosys' lack of asymmetric memories strikes again
<awygle> whitequark: if i use a giant Signal instead of a Memory will i hit that "yosys also hates giant signals" bug? what's the order of magnitude at which that becomes a problem?
<whitequark> iirc verilog mandates at least 100kbit or 1mbit
<ZirconiumX> That's kind of terrifying
<whitequark> but ... also iirc yosys wasn't really working well with signals that large
<awygle> okay this would only be ~12kbit so, sounds feasible?
<whitequark> yes
<awygle> sweet
<whitequark> pysim will likely get somewhat slower but not catastrophically
<whitequark> since it will start using a lot more bigints
<awygle> how do you feel about the nmigen Memory concept generally btw? it feels like a direct mapping of the yosys concept, so i'm curious if you're happy with it or if it's practicality-driven
<whitequark> it's a bit of both
<ZirconiumX> I'm pretty happy with Memory, I think. I trip up on how to instantiate ports sometimes
<whitequark> it's not bad, and it has nice features, and it matches Migen's old memories reasonably well
<whitequark> the main thing that's an issue is transparency
<ZirconiumX> ~~and for some reason VS Code's suggestion system does not play nice with nMigen~~
<awygle> acknowledging that i'm coming at this with a bias, i actually kind of like verilog's concept that memories are not really distinguishable from signals at a language level (pretend for the moment this is true, i know verilog is riddled with cases where it is not)
<whitequark> the problem with that concept is primarily that toolchains have to pattern match to infer actual hardware primitives
<awygle> i think starting with a clean sheet of paper i'd have made Memory part of the standard lib instead of the language, and provided a way to say "i demand this signal become a block RAM - if it doesn't, please error out" (or vice versa)
<whitequark> culminating in xilinx's "use this exact code or we don't guarantee you actually get the memory you want" approach
<whitequark> fun fact: if you build certain migen/nmigen cpus on xilinx platforms, xilinx toolchains will find a register somewhere and fuse it into the memory and turn your BRAM into LUTRAM with design breaking change of semantics
<whitequark> multiple people have hit this bug repeatedly
<awygle> i often find myself wishing for these kinds of next-abstraction-down facilities though
<awygle> ugh. that's gross
<whitequark> there is no way to fix this with verilog's approach
<whitequark> in general, as you may have noticed, nmigen doesn't believe in inference
<whitequark> if you want something, state it explicitly
<daveshah> When someone fixes Yosys' not mapping registers with init values into BRAM bug; then Yosys will have this behaviour too
<awygle> sure, i don't disagree with that either necessarily
<whitequark> the place where verilog's approach really shines is quasi-behavioral rtl code
<whitequark> such as formal testcases
<whitequark> where the fact that you have a zillion async memory read ports is just fine
<awygle> yes lol
<whitequark> well... except for when those form a combinatorial loop through a $mem cell and yosys breaks
<awygle> idk, i often find myself thinking about this kind of thing. as a general language feature of either software or hardware description languages i often want to operate at the level of my desired semantics while _also_ placing constraints on what an acceptable implementation looks like.
<ktemkin> awygle: generally, I find that's actually one of the strengths of a toolkit like nmigen
<whitequark> i agree in general but i think for memories the only thing we can reasonably do is to add an abstraction on top of an rtl language that has a well-defined lowering into rtl
<awygle> like in rust i want to be able to say "if this closure doesn't get inlined out, please let me know", or "if a code change causes the size of this structure to triple, at least make me confirm i did that on purpose"
<ktemkin> awygle: instead of building an abstract behavioral description and inferring down, you wind up creating building blocks with explicitly the implementations you want, and then building -up-
<ktemkin> you can still wind up working at the same level of abstraction; just on top of a foundation that's been solidly created underneath you
<whitequark> awygle: ^ exactly what i think; the reason i'm working on nmigen is to have a foundation i can build upon
<awygle> hm i don't see that as nmigen-specific tbh? like that's how i write verilog as well
<whitequark> a foundation that actually maps to modern synchronous logic in a well-defined and well-known way
<awygle> the difference is just that nmigen's stock provided stuff is more robust
<ktemkin> awygle: the difference is with something like Verilog, you're speaking a behavioral incantation and trying to coax the synth tool into inferring what you want
<awygle> you don't _have_ to be doing that. you can manually instantiate primitives if you want to. but i get your point.
<whitequark> verilog isn't an rtl language; vendor interpretations of verilog are, but there's as many of those as there are vendors
<whitequark> nmigen is basically the c89 of hdl languages
<ktemkin> awygle: you can, but there are as many primitives as there are vendors
<awygle> i think _my_ point is that in nmigen a decision is made for you - care about the primitives - and in verilog a decision is (arguably) made for you - don't care about the primitives - and i want to be able to mix and match
<awygle> "it is important to me that my register file is in block RAM. it is not important to me that any given FIFO is in block RAM. please satisfy these constraints or give me an error"
<awygle> that kind of thing
<whitequark> memories do not guarantee you block RAM
<awygle> but it's a bit magical thinking
<awygle> i'll freely admit
<whitequark> by default the synthesizer will pick whatever is suitable; BRAM, LUTRAM and FFRAM are all options
<ZirconiumX> It would need `ramstyle` attributes for that, right?
<whitequark> yes
<ZirconiumX> Then the former is `ramstyle=bram` or whatever the bloody syntax is
<whitequark> what nmigen *does* guarantee you (or at least tries to; right now that doesn't always happen) is that the semantics of Memory will be exactly what you specified no matter what implementation style is eventually chosen
<awygle> anyway i should go back to actually writing HDL instead of bikeshedding it :p thanks for the disussion as always, it continues to be interesting
<whitequark> for that it has to (or rather, will have to, this doesn't currently happen unfortunately) adapt the netlist to each toolchain
<ZirconiumX> Would that mean instantiation of RAM primitives? Or custom RTLIL to output The Right Thing in verilog?
<whitequark> just setting the right attribute
<whitequark> and in case of yosys, also implementing the right attribute
<whitequark> which i did for ice40 and ecp5
<whitequark> and then a whole lot of hours sunk into testing that it really does what it needs to
<whitequark> awygle: one thing i think we can improve is simplifying the use of memories in formal code
<whitequark> but for the rest, i'm not sure what can be done
<whitequark> it is very rare that you can use either a synchronous or asynchronous read port, for example
<whitequark> you could arguably get rid of the FIFOBuffered variants somehow if you had that functionality, but I never managed to actually implement something like that
<whitequark> awygle: oh btw, regarding asymmetrical memories
<whitequark> I discussed what they would look like in RTLIL
<whitequark> and the answer is that they will likely look like macros you have to instantiate
<whitequark> behavioral macros, not unlike what xilinx asks you to do
<whitequark> so... I think asymmetric memories can already be implemented purely on nMigen side
<whitequark> what it would take is basically cutting off low address bits and feeding them into .part_select()
<whitequark> for reads
<whitequark> for writes, replicating the written data a few times and then using one-hot enable
<ktemkin> tangentially related to the above: one thing I'm very much enjoying about nmigen [vs e.g. verilog] is my increased ability to trust simulation output
<whitequark> nice
<whitequark> one thing i was thinking about adding to pysim is a race condition detector for async logic
<whitequark> but it's a fairly sizable research project
proteus-guy has joined #nmigen
<ktemkin> I'm just happy that my simulations mean something; as opposed to the horror days of having xst 'optimize' my RTL into things that shouldn't exist
<whitequark> oh!
<whitequark> right, yeah, that
<awygle> whitequark: oh cool, i was actually thinking of doing that but assumed it would synth into a horror show
<whitequark> awygle: no, it would actually work as a perfectly good polyfill in meantime
<whitequark> on FPGAs without asymmetric memories that is
proteus-guy has quit [Max SendQ exceeded]
<awygle> self.yak_prio_heap.push("asymmetrical memories", Priority.MID)
<awygle> (by which i mean i will endeavour to send a PR implementing this as soon as i clear a couple of higher-priority tasks)
<whitequark> yup, understandable
<whitequark> i think by far the worst part will be detecting all the edge cases
<whitequark> like cutting off more address bits that you have
<awygle> yeah, certainly
<awygle> "this side is 8 bits, this side is 16" - probably easy
<awygle> "this side is 3 bits, this side is 7" - probably more difficult
<whitequark> impossible, i believe
<awygle> i hope so lol
<whitequark> it only makes sense to support power-of-2 memory port "fragmentation"
<awygle> ok, good, that's a much easier problem
<whitequark> the eventual data width doesn't have to be a power-of-2 but the factor by which you divide the memory width does
<awygle> mhm. power-of-two ratios
<whitequark> in theory you could have 1:3 memories or something like that
<whitequark> so i think the argument that says by how much you fragment the memory should be a full integer divisor that is then checked to be a power of 2
<whitequark> rather than just a power of 2 to make all other ratios unrepresentable
<whitequark> hmm
<awygle> i mean you _can_ do coprime 3:7 memories. it's just.... weird.
<whitequark> i don't think you can
<whitequark> well
<whitequark> you'd have to use multiple ports per smaller port
<whitequark> which doesn't match nmigen's model where if you request a read port, you get exactly one read port
<awygle> mm
<ZirconiumX> Why not just integer factors? one side being, say, 2, and another being 10.
<awygle> we should file an issue, and bikeshed there
<whitequark> ah, i know of a very good reason to not have arbitrary integer factors
<awygle> so we don't lose context
<ZirconiumX> (asking out of lack of knowledge)
<whitequark> the reason is that you'd then have to do a combinatorial division to get the address
<whitequark> which will synthesize to something horrifying
<whitequark> like... imagine a netlist for dividing a 16 bit value by 3
<whitequark> now imagine that in your critical path
<awygle> this is true
<awygle> the picture i had in my head was a fifo, where you can do something at least quasi-sensible, but for arbitrary memories it would certainly end up a mess
<whitequark> plus the general approach of "pass this argument to chop off this many address bits" is very nice
<daveshah> Yeah, I remember doing 32:40 conversion (producing an output word 4 in every 5 cycles) for some MIPI stuff
<ZirconiumX> ...I was going under the assumption here that we were talking in terms of *bits*
<daveshah> But that wasn't even really a FIFO, more a shifter
<awygle> ZirconiumX: do you think you could file this as an issue and summarize the discussion we've had so far? if not i'll do it but it'll have to wait a bit
<ZirconiumX> I'm not entirely sure I understand this quite well enough to summarise it
<awygle> totally fair, i'll take care of it then
awygle is now known as awygle_working
<ZirconiumX> aworkgle
<whitequark> oh you can definitely do it, yosys will even synthesize it if you ask
<whitequark> (that website doesn't resolve for me)
<whitequark> the problem is that it will create more problems for you than it will solve
<whitequark> you are better off making some sort of gearbox instead
<whitequark> like what daveshah did
<daveshah> Interestingly, on a similar modulo by 6 synthesis issue, abc9 gave about 2 orders of magnitude smaller output than abc
<daveshah> As abc9 doesn't do any very fancy carry optimisations, this means there is a lot of redundancy in what Yosys is synthesising
<whitequark> hell__: right, so, that's... three comb adders, in sequence, in your memory address path?
<whitequark> you're better off doing virtually anything else than that
proteus-guy has joined #nmigen
<sorear> what are we trying to do here? synthesize a 768*5 BRAM using a 256*15 BRAM?
<whitequark> more like, have a FIFO with 15 bit input but 5 bit output
<whitequark> so you're looking at the same BRAM in two different ways
<sorear> sounds like you need a state machine on the read side that reads every word three times, only increments the read pointer on every 3rd deq, and muxes the outputs
<whitequark> yes, that's the sane way to do it
<hell__> o_O
<whitequark> but if it was not 1:3 but 1:4, you could do it more directly, by chopping off the low 2 bits of address and using them to mux
<whitequark> this is useful because hardware supports asymmetric BRAMs sometimes
<whitequark> xilinx does, you can have a 36 bit port and a 9 bit port natively
<whitequark> with no fabric logic
<ZirconiumX> Intel memories support a *lot* of asymmetry
<daveshah> Pretty much all FPGAs do
<awygle_working> i forget, what's the right way to phrase `with m.If(x == y && z == w):` ?
<whitequark> i think even ice40 does
<daveshah> Even iCE40 does
<daveshah> Yep
<ZirconiumX> awygle_working: single &
<whitequark> awygle_working: with m.If((x == y) & (z == w)):
<awygle_working> ah, needed parens
<awygle_working> thanks
<whitequark> we should maybe have a diagnostic for x == y & z == w
<ZirconiumX> Or equally |
<whitequark> sure, any binop
<whitequark> hm
<whitequark> actually no, you're right, just ^|&
<whitequark> `x == y + z == w` is normal python code
<ZirconiumX> That horrifies me
<ZirconiumX> My brain can't parse it
<whitequark> it's like `x == y == z`
<whitequark> chained comparisons
<whitequark> that diagnostic will be a real pain to write, i might have to reparse the code
<whitequark> hrm
<ZirconiumX> It seems like something that would be done at an "AST" level
<ZirconiumX> (I'm not sure if nMigen has something like that, thus the scare quotes)
<whitequark> nmigen has its own ast but it does not directly have access to python ast
<whitequark> hence the reparsing
<ZirconiumX> ..I'm looking at the Python operator precedence, and it seems like x == y & z == w is treated as (x == y) & (z == w). Am I missing something?
<ZirconiumX> ...The precedence table is lowest to highest
<ZirconiumX> That's what I'm missing.
<whitequark> >>> 1 == 1 & 3 == 3
<whitequark> False
chipmuenk has quit [Quit: chipmuenk]
<ZirconiumX> So the AST would in the wrong case be (in lisp notation) (== (== 1 (& 1 3) 3), right?
<ZirconiumX> *(== (== 1 (& 1 3)) 3)
<whitequark> yeah
<whitequark> you can definitely just detect that too
<whitequark> it's just more prone to false positives because you could be constructing that expression not all at once
<ZirconiumX> ...More generally the two nested equalities look suspicious to me
<ZirconiumX> a == b == c
<ZirconiumX> Looks kinda odd to be honest
<whitequark> it is a feature of python
<whitequark> one that guido is personally proud of, iirc
<whitequark> "comparisons look like they do in math"
<ZirconiumX> Okay, from that point of view, I kinda get it
<whitequark> so i cna't just flag it wholesale
<ZirconiumX> So would that be syntax sugar for "a == b and b == c"
<whitequark> i think it's for a == b and a == c, actually
<whitequark> but i'm not sure
<mwk> yes, except the middle expression is only evaluated once
<whitequark> this is one of the reasons why this syntax isn't such a great idea in retrospect
<whitequark> oh
<whitequark> oh ok
<mwk> whitequark: it's a == b and b == c btw
<whitequark> gotcha
<mwk> (otherwise it wouldn't work for a < b < c)
<whitequark> right of course
NF6X has joined #nmigen
<ZirconiumX> Well, `a == b == c` doesn't actually elaborate (attempted to convert nMigen value to boolean)
<whitequark> hm
<ZirconiumX> (should it?)
<whitequark> there was an issue about it...
<ZirconiumX> Actually as far as I can tell, `a cmp b cmp c` doesn't elaborate
<whitequark> there definitely was an issue about it
<whitequark> but i can't find it
<NF6X> Hi, everybody! Is there any good introductory documentation about nMigen for somebody who knows Verilog/VHDL and has no Migen experience?
<NF6X> Thanks, I'll take a look at that. I heard about nMigen when the RCR Podcast mentioned Robert Baruch's project using it to implement a 6800 CPU, but I'm sequestered at home with a lousy cell phone tether for Internet, so I don' want to watch his video series right now.
<ZirconiumX> Yeah, this is just Markdown
<ZirconiumX> Which GitHub formats rather nicely.
<NF6X> Cool. I used to be an actual ASIC engineer a long time ago. I'm quite interested in SymbiFlow with Lattice parts as an alternative to Xilinx's massive toolchain. Since Python has become my go-to language, I figure that nMigen is worth my consideration.
<ZirconiumX> There is *slow* progress on a FOSS Xilinx toolchain
<ZirconiumX> And *glacial* progress on a FOSS Intel toolchain
<whitequark> NF6X: perhaps the single most important thing there is to know about nMigen is that it is, confidently, RTL language
<whitequark> *an RTL language
<whitequark> it doesn't have behavioral parts. you use nMigen to define a netlist (that, perhaps, later gets optimized by the toolchain downstream). if you want behavioral code, there's the entirety of Python right there.
<ZirconiumX> I suppose the biggest difference between nMigen and Verilog is that in Verilog is a "how" language (how to implement an FSM, how to implement a memory), and nMigen is a "what" language (you want "an FSM", or "a Memory"
<ZirconiumX> )
<NF6X> I look forward to a time when a noob might start targeting Zynq without such a step-function learning curve! For now, for my home projects (and maybe even some work projects?) I think I could do a lot with inexpensive little Lattice parts.
<ZirconiumX> I mean, the ECP5 is quite capable
* ZirconiumX remembers they need to modify the nMigen code for >= Cyclone 10GX
* ZirconiumX is rather lazy
<NF6X> Yes, and the Lattice parts are available in friendlier packages and lower price points than Xilinx, too. I had to design an 18-layer board around an Artix device in an 1174 (IIRC) ball package with all but about 2-3 ball routed out. Shudder!
<whitequark> ouch, 18 layers!
<NF6X> Yup. On that one I had to throw in the towel and hire an outside consultant to finish the routing. Humbling, but also very educational. Their routing was *art*!
<hell__> doing that is probably enough for one to become a core router
<NF6X> Well, I'm not put off by nMigen being an RTL language. I'm more of a hardware guy than a software guy, and I've always tended to lean more towards the structural side than the behavioral side in my HDL work.
<ZirconiumX> Have you ever seen any nMigen code, or are you going in blind?
<NF6X> Blind!
<ZirconiumX> Do you *want* to?
<ZirconiumX> We have a handful of conventions that might help
<mwk> ZirconiumX: "a == b == c" will never work (as in, will never be able to return an nmigen expression reasonably) due to how python works
<whitequark> yeah, i think that what the issue was about
<NF6X> I peeked at the examples on the nMigen github. I'm happy to learn any sort of best practices and the like.
<whitequark> so we can only raise a more specific diagnostic than that
<mwk> because it compiles down to "a == b and b == c", and `and` has to compute the boolean value of its first argument *right now*
<NF6X> I literally heard of nMigen on a podcast less than a half hour ago. :)
<ZirconiumX> Well, unlike Verilog, nMigen (presently) does not have any concept of signal directions (input/output/inout)
<NF6X> Really? Weird!
<ZirconiumX> So we generally mark those by calling them i_/o_/io_
<whitequark> it's inherited from Migen
<whitequark> nMigen has to compile nearly all Migen code, which constrains the things I can require
<mwk> there were some PEPs proposing changes to how this works IIRC (nmigen is not the only one with this problem), but they never went anywhere AFAIR
<whitequark> there *has* to be a mode where signal direction is inferred from use
<whitequark> (Migen did not have modules; it would produce a single flat Verilog module, so the lack of directions was natural)
<NF6X> Will I pick up these conventions from Robert's tutorial?
<ZirconiumX> I don't know, I'm not sure it existed when I picked up nMigen
<ZirconiumX> I picked them up from reading whitequark's Boneless CPU
<whitequark> it's not quite a strict convention
<whitequark> I just use it when it makes clear what's going on
<whitequark> in FIFOs for example we have r_* signals and w_* signals
<NF6X> Will there eventually be an nMigen analog of PEP8?
<ZirconiumX> <--- here's my kind of messy pipelined chess move generator
Asuu has joined #nmigen
<whitequark> there will be
<whitequark> documentation and guidelines is something I'm focusing on in the coming months
<NF6X> Cool. I try to PEP8 as much as I can.
<ZirconiumX> Some rules I generally stick to are that private signals should *always* be declared in elaborate()
<ZirconiumX> Since stuff put in self is world-viewable
<mwk> hm, PEP 532 and 535 deal with the possibility of overloading and/or operators, both are deferred
<NF6X> I'm guessing that means that the private signals don't exist outside the scope of the place where they're private, right?
<ZirconiumX> Yep
<NF6X> Makes sense to me.
<whitequark> yep. that can present an issue when testing your designs sometimes
<whitequark> so occasionally one has to make them accessible
<whitequark> the advantage of creating them in .elaborate() is that if your module has e.g. parameterizable width, or is otherwise "set up", you can defer deciding on the width of some signals until later
<NF6X> Oh, yeah. Writing test benches that don't need to probe down inside logic is a problem even in Verilog/VHDL world. Even when you are allowed to do it, I prefer to structure the hardware and tests where it's not necessary.
<whitequark> yup
<ZirconiumX> Technically I have an incredibly bad habit in this bit of code: I'm using `reset_less=True` in my `Signal` declarations. By default nMigen builds a reset network to reinitialise everything (which is good!) but I opt-out to save routing resources.
Asu has quit [Ping timeout: 250 seconds]
<whitequark> why not use a reset_less domain instead?
<ZirconiumX> "because it didn't occur to me that that was possible"
<whitequark> ah
<NF6X> After all, if I need to probe down inside blocks in simulation, how the heck am I going to debug the real chip when it arrives from the foundry? To me as a designer, this gives me insight into the problems the poor slob who has to test it will have (i.e., future-me).
<whitequark> nmigen isn't quite ready for ASIC work, primarily because it (very deliberately) has no 'x
<whitequark> this is actually a really complicated topic that i think many HDL designers don't fully appreciate the complexity of
<ZirconiumX> I think the aim was an uninitialised state rather than 'x which means a lot of things
<whitequark> 'x means three things in verilog
<NF6X> I'm still in the mindset of trying to provide observability that the next engineer using the design (who may or may not be me) will need. This extends to anticipating what test points the firmware engineer is going to need on board designs.
<whitequark> "indeterminate but valid value", "indeterminate and illegal value" (e.g. timing violation), and "an indication for toolchain that any value may be used for optimization purposes"
<whitequark> these three things are completely separate and should not be treated in the same way
<NF6X> agreed
<whitequark> unfortunately, they are
<ZirconiumX> The third one is the ? in the matching syntax of nMigen, right? Like Value.matches() and m.Case()
<whitequark> consider emitting verilog that models an uninitialized memory: if it gets filled with 'x, then those 'x can be propagated out of memory
<whitequark> but once they are propagated out of memory, each immediate use of a 'x can be resolved by the toolchain to mean a different value by the third rule
<NF6X> I'm having flashbacks to so many simulations with so many red traces from X propagation.
<whitequark> ZirconiumX: no, that's a wildcard
<whitequark> NF6X: right, so, nmigen not having 'x currently is both a blessing and a curse
<whitequark> a blessing because (provided you do not have CDC violations), every nmigen design on FPGA is fully deterministic
<whitequark> and always fully matches simulation
<whitequark> (and we'll do something about CDC violations too, but that's for the future)
<ktemkin> clock domain crossing
<ZirconiumX> clock-domain crossing
<whitequark> a curse because sometimes, you really do want to know that an indeterminate value is propagating through your design
<NF6X> Ah.
<ZirconiumX> Dammit
<whitequark> (and to a lesser degree, because sometimes you really do want the toolchain to have the freedom to optimize with 'x)
<NF6X> X propagation in Verilog is also a blessing and curse. Just a different balance. Sometimes it shows you real problems, and sometimes it obscures normal behavior.
<whitequark> we do plan to add limited X-propagation to nmigen to model ASIC memories
<whitequark> which as you know do not come initialized or zeroed after reset
<ktemkin> something something, galaxy brain: doing k-maps to optimize away all those gates in your LUT4s
<whitequark> ktemkin: these days I would get an SMT solver to write an instruction decoder rather than (ab)using 'x to do it
<whitequark> it would do a much better job *and* it will prove the result is correct
<whitequark> of course, people will still use excel for it in 50 years...
<whitequark> one of these days i want to golf a microcoded 8051 using an SMT solver to derive the minimal controlling expressions
<NF6X> Well, thanks for the friendly welcome and pointing me towards tutorial stuff, folks. I may be back when I understand more of what's being talked about. Cheers!
<ktemkin> a computer? to do -my- k-maps?
<whitequark> happy to help :)
<ktemkin> whatever did I go to grad school for?
<whitequark> lol
<ktemkin> and I've almost figured out how to draw these things in a sufficiently-dimensional hypercube as to let me start optimizing away whole LUT4s~
NF6X has quit []
* ZirconiumX sometimes still breaks out Espresso for minimal functions
<ZirconiumX> I don't know if this makes me a hipster or ancient
<whitequark> *lights cigarette* i haven't heard that name in a long time
<ktemkin> I kind of love how jedi has become old
<whitequark> i mean last time i used it was uh two months ago
<whitequark> implicitly
<ZirconiumX> Logic Friday v. 1.1.4
<whitequark> in a toolchain from 1999
<ZirconiumX> Copyright (C) 2006-2012 Steve Rickman
<whitequark> damn, just realized some silicon i bought this january has a mask set that can vote
<whitequark> in any country
<ZirconiumX> ...This may be a bit lewd, but I posted a picture of my PS2's motherboard with the caption "hot and noisy teenager exposes all"
<whitequark> lol
<ZirconiumX> I am mature.
<mwk> lol
<ktemkin> I just had to check to see if espresso was older than I am
<ZirconiumX> Logic Friday says 1988-1993 RotUoC
<ktemkin> the README is priceless
<ktemkin> "Please do not forward Espresso questions to UC Berkeley students, faculty, or staff as there is nobody here who can answer them."
<whitequark> "and hasn't been for thirty years"?
* ZirconiumX wonders how long it'll be before ABC meets that fate, still in 0.1
<awygle_working> -10 years
<whitequark> ouch
<sorear> how do ABC version numbers work?
<ktemkin> hey, don't be harsh; it's not like it's Synplify
<whitequark> *ouch*
<ZirconiumX> ABC has developed its own special versioning scheme, sorear
<ZirconiumX> It doesn't matter what version it is, it's 0.1
<whitequark> is... synplify that bad?
<whitequark> it didn't strike me as pathologically bad so far
<ZirconiumX> It's zero-ver but zero-point-one-ver
<ktemkin> whitequark: to be fair, I last used it in 2010-ish, but I assume it looks exactly the same
<whitequark> haven't you synthesized a bunch of things for ECP5 in Diamond?
<ktemkin> only incidentally while fuzzing bitstream stuff
<whitequark> ah, right
<whitequark> i've lost count how many times i have found synplify embedded *somewhere*
<whitequark> by this point it's kind of like
<ktemkin> when I last used it, synplify thought it knew better than me; and the flags they had that overrode its heuristics were ignored
<whitequark> not entirely unlike flexlm which it usually also comes with...
<whitequark> ouch
<whitequark> the flags seem to subtly differ from vendor to vendor
<whitequark> lattice has, i think, at least two different versions of synplify they ship
<whitequark> with incompatible options for basic things
<ktemkin> so you had to go and find whatever gods-forsaken tcl script had broken over the Eternities and fix it
<daveshah> Quite possibly a third slightly different version in Radiant too
<whitequark> there's "fire and forget" and i feel like synplify is more like "vendor and regret"
<ZirconiumX> Oh no
<ZirconiumX> wq: that's great
<whitequark> whereas "fire and forget" is what you do with the staff of the FPGA company you just acquired
<ktemkin> between them and Xilinx, they have invented the singularly most cursed software language ideology I've ever seen: tcl, except some of their functions return Magic Strings, which have an invisible data structure pointer attached to them
<ktemkin> "everything is a string, but some of the strings are magic, and if you ever try to modify those strings in any way the magic is lost"
<whitequark> to be honest, the Magic Strings thing has worked out kind of okay when i used it in Vivado
<whitequark> like, yes, it's unbelievably cursed, but it did work pretty okay.
<whitequark> also i thought that's just normal tcl
<whitequark> like isn't that how all tcl code works?
<ktemkin> tcl has that "everything is a string, but sometimes we hide other things in those strings" thing
<whitequark> yep. i don't quite get how this differs from Magic Strings
<ktemkin> Xilinx has the "this is a string sometimes, and you have to use it that way, but other times there's a magic thing tied to the object that stores the string, and we're not ever going to make it clear which is which"
<whitequark> oh.
<sorear> is that something they had to add to a tcl fork?
<ktemkin> so you go and ask for a file handle, and you get "3", and it seems like that's a handle that represents an object in memory
<ktemkin> but it turns out there are three things with the value "3", and they're all entirely equivalent as far as tcl can tell, but the interpret's tagged them with something you can't interact with that makes some functions behave differently
<awygle_working> > it doesn't have behavioral parts. you use nMigen to define a netlist (that, perhaps, later gets optimized by the toolchain downstream). if you want behavioral code, there's the entirety of Python right there.
<ktemkin> *interpreter
<awygle_working> whitequark: ^ should be in the readme, IMO
<whitequark> yeah
<awygle_working> it's actually cleared a _bunch_ of stuff up for me in the ~15m i've been digesting it
<whitequark> right, good; just need to figure a good phrasing for it
<awygle_working> > this documentation is written in most significant bit 0 (MSB0) format
<awygle_working> well that explains why nothing works. is this a thing?
<whitequark> i think POWER may have been doing it?
<whitequark> it's incredibly cursed
<ktemkin> I like explaining the idea that nmigen -isn't- a hardware description language: you're not describing behaviors and letting something else fill in the details. Instead, nMigen is a gateware composition toolkit that fundamentally starts by building netlists; and then gives you the python tools you need to construct abstractions from there.
<whitequark> i see that concept but also i think going "not a HDL" right in the readme will serve more to confuse
<whitequark> personally i tried emphasizing the RTL aspect of it, with mixed results
<ktemkin> maybe replace "hardware description language" with some variant of "behavioral description language"
<ktemkin> but that's less a phrasing suggestion and more an "idea to get across" suggestion
<whitequark> right, makes sense
<ktemkin> [by the way, off topic, but my complete lack of creativity has resulted in me taking your suggestion of `usb-protocol`]
<whitequark> heh :)
awygle_working is now known as awygle
* awygle gives up on the lie
<awygle> ktemkin: is your usb stack intended to be general purpose? cuz i uh... could use one >_>
<whitequark> phew, write_cxxrtl now doesn't perform UB on out-of-bounds memory access
<ktemkin> awygle: yes
<whitequark> i have one outstanding issue to fix and it can go upstream
<whitequark> (memory port write priorities)
<ktemkin> awygle: probably won't be ready for general use for another week or two; and then another short bit if you want PHY-less FS
rohitksingh has quit [Remote host closed the connection]
<awygle> ktemkin: awesome, i'll keep my eyes open. i want PHY_ful HS so the last bit is fine :P
<awygle> whitequark: is there any way we could strip the top, say, six lines from the traceback when an exception is thrown? it's always in nmigen's guts and i almost always want "what did i fuck up in my code"
rohitksingh has joined #nmigen
rohitksingh has quit [Remote host closed the connection]
<whitequark> awygle: we as in nmigen, or we as in you in your code?
<awygle> the former
<awygle> although the latter is also interesting
<whitequark> the former: unlikely, as messing with global interpreter state is not something libraries should do
<whitequark> i already do that a fair bit and more is not helpful
<whitequark> the latter: fairly easily
Asuu has quit [Remote host closed the connection]
<ktemkin> I tend to *like* the exceptions coming from nmigen's internals
<ktemkin> if it's not immediately obvious what's wrong with my code, it often becomes so when I look at what part of nmigen got angry at me
<awygle> ktemkin: 9 times out of 10 the problem is i typed "sink.we" instead of "sink_we" and all i need is the line number in my code :shrug:
<whitequark> also I try to put in as many asserts as I can, which get shown in the snippets in the backtrace
<awygle> ah, i wasn't sure whether it was a global interpreter thing or a context object on the exception like in Rust's error stuff
<awygle> nvm then
<whitequark> you can replace the uncaught exception hook
<whitequark> (and we already do, but for other, composable reasons)
rohitksingh has joined #nmigen
<whitequark> i think python might have gained backtrace filters recently?
<whitequark> can't quite find what i remember reading
<whitequark> but you can always filter them on your own if you want,
<awygle> cool
<awygle> how do i zero-extend a value? i thought there was a method on Signal but now i can't find it
<ZirconiumX> Cat(Repl(0, N), <value>) works by brute force :P
<awygle> except it's backwards innit?
<ZirconiumX> ...Yeah, I forot
<ZirconiumX> *forgot
rohitksingh has quit [Remote host closed the connection]
<whitequark> awygle: there's no zero-extend operation specifically because it's rarely necessary
<whitequark> there *is* an operation to convert any given value to signed or unsigned format, which will then get sign-extended or zero-extended if need be
<whitequark> you could then use something like `<value> | C(0, <size>)` which is admtitedlly a bit gross
<whitequark> I'm open to a new method for extending the value to a given size
<whitequark> I think really the only case where you *need* this is when you're concatenating this value with something else
<awygle> probably the case i'm encountering is just paranoia then
<whitequark> which is it?
<awygle> fifo.w_data.eq(thing), where w_data may be bigger than thing (or may be the same size)
<whitequark> nmigen arithmetics essentially works as "infinite precision in expressions, modular power-of-2 once assigned"
<whitequark> if it doesn't somewhere, that is a bug
<whitequark> ah
<whitequark> thing will get either sign or zero-extended depending on whether it's signed
<awygle> verilog's promotion rules are so weird i am always uncomfortable assigning differently-sized signals together
<whitequark> right, so, nmigen's promotion rules are much simpler.
<whitequark> 1. any arithmetic operation returns a value that fits all theoretically possible result bits
<whitequark> (if you shift left by a 32-bit amount, you'll get a four billion wide value, yes)
<whitequark> 2. unsigned binop unsigned returns unsigned. otherwise, you get signed
<whitequark> muxes work the same as binops (unlike in verilog!). Mux(a, b, c) where b is signed and c is unsigned, sign-extends b, zero-extends c, and returns a signed value
<whitequark> that's it. that's the entire rules
<cr1901_modern> >if you shift left by a 32-bit amount, you'll get a four billion wide value, yes
<cr1901_modern> Have you managed to crash a system yet by deliberately doing this :P?
<whitequark> no, but someone on the issue tracker *did* manage to crash yosys
<whitequark> where in fact the size overflowed the variable it uses to track bit amounts
<cr1901_modern> Nice!
<whitequark> there *were* a few strange nmigen crashes in similar circumstances in the legalizer
<whitequark> because the legalizer sometimes makes things that look like really wide shifts
<whitequark> but, it has been appropriately hardened so that this no longer happens