野火FPGA进阶(2):基于I2C协议的EEPROM驱动控制

news2024/12/28 19:26:06

文章目录

    • 第49讲:基于I2C协议的EEPROM驱动控制
      • 理论部分
      • 设计与实现
      • i2c_ctrl
      • i2c_rw_data
      • eeprom_byte_rd_wr
      • tb_eeprom_byte_rd_wr

第49讲:基于I2C协议的EEPROM驱动控制

理论部分

I2C通讯协议(Inter-Integrated Circuit)是由Philips公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。

I2C通讯协议和通信接口在很多工程中有广泛的应用,如数据采集领域的串行AD,图像处理领域的摄像头配置,工业控制领域的X射线管配置等等。除此之外,由于I2C协议占用引脚特别少,硬件实现简单,可扩展性强,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述

设计与实现

实验目标:01-10的写入和读取
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

i2c_ctrl

`timescale  1ns/1ns

module  i2c_ctrl
#(
    parameter   DEVICE_ADDR     =   7'b1010_000     ,   //i2c设备地址
    parameter   SYS_CLK_FREQ    =   26'd50_000_000  ,   //输入系统时钟频率
    parameter   SCL_FREQ        =   18'd250_000         //i2c设备scl时钟频率
)
(
    input   wire            sys_clk     ,   //输入系统时钟,50MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效
    input   wire            wr_en       ,   //输入写使能信号
    input   wire            rd_en       ,   //输入读使能信号
    input   wire            i2c_start   ,   //输入i2c触发信号
    input   wire            addr_num    ,   //输入i2c字节地址字节数
    input   wire    [15:0]  byte_addr   ,   //输入i2c字节地址
    input   wire    [7:0]   wr_data     ,   //输入i2c设备数据

    output  reg             i2c_clk     ,   //i2c驱动时钟
    output  reg             i2c_end     ,   //i2c一次读/写操作完成
    output  reg     [7:0]   rd_data     ,   //输出i2c设备读取数据
    output  reg             i2c_scl     ,   //输出至i2c设备的串行时钟信号scl
    inout   wire            i2c_sda         //输出至i2c设备的串行数据信号sda
);

// parameter define
parameter   CNT_CLK_MAX     =   (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3   ;   //cnt_clk计数器计数最大值

parameter   CNT_START_MAX   =   8'd100; //cnt_start计数器计数最大值

parameter   IDLE            =   4'd00,  //初始状态
            START_1         =   4'd01,  //开始状态1
            SEND_D_ADDR     =   4'd02,  //设备地址写入状态 + 控制写
            ACK_1           =   4'd03,  //应答状态1
            SEND_B_ADDR_H   =   4'd04,  //字节地址高八位写入状态
            ACK_2           =   4'd05,  //应答状态2
            SEND_B_ADDR_L   =   4'd06,  //字节地址低八位写入状态
            ACK_3           =   4'd07,  //应答状态3
            WR_DATA         =   4'd08,  //写数据状态
            ACK_4           =   4'd09,  //应答状态4
            START_2         =   4'd10,  //开始状态2
            SEND_RD_ADDR    =   4'd11,  //设备地址写入状态 + 控制读
            ACK_5           =   4'd12,  //应答状态5
            RD_DATA         =   4'd13,  //读数据状态
            N_ACK           =   4'd14,  //非应答状态
            STOP            =   4'd15;  //结束状态

// wire  define
wire            sda_in          ;   //sda输入数据寄存
wire            sda_en          ;   //sda数据写入使能信号

// reg   define
reg     [7:0]   cnt_clk         ;   //系统时钟计数器,控制生成clk_i2c时钟信号
reg     [3:0]   state           ;   //状态机状态
reg             cnt_i2c_clk_en  ;   //cnt_i2c_clk计数器使能信号
reg     [1:0]   cnt_i2c_clk     ;   //clk_i2c时钟计数器,控制生成cnt_bit信号
reg     [2:0]   cnt_bit         ;   //sda比特计数器
reg             ack             ;   //应答信号
reg             i2c_sda_reg     ;   //sda数据缓存
reg     [7:0]   rd_data_reg     ;   //自i2c设备读出数据

// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  8'd0;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        cnt_clk <=  8'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

// i2c_clk:i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_clk <=  1'b1;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        i2c_clk <=  ~i2c_clk;

// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk_en  <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        cnt_i2c_clk_en  <=  1'b0;
    else    if(i2c_start == 1'b1)
        cnt_i2c_clk_en  <=  1'b1;

// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk <=  2'd0;
    else    if(cnt_i2c_clk_en == 1'b1)
        cnt_i2c_clk <=  cnt_i2c_clk + 1'b1;

// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if((state == IDLE) || (state == START_1) || (state == START_2)
                || (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
                || (state == ACK_4) || (state == ACK_5) || (state == N_ACK))
        cnt_bit <=  3'd0;
    else    if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        cnt_bit <=  3'd0;
    else    if((cnt_i2c_clk == 2'd3) && (state != IDLE))
        cnt_bit <=  cnt_bit + 1'b1;

// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else    case(state)
        IDLE:
            if(i2c_start == 1'b1)
                state   <=  START_1;
            else
                state   <=  state;
        START_1:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_D_ADDR;
            else
                state   <=  state;
        SEND_D_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_1;
            else
                state   <=  state;
        ACK_1:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(addr_num == 1'b1)
                        state   <=  SEND_B_ADDR_H;
                    else
                        state   <=  SEND_B_ADDR_L;
                end
             else
                state   <=  state;
        SEND_B_ADDR_H:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_2;
            else
                state   <=  state;
        ACK_2:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  SEND_B_ADDR_L;
            else
                state   <=  state;
        SEND_B_ADDR_L:
            if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
                state   <=  ACK_3;
            else
                state   <=  state;
        ACK_3:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(wr_en == 1'b1)
                        state   <=  WR_DATA;
                    else    if(rd_en == 1'b1)
                        state   <=  START_2;
                    else
                        state   <=  state;
                end
             else
                state   <=  state;
        WR_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_4;
            else
                state   <=  state;
        ACK_4:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  STOP;
            else
                state   <=  state;
        START_2:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_RD_ADDR;
            else
                state   <=  state;
        SEND_RD_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_5;
            else
                state   <=  state;
        ACK_5:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  RD_DATA;
            else
                state   <=  state;
        RD_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  N_ACK;
            else
                state   <=  state;
        N_ACK:
            if(cnt_i2c_clk == 3)
                state   <=  STOP;
            else
                state   <=  state;
        STOP:
            if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
                state   <=  IDLE;
            else
                state   <=  state;
        default:    state   <=  IDLE;
    endcase

// ack:应答信号
always@(*)
    case    (state)
        IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
        WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
            ack <=  1'b1;
        ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
            if(cnt_i2c_clk == 2'd0)
                ack <=  sda_in;
            else
                ack <=  ack;
        default:    ack <=  1'b1;
    endcase

// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
    case    (state)
        IDLE:
            i2c_scl <=  1'b1;
        START_1:
            if(cnt_i2c_clk == 2'd3)
                i2c_scl <=  1'b0;
            else
                i2c_scl <=  1'b1;
        SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
        ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK:
            if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
                i2c_scl <=  1'b1;
            else
                i2c_scl <=  1'b0;
        STOP:
            if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
                i2c_scl <=  1'b0;
            else
                i2c_scl <=  1'b1;
        default:    i2c_scl <=  1'b1;
    endcase

// i2c_sda_reg:sda数据缓存
always@(*)
    case    (state)
        IDLE:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  8'd0;
            end
        START_1:
            if(cnt_i2c_clk <= 2'd0)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_D_ADDR:
            if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b0;
        ACK_1:
            i2c_sda_reg <=  1'b1;
        SEND_B_ADDR_H:
            i2c_sda_reg <=  byte_addr[15 - cnt_bit];
        ACK_2:
            i2c_sda_reg <=  1'b1;
        SEND_B_ADDR_L:
            i2c_sda_reg <=  byte_addr[7 - cnt_bit];
        ACK_3:
            i2c_sda_reg <=  1'b1;
        WR_DATA:
            i2c_sda_reg <=  wr_data[7 - cnt_bit];
        ACK_4:
            i2c_sda_reg <=  1'b1;
        START_2:
            if(cnt_i2c_clk <= 2'd1)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_RD_ADDR:
            if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b1;
        ACK_5:
            i2c_sda_reg <=  1'b1;
        RD_DATA:
            if(cnt_i2c_clk  == 2'd2)
                rd_data_reg[7 - cnt_bit]    <=  sda_in;
            else
                rd_data_reg <=  rd_data_reg;
        N_ACK:
            i2c_sda_reg <=  1'b1;
        STOP:
            if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
                i2c_sda_reg <=  1'b0;
            else
                i2c_sda_reg <=  1'b1;
        default:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  rd_data_reg;
            end
    endcase

// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_data <=  8'd0;
    else    if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        rd_data <=  rd_data_reg;

// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_end <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        i2c_end <=  1'b1;
    else
        i2c_end <=  1'b0;

// sda_in:sda输入数据寄存
assign  sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign  sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2)
                    || (state == ACK_3) || (state == ACK_4) || (state == ACK_5))
                    ? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;

endmodule

i2c_rw_data

在这里插入图片描述

`timescale  1ns/1ns

