Comunicación I2C basada en FPGA (tres) final

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

1. Depuración de simulación

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

Figura: Diagrama de bloques general del sistema de lectura y escritura EEPROM
Figura: Vista RTL del sistema de lectura y escritura EEPROM

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)

Tabla: descripción de la señal del módulo Fifo_ctrl
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)

Tabla: Descripción de la señal del módulo I2C_wr_ctrl
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)

Tabla: Descripción de la señal del módulo I2C_rd_ctrl
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控制器模块的正确性。

 

Supongo que te gusta

Origin blog.csdn.net/qq_33231534/article/details/106594428
Recomendado
Clasificación