Verilog Code for I2C Master: Syntax and Example
An
I2C master in Verilog controls the clock and data lines to communicate with I2C devices. The code typically includes generating SCL clock, managing SDA data line, and implementing start, stop, read, and write sequences. Below is a simple example showing these key parts in Verilog.Syntax
The basic syntax for an I2C master in Verilog includes defining inputs and outputs for clock (clk), reset (rst), serial clock line (scl), and serial data line (sda). You implement a state machine to handle start, stop, read, and write operations. The sda line is bidirectional and usually modeled as an inout port with tri-state control.
clk: System clock input.rst: Reset signal.scl: I2C clock line output.sda: I2C data line, bidirectional.
verilog
module i2c_master(
input wire clk,
input wire rst,
output reg scl,
inout wire sda
);
// Internal signals
reg sda_out;
reg sda_oe; // Output enable for sda
assign sda = sda_oe ? sda_out : 1'bz; // Tri-state control
// State machine states
localparam IDLE = 0, START = 1, WRITE = 2, STOP = 3;
reg [1:0] state;
always @(posedge clk or posedge rst) begin
if (rst) begin
state <= IDLE;
scl <= 1;
sda_out <= 1;
sda_oe <= 0;
end else begin
case(state)
IDLE: begin
scl <= 1;
sda_out <= 1;
sda_oe <= 0;
// Next state logic here
end
// Additional states to be implemented
endcase
end
end
endmoduleExample
This example shows a simple I2C master module that generates a start condition, writes a byte, and then generates a stop condition. It uses a state machine to control the scl and sda lines.
verilog
module i2c_master_example(
input wire clk,
input wire rst,
output reg scl,
inout wire sda
);
reg sda_out;
reg sda_oe;
assign sda = sda_oe ? sda_out : 1'bz;
localparam IDLE=0, START=1, WRITE=2, STOP=3;
reg [1:0] state = IDLE;
reg [3:0] bit_cnt = 0;
reg [7:0] data = 8'hA5; // Example data byte
always @(posedge clk or posedge rst) begin
if (rst) begin
state <= IDLE;
scl <= 1;
sda_out <= 1;
sda_oe <= 0;
bit_cnt <= 0;
end else begin
case(state)
IDLE: begin
scl <= 1;
sda_out <= 1;
sda_oe <= 0;
state <= START;
end
START: begin
sda_out <= 0; // Start condition: SDA goes low while SCL is high
sda_oe <= 1;
scl <= 1;
state <= WRITE;
bit_cnt <= 7;
end
WRITE: begin
scl <= 0;
sda_out <= data[bit_cnt];
sda_oe <= 1;
scl <= 1; // Clock high to latch bit
if (bit_cnt == 0) begin
state <= STOP;
end else begin
bit_cnt <= bit_cnt - 1;
end
end
STOP: begin
scl <= 1;
sda_out <= 0;
sda_oe <= 1;
sda_out <= 1; // Stop condition: SDA goes high while SCL is high
state <= IDLE;
end
endcase
end
end
endmoduleCommon Pitfalls
Common mistakes when writing an I2C master in Verilog include:
- Not properly handling the bidirectional
sdaline, causing bus contention. - Incorrect timing of
sclandsdasignals violating I2C protocol rules. - Forgetting to generate start and stop conditions correctly.
- Not waiting for acknowledgment bits from the slave device.
Always use tri-state control for sda and carefully sequence the clock and data lines.
verilog
/* Wrong way: Driving sda as output without tri-state */ module wrong_i2c_master( input wire clk, output reg sda ); // This will cause bus contention if slave tries to drive sda always @(posedge clk) begin sda <= 1; // Always driving high, no tri-state end endmodule /* Right way: Use tri-state for sda */ module right_i2c_master( input wire clk, inout wire sda ); reg sda_out; reg sda_oe; assign sda = sda_oe ? sda_out : 1'bz; always @(posedge clk) begin sda_out <= 1; sda_oe <= 1; // Enable output only when driving end endmodule
Quick Reference
Key points for writing an I2C master in Verilog:
- Start condition: SDA goes low while SCL is high.
- Stop condition: SDA goes high while SCL is high.
- Data bits: Change SDA when SCL is low, sample SDA when SCL is high.
- Acknowledge bit: Master releases SDA, slave pulls low to acknowledge.
- Use tri-state buffer: Model SDA as
inoutwith output enable.
Key Takeaways
Use tri-state control for the bidirectional SDA line to avoid bus conflicts.
Generate proper start and stop conditions by controlling SDA while SCL is high.
Change SDA data only when SCL is low and sample data when SCL is high.
Implement a state machine to sequence I2C operations cleanly.
Always wait for acknowledgment bits from the slave device after sending data.