Tabla de contenido
Cuatro, diseño de sistema de lectura y escritura EEPROM
1. Descripción general del sistema
2. FIFO_CTRL (módulo de control de escritura de datos)
3. I2C_wr_ctrl (módulo de control de escritura I2C)
4. I2C_rd_ctrl (módulo de control de lectura I2C)
5. Fifo_ctrl2 (módulo de control de lectura de datos)
6. Módulo de nivel superior I2C_eeprom_top
5. Depuración de simulación y verificación a nivel de placa
2. Verificación a nivel de la junta
El enlace de descarga del proyecto para la realización de funciones generales del sistema de lectura y escritura EEPROM (explicado en el próximo blog, incluido el contenido de este artículo) de este tema es el siguiente:
https://download.csdn.net/download/qq_33231534/12503289
Cuatro, diseño de sistema de lectura y escritura EEPROM
1. Descripción general del sistema
Los requisitos de todo el sistema son: primero envíe cuatro bytes de datos a través del asistente de puerto serie, almacene los cuatro bytes de datos en quince, y luego lea y almacene los datos en quince en EEPROM. Tanto la lectura como la escritura de EEPROM utilizan lectura y escritura continuas 4 Después de que se escriben los datos, los 4 bytes de datos que se acaban de escribir en la EEPROM se leen secuencialmente y se almacenan en quince, y luego los datos se leen desde quince y se envían a la exportación, y se muestran en el asistente del puerto serie.
Entre ellos, el módulo I2C_ctrl se ha explicado en el blog anterior. Uart_rx y uart_tx también se describen en detalle en los capítulos del puerto serie y del sistema de adquisición de datos. Son módulos de propósito general y sync_fifo1 y sync_fifo2 son módulos FIFO síncronos, que se pueden lograr mediante el uso de núcleos IP, o Escriba su propio código. El módulo sincrónico de FIFO también es un módulo común. Hay introducciones y códigos en el sistema de adquisición de datos. Más adelante, escribiré un blog sobre FIFO, así que no entraré en detalles aquí.
Fifo_ctrl (módulo de control de escritura de datos): Los datos enviados por el puerto serie son guardados por sync_fifo1, y el módulo Fifo_ctrl es el puente que controla el módulo receptor del puerto serie y el módulo Fifo para garantizar que los datos recibidos por el puerto serie se puedan guardar en Fifo.
I2C_wr_ctrl (módulo de control de escritura I2C): Saque los datos uno por uno de FIFO y escríbalos en EEPROM.
I2C_rd_ctrl (módulo de control de lectura I2C): lee los datos de EEPROM uno por uno y los almacena en FIFO.
Fifo_ctrl2 (módulo de control de lectura de datos): Lea los datos en quince uno por uno y envíelos al módulo de envío del puerto serie.
2. FIFO_CTRL (módulo de control de escritura de datos)
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
data_byte | yo | 8 | Recibir datos del puerto serie |
rx_done | yo | 1 | Puerto serie que recibe el indicador de datos completos |
wrreq | EL | 1 | Fifo solicitud de escritura |
data_to_fifo | EL | 8 | Datos de salida guardados en quince |
el código se muestra a continuación:
//-------------------------------------------------------------------
//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 (módulo de control de escritura I2C)
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
usado | yo | 3 | Número de datos en quince |
q | yo | 8 | datos leídos por FIFO |
wr_data_vaild | yo | 1 | Escriba la señal válida de datos en el módulo I2C_ctrl |
rdreq | EL | 1 | solicitud de lectura Fifo |
reyezuelo | EL | 1 | Habilitación de escritura del módulo I2C_ctrl |
wr_data | EL | 8 | Datos escritos en eeprom |
el código se muestra a continuación:
//-------------------------------------------------------------------
//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 (módulo de control de lectura I2C)
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
hecho | yo | 1 | Indicador de fin de datos de lectura y escritura del módulo I2C_ctrl |
rd_data | yo | 8 | Lectura de datos de EEPROM |
rd_data_vaild | yo | 1 | Leer datos de bits válidos de EEPROM |
wrreq | EL | 1 | Fifo solicitud de escritura |
I2C_rd_data | EL | 8 | Datos que se escribirán en quince |
rd_en | EL | 1 | Habilitación de lectura del módulo I2C_ctrl |
el código se muestra a continuación:
//-------------------------------------------------------------------
//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 (módulo de control de lectura de datos)
Nombre de la señal | E / S | Dígitos | Función descriptiva |
clk | yo | 1 | Reloj del sistema 50MHz |
rst_n | yo | 1 | Reinicio de sistema |
usado | yo | 3 | Número de datos en quince |
q | yo | 8 | Datos leídos en quince |
tx_done | yo | 1 | El puerto serie envía una bandera de finalización de datos |
rdreq | EL | 1 | solicitud de lectura Fifo |
send_en | EL | 1 | Señal de habilitación de envío de puerto serie |
data_send | EL | 8 | Datos enviados por puerto serie |
el código se muestra a continuación:
//-------------------------------------------------------------------
//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. Módulo de nivel superior I2C_eeprom_top
Cree una instancia de los módulos anteriores y conecte las señales, el código es el siguiente:
//-------------------------------------------------------------------
//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. Depuración de simulación y verificación a nivel de placa
1. Depuración de simulación
Envíe 4 bytes de datos desde el puerto serie durante la prueba, 8'b0000_1010, 8'b1000_0101, 8'b0000_1010, 8'b1000_0101, y luego repita nuevamente.
El código de prueba es el siguiente:
`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
Los resultados de la simulación son los siguientes:
Puede ver que los datos enviados por el puerto serie son consistentes con los datos recibidos por el último puerto serie.
2. 板级验证
管脚分配:
由于硬件上没有对 scl 和 sda 信号进行上拉,而I2C协议要求 scl 和 sda 上拉,因此在对管脚分配时可对这两个信号进行弱上拉,具体设置如下:
在分配管脚右侧空白处右击,选择Customize Columns,将 Weak Pull-Up Resistor移到右边,点击OK,再到引脚分配后边将其弱上拉打开,见下图所示:
将jic文件下载到FPGA板子后,打开串口助手,发送四个数据 12 13 12 11,可以看到串口接收到发送的四个数据,验证成功。
测试发现,输入有些数据,接收到会有异常,如下:
在这里,我将别人写好的比较权威的I2C代码移植到我的系统里,测试发现还是这个样子,这里分析了一下,可能是这个测试系统逻辑方面存在不足,但已经通过仿真和部分测试验证了I2C控制器模块的正确性。