<lsneff>
Trying to write a fixed-point clock divider which should be pretty simple, but I think I might be running into a situation where my mental-model of how amaranth translates into verilog differs from what it actually does
<lsneff>
When reading a signal and setting it into the same m.d.sync scope, does that read get the value before or after it's set?
<lsneff>
I was assuming that it works like non-blocking assignments in verilog work, but it seems like it might not be
nak has quit [Ping timeout: 256 seconds]
<agg>
the value of the register is updated at the start of each clock tick, propagates through any combinatorial logic, and is re-registered at the start of the next clock tick
<agg>
so that m.d.sync += a.eq(a + 1) works
<agg>
but.. clock divider in logic?
<lsneff>
Yeah... it's not a clock divider exactly, more like a clock enable divider, but yes
<lsneff>
I'm pretty new to digital logic design tbh, so if there's something I'm doing that doesn't make sense, let me know
<agg>
an enable divider in logic sounds fine, trying to drive actual clocks from logic is where it's easy to trip up
<agg>
at first glance it looks ok, though if frac_overflow is at the same time as int_divisor==1 then increase_divisor won't get set
<agg>
(maybe your parameter check at the start should be <= 0, not <0?)
<agg>
yea, in your testbench it seems like every time frac_overflow is getting set, int_divisor happens to be 1, which sets increase_divisor to 0
<agg>
just as a style thing, I like to try and make it so each signal is only assigned in one place based on one if/elif/elif/else chain, rather than a default assignment, another conditional assignment later, and then another later again, as it makes issues like this a bit harder to track down
<agg>
so that whatever conditions cause a particular signal to set are always nearby each other in code
<lsneff>
Ah, good suggestion thank you
<agg>
your fix is probably to make L65 read "int_divisor.eq(self.integer + increase_divisor + frac_overflow)`
<lsneff>
Hmm, it seems like that decreases the clk_en frequency too much, since that's fractional (2.5), it should vary overtime
<lsneff>
`int_divisor.eq(self.integer + (increase_divisor | frac_overflow))` isn't enough though
<agg>
that's a problem elsewhere then: adding frac_overflow exactly forwards however often increase_divisor would be set had it not been cleared that cycle
lf has quit [Ping timeout: 252 seconds]
lf has joined #amaranth-lang
<lsneff>
Right, okay
<agg>
hmm
<agg>
curious that increase_divisor ends up 1 quite often with it like that, though
<lsneff>
Yeah
<agg>
sure seems to settle into being a 1:4 divider instead of 1:2.5
<lsneff>
I'm having trouble visualizing the style choices you mentioned early, would you be willing to write it out the way you'd have done it?
<Degi>
f_max can be replaced by a signal too, though in this case it was supposed to output a signal with a frequency of f / f_max
<lsneff>
agg: Ah sweet, thank you!
<agg>
it's just a preference thing and it won't always apply, depends on the code
<agg>
sometimes it's nicer to have default assignments and then override them, sometimes i think it's clearer to only have one "active" assignment anywhere
<lsneff>
I definitely agree that it's clearer here
<lsneff>
It's not exactly identical though, clk_en is never getting set back to zero, but I do get the idea
<agg>
oh, yea, that should be inside the Else branch
<Degi>
Ooh I see, this code uses a fractional divider value whereas my code basically uses a fractional multiplier value
<lsneff>
I’m still unsure why it isn’t doing the same as that image I sent
<Degi>
Which one?
<Degi>
Did you look at the vcd trace?
<lsneff>
The one I sent. I have been yeah
<lsneff>
Perhaps there’s a better way to do it then the frac_overflow and increase_divisor stuff
<agg>
can you just have one wider counter?
<agg>
then add 2.5 to it each cycle, the fractional part will naturally overflow into the integer part
<agg>
and compare just the integer bits to ==1?
<agg>
haven't really thought this through but maybe it would simplify it
<Degi>
Basically each step it counts up by 256, one integer part equals 256 and one fraction part equals 1. If the counter gets large enough, then the set division value gets subtracted, essentially resetting it, but keeping leftover bits, I think it should work but didn't test it
<Degi>
(If you replace the 256 by a signal called y and call Cat(self.fraction, self.integer) x, then you have a rational divider which divides by x/y or multiplies by y/x)
<lsneff>
@Degi Ah that’s very clever, thank you!
<lsneff>
I will try those both when I get back to my computer
<Degi>
(That's basically my fractional frequency multiplier but with the constant and signal parts swapped, so I think it should work fine)
<Degi>
(IIRC it uses the outside statement as a fallback if a FSM state doesn't change the signal)
<agg>
yea, the last active assignment wins, basically
<cr1901>
That's what I remember too, just making sure
<Degi>
You can even put stuff inbetween FSM states (though not sure why you would)
<cr1901>
IIRC the classic way to do wishbone ack is to have ACK.eq(0) as your first statement, and then "if CYC && STB, do ACK" as your last statement
<cr1901>
I've definitely done the "last active assignment wins" stuff before, I just don't remember seeing the default case appear last in a code block
<cr1901>
My reading of the linked code would be "increment timer by 1 always wins"
<cr1901>
since it appears last in order
<cr1901>
(but hey I'm gonna test it in a bit)
<agg>
default case first generally works fine, since the other assignments will only override if if they're active (i mean, inside an if/else that's true)
<Degi>
Not sure how it is there, but in amaranth the most indented statement "wins", if all preceding statemens are true
<Degi>
(Or maybe I just never put a default statement after an FSM)
<agg>
An assignment added inside an Amaranth control structure, i.e. with m.<...>: block, is active if the condition of the control structure is satisfied, and inactive otherwise. For any given set of conditions, the final value of every signal assigned in a module is the same as if the inactive assignments were removed and the active assignments were performed unconditionally, taking into account the
<agg>
assignment order.
<agg>
that's a much more clearly expressed version of what i was getting at
<Degi>
Oh I see
<cr1901>
So in amaranth, timer.eq(timer + 1) would always win
<Degi>
If the code is in the order as it is there, I think so
<lsneff>
Degi: your suggestion from early worked perfectly, thank you!
bl0x_ has quit [Ping timeout: 240 seconds]
bl0x_ has joined #amaranth-lang
<lsneff>
How can I use the amaranth-soc package?
<lsneff>
Doesn't look like it's accessible from pip
<d1b2>
<TiltMeSenpai> also you can install from a git repo with git+<git remote> on newer versions of pip (newer being relative, it's been a feature for a while)
<d1b2>
<TiltMeSenpai> aww 😦 what's the error message
<lsneff>
There isn't one, the package is just empty
Degi_ has joined #amaranth-lang
Degi has quit [Ping timeout: 256 seconds]
Degi_ is now known as Degi
<d1b2>
<david.lenfesty> putting this in a requirements.txt and pip install -r requirements.txt worked for me: git+https://github.com/nmigen/nmigen-soc
<d1b2>
<david.lenfesty> (from before the amaranth rename, obv)
nq has joined #amaranth-lang
emeb_mac has quit [Ping timeout: 256 seconds]
jfng[m] has quit [Quit: You have been kicked for being idle]
<Degi>
lsneff: If you connect the output of the code to a :2 frequency divider (toggling a signal every second time its true) and then to a pin, then you can have a variable frequency output. Alternatively, directly connecting it to a pin and putting an RC lowpass filter on it yields a DAC
egg|matrix|egg has quit [Quit: You have been kicked for being idle]
<lsneff>
I was thinking about how it might be possible to change signal assignment to reduce visual clutter a little and came up with this:
<lsneff>
```python
<lsneff>
<some signal>.next = <value>
<lsneff>
```
<lsneff>
and
<lsneff>
<some signal>.assign = <value> # for combinatorial
<d1b2>
<garbile> this got cut off on the Discord side of the bridge. Message is: > And then to change the clock domain to something other than the default, you'd do > python > with m.d.other_sync: > .... >
<lsneff>
Ah sorry, yeah the formatting is a bit wonky
<lsneff>
To make a scope with a different (sync) clock domain, you use `with m.d.<some other domain>: ...`
<lsneff>
Oh, the "..." isn't cutoff, i was just implying you add your code there
<lsneff>
It might require doing `with Module() as m:` at the start of the elaborate block (or just passing in a Module to `elaborate`, now that I'm trying to implement it
<Degi>
You can put it in an array
<Degi>
Like m.d.sync += [
<Degi>
a.eq(b)
<Degi>
]
<lsneff>
I know, but the `.eq` thing just bothers me, adds a lot of parentheses
<Degi>
You could make a function which takes a = b etc. as **kwargs and then somehow interface that with the class that it adds eq statements if you want
<whitequark>
"reducing visual clutter" like that isn't something I consider a goal
<whitequark>
not having hidden global state, which you just added, is, however, a goal
<whitequark>
right now, there's nothing in Amaranth that doesn't follow the usual Python scoping rules. the entity with the largest amount of hidden state is `Module`, and there's nothing special about it, it's just complex. if you don't pass it around between functions, you don't carry its state around
<lsneff>
That's a good point, having no global state is good
<lsneff>
Yeah, the current syntax is good. Not ambiguous