Addressing: 7-bit, Combined Format, 10-bit, Reserved
Draw the first byte from a 7-bit address and R/W, write the combined format for a register read and say why a STOP in the middle is a bug, decode a 10-bit address, and name every reserved address.
- Lesson
- P8.4
- Time
- 40 min
- Runs on
- Your browser
- Check
- 4 questions
A datasheet said "device address A0h", so the firmware put 0xA0 in the controller's 7-bit address register. The hardware shifted it left and sent 0x40: address 0x20, write. A different device at 0x20 acknowledged and accepted the EEPROM's data.
| What the firmware means | Byte on the wire | Decoder prints | Result |
|---|---|---|---|
| 7-bit address 0x50, write | 0xA0 = 1010 000 0 | 50 W | ACK |
| 7-bit address 0x50, read | 0xA1 = 1010 000 1 | 50 R | ACK |
"Address 0x50" sent as the whole byte (eight_bit_addr) | 0x50 = 0101 000 0 | 28 W | NACK: nobody at 0x28 |
Measured: I2C S 28 W NA P. Datasheets quote either the 7-bit address or the 8-bit write byte; the wire carries only the byte.
The First Byte
Seven address bits and one direction bit make the first byte: R/W = 0 to write, 1 to read (§3.1.10, Fig. 10). The target at 0x50 answers to 0xA0 and 0xA1. The fix for the hook is a drawing of the byte, not a firmware patch.
A low lane of 1 pulls the net down. The pull-up raises it only when every device releases.
Read phases and sample points (quarter bit)
- START: columns 2 through 3.
- 0xa0 + ACK: columns 4 through 39.
- 0x3c + ACK: columns 40 through 75.
- START: columns 76 through 79.
- 0xa1 + ACK: columns 80 through 115.
- 0x3c + NACK: columns 116 through 151.
- STOP: columns 152 through 155.
- SDA sampled at 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126, 130, 134, 138, 142, 146, 150.
- Cycle 38
Address 0x50 with W: 0xA0 on the wire. The target acknowledges.
- Cycle 74
The first data byte of a write is the register pointer, here 0x3C (§3.1.10 note 1).
- Cycle 78
Sr, not P: the bus stays busy, so no other controller can move the pointer now.
- Cycle 114
Address 0x50 with R: 0xA1. From here the target drives the data.
- Cycle 150
The register's byte, NACKed by the controller, then STOP.
Generated and decoded back. The bench prints the same transaction: I2C S 50 W A 3c A Sr 50 R A 3c NA P.
Why the Read Uses Sr
Fig. 13 reads a register as S, address + W, pointer, Sr, address + R, data, NACK, P. Whether the pointer increments after each byte is the device designer's choice (note 2); this target increments.
- STOP between
The pointer is written, then P frees the bus.
otherstarts and moves the pointer. The read returns the wrong register: measuredvalue = 7f. - Repeated START
With Sr the bus stays busy until the read is done;
otherwrites afterwards. Measured:value = 3c.
Read the diagram as text
- ctrl writes pointer 0x3C: S 50 W 3c, then P
- Bus free: 100 µs of firmware
- other writes pointer 0x7F: S 50 W 7f P
- ctrl reads: S 50 R: gets register 0x7F
- With Sr instead of P: the bus never goes free
- ctrl writes pointer 0x3C → Bus free: P
- Bus free → other writes pointer 0x7F: S
- other writes pointer 0x7F → ctrl reads: P, S
- ctrl writes pointer 0x3C → With Sr instead of P: Sr
Both runs use the same bench with a second controller that waits for a free bus.
I2C S 50 W A 0f A Sr 50 R A 0f A 00 NA P autoinc_wrap_bug: 0x0F + 1 read as 0x00
value ff, stops 0 undefined ignores_sr10-bit Addresses
A 10-bit address takes two bytes: 1111 0XX + R/W with XX the top two bits, then the low eight bits (§3.1.11, Fig. 14). Several targets may acknowledge the first byte; exactly one acknowledges the second. A read repeats only the first byte after Sr, with R (Fig. 15).
A low lane of 1 pulls the net down. The pull-up raises it only when every device releases.
Read phases and sample points (quarter bit)
- START: columns 2 through 3.
- 0xf4 + ACK: columns 4 through 39.
- 0xaa + ACK: columns 40 through 75.
- 0x3c + ACK: columns 76 through 111.
- START: columns 112 through 115.
- 0xf5 + ACK: columns 116 through 151.
- 0x3c + NACK: columns 152 through 187.
- STOP: columns 188 through 191.
- SDA sampled at 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 118, 122, 126, 130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186.
- Cycle 38
0xF4 = 1111 0 10 0: the 10-bit marker, the top two address bits (10), W. Every 10-bit target with top bits 10 may acknowledge.
- Cycle 74
0xAA: the low eight bits. Only the target at 0x2AA acknowledges this one.
- Cycle 114
To read: Sr, then only the first byte again, now with R (0xF5).
- Cycle 186
The data byte, NACKed by the controller.
Generated and decoded back. The bench's 10-bit target answers: I2C S 7a W A aa A 3c A Sr 7a R A 3c NA P. Its 7-bit decoder shows the first byte as the field 7a; P8.10 decodes 10-bit headers.
Reserved Addresses
Two groups of eight addresses are reserved: 0000 XXX and 1111 XXX (Table 4). Only 1111 0XX starts a 10-bit address; 1111 1XX is the Device ID.
| Address bits, R/W | Table 4 meaning | What the bench does | Measured |
|---|---|---|---|
| 0000 000, 0 | general call | this target ACKs 06h and resets after its ninth clock | I2C S 00 W A 06 A P |
| 0000 000, 1 | START byte (§3.1.15) | nobody ACKs (note 2); Sr follows | I2C S 00 R NA Sr 50 W A P |
| 0000 001, X | CBUS address | I2C devices must not respond (note 3) | I2C S 01 W NA P |
| 0000 010, X / 0000 011, X | other bus formats / future | not used on this bench | – |
| 0000 1XX, X | Hs-mode controller code | NACKed by every device (P8.9) | I2C S 05 W NA P |
| 1111 1XX, 1 | Device ID (§3.1.17) | this target has none | – |
| 1111 0XX, X | 10-bit addressing (§3.1.11) | the 7-bit target NACKs 0xF4 | I2C S 7a W NA P |
UM10204 Table 4 with the bench's answer per row. A reserved address may be used as a target address only when its purpose will never be needed on that bus (§3.1.12).
The general call 0000 0000 carries a second byte (§3.1.13). 06h resets the devices that support it and loads their programmable address bits; 04h loads without reset; 00h is not allowed. A second byte with bit 0 set is a hardware general call carrying the sender's own address. Software reset is optional (§3.1.14). This target implements 06h when general_call and software_reset are enabled. Its fixed ADDRESS is unchanged; restoring mem[i] = i and pointer zero is this teaching model's reset convention. Unsupported commands NACK.
Measured after changing register 06h to 96h and the pointer to 3Ch, then sending the reset: ack 1, resets 1, pointer 00, mem[0x06] 06.
Try It
ctrl writes the pointer 0x3C, sends STOP, and waits 100 µs before the read, as firmware between two driver calls would. other waits for the first STOP on the bus, then writes the pointer 0x7F.
Delete the line with ctrl.stop(); #100000; so the read follows with a repeated START. other now finds the bus busy until the read is done, and the value is 3c.
value = 7fRead included file: i2c_ctrl.sv
`timescale 1ns/1ns
module i2c_ctrl(inout wire scl,sda,output reg scl_low=0,sda_low=0);
parameter time TLOW=1300,THIGH=1200,HOLD=100,TBUF=1300,TIMEOUT=20000,THD_STA=600,TSU_STA=600,TSU_STO=600;
bit ctrl_ignores_scl=0,ctrl_ignores_sda=0,eight_bit_addr=0,stop_between=0,data_change_scl_high=0,clear_eight_clocks=0,no_stop_after_clear=0,hold_zero_early=0,short_tbuf=0,fast_scl=0;
bit loser_keeps_clocking_past_byte=0,loser_no_target_mode=0,drops_on_identical=0,arb_on_scl_low=0;
bit lost=0,timed_out=0,active=0,target_mode=0;int clear_pulses=0;time stopped=0;
assign scl=scl_low?1'b0:1'bz;assign sda=sda_low?1'b0:1'bz;
task automatic wait_clock();time began;began=$time;
if(!ctrl_ignores_scl)while(scl!==1'b1&&!timed_out)begin #10;if($time-began>=TIMEOUT)begin timed_out=1;scl_low=0;sda_low=0;end end
endtask
task automatic high_time();time began;began=$time-1;while(scl===1'b1&&$time-began<(fast_scl?100:THIGH))#1;endtask
task automatic start();
if(!active)begin lost=0;timed_out=0;target_mode=0;if($time-stopped<(short_tbuf?100:TBUF))#((short_tbuf?100:TBUF)-($time-stopped));end
else begin scl_low=1;#(TLOW);end
sda_low=0;#(HOLD);scl_low=0;wait_clock();#(TSU_STA);
if(!timed_out)begin sda_low=1;#(THD_STA);scl_low=1;active=1;end
endtask
task automatic stop();
if(!lost&&!timed_out)begin scl_low=1;#(HOLD);sda_low=1;#(TLOW);scl_low=0;wait_clock();#(TSU_STO);sda_low=0;stopped=$time;#1;end
active=0;scl_low=0;sda_low=0;
endtask
task automatic bit_cycle(input bit value,input bit arbitrate,output bit sampled);
sampled=1;
if(!timed_out&&(!lost||loser_keeps_clocking_past_byte))begin
scl_low=1;#(hold_zero_early?1:HOLD);sda_low=!value;
if(arb_on_scl_low&&arbitrate&&value&&sda===0)lost=1;
#(fast_scl?100:TLOW-(hold_zero_early?1:HOLD));scl_low=0;wait_clock();#1;sampled=sda;
if(arbitrate&&!ctrl_ignores_sda&&((value&&!sampled)||drops_on_identical))begin
lost=1;sda_low=0;target_mode=!loser_no_target_mode;
end
if(data_change_scl_high)begin #100;sda_low=!sda_low;end
high_time();scl_low=1;
if(lost&&!loser_keeps_clocking_past_byte)begin scl_low=0;sda_low=0;end
end
endtask
task automatic write_byte(input reg [7:0] value,output bit ack);bit sampled;
for(int b=7;b>=0;b--)bit_cycle(value[b],1,sampled);
bit_cycle(1,0,sampled);ack=!sampled;sda_low=0;
endtask
task automatic read_byte(input bit ack,output reg [7:0] value);bit sampled;
for(int b=7;b>=0;b--)begin bit_cycle(1,0,sampled);value[b]=sampled;end
bit_cycle(!ack,0,sampled);sda_low=0;
endtask
task automatic address7(input reg [6:0] addr,input bit rd,output bit ack);
write_byte(eight_bit_addr?{1'b0,addr}:{addr,rd},ack);
endtask
task automatic combined_read(input reg [6:0] addr,input reg [7:0] pointer,output reg [7:0] value);
bit ack;start();address7(addr,0,ack);write_byte(pointer,ack);if(stop_between)stop();start();address7(addr,1,ack);read_byte(0,value);stop();
endtask
task automatic bus_clear(output bit cleared);
bit sampled;clear_pulses=0;lost=0;timed_out=0;active=1;sda_low=0;scl_low=0;wait_clock();
while(sda===0&&clear_pulses<(clear_eight_clocks?8:9)&&!timed_out)begin bit_cycle(1,0,sampled);#(HOLD);if(!timed_out)clear_pulses++;end
cleared=sda===1&&!timed_out;
if(!no_stop_after_clear)stop();else begin active=0;scl_low=0;sda_low=0;end
endtask
endmodule
Read included file: i2c_target.sv
`timescale 1ns/1ns
module i2c_target #(parameter [9:0] ADDRESS=10'h050,parameter TEN_BIT=0)
(inout wire scl,sda,output reg scl_low=0,sda_low=0,output wire high_drive);
bit ack_not_released=0,ack_wrong_clock=0,stretch_never_released=0,wrong_addr_ack=0,no_stop_detect=0,autoinc_wrap_bug=0,push_pull_target=0,ignores_sr=0;
// Legal receiver responses, not faults. rx_limit counts post-address bytes,
// including the pointer byte; -1 is unlimited. A busy address is declined once.
bit busy_nack=0;int rx_limit=-1,rx_count=0;
// Optional general-call software reset: UM10204 §§3.1.13–14. This model
// restores mem[i]=i and ptr=0; ADDRESS is fixed, not a programmable register.
// Fault switches and bench configuration are not device reset state.
time stretch_ns=0;bit general_call=1,software_reset=1,general_selected=0,reset_pending=0;int resets=0;
reg [7:0] mem[0:255];reg [7:0] ptr=0,shift=0,tx=0;
int state=0,count=0,bytes=0;bit active=0,selected=0,reading=0,need_ptr=1,ack_value=0,ack_active=0,low_addr_next=0,addressed10=0,reading_ack=0;
// state: 0 receive, 1 receiver ACK, 2 transmit, 3 controller ACK, 4 ignore.
assign high_drive=push_pull_target&&!sda_low;
assign sda=sda_low?1'b0:high_drive?1'b1:1'bz;assign scl=scl_low?1'b0:1'bz;
initial for(int i=0;i<256;i++)mem[i]=i[7:0];
function automatic [7:0] increment(input reg [7:0] p);increment=autoinc_wrap_bug?((p+1)&8'h0f):p+1'b1;endfunction
always @(negedge sda)if(scl===1)begin
if(!(ignores_sr&&active))begin active=1;selected=0;state=0;count=0;bytes=0;rx_count=0;shift=0;reading=0;ack_active=0;sda_low=0;low_addr_next=0;general_selected=0;reset_pending=0;end
end
always @(posedge sda)if(scl===1&&active&&!no_stop_detect)begin active=0;selected=0;state=0;count=0;sda_low=0;addressed10=0;end
always @(posedge scl)if(active)begin
if(state==0)begin
shift={shift[6:0],sda};count++;
if(count==8)begin
ack_value=0;
if(bytes==0)begin
reading=shift[0];
general_selected=general_call&&shift==0;
if(general_selected)begin selected=1;low_addr_next=0;end
else if(TEN_BIT)begin selected=(shift[7:3]==5'b11110&&shift[2:1]==ADDRESS[9:8]&&(!reading||addressed10));low_addr_next=!reading;end
else selected=(shift[7:1]==ADDRESS[6:0]||(wrong_addr_ack&&shift[7:1]==ADDRESS[6:0]+1'b1));
if(selected&&busy_nack)begin selected=0;busy_nack=0;end
ack_value=selected;need_ptr=1;
end else if(low_addr_next)begin selected=selected&&shift==ADDRESS[7:0];ack_value=selected;addressed10=selected;low_addr_next=0;end
else if(selected)begin
ack_value=rx_limit<0||rx_count<rx_limit;
if(general_selected)begin
// Unsupported general-call commands are NACKed, never register writes.
ack_value=ack_value&&bytes==1&&software_reset&&shift==8'h06;
reset_pending=ack_value;
end else if(ack_value)begin rx_count++;if(need_ptr)begin ptr=shift;need_ptr=0;end else begin mem[ptr]=shift;ptr=increment(ptr);end end
if(!ack_value)selected=0;
end
bytes++;state=1;ack_active=0;count=0;
end
end else if(state==2)begin count++;if(count==8)begin state=3;count=0;reading_ack=0;end end
else if(state==3&&reading_ack)begin if(sda===0)begin ptr=increment(ptr);tx=mem[ptr];state=2;count=0;end else state=4;end
end
always @(negedge scl)if(active)begin
// Deliberate early-ACK fault: after seven samples, pull for clock 8.
// The address prefix is already known; its R/W bit has not arrived yet.
if(state==0&&count==7&&ack_wrong_clock)begin
if(bytes==0)sda_low=!busy_nack&&((general_call&&shift[6:0]==0)||(TEN_BIT?(shift[6:2]==5'b11110&&shift[1:0]==ADDRESS[9:8]):(shift[6:0]==ADDRESS[6:0]||(wrong_addr_ack&&shift[6:0]==ADDRESS[6:0]+1'b1))));
else sda_low=selected&&(rx_limit<0||rx_count<rx_limit);
end else if(state==1)begin
if(!ack_active)begin
sda_low=ack_value&&!ack_wrong_clock;ack_active=1;
end else begin
sda_low=ack_not_released&&ack_value;ack_active=0;
if(reset_pending)begin
// Finish the ninth ACK clock before resetting and releasing the bus.
for(int i=0;i<256;i++)mem[i]=i[7:0];
ptr=0;shift=0;tx=0;count=0;bytes=0;rx_count=0;need_ptr=1;
selected=0;reading=0;reading_ack=0;low_addr_next=0;addressed10=0;
general_selected=0;reset_pending=0;resets++;sda_low=0;scl_low=0;state=4;
end else if(!selected)state=4;
else if(reading)begin state=2;tx=mem[ptr];count=0;sda_low=!tx[7];end
else state=0;
// Byte-level stretch after the ACK, as in UM10204 §3.1.9. Prepare
// outgoing data before releasing SCL; overlap the controller's LOW.
if(selected&&ack_value&&(stretch_ns>0||stretch_never_released))begin scl_low=1;if(!stretch_never_released)begin #(stretch_ns);scl_low=0;end end
end
end else if(state==2)sda_low=!tx[7-count];
else if(state==3)begin sda_low=0;reading_ack=1;end
else if(state==4)sda_low=0;
end
endmodule
Read included file: i2c_checker.sv
`timescale 1ns/1ns
// Logical Fm timing only. Instantaneous digital pins cannot measure RC rise/fall or
// establish which high-SCL SDA edge was intended as data; driver intent is separate.
module i2c_checker #(parameter time TLOW=1300,THIGH=600,TBUF=1300,THD_STA=600,TSU_STA=600,TSU_DAT=100,THD_DAT=0,TSU_STO=600)
(input wire scl,sda,input wire [2:0] sda_lows,scl_lows,input wire high_drive);
int broken=0,starts=0,stops=0,pulses=0;bit active=0,seen_rise=0,seen_fall=0,start_pending=0;
time rise=0,fall=0,changed=0,stop_time=0,start_time=0;
task automatic bad(input string rule);broken++;$display("I2C CHECK %s at %0t",rule,$time);endtask
always @(posedge scl)begin
if(seen_fall&&$time-fall<TLOW)bad("tlow");
if(active&&$time-changed<TSU_DAT)bad("tsu_dat");rise=$time;seen_rise=1;if(active)pulses++;
end
always @(negedge scl)begin
if(active&&seen_rise&&$time-rise<THIGH)bad("thigh");
if(start_pending&&$time-start_time<THD_STA)bad("thd_sta");start_pending=0;fall=$time;seen_fall=1;
end
always @(sda)begin
if(scl===1)begin
if(sda===0)begin
if(!active&&stops>0&&$time-stop_time<TBUF)bad("tbuf");
if(active&&$time-rise<TSU_STA)bad("tsu_sta");starts++;active=1;start_pending=1;start_time=$time;
end else if(sda===1&&active)begin if($time-rise<TSU_STO)bad("tsu_sto");stops++;active=0;stop_time=$time;end
end else if(seen_fall&&$time-fall<THD_DAT)bad("thd_dat");
changed=$time;
end
always @(high_drive or sda_lows)begin #1;if(high_drive&&|sda_lows)bad("push_pull_contention");end
// A separate intent-aware bench checks ACK ownership/ninth slot, lost-controller
// withdrawal, timeouts and accidental START/STOP. Those are not inferable from SDA alone.
endmodule
Read included file: i2c_mon.sv
`timescale 1ns/1ns
module i2c_mon(input wire scl,sda);
bit active=0,header=1,trace=0;int count=0,frames=0,starts=0,stops=0;reg [7:0] value=0;
// Assemble the whole S ... Sr ... P transaction before one atomic report.
// Incomplete/stuck transactions produce no line; counters remain live.
string text="";
always @(negedge sda)if(scl===1)begin if(active)text={text," Sr"};else text="I2C S";active=1;header=1;count=0;starts++;end
always @(posedge sda)if(scl===1&&active)begin if(trace)$display("%s P",text);text="";active=0;count=0;frames++;stops++;end
always @(posedge scl)if(active)begin
if(count<8)begin value={value[6:0],sda};count++;end
else begin
if(header)text=$sformatf("%s %02h %s",text,value[7:1],value[0]?"R":"W");
else text=$sformatf("%s %02h",text,value);
text={text,sda?" NA":" A"};header=0;count=0;
end
end
endmodule
// Trusted recovery observer. Driver intent distinguishes the STOP setup edge
// from recovery pulses, including when a stuck target prevents an actual STOP.
module i2c_clear_mon(input wire scl,sda,ctrl_sda_low,enabled,done,timed_out);
int pulses=0;bit stopped=0;
always @(posedge enabled)begin pulses=0;stopped=0;end
always @(posedge scl)if(enabled&&!ctrl_sda_low)pulses++;
always @(posedge sda)if(enabled&&scl===1)stopped=1;
always @(posedge done)$display("I2C CLEAR PULSES %0d STOP %0d CLEARED %0d TIMEOUT %0d",pulses,stopped,(sda===1&&!timed_out),timed_out);
endmodule
Check Yourself
Remember
- Seven address bits plus R/W make the first byte; 0x50 writes as 0xA0 and reads as 0xA1.
- A register read writes the pointer, then Sr, then reads; a STOP in between lets another controller move the pointer.
- A 10-bit address is 1111 0XX + R/W and the low byte; a read repeats only the first byte after Sr.
- Table 4 reserves 0000 XXX and 1111 XXX: general call, START byte, CBUS, Hs code, Device ID, 10-bit.
- General call with 06h is the optional software reset; no device acknowledges the START byte.
Practice
- A Register Target with Repeated START Implement · Hard
- Seven Address Bits Plus Direction Fix · Medium