- module IIC#(parameter id0 = 1'b0,id1 = 1'b0,id2 = 1'b1)(
- input CLK_50M,
- input rst_n,
- input [7:0] data,
- input [7:0] command,
- input req,
- inout iic_SLC,
- inout iic_SDA,
- output reg [7:0]data_out,
- output reg finish,
- output reg io_en
- );
- //iic控制
- reg [8:0] SLC_cnt;
- reg cnt_flag;
- reg [4:0] n_cnt;
- reg n_cnt_flag;
- reg iic_SDA_reg;
- reg iic_SLC_reg;
- //外部输入缓存
- reg [7:0] data_reg;
- reg [7:0] command_reg;
- reg req_reg;
- //输出数据缓存
- reg [7:0] data_out_reg;
- //内部需要被访问的寄存器
- reg [7:0] inner_data;
- //状态信号
- reg busy;
- wire send;
- wire start;
- wire stop;
- //id寻找
- wire match_flag;
- //ack错误
- reg error;
- //状态机
- reg [6:0]cur_state;
- reg [6:0]next_state;
- localparam s_idle = 7'b000_0001;
- localparam s_send = 7'b000_0010;
- localparam s_mwrite = 7'b000_0100;
- localparam s_mread = 7'b000_1000;
- localparam s_busy = 7'b001_0000;
- localparam s_swrite = 7'b010_0000;
- localparam s_sread = 7'b100_0000;
- //id序列检测
- reg [7:0]id_state;
- localparam id_idle = 8'b0000_0001;
- localparam id_1 = 8'b0000_0010;
- localparam id_10 = 8'b0000_0100;
- localparam id_101 = 8'b0000_1000;
- localparam id_1010 = 8'b0001_0000;
- localparam id_1010_0 = 8'b0010_0000;
- localparam id_1010_00 = 8'b0100_0000;
- localparam id_1010_001 = 8'b1000_0000;
我们把这个IIC设备的id低三位设置成001,前四位假设为1010,则此IIC设备的id码为1010 001。
SCL时钟生成
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- cnt_flag <= 1'b0;
- else if(start)
- cnt_flag <= 1'b1;
- else if(stop)
- cnt_flag <= 1'b0;
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- SCL_cnt <= 'd0;
- else if(SCL_cnt == 'd499)
- SCL_cnt <= 'd0;
- else if(cnt_flag)
- SCL_cnt <= SCL_cnt + 1'b1;
- else
- SCL_cnt <= 'd0;
-
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- iic_SCL_reg <= 1'b1;
- else if(SCL_cnt == 'd249 || SCL_cnt == 'd499)
- iic_SCL_reg <= ~iic_SCL_reg;
-
- assign iic_SCL = iic_SCL_reg;
50M时钟计数 250 翻转一次 iic_SCL ,生成100K频率的 iic_SCL 同步时钟 。
由于SCL形成伪握手机制需要配合IIC的物理结构实现,只用verilog实现不了,我们就没有给 iic_SCL 添加IOBUF,使得 iic_SCL 一直输出有效。
同时由于 inout 信号需要为 wire 型,所以用reg型的 iic_SCL_reg 放置在 always 块中,并用 assign 赋值给iic_SCL。
状态信号及状态机
- assign start = ((cur_state == s_send && n_cnt == 'd0) ||
- ((cur_state == s_idle || cur_state == s_busy) && n_cnt == 'd0))? (iic_SCL && ~iic_SDA) : 1'b0;
- assign stop = (n_cnt == 'd19 && SCL_cnt > 'd125)? (iic_SCL && iic_SDA) : 1'b0;
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)begin
- command_reg <= 'd0;
- data_reg <= 'd0;
- req_reg <= 1'b0;end
- else if(req)begin
- command_reg <= command;
- data_reg <= data;
- req_reg <= req;end
- else if(next_state == s_send && SCL_cnt == 'd249)
- req_reg <= 1'b0;
- else if(error)
- req_reg <= 1'b1;
-
- assign send = req_reg && ~finish && ~busy;
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- busy <= 1'b0;
- else if(stop)
- busy <= 1'b0;
- else if(~req_reg && start)
- busy <= 1'b1;
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- next_state <= s_idle;
- else
- next_state <= cur_state;
- always@(*)
- case(next_state)
- s_idle : begin
- if(busy)
- cur_state = s_busy;
- else if(send)
- cur_state = s_send;
- else
- cur_state = s_idle;end
-
- s_send : begin
- if(n_cnt == 'd8 && iic_SDA == 1'b0 && SCL_cnt == 'd499)
- cur_state = s_mwrite;
- else if(n_cnt == 'd8 && iic_SDA == 1'b1 && SCL_cnt == 'd499)
- cur_state = s_mread;
- else
- cur_state = s_send;end
-
- s_busy : begin
- if(match_flag && iic_SDA == 1'b0 && SCL_cnt == 'd499)//这时候作为从机,主机的写是从机的读
- cur_state = s_sread;
- else if(match_flag && iic_SDA == 1'b1 && SCL_cnt == 'd499)//这时候作为从机,主机的读是从机的写
- cur_state = s_swrite;
- else if(stop)
- cur_state = s_idle;
- else
- cur_state = s_busy;end
- s_mwrite : begin
- if(error)
- cur_state = s_idle;
- else if(stop)
- cur_state = s_idle;
- else
- cur_state = s_mwrite;end
- s_mread : begin
- if(error)
- cur_state = s_idle;
- else if(stop)
- cur_state = s_idle;
- else
- cur_state = s_mread; end
- s_swrite : begin
- if(error)
- cur_state = s_idle;
- else if(stop)
- cur_state = s_idle;
- else
- cur_state = s_swrite;end
- s_sread : begin
- if(stop)
- cur_state = s_idle;
- else
- cur_state = s_sread;end
- default : cur_state = s_idle;
- endcase
id序列检测
- always@(posedge iic_SCL or negedge rst_n)
- if(~rst_n)
- id_state <= id_idle;
- else if(busy && n_cnt <= 'd8) begin
- case(id_state)
- id_idle:begin
- if(iic_SDA == 1'b1)
- id_state <= id_1;
- else
- id_state <= id_idle; end
- id_1:begin
- if(iic_SDA == 1'b0)
- id_state <= id_10;
- else
- id_state <= id_idle; end
- id_10:begin
- if(iic_SDA == 1'b1)
- id_state <= id_101;
- else
- id_state <= id_1; end
- id_101:begin
- if(iic_SDA == 1'b0)
- id_state <= id_1010;
- else
- id_state <= id_1; end
- id_1010:begin
- if(iic_SDA == 1'b0)
- id_state <= id_1010_0;
- else
- id_state <= id_101; end
- id_1010_0:begin
- if(iic_SDA == 1'b0)
- id_state <= id_1010_00;
- else
- id_state <= id_1; end
- id_1010_00:begin
- if(iic_SDA == 1'b1)
- id_state <= id_1010_001;
- else
- id_state <= id_idle; end
- id_1010_001:
- id_state <= id_idle;
- default:
- id_state <= id_idle;
- endcase
- end
- else
- id_state <= id_idle;
- assign match_flag = (id_state == id_1010_001);
线性序列机
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- n_cnt <= 'd0;
- else if(~n_cnt_flag)
- n_cnt <= 'd0;
- else if(n_cnt_flag && SCL_cnt == 'd499)
- n_cnt <= n_cnt + 1'b1;
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- n_cnt_flag <= 1'b0;
- else if(cur_state == s_idle)
- n_cnt_flag <= 1'b0;
- else if(stop)
- n_cnt_flag <= 1'b0;
- else if(start)
- n_cnt_flag <= 1'b1;
- always@(*)
- begin
- if(next_state == s_idle) begin
- iic_SDA_reg = 1'bz;
- io_en = 1'b0; end
- else if(next_state == s_send) begin
- case(n_cnt)
- 0:begin iic_SDA_reg = 1'b0;io_en = 1'b1;end
- 1:begin iic_SDA_reg = command_reg[7];io_en = 1'b1;end
- 2:begin iic_SDA_reg = command_reg[6];io_en = 1'b1;end
- 3:begin iic_SDA_reg = command_reg[5];io_en = 1'b1;end
- 4:begin iic_SDA_reg = command_reg[4];io_en = 1'b1;end
- 5:begin iic_SDA_reg = command_reg[3];io_en = 1'b1;end
- 6:begin iic_SDA_reg = command_reg[2];io_en = 1'b1;end
- 7:begin iic_SDA_reg = command_reg[1];io_en = 1'b1;end
- 8:begin iic_SDA_reg = command_reg[0];io_en = 1'b1;end
- default:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
- endcase
- end
-
- else if(next_state == s_mwrite) begin
- case(n_cnt)
- 9: begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
- 10:begin iic_SDA_reg = data_reg[7];io_en = 1'b1;end
- 11:begin iic_SDA_reg = data_reg[6];io_en = 1'b1;end
- 12:begin iic_SDA_reg = data_reg[5];io_en = 1'b1;end
- 13:begin iic_SDA_reg = data_reg[4];io_en = 1'b1;end
- 14:begin iic_SDA_reg = data_reg[3];io_en = 1'b1;end
- 15:begin iic_SDA_reg = data_reg[2];io_en = 1'b1;end
- 16:begin iic_SDA_reg = data_reg[1];io_en = 1'b1;end
- 17:begin iic_SDA_reg = data_reg[0];io_en = 1'b1;end
- 18:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
- 19:begin
- io_en = 1'b1;
- if(SCL_cnt > 'd125)
- iic_SDA_reg = 1'b1;
- else if(SCL_cnt <= 'd124)
- iic_SDA_reg = 1'b0; end
- default:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
- endcase
- end
-
- else if(next_state == s_mread) begin
- case(n_cnt)
- 9,10,11,12,13,14,15,16,17:begin
- iic_SDA_reg = 1'bz;io_en = 1'b0;end
- 18:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
- 19:begin
- io_en = 1'b1;
- if(SCL_cnt > 'd125)begin
- iic_SDA_reg = 1'b1;end
- else if(SCL_cnt <= 'd124)
- iic_SDA_reg = 1'b0; end
- default:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
- endcase
- end
- else if(next_state == s_sread) begin
- case(n_cnt)
- 9:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
- 10,11,12,13,14,15,16,17:begin
- iic_SDA_reg = 1'bz;io_en = 1'b0;end
- 18:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
- 19:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//等待主机发送stop
- default:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
- endcase
- end
- else if(next_state == s_swrite) begin
- case(n_cnt)
- 9 :begin iic_SDA_reg = 1'b1;io_en = 1'b1;end//ack
- 10:begin iic_SDA_reg = inner_data[7];io_en = 1'b1;end
- 11:begin iic_SDA_reg = inner_data[6];io_en = 1'b1;end
- 12:begin iic_SDA_reg = inner_data[5];io_en = 1'b1;end
- 13:begin iic_SDA_reg = inner_data[4];io_en = 1'b1;end
- 14:begin iic_SDA_reg = inner_data[3];io_en = 1'b1;end
- 15:begin iic_SDA_reg = inner_data[2];io_en = 1'b1;end
- 16:begin iic_SDA_reg = inner_data[1];io_en = 1'b1;end
- 17:begin iic_SDA_reg = inner_data[0];io_en = 1'b1;end
- 18:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//ack
- 19:begin iic_SDA_reg = 1'bz;io_en = 1'b0;end//等待主机发送stop
- default:begin iic_SDA_reg = 1'b1;io_en = 1'b1;end
- endcase
- end
-
- else begin
- iic_SDA_reg = 1'bz;io_en = 1'b0;end
- end
- assign iic_SDA = io_en ? iic_SDA_reg : 1'bz;
inner_data,dout,finish控制
- always@(posedge iic_SCL or negedge rst_n)
- if(~rst_n)
- inner_data <= 'd0;
- else if(next_state == s_sread && n_cnt >= 'd11 && n_cnt <= 'd18)
- inner_data <= {inner_data[6:0],iic_SDA};
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- finish <= 1'b0;
- else if(next_state == s_mread && n_cnt == 'd19)
- finish <= 1'b1;
- else if(next_state == s_mwrite && n_cnt == 'd19)
- finish <= 1'b1;
- else
- finish <= 1'b0;
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- error <= 1'b0;
- else if(next_state == s_mwrite)begin
- if(n_cnt == 'd9 ||n_cnt == 'd18) begin
- if(iic_SDA == 1'b0)
- error <= 1'b1;
- else
- error <= 1'b0; end
- else
- error <= 1'b0; end
- else if(next_state == s_mread) begin
- if(n_cnt == 'd9) begin
- if(iic_SDA == 1'b0)
- error <= 1'b1;
- else
- error <= 1'b0; end
- else
- error <= 1'b0; end
- else if(next_state == s_swrite)begin
- if(n_cnt == 'd18) begin
- if(iic_SDA == 1'b0)
- error <= 1'b1;
- else
- error <= 1'b0; end
- else
- error <= 1'b0; end
- else
- error <= 1'b0;
- always@(posedge iic_SCL or negedge rst_n)
- if(~rst_n)
- data_out_reg <= 'd0;
- else if(next_state == s_mread && n_cnt < 'd18)
- data_out_reg <= {data_out_reg[6:0],iic_SDA};
- always@(posedge CLK_50M or negedge rst_n)
- if(~rst_n)
- data_out <= 'd0;
- else if(next_state == s_mread && n_cnt == 'd19)
- data_out <= data_out_reg;
- else
- data_out <= 'd0;
- endmodule
top顶层
- module IIC_top(
- input ack,
- input CLK_50M,
- input rst_n,
- input [7:0] data,
- input [7:0] command,
- input req,
- inout iic_SCL,
- output finish,
- output [7:0] data_out
- );
- wire iic_SDA_top;
- wire io_en;
- assign iic_SDA_top = (!io_en)? ack : 1'bz;
- IIC#(.id0(1'b0),
- .id1 (1'b0),
- .id2 (1'b1)
- )master1(
- .CLK_50M (CLK_50M) ,
- .rst_n (rst_n) ,
- .data (data) ,
- .command (command) ,
- .req (req ) ,
- .iic_SCL (iic_SCL) ,
- .iic_SDA (iic_SDA_top) ,
- .finish (finish ) ,
- .io_en (io_en ) ,
- .data_out (data_out)
- );
- endmodule