FPGA-based I2C communication (three) final

table of Contents

Four, EEPROM reading and writing system design

1. Overall system overview

2. fifo_ctrl (write data fifo control module)

3. I2C_wr_ctrl (I2C write control module)

4. I2C_rd_ctrl (I2C read control module)

5. fifo_ctrl2 (read data fifo control module)

6. I2C_eeprom_top top-level module

5. Simulation debugging and board-level verification

1. Simulation debugging

2. Board-level verification


  The project download link for the overall function realization of the EEPROM reading and writing system (explained in the next blog, including the content of this article) of this topic is as follows:
 https://download.csdn.net/download/qq_33231534/12503289


Four, EEPROM reading and writing system design

1. Overall system overview

Figure: Overall block diagram of EEPROM reading and writing system
Figure: RTL view of EEPROM reading and writing system

The requirements of the entire system are: first send four bytes of data through the serial port assistant, store the four bytes of data in fifo, and then read and store the data in fifo into EEPROM. Both EEPROM read and write use continuous read and write 4 After the data is written, the 4 bytes of data just written in the EEPROM are sequentially read out and stored in fifo, and then the data is read out from fifo and sent to the export, and displayed on the serial port assistant.

Among them, the I2C_ctrl module has been explained in the previous blog. uart_rx and uart_tx are also described in detail in the serial port and data acquisition system chapters. They are general-purpose modules. sync_fifo1 and sync_fifo2 are synchronous FIFO modules. You can use IP cores, or Write your own code. The synchronous fifo module is also a common module. There are introductions and codes in the data acquisition system. Later, I will write a blog about FIFO, so I won’t go into details here.

fifo_ctrl (write data fifo control module): The data sent by the serial port is saved by sync_fifo1, and the fifo_ctrl module is the bridge that controls the serial port receiving module and the fifo module to ensure that the data received by the serial port can be saved in fifo.

I2C_wr_ctrl (I2C write control module): Take out data one by one from FIFO and write it into EEPROM.

I2C_rd_ctrl (I2C read control module): Read data from EEPROM one by one and store it in FIFO.

fifo_ctrl2 (read data fifo control module): Read the data in fifo one by one and send to the serial port sending module.

2. fifo_ctrl (write data fifo control module)

Table: fifo_ctrl module signal description
Signal name I / O Digits Function description
clk I 1 System clock 50MHz
rst_n I 1 System reset
data_byte I 8 Receive data from the serial port
rx_done I 1 Serial port receiving data complete flag
wrreq THE 1 fifo write request
data_to_fifo THE 8 Output data saved to fifo

code show as below:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           fifo_ctrl.v
//Last modified Date:  2020/6.6
//Last Version:        
//Descriptions:        串口发送的数据由sync_fifo1来保存,而fifo_ctrl模块就
//                     是控制串口接收模块和fifo模块的桥梁,保证串口接受的数据能保存在fifo中。
//-------------------------------------------------------------------
module fifo_ctrl(
    input                   clk         ,//系统时钟50MHz
    input                   rst_n       ,//系统复位
    input   [ 7: 0]         data_byte   ,//接收到串口发的数据
    input                   rx_done     ,//串口接收数据完成标志
    output  reg             wrreq       ,//fifo写请求
    output  reg [ 7: 0]     data_to_fifo //输出保存到fifo的数据
);

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wrreq <= 0;
    end
    else if(rx_done) begin
        wrreq <= 1;
    end
    else begin
        wrreq <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_to_fifo <= 0;
    end
    else if(rx_done) begin
        data_to_fifo <= data_byte;
    end
    else begin
        data_to_fifo <= data_to_fifo;
    end
end

endmodule

3. I2C_wr_ctrl (I2C write control module)

Table: I2C_wr_ctrl module signal description
Signal name I / O Digits Function description
clk I 1 System clock 50MHz
rst_n I 1 System reset
usedw I 3 Number of data in fifo
q I 8 data read by fifo
wr_data_vaild I 1 Write data valid signal in I2C_ctrl module
rdreq THE 1 fifo read request
wr_en THE 1 I2C_ctrl module write enable
wr_data THE 8 Data written to eeprom

code show as below:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           I2C_wr_ctrl
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        从FIFO中逐个数据取出写入EEPROM
//-------------------------------------------------------------------
module I2C_wr_ctrl(
    input                   clk             ,//系统时钟50MHz
    input                   rst_n           ,//系统复位
    input   [2:0]           usedw           ,//fifo中数据个数
    input   [7:0]           q               ,//fifo读出的数据
    input                   wr_data_vaild   ,//I2C_ctrl模块中写数据有效信号

    output  reg             rdreq           ,//fifo读请求
    output  reg             wr_en           ,//I2C_ctrl模块写使能
    output  reg [7:0]       wr_data          //写入eeprom的数据
);

