I2C Clock Stretching Support
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:
clkis posedge triggered.rst_nis asynchronous and active-low. On reset, the FSM enters the IDLE state, and all outputs and internal counters clear to 0. - Starting: When
enis 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_drivemust be 1. - WAIT Phase: After the LOW phase, the FSM enters the WAIT phase and
scl_drivebecomes 0. The FSM evaluatesscl_i. Ifscl_iis 0, it remains in the WAIT phase. Ifscl_iis 1, it transitions to the HIGH phase on the next clock edge. - HIGH Phase: Lasts exactly 4 clock cycles. Output
scl_drivemust be 0. - Finishing: After the HIGH phase, the FSM checks
en. Ifenis 1, it loops directly back to the LOW phase. Ifenis 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:#fffWaveform
{ "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_drivemust be strictly 1 when in the LOW phase, and 0 otherwise. - Output
stretch_activemust be combinationally driven to 1 when the FSM is in the WAIT phase andscl_iis 0. enis evaluated in IDLE to start, and at the end of the HIGH phase to determine the next state. Ifendrops during the LOW, WAIT, or HIGH phases, the current cycle must still complete.scl_imust be completely ignored during the LOW and HIGH phases.
Topics
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.
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.