Real I2C devices are used through a handful of transaction patterns: writing a register, reading a register with a repeated START, and reading or writing several registers in one go. A target that needs time can stretch the clock in the middle of a transaction. This article goes through each pattern with the example register target, and shows clock stretching in a simulation.

8 min read | Intermediate

What You'll Learn

  • Write and read device registers over I2C
  • Use a repeated START to change direction without releasing the bus
  • Read and write several bytes with an auto-incrementing pointer
  • Recognize and handle clock stretching

Writing a Register

A register write is one transaction in one direction:


op(START, 0, 0);
op(WRITE, {dev, 1'b0}, 0);               // address + W
op(WRITE, {6'b0, r}, 0);                 // register pointer
op(WRITE, v, 0);                         // data
op(STOP, 0, 0);

The target acknowledges all three bytes. The specification describes this as the controller-transmitter addressing a target-receiver, with the transfer direction never changing.

Reading a Register: the Repeated START

To read a register, the controller must first tell the target which register, which is a write, and then read, which needs a new address byte with R/W = 1. It could send STOP and start again, but another controller could take the bus in between. Instead it sends a repeated START (Sr): a START without a STOP before it. The bus stays busy, and the target keeps its pointer:


op(START, 0, 0);
op(WRITE, {dev, 1'b0}, 0);               // address the target for writing the pointer
op(WRITE, {6'b0, r}, 0);
op(START, 0, 0);                         // repeated START
op(WRITE, {dev, 1'b1}, 0);               // address it again, now for reading
op(READ, 0, 1);                          // one byte, then NACK
op(STOP, 0, 0);

After the second address byte, the direction turns around: the target transmits and the controller acknowledges. To end the read, the controller answers the last byte with a NACK, so that the target releases SDA and the controller can generate STOP.

Quick Check

Q: Why does the controller send NACK, not ACK, after the last byte it reads?

Show Answer

An ACK tells the target to send another byte, so it would start driving SDA with the next bit. The NACK tells it to stop and release SDA, so the controller can generate the STOP condition.

Several Bytes at Once

Most register targets increment their pointer after every byte, like the example target. A controller can then write or read a block of registers in one transaction, which saves the address and pointer bytes each time. EEPROMs use the same idea for pages of memory. How the pointer behaves, whether it wraps and at what boundary, is up to the device designer and is described in its datasheet.

Clock Stretching

A target that cannot keep up may hold SCL low after a byte: the controller releases SCL for the next clock, sees that it stays low, and waits. The specification calls this clock stretching; it is optional, and many targets cannot do it at all. The example target can stretch for a set number of clock cycles after each written data byte:


WDATA: begin
  sda_oe <= 1'b0;
  if (STRETCH != 0)
    hold <= STRETCH;       // need time to store the byte: hold SCL low
end

With the stretch set to 2 µs, a register write and read back still succeed, just later:

Output
7915  write 0x51 reg2 = 0x5a
15925  read  0x51 reg2 = 0x5a

The controller does not need to know that the target stretched: the "wait until SCL is really high" rule in its bit timing absorbs the delay. A controller that ignores SCL and drives the clock blindly breaks with any stretching target.

Quick Check

Q: A controller has a push-pull SCL output. Can it work with a target that stretches the clock?

Show Answer

No. It would drive SCL high while the target pulls it low, a short circuit, and it would never see the stretch. The specification allows a push-pull SCL only for a single controller with no stretching devices on the bus.

Running It in ChipVerify Lab

The example runs the register write and read with clock stretching. Change STRETCH and watch the transaction slow down.

Common Mistakes

Mistake #1: STOP instead of a repeated START

Some targets reset their pointer on STOP, and in a multi-controller system another controller can take the bus. Use a repeated START between the pointer write and the read.

Mistake #2: ACKing the last byte read

The target keeps transmitting and may hold SDA low, so the controller cannot create STOP. NACK the last byte.

Mistake #3: Timeouts too short for stretching

Some targets stretch for milliseconds during internal writes, EEPROMs in particular. Size software and hardware timeouts from the datasheet.

Practice Exercise

Challenge: A Burst Write

Write registers 1, 2 and 3 of target 0x50 with 0x11, 0x22 and 0x33 in a single transaction, then read them back one by one.

Hint

Send the pointer once, then three data bytes before STOP. The target increments its pointer after each one.

Solution

op(START, 0, 0);
op(WRITE, {7'h50, 1'b0}, 0);
op(WRITE, 8'h01, 0);                 // pointer: register 1
op(WRITE, 8'h11, 0);                 // register 1
op(WRITE, 8'h22, 0);                 // register 2 (the pointer incremented)
op(WRITE, 8'h33, 0);                 // register 3
op(STOP, 0, 0);
Output
9675  burst write of 3 bytes from reg1
17685  read  0x50 reg1 = 0x11
25695  read  0x50 reg2 = 0x22
33705  read  0x50 reg3 = 0x33

Three registers in one transaction of six bytes, against nine bytes for three separate writes.

Open the ready-made example in the lab and run the simulation.

Summary

  • A register write is S, address+W, pointer, data, P.
  • A register read writes the pointer, then uses a repeated START and address+R to read.
  • The controller ACKs every byte it reads except the last, which it NACKs before STOP.
  • Auto-incrementing pointers allow multi-byte reads and writes in one transaction.
  • Targets may stretch SCL; controllers must wait until SCL is really high.
Did you find what you were looking for?

Quiz

No quiz questions available for this article.