十八、EEPROM读写(IIC)

1、iic_port.v

/*
*
*@Author: X-Z
*@Date:2023-02-23 17:13:03
*@Function:实现iic接口功能
*/
`include "param.v"

module  iic_port(
    input       wire               clk      ,
    input       wire               rst_n    ,
    input       wire     [3:0]     cmd      ,//要输入的命令
    input       wire               req      ,//表示输入的命令和数据有效
    input       wire     [7:0]     wrdin    ,//要写入的数据/命令

    inout                          sda      ,//串行数据端口
 
    output      reg                scl      ,//串行时钟线
    output      reg      [7:0]     rdout    ,//读出的数据
    output      reg                rdout_vld,//读出的数据有效
    output      wire               ack      ,//从机反馈的应答信号/非应答信号
    output      wire               rw_done   //读写一字节结束
);

//sda中间变量定义
    reg          sda_out    ;
    reg          sda_out_en ;
    wire         sda_in     ;

//sda
    assign  sda_in  = sda;
    assign  sda     = sda_out_en ? sda_out : 1'bz;

//用独热码对状态编码
localparam  IDLE        = 7'b0000_001,//空闲状态
            START       = 7'b0000_010,//开始状态
            WR_DATA     = 7'b0000_100,//写数据
            CHECK_ACK   = 7'b0001_000,//检测应答状态;
            RD_DATA     = 7'b0010_000,//读数据状态,读取一字节/多字节数据
            SEND_ACK    = 7'b0100_000,//主机发送应答或非应答
            STOP        = 7'b1000_000;//停止状态

//定义状态
    reg  [6:0]  state_c;
    reg  [6:0]  state_n;

//状态转移条件定义
    wire    idle_2_start        ;
    wire    idle_2_wr_data      ;
    wire    idle_2_rd_data      ;
    wire    start_2_wr_data     ;
    wire    start_2_rd_data     ;
    wire    wr_data_2_check_ack ;
    wire    check_ack_2_stop    ;
    wire    check_ack_2_idle    ;
    wire    rd_data_2_send_ack  ;
    wire    send_ack_2_idle     ;
    wire    send_ack_2_stop     ;
    wire    stop_2_idle         ;

//scl时钟计数器定义(产生200KHZ的时钟)
    parameter           TIME_SCL  = 8'd250 ,
                        SCL_HALF  = 8'd125 ,
                        LOW_HALF  = 8'd65  ,
                        HIGH_HALF = 8'd190 ;
    reg     [7:0]       cnt_scl            ;
    wire                add_cnt_scl        ;
    wire                end_cnt_scl        ;

//比特计数器定义
    reg     [2:0]       cnt_bit           ;
    wire                add_cnt_bit       ;
    wire                end_cnt_bit       ;
    
    reg     [3:0]       cmd_r             ;//寄存命令     
    reg     [7:0]       wrdin_r           ;//寄存要写入的数据

//cmd_r、wrdin_r
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cmd_r   <= 4'b0000;
            wrdin_r <= 8'd0;
        end
        else if(req)begin
            cmd_r   <= cmd;
            wrdin_r <= wrdin;
        end
    end

    reg     [7:0]        dout_r    ;//寄存读出的中间数据,实现串转并

    reg                  samp_ack  ;//采样ack应答信号

//三段式状态机
    //第一段
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE;
        end
        else begin
            state_c <= state_n;
        end
    end

    //第二段
    always @(*)begin
        case(state_c)
            IDLE : begin
                if(idle_2_start)begin
                    state_n = START;
                end
                else if(idle_2_wr_data)begin
                    state_n = WR_DATA;
                end
                else if(idle_2_rd_data)begin
                    state_n = RD_DATA;
                end
                else begin
                    state_n = state_c;
                end
            end
            START : begin
                if(start_2_wr_data)begin
                    state_n = WR_DATA;
                end
                else if(start_2_rd_data)begin
                    state_n = RD_DATA;
                end
                else begin
                    state_n = state_c;
                end
            end
            WR_DATA : begin
                if(wr_data_2_check_ack)begin
                    state_n = CHECK_ACK;
                end
                else begin
                    state_n = state_c;
                end
            end
            CHECK_ACK : begin
                if(check_ack_2_stop)begin
                    state_n = STOP;
                end
                else if(check_ack_2_idle)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end
            RD_DATA : begin
                if(rd_data_2_send_ack)begin
                    state_n = SEND_ACK;
                end
                else begin
                    state_n = state_c;
                end
            end
            SEND_ACK : begin
                if(send_ack_2_idle)begin
                    state_n = IDLE;
                end
                else if(send_ack_2_stop)begin
                    state_n = STOP;
                end
                else begin
                    state_n = state_c;
                end
            end
            STOP : begin
                if(stop_2_idle)begin
                    state_n = IDLE;
                end
                else begin
                    state_n = state_c;
                end
            end
            default : state_n = IDLE;
        endcase
    end

//状态转移条件赋值
    assign idle_2_start        = state_c == IDLE      && (req && (cmd & `CMD_START)); 
    assign idle_2_wr_data      = state_c == IDLE      && (req && (cmd & `CMD_WRITE)); 
    assign idle_2_rd_data      = state_c == IDLE      && (req && (cmd & `CMD_READ )); 
    assign start_2_wr_data     = state_c == START     && ((cmd_r & `CMD_WRITE) && end_cnt_bit); 
    assign start_2_rd_data     = state_c == START     && ((cmd_r & `CMD_READ ) && end_cnt_bit); 
    assign wr_data_2_check_ack = state_c == WR_DATA   && (end_cnt_bit && end_cnt_scl);     
    assign check_ack_2_stop    = state_c == CHECK_ACK && (((cmd_r & `CMD_STOP) || samp_ack) && end_cnt_bit );//发送的命令中有停止位或者从机没有应答        
    assign check_ack_2_idle    = state_c == CHECK_ACK && (((cmd_r & `CMD_STOP)==0) && end_cnt_bit);//表示发的命令中没有停止,继续写
    assign rd_data_2_send_ack  = state_c == RD_DATA   && (end_cnt_bit && cnt_scl); //读一字节数据结束    
    assign send_ack_2_idle     = state_c == SEND_ACK  && ((cmd_r & `CMD_STOP)==0 && end_cnt_bit); //表示发的命令中没有停止信号
    assign send_ack_2_stop     = state_c == SEND_ACK  && ((cmd_r & `CMD_STOP) && end_cnt_bit);//发的命令中有停止命令 
    assign stop_2_idle         = state_c == STOP      && end_cnt_scl; 

//scl时钟计数器
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt_scl <= 1'b0;
        else if(add_cnt_scl)begin
            if(end_cnt_scl)begin
                cnt_scl <= 1'b0;
            end
            else begin
                cnt_scl <= cnt_scl + 1'b1;
            end
        end 
    end

    assign add_cnt_scl = (state_c != IDLE);//除了空闲状态其他计数器均开启
    assign end_cnt_scl = add_cnt_scl && cnt_scl == TIME_SCL-1;

