IIC读写EEPROM

一.协议介绍

1.IIC概念

IIC是一种两线式串行总线,由数据线SDA时钟线SCL构成通信线路,既可用于发送数据,也可接受数据,是一种半双工通信协议
总线上的主设备和从设备之间以字节为单位进行双向的数据传输。
多用于主机和从机在数据量不大且传输距离短的场合下的主从通信。主机启动总线,并产生时钟用于传送数据,此时任何接收数据的器件均被认为是从机。I2C器件一般采用开漏结构与总线相连,所以I2C_SCL和I2C_SDA均需接上拉电阻,也正因此,当总线空闲时,这两条线路都处于高电平状态,当连到总线上的任一器件输出低电平,都将使总线拉低。
总线上的每一个设备都可以作为主设备或从设备,而且每一个设备都会对应一个唯一的地址(可以从12C器件数据手册得知),主从设备之间就是通过这个地址来确定与哪个器件进行通信。

2.时序要求

  1. 空闲状态
    空闲状态,IIC两条总线被规定都处在高电平,此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

  2. 起始信号
    在SCL保持高电平期间,SDA的电平被拉低,称为 I2C总线的起始信号,标志着一次数据传输的开始。起始信号由主机主动建立,在建立该信号之前I2C总线必须处于空闲状态。

  3. 停止信号
    在SCL处在高电平期间。SDA被释放,被上拉到高电平,成为IIC总线停止信号,标志着一次数据传输终止。 停止信号由主机主动发送,结束后,IIC总线返回空闲状态。

  4. 数据传输
    在IIC总线传输数据时,必须在SCL处在低电平期间才允许SDA数据改变,在SCL处在高电平期间,SDA被要求必须保持稳定,在SCL高电平期间进行采样,如果SDA处于高电平则为1,为低电平则为0

  5. 应答信号
    12C总线上的所有数据都是以字节传送的,发送端每发送一个字节,就必须在第9个SCL脉冲期间释放SDA,由接收端反馈一个应答信号。应答信号为低电平时,称为有效应答位(ACK),表示接收端成功接收了该字节;应答信号为高电平时,称为非应答位(NACK),表示接收端接收该字节失败。对于反馈有效应答位ACK的要求是,接收端在第9个时钟脉冲之前的低电平期间将SDA拉低,并且确保在该时钟周期的高电平期间保持低电平。如果接收端是主控端,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送端结束数据发送,并释放SDA线,以便主控接收器发送停止信号。IIC读写EEPROM_第1张图片

二.EEPROM

1.芯片简介

IIC读写EEPROM_第2张图片
IIC读写EEPROM_第3张图片

1.读写时序

1.写操作

IIC读写EEPROM_第4张图片
byte写:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>相应信号——>8bits数据——>响应信号——>停止位
页写:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>相应信号——>8bits数据——>响应信号——>……——>>8bits数据——>响应信号——>停止位;
注意:页写中每一页最多写入16byte数据,如果超过的的内部指针会滚动,将之前写入的数据从头向后覆盖

2.读操作(当前地址读,随机读,顺序读)

IIC读写EEPROM_第5张图片
当前地址读:byte写:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>响应信号——>接收数据8bits——>不发送响应信号——>发送停止位
(注意当前地址读是在上一个读写操作的地址上+1,也就是上次读写地址的写一个地址)
随机读:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>接收8bits数据——>不发送响应信号——>停止位
页读:起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>数据地址(8bits)——>起始位——>设备地址(4bit+3bit(2bit不关心+1bit块选择位)+1bit读/写)——>相应信号——>接收8bits数据——>发送响应信号——>……——>>8bits数据——>不发送响应信号——>停止位

三.项目设计

1.状态机设计

这里还是采用主从状态机实现,通过接口模块,和控制接口模块来达到读写eeprom
IIC读写EEPROM_第6张图片
控制模块(主状态机模块),仅考虑操作类型,读操作或者写操作,不关系数据如何发送,只需要发送操作指令,等待接口模块发送完成返回结束信号。
接口模块(从状态机),当操作指令到来后,只需要按照时序发送或者接收数据,之后返还一个结束信号

2.代码实现

1.接口模块

