CodiodeCodiode
Home
Problem Solving
Skill Tracks
My Assignments
Contests
Leaderboard
Community
Settings
Codiode/Problems/Sequential Logic

I2C Clock Stretching Support

HardVerilog / SystemVerilogBuild

I2C is a widely used two-wire protocol where a fast master device often needs to communicate with a slow slave device. To prevent the master from overwhelming the slave with data, the I2C protocol includes a synchronization mechanism called clock stretching. When the master releases the clock line (SCL) to float high, a slow slave can hold the line low to pause the master. If the master ignores this and continues its internal state machine, data corruption occurs immediately.

You are designing the SCL generator state machine for an I2C master. When enabled, the generator creates a clock pattern by alternating between driving SCL low and releasing it to float high. A complete cycle consists of three distinct phases: 1. LOW phase: The master drives SCL low for exactly 4 clock cycles. 2. WAIT phase: The master releases SCL and waits for the bus to actually go high. If the external bus remains low, the slave is stretching the clock. The master must hold its state and assert a status flag. 3. HIGH phase: Once the bus is observed high, the master waits for exactly 4 clock cycles with SCL released.

| Signal | Direction | Width | Description | |--------|-----------|-------|-------------| | clk | input | 1 | Positive-edge triggered clock | | rst_n | input | 1 | Asynchronous active-low reset | | en | input | 1 | Enable signal; starts SCL generation | | scl_i | input | 1 | Sampled state of the external SCL bus | | scl_drive | output | 1 | Drive control: 1 pulls SCL low, 0 releases SCL to float high | | stretch_active | output | 1 | Combinational status: 1 when the master is in the WAIT phase and scl_i is 0 |

Timing and Reset Rules

  • Clock and Reset: clk is posedge triggered. rst_n is asynchronous and active-low. On reset, the FSM enters the IDLE state, and all outputs and internal counters clear to 0.
  • Starting: When en is 1 in the IDLE state, the FSM transitions to the LOW phase on the next clock edge.
  • LOW Phase: Lasts exactly 4 clock cycles. Output scl_drive must be 1.
  • WAIT Phase: After the LOW phase, the FSM enters the WAIT phase and scl_drive becomes 0. The FSM evaluates scl_i. If scl_i is 0, it remains in the WAIT phase. If scl_i is 1, it transitions to the HIGH phase on the next clock edge.
  • HIGH Phase: Lasts exactly 4 clock cycles. Output scl_drive must be 0.
  • Finishing: After the HIGH phase, the FSM checks en. If en is 1, it loops directly back to the LOW phase. If en is 0, it returns to IDLE.

Worked Trace

Cycle 1: rst_n=0 → state=IDLE, scl_drive=0, stretch_active=0 Cycle 2: rst_n=1, en=1 → state=LOW, count=1, scl_drive=1, stretch_active=0 Cycle 3: en=1 → state=LOW, count=2, scl_drive=1, stretch_active=0 Cycle 4: en=1 → state=LOW, count=3, scl_drive=1, stretch_active=0 Cycle 5: en=1 → state=LOW, count=4, scl_drive=1, stretch_active=0 Cycle 6: en=1, scl_i=0 → state=WAIT, scl_drive=0, stretch_active=1 (slave stretches clock) Cycle 7: en=1, scl_i=0 → state=WAIT, scl_drive=0, stretch_active=1 (stretching continues) Cycle 8: en=1, scl_i=1 → state=HIGH, count=1, scl_drive=0, stretch_active=0 (slave releases clock) Cycle 9: en=1 → state=HIGH, count=2, scl_drive=0, stretch_active=0 Cycle 10: en=1 → state=HIGH, count=3, scl_drive=0, stretch_active=0 Cycle 11: en=0 → state=HIGH, count=4, scl_drive=0, stretch_active=0 Cycle 12: en=0 → state=IDLE, scl_drive=0, stretch_active=0

State Diagram

flowchart LR
    RESET(( )) -->|reset| IDLE
    IDLE((IDLE)) -->|en=1| LOW
    IDLE -->|en=0| IDLE
    LOW((LOW)) -->|count=4| WAIT
    LOW -->|count<4| LOW
    WAIT(["WAIT ★"]):::out -->|scl_i=1| HIGH
    WAIT -->|scl_i=0| WAIT
    HIGH((HIGH)) -->|count=4, en=1| LOW
    HIGH -->|count=4, en=0| IDLE
    HIGH -->|count<4| HIGH
    classDef out fill:#6C5CE7,stroke:#5B4FE8,color:#fff

Waveform

{ "signal": [
  { "name": "clk", "wave": "p..........." },
  { "name": "rst_n", "wave": "01.........." },
  { "name": "en", "wave": "01........0." },
  { "name": "scl_i", "wave": "1....0.1...." },
  {},
  { "name": "state", "wave": "=.===.=.===.", "data": ["IDLE","LOW","WAIT","HIGH","IDLE"] },
  { "name": "count", "wave": "======.=====", "data": ["0","1","2","3","4","0","1","2","3","4","0"] },
  { "name": "scl_drive", "wave": "01...0......" },
  { "name": "stretch_active", "wave": "0....1.0...." }
], "head": { "text": "Master generates SCL and pauses when the slave stretches the clock." } }

Constraints

  • Output scl_drive must be strictly 1 when in the LOW phase, and 0 otherwise.
  • Output stretch_active must be combinationally driven to 1 when the FSM is in the WAIT phase and scl_i is 0.
  • en is evaluated in IDLE to start, and at the end of the HIGH phase to determine the next state. If en drops during the LOW, WAIT, or HIGH phases, the current cycle must still complete.
  • scl_i must be completely ignored during the LOW and HIGH phases.

Topics

FSMProtocolInterfaces

Solve this problem

Write the module in Verilog, SystemVerilog or VHDL. Your submission is compiled and simulated against a real testbench — you get the waveform back, not a stored answer.

This problem is part of Codiode Pro. The statement above is free to read.

Sign in to solveSee what Pro unlocks

The circuit builder and code editor need a desktop screen. On a phone, read the problem here and open it on a laptop to solve.

Related problems

  • Basic D Flip FlopEasy
  • Debug: Missing Edge in Sensitivity ListMedium
  • Read After Write Hazard DetectionEasy
  • Parameterized Interface with ModportsHard
  • Struct Array PipelineHard
  • T Flip Flop from D Flip Flop TemplateEasy
  • Recursive Generate Reduction TreeHard
  • Four Stage Shift RegisterEasy

Browse all problems · Learning tracks