Protocol 02 · Serial Protocols

I²C Protocol
Open-Drain Bus · 7-Bit Addressing · ACK/NACK

The 2-wire bus that powers almost every sensor, EEPROM, and RTC in embedded systems — learn it from START condition to clock stretching.

SDA + SCL Open-Drain 7-bit Address ACK/NACK Clock Stretching Verilog Master
SCL SDA IDLE START ADDRESS (7-bit) = 0x48 R/W ACK DATA BYTE (8 bits) ACK STOP I²C Write Transaction: START → 7-bit addr (0x48) → W → ACK → 1 data byte → ACK → STOP

1. What is I²C?

I²C (Inter-Integrated Circuit, pronounced "I-squared-C" or "I-two-C") is a 2-wire synchronous serial protocol developed by Philips in 1982. It supports multiple masters and multiple slaves on a single bus — identified by a 7-bit address.

ParameterValue
Wires2 — SDA (data) + SCL (clock)
TopologyMulti-master, multi-slave — all devices share the same 2-wire bus
Addressing7-bit (112 usable) or 10-bit (1008 usable)
Speed modesStandard: 100 kHz · Fast: 400 kHz · Fast+: 1 MHz · High-Speed: 3.4 MHz
DuplexHalf-duplex — SDA carries data in one direction at a time
Error detectionACK/NACK per byte
Pull-up requiredYes — external resistors on SDA and SCL
I²C is the go-to protocol when you need many devices on few wires. A typical embedded board might run 5–10 sensors, a real-time clock, and an EEPROM — all sharing the same 2-wire bus, addressed by their unique 7-bit IDs.

2. SDA, SCL & the Open-Drain Bus

SignalDirectionDescription
SDABidirectionalSerial Data — carries address, data, and ACK bits. Open-drain: device can only pull low or release.
SCLMaster drives, slave can stretchSerial Clock — master generates the clock. Open-drain: slave can hold it low (clock stretching).

Why open-drain?

Open-drain means a device has two states: pull low (drive 0) or release (float). It can never actively drive the line high. External pull-up resistors (connected to VCC) pull released lines high.

This gives a wired-AND effect: the bus is low if any device pulls it low. Multiple devices can pull at the same time with no short-circuit damage — the "winner" is always zero (low).

This property enables: multi-master arbitration (Section 6), clock stretching (Section 5), and safe bus sharing.

Pull-up resistor sizing

Speed ModeTypical RpRule
Standard (100 kHz)4.7 kΩRp_min = (VCC − VOL) / IOL_max ≈ 300 Ω
Fast (400 kHz)2.2 kΩRp_max = rise_time / bus_capacitance
Fast+ (1 MHz)1 kΩSmaller Rp → faster rise, more current

Bus capacitance (traces + device pins) must stay below 400 pF for standard/fast modes. Each device adds ~5–10 pF; keep cable lengths short.

No pull-ups = no I²C. Without external pull-up resistors, SDA and SCL float and the bus never returns high. Always add 4.7 kΩ to VCC on both lines for 100/400 kHz.

3. Bus Transactions — START, Data, ACK, STOP

START condition

SDA falls from HIGH → LOW while SCL is HIGH. This is the unique START signal — no valid data transition happens while SCL is high outside of START/STOP. After START, the master drives SCL low and begins clocking.

Address + R/W byte

The first byte after START is always the address byte: 7 address bits (MSB first) + 1 R/W bit.

Bit positionMeaning
bits [7:1]Device address (7 bits, MSB first)
bit [0]R/W — 0 = write (master sends data), 1 = read (slave sends data)

Example: ADS1115 ADC at address 0x48 (7-bit) for a write → byte = 0x90 = 0b1001_0000.

ACK / NACK

After each 8-bit byte (address or data), the master releases SDA and generates a 9th clock pulse. The receiver responds:

ResponseSDA during 9th clockMeaning
ACKLOW (receiver pulls down)Byte received OK, continue
NACKHIGH (receiver releases)Error, not ready, or end of read transfer

Data bytes

SDA must be stable while SCL is HIGH — transitions only allowed while SCL is LOW. Data is clocked MSB first, 8 bits per byte, followed by 1 ACK bit. The master controls SCL; the sender (master on write, slave on read) drives SDA.

STOP condition

SDA rises from LOW → HIGH while SCL is HIGH. Releases the bus. Any master may issue a REPEATED START (Sr) — a new START without a preceding STOP — to address the same or a different slave while retaining bus ownership.

