实现的代码详情: 端口声明与内部信号 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
|