FPGA刷题——跨时钟域传输(FIFO+打拍+握手)

继续牛客网的刷题,这一部分是跨时钟域传输,有下面的几道题: 

 FIFO的编写详见存储器篇:(2条消息) FPGA刷题——存储器(RAM和FIFO的Verilog实现)_居安士的博客-CSDN博客

目录

格雷码计数器

跨时钟多bit数据同步器

快慢时钟下的脉冲同步电路实现

 握手信号法

边沿检测法


格雷码计数器

使用自然二进制码计数时,相邻数据之间可能会产生多bit的变化。这会产生较大的尖峰电流以及其他问题。格雷码是一种相邻数据只有1bit变化的码制

转换公式如下:

 最高位保留,gray[最高位]=bin[最高位]

低位由本位和高位异或得到, gray[i]=bin[i]^bin[i+1]        或者           gray=bin^(bin>>1)

module gray_counter(
   input   clk,
   input   rst_n,

   output  reg [3:0] gray_out
);
    
    reg [3:0] bin_cnt;//二进制计数器
    reg flag;//
    
    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            flag<=1'b0;
        end
        else begin
            flag<=~flag;//根据波形图,计数器两个周期变化一次
        end
    end
    
     always@(posedge clk or negedge rst_n)begin
        if(~rst_n)begin
            bin_cnt<=4'b0;
        end
        else begin
            bin_cnt<=flag?bin_cnt+4'b1:bin_cnt;//产生二进制码
        end
    end
    
    always@(*)begin
        gray_out<=bin_cnt^(bin_cnt>>1);
    end
    
endmodule

跨时钟多bit数据同步器

 这个题要求我们把a时钟域的数据data_in传递到b时钟域的data_out,由a时钟域的使能信号控制

data_in:数据输入;data_en:输入数据有效;dataout数据输出。

 

步骤如下:

(1)输入数据暂存在data_reg

(2)使能信号data_en在A时钟域缓存,再打两拍的方式跨时钟域传输到时钟域B

(3)最后data_out根据使能信号更新数据

module mux(
	input 				clk_a	, 
	input 				clk_b	,   
	input 				arstn	,
	input				brstn   ,
	input		[3:0]	data_in	,
	input               data_en ,

	output reg  [3:0] 	dataout
);
    
    reg [3:0] data_reg;
    //a时钟域下数据缓存
    always@(posedge clk_a or negedge arstn)begin
        if(~arstn)begin
            data_reg<=4'd0;
        end
        else begin
            data_reg<=data_in;
        end
    end
    
    //使能信号打拍
    reg data_en_a, data_en_b0, data_en_b1;
    
    always@(posedge clk_a or negedge arstn) begin//使能信号暂存
        if(~arstn)
            data_en_a <= 0;
        else
            data_en_a <= data_en;
    end
     
    always@(posedge clk_b or negedge brstn) begin//打2拍送到b时钟域
        if(~brstn) begin
            data_en_b0 <= 0;
            data_en_b1 <= 0;
        end
        else begin
            data_en_b0 <= data_en_a;
            data_en_b1 <= data_en_b0;
        end
    end

    //使能信号控制输出
    always@(posedge clk_b or negedge brstn) begin
        if(~brstn)
            dataout <= 0;
        else
            dataout <= data_en_b1? data_reg: dataout;
    end

endmodule

快慢时钟下的脉冲同步电路实现

题目要求将高频率clk下的脉冲信号传递到低频率clk下,直接传输会导致低clk有可能采集不到脉冲信号,如下图:

 握手信号法

`timescale 1ns/1ns

module pulse_detect(
	input 				clk_fast	, 
	input 				clk_slow	,   
	input 				rst_n		,
	input				data_in		,

	output  		 	dataout
);
    reg fast_req, slow_ack;
    reg [2:0] slow_req;
    reg [2:0] fast_ack;
    
    //fast时钟域
    
    //将slow时钟域的应答信号打三拍,送到fast时钟域
    always @(posedge clk_fast, negedge rst_n) begin
        if(!rst_n) begin
            fast_ack <= 3'b0;
        end else begin
            fast_ack <= {fast_ack[1:0], slow_ack};
        end 
    end
    
    //生成请求信号fast_req
    always @(posedge clk_fast, negedge rst_n) begin
        if(!rst_n) begin
            fast_req <= 1'b0;
        end else if (data_in) begin
            fast_req <= 1'b1;
        end else begin
            fast_req <= (fast_ack[1] & (~fast_ack[2])) ? 1'b0 : fast_req;
        end
    end
    
    //slow时钟域
    
    //将fast时钟域的请求信号打三拍,送到slow时钟域
    always @(posedge clk_slow, negedge rst_n) begin
        if(!rst_n) begin
            slow_req <= 3'b0;
        end else  begin
            slow_req <= {slow_req[1:0], fast_req};
        end 
    end
    
    //生成应答信号slow_ack
    always @(posedge clk_slow, negedge rst_n) begin
        if(!rst_n) begin
            slow_ack <= 1'b0;
        end else if (slow_req[1] & (~slow_req[2])) begin
            slow_ack <= 1'b1;
        end else begin
            slow_ack <= (slow_req[2] & (~slow_req[1])) ? 1'b0 : slow_ack;
        end
    end
    assign dataout = (~slow_req[2]) & (slow_req[1]);
    //
    
endmodule

边沿检测法

边沿检测法适用于快频率clk下的脉冲信号相隔距离够远,否则低clk仍然可能会采集不到脉冲信号

步骤如下:

(1)在快clk下进行脉冲展宽:脉冲信号每高电平一次,脉冲展宽高低电平转换一次

(2)设置3bit寄存器缓存脉冲展宽值(理解为打三拍,取2、3拍的沿)(第一拍用于避免亚稳态)

(3)当脉冲展宽值不相同时说明脉冲信号输出

`timescale 1ns/1ns    //二、基于边沿检测的方式
 
module pulse_detect(
    input               clk_fast    ,
    input               clk_slow    ,  
    input               rst_n       ,
    input               data_in     ,
 
    output              dataout
);
    reg req;
    reg [2:0] req_r;
 
    always @(posedge clk_fast or negedge rst_n) begin
        if(!rst_n)
            req<=1'b0;
        else if(data_in)   //req检测到data_in拉高就翻转,保持住,相当于脉冲展宽。但有缺陷,如果data_in两次高电平变化太快,B来不及采样,就会漏采,产生错误。
            req<=~req;     //题目说A时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题,则不用考虑data_in变化太快。
    end
 
    always @(posedge clk_slow or negedge rst_n) begin
        if(!rst_n)
            req_r<=3'b0;
        else
            req_r<={req_r[1:0],req};
    end
 
    assign dataout=req_r[1] ^ req_r[2];  //用异或表示连续2次data_in变化都被采样到。
 
endmodule

猜你喜欢

转载自blog.csdn.net/weixin_46188211/article/details/126041773