//比特计数器
     always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt_bit <= 1'b0;
        else if(add_cnt_bit)begin
            if(end_cnt_bit)begin
                cnt_bit <= 1'b0;
            end
            else begin
                cnt_bit <= cnt_bit + 1'b1;
            end
        end 
    end

    assign add_cnt_bit = end_cnt_scl;
    assign end_cnt_bit = add_cnt_bit && cnt_bit == ((state_c == WR_DATA || state_c == RD_DATA)?7:0);//除了发送数据/接收数据需要8bit,其他均为1bit

//scl
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            scl <= 1'b1;
        end
        else if(idle_2_start | idle_2_wr_data | idle_2_rd_data)begin
            scl <= 1'b0;
        end
        else if(add_cnt_scl && cnt_scl == SCL_HALF-1)begin//计数到一半翻转时钟电平
            scl <= 1'b1;
        end
        else if(end_cnt_scl && (~stop_2_idle))begin//只要没有到停止状态时钟就会有效运行
            scl <= 1'b0;
        end
    end

//sda_out_en
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sda_out_en <= 1'b0;
        end
        else if(idle_2_start | check_ack_2_stop | rd_data_2_send_ack | idle_2_wr_data)begin
            sda_out_en <= 1'b1;
        end
        else if(wr_data_2_check_ack | stop_2_idle | start_2_rd_data | send_ack_2_idle)begin
            sda_out_en <= 1'b0;
        end
    end