Transaction summary: START → [addr 7-bit + R/W] → ACK → [data byte] → ACK → … → STOP (or Sr for continued access)

4. 7-bit Addressing

7-bit addresses give 128 slots; 16 are reserved by the I²C spec, leaving 112 usable. Devices often have 1–3 address bits configurable via pins — so you can have multiple instances of the same chip on one bus.

DeviceAddress (7-bit)Configurable bits
ADS1115 ADC0x48–0x4BADDR pin: GND/VCC/SDA/SCL
SSD1306 OLED0x3C or 0x3DSA0 pin
AT24Cxx EEPROM0x50–0x57A0, A1, A2 pins
DS3231 RTC0x68Fixed
MPU6050 IMU0x68 or 0x69AD0 pin
PCF8574 I/O expander0x20–0x27A0, A1, A2 pins
BMP280 pressure sensor0x76 or 0x77SDO pin
Address collision: If two devices share the same address and neither supports address configuration, you must use an I²C multiplexer (e.g., TCA9548A) to place them on separate bus segments.

5. Clock Stretching

A slave that needs processing time can hold SCL LOW after the master releases it. Since SCL is open-drain, the master cannot override this — it must wait until SCL goes high before proceeding. This is clock stretching.

When it happensExample device
After receiving address (processing lookup)Some MCU I²C peripherals
After receiving data (write to flash settling)AT24Cxx EEPROM
Between bytes (ADC conversion in progress)ADS1115 in conversion mode
Not all masters support clock stretching. Some I²C masters (especially bit-bang implementations) don't monitor SCL after driving it — they'll time out or corrupt data. Check your master's datasheet. In Verilog, sample SCL after driving it high and wait until it reads back high.

6. Multi-Master & Arbitration

Multiple masters can coexist on one I²C bus. If two masters start a transaction simultaneously:

  1. Both drive SCL — since open-drain, the clock is the AND of both.
  2. Both monitor SDA while driving it. If Master A tries to drive SDA HIGH while Master B holds it LOW, Master A reads back LOW — it lost arbitration.
  3. The losing master immediately stops driving and releases the bus.
  4. The winning master (the one driving LOW) continues its transaction uninterrupted.

Arbitration naturally favors the transaction with lower address bits — no priority configuration needed. The process is non-destructive: the winning transaction completes normally.

Multi-master I²C is used in systems where multiple CPUs (e.g., main MCU + power manager) must access shared peripherals. In FPGA designs, arbitration is typically handled by a dedicated I²C arbiter module rather than relying on the open-drain property alone.

7. Verilog I²C Master

A synthesizable I²C master FSM. Supports write transactions (START → addr+W → ACK → data → ACK → STOP). The SDA output uses a tristate-style output-enable to implement open-drain in RTL.

