1
\$\begingroup\$

I'm trying to implement I2C on an FPGA, but I couldn't seem to figure out a way to detect clock stretching without external circuits. Is there any way that I can detect the wire (I'm using logic not wire, but I'm just calling it a wire for now) being held low by the slave from the master?

I heard about using tri-state or AND gates, but I couldn't figure out the logic behind this; do I hook both input clock and output clock to the buffer/gate or do I do something else?

I'm pretty sure the input clock (they're same wire, just port declaration here) will be low because it was driven low, so an AND gate will always not work?

If my understanding is correct, the signal should look something like this:

Slave
        __    ___ ___ ___ ___ ___ ___ ___ ___ ______
sda       \__/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/ !ACK
        ____   _   _   _   _   _   _   _   _   _   _
scl_in      \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
        ____   _   _   _   _   _   _   _   _
scl_out     \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_______
Master
        __    ___ ___ ___ ___ ___ ___ ___ ___ ______
sda       \__/_7_X_6_X_5_X_4_X_3_X_2_X_1_X_0_/ !ACK
        ____   _   _   _   _   _   _   _   _
scl_in      \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_______
        ____   _   _   _   _   _   _   _   _   _   _
scl_out     \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/

But the clock is driven by the Master only, so scl_in is essentially scl_out right? If I do any comparison scl_in will just be the same value as the last scl_out right? So how should I check if scl_in is low when it could have just been the falling edge of the clock and nothing is pulling it up yet?

I could check if the ACK is being sent but then I won't know if I need to resend the data because it's not receiving, or if it's just taking time processing. Maybe I just count ACK for 2 cycles?

Does anyone know how I can use Verilog/SystemVerilog to detect a wire being driven both high and low at the same time or how to deal with clock stretching?

\$\endgroup\$
3
  • 1
    \$\begingroup\$ it sounds like you need a way to configure the output as open drain. \$\endgroup\$ Sep 2, 2022 at 23:48
  • \$\begingroup\$ something like master_scl_in = master_scl_out AND slave_scl_out and slave_scl_in = slave_scl_out AND master_scl_out \$\endgroup\$
    – Kartman
    Sep 3, 2022 at 2:15
  • \$\begingroup\$ Have they changed Verilog so there's no inout port any more? \$\endgroup\$
    – The Photon
    Sep 3, 2022 at 2:45

1 Answer 1

0
\$\begingroup\$

I2C signals are "open drain" with resistive pullups.

As such, you don't "drive" a signal high, you cease pulling it down and let the resistive pullup bring the signal high.

That's how the clock stretching works. The Master ceases pulling the SCL signal down but notices that the SCL signal doesn't rise because the Slave continues pulling the SCL signal down.

You can do this multiple ways. You can use a pullup primitive along with an nmos instance. Alternatively, you can put signal strengths on an assign statement aka "assign (strong0, highz1) scl = val;"

You should also probably go look at "inout" ports. I suspect that you need that for what you are doing.

Good luck.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.