• 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)

    1. module gray_counter(
    2. input clk,
    3. input rst_n,
    4. output reg [3:0] gray_out
    5. );
    6. reg [3:0] bin_cnt;//二进制计数器
    7. reg flag;//
    8. always@(posedge clk or negedge rst_n)begin
    9. if(~rst_n)begin
    10. flag<=1'b0;
    11. end
    12. else begin
    13. flag<=~flag;//根据波形图,计数器两个周期变化一次
    14. end
    15. end
    16. always@(posedge clk or negedge rst_n)begin
    17. if(~rst_n)begin
    18. bin_cnt<=4'b0;
    19. end
    20. else begin
    21. bin_cnt<=flag?bin_cnt+4'b1:bin_cnt;//产生二进制码
    22. end
    23. end
    24. always@(*)begin
    25. gray_out<=bin_cnt^(bin_cnt>>1);
    26. end
    27. 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根据使能信号更新数据

    1. module mux(
    2. input clk_a ,
    3. input clk_b ,
    4. input arstn ,
    5. input brstn ,
    6. input [3:0] data_in ,
    7. input data_en ,
    8. output reg [3:0] dataout
    9. );
    10. reg [3:0] data_reg;
    11. //a时钟域下数据缓存
    12. always@(posedge clk_a or negedge arstn)begin
    13. if(~arstn)begin
    14. data_reg<=4'd0;
    15. end
    16. else begin
    17. data_reg<=data_in;
    18. end
    19. end
    20. //使能信号打拍
    21. reg data_en_a, data_en_b0, data_en_b1;
    22. always@(posedge clk_a or negedge arstn) begin//使能信号暂存
    23. if(~arstn)
    24. data_en_a <= 0;
    25. else
    26. data_en_a <= data_en;
    27. end
    28. always@(posedge clk_b or negedge brstn) begin//打2拍送到b时钟域
    29. if(~brstn) begin
    30. data_en_b0 <= 0;
    31. data_en_b1 <= 0;
    32. end
    33. else begin
    34. data_en_b0 <= data_en_a;
    35. data_en_b1 <= data_en_b0;
    36. end
    37. end
    38. //使能信号控制输出
    39. always@(posedge clk_b or negedge brstn) begin
    40. if(~brstn)
    41. dataout <= 0;
    42. else
    43. dataout <= data_en_b1? data_reg: dataout;
    44. end
    45. endmodule

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

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

     握手信号法

    1. `timescale 1ns/1ns
    2. module pulse_detect(
    3. input clk_fast ,
    4. input clk_slow ,
    5. input rst_n ,
    6. input data_in ,
    7. output dataout
    8. );
    9. reg fast_req, slow_ack;
    10. reg [2:0] slow_req;
    11. reg [2:0] fast_ack;
    12. //fast时钟域
    13. //将slow时钟域的应答信号打三拍,送到fast时钟域
    14. always @(posedge clk_fast, negedge rst_n) begin
    15. if(!rst_n) begin
    16. fast_ack <= 3'b0;
    17. end else begin
    18. fast_ack <= {fast_ack[1:0], slow_ack};
    19. end
    20. end
    21. //生成请求信号fast_req
    22. always @(posedge clk_fast, negedge rst_n) begin
    23. if(!rst_n) begin
    24. fast_req <= 1'b0;
    25. end else if (data_in) begin
    26. fast_req <= 1'b1;
    27. end else begin
    28. fast_req <= (fast_ack[1] & (~fast_ack[2])) ? 1'b0 : fast_req;
    29. end
    30. end
    31. //slow时钟域
    32. //将fast时钟域的请求信号打三拍,送到slow时钟域
    33. always @(posedge clk_slow, negedge rst_n) begin
    34. if(!rst_n) begin
    35. slow_req <= 3'b0;
    36. end else begin
    37. slow_req <= {slow_req[1:0], fast_req};
    38. end
    39. end
    40. //生成应答信号slow_ack
    41. always @(posedge clk_slow, negedge rst_n) begin
    42. if(!rst_n) begin
    43. slow_ack <= 1'b0;
    44. end else if (slow_req[1] & (~slow_req[2])) begin
    45. slow_ack <= 1'b1;
    46. end else begin
    47. slow_ack <= (slow_req[2] & (~slow_req[1])) ? 1'b0 : slow_ack;
    48. end
    49. end
    50. assign dataout = (~slow_req[2]) & (slow_req[1]);
    51. //
    52. endmodule

     

    边沿检测法

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

    步骤如下:

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

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

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

     

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

  • 相关阅读:
    RK3568平台 sys虚拟文件系统添加节点
    一个基于浏览器前端的可拖拽workbench的开发
    算法 接雨水问题-(双指针)
    设计模式-适配器模式在Java中的使用示例
    交换机和路由器技术-20-动态路由协议
    etoken是什么意思,有什么作用?
    dom4j基本使用与XPath不生效处理
    【MySQL从0到1】第八篇:索引
    git版本回退
    R语言爬虫代码模版:技术原理与实践应用
  • 原文地址:https://blog.csdn.net/weixin_46188211/article/details/126041773