//sda_out
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sda_out <= 1'b1;
        end
        //发起始位
        else if(state_c == START)begin
            if(cnt_scl == LOW_HALF)begin
                sda_out <= 1'b1;//在时钟低电平时将其拉高,以保证开始前它处于高电平
            end
            else if(cnt_scl == HIGH_HALF)begin//满足建立时间与保持时间,建立时间与保持时间为(600ns),我们剩余的时间为(1250ns)
                sda_out <= 1'b0;//在时钟高电平期间将其拉低产生起始信号
            end
        end
        //发数据
        else if(state_c == WR_DATA)begin
            if(cnt_scl == LOW_HALF)begin
                sda_out <= wrdin_r[7-cnt_bit];//时钟低电平发送数据,将并行数据转换为串行数据发送,高位先发
            end
        end
        //发应答位
        else if(state_c == SEND_ACK)begin
            if(cnt_scl == LOW_HALF)begin
                sda_out <= ((cmd_r & `CMD_STOP) ? 1 : 0);//如果发送的命令中带停止位则发送非应答,否则发送应答
            end
        end
        //发停止位
        else if(state_c == STOP)begin
            if(cnt_scl == LOW_HALF)begin
                sda_out <= 1'b0;//先在时钟低电平将其拉低,为结束做准备
            end
            else if(cnt_scl == HIGH_HALF)begin
                sda_out <= 1'b1;//时钟高电平期间将其拉高以保证停止位有效
            end
        end
    end

//dout_r
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dout_r <= 8'd0;
        end
        else if(state_c == RD_DATA)begin
            if(cnt_scl == HIGH_HALF)begin
                dout_r[7-cnt_bit] <= sda_in;//串并转换
            end
        end
    end

//samp_ack
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            samp_ack <= 1'b1;
        end
        else if(state_c == CHECK_ACK)begin
            if(cnt_scl == HIGH_HALF)begin
                samp_ack <= sda_in;//高电平期间进行采样应答信号
            end
        end
    end

//rd_out
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rdout <= 8'd0;
        end
        else if(rd_data_2_send_ack)begin//表示一字节数据接收完成
            rdout <= dout_r;
        end
    end

//rdout_vld
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            rdout_vld <= 1'b0;
        end
        else if(rd_data_2_send_ack)begin//表示一字节数据接收完成,有效的
            rdout_vld <= 1'b1;
        end
        else begin
            rdout_vld <= 1'b0;
        end
    end

//ack
    assign ack = (state_c == CHECK_ACK && end_cnt_scl) ? samp_ack : 1'b1;//主机发送完一字节数据后,从机反馈的应答信号/非应答信号
    //一个时钟周期结束保证主机采样完
//rw_done
    assign rw_done = check_ack_2_idle | send_ack_2_idle | stop_2_idle;//发送或接收完一字节数据时都表示读写完成了一次

endmodule

2、param.v


//i2c命令参数
`define CMD_START   4'b0001
`define CMD_WRITE   4'b0010 
`define CMD_READ    4'b0100 
`define CMD_STOP    4'b1000 

//定义eeprom读写模式
//`define BYTE_WRITE     //字节写
`define PAGE_WRITE     //页写

`ifdef BYTE_WRITE
    `define WR_BYTE 3
`elsif PAGE_WRITE
    `define WR_BYTE 18
`endif

`define RANDOM_READ    //随机地址读
`define SEQU_READ      //顺序地址读

`ifdef RANDOM_READ
    `define RD_BYTE 4
`elsif SEQU_READ
    `define RD_BYTE 19
`endif 

//iic外设地址参数定义
`define    I2C_ADR 6'b1010_00  //6'b1010_00xy x:Block地址 y:读写控制位 WR_BIT/RD_BIT
`define    WR_BIT  1'b0 //bit0
`define    RD_BIT  1'b1 //bit0
`define    BLOCK_A 1'b0 //第一块block
`define    BLOCK_B 1'b1 //第二块block 

`define    STOP_BIT  1'b1
`define    START_BIT 1'b0 

3、eeprom_control.v

/*
*
*@Author: X-Z
*@Date:2023-02-24 19:16:15
*@Function:EEPROM读写控制模块的实现
*/
`timescale  1ns/1ps
`include "param.v"
module  eeprom_control(
    input       wire                clk         ,
    input       wire                rst_n       ,
    input       wire  [7:0]         rdout       ,//从iic接口模块读出的数据
    input       wire                rdout_vld   ,
    input       wire                ack         ,//从机EEPROM给的应答/非应答信号
    input       wire                rw_done     ,//通过iic模块读或写完一字节数据完成
    input       wire                wr_req      ,//写请求
    input       wire                rd_req      ,//读请求

    output      reg                 req         ,
    output      reg   [3:0]         cmd         ,//控制命令
    output      reg   [7:0]         wrdin       ,//要写入从机的数据
    output      reg   [23:0]        data        ,//输出给数码管的数据
    output      reg                 data_vld     
);

//parameter      DATA = 8'haa;//想要写入eeprom的数据

//独热码对状态进行编码
localparam      IDLE         = 6'b000_001,//空闲状态
                WR_REQ       = 6'b000_010,//写请求状态(此状态下完成req、cmd、wrdin的生成)
                WAIT_WR_DONE = 6'b000_100,//等待一字节写完成
                RD_REQ       = 6'b001_000,//读请求状态
                WAIT_RD_DONE = 6'b010_000,//等待一字节数据读完成
                DONE         = 6'b100_000;//读写完成状态

//状态参数定义
    reg [5:0]   state_c,state_n;

    wire        idle_2_wr_req        ; 
    wire        idle_2_rd_req        ; 
    wire        wr_req_2_wait_wr_done; 
    wire        wait_wr_done_2_wr_req; 
    wire        wait_wr_done_2_done  ; 
    wire        rd_req_2_wait_rd_done; 
    wire        wait_rd_done_2_rd_req; 
    wire        wait_rd_done_2_done  ; 

//计数器定义
    reg  [4:0]  cnt_byte    ;
    wire        add_cnt_byte;
    wire        end_cnt_byte;

    reg  [4:0]  BYTE_NUM    ;//寄存读写命令操作所需的字节数
    

    /**********************产生一个计数值赋值给要写的数据*********************************/
    //计数器参数定义
    parameter	 	CNT_MAX = 8'd255	;
    reg	[7:0]	 	cnt_data			;
    wire			add_cnt_data        ;
    wire			end_cnt_data        ;
    reg  [3:0]      data_3              ;//百位
    reg  [3:0]      data_2              ;//十位
    reg  [3:0]      data_1              ;//个位

    reg   [3:0]     rdout_3             ;
    reg   [3:0]     rdout_2             ;
    reg   [3:0]     rdout_1             ;

    //计数器模块
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_data <= 1'd0;//复位初始化计数器值为0
        end
        else if(add_cnt_data)begin//满足计数器开启条件
            if(end_cnt_data)begin//满足计数器结束条件
                cnt_data <= 1'd0;//计数值清零
            end
            else begin//不满足结束条件,计数器加1
                cnt_data <= cnt_data + 1'b1;
            end
        end
    end
        
    assign add_cnt_data = 1'b1;//计数器开始计数条件
    assign end_cnt_data = add_cnt_data && cnt_data == CNT_MAX;//计数器结束计数条件

    /**********************************************************************************/

//三段式状态机
    //第一段
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            state_c <= IDLE ;
        end
        else  begin
            state_c <= state_n;
        end
    end

    //第二段
    always @(*)begin
        case(state_c)
            IDLE :begin
                if(idle_2_wr_req)
                    state_n = WR_REQ;
                else if(idle_2_rd_req)
                    state_n = RD_REQ;
                else 
                    state_n = state_c;
            end
            WR_REQ :begin
                if(wr_req_2_wait_wr_done)
                    state_n = WAIT_WR_DONE;
                else 
                    state_n = state_c; 
            end
            WAIT_WR_DONE :begin
                if(wait_wr_done_2_wr_req)
                    state_n = WR_REQ;
                else if(wait_wr_done_2_done)
                    state_n = DONE;
                else 
                    state_n = state_c; 
            end
            RD_REQ :begin
                if(rd_req_2_wait_rd_done)
                    state_n = WAIT_RD_DONE;
                else 
                    state_n = state_c; 
            end
            WAIT_RD_DONE :begin
                if(wait_rd_done_2_rd_req)
                    state_n = RD_REQ;
                else if(wait_rd_done_2_done)
                    state_n = DONE;
                else 
                    state_n = state_c; 
            end
            DONE :begin//到done状态则下一个时钟周期回到IDLE
                state_n = IDLE;
            end
            default : state_n = IDLE;
        endcase
    end

    //转移条件赋值
    assign  idle_2_wr_req         = state_c == IDLE         && wr_req;   
    assign  idle_2_rd_req         = state_c == IDLE         && rd_req;   
    assign  wr_req_2_wait_wr_done = state_c == WR_REQ       && 1'b1;
    assign  wait_wr_done_2_wr_req = state_c == WAIT_WR_DONE && ((cnt_byte < `WR_BYTE-1) && ack == 1'b0 && rw_done);//3字节数据未写完,从机发送应答并且前一字节数据发送完成
    assign  wait_wr_done_2_done   = state_c == WAIT_WR_DONE && end_cnt_byte;//3字节数据传输完成   
    assign  rd_req_2_wait_rd_done = state_c == RD_REQ       && 1'b1;
    assign  wait_rd_done_2_rd_req = state_c == WAIT_RD_DONE && ((cnt_byte < `RD_BYTE-1) && ack == 1'b0 && rw_done);//4字节数据发送接收完
    assign  wait_rd_done_2_done   = state_c == WAIT_RD_DONE && end_cnt_byte;//随机读结束(写3个字节读一个字节、控制字、地址、控制字、读数据)   

//字节计数器
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt_byte <= 1'b0;
        else if(add_cnt_byte)begin
            if(end_cnt_byte)
                cnt_byte <= 1'b0;
            else 
                cnt_byte <= cnt_byte + 1'b1; 
        end 
    end

    assign add_cnt_byte = rw_done;
    assign end_cnt_byte = add_cnt_byte && cnt_byte == BYTE_NUM - 1;

//BYTE_NUM
    always @(posedge clk or negedge rst_n)begin
       if(!rst_n)begin
            BYTE_NUM <= 5'd0;
       end
       else if(wr_req)begin//字节写需要3字节,页写18字节
            BYTE_NUM <= `WR_BYTE;
       end
       else if(rd_req)begin//随机读需要4字节,连续读19字节
            BYTE_NUM <= `RD_BYTE;
       end
       else if(state_c == IDLE)begin
            BYTE_NUM <= 5'd0;
       end
    end

//输出req、cmd、wrdin
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            tx(1'b0,4'd0,8'd0);//初始化
        end
        else if(state_c == WR_REQ)begin //字节写/页写
            case(cnt_byte)
                5'd0        : tx(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,`BLOCK_A,`WR_BIT});//带起始位的写,eeprom的地址,选择第几个block,读操作还是写操作
                5'd1        : tx(1'b1,`CMD_WRITE,8'h00);//写入要写数据的地址00
                `WR_BYTE-1  : begin
                                tx(1'b1,{`CMD_WRITE | `CMD_STOP},cnt_data);//写入最后一字节数据
                                data_3 <= cnt_data /100;
                                data_2 <= (cnt_data %100)/10;
                                data_1 <= cnt_data %10;
                end
                default     :begin 
                                 tx(1'b1,`CMD_WRITE,cnt_data);//写入其他中间数据(针对页写)
                                data_3 <= cnt_data /100;
                                data_2 <= (cnt_data %100)/10;
                                data_1 <= cnt_data %10;           
                end
            endcase
        end
        else if(state_c == RD_REQ)begin//随机读
            case(cnt_byte)
                5'd0        : tx(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,`BLOCK_A,`WR_BIT});
                5'd1        : tx(1'b1,`CMD_WRITE,8'h00);//写入想要要读数据的地址00
                5'd2        : tx(1'b1,{`CMD_START | `CMD_WRITE},{`I2C_ADR,`BLOCK_A,`RD_BIT});//发送控制命令此时指示下一步要进行读操作
                `RD_BYTE-1  :begin
                                tx(1'b1,{`CMD_READ | `CMD_STOP},8'h00);//带停止位的读读最后一字节数据
                                rdout_3 <= rdout /100;
                                rdout_2 <= (rdout %100)/10;
                                rdout_1 <= rdout %10;
                end
                default     :begin tx(1'b1,`CMD_READ,8'd0);//其他情况读中间数据不要起始位也不要停止位
                                rdout_3 <= rdout /100;
                                rdout_2 <= (rdout %100)/10;
                                rdout_1 <= rdout %10;
                end
            endcase
        end
        else begin
            tx(1'b0,cmd,wrdin);//其他情况请求信号拉低命令与数据保持
        end 
    end

//用task发送请求、命令、数据(地址+数据)
    task tx(
        input           request,
        input    [3:0]  command,
        input    [7:0]  data   
    );
        begin
            req   = request;
            cmd   = command;
            wrdin = data   ;
        end
    endtask

//data
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin//上电复位时数码管熄灭
            data <= 24'hffffff;
        end
        else if(wait_wr_done_2_done)begin
            data <= {4'd0,8'hff,data_3,data_2,data_1};//0代表写操作,中间3个数码管不亮,最后两个显示数据
        end
        else if(rdout_vld)begin//读出的数据有效
        //    data <= {4'd1,12'hfff,rdout[7:4],rdout[3:0]};
              data <= {4'd1,8'hff,rdout_3,rdout_2,rdout_1};
        end
    end

//data_vld
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            data_vld <= 1'b0;
        end
        else if(wait_wr_done_2_done || rdout_vld)begin
            data_vld <= 1'b1;
        end
        else begin
            data_vld <= 1'b0; 
        end
    end

endmodule

4、fsm_key_filter_more.v

//原来代码有bug,现在改了

module fsm_key_filter_more #(parameter KEY_W = 4 ,TIME_20MS = 20'd999_999)(
    input   wire                    clk   ,
    input   wire                    rst_n ,
    input   wire [KEY_W-1:0]        key   ,

    output  reg  [KEY_W-1:0]        key_flag
);

//定义状态变量独热码编码
localparam IDLE         = 4'b0001,//表示空闲状态
           FILETER_DOWN = 4'b0010,//按键按下前抖动状态
           HOLD_DOWN    = 4'b0100,//按键稳定时的状态
           FILETER_UP   = 4'b1000;//按键松手后,后抖动状态

//定义状态
reg [3:0] state_c ;
reg [3:0] state_n ;

//定义状态转移条件
wire [3:0]  idle_2_F_D;
wire [3:0]  F_D_2_H_D ;
wire [3:0]  F_D_2_idle;
wire [3:0]  H_D_2_F_U ;
wire [3:0]  F_U_2_idle;
wire [3:0]  F_U_2_H_D ;

//计数器模块定义
reg [19:0] cnt_20ms ;
wire       add_cnt  ;
wire       end_cnt  ;

//寄存按键信号同步打拍,检测下降沿与上升沿
reg  [KEY_W-1:0]      key_r0   ;//同步信号寄存
reg  [KEY_W-1:0]      key_r1   ;//打拍信号
wire [KEY_W-1:0]      nedge    ;//下降沿信号
wire [KEY_W-1:0]      pedge    ;//上升沿信号


integer               i        ;//循环变量

//状态机设计三段式时序逻辑描述状态转移
//第一段
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        state_c <= IDLE ;
    end
    else begin
        state_c <= state_n;
    end
end

//第二段,组合逻辑,更据状态转移图描述状态转移条件
always@(*)begin
    case(state_c)
        IDLE : begin
            if(idle_2_F_D)begin//条件成立状态转移
                state_n = FILETER_DOWN;
            end
            else begin
                state_n = IDLE ;
            end
        end
        FILETER_DOWN :begin
            if(F_D_2_idle)begin
                state_n = IDLE;
            end
            else if(F_D_2_H_D)begin
                state_n = HOLD_DOWN;
            end
            else begin
                state_n = FILETER_DOWN;
            end
        end
        HOLD_DOWN : begin
            if(H_D_2_F_U)begin
                state_n = FILETER_UP ;
            end
            else begin
                state_n = HOLD_DOWN ;
            end
        end
        FILETER_UP : begin
             if(F_U_2_H_D)begin
                state_n = HOLD_DOWN ;
            end
            else if(F_U_2_idle)begin
                state_n = IDLE ;
            end
            else begin
                state_n = FILETER_UP ;
            end
        end
        default : state_n = IDLE ;
    endcase
end

assign idle_2_F_D = (state_c == IDLE        ) && nedge  ;
assign F_D_2_idle = (state_c == FILETER_DOWN) && pedge  ;
assign F_D_2_H_D  = (state_c == FILETER_DOWN) && end_cnt;
assign H_D_2_F_U  = (state_c == HOLD_DOWN   ) && pedge  ;
assign F_U_2_idle = (state_c == FILETER_UP  ) && end_cnt;
assign F_U_2_H_D  = (state_c == FILETER_UP  ) && nedge  ;   

//第三段,描述输出
always@(posedge clk or negedge rst_n)begin
   if(!rst_n)begin
        // for(i=0; i < KEY_W; i = i+1)begin //通过for循环赋初值
        //     key_flag[i] <= 1'b0;  
        // end  
        key_flag <= {KEY_W{1'b0}};
   end 
   else if(F_D_2_H_D)begin
     key_flag <= ~key_r1;//key_r1更稳定一点,打拍后的信号,当然key也可以
   end
   else begin
        // for(i=0; i < KEY_W; i = i+1)begin 
        //     key_flag[i] <= 1'b0;  
        // end
        key_flag <= {KEY_W{1'b0}};
   end
end

//20ms计数器模块
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 20'd0;
    end
    else if(add_cnt)begin
        if(end_cnt)begin
            cnt_20ms <= 20'd0;
        end
        else begin
            cnt_20ms <= cnt_20ms + 1'd1;
        end
    end
    else begin
        cnt_20ms <= 20'd0;/*888888888888*/
    end
end

assign add_cnt = (state_c == FILETER_DOWN) || (state_c == FILETER_UP);
assign end_cnt = (add_cnt && cnt_20ms == TIME_20MS ); //|| F_D_2_idle || F_U_2_H_D;
//end_cnt ==end_cnt = (add_cnt && cnt_20ms == TIME_20MS )|| F_D_2_idle || F_U_2_H_D;会产生bug,
//从0010-->0001时会使end_cnt满足条件,这样会使得F_D_2_H_D满足从而给输出标志信号置位,事实上此时还处于抖动状态

//延时打拍按键信号
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)begin //也可以key_r0 <= {KEY_W{1'b1}};
        // for(i = 0; i < KEY_W; i = i + 1)begin
        //     key_r0[i] <= 1'b1;
        //     key_r1[i] <= 1'b1;
        // end
        key_r0 <= {KEY_W{1'b1}};
        key_r1 <= {KEY_W{1'b1}};
    end
    else begin
        key_r0 <= key ;//同步
        key_r1 <= key_r0;//打拍
    end
end

//nedge pedge
assign nedge = key_r1 & (~key_r0);//检测下降沿
assign pedge = (~key_r1) & key_r0;//检测上升沿

endmodule

5、dynamic_dig.v

/*
*
*@Author: X-Z
*@Date:2023-02-08 18:46:20
*@Function:数码管驱动
*/

module dynamic_dig (
    input       wire        clk     ,
    input       wire        rst_n   ,
    input       wire [23:0] data    ,//输入24位宽的数据

    output      reg  [5:0]   sel    ,//位选
    output      reg  [7:0]   seg     //段选
);

    //参数定义共阳极
    parameter TIME_1MS = 49_999,
    //1ms扫描一个数码管
              ZERO     = 7'b100_0000,
              ONE      = 7'b111_1001,
              TWO      = 7'b010_0100,
              THREE    = 7'b011_0000,
              FOUR     = 7'b001_1001,
              FIVE     = 7'b001_0010,
              SIX      = 7'b000_0010,
              SEVEN    = 7'b111_1000,
              EIGHT    = 7'b000_0000,
              NINE     = 7'b001_0000,
              A        = 7'b000_1000,
              B        = 7'b000_0011,
              C        = 7'b010_0011,
              D        = 7'b110_0011,
              E        = 7'b01_01011,
              F        = 7'b111_1111;//全灭,4'hf  : seg = 8'b1000_1110;

//信号定义
    reg   [23:0]     cnt_1ms   ;
    //扫描频率计数器
    wire             add_cnt_1ms;
    wire             end_cnt_1ms;

    reg   [3:0]      disp_num   ;//暂存数码管要显示的数值

    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            cnt_1ms <= 1'b0;
        else if(add_cnt_1ms)begin
            if(end_cnt_1ms)
                cnt_1ms <= 1'b0;
            else 
                cnt_1ms <= cnt_1ms + 1'b1;
        end
    end 

    assign add_cnt_1ms = 1'b1;
    assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == TIME_1MS;

    //数码管位选信号
    //时序电路
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            sel <= 6'b111_110;//复位初始化
        end
        else if(end_cnt_1ms)begin
            sel <= {sel[4:0],sel[5]};
        end
    end

    //每片数码管将要显示的数值
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            disp_num <= 4'd0;
        end
        else begin
            case(sel)
                6'b111_110: disp_num <= data[23:20];
                6'b111_101: disp_num <= data[19:16];
                6'b111_011: disp_num <= data[15:12];
                6'b110_111: disp_num <= data[11:8] ;
                6'b101_111: disp_num <= data[7:4]  ;
                6'b011_111: disp_num <= data[3:0]  ;
                default   : disp_num <= 4'd0       ;
            endcase
        end
    end

    //seg 段选信号译码
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            seg <= {1'b0,ZERO};//复位数码管显示字符
        end
        else begin//不需要显示小数点
            case(disp_num)
                0 :seg <= {1'b1,ZERO  }   ;
                1 :seg <= {1'b1,ONE   }   ;
                2 :seg <= {1'b1,TWO   }   ;
                3 :seg <= {1'b1,THREE }   ;
                4 :seg <= {1'b1,FOUR  }   ;
                5 :seg <= {1'b1,FIVE  }   ;
                6 :seg <= {1'b1,SIX   }   ;
                7 :seg <= {1'b1,SEVEN }   ;
                8 :seg <= {1'b1,EIGHT }   ;
                9 :seg <= {1'b1,NINE  }   ; 
                10:seg <= {1'b1, A    }   ;
                11:seg <= {1'b1, B    }   ;
                12:seg <= {1'b1, C    }   ;
                13:seg <= {1'b1, D    }   ;
                14:seg <= {1'b1, E    }   ;
                15:seg <= {1'b1, F    }   ;
                //全灭
                default :seg <= {1'b1,F}  ;
            endcase
        end
    end    
endmodule

6、eeprom_top.v

/*
*
*@Author: X-Z
*@Date:2023-02-23 16:50:52
*@Function:eeprom实现读写顶层设计例化
*/

module eeprom_top (
    input       wire                  clk    ,
    input       wire                  rst_n  ,
    input       wire    [1:0]         key_in ,

    inout                             sda    , //串行数据线

    output      wire                  scl    , //串行时钟线
    output      wire    [5:0]         sel    ,
    output      wire    [7:0]         seg    
);

//中间变量定义
    wire    [1:0]       key_flag    ;
    wire    [3:0]       cmd         ;
    wire                req         ;
    wire    [7:0]       wrdin       ;
    wire    [7:0]       rdout       ;
    wire                rdout_vld   ;
    wire                ack         ;
    wire                rw_done     ;
    wire    [23:0]      data        ;
    wire                data_vld    ;
    wire                wr_req      ;
    wire                rd_req      ;

//wr_req、rd_req
    assign {rd_req,wr_req} = key_flag;//写是key0读是key1

    //按键消抖模块例化
    fsm_key_filter_more #(.KEY_W(2) ,.TIME_20MS(20'd999_999))
    u_fsm_key_filter
    (
        /*input   wire                    */ .clk       (clk       ),
        /*input   wire                    */ .rst_n     (rst_n     ),
        /*input   wire [KEY_W-1:0]        */ .key       (key_in    ),

        /*output  reg  [KEY_W-1:0]        */ .key_flag  (key_flag  )    
    );

    //iic接口模块例化
    iic_port    u_iic_port(
        /*input       wire              */   .clk       (clk       ),
        /*input       wire              */   .rst_n     (rst_n     ),
        /*input       wire     [3:0]    */   .cmd       (cmd       ),//要输入的命令
        /*input       wire              */   .req       (req       ),//表示输入的命令和数据有效
        /*input       wire     [7:0]    */   .wrdin     (wrdin     ),//要写入的数据/命令

        /*inout                         */   .sda       (sda       ),//串行数据端口

        /*output      reg               */   .scl       (scl       ),//串行时钟线
        /*output      reg      [7:0]    */   .rdout     (rdout     ),//读出的数据
        /*output      reg               */   .rdout_vld (rdout_vld ),//读出的数据有效
        /*output      wire              */   .ack       (ack       ),//从机反馈的应答信号/非应答信号
        /*output      wire              */   .rw_done   (rw_done   ) //读写一字节结束
    );


    //eeprom读写控制模块例化
    eeprom_control  u_eeprom_control(
    /*input       wire              */  .clk        (clk        ) ,
    /*input       wire              */  .rst_n      (rst_n      ) ,
    /*input       wire  [7:0]       */  .rdout      (rdout      ) ,//从iic接口模块读出的数据
    /*input       wire              */  .rdout_vld  (rdout_vld  ) ,
    /*input       wire              */  .ack        (ack        ) ,//从机EEPROM给的应答/非应答信号
    /*input       wire              */  .rw_done    (rw_done    ) ,//通过iic模块读或写完一字节数据完成
    /*input       wire              */  .wr_req     (wr_req     ) ,//写请求wr_req
    /*input       wire              */  .rd_req     (rd_req     ) ,//读请求

    /*output      reg               */  .req        (req        ) ,
    /*output      reg   [3:0]       */  .cmd        (cmd        ) ,//控制命令
    /*output      reg   [7:0]       */  .wrdin      (wrdin      ) ,//要写入从机的数据
    /*output      reg   [23:0]      */  .data       (data       ) ,//输出给数码管的数据
    /*output      reg               */  .data_vld   (data_vld   )  
    );
    //数码管驱动模块例化
    dynamic_dig u_dynamic_dig(
    /*input       wire       */ .clk     (clk     ),
    /*input       wire       */ .rst_n   (rst_n   ),
    /*input       wire [23:0]*/ .data    (data    ),//输入24位宽的数据

    /*output      reg  [5:0] */ .sel     (sel     ),//位选
    /*output      reg  [7:0] */ .seg     (seg     )//段选
    );
    
endmodule

7、eeprom_top_tb.v

`timescale 1ns/1ps
module eeprom_top_tb();

parameter   CYCLE = 20;

    reg             clk         ;
    reg             rst_n       ;
    reg     [1:0]   key_in      ;
    wire            scl         ;       
    wire            sda         ;
    wire    [5:0]   sel         ;
    wire    [7:0]   seg         ;
    supply0         VSS         ;//对地建模

/*************cmd_start=4'b0001
              cmd_write=4'b0010
              cmd_read =4'b0100
              cmd_stop =4'b1000  ****************/      
                                
    initial begin
        clk    = 1'b1   ;
        rst_n  = 1'b0   ;
        key_in = 2'b00  ;
        #(20*CYCLE);
        rst_n  = 1'b1   ;
        #(40*CYCLE)
        key_in = 2'b01;//写请求有效
        #CYCLE
        key_in = 2'b00;
        #5000000
        #(CYCLE*30*300)
        key_in = 2'b10;
        #CYCLE 
        key_in = 2'b00;
        #(CYCLE*40*300)
        $stop;
    end   

    always #(CYCLE/2) clk = ~clk;//50MHz

//模块例化
eeprom_top  u_eeprom_top(
    /*input       wire               */   .clk    (clk    ),
    /*input       wire               */   .rst_n  (rst_n  ),
    /*input       wire    [1:0]      */   .key_in (key_in ),

    /*inout                          */   .sda    (sda    ), //串行数据线

    /*output      wire               */   .scl    (scl    ), //串行时钟线
    /*output      wire    [5:0]      */   .sel    (sel    ),
    /*output      wire    [7:0]      */   .seg    (seg    )
);

//例化eeprom模型
M24LC04B  u_24lc04b(
    .A0     (VSS  ), 
    .A1     (VSS  ), 
    .A2     (VSS  ),
    .WP     (VSS  ), 
    .SDA    (scl  ),
    .SCL    (sda  ),
    .RESET  (rst_n)
);

endmodule

8、eeprom.do

vlib work
vmap work work
                                            
#编译testbench文件					       	
vlog    eeprom_top_tb.v
vlog    24LC04B.v
#编译 	设计文件					       	 
#vlog ../test/iic_port.v
vlog ../src/*.v
                                                
#指定仿真顶层 							     
vsim -novopt work.eeprom_top_tb 
#添加信号到波形窗 							  
add wave -position insertpoint sim:/eeprom_top_tb//*

run 20000000

9、M24LC04B.v

// *******************************************************************************************************
// **                                                                           			**
// **   24LC04B.v - Microchip 24LC04B 4K-BIT I2C SERIAL EEPROM (VCC = +2.5V TO +5.5V)			**
// **                                                                           			**
// *******************************************************************************************************
// **                                                                           			**
// **			This information is distributed under license from Young Engineering.		**
// **                              COPYRIGHT (c) 2003 YOUNG ENGINEERING              			**
// **                                      ALL RIGHTS RESERVED                         			**
// **                                                                           			**
// **                                                                                                   **
// **   Young Engineering provides design expertise for the digital world                               **
// **   Started in 1990, Young Engineering offers products and services for your electronic design      **
// **   project.  We have the expertise in PCB, FPGA, ASIC, firmware, and software design.              **
// **   From concept to prototype to production, we can help you.                                       **
// **													**
// **	http://www.young-engineering.com/								**
// **													**
// *******************************************************************************************************
// **	This information is provided to you for your convenience and use with Microchip products only.  **
// **	Microchip disclaims all liability arising from this information and its use.  			**
// **													**
// **	THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF 	**
// **	ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO 	**
// **	THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, 		**
// **	PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE.  			**
// **	MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL 	**
// **	DAMAGES, FOR ANY REASON WHATSOEVER.								**
// **													**
// **	It is your responsibility to ensure that your application meets with your specifications.	**
// **													**
// *******************************************************************************************************
// **   Revision       : 1.3                                                    			**
// **   Modified Date  : 12/04/2006	                                            			**
// **   Revision History:                                                       			**
// **                                                                           			**
// **   02/01/2003:  Initial design                                             			**
// **   07/19/2004:  Fixed the timing checks and the open-drain modeling for SDA.			**
// **   01/06/2006:  Changed the legal information in the header					**
// **   12/04/2006:  Corrected timing checks to reference proper clock edges				**
// **                Added timing check for Tbuf (bus free time)					**
// **                                                                           			**
// *******************************************************************************************************
// **                                       TABLE OF CONTENTS                          			**
// *******************************************************************************************************
// **---------------------------------------------------------------------------------------------------**
// **   DECLARATIONS                                                          				**
// **---------------------------------------------------------------------------------------------------**
// **---------------------------------------------------------------------------------------------------**
// **   INITIALIZATION                                              					**
// **---------------------------------------------------------------------------------------------------**
// **---------------------------------------------------------------------------------------------------**
// **   CORE LOGIC                                                  					**
// **---------------------------------------------------------------------------------------------------**
// **   1.01:  START Bit Detection									**
// **   1.02:  STOP Bit Detection									**
// **   1.03:  Input Shift Register									**
// **   1.04:  Input Bit Counter									**
// **   1.05:  Control Byte Register									**
// **   1.06:  Byte Address Register									**
// **   1.07:  Write Data Buffer									**
// **   1.08:  Acknowledge Generator									**
// **   1.09:  Acknowledge Detect									**
// **   1.10:  Write Cycle Timer									**
// **   1.11:  Write Cycle Processor									**
// **   1.12:  Read Data Multiplexor									**
// **   1.13:  Read Data Processor									**
// **   1.14:  SDA Data I/O Buffer									**
// **                                                                           			**
// **---------------------------------------------------------------------------------------------------**
// **   DEBUG LOGIC                                                  					**
// **---------------------------------------------------------------------------------------------------**
// **   2.01:  Memory Data Bytes									**
// **   2.02:  Write Data Buffer									**
// **                                                                           			**
// **---------------------------------------------------------------------------------------------------**
// **   TIMING CHECKS                                                     				**
// **---------------------------------------------------------------------------------------------------**
// **                                                                           			**
// *******************************************************************************************************


`timescale 1ns/10ps

module M24LC04B (A0, A1, A2, WP, SDA, SCL, RESET);

   input 		A0;				// unconnected pin
   input 		A1;				// unconnected pin
   input 		A2;				// unconnected pin

   input		WP;				// write protect pin

   inout		SDA;				// serial data I/O
   input		SCL;				// serial data clock

   input		RESET;				// system reset


// *******************************************************************************************************
// **   DECLARATIONS                                                            			**
// *******************************************************************************************************

   reg			SDA_DO;				// serial data - output
   reg			SDA_OE;				// serial data - output enable

   wire			SDA_DriveEnable;		// serial data output enable
   reg			SDA_DriveEnableDlyd;		// serial data output enable - delayed

   reg	[03:00]		BitCounter;			// serial bit counter

   reg			START_Rcvd;			// START bit received flag
   reg			STOP_Rcvd;			// STOP bit received flag
   reg			CTRL_Rcvd;			// control byte received flag
   reg			ADDR_Rcvd;			// byte address received flag
   reg			MACK_Rcvd;			// master acknowledge received flag

   reg			WrCycle;			// memory write cycle
   reg			RdCycle;			// memory read cycle

   reg	[07:00]		ShiftRegister;			// input data shift register

   reg  [07:00]		ControlByte;			// control byte register
   wire			BlockSelect;			// memory block select
   wire			RdWrBit;			// read/write control bit

   reg	[08:00]		StartAddress;			// memory access starting address
   reg	[03:00]		PageAddress;			// memory page address

   reg	[07:00]		WrDataByte [0:15];		// memory write data buffer
   wire	[07:00]		RdDataByte;			// memory read data

   reg	[15:00]		WrCounter;			// write buffer counter

   reg	[03:00]		WrPointer;			// write buffer pointer
   reg	[08:00]		RdPointer;			// read address pointer

   reg			WriteActive;			// memory write cycle active

   reg	[07:00]		MemoryBlock0 [0:255];		// EEPROM data memory array
   reg	[07:00]		MemoryBlock1 [0:255];		// EEPROM data memory array

   integer		LoopIndex;			// iterative loop index

   integer 		tAA;				// timing parameter
   integer 		tWC;				// timing parameter


// *******************************************************************************************************
// **   INITIALIZATION                                                         				**
// *******************************************************************************************************

   initial tAA = 900;					// SCL to SDA output delay
   initial tWC = 5000000;				// memory write cycle time

   initial begin
      SDA_DO = 0;
      SDA_OE = 0;
   end

   initial begin
      START_Rcvd = 0;
      STOP_Rcvd  = 0;
      CTRL_Rcvd  = 0;
      ADDR_Rcvd  = 0;
      MACK_Rcvd  = 0;
   end

   initial begin
      BitCounter  = 0;
      ControlByte = 0;
   end

   initial begin
      WrCycle = 0;
      RdCycle = 0;

      WriteActive = 0;
   end


// *******************************************************************************************************
// **   CORE LOGIC                                                    					**
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
//      1.01:  START Bit Detection
// -------------------------------------------------------------------------------------------------------

   always @(negedge SDA) begin
      if (SCL == 1) begin
         START_Rcvd <= 1;
         STOP_Rcvd  <= 0;
         CTRL_Rcvd  <= 0;
         ADDR_Rcvd  <= 0;
         MACK_Rcvd  <= 0;

         WrCycle <= #1 0;
         RdCycle <= #1 0;

         BitCounter <= 0;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.02:  STOP Bit Detection
// -------------------------------------------------------------------------------------------------------

   always @(posedge SDA) begin
      if (SCL == 1) begin
         START_Rcvd <= 0;
         STOP_Rcvd  <= 1;
         CTRL_Rcvd  <= 0;
         ADDR_Rcvd  <= 0;
         MACK_Rcvd  <= 0;

         WrCycle <= #1 0;
         RdCycle <= #1 0;

         BitCounter <= 10;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.03:  Input Shift Register
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      ShiftRegister[00] <= SDA;
      ShiftRegister[01] <= ShiftRegister[00];
      ShiftRegister[02] <= ShiftRegister[01];
      ShiftRegister[03] <= ShiftRegister[02];
      ShiftRegister[04] <= ShiftRegister[03];
      ShiftRegister[05] <= ShiftRegister[04];
      ShiftRegister[06] <= ShiftRegister[05];
      ShiftRegister[07] <= ShiftRegister[06];
   end

// -------------------------------------------------------------------------------------------------------
//      1.04:  Input Bit Counter
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      if (BitCounter < 10) BitCounter <= BitCounter + 1;
   end

// -------------------------------------------------------------------------------------------------------
//      1.05:  Control Byte Register
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (START_Rcvd & (BitCounter == 8)) begin
         if (!WriteActive & (ShiftRegister[07:04] == 4'b1010)) begin
            if (ShiftRegister[00] == 0) WrCycle <= 1;
            if (ShiftRegister[00] == 1) RdCycle <= 1;

            ControlByte <= ShiftRegister[07:00];

            CTRL_Rcvd <= 1;
         end

         START_Rcvd <= 0;
      end
   end

   assign BlockSelect = ControlByte[01];
   assign RdWrBit     = ControlByte[00];

// -------------------------------------------------------------------------------------------------------
//      1.06:  Byte Address Register
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (CTRL_Rcvd & (BitCounter == 8)) begin
         if (RdWrBit == 0) begin
            StartAddress <= {BlockSelect,ShiftRegister[07:00]};
            RdPointer    <= {BlockSelect,ShiftRegister[07:00]};

            ADDR_Rcvd <= 1;
         end

         WrCounter <= 0;
         WrPointer <= 0;

         CTRL_Rcvd <= 0;
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.07:  Write Data Buffer
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (ADDR_Rcvd & (BitCounter == 8)) begin
         if ((WP == 0) & (RdWrBit == 0)) begin
            WrDataByte[WrPointer] <= ShiftRegister[07:00];

            WrCounter <= WrCounter + 1;
            WrPointer <= WrPointer + 1;
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.08:  Acknowledge Generator
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (!WriteActive) begin
         if (BitCounter == 8) begin
            if (WrCycle | (START_Rcvd & (ShiftRegister[07:04] == 4'b1010))) begin
               SDA_DO <= 0;
               SDA_OE <= 1;
            end 
         end
         if (BitCounter == 9) begin
            BitCounter <= 0;

            if (!RdCycle) begin
               SDA_DO <= 0;
               SDA_OE <= 0;
            end
         end
      end
   end 

// -------------------------------------------------------------------------------------------------------
//      1.09:  Acknowledge Detect
// -------------------------------------------------------------------------------------------------------

   always @(posedge SCL) begin
      if (RdCycle & (BitCounter == 8)) begin
         if ((SDA == 0) & (SDA_OE == 0)) MACK_Rcvd <= 1;
      end
   end

   always @(negedge SCL) MACK_Rcvd <= 0;

// -------------------------------------------------------------------------------------------------------
//      1.10:  Write Cycle Timer
// -------------------------------------------------------------------------------------------------------

   always @(posedge STOP_Rcvd) begin
      if (WrCycle & (WP == 0) & (WrCounter > 0)) begin
         WriteActive = 1;
         #(tWC);
         WriteActive = 0;
      end
   end

   always @(posedge STOP_Rcvd) begin
      #(1.0);
      STOP_Rcvd = 0;
   end

// -------------------------------------------------------------------------------------------------------
//      1.11:  Write Cycle Processor
// -------------------------------------------------------------------------------------------------------

   always @(negedge WriteActive) begin
      for (LoopIndex = 0; LoopIndex < WrCounter; LoopIndex = LoopIndex + 1) begin
         if (StartAddress[08] == 0) begin
            PageAddress = StartAddress[03:00] + LoopIndex;

            MemoryBlock0[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];
         end
         if (StartAddress[08] == 1) begin
            PageAddress = StartAddress[03:00] + LoopIndex;

            MemoryBlock1[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.12:  Read Data Multiplexor
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (BitCounter == 8) begin
         if (WrCycle & ADDR_Rcvd) begin
            RdPointer <= StartAddress + WrPointer + 1;
         end
         if (RdCycle) begin
            RdPointer <= RdPointer + 1;
         end
      end
   end

   assign RdDataByte = RdPointer[08] ? MemoryBlock1[RdPointer[07:00]] : MemoryBlock0[RdPointer[07:00]];

// -------------------------------------------------------------------------------------------------------
//      1.13:  Read Data Processor
// -------------------------------------------------------------------------------------------------------

   always @(negedge SCL) begin
      if (RdCycle) begin
         if (BitCounter == 8) begin
            SDA_DO <= 0;
            SDA_OE <= 0;
         end
         else if (BitCounter == 9) begin
            SDA_DO <= RdDataByte[07];

            if (MACK_Rcvd) SDA_OE <= 1;
         end
         else begin
            SDA_DO <= RdDataByte[7-BitCounter];
         end
      end
   end

// -------------------------------------------------------------------------------------------------------
//      1.14:  SDA Data I/O Buffer
// -------------------------------------------------------------------------------------------------------

   bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);

   assign SDA_DriveEnable = !SDA_DO & SDA_OE;
   always @(SDA_DriveEnable) SDA_DriveEnableDlyd <= #(tAA) SDA_DriveEnable;


// *******************************************************************************************************
// **   DEBUG LOGIC                                                           				**
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
//      2.01:  Memory Data Bytes
// -------------------------------------------------------------------------------------------------------

   wire [07:00]	MemoryByte0_00 = MemoryBlock0[00];
   wire [07:00]	MemoryByte0_01 = MemoryBlock0[01];
   wire [07:00]	MemoryByte0_02 = MemoryBlock0[02];
   wire [07:00]	MemoryByte0_03 = MemoryBlock0[03];
   wire [07:00]	MemoryByte0_04 = MemoryBlock0[04];
   wire [07:00]	MemoryByte0_05 = MemoryBlock0[05];
   wire [07:00]	MemoryByte0_06 = MemoryBlock0[06];
   wire [07:00]	MemoryByte0_07 = MemoryBlock0[07];

   wire [07:00]	MemoryByte0_08 = MemoryBlock0[08];
   wire [07:00]	MemoryByte0_09 = MemoryBlock0[09];
   wire [07:00]	MemoryByte0_0A = MemoryBlock0[10];
   wire [07:00]	MemoryByte0_0B = MemoryBlock0[11];
   wire [07:00]	MemoryByte0_0C = MemoryBlock0[12];
   wire [07:00]	MemoryByte0_0D = MemoryBlock0[13];
   wire [07:00]	MemoryByte0_0E = MemoryBlock0[14];
   wire [07:00]	MemoryByte0_0F = MemoryBlock0[15];

   wire [07:00]	MemoryByte1_00 = MemoryBlock1[00];
   wire [07:00]	MemoryByte1_01 = MemoryBlock1[01];
   wire [07:00]	MemoryByte1_02 = MemoryBlock1[02];
   wire [07:00]	MemoryByte1_03 = MemoryBlock1[03];
   wire [07:00]	MemoryByte1_04 = MemoryBlock1[04];
   wire [07:00]	MemoryByte1_05 = MemoryBlock1[05];
   wire [07:00]	MemoryByte1_06 = MemoryBlock1[06];
   wire [07:00]	MemoryByte1_07 = MemoryBlock1[07];

   wire [07:00]	MemoryByte1_08 = MemoryBlock1[08];
   wire [07:00]	MemoryByte1_09 = MemoryBlock1[09];
   wire [07:00]	MemoryByte1_0A = MemoryBlock1[10];
   wire [07:00]	MemoryByte1_0B = MemoryBlock1[11];
   wire [07:00]	MemoryByte1_0C = MemoryBlock1[12];
   wire [07:00]	MemoryByte1_0D = MemoryBlock1[13];
   wire [07:00]	MemoryByte1_0E = MemoryBlock1[14];
   wire [07:00]	MemoryByte1_0F = MemoryBlock1[15];

// -------------------------------------------------------------------------------------------------------
//      2.02:  Write Data Buffer
// -------------------------------------------------------------------------------------------------------

   wire [07:00]	WriteData_0 = WrDataByte[00];
   wire [07:00]	WriteData_1 = WrDataByte[01];
   wire [07:00]	WriteData_2 = WrDataByte[02];
   wire [07:00]	WriteData_3 = WrDataByte[03];
   wire [07:00]	WriteData_4 = WrDataByte[04];
   wire [07:00]	WriteData_5 = WrDataByte[05];
   wire [07:00]	WriteData_6 = WrDataByte[06];
   wire [07:00]	WriteData_7 = WrDataByte[07];
   wire [07:00]	WriteData_8 = WrDataByte[08];
   wire [07:00]	WriteData_9 = WrDataByte[09];
   wire [07:00]	WriteData_A = WrDataByte[10];
   wire [07:00]	WriteData_B = WrDataByte[11];
   wire [07:00]	WriteData_C = WrDataByte[12];
   wire [07:00]	WriteData_D = WrDataByte[13];
   wire [07:00]	WriteData_E = WrDataByte[14];
   wire [07:00]	WriteData_F = WrDataByte[15];


// *******************************************************************************************************
// **   TIMING CHECKS                                                           			**
// *******************************************************************************************************

   wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);

   specify
      specparam
         tHI = 600,                                     // SCL pulse width - high
         tLO = 1300,                                    // SCL pulse width - low
         tSU_STA = 600,                                 // SCL to SDA setup time
         tHD_STA = 600,                                 // SCL to SDA hold time
         tSU_DAT = 100,                                 // SDA to SCL setup time
         tSU_STO = 600,                                 // SCL to SDA setup time
         tBUF = 1300;                                   // Bus free time

      $width (posedge SCL, tHI);
      $width (negedge SCL, tLO);

      $width (posedge SDA &&& SCL, tBUF);

      $setup (posedge SCL, negedge SDA &&& TimingCheckEnable, tSU_STA);
      $setup (SDA, posedge SCL &&& TimingCheckEnable, tSU_DAT);
      $setup (posedge SCL, posedge SDA &&& TimingCheckEnable, tSU_STO);

      $hold  (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);
   endspecify

endmodule

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值