Bug 746 - I2C needs an Open Drain IO Pad
Summary: I2C needs an Open Drain IO Pad
Status: RESOLVED FIXED
Alias: None
Product: Libre-SOC's second ASIC
Classification: Unclassified
Component: source code (show other bugs)
Version: unspecified
Hardware: Other Linux
: --- enhancement
Assignee: Staf Verhaegen
URL:
Depends on:
Blocks: 50 739
  Show dependency treegraph
 
Reported: 2021-11-15 21:09 GMT by Luke Kenneth Casson Leighton
Modified: 2021-11-16 10:31 GMT (History)
3 users (show)

See Also:
NLnet milestone: ---
total budget (EUR) for completion of task and all subtasks: 0
budget (EUR) for this task, excluding subtasks' budget: 0
parent task for budget allocation:
child tasks for budget allocation:
The table of payments (in EUR) for this task; TOML format:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Luke Kenneth Casson Leighton 2021-11-15 21:09:07 GMT
I2C Data needs an Open Drain IO Pad, but if this is to be Muxed then the OD
needs to be controlled via CSRs not hardwired.  i.e. the IOPad needs to be
possible to switch between Open Drain and Push-Push under the control
of GPIO CSRs.

Staf, is this possible? i know you designed the original FlexLib IOPad
to be general purpose, it was hardwired in alliance-check-toolkit iolib
Comment 1 Cesar Strauss 2021-11-15 21:38:55 GMT
It seems that Open Drain can be implemented by a constant zero output, and switching the tri-state/direction/oe control signal (1=tri-state, 0=push-pull to ground).
Comment 2 Luke Kenneth Casson Leighton 2021-11-15 21:47:23 GMT
(In reply to Cesar Strauss from comment #1)
> It seems that Open Drain can be implemented by a constant zero output, and
> switching the tri-state/direction/oe control signal (1=tri-state,
> 0=push-pull to ground).

interesting.  this would however need to be done by a mux, where
"if the output is zero, also flip the IOpad out-enable to zero"

which, now that i think about it, can be handled by the GPIO
subsystem.

my only concern is: timing. but, hey, Open Drain does not switch
quickly anyway.  I2C maxes out at 400 khz.
Comment 3 Staf Verhaegen 2021-11-15 22:23:47 GMT
I don't see the problem. IC controller will deliver i, o, oe signals; if you want to MUX it with a signal that is only output you need to mux 1 on oe. If you want to mux it with only input signal you need to mux 0 on oe pin.
Comment 4 Luke Kenneth Casson Leighton 2021-11-15 22:46:25 GMT
(In reply to Staf Verhaegen from comment #3)
> I don't see the problem. IC controller will deliver i, o, oe signals; if you
> want to MUX it with a signal that is only output you need to mux 1 on oe. If
> you want to mux it with only input signal you need to mux 0 on oe pin.

I2C is very strange, it is a shared cooperative bus.  even when one master
one slave the SDA data line is timeshared between them.

however SDA it is not a push-push driven line: it is Open Drain with
external pullup 2.2 kOhm resistors at each peripheral.

so you are only allowed to pull it to GND, to signal a zero,
and to indicate a 1 it must be put tristate, letting the 2.2k
resistor bring the line HI.

(actually that might be the other way round: to signal
a *one* pull to GND, to signal a zero it must be tristated)

the danger of multiple slaves going amok and accidentally
getting into a current fight by one trying to push HI and
the other push the line LO if ever they get out of sync is thus
avoided.

two push-push drivers would be a short circuit and would damage
one or both IO Pads.
Comment 5 Luke Kenneth Casson Leighton 2021-11-15 22:52:20 GMT
(In reply to Cesar Strauss from comment #1)
> It seems that Open Drain can be implemented by a constant zero output, and
> switching the tri-state/direction/oe control signal (1=tri-state,
> 0=push-pull to ground).

(edited, i got it hi not lo)

# Open Drain Mode, made with a standard FlexLib IOlib cell
with m.If(sda.oe):
  # Open Drain Output
  with m.If(sda.o == 1):
    # HI output is floating
    comb += pad.oe.eq(0)
    comb += pad.o.eq(0)
  with m.Else():
    # LO output, drive output to GND.
    comb += pad.oe.eq(1)
    comb += pad.o.eq(0)
with m.Else():
    comb += pad.oe.eq(0)
    comb += sda.i.eq(pad.i)

this will do the job, Staf: no need to change IOLib
Comment 6 Jacob Lifshay 2021-11-15 23:53:57 GMT
(In reply to Luke Kenneth Casson Leighton from comment #5)
> # Open Drain Mode, made with a standard FlexLib IOlib cell
simplified:
comb += pad.o.eq(0)
comb += pad.oe.eq(sda.oe & ~sda.o)
comb += sda.i.eq(pad.i)
# i is always connected, allowing us to later add support for detecting when the i2c bus is being driven by two or more endpoints at once by checking that i matches what we were trying to transmit.
Comment 7 Staf Verhaegen 2021-11-16 09:04:09 GMT
(In reply to Luke Kenneth Casson Leighton from comment #5)
> (In reply to Cesar Strauss from comment #1)
> > It seems that Open Drain can be implemented by a constant zero output, and
> > switching the tri-state/direction/oe control signal (1=tri-state,
> > 0=push-pull to ground).
> 
> (edited, i got it hi not lo)
> 
> # Open Drain Mode, made with a standard FlexLib IOlib cell
> with m.If(sda.oe):
>   # Open Drain Output
>   with m.If(sda.o == 1):
>     # HI output is floating
>     comb += pad.oe.eq(0)
>     comb += pad.o.eq(0)
>   with m.Else():
>     # LO output, drive output to GND.
>     comb += pad.oe.eq(1)
>     comb += pad.o.eq(0)
> with m.Else():
>     comb += pad.oe.eq(0)
>     comb += sda.i.eq(pad.i)
> 
> this will do the job, Staf: no need to change IOLib

This should not be needed, your I2C master should already make sure oe=0 when wanting 1 on output and oe=1 + o=0 when wanting 0 on output.
Comment 8 Staf Verhaegen 2021-11-16 09:05:36 GMT
> This should not be needed, your I2C master should already make sure oe=0
> when wanting 1 on output and oe=1 + o=0 when wanting 0 on output.

What I have seen is an I2C controller that puts always 0 on o and then control the output by only enabling or disabling oe.
Comment 9 Luke Kenneth Casson Leighton 2021-11-16 09:58:40 GMT
(In reply to Staf Verhaegen from comment #7)

> This should not be needed, your I2C master should already make sure oe=0
> when wanting 1 on output and oe=1 + o=0 when wanting 0 on output.

this is for an I2C master.

in STM32F Embedded Controllers, for convenience, it is the PHY that
[automatically] does this OE/O trick for you, and to do so the pinmux
GPIO settings must put the Pad into this "Open Drain" config mode.

(the ability to set an OD Mode has been standard practice for the entire
STM32F range since its inception)

if bit-banging as a software-controlled pad, then yes absolutely
it is perfectly fine to do - under software control - "rewrite
GPIO to OE and switch O".

...HOWEVER...

if there are separate CSR memory-mapped registers for OE as separate
and distinct from O

OR

if, internally, the CSRs are split (wishbone bus 32-bit to 8-bit)
internally such that OE is set on one clock cycle and O is set on
another

then you have a problem: glitching of the output.

so, software bit-banging control to emulate I2C has two problems:

1) it's bit-banging.  it takes up CPU cycles and requires great care
   in the design of the bit-banging.
2) very careful design of the GPIO CSR Banks is required

compared to adding an Open Drain Mode in the GPIO CSR Bank, which
is what all STM32F Series ECs do, the problems go away, both at
the hardware level and the software level (even if bit-banging
is used).

(In reply to Staf Verhaegen from comment #8)
> > This should not be needed, your I2C master should already make sure oe=0
> > when wanting 1 on output and oe=1 + o=0 when wanting 0 on output.
> 
> What I have seen is an I2C controller that puts always 0 on o and then
> control the output by only enabling or disabling oe.

interesting! yes that would work.  and would avoid output glitching
in software-level bit-banging.

Open Collector would be "always put a 1 on o".

anyway: the main point of this bugreport was: "is any change needed
to FlexLib IOPad Cell" and the answer is definitely "no".  so i am
closing this bugreport as resolved.
Comment 10 Luke Kenneth Casson Leighton 2021-11-16 10:31:59 GMT
as an fyi:

https://github.com/libopencm3/libopencm3/blob/ed2aada3e8c84d085f4f3451a9b9cdf36b893d62/include/libopencm3/stm32/common/gpio_common_f234.h#L192

/* --- GPIOx_OTYPER values ------------------------------------------------- */

/** @defgroup gpio_output_type GPIO Output Pin Driver Type
@ingroup gpio_defines
@{*/
/** Push Pull */
#define GPIO_OTYPE_PP			0x0
/** Open Drain */
#define GPIO_OTYPE_OD			0x1