Multi-bit Tristate has only single OE pin, and does not emit errors if you give it a bus
I'm trying to use the following mechanism to drive two pins on a multi-bit bus, and leave the third tristated (Cypress FX3 PMODE):
self.pmode_o = Signal(3)
self.pmode_oe = Signal(3)
self.specials += Tristate(pads.pmode_con, self.pmode_o, self.pmode_oe)
self.comb += self.pmode_oe.eq(3) # USB boot
self.comb += self.pmode_o.eq(3) # USB boot
This generates the following RTL, which was not what I intuitively expected:
wire [2:0] build_with_platform_pmode_o;
wire [2:0] build_with_platform_pmode_oe;
/* ... */
assign build_with_platform_pmode_oe = 2'd3;
assign build_with_platform_pmode_o = 2'd3;
/* ... */
assign fx30_pmode_con0_OUT = (build_with_platform_pmode_o >>> 1'd0);
assign fx30_pmode_con0_OE = build_with_platform_pmode_oe;
assign fx30_pmode_con1_OUT = (build_with_platform_pmode_o >>> 1'd1);
assign fx30_pmode_con1_OE = build_with_platform_pmode_oe;
assign fx30_pmode_con2_OUT = (build_with_platform_pmode_o >>> 2'd2);
assign fx30_pmode_con2_OE = build_with_platform_pmode_oe;
It looks like the implementation of this on Efinix is as such:
io_o = platform.add_iface_io(io_name + "_OUT")
io_oe = platform.add_iface_io(io_name + "_OE")
io_i = platform.add_iface_io(io_name + "_IN")
self.comb += io_o.eq(o >> bit)
self.comb += io_oe.eq(oe)
So I think the workaround here is that I should generate my own for set of Tristates to do this. But the ergonomics would be nicer if Tristate automatically split the OE up if value_bits_sign(oe) > 1 -- or, failing that, if migen.fhdl.specials.Tristate.__init__ threw a ValueError if oe had more than one bit? Certainly it would have saved me a few hours of debugging :-)
Indeed: a Tristate for 1 or more bits uses only one oe. This is true for for efinix but for others vendors too. I'm agree it may be possible to have something to check if oe is a single or multiple bit.
The idea here is to consider two use case:
- a
Tristateis used to drive a single bit: no surprise here onei,o,io,oe. - a
Tristateis used to drive a bus. All bits may be in or out at the same time (SPI flash in quad mode for example, where all 4 IOs are configured has output or input. It's also true for most of the RAM's variant).
So, if you wish to drive separately each bits of a bus you have to instanciate more than one Tristate module.
Could you adds more details on your application?
In my case, I have a thing that is "bus-like" inasmuch as it is a group of signals: the Cypress EZ-USB FX3 has a group of signals called "PMODE" to define how the thing boots. Each signal needs to be configured independently as being either 0, 1, or high-Z, to define how the FX3 should boot.
Ultimately I did end up instantiating three Tristates. I think this is mostly an ergonomics question, rather than a functionality question -- Tristate should either support what I was trying to do, or it should throw an error, but silently coercing oe to its LSB is a painful outcome :)