📄 i2c_control.v.bak
字号:
// File Name: i2c_control.v
//
// Module Description:
// This module implements the I2C slave controller. This module interprets incoming I2C read and write
// accesses and generates output signals or reads input signals that indicate the type of access that is being performed.
module i2c_control(
reset_i, // global reset
// I2C bus.
i2c_scl_i, // I2C master clock.
i2c_sda_i, // I2C data input
i2c_sda_o, // I2C data output
i2c_sda_oe, // I2C SDA OE
// The external register file control and data signals.
reg_add_o, // register file address
reg_data_i, // register file read data
reg_data_o, // register file write data
reg_wr_o // register file write strobe
);
input i2c_scl_i;
input reset_i;
input i2c_sda_i;
output i2c_sda_o;
output i2c_sda_oe;
output [2:0] reg_add_o;
input [7:0] reg_data_i;
output [7:0] reg_data_o;
output reg_wr_o;
reg reg_wr_o;
// Miscellaineous parameters.
parameter COUNT_DONE = 4'h8;
parameter SLAVE_ADD = 4'b0100;
// I2C state machine states.
parameter STATE_IDLE = 3'b000;
parameter STATE_HEADER = 3'b001;
parameter STATE_ACK_HEADER = 3'b010;
parameter STATE_RCV_DATA = 3'b011;
parameter STATE_XMT_DATA = 3'b100;
parameter STATE_ACK_DATA = 3'b101;
parameter STATE_ILL1 = 3'b110; //illegal state 1
parameter STATE_ILL2 = 3'b111; //illegal state 2
// I2C state variables.
reg [4:0] bit_count; // number of incoming bits received of current byte
reg [7:0] command; // command byte captured from incoming I2C stream
reg detect_start; // I2C start detected
reg detect_stop; // I2C stop detected
reg [7:0] i2c_data_in; // data captured from incoming I2C stream
reg [7:0] i2c_data_out; // data to transmit on I2C bus
reg i2c_sda_o; // single bit data stream out to I2C bus
reg [2:0] state /*synthesis syn_encoding = "sequential" */; // state machine state
reg sda_1;
reg sda_2;
assign i2c_sda_oe = (state == STATE_XMT_DATA) ? 1 : 0;
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (~reset_i)
detect_stop <= 1'b0;
else
if (sda_1 == 1'b0 && sda_2 == 1'b1)
detect_stop <= 1'b1;
else
detect_stop <= 1'b0;
end
always @(negedge i2c_scl_i or negedge reset_i) //capture on falling edge of scl
begin
if (~reset_i)
sda_2 <= 1'b1;
else
sda_2 <= i2c_sda_i;
end
always @(posedge i2c_scl_i or negedge reset_i) //capture on rising edge of scl
begin
if (~reset_i)
sda_1 <= 1'b1;
else
sda_1 <= i2c_sda_i;
end
// Signal assignment
assign reg_data_o = i2c_data_in;
assign reg_add_o = command[3:1];
assign reset_start = (state == STATE_HEADER)?1:0;
// Load the I2C output register when a complete address is received.
wire load_i2c_data_in;
assign load_i2c_data_out = (state == STATE_ACK_HEADER);
// Write data to the register when it is completely received.
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
reg_wr_o <= 8'h00;
else
reg_wr_o <= ((state == STATE_RCV_DATA) && (bit_count == (COUNT_DONE - 1))) ;
end
// Identify the I2C start condition as sda falling when scl is high.
always @(negedge i2c_scl_i or posedge reset_start)
begin
if (reset_start)
detect_start <= 0;
else
if (i2c_sda_i == 1'b0 && sda_1 == 1'b1 && state == STATE_IDLE)
begin
detect_start <= 1;
end
else
detect_start <= 0;
end
// The I2C input register. Data is shifted into this register MSB to LSB.
// Bits are continually captured from the I2C bus on the rising edge of scl.
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
i2c_data_in <= 8'h00;
else
begin
i2c_data_in[7:0] <= {i2c_data_in[6:0], i2c_sda_i};
end
end
// The I2C read data register back. Data is shifted out of this register MSB to LSB.
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
i2c_data_out <= 8'h00;
else
if (load_i2c_data_out)
i2c_data_out[7:0] <= reg_data_i[7:0];
else
// Data is shifted on each scl transition during a I2C bus read.
if (state == STATE_XMT_DATA)
i2c_data_out[7:0] <= {i2c_data_out[6:0], 1'b0};
end
// Track the number of bits received of the current byte on the I2C bus.
wire bit_count_clr;
assign bit_count_clr = (state == STATE_IDLE) || (state == STATE_ACK_HEADER); // || detect_start
wire bit_count_en;
assign bit_count_en = (state == STATE_HEADER) || (state == STATE_RCV_DATA) || (state == STATE_XMT_DATA) ? 1 : 0;
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
bit_count <= 4'h0;
else
begin
if (bit_count_clr)
bit_count <= 4'h0;
else
// Increment the bit count on the rising edge of scl.
if (bit_count_en )
bit_count <= bit_count + 1;
end
end
// Determine if our slave address matches that on the I2C bus, and record the command byte.
wire add_compares;
//assign add_compares = (i2c_data_in[7:4] == SLAVE_ADD);
// Load the command register after 7 address bits, and RWn bit
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
command <= 8'h0;
else
if (state == STATE_HEADER && bit_count == (COUNT_DONE - 1))
command <= i2c_data_in;
end
// The I2C state machine.
always @(posedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
begin
state <= STATE_IDLE;
end
else
begin
case (state)
// Waiting for a I2C bus transaction to begin.
STATE_IDLE :
begin
if (detect_start)
state <= STATE_HEADER;
else
state <= STATE_IDLE;
end
// Receiving the first byte of the transaction.
STATE_HEADER :
begin
// We'll have received all the bits at the rising edge of scl, but issue acknowledge on falling
// edge of scl
//if (detect_stop)
// state <= STATE_IDLE;
//else
if ((bit_count == (COUNT_DONE - 1)) && (i2c_data_in[7:4] == SLAVE_ADD))
state <= STATE_ACK_HEADER;
//else
//if (bit_count == COUNT_DONE && (i2c_data_in[7:4] != SLAVE_ADD))
// state <= STATE_IDLE;
//else
// state <= STATE_HEADER;
end
// Delay one scl period while ack is generated. The master can terminate an
// access while waiting for an ack.
STATE_ACK_HEADER :
begin
if (detect_stop)
state <= STATE_IDLE;
else
state <= command[0] ? STATE_XMT_DATA: STATE_RCV_DATA;
end
// Receive the data when an I2C write is taking place.
STATE_RCV_DATA :
begin
// Check for a repeated start.
if (detect_stop)
state <= STATE_IDLE;
else
// We'll have received all the bits at the rising edge of scl, but wait until
// scl falls before issuing an acknowledge.
if (bit_count == COUNT_DONE )
state <= STATE_ACK_DATA;
else
state <= STATE_RCV_DATA;
end
// Transmitting the data when an I2C read is taking place. We will return a single byte
// and go idle, ignoring the master acknowledge.
STATE_XMT_DATA :
begin
// Check for a repeated start.
if (detect_stop)
state <= STATE_IDLE;
else
if (bit_count == COUNT_DONE )
state <= STATE_ACK_DATA;
else
state <= STATE_XMT_DATA;
end
// Delay one scl period while ack is generated. The master can terminate an
// access while waiting for an ack.
STATE_ACK_DATA :
begin
state <= STATE_IDLE;
end
// Default to idle.
default:
begin
state <= STATE_IDLE;
end
endcase
end
end
// Generate the sda signal. The sda signal will either be an acknowledge, or data
// being shifted from the i2c_data_out register.
always @(negedge i2c_scl_i or negedge reset_i)
begin
if (reset_i == 1'b0)
i2c_sda_o <= 1'b1;
else
// I2C header byte and data acknowledges.
if ((state == STATE_HEADER && bit_count == COUNT_DONE - 1)|| (state == STATE_RCV_DATA && bit_count == COUNT_DONE))
i2c_sda_o <= 1'b0;
else
// Output the requested read data.
if ((state == STATE_ACK_HEADER && command[0]== 1'b1) || (state == STATE_XMT_DATA))
i2c_sda_o <= i2c_data_out[7];
else
i2c_sda_o <= 1'b1;
end
endmodule
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -