FPGA模块——IIC接口设计
- IIC协议
- IIC接口代码
- 应用IIC接口的代码
IIC协议
IIC接口代码
module iic_drive#(parameter P_ADDR_WIDTH = 16
)( input i_clk ,//模块输入时钟input i_rst ,//模块输入复位-高有效/*--------用户接口--------*/input [6 :0] i_drive ,//用户输入设备地址input [15:0] i_operation_addr ,//用户输入存储地址input [7 :0] i_operation_len ,//用户输入读写长度input [1 :0] i_operation_type ,//用户输入操作类型input i_opeartion_valid ,//用户输入有效信号output o_operation_ready ,//用户输出准备信号input [7 :0] i_write_data ,//用户输入写数据output o_write_req ,//用户写数据请求信号output [7 :0] o_read_data ,//输出IIC读到的数据output o_read_valid ,//输出IIC读数据有效/*--------IIC接口--------*/output o_iic_scl ,//IIC的时钟inout io_iic_sda //IIC的双向数据项
);/***************function**************//***************parameter*************/
parameter P_ST_IDLE = 0 ,//状态机-空闲P_ST_START = 1 ,//状态机-起始P_ST_UADDR = 2 ,//状态机-设备地址P_ST_DADDR1 = 3 ,//状态机-存储地址高位P_ST_DADDR2 = 4 ,//状态机-存储地址地位P_ST_WRITE = 5 ,//状态机-写数据P_ST_RESTART= 6 ,P_ST_READ = 7 ,//状态机-读数据P_ST_WAIT = 8 ,P_ST_STOP = 9 ,//状态机-停止P_ST_EMPTY = 10 ;localparam P_W = 1 ,//2'b01P_R = 2 ;//2'b10/***************port******************/ /***************mechine***************/
reg [7 :0] r_st_current ;//当前状态机
reg [7 :0] r_st_next ;//下一个状态机
reg [7 :0] r_st_cnt ;//状态机计数器/***************reg*******************/
reg ro_operation_ready ;//操作准备信号
reg ro_write_req ;//写数据请求
reg ro_write_valid ;//写数据有效
reg [7 :0] ro_read_data ;//读数据
reg ro_read_valid ;//读数据有效
reg ro_iic_scl ;//IIC的SCL输出寄存器
reg [7 :0] ri_drive ;//输入的设备地址
reg [15:0] ri_operation_addr ;//输入的存储地址
reg [7 :0] ri_operation_len ;//输入的读写长度
reg [1 :0] ri_operation_type ;//输入读写类型
reg [7 :0] ri_write_data ;//输入的写数据
reg r_iic_st ;//iic时钟状态
reg r_iic_sda_ctrl ;//iic数据三态门控制信号
reg ro_iic_sda ;//iic数据信号
reg [7 :0] r_wr_cnt ;//读写数据bit计数器
reg r_slave_ack ;//iic操作里的从机应答
reg r_ack_valid ;//应答有效
reg r_st_restart ;
reg r_ack_lock ;
reg [7 :0] r_read_drive ;/***************wire******************/
wire w_operation_active ;//操作激活信号
wire w_st_trun ;//状态机跳转信号
wire w_iic_sda ;//iic数据线输入信号/***************component*************//***************assign****************/
assign o_operation_ready = ro_operation_ready ;//准备信号
assign o_write_req = ro_write_req ;//写数据请求信号
assign o_read_data = ro_read_data ;//读数据
assign o_read_valid = ro_read_valid ;//读数据有效
assign o_iic_scl = ro_iic_scl ;//iic的scl
assign w_operation_active = i_opeartion_valid & o_operation_ready ;//激活信号
assign w_st_trun = r_st_cnt == 8 && r_iic_st ;//状态机跳转条件
//三态门使用
assign io_iic_sda = r_iic_sda_ctrl ? ro_iic_sda : 1'bz ;//三态门输出
assign w_iic_sda = !r_iic_sda_ctrl ? io_iic_sda : 1'b0 ;//三态门输入// IOBUF #(
// .DRIVE (12 ),
// .IBUF_LOW_PWR ("TRUE" ),
// .IOSTANDARD ("DEFAULT" ),
// .SLEW ("SLOW" )
// )
// IOBUF_u0
// (
// .O (ro_iic_sda ),
// .IO (io_iic_sda ),
// .I (w_iic_sda ),
// .T (!r_iic_sda_ctrl )
// );/***************always****************/
//第一段状态
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_st_current <= P_ST_IDLE;else r_st_current <= r_st_next;
end//状态机跳转
always@(*)
begincase(r_st_current)P_ST_IDLE : r_st_next <= w_operation_active ? P_ST_START : P_ST_IDLE ;//空闲,操作激活时跳转到起始状态P_ST_START : r_st_next <= P_ST_UADDR; //起始状态,跳转设备地址P_ST_UADDR : r_st_next <= w_st_trun ? r_st_restart ? P_ST_READ : P_ST_DADDR1 //判断是否时重启,重启转入读状态,不是重启转入写存储地址状态: P_ST_UADDR ; //写设备地址,写完跳转存储地址P_ST_DADDR1 : r_st_next <= r_slave_ack ? P_ST_STOP : //存储地址,先等待应答,应答后操作结束跳转存储地址低位w_st_trun ? P_ST_DADDR2 : P_ST_DADDR1 ;P_ST_DADDR2 : r_st_next <= w_st_trun & ri_operation_type == P_W ? P_ST_WRITE ://存储地址低位,判断读写,读跳转读状态,写跳转写状态 w_st_trun & ri_operation_type == P_R ? P_ST_RESTART :P_ST_DADDR2 ;P_ST_WRITE : r_st_next <= w_st_trun & r_wr_cnt == ri_operation_len - 1 ? P_ST_WAIT : P_ST_WRITE ;//写数据状态,写完目标长度跳转结束P_ST_RESTART: r_st_next <= P_ST_STOP; //读数据时,重启总线状态P_ST_READ : r_st_next <= w_st_trun ? P_ST_WAIT : P_ST_READ ;//读数据状态,写完目标长度跳转结束P_ST_WAIT : r_st_next <= P_ST_STOP ;P_ST_STOP : r_st_next <= r_st_cnt == 1? P_ST_EMPTY : P_ST_STOP;P_ST_EMPTY : r_st_next <= r_st_restart | r_ack_lock ? P_ST_START : P_ST_IDLE; //空状态,等待IIC成功停止,判断是否重启,重启转入START,不重启转入IDLEdefault : r_st_next <= P_ST_IDLE;endcase
end//iic应答状态,1为没应答
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_ack_lock <= 'd0;else if(r_ack_valid && !w_iic_sda && r_st_current == P_ST_DADDR1)r_ack_lock <= 'd0;else if(r_ack_valid && w_iic_sda && r_st_current == P_ST_DADDR1)r_ack_lock <= 'd1;else r_ack_lock <= r_ack_lock;
end//读数据时,假写操作后重启信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_st_restart <= 'd0;else if(r_st_current == P_ST_READ)r_st_restart <= 'd0;else if(r_st_current == P_ST_RESTART)r_st_restart <= 'd1;else r_st_restart <= r_st_restart;
end//操作准备信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_operation_ready <= 'd1;else if(w_operation_active)ro_operation_ready <= 'd0;else if(r_st_current == P_ST_IDLE)ro_operation_ready <= 'd1;else ro_operation_ready <= ro_operation_ready;
end//寄存操作数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_drive <= 'd0;ri_operation_addr <= 'd0;ri_operation_len <= 'd0;ri_operation_type <= 'd0;end else if(w_operation_active) beginri_drive <= {i_drive,1'b0};ri_operation_addr <= i_operation_addr ;ri_operation_len <= i_operation_len ;ri_operation_type <= i_operation_type ;end else beginri_drive <= ri_drive ;ri_operation_addr <= ri_operation_addr ;ri_operation_len <= ri_operation_len ;ri_operation_type <= ri_operation_type ;end
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_read_drive <= 'd0;else if(w_operation_active)r_read_drive <= {i_drive,1'b1};else r_read_drive <= r_read_drive;
end//状态计数器
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) r_st_cnt <= 'd0;else if(r_st_current != r_st_next || ro_write_valid || ro_read_valid)//状态跳转、写完8bit数、读写8bit数r_st_cnt <= 'd0;else if(r_st_current == P_ST_STOP)r_st_cnt <= r_st_cnt + 1;else if(r_iic_st)r_st_cnt <= r_st_cnt + 1;elser_st_cnt <= r_st_cnt;
end//iic时钟
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_iic_scl <= 'd1;else if(r_st_current >= P_ST_UADDR && r_st_current <= P_ST_WAIT)ro_iic_scl <= ~ro_iic_scl;elsero_iic_scl <= 'd1;
end//iic时钟状态
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_iic_st <= 'd0;else if(r_st_current >= P_ST_UADDR && r_st_current <= P_ST_WAIT)r_iic_st <= ~r_iic_st;elser_iic_st <= 'd0;
end//iic数据线三态门控制
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_iic_sda_ctrl <= 'd0;else if(r_st_cnt == 8 || r_st_next == P_ST_IDLE)r_iic_sda_ctrl <= 'd0;else if(r_st_current >= P_ST_START && r_st_current <= P_ST_WRITE || r_st_current == P_ST_STOP)r_iic_sda_ctrl <= 'd1;elser_iic_sda_ctrl <= r_iic_sda_ctrl;
end//iic数据线写数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_iic_sda <= 'd0;else if(r_st_current == P_ST_START)ro_iic_sda <= 'd0;else if(r_st_current == P_ST_UADDR)ro_iic_sda <= r_st_restart ? r_read_drive[7 - r_st_cnt] : ri_drive[7 - r_st_cnt];else if(r_st_current == P_ST_DADDR1)ro_iic_sda <= ri_operation_addr[15 - r_st_cnt];else if(r_st_current == P_ST_DADDR2)ro_iic_sda <= ri_operation_addr[7 - r_st_cnt];else if(r_st_current == P_ST_WRITE)ro_iic_sda <= ri_write_data[7 - r_st_cnt];else if(r_st_current == P_ST_STOP && r_st_cnt == 1)ro_iic_sda <= 'd1;elsero_iic_sda <= 'd0;
end//写请求
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_write_req <= 'd0;else if(r_st_current == P_ST_DADDR2 && ri_operation_type == P_W && r_st_cnt == 7 && r_iic_st)ro_write_req <= 'd1;else if(r_st_current >= P_ST_DADDR2 && ri_operation_type == P_W && r_st_cnt == 7 && r_iic_st)ro_write_req <= r_wr_cnt < ri_operation_len - 1 ? 1'b1 : 1'b0;elsero_write_req <= 'd0;
end//写有效
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_write_valid <= 'd0;else ro_write_valid <= ro_write_req;
end//写数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ri_write_data <= 'd0;else if(ro_write_valid)ri_write_data <= i_write_data;else ri_write_data <= ri_write_data;
end//读写计数器
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_wr_cnt <= 'd0;else if(r_st_current == P_ST_IDLE)r_wr_cnt <= 'd0;else if((r_st_current == P_ST_WRITE || r_st_current == P_ST_READ) && w_st_trun)r_wr_cnt <= r_wr_cnt + 1;else r_wr_cnt <= r_wr_cnt;
end//读出数据
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_read_data <= 'd0;else if(r_st_current == P_ST_READ && r_st_cnt >= 1 && r_st_cnt <= 8 && !r_iic_st) ro_read_data <= {ro_read_data[6:0],w_iic_sda};else ro_read_data <= ro_read_data;
end//读出数据有效
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_read_valid <= 'd0;else if(r_st_current == P_ST_READ && r_st_cnt == 8 && !r_iic_st)ro_read_valid <= 'd1;else ro_read_valid <= 'd0;
end//从机应答信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_slave_ack <= 'd0;else if(r_ack_valid)r_slave_ack <= w_iic_sda;else r_slave_ack <= 'd0;
end//指示应答有效信号
always@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_ack_valid <= 'd0;else r_ack_valid <= w_st_trun;
end
endmodule
应用IIC接口的代码
module my_ctrl(input i_clk ,input i_rst ,input [2 :0] i_ctrl_eeprom_addr ,input [15:0] i_ctrl_operation_addr ,input [1 :0] i_ctrl_operation_type ,input [7 :0] i_ctrl_operation_len ,input i_ctrl_opeartion_valid ,output o_ctrl_operation_ready ,input [7 :0] i_ctrl_write_data ,input i_ctrl_write_sop ,input i_ctrl_write_eop ,input i_ctrl_write_valid ,output [7 :0] o_ctrl_read_data ,output o_ctrl_read_valid ,/*--------iic dirve--------*/output [6 :0] o_drive ,//用户输入设备地址output [15:0] o_operation_addr ,//用户输入存储地址output [7 :0] o_operation_len ,//用户输入读写长度output [1 :0] o_operation_type ,//用户输入操作类型output o_opeartion_valid ,//用户输入有效信号input i_operation_ready ,//用户输出准备信号output [7 :0] o_write_data ,//用户输入写数据input i_write_req ,//用户写数据请求信号input [7 :0] i_read_data ,//输出IIC读到的数据input i_read_valid //输出IIC读数据有效
);/***************function**************//***************parameter*************/
localparam P_ST_IDLE = 0 ,P_ST_WRITE = 1 ,P_ST_WAIT = 2 ,P_ST_READ = 3 ,P_ST_REREAD = 4 ,P_ST_OREAD = 5 ;/***************port******************/ /***************mechine***************/
reg [7 :0] r_st_current ;
reg [7 :0] r_st_next ;/***************reg*******************/
reg ro_ctrl_operation_ready ;
reg [7 :0] ro_ctrl_read_data ;
reg ro_ctrl_read_valid ;
reg [7 :0] ri_ctrl_write_data ;
reg ri_ctrl_write_sop ;
reg ri_ctrl_write_eop ;
reg ri_ctrl_write_valid ;
reg [2 :0] ri_ctrl_eeprom_addr ;
reg [15:0] ri_ctrl_operation_addr ;
reg [1 :0] ri_ctrl_operation_type ;
reg [7 :0] ri_ctrl_operation_len ;
reg ri_operation_ready ;
reg [7 :0] ri_read_data ;
reg ri_read_valid ;
reg [6 :0] ro_drive ;
reg [15:0] ro_operation_addr ;
reg [7 :0] ro_operation_len ;
reg [1 :0] ro_operation_type ;
reg ro_opeartion_valid ;
reg r_fifo_read_en ;
reg [7 :0] r_read_cnt ;
reg [15:0] r_read_addr ;
reg r_read_vld_1d ;/***************wire******************/
wire w_ctrl_active ;
wire w_drive_end ;
wire w_drive_act ;
wire [7 :0] w_fifo_read_data ;
wire w_fifo_empty ;/***************component*************/FIFO_8X1024 FIFO_8X1024_WRITE_U0 (.clk (i_clk ),.srst (i_rst ),.din (ri_ctrl_write_data ),.wr_en (ri_ctrl_write_valid ),.rd_en (i_write_req ),.dout (o_write_data ),.full (),.empty () );FIFO_8X1024 FIFO_8X1024_READ_U0 (.clk (i_clk ),.srst (i_rst ),.din (ri_read_data ),.wr_en (ri_read_valid ),.rd_en (r_fifo_read_en ),.dout (w_fifo_read_data ),.full (),.empty (w_fifo_empty ) );/***************assign****************/
assign o_ctrl_operation_ready = ro_ctrl_operation_ready ;
assign o_ctrl_read_data = ro_ctrl_read_data ;
assign o_ctrl_read_valid = r_read_vld_1d ;
assign w_ctrl_active = i_ctrl_opeartion_valid&o_ctrl_operation_ready;
assign w_drive_end = i_operation_ready & !ri_operation_ready;
assign w_drive_act = o_opeartion_valid & i_operation_ready;
assign o_drive = ro_drive ;
assign o_operation_addr = ro_operation_addr ;
assign o_operation_len = ro_operation_len ;
assign o_operation_type = ro_operation_type ;
assign o_opeartion_valid = ro_opeartion_valid;
/***************always****************/
always@(posedge i_clk,posedge i_rst)
beginif(i_rst) r_st_current <= P_ST_IDLE;elser_st_current <= r_st_next;
endalways@(*)
begincase(r_st_current)P_ST_IDLE :r_st_next = w_ctrl_active && i_ctrl_operation_type == 1 ? P_ST_WRITE : w_ctrl_active && i_ctrl_operation_type == 2 ? P_ST_WAIT :P_ST_IDLE; P_ST_WRITE :r_st_next = w_drive_end ? P_ST_IDLE : P_ST_WRITE; P_ST_WAIT :r_st_next = P_ST_READ;P_ST_READ :r_st_next = w_drive_end ? r_read_cnt == ri_ctrl_operation_len - 1 ? P_ST_OREAD : P_ST_REREAD : P_ST_READ; P_ST_REREAD :r_st_next = P_ST_READ;P_ST_OREAD :r_st_next = w_fifo_empty ? P_ST_IDLE : P_ST_OREAD;default :r_st_next = P_ST_IDLE;endcase
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) r_fifo_read_en <= 'd0;else if(w_fifo_empty)r_fifo_read_en <= 'd0;else if(r_st_current != P_ST_OREAD && r_st_next == P_ST_OREAD)r_fifo_read_en <= 'd1;else r_fifo_read_en <= r_fifo_read_en;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) ro_ctrl_read_data <= 'd0;else ro_ctrl_read_data <= w_fifo_read_data;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) ro_ctrl_read_valid <= 'd0;else if(w_fifo_empty)ro_ctrl_read_valid <= 'd0;else if(r_fifo_read_en)ro_ctrl_read_valid <= 'd1;else ro_ctrl_read_valid <= ro_ctrl_read_valid;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) r_read_vld_1d <= 'd0;else r_read_vld_1d <= ro_ctrl_read_valid;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_ctrl_eeprom_addr <= 'd0;ri_ctrl_operation_addr <= 'd0;ri_ctrl_operation_type <= 'd0;ri_ctrl_operation_len <= 'd0;end else if(w_ctrl_active) beginri_ctrl_eeprom_addr <= i_ctrl_eeprom_addr ;ri_ctrl_operation_addr <= i_ctrl_operation_addr;ri_ctrl_operation_type <= i_ctrl_operation_type;ri_ctrl_operation_len <= i_ctrl_operation_len;end else beginri_ctrl_eeprom_addr <= ri_ctrl_eeprom_addr ;ri_ctrl_operation_addr <= ri_ctrl_operation_addr;ri_ctrl_operation_type <= ri_ctrl_operation_type;ri_ctrl_operation_len <= ri_ctrl_operation_len;end
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_ctrl_write_data <= 'd0;ri_ctrl_write_sop <= 'd0;ri_ctrl_write_eop <= 'd0;ri_ctrl_write_valid <= 'd0;end else beginri_ctrl_write_data <= i_ctrl_write_data ;ri_ctrl_write_sop <= i_ctrl_write_sop ;ri_ctrl_write_eop <= i_ctrl_write_eop ;ri_ctrl_write_valid <= i_ctrl_write_valid ;end
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)ro_ctrl_operation_ready <= 'd1;else if(w_ctrl_active)ro_ctrl_operation_ready <= 'd0;else if(r_st_current == P_ST_IDLE)ro_ctrl_operation_ready <= 'd1;elsero_ctrl_operation_ready <= ro_ctrl_operation_ready;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)ri_operation_ready <= 'd0;elseri_operation_ready <= i_operation_ready;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginri_read_data <= 'd0;ri_read_valid <= 'd0;end else beginri_read_data <= i_read_data ;ri_read_valid <= i_read_valid;end
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst) beginro_drive <= 'd0;ro_operation_addr <= 'd0;ro_operation_len <= 'd0;ro_operation_type <= 'd0;ro_opeartion_valid <= 'd0;end else if(w_drive_act) beginro_drive <= 'd0;ro_operation_addr <= 'd0;ro_operation_len <= 'd0;ro_operation_type <= 'd0;ro_opeartion_valid <= 'd0;end else if(ri_ctrl_write_eop) beginro_drive <= {4'b1010,ri_ctrl_eeprom_addr};ro_operation_addr <= ri_ctrl_operation_addr;ro_operation_len <= ri_ctrl_operation_len;ro_operation_type <= ri_ctrl_operation_type;ro_opeartion_valid <= 'd1;end else if(r_st_next == P_ST_READ && r_st_current != P_ST_READ) beginro_drive <= {4'b1010,ri_ctrl_eeprom_addr};ro_operation_addr <= r_read_addr;ro_operation_len <= 1;ro_operation_type <= ri_ctrl_operation_type;ro_opeartion_valid <= 'd1;end else beginro_drive <= ro_drive ;ro_operation_addr <= ro_operation_addr ;ro_operation_len <= ro_operation_len ;ro_operation_type <= ro_operation_type ;ro_opeartion_valid <= ro_opeartion_valid;end
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_read_addr <= 'd0;else if(w_ctrl_active)r_read_addr <= i_ctrl_operation_addr;else if(r_st_current == P_ST_READ && w_drive_end)r_read_addr <= r_read_addr + 1 ;elser_read_addr <= r_read_addr;
endalways@(posedge i_clk,posedge i_rst)
beginif(i_rst)r_read_cnt <= 'd0;else if(r_st_current == P_ST_IDLE)r_read_cnt <= 'd0;else if(r_st_current == P_ST_READ && w_drive_end)r_read_cnt <= r_read_cnt +1;elser_read_cnt <= r_read_cnt;
endendmodule