▼ Follow for more valuable insights
“ On the journey of designing the I2C controller, we have successfully established the clock skeleton (SCL) and the command center (state machine). However, to truly make the data “move” and accurately control the transmission of each bit and byte, we need two key “counters”. This article will delve into the design and implementation of the bit counter and byte counter in I2C transmission, bringing these two “behind-the-scenes heroes” to the forefront, ensuring precise data transmission control for you!”
01 Design Concepts for Bit and Byte Counters
In the previous article, we detailed the design of the SCL clock and the timing state machine. At this point, the I2C master controller has core timing coordination capabilities. This article mainly focuses on the detailed design of the bit counter and the multi-byte read/write byte counter during data transmission.
Timing for Bit Counting During Transmission
We use the logic divider generated SCL_CLK as the timing control clock. The I2C bus protocol stipulates:
-
SDA data switches during the low level of the SCL clock
-
The transmitted byte data is shifted on the bus according to MSB transmission
Based on this, we design the bit counting during transmission as follows:
-
The falling edge of SCL_CLK serves as the timing control trigger
-
Counting occurs only in the write data state (WRITE) and read state (READ)
-
Counting is performed in a decrementing manner
Timing for Byte Counting During Transmission
When the bit counter decrements to 0 in the previous section, the data byte has been fully transmitted. Therefore, the conditions for incrementing the byte counter during transmission are as follows:
-
Write operation: when the bit counter decrements to 0, increment the write byte counter by 1
-
Read operation: when the bit counter decrements to 0, increment the read byte counter by 1
02 Design and Implementation of Bit and Byte Counters The timing logic control clock for the I2C master controller is SCL_CLK. The data on the SDA line switches during the low level of SCL and is valid during the high level. Therefore, for both bit counting and multi-byte counting, the timing logic is executed on the falling edge of SCL_CLK.
Design of the Bit Counter for Transmission
Combining the previous state machine design, we design the bit counter as follows:
-
Counting occurs only in the write data state and read data state.
-
Other states are set to the initial decrement value
In I2C protocol transmission, data is transmitted in bytes (8 bits), so the number of decrements for our bit count is also 8. However, for FPGA developers, we typically count down from 7 to 0; thus, the initial decrement value for our bit counter is set to 7. The resources required for the bit counter are as follows:
| Signal | Type | Bits | Description |
| bit_cnt | reg | 3 | Bit count value |
The timing logic Verilog code for the bit counter is as follows:
reg [2:0] bit_cnt;
// bit counter: read|write single bytes.
always @(negedge scl_clk or negedge rst_n) begin if(rst_n == 1'b0) begin bit_cnt <= 3'd7; end else if( (current_state == WRITE) || (current_state == READ) ) begin if(bit_cnt > 3'd0) begin bit_cnt <= bit_cnt - 1'b1; end else begin bit_cnt <= 3'd7; end end else begin bit_cnt <= 3'd7; endend
Design of the Write Byte Counter
Combining the previous state machine design, we design the write byte counter as follows:
-
Counting occurs only in the write data state.
-
Reset to zero in the start state, while other states remain unchanged
When the bit counter from the previous section decrements to 0, the 8-bit transmission of the written byte is complete, which serves as the increment condition for our write byte counter. For the multi-byte write function, we design to support a maximum of 255 bytes written, thus the resources required for the write byte counter are as follows:
| Signal | Type | Bits | Description |
| write_byte_cnt | reg | 8 | Write byte count value |
The timing logic Verilog code for the write byte counter is as follows:
reg [7:0] write_byte_cnt;
// write bytes counter: write_byte_cnt signal timing control.
always @(negedge scl_clk or negedge rst_n) begin if(rst_n == 1'b0) begin write_byte_cnt <= 8'h0; end else begin case (current_state) START: begin write_byte_cnt <= 8'h0; end WRITE: begin if(bit_cnt == 3'd0) write_byte_cnt <= write_byte_cnt + 1'b1; end default: begin write_byte_cnt <= write_byte_cnt; end endcase endend
Design of the Read Byte Counter
Combining the previous state machine design, we design the read byte counter as follows:
-
Counting occurs only in the read data state.
-
Reset to zero in the start state and re-start state, while other states remain unchanged
When the bit counter from the previous section decrements to 0, the 8-bit transmission of the byte sent by the slave is complete, which serves as the increment condition for our read byte counter. Similar to multi-byte writing, we design to support a maximum of 255 bytes read, thus the resources required for the read byte counter are as follows:
| Signal | Type | Bits | Description |
| read_byte_cnt | reg | 8 | Read byte count value |
The timing logic Verilog code for the read byte counter is as follows:
reg [7:0] read_byte_cnt;
// read bytes counter: read_byte_cnt signal timing control.
always @(negedge scl_clk or negedge rst_n) begin if(rst_n == 1'b0) begin read_byte_cnt <= 8'd0; end else begin case (current_state) START: begin read_byte_cnt <= 8'd0; end RE_START: begin read_byte_cnt <= 8'd0; end READ: begin if(bit_cnt == 3'd0) read_byte_cnt <= read_byte_cnt + 1'b1; end default: begin read_byte_cnt <= read_byte_cnt; end endcase endend
03 Conclusion
At this point, we have completed the skeleton structure of the I2C master controller. The essence of the design for the bit counter and byte counter lies in their close alignment with the state machine and the SCL clock, completing counting and resetting at the correct state and clock edge.
With precise bit and byte counting capabilities, our controller is now equipped to handle data read and write dialogues of arbitrary lengths. The next step will be to design the shifting of written data bytes and read data bytes based on this, ultimately completing the functionality of multi-byte writing and reading. Stay tuned!
✨ Thank you for reading! ✨
Click “Like” to let more friends see this valuable content 🌟