//-----------------------------------------------------------------------------------------------//
//PURPOSE :
//process i2c interface to local ram interface
//-----------------------------------------------------------------------------------------------//
//Description:
///////////////////////////////////////////////////////////////////////////////////////////////////
//////--------------------------------------------------------+
//////|Master|start|dev_addr+w| |byte_addr| |data| |stop|
//////+-------------------------------------------------------+
//////|Slave | | |ack| |ack| |ack| |
//////--------------------------------------------------------+
///////////////////////////////////////////////////////////////////////////////////////////////////
//////--------------------------------------------------------+--------+
//////|Master|start|dev_addr+w| |byte_addr| |data| |data| |stop|
//////+-------------------------------------------------------+--------+
//////|Slave | | |ack| |ack| |ack| |ack| |
//////--------------------------------------------------------+---------
///////////////////////////////////////////////////////////////////////////////////////////////////
//////-------------------------------------------------------------------------------+
//////|Master|start|dev_addr+w| |byte_addr| |start|dev_addr+r| | |nmack|stop|
//////-------------------------------------------------------------------------------+
//////|Slave | | |ack| |ack| | |ack|data| | |
//////-------------------------------------------------------------------------------+
///////////////////////////////////////////////////////////////////////////////////////////////////
//////-----------------------------------------------------------------------------------------+
//////|Master|start|dev_addr+w| |byte_addr| |start|dev_addr+r| | |mack| |nmack|stop|
//////-----------------------------------------------------------------------------------------+
//////|Slave | | |ack| |ack| | |ack|data| |data| | |
//////-----------------------------------------------------------------------------------------+
///////////////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------------------------//
`timescale 1ns/1ps
module i2c_intf
#(
parameter C_CLK_FREQ = 133_000_000, //时钟频率
parameter C_IIC_FREQ = 400_000 //IIC 总线速率
)
(
input clk, //时钟
input rst, //异步复位,高有效
input [6:0] i2c_dev_addr, //IIC器件地址
input [6:0] i2c_sub_addr, //器件内存储空间地址
output reg [6:0] i2c_wdata_addr,
output reg i2c_wdata_en,
input [7:0] i2c_wdata, //待写数据
input i2c_data_wr, //写使能,脉冲,高有效
input [6:0] i2c_data_wr_len, //待写数据长度,单位Byte
input i2c_data_rd, //读使能,脉冲,高有效
input [6:0] i2c_data_rd_len, //读数据长度,单位Byte,
output reg [7:0] i2c_rdata, //读出数据
output reg i2c_rdata_rd_v, //读出数据有效指示,脉冲,高有效
output reg [6:0] i2c_rddata_addr,
output reg i2c_ready, //IIC总线空闲,高有效,空闲时可发送I_data_we或I_data_rd信号
output reg i2c_scl, //IIC时钟信号
inout i2c_sda //IIC数据信号
);
localparam C_BIT_PERIOD = (C_CLK_FREQ/C_IIC_FREQ)/2-1;
localparam C_BIT_HALF_PERIOD = C_CLK_FREQ/C_IIC_FREQ/4-1;
localparam C_BIT_QUAR_PERIOD = C_CLK_FREQ/C_IIC_FREQ/8-1;
localparam C_PERIOD_WIDTH = F_width(C_BIT_PERIOD)+1;
localparam ST_IDLE = 4'd0;
localparam ST_START = 4'd1;
localparam ST_DEV_ADDR = 4'd2;
localparam ST_RCV_ACK = 4'd3;
localparam ST_BYTE_ADDR = 4'd4;
localparam ST_WDATA = 4'd5;
localparam ST_RDATA = 4'd6;
localparam ST_SEND_ACK = 4'd7;
localparam ST_STOP = 4'd8;
localparam ST_PRO = 4'd9;
localparam C_PRO_LEN = 128;
localparam C_PRO_WIDTH = 8;
reg [C_PERIOD_WIDTH-1:0] i2c_clk_cnt;
reg i2c_scl_dly;
reg i2c_clk_rise;
reg i2c_clk_fall;
reg [2:0] i2c_byte_cnt;
reg byte_over;
reg s_ack;
reg [6:0] rdata_byte_cnt;
reg rd_stop_id;
reg [3:0] cur_state;
reg [3:0] next_state;
reg [3:0] cur_state_dly;
reg s_iic_sda_v;
reg s_iic_sda;
reg [7:0] dev_addr_shift;
reg s_iic_stop_id;
reg [C_PRO_WIDTH-1:0] s_pro_cnt;
reg s_pro_over;
reg s_iic_start_id;
reg i2c_clk_fall_dly1;
reg i2c_clk_fall_dly2;
reg wr_rd_id;//1, read; 0, write;
reg [6:0] sack_cnt;
reg [7:0] wr_data_shift;
reg wr_finish;
reg i2c_data_wr_lat;
reg i2c_data_rd_lat;
//----------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
cur_state <= ST_IDLE;
else
cur_state <= next_state;
end
always @(posedge clk or posedge rst)
begin
if (rst == 1'b1)
cur_state_dly <= 4'd0;
else
cur_state_dly <= cur_state;
end
always @(*)
begin
case(cur_state)
ST_IDLE: //0
if ((i2c_data_wr || i2c_data_rd) && i2c_ready)//write enable, read enable,
next_state = ST_START;
else
next_state = ST_IDLE;
ST_START: //1
if (i2c_clk_fall_dly2 == 1'b1)
next_state = ST_DEV_ADDR;
else
next_state = ST_START;
ST_DEV_ADDR: //2
if (byte_over == 1'b1)
next_state = ST_RCV_ACK;
else
next_state = ST_DEV_ADDR;
ST_RCV_ACK: //3
if(i2c_clk_fall_dly2)
begin
if(s_ack == 1'b1) //no ack,
next_state = ST_IDLE;
else if (sack_cnt == 6'd1)
next_state = ST_BYTE_ADDR;
else if (sack_cnt == 6'd2)
begin
if (wr_rd_id == 1'b0)//write
next_state = ST_WDATA;
else
next_state = ST_START;
end
else if (sack_cnt >= 6'd3)
begin
if (wr_rd_id == 1'b0)//write
begin
if (wr_finish == 1'b1)
next_state = ST_STOP;
else
next_state = ST_WDATA;
end
else
next_state = ST_RDATA;
end
else
next_state = ST_IDLE;//2017/8/29 16:02:28,
end
else
next_state = ST_RCV_ACK;
ST_BYTE_ADDR: //4
if(byte_over) //rec 1 Byte dat
next_state = ST_RCV_ACK;
else
next_state = ST_BYTE_ADDR;
ST_WDATA: //5
if(byte_over)
next_state = ST_RCV_ACK;
else
next_state = ST_WDATA;
ST_RDATA: //6
if(byte_over)
next_state = ST_SEND_ACK;
else
next_state = ST_RDATA;
ST_SEND_ACK: //7
if(i2c_clk_fall_dly2)
begin
if (rd_stop_id == 1'b1)
next_state = ST_STOP;
else
next_state = ST_RDATA;
end
else
next_state = ST_SEND_ACK;
ST_STOP: //8
if(s_iic_stop_id)
next_state = ST_PRO;
else
next_state = ST_STOP;
ST_PRO:
if(s_pro_over)
next_state = ST_IDLE;
else
next_state = ST_PRO;
default: next_state = ST_IDLE;
endcase
end
//----------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_ack <= 1'b0;
else if(i2c_clk_rise)
s_ack <= i2c_sda;
else ;
end
//-----------------------------------------------------------------------------------------------//
//i2c clock
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_clk_cnt <= 'd0;
else if (cur_state == ST_IDLE || i2c_clk_cnt == C_BIT_PERIOD)
i2c_clk_cnt <= 'd0;
else
i2c_clk_cnt <= i2c_clk_cnt + 'd1;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_scl <= 1'b1;
else if(cur_state == ST_IDLE)
i2c_scl <= 1'b1;
else if(i2c_clk_cnt == C_BIT_HALF_PERIOD)
i2c_scl <= ~i2c_scl;
else ;
end
//-----------------------------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_scl_dly <= 1'b0;
else
i2c_scl_dly <= i2c_scl;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
begin
i2c_clk_rise <= 1'b0;
i2c_clk_fall <= 1'b0;
end
else
begin
i2c_clk_rise <= i2c_scl && (~i2c_scl_dly);
i2c_clk_fall <= i2c_scl_dly && (~i2c_scl);
end
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
begin
i2c_clk_fall_dly1 <= 1'b0;
i2c_clk_fall_dly2 <= 1'b0;
end
else
begin
i2c_clk_fall_dly1 <= i2c_clk_fall;
i2c_clk_fall_dly2 <= i2c_clk_fall_dly1;
end
end
//-----------------------------------------------------------------------------------------------//
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_byte_cnt <= 3'd0;
else if (i2c_clk_fall_dly2 == 1'b1)
begin
if (cur_state == ST_DEV_ADDR || cur_state == ST_BYTE_ADDR ||
cur_state == ST_WDATA || cur_state == ST_RDATA)
i2c_byte_cnt <= i2c_byte_cnt + 3'd1;
else
i2c_byte_cnt <= 3'd0;
end
else ;
end
always @ ( * )
begin
if (rst == 1'b1)
byte_over = 1'b0;
else if ((i2c_byte_cnt == 3'd7) && (i2c_clk_fall_dly2 == 1'b1))
byte_over = 1'b1;
else
byte_over = 1'b0;
end
always @(posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_iic_stop_id <= 1'b0;
else
s_iic_stop_id <= i2c_scl && (i2c_clk_cnt == C_BIT_PERIOD) && (cur_state == ST_STOP);
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_iic_start_id <= 1'b0;
else if(i2c_scl && (i2c_clk_cnt == C_BIT_QUAR_PERIOD) && (cur_state == ST_START))
s_iic_start_id <= 1'b1;
else if(cur_state != ST_START)
s_iic_start_id <= 1'b0;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
rdata_byte_cnt <= 7'd0;
else if(cur_state == ST_IDLE)
rdata_byte_cnt <= 7'd0;
else if(cur_state == ST_RDATA && cur_state_dly != ST_RDATA)
rdata_byte_cnt <= rdata_byte_cnt + 7'd1;
else ;
end
reg [7:0] rd_data_shift;
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
rd_data_shift <= 8'd0;
else if (cur_state == ST_IDLE)
rd_data_shift <= 8'd0;
else if (cur_state == ST_RDATA && i2c_clk_rise)
rd_data_shift <= {rd_data_shift[6:0],i2c_sda};
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_rdata <= 8'd0;
else if (cur_state == ST_IDLE)
i2c_rdata <= 8'd0;
else if (cur_state == ST_SEND_ACK)
i2c_rdata <= rd_data_shift;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_rdata_rd_v <= 1'b0;
else if (cur_state == ST_SEND_ACK && i2c_clk_rise)
i2c_rdata_rd_v <= 1'b1;
else
i2c_rdata_rd_v <= 1'b0;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_rddata_addr <= 7'd0;
else if (cur_state == ST_IDLE)
i2c_rddata_addr <= i2c_sub_addr - 7'd1;
else if (cur_state == ST_SEND_ACK && i2c_clk_rise)
i2c_rddata_addr <= i2c_rddata_addr + 7'd1;
else ;
end
always @ (*)
begin
if (rdata_byte_cnt == i2c_data_rd_len)
rd_stop_id = 1'b1;
else
rd_stop_id = 1'b0;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_ready <= 1'b0;
else if(i2c_data_wr || i2c_data_rd)
i2c_ready <= 1'b0;
else if(cur_state == ST_IDLE)
i2c_ready <= 1'b1;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_iic_sda_v <= 1'b0;
else if (((cur_state_dly == ST_DEV_ADDR) || (cur_state_dly == ST_BYTE_ADDR) ||
(cur_state_dly == ST_WDATA) || (cur_state_dly == ST_SEND_ACK)) &&
(s_iic_sda == 1'b0) || (cur_state == ST_STOP) || (s_iic_start_id == 1'b1))
s_iic_sda_v <= 1'b1;
else
s_iic_sda_v <= 1'b0;
end
reg i2c_data_rd_latch;
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_data_rd_latch <= 1'b0;
else if (cur_state == ST_IDLE)
i2c_data_rd_latch <= i2c_data_rd;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
wr_rd_id <= 1'b0;
else if (cur_state == ST_IDLE)
wr_rd_id <= 1'b0;
else if (cur_state == ST_BYTE_ADDR)
wr_rd_id <= i2c_data_rd_latch;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
dev_addr_shift <= 8'd0;
else if(cur_state == ST_START)
dev_addr_shift <= {i2c_dev_addr,wr_rd_id};
else if(cur_state == ST_DEV_ADDR && i2c_clk_fall_dly2)
dev_addr_shift <= dev_addr_shift << 1;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
sack_cnt <= 6'd0;
else if (cur_state == ST_IDLE)
sack_cnt <= 6'd0;
else if (cur_state == ST_RCV_ACK && cur_state_dly != ST_RCV_ACK)
sack_cnt <= sack_cnt + 6'd1;
else ;
end
reg [7:0] byte_addr_shift;
reg i2c_wr_mode;//1 burst mode; 0, single mode
always @ (*)
begin
if ((i2c_data_wr_lat == 1'b1) && (i2c_data_wr_len > 7'd1))
i2c_wr_mode = 1'b1;
else if ((i2c_data_rd_lat == 1'b1) && (i2c_data_rd_len > 7'd1))
i2c_wr_mode = 1'b1;
else
i2c_wr_mode = 1'b0;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
byte_addr_shift <= 'd0;
else if(cur_state == ST_START)
byte_addr_shift <= {i2c_wr_mode,i2c_sub_addr};
else if(cur_state == ST_BYTE_ADDR && i2c_clk_fall_dly2)
byte_addr_shift <= byte_addr_shift << 1;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_iic_sda <= 1'b0;
else if(cur_state == ST_DEV_ADDR)
s_iic_sda <= dev_addr_shift[7];
else if(cur_state == ST_BYTE_ADDR)
s_iic_sda <= byte_addr_shift[7];
else if(cur_state == ST_WDATA)
s_iic_sda <= wr_data_shift[7];
else if(cur_state == ST_SEND_ACK)
begin
if (rd_stop_id == 1'b0)
s_iic_sda <= 1'b0;
else
s_iic_sda <= 1'b1;
end
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
wr_data_shift <= 8'd0;
else if (cur_state == ST_RCV_ACK)
wr_data_shift <= i2c_wdata;
else if (cur_state == ST_WDATA && i2c_clk_fall_dly2)
wr_data_shift <= wr_data_shift << 1;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_data_wr_lat <= 1'b0;
else if (cur_state == ST_STOP)
i2c_data_wr_lat <= 1'b0;
else if (i2c_data_wr == 1'b1)
i2c_data_wr_lat <= 1'b1;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
i2c_data_rd_lat <= 1'b0;
else if (cur_state == ST_IDLE)
i2c_data_rd_lat <= 1'b0;
else if (i2c_data_rd == 1'b1)
i2c_data_rd_lat <= 1'b1;
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
begin
i2c_wdata_addr <= 7'd0;
i2c_wdata_en <= 1'b0;
end
else if (cur_state == ST_RCV_ACK && i2c_clk_rise && i2c_data_wr_lat == 1'b1)//5
begin
if (sack_cnt == 6'd2)
begin
i2c_wdata_addr <= i2c_sub_addr;
i2c_wdata_en <= 1'b1;
end
else if ((i2c_wdata_addr < (i2c_sub_addr + i2c_data_wr_len - 7'd1)) && (sack_cnt > 6'd2))
begin
i2c_wdata_addr <= i2c_wdata_addr + 7'd1;
i2c_wdata_en <= 1'b1;
end
else
i2c_wdata_en <= 1'b0;
end
else
i2c_wdata_en <= 1'b0;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
wr_finish <= 1'b0;
else if (cur_state == ST_IDLE)
wr_finish <= 1'b0;
else if (cur_state == ST_WDATA && i2c_clk_fall_dly2)//5
begin
if (i2c_wdata_addr >= (i2c_sub_addr + i2c_data_wr_len - 7'd1))
wr_finish <= 1'b1;
else
wr_finish <= 1'b0;
end
else ;
end
always @ (posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_pro_cnt <= 'd0;
else if(cur_state == ST_PRO)
s_pro_cnt <= s_pro_cnt + 'd1;
else
s_pro_cnt <= 'd0;
end
always @(posedge clk or posedge rst)
begin
if (rst == 1'b1)
s_pro_over <= 1'b0;
else
s_pro_over <= (s_pro_cnt == C_PRO_LEN);
end
assign i2c_sda = s_iic_sda_v ? 1'b0 : 1'bz;
function integer F_width;
input integer I_num;
integer i;
begin
for(i=0;2**i<=I_num;i=i+1)
F_width = i;
end
endfunction
endmodule