A Comprehensive Guide to Implementing I2C Master Core with FPGA (Part 5): Bit Counting & Byte Counting in Data Transmission

▼ 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 🌟

Leave a Comment