Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-bit Tristate has only single OE pin, and does not emit errors if you give it a bus #2038

Open
jwise opened this issue Aug 12, 2024 · 2 comments

Comments

@jwise
Copy link
Contributor

jwise commented Aug 12, 2024

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 :-)

@trabucayre
Copy link
Collaborator

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 Tristate is used to drive a single bit: no surprise here one i, o, io, oe.
  • a Tristate is 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?

@jwise
Copy link
Contributor Author

jwise commented Aug 14, 2024

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 :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants