基于verilog的i2c协议实现与eeprom读写

i2c通信过程

sda和scl

i2c使用scl、sda这两条线进行通信,通信方向是双向的,通信过程有主从之分,是串行、同步的。可以进行多对多的通信,i2c有5种通信速率:标准模式(100 kbps)、快速模式(400 kbps)和高速模式(3.4 Mbps),另外一些变种实现了低速模式(10 kbps)和快速+模式(1 Mbps)。

i2c为了进行多对多的通信引入了时钟同步+总线仲裁的机制:
SCL同步是由于总线具有线“与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。当所有的节点都发送高电平时,总线才能表现为高电平。正是由于线“与”逻辑功能的原理,当多个节点同时发送时钟信号时,在总线上表现的是统一的时钟信号。
各I2C设备的数据串行线SDA也是线“与”的关系。SDA线的仲裁也是建立在总线具有线“与”逻辑功能的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,退出竞争。SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线
sda可进行传输或者解收数据,因此采用inout端口。主机通过地址找到具体的从机设备并实现数据传输或者接收,任何被寻址的器件都被认为是从机。

器件

在执行数据传输时,器件也可以被看作主器件(主机)或从器件(从机)。主器件是用于启动总线传送数据,并产生时钟的器件,此时任何被寻址的器件均被认为是从器件(一主多从)。在总线上主和从、发和收的关系取决于此时的数据传送方向,而不是恒定的。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件,然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下,主机负责产生定时时钟和终止数据传送。
硬件上都需要接一个上拉电阻到VCC。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,这样,各控制电路虽然挂在同一条总线上,却彼此独立,互不相关.
i2c接口设备有很多,包括温度传感器、模数转换芯片以及eeprom等

sda和scl时序图:
在这里插入图片描述
在这里插入图片描述

读写过程

开始和停止条件:
非传输状态下拉高SCL和SDA,SDA处于下降沿SCL为高电平的时候,表示启动信号。SDA处于上升沿SCL为高电平的时候,表示结束信号。注意,在传输时按照数据位从高到低传输,且数据在scl低电平时改变,数据传输时SDA必须在SCL高电平时保持不变。
对一个器件中的存储单元(包括寄存器)进行读写时,首先要指定存储单元的地址即字地址,然后再向该地址写入内容。该地址为一个或两个字节长度,具体长度由器件内部的存储单元的数量决定,当存储单元数量不超过一个字节所能表示的最大数量(2^8=256)时,用一个字节表示,超过一个字节所能表示的最大数量时,就需要用两个字节来表示

下面给出读写的基本流程:

写数据
确认器件地址,写入字地址,将sda设置为out,发送写命令等待从机应答。收到应答后开始写入数据,直到写完数据后主机发送停止数据结束发送过程。在写过程中,只有发送ack或者nack是不受主机控制的
1、Master在SCL为高电平期间,拉低SDA,发起START。
2、Master发送设备地址(7bit)和写操作0(1bit),等待ACK。
3、对应的Slave回应ACK。
4、Master发送寄存器字地址(8bit),等待ACK。
5、对应的Slave回应ACK。
6、Master发送数据(8bit),也就是要写入Slave寄存器中的数据,等待ACK。
7、对应的Slave回应ACK。
8、其中的6,7步可重复执行多次,即按顺序对多个寄存器进行写操作。如果写入的地址是连续的,那么在得到初始的写入地址后继续向下一个地址写入,不必重新传入地址。连续写又被称为页写
9、Master发起STOP。
在这里插入图片描述
读数据:
将sda设置为in,发送读命令等待主机应答。收到应答后开始读入数据,直到写完数据后主机停止读取数据并发送停止信号 。在读过程中,发送ack或者nack以及接收数据是不受主机控制的
1、Master在SCL为高电平期间,拉低SDA,发起START。
2、Master发送设备地址(7bit)和写操作0(1bit),等待ACK。
3、Slave发送ACK。
4、Master发送寄存器字地址(8bit)并释放SDA信号线,slave控制sda低电平并发送ACK。如果是nack则slave控制sda高电平
5、Slave发ACK。
6、Master发起START。
7、Master发送I2C设备地址(7bit)和读操作1(1bit),等待ACK。
8、Slave发送ACK。
9、Slave发送data(以字节为单位),即对应寄存器中的值。
10、Master发送ACK。
11、第9步和第10步可重复进行多次,即按顺序读多个寄存器。
在这里插入图片描述

