<alyssa>
I admit, I've never started writing an emulator by... looking up a physical pin out
<alyssa>
Amaranth is changing me for the better :]
* GenTooMan
is glad something is changing.
bl0x_ has quit [Ping timeout: 250 seconds]
bl0x_ has joined #amaranth-lang
* alyssa
tries to understand how to add a clock to a submodule
<tpw_rules>
clocks are sort of orthogonal to modules?
<alyssa>
so they are..
<d1b2>
<dragonmux> clock domains propagate through all modules automatically and only need defining once
<tpw_rules>
can you share a little bit more about what you want to do?
<tpw_rules>
you can use a DomainRenamer to change a module's clocks around though when you instantiate it
<alyssa>
I want to use a particular Signal as the clock for a submodule's sync domain
<alyssa>
(said Signal is a flip-flop implementing a clock divide-by-2, probably poorly, but um)
<adamgreig[m]>
usually i'd just use a named clock domain in my submodule and it's up to the final design to instantiate it correctly, or just use sync and expect the higher level modules to use DomainRenamer to do the right thing
* alyssa
looks for docs on DomainRenamer
<alyssa>
(and for ClockDomain i guess)
<adamgreig[m]>
generating a /2 clock using a FF is sort of frowned upon, ideally you'd either run the logic half the time (check out EnableInserter) or use dedicated FPGA resources to generate a lower freq clock
<alyssa>
EnableInserter, hh
<alyssa>
uh
<adamgreig[m]>
(the FF output might not be glitch free, and generally you don't want to drive downstream FF clocks with data output from another FF, in terms of how the fpga is wired up, aiui)
<alyssa>
OK
<adamgreig[m]>
well the FF output should be glitch free, I guess I'm talking about the lut part :p but anyway
<adamgreig[m]>
it seems like an obvious thing to want to do, it just turns out to often not be a great idea on real fpgas
<alyssa>
good to know, thank you
<alyssa>
i guess there is a lot more undocumented Amaranth than i thought
<adamgreig[m]>
if you just want something to happen at half the speed, keeping it all clocked from the same sync but only taking action every other clock cycle is probably easiest
<adamgreig[m]>
EnableInserter is one cute amaranth way to help with that, but you can express it in normal logic too
* alyssa
still trying to understand EnableInserter
<adamgreig[m]>
yea... I guess it's a bit more niche. maybe you can think of it as wrapping the entire submodule's sync logic in "with m.If(enable):"
<adamgreig[m]>
and so if you toggle enable every cycle, the sync logic only runs every other cycle
<alyssa>
heh, that seems nice
<adamgreig[m]>
there's an example in examples/basic/ctr_en.py
* alyssa
literally wrapped with m.If in the mean time, given EnableInserter is afaik undocumented
<adamgreig[m]>
that's fine!
<adamgreig[m]>
i think a big use case for enableinserter is using it to wrap a submodule that didn't come with an enable, but you'd like one
<d1b2>
<dragonmux> so, there are two alyssa: EnableInserter and ResetInserter - both work the same fundamental way but on creating a clock enable signal and a submodule-specific reset signal respectively
<d1b2>
<dragonmux> reset and busy_n are two otherwise ordinary Signal()s
<d1b2>
<dragonmux> PIC16() is an Elaboratable
<alyssa>
Ooh
<adamgreig[m]>
do...do you have a pic16 softcore
<d1b2>
<dragonmux> we do
<adamgreig[m]>
amazing, is it fun
<d1b2>
<dragonmux> OpenPICle as it's actually open source
<adamgreig[m]>
PICle :D
<alyssa>
ResetInserter inserts `with m.If(reset): m.d.sync += [x.eq(RESET VALUE) for all signals x]` then?
<alyssa>
synchronous signals, I guess
<d1b2>
<dragonmux> essentially, yes
<d1b2>
<dragonmux> all signals - sync and async
<tpw_rules>
btw, the DomainRenamer("blah")(SomeElaboratable()) renames the sync domain of the elaboratable to blah
<adamgreig[m]>
does it reset comb signals?
<d1b2>
<dragonmux> Amaranth doesn't as such hold a distinction (because it's not very useful overall) between sync and async
<alyssa>
tpw_rules: Ahh sure
<d1b2>
<dragonmux> it does adamgreig
<tpw_rules>
you can also pass it a dict whose keys are domains to rename and values are their new names
<tpw_rules>
then you can just do m.d.comb += ClockSignal("blah").eq(something_else) to control the clock signal of the new blah domain
<d1b2>
<dragonmux> all signals in Amaranth are plumed to the module reset signal so the whole lot comes up in a known state when you put power to it
<tpw_rules>
(or ResetSignal)
<alyssa>
..ClockSignal? ResetSignal?
<d1b2>
<dragonmux> well, the module reset signals is always ResetSignal, yes, but it's transparent
<alyssa>
(should I be reading Migen docs now I guess?)
<adamgreig[m]>
is that meaningful vs just saying it resets all synchronously-assigned signals?
<d1b2>
<dragonmux> handily this also means (with ResetInserter) that if you can hold a module in reset and when you release it, it's all in a known good state
<d1b2>
<dragonmux> nothing has to settle first
<tpw_rules>
actually, how does ResetInserter interact with multiple domains?
<d1b2>
<dragonmux> each clock domain technically has its own reset signal and ResetInserter forms a resync point with the CDC FFSynchroniser stuff - IWRC
<whitequark>
o/
<tpw_rules>
yeah i knew the first bit, so it just sort of resets them all together?
<d1b2>
<dragonmux> (it's either that or it just.. doesn't.. and the reset signal is only sync against m.d.sync - we'd have to check the code)
<whitequark>
ResetInserter by default adds a reset for the sync domain
<d1b2>
<dragonmux> ah, okie
<whitequark>
if you don't provide an explicit dict of domains to be reset
<d1b2>
<dragonmux> thank you whitequark! ๐
<whitequark>
that's high on the list of things i should document once i recover from surgery
<whitequark>
ClockSignal/ResetSignal implement weak binding for clock domain signals
<alyssa>
whitequark: Oh hi :)
<tpw_rules>
dict? can you reset domains with different signals?
<whitequark>
since you can change what domain they end up referring to with DomainRenamer
<alyssa>
thank you for amaranth this has been really fun :-)
<whitequark>
and, similarly, since you can refer to domains that don't exist yet
<whitequark>
hi alyssa !
<whitequark>
i'm glad ^__^
<alyssa>
("Fun? Not useful?" "I don't even own an FPGA, I have no business deciding usefulness of an HDL")
<adamgreig[m]>
I'm not sure how it would make sense anyway though
nak has joined #amaranth-lang
<adamgreig[m]>
but e.g. there I have an output with reset=1 that's combinatorially eq a+b, with a resetting to 2 and b resetting to 3, and while held in reset o is equal to 5 (a+b), not 1 (its "reset" value)
<whitequark>
comb signals are not reset by ResetInserter
<adamgreig[m]>
I should have just checked there instead of cooking up a testcase :P
<adamgreig[m]>
in essence it never makes sense for a comb-assigned signal to have a reset value, does it?
<adamgreig[m]>
like it will just always take on the result of its assignment instead
<adamgreig[m]>
(I appreciate it's a big vague to say "comb-assigned signal"...)
<d1b2>
<dragonmux> oh, right.. comb resets only determine value when not given an assignment in a m.d.comb block
<whitequark>
so, it makes sense to do something like this:
<whitequark>
s = Signal(reset=1)
<whitequark>
m.d.comb += s.eq(0)
<whitequark>
with m.If(clear):
<d1b2>
<dragonmux> ie, if you define a signal and use it to drive an output, but don't drive it in some manner, then the reset value is constant propagated
<adamgreig[m]>
aah, yep, that makes sense
<adamgreig[m]>
a 'default' value
<whitequark>
yes.
<adamgreig[m]>
rather than an 'initial' value for a sync signal, I guess
<adamgreig[m]>
but in that case ResetInserter still wouldn't affect it - it's just always ~clear
<adamgreig[m]>
(which of course might be affected by resetinserter)
<whitequark>
yes
<alyssa>
whitequark: to clarify, that snippet is the same as "s = ~clear"?
<whitequark>
yes
<alyssa>
cool, thanks
<alyssa>
The feeling of satisfaction of getting HDL stuff working exceeds that of software, idk :)
<alyssa>
That's prob the novelty
<adamgreig[m]>
it hasn't gotten old yet :D
<alyssa>
unit test caught an issue I missed. though I think it's correct and my unit test was wrong. but my expectations needed fixing :'D
Vonter has joined #amaranth-lang
Degi_ has joined #amaranth-lang
Degi has quit [Ping timeout: 256 seconds]
Degi_ is now known as Degi
Degi_ has joined #amaranth-lang
mwk_ has joined #amaranth-lang
cyrozap-ZNC has joined #amaranth-lang
Degi has quit [*.net *.split]
mwk has quit [*.net *.split]
cyrozap has quit [*.net *.split]
alanvgreen has quit [*.net *.split]
richardeoin has quit [*.net *.split]
Wolfvak has quit [*.net *.split]
Degi_ is now known as Degi
cyrozap-ZNC has quit [Client Quit]
cyrozap has joined #amaranth-lang
Wolfvak has joined #amaranth-lang
alanvgreen has joined #amaranth-lang
richardeoin has joined #amaranth-lang
nak has quit [Ping timeout: 240 seconds]
nak has joined #amaranth-lang
Vonter has quit [Ping timeout: 240 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Read error: Connection reset by peer]
Vonter has joined #amaranth-lang
cr1901_ has joined #amaranth-lang
cr1901 has quit [Ping timeout: 240 seconds]
Vonter has quit [Ping timeout: 245 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Ping timeout: 240 seconds]
Vonter has joined #amaranth-lang
FL4SHK has quit [Ping timeout: 250 seconds]
FL4SHK has joined #amaranth-lang
Vonter has quit [Ping timeout: 250 seconds]
Vonter has joined #amaranth-lang
nak has quit [Ping timeout: 260 seconds]
Vonter has quit [Read error: Connection reset by peer]
nak has joined #amaranth-lang
Vonter has joined #amaranth-lang
Vonter has quit [Read error: Connection reset by peer]
Vonter has joined #amaranth-lang
Vonter has quit [Ping timeout: 260 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Read error: Connection reset by peer]
Vonter has joined #amaranth-lang
Vonter has quit [Ping timeout: 240 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Ping timeout: 240 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Ping timeout: 245 seconds]
Vonter has joined #amaranth-lang
Vonter has quit [Ping timeout: 252 seconds]
bvernoux has joined #amaranth-lang
Vonter has joined #amaranth-lang
bl0x_ has quit [Ping timeout: 250 seconds]
bl0x_ has joined #amaranth-lang
Vonter has quit [Ping timeout: 250 seconds]
Vonter has joined #amaranth-lang
<alyssa>
I'm trying to understand how timing works for the simulator.. values assigned from simulation seem to arrive a cycle "late". I'm not familiar enough with hardware to know if the same issue happens with real hardware or not.
<alyssa>
The simple example-- a module has an address signal out and a data signal in. Assuming memory is faster than the module's clock, is it reasonable for the module to "write an address on even cycles, read back data from that address on odd cycles", one cycle delay only?
<alyssa>
Naively I think so. Though maybe that requires the clock for the module feeding it data to be 90 degrees out of phase.
<alyssa>
At any rate, when simulating that module, the following doesn't work "yield; address = yield dut.address; yield data.eq(TEST_READ(address)); yield;"
<alyssa>
(The data reaches the module a cycle late.)
<alyssa>
I think that's explained by the simulated test code running in-phase with the module's clock itself?
<alyssa>
Anyway ... I'm trying to understand if I'm making a silly mistake, if this is a limitation of simulation, or this is a fundamental limitation of hardware and I'd need to clock the system 2x higher to get that timing.
<adamgreig[m]>
essentially your "yield bla.eq(foo)" only gets noticed by your logic after the next yield
<alyssa>
right, by which point we've missed the cycle
<adamgreig[m]>
normally memory runs at the module's clock, so if you present an address by one clock edge, the data is valid only on the next clock edge
<nak>
think of yielding as writing to a register, that's how I manage not to mess up the timing too badly usually
<alyssa>
adamgreig[m]: Right... but I think the data is valid only two clocks later because of the linked issue
<adamgreig[m]>
is your module address wired comb or sync to the memory address?
<alyssa>
(It takes 1 clock for the test case to get the address, and then takes another clock for the data to get noticed)
<adamgreig[m]>
but yes, that's basically what you're seeing, the values you push into the testbench apply after you next call yield, and then you need to call yield again for the memory to react to them
<adamgreig[m]>
it's pipelined though, as it were, you could push the next address after the first yield and get the second data right after the first data
<alyssa>
Looking through the issue tracker this seems to be "simulator issue" and not design one, if I understand
<adamgreig[m]>
if you could change testbench inputs on the falling clock edge, they'd be noticed on the next rising clock edge, so you'd save that cycle of latency
<adamgreig[m]>
and it would be much easier to understand what was going on from the vcd dumps, heh
<alyssa>
yeah, I think that's what I want (and what the original hardware I'm trying to model did back when my parents were young :-p)
<adamgreig[m]>
in principle you can do this with multiple clocks in the simulator, one inverted from the other, but I've not tried seeing if it helps with this particular thing