whitequark changed the topic of #amaranth-lang to: Amaranth hardware definition language · code https://github.com/amaranth-lang · logs https://libera.irclog.whitequark.org/amaranth-lang
nelgau has joined #amaranth-lang
lf_ has quit [Ping timeout: 268 seconds]
lf has joined #amaranth-lang
nelgau has quit [Ping timeout: 256 seconds]
nelgau has joined #amaranth-lang
nelgau has quit [Remote host closed the connection]
nelgau has joined #amaranth-lang
lf has quit [Ping timeout: 240 seconds]
lf has joined #amaranth-lang
nelgau has quit [Ping timeout: 256 seconds]
nelgau has joined #amaranth-lang
bl0x_ has quit [Ping timeout: 268 seconds]
bl0x_ has joined #amaranth-lang
nelgau has quit [Ping timeout: 240 seconds]
nelgau has joined #amaranth-lang
nelgau has quit [Remote host closed the connection]
nelgau has joined #amaranth-lang
nelgau has quit [Ping timeout: 256 seconds]
Degi has quit [Ping timeout: 240 seconds]
Degi_ has joined #amaranth-lang
Degi_ is now known as Degi
nelgau has joined #amaranth-lang
nelgau has quit [Remote host closed the connection]
nelgau has joined #amaranth-lang
nelgau has quit [Ping timeout: 256 seconds]
rektide has quit [Ping timeout: 250 seconds]
rektide has joined #amaranth-lang
nelgau has joined #amaranth-lang
nelgau has quit [Ping timeout: 256 seconds]
donc has joined #amaranth-lang
<_whitenotifier-8> [amaranth] whitequark commented on issue #675: Add option to export only standards compliant VCD - https://git.io/JSHI7
Vonter has quit [Ping timeout: 256 seconds]
Vonter has joined #amaranth-lang
nelgau has joined #amaranth-lang
nelgau has quit [Ping timeout: 240 seconds]
nelgau has joined #amaranth-lang
nelgau has quit [Ping timeout: 256 seconds]
Vonter has quit [Ping timeout: 256 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Quit: WeeChat 3.4]
a has quit [Quit: good, and i cannot stress this enough, bye]
donc has quit [Quit: Client closed]
nelgau has joined #amaranth-lang
nelgau has quit [Ping timeout: 240 seconds]
FL4SHK has quit [Ping timeout: 256 seconds]
FL4SHK has joined #amaranth-lang
Guest7653 has joined #amaranth-lang
Guest7653 has quit [Client Quit]
roflin has joined #amaranth-lang
<roflin> Hi, This might be a more general question about FPGA desgin than a question about Amaranth, but how do I deal with external clocks. Is there someway to use an external clock directly as a clock for a module, or should I observe the clock as just another input pin and detect edges myself synchronously with the clock of the fpga?
<roflin> I've read about Clock Domains, but I'm unsure on how to use them and if this is a use-case for them.
<lambda> roflin: it depends, if the input comes from an oscillator, you can probably use it as-is (though you might have to use dedicated FPGA pins to get it into a clock buffer), if it's e.g. an SPI clock, it's probably easier to just oversample it using your system clock
<roflin> it is in fact an SPI clock.
<lambda> if you run your SPI logic off the SPI clock, you then have the problem of getting that data into your main clock domain, which is a big hassle, so if you can afford it, just sample it with your main clock and detect the clock edges yourself
<lambda> obviously that requires a significantly higher sample clock than the SPI clock
<roflin> Yeah I'll have to see what frequency the SPI bus is running at, it's a blackbox thing. but yeah it would be simpeler to sample the clock it seems. Any tips on writing test-benches, can I easily generate an SPI clock for my testbench.
<lambda> not much experience with amaranth, sorry
<agg> roflin: since it's not a "real clock" anyway, you can just have some input that you toggle from the testbench every few cycles
<agg> amaranth does come with a few helpful primitives for moving data across clock domains, so if the SPI clock is more than 1/3 of my sync clock I'd probably run a little bit of logic off the SPI clock and move the data into sync internally, but if it's slow enough that you can reliably get a couple of samples per half-cycle of SPI then oversampling it should be pretty easy
<agg> roflin: here's a quick example of an SPI peripheral and a testbench for it: https://github.com/adamgreig/amaranth-examples/blob/master/amaranth_examples/spi_oversampled.py
<agg> be aware that if you're sampling external signals at all (clocks or otherwise) you need to register them against your sync domain using the IO registers, which will add a cycle of latency in each direction, which is part of why your clock has to be a fair bit faster than the SPI clock
<Sarayan> hey, since we're talking about clock domain crossing, I have a question
<Sarayan> I have a timer circuit I want to simulate with two clocks, a bus clock and a timer clock
<Sarayan> the bus clock is around 8Mhz (there's a couple of possible frequencies) and the timer clock is 2457600Hz
<Sarayan> both are internal, coming from a couple of plls, of course
<Sarayan> how should I handle the counter clocking?
<Sarayan> the counters have to be accessible and trigger event at bus speed, but decrement at timer speed
<Sarayan> should I avoid the second pll and just bresenham it?
<Sarayan> that would cost me around a LAB, but otoh there's no risk of metastability
<agg> in terms of simulation in amaranth it's straightforward to add multiple clocks at different frequencies which each drive their respective clock domains
<Sarayan> sure, but once I want it to run reliably on the cyclonev?
<Sarayan> also you can't write the same signal from different clock domains
<agg> is your question how to simulate a multi-clock design in amaranth, how how to design a timer that uses a separate bus and timekeeping clock?
<Sarayan> the latter, and design it in amaranth
<d1b2> <dragonmux> adding a testbench Elaboratable that is then the top-level for the sim, and having that have a clock domain for your timer clock if it's not already and plumbing that into the DUT, then add the clock to the sim runner, is how we'd do that
<Sarayan> the aim is to actually run it on the de10-nano
<agg> assuming the timer elaboratable already has two clock domains you can just call sim.add_clock twice, once for each domain, don't strictly need a top-level wrapper or anything
<Sarayan> and the configuration I'm talking about is how the MC68901 is setup in the atari st
<d1b2> <dragonmux> as long as it has the real resources to run the PLLs at the desired frequencies.. there shouldn't be a problem
<d1b2> <dragonmux> as long as the clocks are on suitable pins
<agg> dragonmux: but the Q is how you design this logic block at all, given two incoming clocks from the PLL
<d1b2> <dragonmux> oh, I see
<agg> which really I guess is a question about how you synchronise a register that's counting in a slow domain onto bus access from a fast domain
<d1b2> <dragonmux> well the two clocks are two clock domains
<d1b2> <dragonmux> then use the CDC library helpers to cross the domains as required
<agg> Sarayan: I've done something very similar in the past, though my bus freq was lower than my timer freq
<agg> and I did the comparison/match in the timer domain, and export the trigger pulse to the bus domain
<Sarayan> can I trust the CDC helpers?
<agg> yes but none of them are especially helpful by themselves for this
<agg> I mean, they work, otherwise that's a bug in amaranth
<Sarayan> I don't understand how metastability can actually be avoided
<Sarayan> after a while you're going to have seen pretty much any phase relationship between the two clocks and that's a little frightening
<agg> sure, the idea is you have a couple (or more) flipflops in a row, all clocked by your output domain
<agg> the first one takes the signal from the input domain as its input, and its output goes to the next's input, etc
<agg> it's likely the first output will be metastable occasionally if it samples poorly, but it's very likely it will have settled by the next clock when the second FF samples it
<agg> and if not, the second FF becomes metastable, but it's extremely likely it's settled by the time the third one samples it, etc
<agg> the more FFs in a row, the less likely you'll get metastability at the output
<agg> usually 2 is fine
<agg> amaranth provides FFSynchronizer which creates this chain of FFs (and marks it as required for your platform, if the platform can be told to ensure those FFs go next to each other or otherwise provide some useful primitive)
<agg> that only moves one bit in one direction, so it's no use for e.g. a whole word, because there's no guarantee that all the bits of the word will arrive together (unknown phase relationship between the bits)
<Sarayan> so... you get a same latency which doesn't matter, but you're cool
<agg> amaranth provides a PulseSynchroniser which can move a one-clock-cycle pulse on input to a one-clock-cycle pulse on output (by using some FF syncs internally)
<Sarayan> well, that would be about turning a clock into a pulse, I think
<agg> hmm it's probably better to think of it as turning a valid pulse or something into a pulse out, rather than feeding a clock directly, but sure I guess it would work?
<Sarayan> which n' & !n tends to handle nicely I think
<agg> not across clock domains
<Sarayan> after the two levels of ff
<Sarayan> adding a third level
<agg> it's a bit more complicated than that in practice (for pulse sync) but sure
<Sarayan> which is not very expensive especially with the feedbacks available in the cyclone
<Sarayan> it is?
<agg> what you described is a falling edge detector on the synchronised signal
<Sarayan> timer clock as data -> ff -> ff (n) -> ff (n') (n' & n) -> enable for decrement/increment (or value for the increment directly)
<vup> FFSynchronizer does a n bit Signal not just a single bit
<Sarayan> that's n' & !n of course
<Sarayan> with all the ffs on the bus clock
<agg> vup: but there's no guarantee at all about phase relationship between those bits, you can't just use it to synchronise a word
<Sarayan> if I can handle that with 3 ff, one lut and one pll that's cheap (plls are plentiful compared to the number of clock frequencies needed)
<agg> Sarayan: I guess that sounds reasonable for your case! it's not the same as pulse sync but it should work OK to edge detect the resync'd clock I think
<Sarayan> yeah, I don't have a pulse in the first place, so that's not entirely the same problem
<agg> yea
<Sarayan> I just need to implement the MC68901 with a timer clock signal and not a timer clock domain
<agg> pulsesync isn't much more complicated: it basically has a register on the input that XORs with the input each input clock, then outputs n' ^ n
<Sarayan> so you get a pulse on both raising and falling edge?
<agg> when I've done something similar I had the timer clock domain drive the actual timer register, and the bus clock gets a synchronised sample from that register on request
<agg> but my bus was slower, so i couldn't do anything like what you've done
<agg> you get one output pulse per input cycle where input is asserted
<agg> so long as output >= input clock anyway
<Sarayan> yeah, everything possible input for the timer is slower than the bus clock
<Sarayan> in my case (and I think in the MC in general, that's required)
<agg> vup: for example, I've used an n-bit FFsync to bring a whole word into the new clock domain, _and_ a PulseSync to bring a 'valid' pulse from input to output, then sample the synchronised word on that pulse
<agg> but if you just use the n-bit FF and another FF for 'valid', the output 'valid' might assert before all the bits are copied across
<agg> and you can end up with any number of the bits updated or not
<Sarayan> hmmmm
<agg> (my explanation of what I did is a bit simplified: the input side registers the input word and holds it constant until the output has read it)
<Sarayan> valid might assert "anytime" compared to the data bits, but does that matter? As in, is the question not "are they all there before the next clock edge?"
<Sarayan> I mean, that's the point of a sync design isn't it?
<agg> well it's not all sync when it's got multiple clock domains
<Sarayan> after the two ff it's supposed to be, no?
<agg> the FFs in the synchroniser all run off the output domain, but it reaches each one at different phases, so the input signal is sampled at different times
<Sarayan> ahhhhhh indeed
<agg> if that happens to fall either side of an input clock edge, they'll read different things
<Sarayan> I understand
<agg> (or become metastable etc)
<agg> so to synchronise a whole word you need some extra handshaking of some description to ensure the inputs stay the same until they're all read on the output
<vup> agg: hmm then FFSynchronizer doing n bits seems like pretty big footgun?
<agg> vup: it's really useful to be able to have an n-bit FF synchroniser, e.g. as a building block for other CDC primitives
<vup> sure
<vup> but shouldn't those then actually be the ones exposed?
<agg> maybe!
<agg> I think even if you just exposed single-bit FFsync people would make an array of them to synchronise a word if they didn't know better anyway
<agg> probably it's enough to just also expose some kind of word synchroniser...
<agg> but I think they tend to be a bit more open to different designs to fit particular use cases
<agg> for example, nmigen already has an AsyncFIFO which is a super convenient and safe way to move words between clock domains
<agg> (and uses n-bit FF synchronisers internally for that purpose, too)
<agg> in that case, with grey-coded counters, so only one bit changes at a time, which also gets around the problem)
<Sarayan> thanks and 'night
<vup> I feel like atleast the documentation of FFSynchronizer should warn you against using it in cases where you want a defined relationship between the bits
bvernoux has quit [Read error: Connection reset by peer]