Bootstrap

IIC _tx


```c
module icc_tx#(
    parameter SIZE   = 2          ,
    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 [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+1)*8: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'b100_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<={1'b0,ADDR,1'b0,data};
    else  if(cunt_1==1)
    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+1))
                        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==0)   ///一定要等于0//*************
            sda_o<=data_r[(SIZE+1)*8];
            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    ;

initial begin
    clk   =1;
    rst_n<=0;
    valid<=0;
    data <=0;
    #100
    rst_n<=1;
    #100
    valid<=1;
    data <=16'b1111_0000_0000_1111;   
    #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  ),
    /*output    wire              */ .a    (a    ) //ASK状态 1的时候就是该状态否则不是该状态
);
endmodule

在这里插入图片描述

;