module  i2c_rw_data
(
    input   wire            sys_clk     ,   //输入系统时钟,频率50MHz
    input   wire            i2c_clk     ,   //输入i2c驱动时钟,频率1MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低有效
    input   wire            write       ,   //输入写触发信号
    input   wire            read        ,   //输入读触发信号
    input   wire            i2c_end     ,   //一次i2c读/写结束信号
    input   wire    [7:0]   rd_data     ,   //输入自i2c设备读出的数据

    output  reg             wr_en       ,   //输出写使能信号
    output  reg             rd_en       ,   //输出读使能信号
    output  reg             i2c_start   ,   //输出i2c读/写触发信号
    output  reg     [15:0]  byte_addr   ,   //输出i2c设备读/写地址
    output  reg     [7:0]   wr_data     ,   //输出写入i2c设备的数据
    output  wire    [7:0]   fifo_rd_data    //输出自fifo中读出的数据
);

// parameter  define
parameter   DATA_NUM        =   8'd10       ,   //读/写操作读出或写入的数据个数
            CNT_START_MAX   =   16'd4000    ,   //cnt_start计数器计数最大值
            CNT_WR_RD_MAX   =   8'd200      ,   //cnt_wr/cnt_rd计数器计数最大值
            CNT_WAIT_MAX    =   28'd500_000 ;   //cnt_wait计数器计数最大值