reg                    rdreq_r;

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rdreq_r <= 0;
    end
    else begin
        rdreq_r <= rdreq;
    end
end
always  @(*)begin
    if(usedw>=4 || wr_data_vaild) begin
        rdreq = 1;
    end
    else begin
        rdreq = 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wr_en <= 0;
    end
    else if(usedw>=4) begin
        wr_en <= 1;
    end
    else begin
        wr_en <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wr_data <= 0;
    end
    else if(rdreq_r) begin
        wr_data <= q;
    end
    else begin
        wr_data <= wr_data;
    end
end

endmodule

4. I2C_rd_ctrl (I2C read control module)

Table: I2C_rd_ctrl module signal description
Signal name I / O Digits Function description
clk I 1 System clock 50MHz
rst_n I 1 System reset
done I 1 I2C_ctrl module read and write data end flag
rd_data I 8 Data read from EEPROM
rd_data_vaild I 1 Read data valid bit from EEPROM
wrreq THE 1 fifo write request
I2C_rd_data THE 8 Data to be written to fifo
rd_en THE 1 I2C_ctrl module read enable 

code show as below:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           I2C_rd_ctrl
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        从EEPROM逐个读出数据存入FIFO中
//-------------------------------------------------------------------
module I2C_rd_ctrl(
    input                   clk             ,//系统时钟50MHz
    input                   rst_n           ,//系统复位
    input                   done            ,//I2C_ctrl模块读写数据结束标志
    input   [7:0]           rd_data         ,//从EEPROM读出的数据
    input                   rd_data_vaild   ,//从EEPROM读出数据有效位

    output  reg             wrreq           ,//fifo写请求
    output  reg [7:0]       I2C_rd_data     ,//要写入fifo的数据
    output  reg             rd_en            //I2C_ctrl模块读使能   
);
reg                    flag_rd;

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        flag_rd <= 0;
    end
    else if(done)begin
        flag_rd <= ~flag_rd;
    end
	 else begin
		  flag_rd <= flag_rd;
	 end
	 
end    


always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rd_en <= 0;
    end
    else if(done && flag_rd==0) begin
        rd_en <= 1;
    end
    else begin
        rd_en <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        I2C_rd_data <= 0;
    end
    else if(rd_data_vaild) begin
        I2C_rd_data <= rd_data;
    end
    else begin
        I2C_rd_data <= I2C_rd_data;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        wrreq <= 0;
    end
    else if(rd_data_vaild) begin
        wrreq <= 1;
    end
    else begin
        wrreq <= 0;
    end
end

endmodule

5. fifo_ctrl2 (read data fifo control module)

Signal name I / O Digits Function description
clk I 1 System clock 50MHz
rst_n I 1 System reset
usedw I 3 Number of data in fifo
q I 8 Data read in fifo
tx_done I 1 The serial port sends a data completion flag
rdreq THE 1 fifo read request
send_en THE 1 Serial port send enable signal
data_send THE 8 Data sent by serial port

code show as below:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           fifo_ctrl2.v
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        将fifo中的数据逐个读出发送到串口发送模块
//-------------------------------------------------------------------
module fifo_ctrl2(
    input                   clk         ,//系统时钟50MHz
    input                   rst_n       ,//系统复位
    input   [2:0]           usedw       ,//fifo中数据个数
    input   [7:0]           q           ,//fifo中读出的数据
    input                   tx_done     ,//串口发送一次数据完成标志

    output  reg             rdreq       ,//fifo读请求
    output  reg             send_en     ,//串口发送使能信号
    output  reg [7:0]       data_send    //串口发送的数据
);

reg                    rdreq_r;

