所谓数据流跨时钟域即:时钟不同但是时间段内的数据量一定要相同。
常用的数据流跨时钟域可以使用fifo或者ram实现。fifo的实现我们之前文章中fifo资源的介绍中已经详细讲解过了,xilinx FIFO 硬核的结构如下:
但是FIFO实现有一个问题,由于没有地址控制,需要固定时延是不方便,另外就是xilinx IP的fifo深度较大,可能存在资源浪费。
第二种方法就是使用ram自己搭建跨时钟域模块。我们使用bram做数据流跨时钟域。
例示代码如下 ,代码时钟比例为5:4,a时钟每5个clk写入4个数据,b时钟每个clk输出,代码中有两个点要注意:
1、0地址的写是能;
2、有一个防抖处理。
// ============================================================// File Name: cm_cdc_bram// VERSION : V1.0// DATA : 2022/10/4// Author : FPGA干货分享// ============================================================// 功能:数据流使用bram跨时钟域模块// ============================================================ `timescale 1ns/1psmodule cm_cdc_bram ( input wire I_clk_a , ///输入时钟a,500Mhz input wire I_clk_b , ///输入时钟b,400Mhz input wire I_data_a_valid , ///输入时钟b input wire [31:0] I_data_a , ///a时钟输入信号 output wire[31:0] O_data_b ///b时钟输出信号 );// ============================================================// wire reg// ============================================================reg [8:0] S_wr_addr ;reg S_wr_zero_flag ; wire S_wr_zero_flag_rd ;reg [3:0] S_clk_cnt ;reg S_wr_clr_falg_temp ;reg S_wr_clr_falg ;reg [8:0] S_rd_addr ; // ============================================================// main code// ============================================================ // ============================================================// clk_a// ============================================================ always @(posedge I_clk_a) if(I_data_a_valid) S_wr_addr <= S_wr_addr + 9'd1; else S_wr_addr <= S_wr_addr ; always @(posedge I_clk_a) S_wr_zero_flag <= (!(|S_wr_addr)); // ============================================================// clk_b// ============================================================ cm_cdc_1bit cm_cdc_1bit ( .I_clk_a (I_clk_a ) , ///输入时钟a .I_clk_b (I_clk_b ) , ///输入时钟b .I_single_a (S_wr_zero_flag ) , ///a时钟输入信号 .O_single_b (S_wr_zero_flag_rd ) ///b时钟输出信号 ); always @(posedge I_clk_b) if(S_wr_zero_flag_rd & (S_clk_cnt > 4'd2)) S_clk_cnt <= 4'd2; else S_clk_cnt <= S_clk_cnt + 4'd1; always @(posedge I_clk_b) if(S_wr_zero_flag_rd) S_wr_clr_falg_temp <= 1'b1; else if(&S_clk_cnt) S_wr_clr_falg_temp <= 1'b0; else S_wr_clr_falg_temp <= S_wr_clr_falg_temp ; always @(posedge I_clk_b) S_wr_clr_falg <= S_wr_clr_falg_temp & (&S_clk_cnt) ; always @(posedge I_clk_b) if(S_wr_clr_falg) S_rd_addr <= 9'd256; else S_rd_addr <= S_rd_addr + 'd1; BRAM_SDP_MACRO #( .BRAM_SIZE ("18Kb" ), // Target BRAM, "18Kb" or "36Kb" .DEVICE ("7SERIES" ), // Target device: "7SERIES" .WRITE_WIDTH (32 ), // Valid values are 1-72 (37-72 only valid when BRAM_SIZE="36Kb") .READ_WIDTH (32 ), // Valid values are 1-72 (37-72 only valid when BRAM_SIZE="36Kb") .DO_REG (1 ), // Optional output register (0 or 1) .INIT_FILE ("NONE" )) BRAM_SDP_MACRO_inst ( .DO (O_data_b ), // Output read data port, width defined by READ_WIDTH parameter .DI (I_data_a ), // Input write data port, width defined by WRITE_WIDTH parameter .RDADDR (S_rd_addr ), // Input read address, width defined by read port depth .RDCLK (I_clk_b ), // 1-bit input read clock .RDEN (1'b1 ), // 1-bit input read port enable .REGCE (1'b1 ), // 1-bit input read output register enable .RST (1'b0 ), // 1-bit input reset .WE (4'hf ), // Input write enable, width defined by write port depth .WRADDR (S_wr_addr ), // Input write address, width defined by write port depth .WRCLK (I_clk_a ), // 1-bit input write clock .WREN (I_data_a_valid ) // 1-bit input write port enable); endmodule