用verilog实现IIC。
时间:10-02
整理:3721RD
点击:
我想用verilog实现一个控制AT24C08读写的IIC协议的状态机,现在出现了一个很奇怪的问题,我的状态机在功能仿真时可以实现读写,但下到芯片里却发现不成功。特此求教大家,望大家不吝赐教,如能解答问题,鄙人十分感激。关于代码的详细内如下
/*这是一个控制AT24C08读写的基于IIC协议的状态机,具体功能是,向AT24C08输入一个数,再把这个数读回来。
输入的数和读回的数分别用一位数码管显示。IIC的实现思路是将SCL时钟分成四段,操作SDA变换。*/
module i2c_controller_top(write,read,datain,clock,rst,scl,sda,seg,dig);
input clock,rst;
input[3:0]datain;//数据输入端
input write,read;//控制读写的按键
output reg [7:0] seg;//数码管段信号
output reg[1:0]dig;//数码管位信号
output reg scl;
inout wire sda;
//--------------数码管显示部分---------------//
reg clk_dig;//数码管动态显示时钟
reg[15:0]cnt1;//数码管显示用的分频系数
reg[7:0]seg_code;//数码管显示译码器的输入码
//reg st;
reg [7:0]dout,dout_buf;//数据输出端及其缓冲器(输出从AT24C08读回的数据)
wire[7:0]din;//主状态机的数据输入端
always@(posedge clock or negedge rst)begin //产生数码管动态显示时钟
if(!rst) begin
cnt1<=0;
clk_dig<=0;
end
else if (cnt1==16'd50000)begin
cnt1<=0;
clk_dig<=~clk_dig;
end
else cnt1<=cnt1+16'd1;
end
always@(seg_code)begin//数码管显示译码器
case(seg_code)
8'd0:seg=8'hc0;
8'd1:seg=8'hf9;
8'd2:seg=8'ha4;
8'd3:seg=8'hb0;
8'd4:seg=8'h99;
8'd5:seg=8'h92;
8'd6:seg=8'h82;
8'd7:seg=8'hf8;
8'd8:seg=8'h80;
8'd9:seg=8'h90;
8'd10:seg=8'h88;
8'd11:seg=8'h83;
8'd12:seg=8'hc6;
8'd13:seg=8'ha1;
8'd14:seg=8'h86;
8'd15:seg=8'h8e;
default:seg=0;
endcase
end
always@(posedge clk_dig or negedge rst)begin//数码管显示
if(!rst)
//st<=0;
dig<=2'b10;
else begin
//st<=~st;
dig<=~dig;
case(dig)
2'd1:begin seg_code<=din;end
2'd2:begin seg_code<=dout;end
endcase
end
end
//--------------状态机部分------------//
reg sel;//sda三态输出选择端
reg sda_buf;
reg[7:0]send_dat;//数据发送缓冲器
reg[6:0] cnt2;//产生i2c状态机时钟的分频计数器
reg clk_iic;//状态机时钟
reg[19:0] in_state;//内部状态
reg[1:0] state;//主状态
reg[2:0]dcnt;//数据位计数器
reg[1:0]wcmd;//发送内容控制命令端,详见最下方always块
reg rw;//读写标志
parameter ba=8'b00000010,//字节地址
saw=8'b10100000,//从机地址(写)
sar=8'b10100001;//从机地址(读)
assign din={4'd0,datain};
assign sda=sel?sda_buf:1'bz;//sda三态输出
/* 状态编码*/
parameter idle=2'b00,//空闲状态
send=2'b01,//发送状态
recv=2'b10;//接受状态
parameter write_a=20'b0000_0000_0000_0000_0001,//写状态
write_b=20'b0000_0000_0000_0000_0010,
write_c=20'b0000_0000_0000_0000_0100,
write_d=20'b0000_0000_0000_0000_1000;
parameter read_a=20'b0000_0000_0000_0001_0000,//读状态
read_b=20'b0000_0000_0000_0010_0000,
read_c=20'b0000_0000_0000_0100_0000,
read_d=20'b0000_0000_0000_1000_0000;
parameter sta_a=20'b0000_0000_0001_0000_0000,//开始
sta_b=20'b0000_0000_0010_0000_0000,
sta_c=20'b0000_0000_0100_0000_0000,
sta_d=20'b0000_0000_1000_0000_0000,
sto_a=20'b0000_0001_0000_0000_0000,//结束
sto_b=20'b0000_0010_0000_0000_0000,
sto_c=20'b0000_0100_0000_0000_0000,
sto_d=20'b0000_1000_0000_0000_0000;
parameter ack_a=20'b0001_0000_0000_0000_0000,//响应
ack_b=20'b0010_0000_0000_0000_0000,
ack_c=20'b0100_0000_0000_0000_0000,
ack_d=20'b1000_0000_0000_0000_0000;
always@(posedge clock or negedge rst)begin//产生IIC状态机时钟
if(!rst)begin
cnt2<=0;
clk_iic<=0;
end
else if(cnt2==7'd24)begin
cnt2<=0;
clk_iic<=1;
end
else begin
cnt2<=cnt2+7'd1;
clk_iic<=0;
end
end
always@(posedge clk_iic or negedge rst)begin
if(!rst) begin
sel<=1;
state<=idle;
wcmd<=0;
scl<=1;
sda_buf<=1;
dcnt<=7;
rw<=0;
end
else begin
case(state)
idle: begin
if(!write) begin//如果write键被按下了,rw=0,进入发送状态
state<=send;
rw<=0;
end
else if(!read)begin
rw<=1;
state<=recv;
end
else state<=idle;
//rw<=0;
wcmd<=0;
dcnt<=7;
in_state<=sta_a;
end
send: begin//发送状态机
case(in_state)
sta_a: begin
sel<=1;
scl<=1;
sda_buf<=1;
in_state<=sta_b;
end
sta_b: begin
scl<=1;
sda_buf<=0;
in_state<=sta_c;
end
sta_c: begin
scl<=1;
sda_buf<=0;
in_state<=sta_d;
end
sta_d: begin
scl<=0;
sda_buf<=0;
in_state<=write_a;
//if(wcmd==3'd2)
// wcmd<=0;
// else sim:/IIC_top/u2/wcmd sim:/IIC_top/u2/send_dat sim:/IIC_top/u2/saw sim:/IIC_top/u2/ba sim:/IIC_top/u2/din
// wcmd<=wcmd+2'b1;
// case(wcmd)
// 3'd0:send_dat<=saw;
// 3'd1:send_dat<=ba;
// 3'd2:begin send_dat<=din;end
// endcase
end
write_a: begin
sel<=1;
scl<=0;
sda_buf<=send_dat[dcnt];
// if(wcmd==3'd2)
// wcmd<=0;
// else
//wcmd<=wcmd+2'b1;
// case(wcmd)
// 3'd0:send_dat<=saw;
// 3'd1:send_dat<=ba;
// 3'd2:begin send_dat<=din;end
// endcase
in_state<=write_b;
end
write_b: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_c;
end
write_c: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_d;
end
write_d: begin
scl<=0;
sda_buf<=send_dat[dcnt];
if(dcnt==0)begin//判断数据是否发送完,送完则进入响应状态,否则继续发送
dcnt<=7;
in_state<=ack_a;
end
else begin
dcnt<=dcnt-3'b1;
in_state<=write_a;
end
end
ack_a: begin
sel<=0;
scl<=0;
in_state<=ack_b;
end
ack_b: begin
sel<=0;
scl<=1;
in_state<=ack_c;
end
ack_c: begin
sel<=0;
scl<=1;
in_state<=ack_d;
end
ack_d: begin
scl<=0;
if(sda==0)begin//判断从机是否响应
wcmd<=wcmd+2'b1;
if(wcmd<2)
in_state<=write_a;
else begin in_state<=sto_a; wcmd <=0;end
end
else in_state<=ack_d;
end
sto_a: begin
sel<=1;
scl<=0;
sda_buf<=0;
in_state<=sto_b;
end
sto_b: begin
scl<=1;
sda_buf<=0;
in_state<=sto_c;
end
sto_c: begin
scl<=1;
sda_buf<=0;
in_state<=sto_d;
end
sto_d: begin
scl<=1;
sda_buf<=1;
state<=idle;
in_state<=sta_a;
end
endcase
end
recv: begin//接收状态机
case(in_state)
sta_a: begin
sel<=1;
scl<=0;
sda_buf<=1;
in_state<=sta_b;
end
sta_b: begin
scl<=1;
sda_buf<=1;
in_state<=sta_c;
end
sta_c: begin
scl<=1;
sda_buf<=0;
in_state<=sta_d;
end
sta_d: begin
scl<=0;
sda_buf<=0;
in_state<=write_a;
if(wcmd==3)
in_state<=read_a;
end
write_a: begin
sel<=1;
scl<=0;
sda_buf<=send_dat[dcnt];
in_state<=write_b;
end
write_b: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_c;
end
write_c: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_d;
end
write_d: begin
scl<=0;
sda_buf<=send_dat[dcnt];
in_state<=ack_a;
if(dcnt==0)begin
in_state<=ack_a;
dcnt<=7;
end
else begin
in_state<=write_a;
dcnt<=dcnt-3'd1;
end
end
ack_a: begin
sel<=0;
scl<=0;
sda_buf<=1;
in_state<=ack_b;
end
ack_b: begin
sel<=0;
scl<=1;
in_state<=ack_c;
end
ack_c: begin
sel<=0;
scl<=1;
in_state<=ack_d;
end
ack_d: begin
sel<=0;
scl<=0;
if(sda==0)begin
wcmd<=wcmd+2'b1;
if(wcmd==0)
in_state<=write_a;
else if(wcmd==1)begin
&nb
/*这是一个控制AT24C08读写的基于IIC协议的状态机,具体功能是,向AT24C08输入一个数,再把这个数读回来。
输入的数和读回的数分别用一位数码管显示。IIC的实现思路是将SCL时钟分成四段,操作SDA变换。*/
module i2c_controller_top(write,read,datain,clock,rst,scl,sda,seg,dig);
input clock,rst;
input[3:0]datain;//数据输入端
input write,read;//控制读写的按键
output reg [7:0] seg;//数码管段信号
output reg[1:0]dig;//数码管位信号
output reg scl;
inout wire sda;
//--------------数码管显示部分---------------//
reg clk_dig;//数码管动态显示时钟
reg[15:0]cnt1;//数码管显示用的分频系数
reg[7:0]seg_code;//数码管显示译码器的输入码
//reg st;
reg [7:0]dout,dout_buf;//数据输出端及其缓冲器(输出从AT24C08读回的数据)
wire[7:0]din;//主状态机的数据输入端
always@(posedge clock or negedge rst)begin //产生数码管动态显示时钟
if(!rst) begin
cnt1<=0;
clk_dig<=0;
end
else if (cnt1==16'd50000)begin
cnt1<=0;
clk_dig<=~clk_dig;
end
else cnt1<=cnt1+16'd1;
end
always@(seg_code)begin//数码管显示译码器
case(seg_code)
8'd0:seg=8'hc0;
8'd1:seg=8'hf9;
8'd2:seg=8'ha4;
8'd3:seg=8'hb0;
8'd4:seg=8'h99;
8'd5:seg=8'h92;
8'd6:seg=8'h82;
8'd7:seg=8'hf8;
8'd8:seg=8'h80;
8'd9:seg=8'h90;
8'd10:seg=8'h88;
8'd11:seg=8'h83;
8'd12:seg=8'hc6;
8'd13:seg=8'ha1;
8'd14:seg=8'h86;
8'd15:seg=8'h8e;
default:seg=0;
endcase
end
always@(posedge clk_dig or negedge rst)begin//数码管显示
if(!rst)
//st<=0;
dig<=2'b10;
else begin
//st<=~st;
dig<=~dig;
case(dig)
2'd1:begin seg_code<=din;end
2'd2:begin seg_code<=dout;end
endcase
end
end
//--------------状态机部分------------//
reg sel;//sda三态输出选择端
reg sda_buf;
reg[7:0]send_dat;//数据发送缓冲器
reg[6:0] cnt2;//产生i2c状态机时钟的分频计数器
reg clk_iic;//状态机时钟
reg[19:0] in_state;//内部状态
reg[1:0] state;//主状态
reg[2:0]dcnt;//数据位计数器
reg[1:0]wcmd;//发送内容控制命令端,详见最下方always块
reg rw;//读写标志
parameter ba=8'b00000010,//字节地址
saw=8'b10100000,//从机地址(写)
sar=8'b10100001;//从机地址(读)
assign din={4'd0,datain};
assign sda=sel?sda_buf:1'bz;//sda三态输出
/* 状态编码*/
parameter idle=2'b00,//空闲状态
send=2'b01,//发送状态
recv=2'b10;//接受状态
parameter write_a=20'b0000_0000_0000_0000_0001,//写状态
write_b=20'b0000_0000_0000_0000_0010,
write_c=20'b0000_0000_0000_0000_0100,
write_d=20'b0000_0000_0000_0000_1000;
parameter read_a=20'b0000_0000_0000_0001_0000,//读状态
read_b=20'b0000_0000_0000_0010_0000,
read_c=20'b0000_0000_0000_0100_0000,
read_d=20'b0000_0000_0000_1000_0000;
parameter sta_a=20'b0000_0000_0001_0000_0000,//开始
sta_b=20'b0000_0000_0010_0000_0000,
sta_c=20'b0000_0000_0100_0000_0000,
sta_d=20'b0000_0000_1000_0000_0000,
sto_a=20'b0000_0001_0000_0000_0000,//结束
sto_b=20'b0000_0010_0000_0000_0000,
sto_c=20'b0000_0100_0000_0000_0000,
sto_d=20'b0000_1000_0000_0000_0000;
parameter ack_a=20'b0001_0000_0000_0000_0000,//响应
ack_b=20'b0010_0000_0000_0000_0000,
ack_c=20'b0100_0000_0000_0000_0000,
ack_d=20'b1000_0000_0000_0000_0000;
always@(posedge clock or negedge rst)begin//产生IIC状态机时钟
if(!rst)begin
cnt2<=0;
clk_iic<=0;
end
else if(cnt2==7'd24)begin
cnt2<=0;
clk_iic<=1;
end
else begin
cnt2<=cnt2+7'd1;
clk_iic<=0;
end
end
always@(posedge clk_iic or negedge rst)begin
if(!rst) begin
sel<=1;
state<=idle;
wcmd<=0;
scl<=1;
sda_buf<=1;
dcnt<=7;
rw<=0;
end
else begin
case(state)
idle: begin
if(!write) begin//如果write键被按下了,rw=0,进入发送状态
state<=send;
rw<=0;
end
else if(!read)begin
rw<=1;
state<=recv;
end
else state<=idle;
//rw<=0;
wcmd<=0;
dcnt<=7;
in_state<=sta_a;
end
send: begin//发送状态机
case(in_state)
sta_a: begin
sel<=1;
scl<=1;
sda_buf<=1;
in_state<=sta_b;
end
sta_b: begin
scl<=1;
sda_buf<=0;
in_state<=sta_c;
end
sta_c: begin
scl<=1;
sda_buf<=0;
in_state<=sta_d;
end
sta_d: begin
scl<=0;
sda_buf<=0;
in_state<=write_a;
//if(wcmd==3'd2)
// wcmd<=0;
// else sim:/IIC_top/u2/wcmd sim:/IIC_top/u2/send_dat sim:/IIC_top/u2/saw sim:/IIC_top/u2/ba sim:/IIC_top/u2/din
// wcmd<=wcmd+2'b1;
// case(wcmd)
// 3'd0:send_dat<=saw;
// 3'd1:send_dat<=ba;
// 3'd2:begin send_dat<=din;end
// endcase
end
write_a: begin
sel<=1;
scl<=0;
sda_buf<=send_dat[dcnt];
// if(wcmd==3'd2)
// wcmd<=0;
// else
//wcmd<=wcmd+2'b1;
// case(wcmd)
// 3'd0:send_dat<=saw;
// 3'd1:send_dat<=ba;
// 3'd2:begin send_dat<=din;end
// endcase
in_state<=write_b;
end
write_b: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_c;
end
write_c: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_d;
end
write_d: begin
scl<=0;
sda_buf<=send_dat[dcnt];
if(dcnt==0)begin//判断数据是否发送完,送完则进入响应状态,否则继续发送
dcnt<=7;
in_state<=ack_a;
end
else begin
dcnt<=dcnt-3'b1;
in_state<=write_a;
end
end
ack_a: begin
sel<=0;
scl<=0;
in_state<=ack_b;
end
ack_b: begin
sel<=0;
scl<=1;
in_state<=ack_c;
end
ack_c: begin
sel<=0;
scl<=1;
in_state<=ack_d;
end
ack_d: begin
scl<=0;
if(sda==0)begin//判断从机是否响应
wcmd<=wcmd+2'b1;
if(wcmd<2)
in_state<=write_a;
else begin in_state<=sto_a; wcmd <=0;end
end
else in_state<=ack_d;
end
sto_a: begin
sel<=1;
scl<=0;
sda_buf<=0;
in_state<=sto_b;
end
sto_b: begin
scl<=1;
sda_buf<=0;
in_state<=sto_c;
end
sto_c: begin
scl<=1;
sda_buf<=0;
in_state<=sto_d;
end
sto_d: begin
scl<=1;
sda_buf<=1;
state<=idle;
in_state<=sta_a;
end
endcase
end
recv: begin//接收状态机
case(in_state)
sta_a: begin
sel<=1;
scl<=0;
sda_buf<=1;
in_state<=sta_b;
end
sta_b: begin
scl<=1;
sda_buf<=1;
in_state<=sta_c;
end
sta_c: begin
scl<=1;
sda_buf<=0;
in_state<=sta_d;
end
sta_d: begin
scl<=0;
sda_buf<=0;
in_state<=write_a;
if(wcmd==3)
in_state<=read_a;
end
write_a: begin
sel<=1;
scl<=0;
sda_buf<=send_dat[dcnt];
in_state<=write_b;
end
write_b: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_c;
end
write_c: begin
scl<=1;
sda_buf<=send_dat[dcnt];
in_state<=write_d;
end
write_d: begin
scl<=0;
sda_buf<=send_dat[dcnt];
in_state<=ack_a;
if(dcnt==0)begin
in_state<=ack_a;
dcnt<=7;
end
else begin
in_state<=write_a;
dcnt<=dcnt-3'd1;
end
end
ack_a: begin
sel<=0;
scl<=0;
sda_buf<=1;
in_state<=ack_b;
end
ack_b: begin
sel<=0;
scl<=1;
in_state<=ack_c;
end
ack_c: begin
sel<=0;
scl<=1;
in_state<=ack_d;
end
ack_d: begin
sel<=0;
scl<=0;
if(sda==0)begin
wcmd<=wcmd+2'b1;
if(wcmd==0)
in_state<=write_a;
else if(wcmd==1)begin
&nb
