table des matières
Quatre, conception du système de lecture et d'écriture EEPROM
2. fifo_ctrl (module de commande de fifo de données d'écriture)
3. I2C_wr_ctrl (module de contrôle d'écriture I2C)
4. I2C_rd_ctrl (module de contrôle de lecture I2C)
5. fifo_ctrl2 (lire les données du module de contrôle fifo)
6. Module de niveau supérieur I2C_eeprom_top
5. Débogage de la simulation et vérification au niveau de la carte
2. Vérification au niveau du Conseil
Le lien de téléchargement du projet pour la réalisation de la fonction globale du système de lecture et d'écriture EEPROM (expliqué dans le prochain blog, y compris le contenu de cet article) de cette rubrique est le suivant:
https://download.csdn.net/download/qq_33231534/12503289
Quatre, conception du système de lecture et d'écriture EEPROM
1. Aperçu général du système
Les exigences de l'ensemble du système sont les suivantes: envoyer d'abord quatre octets de données via l'assistant de port série, stocker les quatre octets de données dans fifo, puis lire et stocker les données dans fifo dans EEPROM. Les deux EEPROM en lecture et en écriture utilisent la lecture et l'écriture continues 4 Une fois les données écrites, les 4 octets de données qui viennent d'être écrites dans l'EEPROM sont lus séquentiellement et stockés dans fifo, puis les données sont lues à partir de fifo et envoyées à l'exportation, et affichées sur l'assistant de port série.
Parmi eux, le module I2C_ctrl a été expliqué dans le blog précédent. Uart_rx et uart_tx sont également décrits en détail dans les chapitres sur le port série et le système d'acquisition de données. Ce sont des modules à usage général, et sync_fifo1 et sync_fifo2 sont des modules FIFO synchrones, qui peuvent être réalisés en utilisant des cœurs IP, ou Écrivez le code par vous-même. Le module synchrone fifo est aussi un module commun. Il y a des introductions et des codes dans le système d'acquisition de données. J'écrirai un blog sur FIFO dans le futur, donc je ne le répéterai pas ici.
fifo_ctrl (module de contrôle d'écriture de données fifo): Les données envoyées par le port série sont sauvegardées par sync_fifo1, et le module fifo_ctrl est le pont qui contrôle le module de réception du port série et le module fifo pour s'assurer que les données reçues par le port série peuvent être sauvegardées dans fifo.
I2C_wr_ctrl (module de contrôle d'écriture I2C): retirez les données une par une de la FIFO et écrivez-les dans l'EEPROM.
I2C_rd_ctrl (module de contrôle de lecture I2C): Lire les données de l'EEPROM une par une et les stocker dans la FIFO.
fifo_ctrl2 (lire les données du module de contrôle fifo): lire les données dans fifo une par une et les envoyer au module d'envoi du port série.
2. fifo_ctrl (module de commande de fifo de données d'écriture)
Nom du signal | E / S | Chiffres | Description de la fonction |
clk | je | 1 | Horloge système 50 MHz |
rst_n | je | 1 | Réinitialisation du système |
data_byte | je | 8 | Recevoir les données du port série |
rx_done | je | 1 | Port série recevant l'indicateur de données complètes |
Wrreq | LES | 1 | demande d'écriture fifo |
data_to_fifo | LES | 8 | Données de sortie enregistrées dans fifo |
code montrer comme ci-dessous:
//-------------------------------------------------------------------
//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 (module de contrôle d'écriture I2C)
Nom du signal | E / S | Chiffres | Description de la fonction |
clk | je | 1 | Horloge système 50 MHz |
rst_n | je | 1 | Réinitialisation du système |
utiliséw | je | 3 | Nombre de données dans fifo |
q | je | 8 | données lues par fifo |
wr_data_vaild | je | 1 | Ecrire un signal valide de données dans le module I2C_ctrl |
rdreq | LES | 1 | demande de lecture fifo |
roitelet | LES | 1 | Activation d'écriture du module I2C_ctrl |
wr_data | LES | 8 | Données écrites sur eeprom |
code montrer comme ci-dessous:
//-------------------------------------------------------------------
//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 (module de contrôle de lecture I2C)
Nom du signal | E / S | Chiffres | Description de la fonction |
clk | je | 1 | Horloge système 50 MHz |
rst_n | je | 1 | Réinitialisation du système |
terminé | je | 1 | Indicateur de fin de lecture et d'écriture du module I2C_ctrl |
rd_data | je | 8 | Données lues à partir de l'EEPROM |
rd_data_vaild | je | 1 | Lire le bit valide de données de l'EEPROM |
Wrreq | LES | 1 | demande d'écriture fifo |
I2C_rd_data | LES | 8 | Données à écrire sur fifo |
rd_en | LES | 1 | Activation de lecture du module I2C_ctrl |
code montrer comme ci-dessous:
//-------------------------------------------------------------------
//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 (lire les données du module de contrôle fifo)
Nom du signal | E / S | Chiffres | Description de la fonction |
clk | je | 1 | Horloge système 50 MHz |
rst_n | je | 1 | Réinitialisation du système |
utiliséw | je | 3 | Nombre de données dans fifo |
q | je | 8 | Données lues dans fifo |
tx_done | je | 1 | Le port série envoie un indicateur d'achèvement des données |
rdreq | LES | 1 | demande de lecture fifo |
send_en | LES | 1 | Signal d'activation d'envoi du port série |
data_send | LES | 8 | Données envoyées par port série |
code montrer comme ci-dessous:
//-------------------------------------------------------------------
//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. Module de niveau supérieur I2C_eeprom_top
Instanciez les modules ci-dessus et connectez les signaux, le code est le suivant:
//-------------------------------------------------------------------
//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. Débogage de la simulation et vérification au niveau de la carte
1. Débogage de la simulation
Envoyez 4 octets de données depuis le port série pendant le test, 8'b0000_1010, 8'b1000_0101, 8'b0000_1010, 8'b1000_0101, puis recommencez.
Le code de test est le suivant:
`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
Les résultats de la simulation sont les suivants:
Vous pouvez voir que les données envoyées par le port série sont cohérentes avec les données reçues par le dernier port série.
2. 板级验证
管脚分配:
由于硬件上没有对 scl 和 sda 信号进行上拉,而I2C协议要求 scl 和 sda 上拉,因此在对管脚分配时可对这两个信号进行弱上拉,具体设置如下:
在分配管脚右侧空白处右击,选择Customize Columns,将 Weak Pull-Up Resistor移到右边,点击OK,再到引脚分配后边将其弱上拉打开,见下图所示:
将jic文件下载到FPGA板子后,打开串口助手,发送四个数据 12 13 12 11,可以看到串口接收到发送的四个数据,验证成功。
测试发现,输入有些数据,接收到会有异常,如下:
在这里,我将别人写好的比较权威的I2C代码移植到我的系统里,测试发现还是这个样子,这里分析了一下,可能是这个测试系统逻辑方面存在不足,但已经通过仿真和部分测试验证了I2C控制器模块的正确性。