基于verilog的EEPROM读写

来源:互联网 发布:c语言初始化顺序表 编辑:程序博客网 时间:2024/05/16 15:21

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

这里以一个byte的读写为例

时序图:

写时序
这里写图片描述

读时序
这里写图片描述

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

控制命令和状态的定义

//instruparameter             WR_CTRL_DATA=   8'hA0,            HIGH_DATA   =   8'h00,            LOW_DATA    =   8'h05,            DATA_WR     =   8'hAf,            RD_CTRL_DATA=   8'hA1;    //stateparameter         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        :    endcaseend    //-----------------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 抓取 了波形
这里写图片描述

0 0
原创粉丝点击