基于verilog的EEPROM读写

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;

用两个按键控制写和读

//----------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;

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;

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

//-------------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;

控制字命令的

//此处建议使用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;

移位计数器:因为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;

当写控制完成后产生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;

写状态完成和读状态完成后都会产生一个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;

因为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;

状态机

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

读数据串行数据转换

//-----------------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;

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

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

  • 10
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值