// wire  define
wire    [7:0]   data_num    ;   //fifo中数据个数

// reg   define
reg     [7:0]   cnt_wr          ;   //写触发有效信号保持时间计数器
reg             write_valid     ;   //写触发有效信号
reg     [7:0]   cnt_rd          ;   //读触发有效信号保持时间计数器
reg             read_valid      ;   //读触发有效信号
reg     [15:0]  cnt_start       ;   //单字节数据读/写时间间隔计数
reg     [7:0]   wr_i2c_data_num ;   //写入i2c设备的数据个数
reg     [7:0]   rd_i2c_data_num ;   //读出i2c设备的数据个数
reg             fifo_rd_valid   ;   //fifo读有效信号
reg     [27:0]  cnt_wait        ;   //fifo读使能信号间时间间隔计数
reg             fifo_rd_en      ;   //fifo读使能信号
reg     [7:0]   rd_data_num     ;   //读出fifo数据个数

//cnt_wr:写触发有效信号保持时间计数器,计数写触发有效信号保持时钟周期数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_wr    <=  8'd0;
    else    if(write_valid == 1'b0)
        cnt_wr    <=  8'd0;
    else    if(write_valid == 1'b1)
        cnt_wr    <=  cnt_wr + 1'b1;

//write_valid:写触发有效信号
//由于写触发信号保持时间为一个系统时钟周期(20ns),
//不能被i2c驱动时钟i2c_scl正确采集,延长写触发信号生成写触发有效信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        write_valid    <=  1'b0;
    else    if(cnt_wr == (CNT_WR_RD_MAX - 1'b1))
        write_valid    <=  1'b0;
    else    if(write == 1'b1)
        write_valid    <=  1'b1;

//cnt_rd:读触发有效信号保持时间计数器,计数读触发有效信号保持时钟周期数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_rd    <=  8'd0;
    else    if(read_valid == 1'b0)
        cnt_rd    <=  8'd0;
    else    if(read_valid == 1'b1)
        cnt_rd    <=  cnt_rd + 1'b1;

//read_valid:读触发有效信号
//由于读触发信号保持时间为一个系统时钟周期(20ns),
//不能被i2c驱动时钟i2c_scl正确采集,延长读触发信号生成读触发有效信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        read_valid    <=  1'b0;
    else    if(cnt_rd == (CNT_WR_RD_MAX - 1'b1))
        read_valid    <=  1'b0;
    else    if(read == 1'b1)
        read_valid    <=  1'b1;

//cnt_start:单字节数据读/写操作时间间隔计数
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_start   <=  16'd0;
    else    if((wr_en == 1'b0) && (rd_en == 1'b0))
        cnt_start   <=  16'd0;
    else    if(cnt_start == (CNT_START_MAX - 1'b1))
        cnt_start   <=  16'd0;
    else    if((wr_en == 1'b1) || (rd_en == 1'b1))
        cnt_start   <=  cnt_start + 1'b1;

//i2c_start:i2c读/写触发信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_start   <=  1'b0;
    else    if((cnt_start == (CNT_START_MAX - 1'b1)))
        i2c_start   <=  1'b1;
    else
        i2c_start   <=  1'b0;

//wr_en:输出写使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_en   <=  1'b0;
    else    if((wr_i2c_data_num == DATA_NUM - 1) 
                && (i2c_end == 1'b1) && (wr_en == 1'b1))
        wr_en   <=  1'b0;
    else    if(write_valid == 1'b1)
        wr_en   <=  1'b1;

//wr_i2c_data_num:写入i2c设备的数据个数
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_i2c_data_num <=  8'd0;
    else    if(wr_en == 1'b0)
        wr_i2c_data_num <=  8'd0;
    else    if((wr_en == 1'b1) && (i2c_end == 1'b1))
        wr_i2c_data_num <=  wr_i2c_data_num + 1'b1;

//rd_en:输出读使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en   <=  1'b0;
    else    if((rd_i2c_data_num == DATA_NUM - 1) 
                && (i2c_end == 1'b1) && (rd_en == 1'b1))
        rd_en   <=  1'b0;
    else    if(read_valid == 1'b1)
        rd_en   <=  1'b1;

//rd_i2c_data_num:写入i2c设备的数据个数
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_i2c_data_num <=  8'd0;
    else    if(rd_en == 1'b0)
        rd_i2c_data_num <=  8'd0;
    else    if((rd_en == 1'b1) && (i2c_end == 1'b1))
        rd_i2c_data_num <=  rd_i2c_data_num + 1'b1;

//byte_addr:输出读/写地址
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        byte_addr   <=  16'h00_5A;
    else    if((wr_en == 1'b0) && (rd_en == 1'b0))
        byte_addr   <=  16'h00_5A;
    else    if(((wr_en == 1'b1) || (rd_en == 1'b1)) && (i2c_end == 1'b1))
        byte_addr   <=  byte_addr + 1'b1;

//wr_data:输出待写入i2c设备数据
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_data <=  8'h01;
    else    if(wr_en == 1'b0)
        wr_data <=  8'h01;
    else    if((wr_en == 1'b1) && (i2c_end == 1'b1))
        wr_data <=  wr_data + 1'b1;

//fifo_rd_valid:fifo读有效信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        fifo_rd_valid  <=  1'b0;
    else    if((rd_data_num == DATA_NUM)
                && (cnt_wait == (CNT_WAIT_MAX - 1'b1)))
        fifo_rd_valid  <=  1'b0;
    else    if(data_num == DATA_NUM)
        fifo_rd_valid  <=  1'b1;

//cnt_wait:fifo读使能信号间时间间隔计数,计数两fifo读使能间的时间间隔
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_wait    <=  28'd0;
    else    if(fifo_rd_valid == 1'b0)
        cnt_wait    <=  28'd0;
    else    if(cnt_wait == (CNT_WAIT_MAX - 1'b1))
        cnt_wait    <=  28'd0;
    else    if(fifo_rd_valid == 1'b1)
        cnt_wait    <=  cnt_wait + 1'b1;

//fifo_rd_en:fifo读使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        fifo_rd_en <=  1'b0;
    else    if((cnt_wait == (CNT_WAIT_MAX - 1'b1))
                && (rd_data_num < DATA_NUM))
        fifo_rd_en <=  1'b1;
    else
        fifo_rd_en <=  1'b0;

//rd_data_num:自fifo中读出数据个数计数
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_data_num <=  8'd0;
    else    if(fifo_rd_valid == 1'b0)
        rd_data_num <=  8'd0;
    else    if(fifo_rd_en == 1'b1)
        rd_data_num <=  rd_data_num + 1'b1;

//------------- fifo_read_inst -------------
fifo_data   fifo_read_inst
(
    .clock  (i2c_clk            ),  //输入时钟信号,频率1MHz,1bit
    .data   (rd_data            ),  //输入写入数据,1bit
    .rdreq  (fifo_rd_en         ),  //输入数据读请求,1bit
    .wrreq  (i2c_end && rd_en   ),  //输入数据写请求,1bit

    .q      (fifo_rd_data       ),  //输出读出数据,1bit
    .usedw  (data_num           )   //输出fifo内数据个数,1bit
);

endmodule

eeprom_byte_rd_wr

`timescale  1ns/1ns

module  eeprom_byte_rd_wr
(
    input   wire            sys_clk     ,   //输入工作时钟,频率50MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效
    input   wire            key_wr      ,   //按键写
    input   wire            key_rd      ,   //按键读

    inout   wire            sda         ,   //串行数据
    output  wire            scl         ,   //串行时钟
    output  wire            stcp        ,   //输出数据存储器时钟
    output  wire            shcp        ,   //移位寄存器的时钟输入
    output  wire            ds          ,   //串行数据输入
    output  wire            oe              //使能信号
);

//wire  define
wire            read        ;   //读数据
wire            write       ;   //写数据
wire    [7:0]   po_data     ;   //fifo输出数据
wire    [7:0]   rd_data     ;   //eeprom读出数据
wire            wr_en       ;
wire            rd_en       ;
wire            i2c_end     ;
wire            i2c_start   ;
wire    [7:0]   wr_data     ;
wire    [15:0]  byte_addr   ;
wire            i2c_clk     ;

//------------- key_wr_inst -------------
key_filter  key_wr_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟50Mhz
    .sys_rst_n  (sys_rst_n  ),  //全局复位
    .key_in     (key_wr     ),  //按键输入信号

    .key_flag   (write      )   //key_flag为1时表示按键有效,0表示按键无效
);

//------------- key_rd_inst -------------
key_filter  key_rd_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟50Mhz
    .sys_rst_n  (sys_rst_n  ),  //全局复位
    .key_in     (key_rd     ),  //按键输入信号

    .key_flag   (read       )   //key_flag为1时表示按键有效,0表示按键无效
);

//------------- i2c_rw_data_inst -------------
i2c_rw_data i2c_rw_data_inst
(
    .sys_clk     (sys_clk   ),  //输入系统时钟,频率50MHz
    .i2c_clk     (i2c_clk   ),  //输入i2c驱动时钟,频率1MHz
    .sys_rst_n   (sys_rst_n ),  //输入复位信号,低有效
    .write       (write     ),  //输入写触发信号
    .read        (read      ),  //输入读触发信号
    .i2c_end     (i2c_end   ),  //一次i2c读/写结束信号
    .rd_data     (rd_data   ),  //输入自i2c设备读出的数据

    .wr_en       (wr_en     ),  //输出写使能信号
    .rd_en       (rd_en     ),  //输出读使能信号
    .i2c_start   (i2c_start ),  //输出i2c读/写触发信号
    .byte_addr   (byte_addr ),  //输出i2c设备读/写地址
    .wr_data     (wr_data   ),  //输出写入i2c设备的数据
    .fifo_rd_data(po_data   )   //输出自fifo中读出的数据
);

//------------- i2c_ctrl_inst -------------
i2c_ctrl
#(
    .DEVICE_ADDR    (7'b1010_011     ), //i2c设备器件地址
    .SYS_CLK_FREQ   (26'd50_000_000  ), //i2c_ctrl模块系统时钟频率
    .SCL_FREQ       (18'd250_000     )  //i2c的SCL时钟频率
)
i2c_ctrl_inst
(
    .sys_clk     (sys_clk   ),   //输入系统时钟,50MHz
    .sys_rst_n   (sys_rst_n ),   //输入复位信号,低电平有效
    .wr_en       (wr_en     ),   //输入写使能信号
    .rd_en       (rd_en     ),   //输入读使能信号
    .i2c_start   (i2c_start ),   //输入i2c触发信号
    .addr_num    (1'b1      ),   //输入i2c字节地址字节数
    .byte_addr   (byte_addr ),   //输入i2c字节地址
    .wr_data     (wr_data   ),   //输入i2c设备数据

    .rd_data     (rd_data   ),   //输出i2c设备读取数据
    .i2c_end     (i2c_end   ),   //i2c一次读/写操作完成
    .i2c_clk     (i2c_clk   ),   //i2c驱动时钟
    .i2c_scl     (scl       ),   //输出至i2c设备的串行时钟信号scl
    .i2c_sda     (sda       )    //输出至i2c设备的串行数据信号sda
);

//------------- seg7_dynamic_inst -------------
seg_595_dynamic seg_595_dynamic_inst
(
    .sys_clk     (sys_clk   ), //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n ), //复位信号,低有效
    .data        (po_data   ), //数码管要显示的值
    .point       (          ), //小数点显示,高电平有效
    .seg_en      (1'b1      ), //数码管使能信号,高电平有效
    .sign        (          ), //符号位,高电平显示负号

    .stcp        (stcp      ), //数据存储器时钟
    .shcp        (shcp      ), //移位寄存器时钟
    .ds          (ds        ), //串行数据输入
    .oe          (oe        )  //使能信号
);

endmodule

tb_eeprom_byte_rd_wr

`timescale  1ns/1ns

module  tb_eeprom_byte_rd_wr();
//wire define
wire            scl ;
wire            sda ;
wire            stcp;
wire            shcp;
wire            ds  ;
wire            oe  ;

//reg define
reg           clk   ;
reg           rst_n ;
reg           key_wr;
reg           key_rd;

//时钟、复位信号
initial
  begin
    clk     =   1'b1  ;
    rst_n   <=  1'b0  ;
    key_wr  <=  1'b1  ;
    key_rd  <=  1'b1  ;
    #200
    rst_n   <=  1'b1  ;
    #1000
    key_wr  <=  1'b0  ;
    key_rd  <=  1'b1  ;
    #400
    key_wr  <=  1'b1  ;
    key_rd  <=  1'b1  ;
    #20000000
    key_wr  <=  1'b1  ;
    key_rd  <=  1'b0  ;
    #400
    key_wr  <=  1'b1  ;
    key_rd  <=  1'b1  ;
    #40000000
    $stop;
  end

always  #10 clk = ~clk;

defparam eeprom_byte_rd_wr_inst.key_wr_inst.CNT_MAX = 5;
defparam eeprom_byte_rd_wr_inst.key_rd_inst.CNT_MAX = 5;
defparam eeprom_byte_rd_wr_inst.i2c_rw_data_inst.CNT_WAIT_MAX = 1000;

//-------------eeprom_byte_rd_wr_inst-------------
eeprom_byte_rd_wr   eeprom_byte_rd_wr_inst
(
    .sys_clk        (clk    ),    //输入工作时钟,频率50MHz
    .sys_rst_n      (rst_n  ),    //输入复位信号,低电平有效
    .key_wr         (key_wr ),    //按键写
    .key_rd         (key_rd ),    //按键读

    .sda            (sda    ),    //串行数据
    .scl            (scl    ),    //串行时钟
    .stcp           (stcp   ),   //输出数据存储寄时钟
    .shcp           (shcp   ),   //移位寄存器的时钟输入
    .ds             (ds     ),   //串行数据输入
    .oe             (oe     )
);

//-------------eeprom_inst-------------
M24LC64  M24lc64_inst
(
    .A0     (1'b0       ),  //器件地址
    .A1     (1'b0       ),  //器件地址
    .A2     (1'b0       ),  //器件地址
    .WP     (1'b0       ),  //写保护信号,高电平有效
    .RESET  (~rst_n     ),  //复位信号,高电平有效

    .SDA    (sda        ),  //串行数据
    .SCL    (scl        )   //串行时钟
);

endmodule

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/50976.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flume环境搭建

1、新建一个专门放文件的目录&#xff1a;mkdir /export/server 2、将Flume的安装包上传到/export/server目录并解压&#xff0c;重命名&#xff1a; tar -zxvf apache-flume-1.8.0-bin.tar.gz -C /export/server mv apache-flume-1.8.0-bin flume 3、修改flume-env.sh文件…

[附源码]计算机毕业设计JAVA校园飞毛腿系统

[附源码]计算机毕业设计JAVA校园飞毛腿系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis …

第2-4-10章 规则引擎Drools实战(3)-保险产品准入规则

文章目录9.3 保险产品准入规则9.3.1 决策表9.3.2 规则介绍9.3.3 实现步骤9.3 保险产品准入规则 全套代码及资料全部完整提供&#xff0c;点此处下载 9.3.1 决策表 前面我们编写的规则文件都是drl形式的文件&#xff0c;Drools除了支持drl形式的文件外还支持xls格式的文件&am…

天宇优配|钠电池汽车要来了,续航达500公里!持续高成长股揭秘

钠电池行将进入量产元年。 11月29日&#xff0c;在钠离子电池产业链与规范开展论坛上&#xff0c;宁德年代研究院副院长黄起森表明&#xff0c;在乘用车使用方面&#xff0c;钠离子电池遍及可以满意续航400公里以下的车型需求&#xff0c;宁德年代则通过首创的AB电池体系集成技…

内容、文档和流程数字化如何支持精益原则

内容、文档和流程数字化如何支持精益原则 无论您身处哪个行业&#xff0c;想要在业务中取得真正的成功就必须把客户放在所有决策的中心位置&#xff0c;以便您能够给客户提供最佳服务。同时公司以最高效的方式向前发展也很重要。幸运的是&#xff0c;有一种方法可以确保两者兼而…

SpringCloud系列(四)Nacos 的三个有利配置(服务分级存储 / 环境隔离 / 负载均衡策略))

Nacos 的三个有利配置&#x1f697; Nacos 服务分级存储模型&#x1f697; &#x1f697; 环境隔离 - namespace&#x1f697; &#x1f697; &#x1f697; NacosRule 负载均衡策略我们都知道在京东商城购买商品&#xff0c;如果选择的是京东自营&#xff0c;那么几乎都是次日…

Springboot毕业设计毕设作品,农产品销售系统设计与实现

功能清单 【后台管理功能模块】 系统设置&#xff1a;设置关于我们、联系我们、加入我们、法律声明的信息。 广告管理&#xff1a;设置网站首页轮播图和链接地址。 留言管理&#xff1a;显示用户通过前台留言的列表&#xff0c;支持删除。 会员中心&#xff1a;显示所有注册用户…

Python图像处理【1】图像与视频处理

图像与视频处理0. 前言1. 在 3D 空间中显示 RGB 图像颜色通道1.1 图像表示1.2 在 3D 空间中绘制颜色通道2. 使用 scikit-video 读/写视频文件2.1 scikit-video 库2.2 读/写视频文件2.3 提取视频文件属性2.4 读取并保存视频3. 使用 OpenCV 从相机捕获实时视频4. 实现 Gotham 图像…

[附源码]计算机毕业设计springboot葡萄酒销售管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Python jupyter notebook Katex|Latex

目录 1.行内公式编辑 2.矩阵公式编辑 3.添加括号 4.添加图片 1.行内公式编辑 $\alpha\beta \gamma$点击control回车&#xff1b; 运行结果&#xff1a; 2.矩阵公式编辑 \begin{matrix} a & b &c \\ d & e & f \\ j & h & i \end{matrix} 点击…

基于随机森林实现特征选择降维及回归预测(Matlab代码实现)

目录 摘要&#xff1a; 1.随机森林&#xff1a; 2.随机森林的特征选取&#xff1a; 3.基于Matlab自带的随机森林函数进行特征选取具体步骤 &#xff08;1&#xff09;加载数据 &#xff08;2&#xff09;首先建立随机森林并使用全部特征进行车辆经济性预测 &#xff08;3&#…

Shell脚本学习指南(五)——变量、判断、重复动作

文章目录前言变量与算术变量赋值与环境参数展开展开运算符位置参数特殊变量算术展开退出状态退出状态值if-else-else-fi语句逻辑的NOT、AND与ORtest命令case语句循环for循环while与until循环break与continueshift与选项处理函数前言 变量对于正规程序而言很重要。处理维护有用…

Android: SimpleAdapter+GridView 简单图片展示

1&#xff1a;原理解析&#xff1a; 一个xml放总布局&#xff0c;一个xml放适配器要加载的模板&#xff08;我喜欢这样理解&#xff09;&#xff1b; java中写适配事件&#xff1b; 2&#xff1a;目录&#xff1a; 3&#xff1a;主布局&#xff1a;最重要的是要放一个GridView …

[附源码]Python计算机毕业设计Django港口集团仓库管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Google单元测试框架gtest之官方sample笔记3--值参数化测试

1.7 sample7--接口测试 值参数不限定类型&#xff0c;也可以是类的引用&#xff0c;这就可以实现对类接口的测试&#xff0c;一个基类可以有多个继承类&#xff0c;那么可以测试不同的子类功能&#xff0c;但是只需要写一个测试用例&#xff0c;然后使用参数列表实现对每个子类…

m基于可见光通信系统的RFID接口过程以及ALOHA防碰撞算法的matlab仿真

目录 1.算法描述 2.matlab算法仿真效果 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 射频识别技术(Radio Frequency Identification&#xff0c;RFID)是一种非接触式自动识别技术&#xff0c;与传统的识别方式相比&#xff0c;它无需直接接触、无需光学可视、无需人工干预即…

产品经理的七大定律的总结

最近学习了产品经理的七大定律&#xff0c;这些设计准则都基于人类心理学&#xff1a;人们如何感知、学习、推理、记忆&#xff0c;以及把意图转换为行动。 1、菲茨&#xff08;Paul Fitt&#xff09;定律 菲茨定律的对于产品设计时的启示&#xff1a; 1&#xff09;按钮等可…

SpringBoot 3.0 新特性,内置声明式HTTP客户端

http interface 从 Spring 6 和 Spring Boot 3 开始&#xff0c;Spring 框架支持将远程 HTTP 服务代理成带有特定注解的 Java http interface。类似的库&#xff0c;如 OpenFeign 和 Retrofit 仍然可以使用&#xff0c;但 http interface 为 Spring 框架添加内置支持。 什么是…

steam deck科普、上手教程及模拟器配置指南

steam_deck前言 早在2021年得时候&#xff0c;坊间就开始流传steam deck这个东西要问世了。但是中途跳了几次票&#xff0c;直到2022年2月&#xff0c;第一批steam deck才正式面向大众玩家。在熟悉steam deck之前&#xff0c;我们有必要了解如下的知识: Steam 准确来说&…

G1D27-deberta右键创建md文档

回家啦&#xff01;&#xff01;&#xff01;中午的炒饭太好吃了&#xff01;&#xff01;吃的好撑&#xff01;&#xff01;回家后和mm去了超市&#xff0c;买了冰淇淋、薯片和水果&#xff0c;好开心&#xff01;&#xff01;&#xff01; 下午睡了一会觉&#xff0c;真的好舒…