always  @(*)begin
    if(rst_n==1'b0)begin
        rdreq = 0;
    end
    else if(usedw>=4 || usedw>0&&tx_done) begin
        rdreq = 1;
    end
    else begin
        rdreq = 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rdreq_r <= 0;
    end
    else begin
        rdreq_r <= rdreq;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        data_send <= 0;
    end
    else if(rdreq_r) begin
        data_send <= q;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        send_en <= 0;
    end
    else if(rdreq_r) begin
        send_en <= 1;
    end
    else begin
        send_en <= 0;
    end
end

endmodule

6. I2C_eeprom_top top-level module

Instantiate the above modules and connect the signals, the code is as follows:

//-------------------------------------------------------------------
//https://blog.csdn.net/qq_33231534 PHF的CSDN   
//File name:           I2C_eeprom_top.v
//Last modified Date:  2020/6/6
//Last Version:        
//Descriptions:        EEPROM读写系统顶层模块
//-------------------------------------------------------------------

module I2C_eeprom_top(
    input                   clk         ,
    input                   rst_n       ,
    input                   rs232_rx    ,

    output                  rs232_tx    ,
    output                  scl         ,
    inout                   sda             
);

wire  [  7: 0]         data_byte        ;
wire                   rx_done          ;
wire                   wrreq1           ;
wire                   rdreq1           ;
wire  [  7: 0]         data_to_fifo     ;
wire  [  2: 0]         usedw1           ;
wire  [  7: 0]         data_to_eeprom   ;
wire                   wr_en            ;
wire                   wr_data_vaild    ;
wire  [  7: 0]         wr_data          ;

wire                   done             ;
wire  [  7: 0]         rd_data          ;
wire                   rd_data_vaild    ;
wire                   wrreq2           ;
wire  [  7: 0]         I2C_rd_data      ;
wire                   rd_en            ;
wire  [  2: 0]         usedw2           ;
wire  [  7: 0]         data_to_uart     ;
wire                   rdreq2           ;
wire                   tx_done          ;
wire                   send_en          ;
wire  [  7: 0]         data_send        ;


UART_Byte_Rx u_UART_Byte_Rx(
    .clk       (clk) ,//系统时钟50MHz
    .rst_n     (rst_n) ,//系统复位
    .rs232_rx  (rs232_rx) ,//串口串行数据发送数据口
    .baud_set  (3'd1) ,//波特率选择信号

    .data_byte (data_byte) ,//并行数据输出
    .rx_done   (rx_done)  //接收1字节数据完成标志,rx_done可以作为输出有效信号使用
);

fifo_ctrl u_fifo_ctrl(
    .clk           (clk),
    .rst_n         (rst_n),
    .data_byte     (data_byte),
    .rx_done       (rx_done),
    .wrreq         (wrreq1),
    .data_to_fifo  (data_to_fifo)  
);

sync_fifo
#(.WIDTH         (8),          //缓存的数据宽度
  .DEPTH         (5),         //缓存的数据深度
  .MAX_DEPTH_BIT (3))           //可设置的最大深度位数7,即最大深度为2^7-1
u_sync_fifo1(
    .clk       (clk), //系统时钟50MHz
    .rst_n     (rst_n), //系统复位
    .wrreq     (wrreq1), //写使能
    .data      (data_to_fifo), //写数据
    .rdreq     (rdreq1), //读使能

    .q         (data_to_eeprom), //读数据
    .empty     (), //空信号
    .full      (), //满信号
    .half      (), //半满信号
    .usedw     (usedw1)  //fifo中剩余数据个数    
);

I2C_wr_ctrl u_I2C_wr_ctrl(
    .clk           (clk) ,
    .rst_n         (rst_n) ,
    .usedw         (usedw1) ,
    .q             (data_to_eeprom) ,
    .wr_data_vaild (wr_data_vaild) ,

    .rdreq         (rdreq1) ,
    .wr_en         (wr_en) ,
    .wr_data       (wr_data)          
);

I2C_ctrl u_I2C_ctrl(
    .clk           (clk) ,
    .rst_n         (rst_n) ,
    .rd_data_num   (6'd4) ,//最大32
    .wr_data_num   (6'd4) ,//最大32
    .word_addr     (16'h05) ,
    .device_addr   (3'b001) ,
    .wr_en         (wr_en) ,
    .wr_data       (wr_data) ,
    .rd_en         (rd_en) ,

    .wr_data_vaild (wr_data_vaild) ,
    .rd_data       (rd_data) ,
    .rd_data_vaild (rd_data_vaild) ,
    .done          (done) ,
    .scl           (scl) ,

    .sda           (sda)   
);

I2C_rd_ctrl u_I2C_rd_ctrl(
    .clk           (clk) ,
    .rst_n         (rst_n) ,
    .done          (done) ,
    .rd_data       (rd_data) ,
    .rd_data_vaild (rd_data_vaild) ,

    .wrreq         (wrreq2) ,
    .I2C_rd_data   (I2C_rd_data) , 
    .rd_en         (rd_en)     
);

sync_fifo
#(.WIDTH         (8),          //缓存的数据宽度
  .DEPTH         (5),         //缓存的数据深度
  .MAX_DEPTH_BIT (3))           //可设置的最大深度位数7,即最大深度为2^7-1
u_sync_fifo2(
    .clk       (clk), //系统时钟50MHz
    .rst_n     (rst_n), //系统复位
    .wrreq     (wrreq2), //写使能
    .data      (I2C_rd_data), //写数据
    .rdreq     (rdreq2), //读使能

    .q         (data_to_uart), //读数据
    .empty     (), //空信号
    .full      (), //满信号
    .half      (), //半满信号
    .usedw     (usedw2)  //fifo中剩余数据个数    
);

fifo_ctrl2 u_fifo_ctrl2(
    .clk       (clk) ,
    .rst_n     (rst_n) ,
    .usedw     (usedw2) ,
    .q         (data_to_uart) ,
    .tx_done   (tx_done) ,

    .rdreq     (rdreq2) ,
    .send_en   (send_en) ,
    .data_send (data_send)     
);

Uart_Byte_Tx u_Uart_Byte_Tx(
    .clk        (clk), //系统时钟
    .rst_n      (rst_n), //系统复位
    .send_en    (send_en), //发送使能
    .data_byte  (data_send), //发送的数据
    .baud_set   (3'd1), //波特率设置
    .rs232_tx   (rs232_tx), //FPGA将数据转换成串行数据发出
    .tx_done    (tx_done), //发送数据完毕标志
    .uart_state ()  //串口发送状态,1为忙,0为空闲
);

endmodule

5. Simulation debugging and board-level verification

1. Simulation debugging

Send 4 bytes of data from the serial port during the test, 8'b0000_1010, 8'b1000_0101, 8'b0000_1010, 8'b1000_0101, and then repeat again.

The test code is as follows:

`timescale 1 ns/ 1 ns
module I2C_eeprom_top_tb();
// constants                                           
// test vector input registers
reg clk;
reg rs232_rx;
reg rst_n;
//reg treg_sda;
// wires                                               
wire rs232_tx;
wire scl;
wire sda;

parameter clk_period = 20;

// assign statements (if any)                          
//assign sda = treg_sda;
I2C_eeprom_top i1 (
// port map - connection between master ports and signals/registers   
	.clk(clk),
	.rs232_rx(rs232_rx),
	.rs232_tx(rs232_tx),
	.rst_n(rst_n),
	.scl(scl),
	.sda(sda)
);
M24LC64 u_M24LC64(
    .A0(1'b1), 
    .A1(1'b0), 
    .A2(1'b0), 
    .WP(1'b0), 
    .SDA(sda), 
    .SCL(scl), 
    .RESET(!rst_n)
);

pullup(sda);

initial clk = 0;
always #(clk_period/2) clk = ~clk;

initial begin
	#1;
	rst_n = 0;
	rs232_rx = 1;
	#(clk_period*5);
	rst_n = 1;
	#(clk_period*3);
	
	repeat(2)begin
	//0000_1010
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	
	//1000_0101
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);	
	end
	#(clk_period*500000);

    repeat(2)begin
	//0000_1010
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	
	//1000_0101
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208*4);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 0;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	rs232_rx = 1;
	#(clk_period*5208);
	end
	#(clk_period*500000);

	$stop;
end
endmodule

The simulation results are as follows:

You can see that the data sent by the serial port is consistent with the data received by the last serial port.

2. 板级验证

管脚分配:

由于硬件上没有对 scl 和 sda 信号进行上拉,而I2C协议要求 scl 和 sda 上拉,因此在对管脚分配时可对这两个信号进行弱上拉,具体设置如下:

在分配管脚右侧空白处右击,选择Customize Columns,将 Weak Pull-Up Resistor移到右边,点击OK,再到引脚分配后边将其弱上拉打开,见下图所示:

 将jic文件下载到FPGA板子后,打开串口助手,发送四个数据 12 13 12 11,可以看到串口接收到发送的四个数据,验证成功。

测试发现,输入有些数据,接收到会有异常,如下:

在这里,我将别人写好的比较权威的I2C代码移植到我的系统里,测试发现还是这个样子,这里分析了一下,可能是这个测试系统逻辑方面存在不足,但已经通过仿真和部分测试验证了I2C控制器模块的正确性。

 

Guess you like

Origin blog.csdn.net/qq_33231534/article/details/106594428