当前位置:   article > 正文

基于verilog的EEPROM读写_otp verilog读写

otp verilog读写

I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL

这里以一个byte的读写为例

时序图

写时序
这里写图片描述

读时序
这里写图片描述

通过时序图可知,IIC读的时候需要先完成写的控制字和地址的命令,因为读的部分和写的部分有重复,所以这里的IIC控制模块我使用状态机来完成的,划分状态时,写时序的start—ack_low_addr这段为复用状态

控制命令和状态的定义

//instru
parameter 
            WR_CTRL_DATA=   8'hA0,
            HIGH_DATA   =   8'h00,
            LOW_DATA    =   8'h05,
            DATA_WR     =   8'hAf,
            RD_CTRL_DATA=   8'hA1;
    //state
parameter 
        WR_START    =   15'b000_0000_0000_0001,
        WR_CTRL_BYTE=   15'b000_0000_0000_0010,
        ACK_WR_CTRL =   15'b000_0000_0000_0100,
        HIGH_ADDR   =   15'b000_0000_0000_1000,
        ACK_HIGH    =   15'b000_0000_0001_0000,
        LOW_ADDR    =   15'b000_0000_0010_0000,
        ACK_LOW     =   15'b000_0000_0100_0000,
        WR_DATA     =   15'b000_0000_1000_0000,
        ACK_WR      =   15'b000_0001_0000_0000,
        RD_START    =   15'b000_0010_0000_0000,
        RD_CTRL_BYTE=   15'b000_0100_0000_0000,
        ACK_RD_CTRL =   15'b000_1000_0000_0000,
        RD_DATA     =   15'b001_0000_0000_0000,
        NO_ACK      =   15'b010_0000_0000_0000,
        STOP        =   15'b100_0000_0000_0000;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

用两个按键控制写和读

//----------key_flag--------------------
    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        key_wr_state <= 1'b0;
    else if(w_valid)
        key_wr_state <= 1'b1;
    else if(key_clr)
        key_wr_state <= 1'b0;
    else 
        key_wr_state <= key_wr_state;

    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        key_rd_state <= 1'b0;
    else if(r_valid)
        key_rd_state <= 1'b1;
    else if(key_clr)
        key_rd_state <= 1'b0;
    else 
        key_rd_state <= key_rd_state;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

iic的scl这里用的是200K

