▼ Follow for more valuable content
“ In FPGA development, the design and implementation of the I2C Master controller is a critical aspect. Especially when handling multi-byte data read and write operations, the design of byte shifting directly affects the stability and efficiency of communication. This article will delve into the design concepts and specific implementations of byte shifting for reading and writing in the I2C Master core, detailing how to achieve precise control of multi-byte data transmission through Verilog code, from interface parameter definitions to timing state control. Whether you are a beginner or an experienced engineer, these practical experiences will provide strong references for your project development.”
01 Design Concepts for Read & Write Byte Shifting
Interface and Parameters for Multi-byte Read/Write
In the previous article: A Guide to Implementing I2C Master Core in FPGA (Part 2): Module Interface Design, weconfigured the multi-byte data read and write functionality through the maximum length parameters for read and write. The interface, parameters, and their Verilog code are as follows:
| Interface | Direction | Type | Bit | Description |
| write_byte_len | input | wire | 8 | Write transmission: number of bytes N. |
| write_data | input | wire | N*8 | Write transmission: data. |
| read_byte_len | input | wire | 8 | Read transmission: number of bytes M. |
| read_data | output | reg | M*8 | Read transmission: the data read. |
| Parameter | Type | Description |
| WMEN_LEN | integer | Maximum length of data written during a single communication. Unit: bytes |
| RMEN_LEN | integer | Maximum length of data read during a single communication. Unit: bytes |
module iic_master_control #( parameter integer WMEN_LEN = 8, // write data length. (bytes) parameter integer RMEN_LEN = 8, // read data length. (bytes) ...)( ... input wire [WMEN_LEN*8-1:0] write_data , // write data. input wire [7:0] write_byte_len , // write data byte len.
output reg [RMEN_LEN*8-1:0] read_data , // read data. input wire [7:0] read_byte_len , // read data byte len. ... );
Design Concepts for Write Byte Shifting
In the I2C communication protocol, the following conventions apply to the control byte:
-
The high 7 bits ([7:1]) are used as the slave address
-
The 0th bit ([0]) is for transmission control (1 – read, 0 – write)
In our I2C master controller, the first byte of the write_data interface is used as the control byte, which is utilized after the start bit (START) for write operations and the repeated start bit (RE_START) for read operations. The pseudocode is as follows:
After START: sda_data_reg <= write_data[7:0];
After RE_START: sda_data_reg <= {write_data[7:1], 1'b1};
Explanation: 1. The RE_START is initiated during data reading; 2. The control byte after RE_START has bit0=1 indicating read, and bits 7~1 indicating the slave address.
When designing the byte shifting for write operations, in addition to the above situations, we also need to consider the following:
-
Bit shifting of a single byte
-
Switching assignment of the byte to be written
| Situation | Description |
| Bit shifting of a single byte | The I2C bus protocol data transmission mode is MSB. That is, the high bit is in front, and the low bit is in the back. |
| Switching assignment of the byte to be written | After the current byte (8 bits) transmission is completed, the byte to be written completes the switching assignment while waiting for the write acknowledgment (ACK). |
Design Concepts for Read Byte Shifting
When designing the byte shifting for read operations, consider the following:
-
Bit shifting of a single byte
-
Shifting of buffer data during reading
| Situation | Description |
| Bit shifting of a single byte | The I2C bus protocol data transmission mode is MSB. That is, the high bit is in front, and the low bit is in the back. |
| Switching assignment of the byte to be written | After the current byte (8 bits) transmission is completed, the byte to be written completes the switching assignment while waiting for the write acknowledgment (ACK). |
02 Implementation of Read & Write Byte Shifting Design
The timing logic control clock for the I2C master controller is SCL_CLK. The data on the SDA line is switched on the falling edge of SCL, and is valid on the high level. For write byte shifting, the timing logic is executed on the falling edge of SCL_CLK; for read byte shifting, the timing logic is executed on the rising edge of SCL_CLK.
Design for Write Byte Shifting
For write byte shifting, the resources we need are as follows:
| Signal | Type | Bit | Description |
| sda_data_reg | reg | 8 | Register for write byte shifting data |
Combining the content from the previous section, we design the assignment for each timing state of the byte shifting data register as follows:
| State | Assignment |
| START |
write_data[7:0] |
| RE_START |
{write_data[7:1], 1’b1} |
| W_ACK |
write_data[(write_byte_cnt*8) +: 8] |
| WRITE |
{sda_data_reg[6:0], 1’b1} |
The Verilog code for the timing logic of write byte shifting is as follows:
reg [7:0] sda_data_reg;
// write bytes shift control: sda_data_reg signal timing control.
always @(negedge scl_clk or negedge rst_n) begin if(rst_n == 1'b0) begin sda_data_reg <= 8'hFF; end else begin case (current_state) START: begin sda_data_reg <= write_data[7:0]; end RE_START: begin sda_data_reg <= {write_data[7:1], 1'b1}; end W_ACK: begin if(write_byte_cnt < write_byte_len) sda_data_reg <= write_data[(write_byte_cnt*8) +: 8]; end WRITE: begin sda_data_reg <= {sda_data_reg[6:0], 1'b1}; end default: begin sda_data_reg <= sda_data_reg; end endcase endend
Design for Read Byte Shifting
For read byte shifting, the resources we need are as follows:
| Signal | Type | Bit | Description |
| sda_read_reg | reg | 8 | Register for read byte shifting data |
Combining the content from the previous section, we design the assignment for each timing state of the read byte shifting data register as follows:
| State | Assignment |
| READ |
{sda_read_reg[6:0], sda_i} |
| R_ACK |
8’h00 |
The Verilog code for the timing logic of read byte shifting is as follows:
reg [7:0] sda_read_reg;
// read bytes shift control: sda_read_reg signal timing control.
always @(posedge scl_clk or negedge rst_n) begin if(rst_n == 1'b0) begin sda_read_reg <= 8'h00; end else begin case (current_state) READ: sda_read_reg <= {sda_read_reg[6:0], sda_i}; R_ACK: sda_read_reg <= 8'h00; default: sda_read_reg <= sda_read_reg; endcase endend
// read_data: timing control.
always @(posedge scl_clk or negedge rst_n) begin if(rst_n == 1'b0) read_data <= {RMEN_LEN*8{1'b0}}; else if(current_state == R_ACK) read_data[((read_byte_cnt-1'b1)*8) +: 8] <= sda_read_reg; else read_data <= read_data;end
03 Conclusion
Through this detailed analysis of the design of read and write byte shifting for the I2C Master controller, we can see that a stable and reliable I2C communication core requires careful design of data shifting and switching at each timing state. The clever handling of control bytes in write operations and the precise collection of data bits in read operations reflect the extreme pursuit of detail in hardware design. This state machine-based shifting control method not only ensures strict adherence to the I2C protocol but also lays a solid foundation for efficient transmission of multi-byte data. We hope these design concepts and code implementations can inspire your FPGA project development, and in the next issue, we will continue to explore other key technical points in I2C communication.
✨ Thank you for reading! ✨
Click “Like” to let more friends see this valuable content 🌟