在通信中还可能需要知道从机内部的寄存器位置(字地址),这样才能把数据成功传输到我们想要的位置,在传输时每一个byte后面附带一个ack或者nack信号
发送模块应该具有以下接口:clk、rtsn、send_en(用来提示开始发送)、device_addr(设备地址,不同设备位宽不同)、word_addr(字节地址,一般是8位)、data_w(发送有效数据,8位)、done(传输完成信号)、scl串行时钟线与sda串行信号线
在这里插入图片描述lk、rtsn、send_en(用来提示开始发送)、device_addr(设备地址,不同设备位宽不同)、word_addr(字节地址,一般是8位)、data_r(接收有效数据,8位)、done(传输完成信号)、scl串行时钟线与sda串行信号线
接收模块的定义类似,在这里插入图片描述

eeprom

eeprom支持断电后保持数据并可以修改,实验的芯片型号是at24c64,容量为65536bit
器件地址位宽8位, 前4位固定等于1010,5-7位可自行配置所以最大可以支持8个设备,第8位是读写控制位,写为0读为1。sda的控制在fpga和eeprom之间切换

实验任务与代码设计

实验任务是先向EEPROM(AT24C64)的存储器地址0至255分别写入数据0~255; 写完之后再读取存储器地址0-255中的数据,若读取的值全部正确则LED灯常亮,否则LED灯闪烁。

在这里插入图片描述
状态机设计:
在这里插入图片描述
我们把发送接收模块合在一起编写

module i2c_driver(
input clk,input rstn,
inout sda,output reg scl,
input en,input[15:0] device_addr,
input wl_rh,input bit_ctrl,
output reg ack, input  [7:0]data_w,
output reg[7:0]data_r,
output reg done,output reg dri_clk  //driver clock
);
parameter clk_freq=50_000_000,  i2c_freq= 250_000,
bps_cnt=(clk_freq/i2c_freq)>>2'd3,slav_addr=7'b1_010_000;
localparam 
st_idle     =8'b0000_0001,st_slv_addr  =8'b0000_0010,
st_addr16_wr=8'b0000_0100,st_addr8_wr =8'b0000_1000,
st_data_wr  =8'b0001_0000,st_addr_rd  =8'b0010_0000,
st_data_rd  =8'b0100_0000,st_stop     =8'b1000_0000;
wire sda_in;reg sda_out;reg  sda_dir;
reg[7:0]cstate,nstate;
reg[7:0]data_rd,data_wr_d0;
reg[15:0]addr_t;
reg  done_flag,wr_flag   ; //写标志
reg[6:0]cnt;reg[9:0]clk_cnt;
assign  sda  = sda_dir ?  sda_out : 1'bz, 
 sda_in = sda ;
always @(posedge dri_clk or negedge rstn) begin
    if(!rstn)
    cstate<=st_idle;
    else cstate<=nstate;
