基于verilog的EEPROM读写

原创 2016年08月28日 22:52:37

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 抓取 了波形
这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

EEPROM的verilog HDL程序

抄了一下《Verilog数字系统设计教程》模拟MCU通过IIC随机读写EEPROM的程序。 程序说明: 本程序用于模拟IIC,对EEPROM采取字节写和字节读的方式进行操作。 协议内容: 1.只有在...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

verilog 实现 IIC

verilog 实现IIC协议 算是一个简单的IP核,本来是挂在AXI总线上,可以通过microblaze对其进行配置。最近在弄ADV7511,用到IIC来配置它.直接上代码: `ti...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Verilog iic

收获: 1.cstate状态机一旦作为其他过程块的输入,就会被综合掉。 2.signaltap 中状态机会跑飞,网上有人说是clk时钟不稳定造成的。 3.发现写的程序正确,数据可以用单片机读取;读的程...

EEPROM读写控制器学习小结

花了近两三个星期,从夏宇闻的书中

14.6 EEPROM的页写入

在向 EEPROM 连续写入多个字节的数据时,如果每写一个字节都要等待几 ms 的话,整体上的写入效率就太低了。因此 EEPROM 的厂商就想了一个办法,把 EEPROM 分页管理。24C01、24C...
  • softn
  • softn
  • 2016年07月08日 06:23
  • 620

Xilinx FPGA的配置

http://blog.sina.com.cn/s/blog_98d98c7f0102v4ex.html http://www.cnblogs.com/aikimi7/p/3499633.html ...

Arduino - 使用EEPROM保存数据

EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器–一种掉电后数据不丢失的存储芯片。 arduino已经...
  • sdlgq
  • sdlgq
  • 2016年07月17日 13:34
  • 3810

eeprom读写

#define EEP_MAX_PAGE_SIZE 32 // 最大页写字节数 #define EEP_MAX_ROM_SIZE 8192 // EEROM容量 #define EEP_A...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于verilog的EEPROM读写
举报原因:
原因补充:

(最多只允许输入30个字)