/**************************************功能介绍***********************************
Date	: 
Author	: WZY.
Version	: 
Description: 接口命令(cmd)
                                0       1
             bit0 (起始位)      NO      YES
             bit1 (写数据)      NO      YES
             bit2 (读数据)      NO      YES
             bit3 (停止位)      NO      YES
             bit4 (相应位)      ACK     NO_ACK
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module iic_dirver#
(
    parameter T = "100k",
              SYSTEM_CLOCK = 50_000_000
)
( 
    input 	wire				clk		,
    input 	wire				rst_n	,

    //控制接口
    input   wire    [4:0]       cmd     ,
    input   wire                cmd_vld ,
    output  wire                done    ,

    //数据接口
    input   wire    [7:0]       wr_data ,
    output  wire    [7:0]       rd_data ,
    output  wire                rd_data_vld,

    output  wire                iic_scl ,
    inout   wire                iic_sda 
);								 
//---------<参数定义>--------------------------------------------------------- 
//状态机参数定义
localparam  IDLE    =   7'b0000001,
            START   =   7'b0000010,
            WRITE   =   7'b0000100,
            READ    =   7'b0001000,
            R_ACK   =   7'b0010000,
            S_ACK   =   7'b0100000,
            STOP    =   7'b1000000;  

//时间参数定义
parameter   IICT  = (T == "100k")?SYSTEM_CLOCK/100_000:
                   (T == "400k")?SYSTEM_CLOCK/400_000:
                   SYSTEM_CLOCK/3_400_000;
//  parameter   IICT = 500;
parameter   IICT_1_4 = IICT >> 2,
            IICT_1_2 = IICT >> 1,
            IICT_3_4 = IICT - IICT_1_4;

//指令定义
`define     START_BIT  5'b00001
`define     WRITE_BIT  5'b00010
`define     READ_BIT   5'b00100
`define     STOP_BIT   5'b01000
`define     ACK_BIT    5'b10000
//响应信号定义
`define     ACK        0
`define     NO_ACK     1
//---------<内部信号定义>-----------------------------------------------------
reg 	[6:0]	cstate     ;//现态
reg	    [6:0]	nstate     ;//次态

wire            idle2start  ;
wire            idle2write  ;
wire            idle2read   ;
wire            start2write ;
wire            write2r_ack ;
wire            read2s_ack  ;
wire            r_ack2idle  ;
wire            r_ack2stop  ;
wire            s_ack2idle  ;
wire            s_ack2stop  ;
wire            stop2idle   ;


//计数器参数定义
reg			[8:0]	cnt_1bit	   	;
wire				add_cnt_1bit	;
wire				end_cnt_1bit	;

reg			[2:0]	cnt_num	   	;
wire				add_cnt_num	;
wire				end_cnt_num	;

reg         [3:0]   num;

//命令信号寄存
reg         [4:0]   cmd_r           ;

//存在信号
reg                 rev_ack         ;

//三态门参数
reg                 OE;
wire                din;
reg                 dout;

//数据寄存
reg         [7:0]   rd_data_r       ;
reg         [7:0]   wr_data_r       ;

//****************************************************************
//                  数据寄存
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        wr_data_r <= 8'd0;
    end
    else if (cmd_vld) begin
        wr_data_r <= wr_data;
    end
end

//****************************************************************
//                  状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE  : begin
                    if (idle2start) begin
                        nstate = START;
                    end
                    else if (idle2write) begin
                        nstate = WRITE;
                    end
                    else if (idle2read) begin
                        nstate = READ;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        START : begin
                    if (start2write) begin
                        nstate = WRITE;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        WRITE : begin
                    if (write2r_ack) begin
                        nstate = R_ACK;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        READ  : begin
                    if (read2s_ack) begin
                        nstate = S_ACK;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        R_ACK : begin
                    if (r_ack2idle) begin
                        nstate = IDLE;
                    end
                    else if (r_ack2stop) begin
                        nstate = STOP;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        S_ACK : begin
                    if (s_ack2stop) begin
                        nstate = STOP;
                    end
                    else if (s_ack2idle) begin
                        nstate = IDLE;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        STOP  : begin
                    if (stop2idle) begin
                        nstate = IDLE;
                    end
                    else begin
                      nstate = cstate;
                    end
                end
        default : ;
    endcase
end
assign  idle2start  = cstate == IDLE    && cmd_vld && (cmd & `START_BIT);//接收到命令信号并且命令中包含开始命令
assign  idle2write  = cstate == IDLE    && cmd_vld && (cmd & `WRITE_BIT);//接收到命令信号并且命令中没有开始命令,有写命令
assign  idle2read   = cstate == IDLE    && cmd_vld && (cmd & `READ_BIT );//接收到命令信号并且命令中没有开始命令,有读命令
assign  start2write = cstate == START   && end_cnt_num && (cmd_r & `WRITE_BIT);
assign  write2r_ack = cstate == WRITE   && end_cnt_num;
assign  read2s_ack  = cstate == READ    && end_cnt_num;
assign  r_ack2idle  = cstate == R_ACK   && end_cnt_num && !(cmd_r & `STOP_BIT);//接收到响应,并且命令中没有停止命令
assign  r_ack2stop  = cstate == R_ACK   && end_cnt_num &&  (cmd_r & `STOP_BIT);//接收到相应信号,并且命令中有停止命令
assign  s_ack2idle  = cstate == S_ACK   && end_cnt_num && !(cmd_r & `STOP_BIT);//等待1bit时间并且没有停止命令
assign  s_ack2stop  = cstate == S_ACK   && end_cnt_num &&  (cmd_r & `STOP_BIT);//等待1bit时间并且有停止命令
assign  stop2idle   = cstate == STOP    && end_cnt_num;
            
//第三段:描述输出,时序逻辑或组合逻辑皆可
            
//****************************************************************
//                  命令寄存
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        cmd_r <= 0;
    end
    else if (cmd_vld) begin//每次接收到命令使能更新命令
        cmd_r <= cmd;
    end
end

//****************************************************************
//              计数器复用
//**************************************************************** 
//1bit计数器


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_1bit <= 9'd0;
    end 
    else if(add_cnt_1bit)begin 
        if(end_cnt_1bit)begin 
            cnt_1bit <= 'd0;
        end
        else begin 
            cnt_1bit <= cnt_1bit + 1'b1;
        end 
    end
end 

assign add_cnt_1bit = cstate != IDLE;
assign end_cnt_1bit = add_cnt_1bit && cnt_1bit == IICT-1;

//计数器复用


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_num <= 'd0;
    end 
    else if(add_cnt_num)begin 
        if(end_cnt_num)begin 
            cnt_num <= 'd0;
        end
        else begin 
            cnt_num <= cnt_num + 1'b1;
        end 
    end
end 

assign add_cnt_num = end_cnt_1bit;
assign end_cnt_num = add_cnt_num && cnt_num == num - 1;


always @(*) begin
    case (cstate)
        START   : num = 1 ;
        WRITE   : num = 8 ;
        READ    : num = 8 ;
        R_ACK   : num = 1 ;
        S_ACK   : num = 1 ;
        STOP    : num = 1 ;
        default: num = 1;
    endcase
end
  
//****************************************************************
//                  三态门
//****************************************************************
//OE使能控制
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        OE <= 0;
    end
    else if (idle2start||idle2write||start2write||r_ack2stop||read2s_ack||s_ack2stop) begin
            OE <= 1;
        end
        else if (idle2read||write2r_ack||stop2idle||s_ack2idle||r_ack2idle) begin
            OE <= 0;
        end
end
assign iic_sda = OE?dout:1'bz;
assign din        = iic_sda;
assign iic_scl = (cnt_1bit >= (IICT >>1))?1:0;//发送周期波形

// SDA信号控制
always @(*) begin
    if (!rst_n) begin
        dout = 1;
    end
    else case (cstate)
        IDLE : begin
                dout = 1;
            end
        START : begin
                if (cnt_1bit == IICT_3_4) begin
                    dout = 0;
                end
            end
        WRITE : begin
                if (cnt_1bit == IICT_1_4) begin
                    dout = wr_data_r[7-cnt_num];
                end
            end
        READ : begin
                if (cnt_1bit == IICT_3_4) begin
                    rd_data_r[7-cnt_num] = din;
                end
            end
        R_ACK: begin
                if (cnt_1bit == IICT_3_4) begin
                    rev_ack <= din;
                end
            end
        S_ACK: begin
                if (cnt_1bit == IICT_1_4) begin
                    dout = 0;
                end
                if (cmd_r & `ACK_BIT) begin
                    dout = `NO_ACK;
                end
                else begin
                    dout = `ACK;
                end
               
            end
        STOP : begin
                if (cnt_1bit == IICT_3_4) begin
                    dout = 1;
                end
            end
        default: dout = dout;
    endcase
end
assign rd_data = rd_data_r;
//****************************************************************
//                  使能信号
//***************************************************************
assign  done = stop2idle||r_ack2idle||s_ack2idle;
assign  rd_data_vld = s_ack2stop;
endmodule

2.控制模块

/**************************************功能介绍***********************************
Date	: 
Author	: WZY.
Version	: 
Description: 
*********************************************************************************/
/*
1、带起始位、不带停止位的写操作,主要是每次开始传输时候的第一字节;
2、不带起始位、也不带停止位的写操作,主要是传输器件地址和连续写多个字节数据到从机中的操作,比如 EEPROM的页写操作;
3、不带起始位、带停止位的写操作,主要是写入数据时候的最后一段,也就是完整写的最后一字节;
4、不带起始位、也不带停止位的读操作,主要是从器件中连续读出多个字节数据的操作,比如EEPROM的连续地址读操作。
5、不带起始位、带停止位的读操作,主要是从器件中读出最后一个字节。
*/
//---------<模块及端口声名>------------------------------------------------------
module eeprom_control#
(
    parameter ADDR_BIT = 8
)
( 
    input 	wire				    clk		,
    input 	wire				    rst_n	,

    //交互接口  
    input   wire       [6:0]        device_id,//IIC从设备ID
    input   wire [ADDR_BIT-1:0]     reg_adder,//读写地址
    input   wire                    reg_adder_vld,
    input   wire                    rd_en   ,
    input   wire                    wr_en   , 
    //数据接口  
    input   wire       [7:0]        wr_data ,
    input   wire                    wr_data_vld,
    output  wire       [7:0]        rd_data ,
    output  wire                    rd_data_vld,
    output  wire                    ready,

    //iic接口   
    output  wire                    iic_scl,
    inout   wire                    iic_sda
              
);								 
//---------<参数定义>--------------------------------------------------------- 
localparam  IDLE            = 6'b000001,
            WR_REQ          = 6'b000010,
            WAIT_WR_DONE    = 6'b000100,
            RD_REQ          = 6'b001000,
            WAIT_RD_DONE    = 6'b010000,
            DONE            = 6'b100000; 