module i2c_master #(
  parameter CLK_DIV = 125  // 100 kHz SCL from 25 MHz system clk (25M/125/2 = 100kHz)
)(
  input        clk, rst_n,
  input        start,       // pulse to begin transaction
  input  [6:0] addr,        // 7-bit device address
  input        rw,          // 0=write, 1=read
  input  [7:0] tx_data,     // byte to write
  output reg [7:0] rx_data, // byte read from slave
  output reg   done,        // transaction complete
  output reg   ack_err,     // slave sent NACK
  // open-drain I2C pins (use external pull-ups)
  inout        sda,
  output reg   scl
);

  // ── Clock divider ────────────────────────────────────────────
  reg [$clog2(CLK_DIV)-1:0] clk_cnt;
  reg clk_en;
  always @(posedge clk) begin
    if (!rst_n) begin clk_cnt <= 0; clk_en <= 0; end
    else if (clk_cnt == CLK_DIV-1) begin clk_cnt <= 0; clk_en <= 1; end
    else begin clk_cnt <= clk_cnt + 1; clk_en <= 0; end
  end

  // ── Open-drain SDA ───────────────────────────────────────────
  reg sda_oe, sda_out;
  assign sda = sda_oe ? (1'b0) : 1'bz;  // drive low or release (pull-up takes it high)

  // ── FSM states ───────────────────────────────────────────────
  localparam
    IDLE     = 4'd0, ST_START = 4'd1, ST_ADDR  = 4'd2,
    ST_ACK1  = 4'd3, ST_DATA  = 4'd4, ST_ACK2  = 4'd5,
    ST_STOP  = 4'd6, ST_DONE  = 4'd7;

  reg [3:0] state;
  reg [3:0] bit_cnt;
  reg [7:0] shift_reg;
  reg        scl_phase;   // 0=low half, 1=high half

  always @(posedge clk) begin
    if (!rst_n) begin
      state <= IDLE; scl <= 1; sda_oe <= 0;
      done <= 0; ack_err <= 0; scl_phase <= 0;
    end else if (clk_en) begin
      done <= 0;
      case (state)

        IDLE: begin
          scl <= 1; sda_oe <= 0;  // bus idle (both lines released HIGH)
          if (start) begin
            shift_reg <= {addr, rw};
            bit_cnt   <= 7;
            state     <= ST_START;
          end
        end

        ST_START: begin
          // SDA falls while SCL high → START condition
          sda_oe <= 1;  // pull SDA low
          scl    <= 1;
          state  <= ST_ADDR;
          scl_phase <= 0;
        end

        ST_ADDR: begin
          if (!scl_phase) begin
            scl <= 0;
            sda_oe <= !shift_reg[7];  // drive 0: pull low; drive 1: release
            scl_phase <= 1;
          end else begin
            scl <= 1;
            scl_phase <= 0;
            if (bit_cnt == 0) state <= ST_ACK1;
            else begin shift_reg <= {shift_reg[6:0], 1'b0}; bit_cnt <= bit_cnt - 1; end
          end
        end

        ST_ACK1: begin
          if (!scl_phase) begin
            scl <= 0; sda_oe <= 0;  // release SDA — slave drives ACK
            scl_phase <= 1;
          end else begin
            scl <= 1;
            ack_err   <= sda;   // if SDA=1 → NACK
            shift_reg <= tx_data;
            bit_cnt   <= 7;
            scl_phase <= 0;
            state     <= ST_DATA;
          end
        end

        ST_DATA: begin
          if (!scl_phase) begin
            scl    <= 0;
            sda_oe <= !shift_reg[7];
            scl_phase <= 1;
          end else begin
            scl <= 1;
            scl_phase <= 0;
            if (bit_cnt == 0) state <= ST_ACK2;
            else begin shift_reg <= {shift_reg[6:0], 1'b0}; bit_cnt <= bit_cnt - 1; end
          end
        end

        ST_ACK2: begin
          if (!scl_phase) begin
            scl <= 0; sda_oe <= 0;
            scl_phase <= 1;
          end else begin
            scl <= 1;
            ack_err <= ack_err | sda;
            scl_phase <= 0;
            state <= ST_STOP;
          end
        end

        ST_STOP: begin
          // SDA rises while SCL high → STOP condition
          if (!scl_phase) begin
            scl <= 0; sda_oe <= 1;  // pull SDA low first
            scl_phase <= 1;
          end else begin
            scl <= 1;
            sda_oe <= 0;   // release SDA → HIGH while SCL HIGH = STOP
            state <= ST_DONE;
          end
        end

        ST_DONE: begin done <= 1; state <= IDLE; end
      endcase
    end
  end
endmodule
Synthesis note: The inout sda port becomes a bidirectional pad in synthesis. In FPGA designs, instantiate a IOBUF primitive explicitly for the SDA pin. In simulation, the open-drain behavior (drive-low-or-release) is correctly modeled by assigning 1'bz when not driving.

8. Applications

DeviceTypeAddressNotes
ADS111516-bit ADC0x48–0x4B4-channel, programmable gain, up to 860 SPS
AT24C256EEPROM 256 kbit0x50–0x57Page write 64 bytes, 5 ms write cycle
DS3231RTC0x68Temperature-compensated oscillator, ±2 ppm
SSD1306OLED controller 128×640x3C/0x3DFast mode 400 kHz, command/data register
MPU60506-axis IMU0x68/0x693-axis gyro + 3-axis accel, DMP on-chip
PCF85748-bit I/O expander0x20–0x27Adds 8 GPIO pins via 2 wires
TCA9548AI²C multiplexer 8-ch0x70–0x77Isolates address-conflicting devices on separate segments
MAX17043LiPo fuel gauge0x36SoC%, voltage, sleep mode via I²C
Compare I²C with SPI (faster, more wires) and UART (simpler, point-to-point async). See the full protocol comparison table.