end
always @(posedge clk or negedge rstn) begin
    if(!rstn)
    begin
        dri_clk<='d0;
        clk_cnt<='d0;
    end
    else if(clk_cnt==bps_cnt-'d1)
        begin
            clk_cnt<='d0;
            dri_clk<=~dri_clk;
        end
    else clk_cnt<=clk_cnt+'d1;
end
always @(*) begin
    if(!rstn)
    nstate=st_idle;
    else begin
        case(cstate)
st_idle:
    if(en)
    nstate=st_slv_addr;
    else ; 
st_slv_addr:
    if(done_flag)begin
        if(bit_ctrl)// bit_ctrl=1,addr width=16 else addr width=8
        nstate=st_addr16_wr;
        else  nstate=st_addr8_wr;
    end
    else ;
st_addr16_wr:begin
    if(done_flag)nstate=st_addr8_wr;
    else ;
end
st_addr8_wr:begin
    if(done_flag)begin
        if(wr_flag==1'b0)               //读写判断
        nstate = st_data_wr;
    else
        nstate = st_addr_rd;
    end
    else ;
end 
st_data_wr :begin
    if(done_flag)
    nstate=st_stop;
    else ;
end 
st_addr_rd:begin
    if(done_flag)
    nstate=st_data_rd;
    else ;
end  
st_data_rd  :begin
    if(done_flag)
    nstate=st_stop;
    else ;
end
st_stop :begin
    if(done_flag)
    nstate=st_idle;
    else ;
end
default : nstate=st_idle;    
        endcase
    end
end
always @(posedge dri_clk or negedge rstn) begin
    if(!rstn)
    scl<='d1;
    else case(cstate)
    st_idle: scl<='d1;
st_slv_addr :begin
case(cnt)
7'd3,7'd7,7'd11,7'd15,7'd19,7'd23,
7'd27,7'd31,7'd35,7'd39:scl<='d0; 
7'd5,7'd9,7'd13,7'd17,7'd21,
7'd25,7'd29,7'd33,7'd37:scl<='d1;
default :;
endcase
end
st_addr_rd:begin
    case(cnt)
7'd1,7'd5,7'd9,7'd13,7'd17,7'd21,
7'd25,7'd29,7'd33,7'd37:scl<='d1;
7'd3,7'd7,7'd11,7'd15,7'd19,7'd23,
7'd27,7'd31,7'd35,7'd39:scl<='d0; 
default :;endcase
 end
st_addr16_wr, st_addr8_wr,
st_data_wr,st_data_rd: begin
case(cnt)
7'd1,7'd5,7'd9,7'd13,7'd17,7'd21,
7'd25,7'd29,7'd33:scl<='d1;
7'd3,7'd7,7'd11,7'd15,7'd19,7'd23,
7'd27,7'd31,7'd35:scl<='d0; 
default:;
endcase
end
st_stop:if(cnt==7'd1)scl<='d1; else ;
default :; 
endcase       
end

always @(posedge dri_clk or negedge rstn) begin
    if(!rstn)
    begin
        sda_out<='d1;
        sda_dir<='d1;
        ack<='d0;
        cnt<='d0;
        done<='d0;
        done_flag<='d0;
        data_r<='d0;
        data_rd<='d0;
        data_wr_d0<='d0;
        addr_t<='d0;
        wr_flag<='d0;
    end
    else begin
        done_flag<='d0;
        cnt<=cnt+1'b1;
       case(cstate) 
st_idle:begin
    sda_out<='d1;
    sda_dir<='d1;
    done<='d0;
    cnt<='d0;
    if(en)begin// store data when en
        addr_t<=device_addr;
        data_wr_d0<=data_w;
        wr_flag<=wl_rh;
        ack<='d0;
    end
    else ;
end     
st_slv_addr :begin 
    case(cnt)
7'd1:sda_out<='d0;7'd4 : sda_out <= slav_addr[6];
7'd8 : sda_out <= slav_addr[5];7'd12:sda_out <= slav_addr[4];
7'd16 : sda_out <= slav_addr[3];7'd20 : sda_out <= slav_addr[2];
7'd24 : sda_out <= slav_addr[1];7'd28 : sda_out <= slav_addr[0];
7'd32:sda_out<='d0;
7'd36: begin                     
    sda_dir <= 1'b0;sda_out <= 1'b1;                         
end 
7'd38: begin                     //从机应答 
    done_flag <= 1'b1;
    if(sda_in )           //高电平表示nack
        ack <= 1'b1;         //拉高应答标志位     
end
7'd39:cnt<='d0;    
default :; 
    endcase
end
st_addr16_wr:begin
    case(cnt)
7'd0:begin
sda_dir<='d1;
sda_out<=addr_t[15];   
end 
7'd4:sda_out<=addr_t[14];  7'd8:sda_out<=addr_t[13];
7'd12:sda_out<=addr_t[12];7'd16:sda_out<=addr_t[11];
7'd20:sda_out<=addr_t[10];7'd24:sda_out<=addr_t[9];
7'd28:sda_out<=addr_t[8];
7'd32: begin                     
sda_dir <= 1'b0;sda_out <= 1'b1;   end   
7'd34: begin
done_flag<='d1;if(sda_in) ack<='d1;end
7'd35:cnt<='d0;
default:;    
endcase
end
st_addr8_wr :begin
    case(cnt)
    7'd0:begin
    sda_dir<='d1;
    sda_out<=addr_t[7];   
    end 
    7'd4:sda_out<=addr_t[6];  7'd8:sda_out<=addr_t[5];
    7'd12:sda_out<=addr_t[4];7'd16:sda_out<=addr_t[3];
    7'd20:sda_out<=addr_t[2];7'd24:sda_out<=addr_t[1];
    7'd28:sda_out<=addr_t[0];
    7'd32: begin                     
        sda_dir <= 1'b0;sda_out <= 1'b1;   end   
    7'd34: begin
        done_flag<='d1;if(sda_in) ack<='d1;end
    7'd35:cnt<='d0;
    default:;    
    endcase
end
st_data_wr :begin
    case(cnt)
    7'd0:begin
    sda_dir<='d1;
    sda_out<=data_wr_d0[7];   
    end 
    7'd4:sda_out<=data_wr_d0[6];  7'd8:sda_out<=data_wr_d0[5];
    7'd12:sda_out<=data_wr_d0[4];7'd16:sda_out<=data_wr_d0[3];
    7'd20:sda_out<=data_wr_d0[2];7'd24:sda_out<=data_wr_d0[1];
    7'd28:sda_out<=data_wr_d0[0];
    7'd32: begin                     
        sda_dir <= 1'b0;sda_out <= 1'b1;   end   
    7'd34: begin
        done_flag<='d1;if(sda_in) ack<='d1;end
    7'd35:cnt<='d0;
    default:;    
    endcase   
end 
st_addr_rd:begin
    case(cnt)
7'd0:begin
sda_dir<='d1;sda_out<='d1;end
7'd2:sda_out<='d0;7'd4:sda_out <= slav_addr[6];
7'd8:sda_out <= slav_addr[5];7'd12:sda_out <= slav_addr[4];
7'd16:sda_out <= slav_addr[3];7'd20:sda_out <= slav_addr[2];
7'd24:sda_out <= slav_addr[1];7'd28:sda_out <= slav_addr[0];
7'd32:sda_out<='d1;
7'd36: begin                     
    sda_dir <= 1'b0;sda_out <= 1'b1;                         
end 
7'd38: begin                     //从机应答 
    done_flag <= 1'b1;
    if(sda_in )           //高电平表示未应答
        ack <= 1'b1;         //拉高应答标志位     
end 
7'd39:cnt<='d0;   
default :;
    endcase    
end     
st_data_rd :begin
case(cnt)
7'd0:sda_dir<='d0;7'd1:data_rd[7]<=sda_in;
7'd5:data_rd[6]<=sda_in;7'd9:data_rd[5]<=sda_in;
7'd13:data_rd[4]<=sda_in;7'd17:data_rd[3]<=sda_in;
7'd21:data_rd[2]<=sda_in;7'd25:data_rd[1]<=sda_in;
7'd29:data_rd[0]<=sda_in;  
7'd32: begin
    sda_dir <= 1'b1;             
    sda_out <= 1'b1;
end
7'd34:done_flag<='d1;
7'd35: begin data_r<=data_rd; cnt<='d0; end
default:;    
endcase
end 
st_stop  :begin
    case(cnt)
7'd0:begin sda_dir<='d1 ;    sda_out<='d0;end
7'd3:sda_out<='d1; 
7'd15:done_flag<='d1;
7'd16: begin done<='d1;cnt<='d0; end
    endcase
end
default:;   endcase
end
end
endmodule

tesdtbench文件:

`timescale 1ns/1ns
module i2c_driver_tb();
parameter T=20,wr_cycle  = 10_000;
reg clk, rstn,en,wl_rh,bit_ctrl;
reg[15:0]device_addr;
reg[7:0]data_w;
reg   [3:0]  flow_cnt  ;
reg   [13:0] delay_cnt ;
wire scl,ack,done, dri_clk,sda;
wire [7:0]data_r; 
always #(T/2)clk=~clk;
initial begin
    clk=0;
    rstn=0;
    #(T+1) rstn=1;
end
always @(posedge dri_clk or negedge rstn) begin
    if(!rstn)
    begin
    en<='d0;bit_ctrl<='d0;
    wl_rh<='d0;device_addr<='d0;
    data_w<='d0;delay_cnt<='d0;
    flow_cnt<='d0;
    end
    else begin
    case(flow_cnt)  
    'd0:flow_cnt<=flow_cnt+1'b1;
    'd1:begin
     en<='d1;
     bit_ctrl<='d1;
     wl_rh<='d0;
     device_addr<='h0555;
     data_w<=8'h9c;
     flow_cnt<=flow_cnt+1'b1;
     end
     'd2:begin
        en<='d0;
        flow_cnt<=flow_cnt+'d1;
     end
     'd3:begin
        if(done)flow_cnt<=flow_cnt+'d1;
     end
     'd4:begin
        delay_cnt<=delay_cnt+'d1;
        if(delay_cnt==wr_cycle-'d1)// add delay_cnt until finish write cycle
        flow_cnt<=flow_cnt+'d1;
     end
     'd5:begin
        en<='d1;
        bit_ctrl<='d1;
        wl_rh<='d1;
        device_addr<='h0555;
        data_w<=8'h3a;
        flow_cnt<=flow_cnt+1'b1;
        end
    'd6:begin
            en<='d0;
            flow_cnt<=flow_cnt+1'b1;
    end
    'd7:if(done) flow_cnt<=flow_cnt+1'b1;
    default :;   
    endcase  
    end  
end
pullup(sda);
i2c_driver i2c_driver_u(
    .clk(clk),
    .rstn(rstn),
    .sda(sda),
    .scl(scl),
    .en(en),
    .device_addr(device_addr),
    .wl_rh(wl_rh),
    .bit_ctrl(bit_ctrl),
    .ack(ack), 
    .data_r(data_r),
    .data_w(data_w),
    .done(done),
    .dri_clk(dri_clk)  
);
EEPROM_AT24C64 eeprom_u(
    .scl(scl),
    .sda(sda)
);
endmodule

仿真结果:
在这里插入图片描述

eeprom模块:

module eeprom_driver(
input clk,rstn,
output reg wl_rh,en,
output reg[15:0]device_addr,
output reg[7:0]data_w,
input [7:0]data_r,
input done, ack,
output reg rw_done,output reg suce
);
parameter max_byte=256,//max data unm=256
 wait_time= 5_000;//delay 5 ms
reg   [1:0]  flow_cnt  ;
reg   [13:0] delay_cnt ;
always @(posedge clk or negedge rstn) begin
    if(!rstn)
    delay_cnt<='d0;
    else begin
    case(flow_cnt)    
    2'd0:begin
    if(delay_cnt==wait_time-'d1)
        delay_cnt<='d0;
    else delay_cnt<=delay_cnt+'d1;
    end
    default:;
    endcase
    end
end
always @(posedge clk or negedge rstn) begin
    if(!rstn)begin
        flow_cnt<='d0;
        delay_cnt<='d0;
        wl_rh<='d0;
        en<='d0;
        device_addr<='d0;
        data_w<='d0;
        rw_done<='d0;
        suce<='d0;
    end
    else begin
        en<='d0;
        rw_done<='d0;
    case(flow_cnt)
    2'd0:begin
        if(delay_cnt==wait_time-'d1)
        begin
          if(device_addr==max_byte) 
          begin 
            device_addr<='d0;
            wl_rh<='d1;
            flow_cnt<=2'd2;
          end 
          else begin   flow_cnt<=flow_cnt+'d1;
            en<='d1; end//start wr or rd
        end
     end
    2'd1: begin
        if(done)begin
         flow_cnt<=2'd0;
         device_addr<=device_addr+'d1;
         data_w<=data_w+'d1;
        end
    end
    2'd2:begin//when write dfata fulliy, jump to step 2
        flow_cnt<=flow_cnt+'d1;
        en<='d1;
    end
    2'd3:begin//read written data
      if(done)
        if(device_addr[7:0]!=data_r||ack)begin//write data failed
          rw_done<='d1;
          suce<='d0;
        end
      else if(device_addr==max_byte-'d1) begin
        rw_done<='d1;
        suce<='d1;//write all data,finish process
      end
      else begin
        flow_cnt<='d2;
        device_addr<=device_addr+'d1;
    end
      end 
      default:;
    endcase 
    end 
    end
endmodule

led模块

module led_light(
 input clk,rstn,
 input rw_done,input suce,output reg led   
);
parameter clk_freq=50_000_000,  i2c_freq= 250_000,
bps_cnt=(clk_freq/i2c_freq)>>2'd3,
slav_addr=7'b1_010_000,bit_ctrl='d1,
ltime=18'd225_000;
reg rw_done_flag;
reg[24:0]led_cnt;
always @(posedge clk or negedge rstn) begin
    if(!rstn)
    begin
        rw_done_flag<='d0;
    end
    else if(rw_done)
    begin
        rw_done_flag<='d1;
    end
    else ;
end
always @(posedge clk or negedge rstn) begin
    if(!rstn)
    begin
     led_cnt<='d0;   
     led<='d0;
    end
    else begin
        if(rw_done_flag)
        begin
         if(suce)
         led<='d1;
         else begin
    led_cnt<=led_cnt+25'd1;
    if(led_cnt==ltime-'d1)
    begin
        led_cnt<='d0;
        led<=~led;
    end
         end   
        end
        else led<='d0;
    end
end
endmodule

顶层模块:


module e2prom_top(
    input               clk    ,      //系统时钟
    input              rstn  ,      //系统复位
    //eeprom interface
    output              scl    ,      //eeprom的时钟线scl
    inout               sda    ,      //eeprom的数据线sda
    //user interface
    output              led               //led显示
);

//parameter define
parameter    SLAVE_ADDR = 7'b1010000     ; //器件地址(SLAVE_ADDR)
parameter    BIT_CTRL   = 1'b1            ; //字地址位控制参数(16b/8b)
parameter    CLK_FREQ   = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000    ; //I2C的SCL时钟频率
parameter    L_TIME     = 17'd125_000    ; //led闪烁时间参数

//wire define
wire           dri_clk   ; //I2C操作时钟
wire           en  ; //I2C触发控制
wire   [15:0]  device_addr  ; //I2C操作地址
wire   [ 7:0]  data_w; //I2C写入的数据
wire   [ 7:0]  data_r; //I2C读出的数据
wire           done  ; //I2C操作结束标志
wire           ack   ; //I2C应答标志 0:应答 1:未应答
wire           wl_rh ; //I2C读写控制
wire           rw_done   ; //E2PROM读写测试完成
wire           suce ; //E2PROM读写测试结果 0:失败 1:成功 

//*****************************************************
//**                    main code
//*****************************************************

//e2prom读写测试模块
eeprom_driver u_e2prom_rw(
    .clk         (dri_clk   ),  //时钟信号
    .rstn       (rstn),  //复位信号
    //i2c interface
    .en    (en  ),  //I2C触发执行信号
    .wl_rh   (wl_rh ),  //I2C读写控制信号
    .device_addr    (device_addr  ),  //I2C器件内地址
    .data_w  (data_w),  //I2C要写的数据
    .data_r  (data_r),  //I2C读出的数据
    .done    (done  ),  //I2C一次操作完成
    .ack     (ack   ),  //I2C应答标志 
    //user interface
    .rw_done     (rw_done   ),  //E2PROM读写测试完成
    .suce   (suce )   //E2PROM读写测试结果 0:失败 1:成功
);
//i2c驱动模块
i2c_driver #(
    .slav_addr  (SLAVE_ADDR),  //EEPROM从机地址
    .clk_freq    (CLK_FREQ  ),  //模块输入的时钟频率
    .i2c_freq    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u_i2c_dri(
    .clk         (clk   ),  
    .rstn       (rstn ),  
    //i2c interface
    .en    (en  ),  //I2C触发执行信号
    .bit_ctrl    (BIT_CTRL  ),  //器件地址位控制(16b/8b)
    .wl_rh   (wl_rh ),  //I2C读写控制信号
    .device_addr    (device_addr  ),  //I2C器件内地址
    .data_w  (data_w),  //I2C要写的数据
    .data_r  (data_r),  //I2C读出的数据
    .done    (done  ),  //I2C一次操作完成
    .ack     (ack   ),  //I2C应答标志
    .scl         (scl   ),  //I2C的SCL时钟信号
    .sda         (sda   ),  //I2C的SDA信号
    .dri_clk     (dri_clk   )   //I2C操作时钟
);

//led指示模块
led_light  u_led_alarm(
    .clk         (dri_clk   ),  
    .rstn       (rstn), 
    .rw_done     (rw_done   ),  
    .suce   (suce ),
    .led         (led )    
);

endmodule

当然,也可以在顶层添加ila查看分析结果,不过个人试过后发现效果不太好,应该是深度不够导致后面的数据还没有出现

参考博客

协议基本知识:
https://www.cnblogs.com/yjw951012/p/11594694.html
verilog代码实现:
https://www.cnblogs.com/liujinggang/p/9656358.html
eeprom实验:
https://blog.csdn.net/mikusic/article/details/114936770
相关硬件介绍和eeprom实验:
https://www.amobbs.com/thread-5758040-1-5.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值