table of Contents
Four, EEPROM reading and writing system design
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
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
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)
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)
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)
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控制器模块的正确性。