```verilog
module icc_tx#(
parameter SIZE = 2 , //用来控制写多少次 比如地址是0000 一个地址只能存放8bit数据 超出指针就会到下一个地址0001
parameter CLK_DIV= 50_000_000 ,
parameter SPEED = 100_000 ,
parameter LED = 50
)(
input wire clk ,//系统层
input wire rst_n ,
inout wire sda ,//物理侧
output wire scl ,
input wire valid ,//用户侧(写使能)
input wire [15:0] addr ,
input wire [8*SIZE-1:0] data ,
output wire led ,
output wire a //ASK状态 1的时候就是该状态否则不是该状态
);
reg scl_r ;
reg sda_o ;
reg sda_i ;
reg [5:0] state ;
reg [((SIZE+3)*8-1):0] data_r ;//SIZE+1 因为还有带上地址加写命令0 不减一是为了产生空白的位不然移位会多移一位
reg [9:0] cunt_0 ;//开始位的计数器 记到500 start
reg [9:0] cunt_1 ;//BUSY与ASK的计数器 用来产生时钟 也方便cunt_2也就是cunt_bit计数
reg [9:0] cunt_2 ;//每一bit数据维持的时间 只在BUSY状态下计数
reg [3:0] cunt_3 ;// 每一次应答加一 计数发了多少数据
reg [30:0] cunt_4 ;//计数小灯点亮时间 1s
reg [9:0] cunt_5 ;
parameter CUNT_MAX=CLK_DIV/SPEED ;
parameter ADDR =7'b101_0000 ;//地址
parameter IDEL =6'b000_001 ;
parameter START =6'b000_010 ;
parameter BUSY =6'b000_100 ;
parameter ASK =6'b001_000 ;
parameter ERROR =6'b010_000 ;
parameter STOP =6'b100_000 ;
assign scl=scl_r ;
assign a =(state==ASK)?1'b1:1'b0 ;
assign sda=(state==ASK)?1'bz:sda_o;
//assign sda = sda_o;
//错误状态下led一直亮
assign led=(state==ERROR)?1:0 ;
//
always @(posedge clk ) begin
if(state==ASK&&cunt_1==(CUNT_MAX/4*3))
sda_i<=sda;
else
sda_i<=sda_i;
end
//数据的缓存 加移位
always @(posedge clk ) begin
if(state==IDEL&&valid==1)
data_r<={ADDR,1'b0,addr,data}; //单次写
else if(cunt_1==4&&state==BUSY) ///
data_r<=(data_r<<1); //先赋值再移位了
else
data_r<=data_r;
end
//开始位的 start计数器 记到500
always @(posedge clk ) begin
if(state==START)
cunt_0<=cunt_0+1;
else
cunt_0<=0;
end
//cunt_1
always @(posedge clk ) begin
if(state==BUSY||state==ASK)begin
if(cunt_1==CUNT_MAX-1)
cunt_1<=0;
else
cunt_1<=cunt_1+1;
end
else
cunt_1<=0;
end
//cunt_2
always @(posedge clk ) begin
if(state==BUSY)begin
if(cunt_1==CUNT_MAX-1)
cunt_2<=cunt_2+1;
else
cunt_2<=cunt_2;
end
else
cunt_2<=0;
end
//cunt_3 计数发送了几个bit数据
always @(posedge clk ) begin
if(state==ASK)begin
if(cunt_1==66)
cunt_3<=cunt_3+1;
else
cunt_3<=cunt_3;
end
else if(state==BUSY)
cunt_3<=cunt_3;
else
cunt_3<=0;
end
//cunt_4
always @(posedge clk ) begin
if(state==ERROR)
cunt_4<=cunt_4+1;
else
cunt_4<=0;
end
//结束位计数 cunt_5
always @(posedge clk ) begin
if(state==STOP)
cunt_5<=cunt_5+1;
else
cunt_5<=0;
end
//状态的转移
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
state<=IDEL;
else begin
case (state)
IDEL :begin
if(valid==1)
state<=START;
else
state<=state;
end
START:begin
if(cunt_0==CUNT_MAX-1)
state<=BUSY;
else
state<=state;
end
BUSY : begin
if(cunt_2==10'd7&&cunt_1==CUNT_MAX-1)
state<=ASK;
else
state<=state;
end
ASK :begin
if(cunt_1==CUNT_MAX-1)begin
if(sda_i==0)begin
if(cunt_3==(SIZE+3))
state<=STOP;
else
state<=BUSY;
end
else
state<=ERROR;
end
else
state<=state;
end
ERROR :begin
if(cunt_4==LED)
state<=IDEL;
else
state<=state;
end
STOP :begin
if(cunt_5==CUNT_MAX-1)
state<=IDEL;
else
state<=state;
end
default: state<=state;
endcase
end
end
//时钟线scl的描述
always @(posedge clk ) begin
if(state == IDEL || state == START)
scl_r <= 1'b1;
else if(state == BUSY || state == ASK)begin
if(cunt_1 >=0 && cunt_1 <= (CUNT_MAX / 2))
scl_r <= 1'b0;
else
scl_r <= 1'b1;
end
else if(state == STOP)begin
scl_r <= 1'b1;
end
else
scl_r <= 1'b1;
end
//数据线sda的描述
always @(posedge clk ) begin
case (state)
IDEL : sda_o<=1'b1;
START: begin
if(cunt_0<CUNT_MAX/2) //也可以起始位状态直接置低置低时间就是cunt_0==250
sda_o<=1;
else
sda_o<=0;
end
BUSY :begin
if(cunt_1==1) ///一定要等于0//*************
sda_o<=data_r[((SIZE+3)*8-1)];
else
sda_o<=sda_o;
end
ASK :begin
if(cunt_1==CUNT_MAX-1)
sda_o<=1'b0; //给0才可以 因为busy中的保持导致给1会在结束位sda也是一个脉冲1
else
sda_o<=1'bz;
end
ERROR:sda_o<=1'b1;
STOP :begin
if(cunt_5<CUNT_MAX/2)
sda_o<=1'b0;
else
sda_o<=1'b1;
end
default: sda_o<=1'b1;
endcase
end
endmodule
仿真
`timescale 1ns / 1ps
module tb_icc_tx(
);
reg clk ;///
reg rst_n;///
wire sda ;
wire scl ;
reg valid;///
reg [15:0] data ;///
wire led ;
wire a ;
reg [15:0] addr ;
initial begin
clk =1;
rst_n<=0;
valid<=0;
data <=0;
#100
rst_n<=1;
#100
valid<=1;
data <=16'b1111_0000_0000_1111;
addr <=16'b1111_0000_0000_0000;
#20
valid<=0;
data <=0;
end
assign sda= (a==1)?1'b0:1'bz;//从机发的
always #10 clk=~clk;
icc_tx#(
/*parameter */. SIZE (2 ),
/*parameter */. CLK_DIV(50_000_000 ),
/*parameter */. SPEED (100_000 ),
/*parameter */. LED (50 )
)u_icc_tx(
/*input wire */ .clk (clk ),//系统层
/*input wire */ .rst_n(rst_n),
/*inout wire */ .sda (sda ),//物理侧
/*output wire */ .scl (scl ),
/*input wire */ .valid(valid),//用户侧
/*input wire [8*SIZE-1:0] */ .data (data ),
/*output wire */ .led (led ),
/*input wire [15:0] */ .addr (addr ) ,
/*output wire */ .a (a ) //ASK状态 1的时候就是该状态否则不是该状态
);
endmodule