... which makes me wonder whether the bug comes from nmigen-soc or nmigen.
Unfortunately, the bug comes from the way you are using csr.Multiplexer. It looks like I did not explain clearly how to use it. Let me try again.
First, the alignment argument is not an amount, but it is the logarithm of amount. That is, if you wanted each element to be aligned to an address that is a multiple of 8, you would specify alignment=3, because log2(8)=3. The idea here is that it removes a lot of range checks (because every positive value is valid), and also simplifies the implementation (because in many places you want log2(alignment).
Second, the alignment argument is not specified in bits, but in words. That is, if you make a CSR bus which is connected to a 8-bit host bus, and you want registers to be densely packed, you should use:
If you make a CSR bus which is connected to a 32-bit host bus, but want the internal CSR accesses to be 8-bit wide to conserve resources, and want registers to start at 32 bit boundaries, you should use:
Oh, and I still consider this issue a bug, it's just a bug in documentation.
The MemoryMap docstring actually mentions it:
alignment : int
Range alignment. Each added resource and window will be placed at an address that is
a multiple of ``2 ** alignment``, and its size will be rounded up to be a multiple of
``2 ** alignment``.