




module iic(
input clk ,
input rst ,
input [ 4:0] cmd ,
input cmd_vld ,
input [ 7:0] wr_data ,
output [ 7:0] rd_data ,
output rd_data_vld ,
output reg rev_ack ,
output done ,
output reg scl ,
inout sda
);
/*
模拟过程
1. cnt_num
2. 二阶状态机
3. scl的生成
cmd和cmd_vld是一起过来的!!!
*/
`define START_BIT 5'b00001
`define WRITE_BIT 5'b00010
`define READ_BIT 5'b00100
`define STOP_BIT 5'b01000
`define ACK_BIT 5'b10000
`define ACK 0
`define NO_ACK 1
localparam STATUS_IDLE = 7'b000_0001;
localparam STATUS_START = 7'b000_0010;
localparam STATUS_WR_DATA = 7'b000_0100;
localparam STATUS_RD_DATA = 7'b000_1000;
localparam STATUS_T_ACK = 7'b001_0000;
localparam STATUS_R_ACK = 7'b010_0000;
localparam STATUS_STOP = 7'b100_0000;
localparam T = 100_000;
localparam SCL_MAX = 50_000_000/T; // 一个完整的SCL周期
/*
localparam和define放在最前面
*/
reg [3:0]cnt_num ;
reg [6:0]cstate;
reg [6:0]nstate;
reg [7:0]wr_data_r;
reg sda_out;
parameter SCL_LOW_HALF = (SCL_MAX * 1 / 4) - 1,
SCL_HIGH_HALF = (SCL_MAX * 3 / 4) - 1;
reg [25:0]cnt_bit;
// 下面是关键的 状态转换的检测信号
reg OE;
reg[8*20-1:0]state_name;
reg [7:0]rd_data_r;
reg [4:0]num;
wire end_cnt_num;
/*下面是输入输出信号*/
assign rd_data = rd_data_r; //读也从寄存器开始输出
assign sda = OE?sda_out:1'bz;
wire sda_in = sda;
reg [4:0]cmd_r;
wire IF_IDLE_START = (cstate == STATUS_IDLE ) && cmd_vld && (cmd & `START_BIT) ;
wire IF_START_WR_DATA = (cstate == STATUS_START ) && end_cnt_num && (cmd_r & `WRITE_BIT) ;
wire IF_WR_DATA_R_ACK = (cstate == STATUS_WR_DATA ) && end_cnt_num ;
wire IF_R_ACK_IDLE = (cstate == STATUS_R_ACK ) && end_cnt_num && !(cmd_r & `STOP_BIT) ; // 可以接收到响应后停止(比如说在读数据之前)
wire IF_IDLE_WR_DATA = (cstate == STATUS_IDLE ) && cmd_vld && (cmd & `WRITE_BIT) ;
wire IF_R_ACK_STOP = (cstate == STATUS_R_ACK ) && end_cnt_num && (cmd_r & `STOP_BIT) ;
wire IF_STOP_IDLE = (cstate == STATUS_STOP ) && end_cnt_num ;
wire IF_IDLE_RD_DATA = (cstate == STATUS_IDLE ) && cmd_vld && (cmd & `READ_BIT) ;
wire IF_RD_DATA_T_ACK = (cstate == STATUS_RD_DATA ) && end_cnt_num ;
wire IF_T_ACK_IDLE = (cstate == STATUS_T_ACK ) && end_cnt_num && !(cmd_r & `STOP_BIT) ;
wire IF_T_ACK_STOP = (cstate == STATUS_T_ACK ) && end_cnt_num && (cmd_r & `STOP_BIT) ;
/*
cmd_vld对应cmd 是由外界的cmd触发的
end_cnt_num对应cmd_r 是由自动触发引起的
*/
assign add_cnt_bit = cstate != STATUS_IDLE;
assign end_cnt_bit = add_cnt_bit && cnt_bit == SCL_MAX - 1'd1;
assign add_cnt_num = end_cnt_bit;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;
assign done = IF_R_ACK_IDLE || IF_STOP_IDLE || IF_T_ACK_IDLE;//都是回到IDLE 用来对应eeprom的cnt_byte
assign rd_data_vld = IF_T_ACK_STOP || IF_T_ACK_IDLE;// 快发送ACK的时候
/*
cnt_bit的目的是组成完整的scl时钟周期
*/
always @(posedge clk or negedge rst)
if (!rst) begin
cnt_bit <= 26'd0;
end else if(add_cnt_bit)begin
if(end_cnt_bit)
cnt_bit <= 26'd0;
else
cnt_bit <= cnt_bit + 26'd1;
end
//IIC_SCL
always @(posedge clk or negedge rst) begin
if (!rst) begin
scl <= 1'b1;
end else if(cnt_bit == (SCL_MAX - 1'd1)>>1 || IF_STOP_IDLE)begin
scl <= 1'b1;
end else if(end_cnt_bit)begin
scl <= 1'b0;//start信号 默认是1 过了第一个end_cnt_bit才把scl拉低 妙啊
end
end
// 能用寄存器 都用寄存器
always @(posedge clk or negedge rst)
if (!rst) begin
OE = 1'b1;
end else if(IF_IDLE_START || IF_START_WR_DATA || IF_IDLE_WR_DATA || IF_RD_DATA_T_ACK || IF_T_ACK_STOP || IF_R_ACK_STOP)begin// !!!添加了IF_T_ACK_STOP
OE = 1'b1;
end else if(IF_WR_DATA_R_ACK || IF_IDLE_RD_DATA || IF_STOP_IDLE)begin
OE = 1'b0;
end
always @(posedge clk or negedge rst) begin
if (!rst) begin
sda_out <= 1;
end
else begin
case (cstate)
STATUS_IDLE :sda_out <= 1;
STATUS_START :begin
if(cnt_bit == SCL_LOW_HALF)
sda_out <= 1;
else if(cnt_bit == SCL_HIGH_HALF)
sda_out <= 0;
end
STATUS_WR_DATA :begin
if(cnt_bit == SCL_LOW_HALF)
sda_out <= wr_data_r[7-cnt_num];
end
STATUS_T_ACK :begin//
if(cnt_bit == SCL_LOW_HALF)
if(cmd_r & `ACK_BIT)
sda_out <= `NO_ACK;
else
sda_out <= `ACK;
end
STATUS_STOP :begin
if(cnt_bit == SCL_LOW_HALF)
sda_out <= 0;
else if(cnt_bit == SCL_HIGH_HALF)
sda_out <= 1;
end
default: sda_out <= 'b1;
endcase
end
end
always@(*)
case(cstate)
STATUS_IDLE : state_name = "IDLE ";
STATUS_START : state_name = "START ";
STATUS_WR_DATA : state_name = "WR_DATA";
STATUS_RD_DATA : state_name = "RD_DATA";
STATUS_T_ACK : state_name = "T_ACK ";
STATUS_R_ACK : state_name = "R_ACK ";
STATUS_STOP : state_name = "STOP ";
default : state_name = "IDLE ";
endcase
always @(posedge clk or negedge rst) begin
if (!rst) begin
rev_ack <= 0;
rd_data_r <= 8'b0;
end else case (cstate)
STATUS_RD_DATA:
if(cnt_bit == SCL_HIGH_HALF)
rd_data_r[7 - cnt_num] <= sda;
STATUS_R_ACK:
if(cnt_bit == SCL_HIGH_HALF)
rev_ack <= sda_in;
default:;
endcase
end
/*
在scl的1/4发送数据,在3/4处接受数据
*/
always @(*) begin
case (cstate)
STATUS_IDLE : num = 1;
STATUS_START : num = 1;
STATUS_WR_DATA : num = 8;
STATUS_RD_DATA : num = 8;
STATUS_R_ACK : num = 1;
STATUS_T_ACK : num = 1;
STATUS_STOP : num = 1;
default : num = 1;
endcase
end
// 把输入的东西先存放在寄存器里面 确保在状态机运行期间,这些信号的值保持稳定
always @(posedge clk or negedge rst) begin
if (!rst) begin
wr_data_r <= 'd0;
cmd_r <= 'd0;
end
else if (cmd_vld) begin
wr_data_r <= wr_data;
cmd_r <= cmd;
end
end
always@(posedge clk or negedge rst)
if(!rst)begin
cstate <= STATUS_IDLE;
end else begin
cstate <= nstate;
end
always @(*) begin
case(cstate)
STATUS_IDLE : begin
if (IF_IDLE_START) begin
nstate = STATUS_START;
end
else if (IF_IDLE_WR_DATA) begin
nstate = STATUS_WR_DATA;
end
else if (IF_IDLE_RD_DATA) begin
nstate = STATUS_RD_DATA;
end
else begin
nstate = cstate; //这个要注意
end
end
STATUS_START : begin
if (IF_START_WR_DATA) begin
nstate = STATUS_WR_DATA;
end
else begin
nstate = cstate;
end
end
STATUS_WR_DATA : begin
if (IF_WR_DATA_R_ACK) begin
nstate = STATUS_R_ACK;
end
else begin
nstate = cstate;
end
end
STATUS_RD_DATA : begin
if (IF_RD_DATA_T_ACK) begin
nstate = STATUS_T_ACK;
end
else begin
nstate = cstate;
end
end
STATUS_R_ACK : begin
if (IF_R_ACK_STOP) begin
nstate = STATUS_STOP;
end
else if (IF_R_ACK_IDLE) begin
nstate = STATUS_IDLE;
end
else begin
nstate = cstate;
end
end
STATUS_T_ACK : begin
if (IF_T_ACK_STOP) begin
nstate = STATUS_STOP;
end
else if (IF_T_ACK_IDLE) begin
nstate = STATUS_IDLE;
end
else begin
nstate = cstate;
end
end
STATUS_STOP : begin
if (IF_STOP_IDLE) begin
nstate = STATUS_IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = cstate;
endcase
end
always @(posedge clk or negedge rst)begin
if(!rst)begin
cnt_num <= 'd0;
end
else if(add_cnt_num)begin
if(end_cnt_num)begin
cnt_num <= 'd0;
end
else begin
cnt_num <= cnt_num + 1'd1;
end
end
end
endmodule