whitequark[cis] changed the topic of #amaranth-lang to: Amaranth hardware definition language · weekly meetings: Amaranth each Mon 1700 UTC, Amaranth SoC each Fri 1700 UTC · code https://github.com/amaranth-lang · logs https://libera.irclog.whitequark.org/amaranth-lang · Matrix #amaranth-lang:matrix.org
<iposthuman[m]> Here is a question relating to earlier IO Pin direction: If I manually control the direction of the Pin using ```sram_0.d.oe.eq(1 or 0)``` then what is the purpose of the *d* field having an *.i* and *.o*. I ask because my code was setting *.o* to data I wanted to send out the fpga Pin and *.i*, during a Write cycle, and to read data from the fpga Pin during a Read cycle. Amaranth compiles this without errors. It just seems to
<iposthuman[m]> me that the generated code would recognize that I'm either setting *.i* or *.o* and set the *oe* attribute accordingly. Is my thoughts on this correct?
<adamgreig[m]> oe doesn't set direction, it sets whether the output driver is enabled
<whitequark[cis]> Amaranth does not recognize uses of .i or .o in any special way
<whitequark[cis]> these are treated as normal signals
<iposthuman[m]> Ah. Ok.
<adamgreig[m]> direction is a build-time concept, oe is a run-time concept
<iposthuman[m]> Oh. i didn't think of it that way. make sense.
<adamgreig[m]> direction=io means you have an i, o, and oe signal which exist in the circuit (if you use them, anyway); whenever oe=1 (maybe all the time), the output driver is turned on and the fpga electrically drives that pin, when it's 0, it doesn't (so the value for .o only has effect when .oe=1, but .i will always let you read the current electrical state of the pin)
<adamgreig[m]> if you have just dir=o, you can imagine the oe signal is just always set to 1
<adamgreig[m]> and if you don't use the .i input signal, it's the same as if it was never there
<adamgreig[m]> so dir=i and dir=o are sort of just special case simplifications of dir=io (although usual caveat with fpgas: these are all abstractions over whatever 'real' primitive your fpga uses for IO cells, which might have other weird behaviours too)
<iposthuman[m]> Put in those terms in is starting to sink in. Thanks adamgreig (@_discord_614384955182678017:catircservices.org) , @Catherine for helping me understand...much appreciated 🙂
<adamgreig[m]> (also, it's bad to accidentally drive a pin that should only be an input, because you might electrically fight whatever was actually driving the pin, so it would be silly to mark it io!)
phire has quit [Ping timeout: 276 seconds]
<crzwdjk> The direction="i" thing is a kind of static typing I guess, you don't want to drive an input pin so it doesn't have a .o at all. And the 'real' FPGA primitives have all sorts of other fun things in them, some of which Amaranth also gives you access to
Degi has quit [Ping timeout: 256 seconds]
Degi_ has joined #amaranth-lang
Degi_ is now known as Degi
phire has joined #amaranth-lang
jjsuperpower has quit [Ping timeout: 268 seconds]
sporniket has joined #amaranth-lang
<sporniket> Hello, just pinging to say that I am slowly working on having a yices2 build with WASI sdk
<sporniket> so far I have a script building GMP library required by yices
<sporniket> the hard part is to refresh my shallow knowledge of autotools
<whitequark[cis]> that looks nightmarish
<whitequark[cis]> I didn't realize it requires GMP, though it makes sense
<sporniket> any way I hope to the build done for yices this week two, then testing if it works. (it's slow as I allocate only free time on thursday and friday to fpga projects)
<whitequark[cis]> oh, also the YoWASP build scripts intentionally do not clean intermediate build trees, because this can make repeated rebuilds of the final build product with minor changes extremely slow (and doing such repeated rebuilds is common)
<sporniket> the lzip format is from the GMP download site (it's either that or other exotic format, no bonafide gz...)
<sporniket> (and the repository is mercurial, and ip of cloners are quickly banned for abuse anyway)
<whitequark[cis]> (I usually use .tar.bz2 which provides a better compression ratio)
<whitequark[cis]> * compression ratio, and .tar.bz2 is also present)
<sporniket> ho.... was not listed, or that's what you get when I get up too early...
<whitequark[cis]> that PKG_CONFIG_SYSROOT_DIR is almost certainly wrong
<sporniket> not listed in the download section of the page https://gmplib.org/#DOWNLOAD
<whitequark[cis]> (different SYSROOT)
<whitequark[cis]> or, hm, you are reusing the WASI sysroot as the installation sysroot, which you should not be doing (it contaminates what should be the purely WASI tree as-is after unpacking with build products)
<whitequark[cis]> if you look at nextpnr build script, it shows you how to install intermediate build products to some other sysroot, which is what you should be doing
<whitequark[cis]> I'm a bit out of date on autotools but it's possible you can just skip pkg-config entirely, which could potentially save you a bunch of trouble (you know exactly where gmp is installed anyway, you don't need a tool and a bunch of .pc files to tell yices2 that)
<sporniket> ok. And for the environment vars that are exported, for now I just try to get something, and then I will make things like you did for yosys and all.
<whitequark[cis]> right, yeah
<whitequark[cis]> just making it clear what the acceptance requirements for yowasp are
<sporniket> no problem at all
<sporniket> thanks for the feedback
<sporniket> I will ping again when I have updates :) have a nice day
<whitequark[cis]> (as far as I recall, you only need to provide CC, CFLAGS, etc to ./configure and not make)
<sporniket> (ok)
sporniket has quit [Quit: Client closed]
notgull has quit [Ping timeout: 255 seconds]
notgull has joined #amaranth-lang
<omnitechnomancer> or if you have open drain output mode configured for that pin which one might for example if doing I2C
<iposthuman[m]> I got up this morning and added ```sram_0.d.oe.eq(1)``` and it worked! Again thanks everyone for the help 🙏 . Now I understand Pin control.
<galibert[m]> did you add the .eq(0) in the read phase?
<omnitechnomancer> I would probably only declare it working if you can write two different values to two different addresses and then read the first one back and get what you expected
<iposthuman[m]> Yup
<iposthuman[m]> You have a point. I've already begun writing a simple memory test/exerciser.
<galibert[m]> Yeah, because without the eq(0) you’re just reading back what you put on the lines
nelgau has quit [Ping timeout: 246 seconds]
<iposthuman[m]> At first i was missing it. then upon a later review i realized it was missing 😊
<iposthuman[m]> Although my notes from yesterday are:
<iposthuman[m]> Write: sram_0.d.oe.eq(1), which “drives the bus”
<iposthuman[m]> Read: sram_0.d.oe.eq(0) to stop “driving the d bus”
<iposthuman[m]> Although, I'm able to write/read on the SRAM, *dm* doesn't seem to control the mask: ``` # Enable lower and upper byte write mask
<iposthuman[m]> sram_0.dm.o.eq(0b11),
<iposthuman[m]> ```
<iposthuman[m]> No matter what bit pattern both bytes are always written to.
<whitequark[cis]> is it connected on your board?
<iposthuman[m]> I can't seem to mask out the upper byte. So it works but not exactly byte controllable.
<whitequark[cis]> wait, only upper?
<iposthuman[m]> I was testing to see if i could only write to either lower or upper, but always writes to both
<whitequark[cis]> ah
<iposthuman[m]> It's a BGA package so it would be hard to trace it.
<iposthuman[m]> However, Verilog seems to work, and it does "roughly" the same thing.
<whitequark[cis]> ah
<whitequark[cis]> can you post the entire code?
<iposthuman[m]> sure. i'll commit my latest stuff.
<iposthuman[m]> Keep in mind it is a "work-in-progress" 😬 and I'm still learning
<iposthuman[m]> Adjusting ```sram_0.dm.o.eq(0b00),``` to values like "01" or "10" doesn't actually have an affect.
<iposthuman[m]> I even tried omitting the *.o* because it is dir="o"
<whitequark[cis]> is this for writes or for reads?
<whitequark[cis]> as in, where are you adjusting dm.o?
<iposthuman[m]> It is for both. It currently does a single write followed by a read, then stops. I'm working on adding interation, but then i noticed i couldn't control the mask.
<whitequark[cis]> while writing, while reading, or both?
<iposthuman[m]> writing only
<iposthuman[m]> crap, it looks like i forgot to use the proper syntax during the read
<whitequark[cis]> oh?
<whitequark[cis]> this does the same thing as sram_0.dm.o.eq(0b00)
<iposthuman[m]> And i just reminded myself the dm is using PinsN so I'll need to heed your advice about usine 11 instead of 00
<iposthuman[m]> hmmm. that still doesn't work. Committed read cycle changes to repo.
<whitequark[cis]> # Stop driving the bus (i.e. careen off the cliff ;-)
<whitequark[cis]> sram_0.d.oe.eq(1),
<whitequark[cis]> this doesn't do what the comment says it does
<whitequark[cis]> (it starts driving the bus)'
<whitequark[cis]> * (it starts driving the bus)
<iposthuman[m]> Argh! I didn't realize *d* was just Pins and not PinsN
<whitequark[cis]> iposthuman: neither Pins or PinsN inverts `oe`
<whitequark[cis]> the inversion only applies to the data on the pin
<whitequark[cis]> the output enable is always active-high
<whitequark[cis]> basically, almost all of your logic is flipped
<iposthuman[m]> Doh!
<whitequark[cis]> remember: in Amaranth, 1 means active/enabled/asserted, always.
<iposthuman[m]> What is the PinsN for then?
<iposthuman[m]> I thought that was Positive vs Negative logic thinking
<whitequark[cis]> PinsN exists to match external devices (which often use active-low signals) to Amaranth conventions
<whitequark[cis]> so your SRAM is using CE#, OE#, WE#, LB#/UB#. Amaranth has CE, OE, WE, LB/UB (note the lack of #, which has the same meaning as a line above, which is inversion)
<iposthuman[m]> Ah
<iposthuman[m]> Ok
Wanda[cis] has quit [Quit: Idle timeout reached: 172800s]
<iposthuman[m]> So I went ahead and added constants to help and it still write/reads both bytes:``` ASSERT_SIGNAL = Const(1, 1)
<iposthuman[m]> ```
<iposthuman[m]> DEASSERT_SIGNAL = Const(0, 1)
<iposthuman[m]> code commited
<iposthuman[m]> Need to look at it later. i have to go out and aboot.
<whitequark[cis]> is "ASSERT_SIGNAL" really that easier to understand than 1?..
<whitequark[cis]> > # The bits are reversed because the Rresource reverses them:
<whitequark[cis]> nope, in the resource, bits are defined LSB to MSB (in general, whenever there is a sequence of bits, Amaranth uses LSB to MSB order, which is the same as the order of indexes in a Python array: [<element 0>, <element 1>, ...]
<whitequark[cis]> * > The bits are reversed because the Rresource reverses them:
<whitequark[cis]> nope, in the resource, bits are defined LSB to MSB (in general, whenever there is a sequence of bits, Amaranth uses LSB to MSB order, which is the same as the order of indexes in a Python array: \[\<element 0>, \<element 1>, ...\]
<whitequark[cis]> (however "lower byte" and "upper byte" don't really mean anything in this particular situation, so it's not the source of your issues)
<whitequark[cis]> anyway, I think the reason data mask probably doesn't work is because there is a UB/LB-to-WE setup time
<whitequark[cis]> basically, you should think of the data and the data mask as things that are being used together (during writes)
<whitequark[cis]> also, you should be asserting d.oe together with d.o, or it doesn't actually setup anything
<whitequark[cis]> you can assert CS together with WE, they are internally ANDed together in the chip, so doing only one without the other is meaningless (in fact you can tie CS low permanently and control the chip with just WE if you want)
<whitequark[cis]> looks reasonable to me other than the things I raised
<whitequark[cis]> zyp: I think I figured out the perfect way to make arbitrary custom protocol decoders that work in both pysim and cxxsim
<whitequark[cis]> whenever we have support for the Print statement, so you can do e.g. m.d.sync += Print("foo {bar}", bar=Signal(1)), we should add a way for (a) a shape-castable to define an elaboratable that is added to the design to decode the value of the shape-castable and (b) for the output of the print cells within that elaboratable to be captured and stored as VCD values
<whitequark[cis]> works exactly the same in Python and C++, requires no manual C++ work, is incredibly flexible (doesn't really work if you have to have really big arrays for the state or something, but e.g. for AXI, as long as you have a limit on the amount of pipelined transactions--something that is common in practical designs--you can just have a memory that large to store the state of the decoder
<whitequark[cis]> * works exactly the same in Python and C++, requires no manual C++ work, is incredibly flexible (doesn't really work if you have to have really big arrays for the state or something, but e.g. for AXI, as long as you have a limit on the amount of pipelined transactions--something that is common in practical designs--you can just have a memory that large to store the state of the decoder)
<whitequark[cis]> naturally if your decoder is as simple as a fixed point number it's a single m.d.comb += Print(f"{{0{len(self)}d}}.{{0{len(self) - self.q}d}}", self[self.q:], self[:self.q]) statement or something like that
<whitequark[cis]> you could even write a complete disassembler using only Print statements and other standard Amaranth constructs
mindw0rk has quit [Ping timeout: 252 seconds]
mindw0rk has joined #amaranth-lang
<zyp[m]> that's an interesting approach, is the Print statement already defined somehow?
<whitequark[cis]> it's in Yosys but not (yet) in Amaranth
<whitequark[cis]> it's been planned for a whilwe
<whitequark[cis]> s/whilwe/while/
<whitequark[cis]> current status: inflicting psychic damage https://mastodon.social/@whitequark/111461871906179224
<galibert[m]> You’re evil
<whitequark[cis]> I mean, it's clearly more in scope for MPEG-4 than some of the existing parts (e.g. genomic data)
<galibert[m]> Doesn’t gtkwave have better formats than vcd?
<whitequark[cis]> no they're all garbage
<galibert[m]> Damn
<whitequark[cis]> primarily because the sole definition of these formats is "the shittiest source code you have ever seen that lives in the gtkwave source code and has to be copied out of it in order to be used in your simulation"
<whitequark[cis]> s/code/tree/
<galibert[m]> Ah
<whitequark[cis]> I have seen the quality of it and I will never put it in anything I care about
<whitequark[cis]> well, there is the VHDL replacement for VCD, that one is okayish, but it's still very ... expansive
<whitequark[cis]> (it's another text format supporting 9-valued logic, etc)
<galibert[m]> Ew
<whitequark[cis]> but at least it supports things like enums and structs
<galibert[m]> Random access, not having to read everything, that’s too much to ask I guess?
<whitequark[cis]> I'm currently working on something I call "CXXRTL debug server"
<whitequark[cis]> it's sort of a replacement for both VCD and half of gtkwave
<whitequark[cis]> instead of having a file where you wastefully dump every signal in your simulation just in case it's useful later (thus making it very slow), the waveform database only records the changes to the primary inputs and register state
<galibert[m]> Intriguing
<galibert[m]> Ok, that’s quite reasonable
<galibert[m]> I mean state saving only saving the state…
<whitequark[cis]> if you want to render waveforms, you send it a request (over a socket), it rewinds the simulation to that time point (by replaying the recording from the nearest full state dump), and for each time point and signal you are actually interested in, it responds with the data
<whitequark[cis]> this means that you get 100% of the waveform coverage at only approx. 20% runtime overhead
<whitequark[cis]> basically, you can always run your simulations with what is effectively enabled full waveform dump, because it costs so little to maintain it
<galibert[m]> Amusingly it’s quite similar to what we do in mame to do things like step back
<whitequark[cis]> yes, it's based on the time travel debugging concept
<whitequark[cis]> the waveform database is basically a series of diffs, each of which stores the previous and the next state
<whitequark[cis]> (plus occasional full state dumps, to improve seek time)
<galibert[m]> Yeah
<whitequark[cis]> there are some mildly cursed design choices in the server, e.g. the protocol is basically JSON-RPC (custom, but similar in spirit) and the waveform data is encoded in base64
<whitequark[cis]> I'm finishing the protocol design document right now, should be able to upload it soon
<whitequark[cis]> it's not strictly speaking Amaranth-related
<galibert[m]> Stupid question maybe, but why a server instead of say a library?
<whitequark[cis]> CXXRTL is already a library
<whitequark[cis]> if you want to deeply integrate with it and to optimize every choice for your application, you are very much enabled to do so
<galibert[m]> Nice
<whitequark[cis]> however most people don't really care about it, they want some turnkey solution that takes care of a bunch of annoying details for you
<whitequark[cis]> and throwing JSON at a problem is the classic way to make something kind of work pretty fast
<whitequark[cis]> the server is going to make relatively opinionated choices about things like "replay buffer serialization format" that may not work well for every application
<galibert[m]> Ah, I see, you don't serialize the expressions to the logs, you use the generated code for that
<whitequark[cis]> (it will likely write an uncompressed stream of 32-bit words with a fair amount of redundancy and with every value change being represented by at least 3 32-bit words)
<whitequark[cis]> expressions?
<galibert[m]> to compute any intermediate signal from the state
<whitequark[cis]> yeah
<whitequark[cis]> it's a waste to not use the simulation code that is right here
<galibert[m]> heh
<galibert[m]> there is an interest in having a format with the full information, but for performance optimized c++ code is hard to beat
<whitequark[cis]> well, you can just compile the C++ code to wasm and stuff it into the file.
<galibert[m]> intriguing
<galibert[m]> I don't have a wasm reflex :-)
<whitequark[cis]> I like having code as data
<whitequark[cis]> and lisp has failed as a concept
<galibert[m]> wasm is really code-as-data, or it's just yet another bytecode/vm combo?
<whitequark[cis]> wasm is a vm that has deterministic semantics and is isolated from the environment unless you break the isolation yourself
<whitequark[cis]> I am using a narrow definition of "code-as-data", namely I want to be able to build some (C, C++, Rust, whatever) code and manipulate it as a chunk of data that doesn't have weird implicit system-level dependencies after. I just want to yeet it somewhere and know I can actually execute it later, deterministically, without having to build cross-toolchains, or having it break under me
<whitequark[cis]> you can do interesting transformations on wasm code (e.g. you can "pre-run" the initialization steps of an application to reduce its startup latency to zero, map and it is already at its first syscall) but it's not really about that
<galibert[m]> fun
<whitequark[cis]> it's about composition of small wasm modules to build applications that have guaranteed fault isolation and cross-unit integrity
<galibert[m]> I guess things like lua bytecode has no real vm definition, and both java and .net are gigantic messes
<whitequark[cis]> one way in which Firefox uses Wasm right now is by confining legacy image format readers (all the old stuff people don't use that much anymore, like targa or whatever, i don't recall) to sandboxes and completely eliminating a class of bugs where your browser is popped by some cursed thing like an animated cursor buffer overflow
<whitequark[cis]> yes, exactly
<whitequark[cis]> wasm also has formal small-step operational semantics, which is huge
<galibert[m]> small-step?
<whitequark[cis]> a kind of formalism, the specifics are unimportant
<whitequark[cis]> the point is that you can make proofs about properties of wasm bytecode, like "it will never have type confusion" or something and that will actually have a good theoretical basis under it
<galibert[m]> oh nice
<whitequark[cis]> of course wasm engines still have all sorts of bugs, many of them are complicated as hell, but this is a good direction to advance in
<whitequark[cis]> especially as wasm gets more complicated by gaining features such as dynamic linking
<whitequark[cis]> also, you can do wasm code emission in one pass*
<whitequark[cis]> * almost, the details are again unimportant, the point is that you can easily write a streaming codegen and you don't have to keep huge tables just to be able to resolve jumps
<whitequark[cis]> s/*/\*/
<whitequark[cis]> in fact modern browsers start compiling the wasm to native code before it even finishes downloading
<whitequark[cis]> (this is the reason wasm does not have goto)
<galibert[m]> cute
<whitequark[cis]> (yes, it's a bytecode that only offers structured control flow)
<whitequark[cis]> in practice the compiled code is really, really fast, has reasonably light resource requirements (people AOT it to run on things like low-end STM32s, it's a bit tight but you can ship actual applications that way on real commercial devices), the VM is painstakingly designed to actually work well and be secure
<galibert[m]> very interesting
<whitequark[cis]> and you even have things like SIMD and multithreading nowadays
<galibert[m]> and at least there isn't npm :-)
<galibert[m]> or node.js
<whitequark[cis]> I mean, node.js runs wasm
<galibert[m]> heh, there's no escape
<whitequark[cis]> that's actually one of the easiest ways to do some local development without embedding a wasm vm
<whitequark[cis]> I tested some of yowasp that way
<galibert[m]> nice
<galibert[m]> Looks interesting
<galibert[m]> Too late for today but I need to learn how to use the amaranth/cxxrtl sim combo
<whitequark[cis]> it's ... not
<whitequark[cis]> it doesn't really exist yet
<galibert[m]> Oh
<whitequark[cis]> there is a branch but it has the same performance as python sim
<whitequark[cis]> for reasons
<galibert[m]> Reasons like how to input the sim data?