parameter   WR_CTRL_BIT     = 8'b10100000,
            RD_CTRL_BIT     = 8'b10100001;


`define     START_BIT  5'b00001
`define     WRITE_BIT  5'b00010
`define     READ_BIT   5'b00100
`define     STOP_BIT   5'b01000
`define     ACK_BIT    5'b10000
//---------<内部信号定义>-----------------------------------------------------

//状态机参数定义
reg 	[5:0]	cstate     ;//现态
reg	    [5:0]	nstate     ;//次态

wire            idle2wr_req         ;
wire            idle2rd_req         ;
wire            wr_req2wait_wr_done ;
wire            wait_wr_done2done   ;
wire            wait_wr_done2wr_req ;
wire            wait_wr_done2rd_req ;
wire            rd_req2wait_rd_done ;
wire            wait_rd_done2done   ;
wire            wait_rd_done2rd_req ;
wire            done2idle           ;

reg			[2:0]	cnt_cmd	   	;
wire				add_cnt_cmd	;
wire				end_cnt_cmd	;
reg         [2:0]   cmd_num;

wire                done            ;
reg         [4:0]   cmd             ;
reg                 cmd_vld         ;
reg         [7:0]   op_wr_data      ;

reg                 rd_en_r           ;
reg                 wr_en_r           ;
//****************************************************************
//                      同步
//****************************************************************
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rd_en_r <= 0;
        wr_en_r <= 0;
    end
    else begin
        rd_en_r <= rd_en;
        wr_en_r <= wr_en;
    end
end

//****************************************************************
//                  数据寄存
//****************************************************************
reg     [ADDR_BIT-1:0]  addr_r;
reg     [7:0]           wr_data_r;
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        addr_r <= 0;
    end
    else if (reg_adder_vld) begin
        addr_r <= reg_adder;
    end
    else begin
      addr_r <= addr_r;
    end
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        wr_data_r <= 0;
    end
    else if (wr_data_vld) begin
        wr_data_r <= wr_data;
    end
    else begin
      wr_data_r <= wr_data_r;
    end
end

//****************************************************************
//                  状态机
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin 
    if(!rst_n)begin
        cstate <= IDLE;
    end 
    else begin 
        cstate <= nstate;
    end 
end

//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
    case(cstate)
        IDLE         : begin
                            if (idle2wr_req) begin
                                nstate = WR_REQ;
                            end
                            else if (idle2rd_req) begin
                                nstate = RD_REQ;
                            end
                            else begin
                              nstate = cstate;
                            end
                    end
        WR_REQ       : begin
                            if (wr_req2wait_wr_done) begin
                                nstate = WAIT_WR_DONE;
                            end
                            else begin
                              nstate = cstate;
                            end
                    end
        WAIT_WR_DONE : begin
                            if (wait_wr_done2done) begin
                                nstate = DONE;
                            end
                            else if (wait_wr_done2wr_req) begin
                                nstate = WR_REQ;
                            end
                            else begin
                              nstate = cstate;
                            end
                    end
        RD_REQ       : begin
                            if (rd_req2wait_rd_done) begin
                                nstate = WAIT_RD_DONE;
                            end
                            else begin
                              nstate = cstate;
                            end
                    end
        WAIT_RD_DONE : begin
                            if (wait_rd_done2done) begin
                                nstate = DONE;
                            end
                            else if (wait_rd_done2rd_req) begin
                                nstate = RD_REQ;
                            end
                            else begin
                              nstate = cstate;
                            end
                    end
        DONE         : begin
                            if (done2idle) begin
                                nstate = IDLE;
                            end
                            else begin
                              nstate = cstate;
                            end
                    end
        default : ;
    endcase
end

assign  idle2wr_req         = cstate == IDLE         && wr_en_r;
assign  idle2rd_req         = cstate == IDLE         && rd_en_r;
assign  wr_req2wait_wr_done = cstate == WR_REQ       && 1;
assign  wait_wr_done2done   = cstate == WAIT_WR_DONE && end_cnt_cmd;
assign  wait_wr_done2wr_req = cstate == WAIT_WR_DONE && done && !end_cnt_cmd;
assign  rd_req2wait_rd_done = cstate == RD_REQ       && 1;
assign  wait_rd_done2done   = cstate == WAIT_RD_DONE && end_cnt_cmd;
assign  wait_rd_done2rd_req = cstate == WAIT_RD_DONE && done && !end_cnt_cmd;
assign  done2idle           = cstate == DONE         && 1;

//****************************************************************
//                  指令计数器
//****************************************************************


always @(posedge clk or negedge rst_n)begin 
   if(!rst_n)begin
        cnt_cmd <= 3'd0;
    end 
    else if(add_cnt_cmd)begin 
        if(end_cnt_cmd)begin 
            cnt_cmd <= 3'd0;
        end
        else begin 
            cnt_cmd <= cnt_cmd + 1'b1;
        end 
    end
    else if (cstate == IDLE) begin
        cnt_cmd <= 3'd0;
    end
end 

assign add_cnt_cmd = done;
assign end_cnt_cmd = add_cnt_cmd && cnt_cmd == cmd_num - 1;

always @(posedge clk  or negedge rst_n) begin
    if (!rst_n) begin
        cmd_num <= 1;
    end
    else if (rd_en) begin
        cmd_num <= 4;
    end
    else if (wr_en) begin
        cmd_num <= 3;
    end
    else if (end_cnt_cmd) begin
        cmd_num <= 1;
    end
end
//****************************************************************
//                  发送指令
//**************************************************************** 
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        TX(0,5'b0,8'h00);
    end
    else case (cstate)
        WR_REQ: case (cnt_cmd)//线性序列机
                    0   :   TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BIT);//写器件地址
                    1   :   TX(1,(`WRITE_BIT),addr_r);//写入数据地址
                    2   :   TX(1,(`WRITE_BIT | `STOP_BIT),wr_data_r);//写入数据
                default: TX(0,cmd,op_wr_data);
        endcase
        RD_REQ: case (cnt_cmd)
                    0   :   TX(1,(`START_BIT | `WRITE_BIT),WR_CTRL_BIT);//写入器件地址(写)
                    1   :   TX(1,(`WRITE_BIT),addr_r) ;//写入数据地址
                    2   :   TX(1,(`START_BIT | `WRITE_BIT),RD_CTRL_BIT);//写入器件地址(读)
                    3   :   TX(1,(`READ_BIT| `ACK_BIT | `STOP_BIT),8'h00);//读取数据
                default: TX(0,cmd,op_wr_data);
        endcase
        default:TX(0,cmd,op_wr_data) ;
    endcase
end
//****************************************************************
//                  任务
//****************************************************************
task TX;
    input               task_cmd_vld;
    input   [4:0]       task_cmd;
    input   [7:0]       task_wr_data;
    begin
      cmd_vld = task_cmd_vld;
      cmd     = task_cmd;
      op_wr_data = task_wr_data;
    end
endtask

assign ready = cstate == IDLE;

//****************************************************************
//                      子模块例化
//****************************************************************
iic_dirver#
(
   .T ("100k"),
   .SYSTEM_CLOCK(50_000_000)
)
iic_dirver_inst
( 
    /*input 	wire		*/		.clk		(clk),
    /*input 	wire		*/		.rst_n	    (rst_n),

    /*input   wire    [4:0] */      .cmd        (cmd),
    /*input   wire          */      .cmd_vld    (cmd_vld),
    /*output  wire          */      .done       (done),

    /*input   wire    [7:0] */      .wr_data    (op_wr_data),
    /*output  wire    [7:0] */      .rd_data    (rd_data),
    /*output  wire          */      .rd_data_vld(rd_data_vld),

    /*output  wire          */      .iic_scl    (iic_scl),
    /*inout   wire          */      .iic_sda    (iic_sda) 
);
endmodule

3.顶层

其他按键模块,串口模块不过多赘述

/**************************************功能介绍***********************************
Date	: 
Author	: WZY.
Version	: 
Description: 
*********************************************************************************/
    
//---------<模块及端口声名>------------------------------------------------------
module top( 
    input 	wire				clk		,
    input 	wire				rst_n	,
    input   wire                key_in  ,
    input   wire                rx      ,

    output  wire                tx      ,
    output  wire                iic_scl ,
    inout   wire                iic_sda
);								 
//---------<参数定义>--------------------------------------------------------- 
wire    [7:0]   wr_data;
wire            wr_data_vld;
wire    [7:0]   rd_data;
wire            rd_data_vld;
wire            key_debounce;
//---------<内部信号定义>-----------------------------------------------------
eeprom_control#
(
    .ADDR_BIT  (8)
)
eeprom_control_inst
( 
    /*input 	wire				*/  .clk		    (clk),
    /*input 	wire				*/  .rst_n	        (rst_n),
    /*input   wire       [6:0]      */  .device_id      (1010000),//IIC从设备ID
    /*input   wire [ADDR_BIT-1:0]   */  .reg_adder      (8'h00),//读写地址
    /*input   wire                  */  .reg_adder_vld  (1),
    /*input   wire                  */  .rd_en          (!key_debounce),
    /*input   wire                  */  .wr_en          (wr_data_vld),   
    /*input   wire       [7:0]      */  .wr_data        (wr_data),
    /*input   wire                  */  .wr_data_vld    (wr_data_vld),
    /*output  wire       [7:0]      */  .rd_data        (rd_data),
    /*output  wire                  */  .rd_data_vld    (rd_data_vld),
    /*output  wire                  */  .ready          (), 
    /*output  wire                  */  .iic_scl        (iic_scl),
    /*inout   wire                  */  .iic_sda        (iic_sda)
              
);  

//模块例化
uart_rx#
(
       .BPS  (115200),
       .CLK_FRE  (50_000_000),
       .CHECK_BIT  ("NONE")//NONE 不校验 DDO奇校验 EVEN偶校验

) uart_rx_inst
( 
    /*input   wire			*/	   .clk		    (clk),
    /*input   wire			*/	   .rst_n	    (rst_n),
    /*input   wire          */     .rx          (rx),
    /*output  wire          */     .ready       (),
    /*output  wire     [7:0]*/     .rx_data     (wr_data),
    /*output  wire          */     .rx_data_vld (wr_data_vld)
);


//模块例化
uart_tx#
(
    .BPS  (115200),
    .CLK_FRE  (50_000_000),
    .CHECK_BIT  ("NONE")//NONE 不校验 DDO奇数 EVEN偶数

)uart_tx_inst
( 
    /*input   wire			*/	           .clk		    (clk),
    /*input   wire			*/	           .rst_n	    (rst_n),
    /*input   wire          */             .tx_data_vld (rd_data_vld),
    /*input   wire    [7:0] */             .tx_data     (rd_data),
    /*output  reg           */             .tx          (tx), 
    /*output  wire          */             .ready       ()
);
    
key_debounce key_debounce_inst(
    /*input   wire          */  .clk     (clk),
    /*input   wire          */  .rst_n   (rst_n),
    /*input   wire    [3:0] */  .key_in  (key_in),
    /*output  wire    [3:0] */  .key_out (key_debounce)
); 
    
endmodule

3.仿真

1.仿真模型

// *******************************************************************************************************
// **                                                                           			**
// **   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 = 0;                                   // SCL to SDA output delay
   initial tWC = 0;                                   // memory write cycle time
   // initial tAA = 900;                                   // SCL to SDA output delay
   // initial tWC = 500;                                   // memory write cycle time

//   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
         tLO = 600, 
			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
         tBUF = 600;
			
      $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.testbench

`timescale 1ns/1ns
    
module top_tb();

//激励信号定义 
    reg				clk  	;
    reg				rst_n	;
    reg             key_in  ;
    reg             rx      ;

//输出信号定义	 
    wire            tx      ;
    wire            iic_scl ;
    wire            iic_sda ;

//时钟周期参数定义	
    parameter		CYCLE = 20;   
    pullup(iic_sda);

//模块例化
top top_inst( 
    /*input 	wire	*/			.clk		(clk),
    /*input 	wire	*/			.rst_n	    (rst_n),
    /*input   wire      */          .key_in     (key_in),
    /*input   wire      */          .rx         (rx),
    /*output  wire      */          .tx         (tx),
    /*output  wire      */          .iic_scl    (iic_scl),  
    /*inout   wire      */          .iic_sda    (iic_sda)
);	

M24LC04B u_M24LC04B(
    .A0(0),
    .A1(0),
    .A2(0),
    .WP(0),
    .SDA(iic_sda), 
    .SCL(iic_scl), 
    .RESET(~rst_n)
    
    );

//产生时钟
    initial 		clk = 1'b1;
    always #(CYCLE/2) clk = ~clk;

//产生激励
    initial  begin 
        rst_n = 1'b1;
        #(CYCLE*2);
        rst_n = 1'b0;
        key_in = 1'b0;
        rx     = 1'b0;
        #(CYCLE*20);
        rst_n = 1'b1;
        #20
        rx = 1  ;
        #(CYCLE*434)
        rx = 0  ;   //开始位发送
        #(CYCLE*434)
        rx = 0  ;   //发送数据1
        #(CYCLE*434)
        rx = 1  ;   //发送数据0
        #(CYCLE*434)
        rx = 0  ;   //发送数据1
        #(CYCLE*434)
        rx = 1  ;   //发送数据0
        #(CYCLE*434)
        rx = 0  ;   //发送数据1
        #(CYCLE*434)
        rx = 1  ;   //发送数据0
        #(CYCLE*434)
        rx = 0  ;   //发送数据1
        #(CYCLE*434)
        rx = 1  ;   //发送数据0
        #(CYCLE*5000)
        key_in = 1;
        #20;
        key_in = 0;
        #(CYCLE*434)
        // rx = 1  ;   //发送标志位0//偶校验
        // #(CYCLE*10)
        // rx = 1  ;  //发送结束位
        // #(CYCLE*434)
        // rx = 1  ;
        // #(CYCLE*434)
        // rx = 0  ;   //开始位发送
        // #(CYCLE*434)
        // rx = 0  ;   //发送数据1
        // #(CYCLE*434)
        // rx = 1  ;   //发送数据0
        // #(CYCLE*434)
        // rx = 0  ;   //发送数据1
        // #(CYCLE*434)
        // rx = 1  ;   //发送数据0
        // #(CYCLE*434)
        // rx = 0  ;   //发送数据1
        // #(CYCLE*434)
        // rx = 1  ;   //发送数据0
        // #(CYCLE*434)
        // rx = 0  ;   //发送数据1
        // #(CYCLE*434)
        // rx = 1  ;   //发送数据0
        // #(CYCLE*434)
        // // rx = 1  ;   //发送标志位0//偶校验
        // // #(CYCLE*10)
        // rx = 1  ;  //发送结束位
        // #(CYCLE*1000)
        // key_in = 1;
        // #20;
        // key_in = 0;
        // #(CYCLE*434)
        #1000000;
        $stop;


    end

endmodule 

3.仿真波形

IIC读写EEPROM_第7张图片
状态机正常跳转,数据读写成功

四.板级验证

略(经检验效果成功)

你可能感兴趣的:(FPGA实战,fpga开发)