enjoy-digital / litex

Build your hardware, easily!
Other
2.92k stars 558 forks source link

Modularize CSR integration/code generation #1107

Open thirtythreeforty opened 2 years ago

thirtythreeforty commented 2 years ago

In my design, I need for CSRs to be synchronous with blocks in multiple clock domains; currently, CSRBankArray collects then all into the sys domain regardless of the primary clock domain of the block in question, so the design can't synchronously update registers or use pulses to start/stop state machines.

To fix this I will need to provide a new CSR collection implementation that provides a separate sub-CSR bus for each clock domain, with an async crossing between them. (Not proposing that this becomes the new default, although I do think it would be more or less free for those not using clock domains other than sys.) Roughly:

Clock domain crossing between Wishbone2CSR and CSRBankArrays using a new component

There's a couple issues with LiteX that means this is difficult to do modularly. Questions:

enjoy-digital commented 2 years ago

Thanks @thirtythreeforty, we could indeed improve support for multiple clock domains on the CSR. I'm generally putting the CDC logic between the CSRBankArray and the logic of the core. Before discussing the implementation details for the changes on the CSR collection/CDC, could you provide more information about the current limitations you are facing in your design? I'll maybe have suggestions to at least work around these.

thirtythreeforty commented 2 years ago

It's true that putting the CDC behind the CSRBankArray would be a smaller code change. The reason I was thinking it would be more appropriate to group all the modules in a domain would be that you'd only need a single clock domain crossing per clock domain, rather than one per component per clock domain. I can be persuaded on this, however.

Here's an example use case: I have a multi-bit command register that triggers a capture state machine. The state machine updates multiple other status registers every cycle as it runs (memory address, remaining samples, etc). When stopped, the CPU needs to be able to read and update these to re-arm the module.

It's true that I could probably work around this by using a bunch of single-bit clock domain crossings and PulseSynchronizer (although this wiki section makes me nervous). And then, declare that the CPU can only read registers while the module is stopped. But it makes the CSR dramatically more difficult to use; it's not a single class any more, the CPU has to do a bunch of writes that are logically one write, and the module has to consider all the async behavior of CSRs. It's not an isolated module, most of the chip runs faster than the RAM and CPU, so I figured the effort to retool CSRs would be similar and reusable.

enjoy-digital commented 2 years ago

Due to the nature of the CSR (simple bus with single cycle write, 2 cycles read, no handshake), the solution you are suggesting is not really possible. The CDC would have to happen on the Wishbone bus, before Wishbone2CSR. We would then need to have different groups of CSRBankArray (one for each clock domain) with one Wishbone2CSR/WishboneCDC for each.

This is however a non-trivial change and I would recommend putting the CDC behind the CSRBankArray for now.

You have different solutions for this:

The right architecture on the control/datapath can generally greatly simplify CDC on such cases. Happy to work with you on your specific use case if you want to see if we can find a simple solution.