继续牛客网的刷题,这一部分是跨时钟域传输,有下面的几道题:
FIFO的编写详见存储器篇:(2条消息) FPGA刷题——存储器(RAM和FIFO的Verilog实现)_居安士的博客-CSDN博客
目录
使用自然二进制码计数时,相邻数据之间可能会产生多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
这个题要求我们把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