//------scl 200K------------------------------------
    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        cnt_clk <= 'd0;
    else if(key_wr_state || key_rd_state)begin
        if(cnt_clk == 'd124)
            cnt_clk <= 'd0;
        else
            cnt_clk <= cnt_clk + 1'b1;
    end
    else 
        cnt_clk <= 'd0;

    always @(posedge clk or negedge rst_n)
    if(!rst_n)  
        eeprom_scl <= 1'b1;
    else if((key_wr_state || key_rd_state) && cnt_clk == 'd124)
        eeprom_scl <= ~eeprom_scl;
    else 
        eeprom_scl <= eeprom_scl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

状态开始信号由按键标志产生

//-------------start------------------- 
    always @(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_start <= 1'b0;
    else if(w_valid || r_valid)
        wr_start <= 1'b1;
    else if(eeprom_scl == 1 && eeprom_sda == 1'b0 && cnt_clk == 'd124)
        wr_start <= 1'b0;
    else 
        wr_start <= wr_start;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

控制字命令的

//此处建议使用case语句
//参考
alw @(posedge clk or negedge rst_n)
    instru <= 'd0;
else begin
    case(state)
        WR_CTRL_BYTE   : 
        HIGH_ADDR      :
        LOW_ADDR       :
        WR_DATA        :
        RD_CTRL_BYTE   :
        default        :
    endcase
end

    //-----------------instru---------------------

    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        instru <= 'd0;
    else if(state == WR_CTRL_BYTE)
        instru <= WR_CTRL_DATA;
    else if(state == HIGH_ADDR)
        instru <= HIGH_DATA;
    else if(state == LOW_ADDR)
        instru <= LOW_DATA;
    else if(state == WR_DATA)
        instru <= DATA_WR;
    else if(state == RD_CTRL_BYTE)
        instru <= RD_CTRL_DATA;
    else 
        instru <= instru;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

移位计数器:因为iic需要从机ack应答
所以这里移位计数器设置9位,1到8位为串行命令的移位输入,第9位为从机应答

//--------------shift_byte-----------------------
    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        shift_cnt <= 'd0;
    else if(shift_flag && state != RD_START && state != STOP)begin
        if(shift_cnt== 'd8 &&eeprom_scl == 'd0 && cnt_clk == 'd63)
            shift_cnt <= 'd0;
        else if(state != WR_START && eeprom_scl == 'd0 && cnt_clk == 'd63)
            shift_cnt <= shift_cnt + 'b1;
    end
    else
        shift_cnt <=shift_cnt; 

    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        shift_byte <= 'd0;
    else if(shift_cnt == 'd0)
        shift_byte <= instru;
    else if(shift_cnt != 'd0 && cnt_clk == 'd64 && eeprom_scl == 'd0)
        shift_byte <= {shift_byte[6:0],shift_byte[7]};
    else 
        shift_byte <= shift_byte;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

当写控制完成后产生stop信号,然后需要读的时候这里设置了读数据的开始信号

//-------------------rd_start_flag--------------

    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        rd_start_flag <= 'd0;
    else if(key_rd_state && state == ACK_LOW && eeprom_scl == 'd0 && cnt_clk == 'd63)
        rd_start_flag <= 'd1;
    else 
        rd_start_flag <= 'd0;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

写状态完成和读状态完成后都会产生一个stop标志回到程序复位状态等待下次读写

//------------------stop_wr-----------------------------
    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        stop_wr <= 'd0;
    else if((state == ACK_WR||state == NO_ACK) && eeprom_scl =='d0&& cnt_clk == 'd63)
        stop_wr <= 'd1;
    else 
        stop_wr <= 'd0;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

因为iic需要从机应答,所以仿真时应答信号可以设置为高阻态

//---------eeprom_sda----------------------------------------
    always @(posedge clk or negedge rst_n)
    if(rst_n == 1'b0)
        oe <= 1'b1;
    else if((shift_cnt =='d8 && state != NO_ACK) || state ==RD_DATA)//
        oe <= 1'b0;
    else
        oe <= 1'b1;

    assign  eeprom_sda = (oe == 1'b1)? sda_r: 1'bz;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

状态机

wire key_clr;
    assign key_clr = (state == STOP && eeprom_scl == 'd1 && cnt_clk == 'd123)? 1'b1:1'b0;
    wire state_jump_flag;
    assign state_jump_flag = (eeprom_scl == 0 && cnt_clk == 'd63 && shift_cnt=='d7)? 1'b1:1'b0;
    wire ack_jump;
    assign ack_jump = (shift_cnt=='d8 && eeprom_scl == 0 && cnt_clk == 'd63)? 1'b1:1'b0;


//----------------state_ctrl------------------------------
    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        state <= WR_START;
    else begin
        case(state)
            //1-------------------------wr_state----------------------
            WR_START        :   if(shift_flag && eeprom_scl == 0 && cnt_clk == 'd63)
                                    state <= WR_CTRL_BYTE;
                                else
                                    state <= WR_START;
            //2                 
            WR_CTRL_BYTE    :   if(state_jump_flag)
                                    state <= ACK_WR_CTRL;
                                else 
                                    state <= WR_CTRL_BYTE;
            //4                 
            ACK_WR_CTRL     :   if(ack_jump)
                                    state <= HIGH_ADDR;
                                else 
                                    state <= ACK_WR_CTRL;
            //8
            HIGH_ADDR       :   if(state_jump_flag)
                                    state <= ACK_HIGH;
                                else 
                                    state <= HIGH_ADDR;
            //10                        
            ACK_HIGH        :   if(ack_jump)
                                    state <= LOW_ADDR;
                                else 
                                    state <= ACK_HIGH;
            //20                        
            LOW_ADDR        :   if(state_jump_flag)
                                    state <= ACK_LOW;
                                else 
                                    state <= LOW_ADDR;
            //40                        
            ACK_LOW         :   if(ack_jump)begin
                                    if(key_wr_state)
                                        state <= WR_DATA;
                                    else if(key_rd_state) 
                                        state <= RD_START;
                                end
                                else 
                                    state <= ACK_LOW;
            //                  
            WR_DATA         :   if(state_jump_flag)
                                    state <= ACK_WR;
                                else 
                                    state <= WR_DATA;
            //                  
            ACK_WR          :   if(ack_jump)
                                    state <= STOP;
                                else 
                                    state <= ACK_WR;

            //---------------------rd_state--------------------------                   
            RD_START        :   if(shift_flag && eeprom_scl == 0 && cnt_clk == 'd63)
                                    state <= RD_CTRL_BYTE;
                                else
                                    state <= RD_START;

            RD_CTRL_BYTE    :   if(state_jump_flag)
                                    state <= ACK_RD_CTRL;
                                else 
                                    state <= RD_CTRL_BYTE;

            ACK_RD_CTRL     :   if(ack_jump)
                                    state <= RD_DATA;
                                else 
                                    state <= ACK_RD_CTRL;

            RD_DATA         :   if(state_jump_flag)
                                    state <= NO_ACK;
                                else 
                                    state <= RD_DATA;

            NO_ACK          :   if(ack_jump)
                                    state <= STOP;
                                else
                                    state <= NO_ACK;

            //-------------------wr_rd_stop------------------------------
            STOP            :   if(eeprom_scl && cnt_clk == 'd124)
                                    state <= WR_START;
                                else 
                                    state <= STOP;

            default :   state <= WR_START;
        endcase     
    end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100

读数据串行数据转换

//-----------------RD_DATA--------------------------    
    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        DATA_r <= 8'd0;
    else if(state == RD_DATA)begin
        case(shift_cnt)
            4'd0    :   DATA_r[7] <= eeprom_sda;
            4'd1    :   DATA_r[6] <= eeprom_sda;
            4'd2    :   DATA_r[5] <= eeprom_sda;
            4'd3    :   DATA_r[4] <= eeprom_sda;
            4'd4    :   DATA_r[3] <= eeprom_sda;
            4'd5    :   DATA_r[2] <= eeprom_sda;
            4'd6    :   DATA_r[1] <= eeprom_sda;
            4'd7    :   DATA_r[0] <= eeprom_sda;
            default :   DATA_r <= 8'd0;
        endcase     
    end

    always @(posedge clk or negedge rst_n)
    if(!rst_n)
        data <= 8'd0;
    else if(shift_cnt == 'd7 && eeprom_scl == 1'b0 && cnt_clk == 'd63)
        data <= DATA_r;
    else 
        data <= data;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

整个控制模块基本完成了
下面看一下仿真
这里写图片描述
这里写图片描述

这里用singtap II 抓取 了波形
这里写图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/461733
推荐阅读
相关标签
  

闽ICP备14008679号