• 异步复位同步释放在实际项目中的应用


    异步复位同步释放在实际项目中的应用

    1 引言

    最近看了“How do I reset my FPGA?”和一些时序分析的内容,发现之前ov5640图像采集项目中的几个问题:

    问题1:用了全局时钟复位,全局复位一般具有高扇出(需要驱动的后级逻辑信号多),因为它需要扩展到设计中的每一个触发器。这样会消耗大量的布线资源,对器件的利用率和时序性能造成不利影响。

    问题2:设计全采用了异步复位,对毛刺敏感且复位结束会处于亚稳态。

    问题3:在locked1,locked2为低时,时钟是不稳定的,此时送入后续模块的是不稳定的时钟,触发器可能出现功能错误。

    那么我们是否需要重构全部的代码呢?其实也没有必要。重构代码很麻烦,需要对所有18个模块进行代码修改,然后重新仿真十八次。

    这里我们选择在顶层模块进行复位模块局部化的划分,若实现送入每一个模块的都是异步复位同步释放后的复位信号,保证信号已经同步,本质上也是一样的。而且打两拍之后的复位信号送到时,一方面信号对毛刺不敏感,不会受到脉冲干扰,另外一方面,也相当于复位信号延后了一个或两个clock,最后一个clock时两个locked信号均已稳定,保证系统正常工作。

    2 代码

    2.1 时钟模块

    首先,我们需要确定项目中各模块工作的时钟域。此处有一点注意事项,我们pll2中的输入时钟为pll1生成的100Mhz时钟而非外部时钟,因此调用ip核时需要选用"no buffer"否则会报错。我们调用的两个pll代码如下:

    //复位模块复位信号
    assign rst_n = locked1 && locked2 && sys_rst_n;
    //时钟模块1例化
    clk_gen clk_gen_inst
    (
    .clk_125m (clk_125m ), // output sdram clk
    .clk_shift_125m (clk_shift_125m ), // output sdram output clk
    .clk_50m (clk_50m ), // output ov5640 clk
    .clk_100m (clk_100m ), // output pll2 clk
    .clk_24m (clk_24m ), // output ov5640 output clk
    .reset (~pll_rst_n1 ), // input
    .locked (locked1 ), // output
    .sys_clk (sys_clk ) // input sys_clk
    );
    //时钟模块2例化
    clk_gen_hdmi clk_gen_hdmi_inst
    (
    .clk_74m (clk_74m ), // output vga clk
    .clk_371m (clk_371m ), // output hdmi tmds clk
    .reset (~pll_rst_n2 ), // input
    .locked (locked2 ), // output
    .clk_100m (clk_100m ) // input clk_100m pll1 output clk_100Mhz
    );

    此处我们会发现一个问题,我们后续模块的复位信号是在时钟稳定的情况下基础上生成的,而实际上pll本身也有复位信号,为了保障整个工程的稳定性,我们需要对pll的复位信号也进行异步复位同步释放

    图1 结果时序图

    2.2 复位模块

    下面是我们设置的复位模块代码,在时钟稳定后, 将工作时钟,外部输入复位信号送入模块后,在各模块时钟下同步后的复位信号输出至各模块

    那么这里有个问题,如果在时钟没稳定前,如果有复位信号输入本模块会不会出现亚稳态的问题呢,实际上是不会的,我们可以看复位模块的复位信号,在时钟信号没稳定下,locked1,locked2常为0,不存在信号变化也就不存在亚稳态的问题了。

    复位模块复位信号代码如下:

    //复位模块复位信号
    assign rst_n = locked1 && locked2 && sys_rst_n;

    复位模块代码如下:

    //**************************************************************************
    // *** 名称 : sys_reset.v
    // *** 作者 : 吃豆熊
    // *** 日期 : 2021-4-1
    // *** 描述 : 异步复位同步释放模块
    //**************************************************************************
    module sys_reset
    //========================< 端口 >==========================================
    (
    input wire sys_clk, //系统时钟
    input wire pll_clk1,
    input wire pll_clk2,
    input wire vga_clk,
    input wire sdram_clk,
    input wire hdmi_clk,
    input wire sys_rst_n, //外界输入复位
    input wire rst_n, //两级时钟pll稳定后复位
    output wire pll_rst_n1, //第一级pll复位 异步复位同步释放后复位
    output wire pll_rst_n2, //第二级pll复位
    output wire camera_rst_n, //摄像头模块复位
    output wire vga_rst_n, //vga模块复位
    output wire sdram_rst_n, //sdram模块复位
    output wire hdmi_rst_n //hdmi模块复位
    );
    //========================< 信号 >==========================================
    //第一级pll复位
    reg pll_rst_n_reg1;
    reg pll_rst_n_reg2;
    //第二级pll复位
    reg pll_rst_n_reg3;
    reg pll_rst_n_reg4;
    //摄像头复位
    reg camera_rst_n_reg1;
    reg camera_rst_n_reg2;
    //sdram复位
    reg sdram_rst_n_reg1;
    reg sdram_rst_n_reg2;
    //vga复位
    reg vga_rst_n_reg1;
    reg vga_rst_n_reg2;
    //hdmi复位
    reg hdmi_rst_n_reg1;
    reg hdmi_rst_n_reg2;
    //==========================================================================
    //== 信号生成
    //==========================================================================
    //第一级pll复位信号
    always@(posedge pll_clk1 or negedge sys_rst_n )begin
    if(sys_rst_n == 1'b0)begin
    pll_rst_n_reg1 <= 1'b0;
    pll_rst_n_reg2 <= 1'b0;
    end
    else begin
    pll_rst_n_reg1 <= 1'b1;
    pll_rst_n_reg2 <= pll_rst_n_reg1;
    end
    end
    assign pll_rst_n1 = pll_rst_n_reg2;
    //第二级pll复位信号
    always@(posedge pll_clk2 or negedge sys_rst_n )begin
    if(sys_rst_n == 1'b0)begin
    pll_rst_n_reg3 <= 1'b0;
    pll_rst_n_reg4 <= 1'b0;
    end
    else begin
    pll_rst_n_reg3 <= 1'b1;
    pll_rst_n_reg4 <= pll_rst_n_reg3;
    end
    end
    assign pll_rst_n2 = pll_rst_n_reg4;
    //摄像头复位信号
    always@(posedge pll_clk2 or negedge rst_n )begin
    if(rst_n == 1'b0)begin
    camera_rst_n_reg1 <= 1'b0;
    camera_rst_n_reg2 <= 1'b0;
    end
    else begin
    camera_rst_n_reg1 <= 1'b1;
    camera_rst_n_reg2 <= camera_rst_n_reg1;
    end
    end
    assign camera_rst_n = camera_rst_n_reg2;
    //sdram复位信号
    always@(posedge sdram_clk or negedge rst_n )begin
    if(rst_n == 1'b0)begin
    sdram_rst_n_reg1 <= 1'b0;
    sdram_rst_n_reg2 <= 1'b0;
    end
    else begin
    sdram_rst_n_reg1 <= 1'b1;
    sdram_rst_n_reg2 <= sdram_rst_n_reg1;
    end
    end
    assign sdram_rst_n = sdram_rst_n_reg2;
    //vga复位信号
    always@(posedge vga_clk or negedge rst_n )begin
    if(rst_n == 1'b0)begin
    vga_rst_n_reg1 <= 1'b0;
    vga_rst_n_reg2 <= 1'b0;
    end
    else begin
    vga_rst_n_reg1 <= 1'b1;
    vga_rst_n_reg2 <= vga_rst_n_reg1;
    end
    end
    assign vga_rst_n = vga_rst_n_reg2;
    //hdmi复位信号
    always@(posedge hdmi_clk or negedge rst_n )begin
    if(rst_n == 1'b0)begin
    hdmi_rst_n_reg1 <= 1'b0;
    hdmi_rst_n_reg2 <= 1'b0;
    end
    else begin
    hdmi_rst_n_reg1 <= 1'b1;
    hdmi_rst_n_reg2 <= hdmi_rst_n_reg1;
    end
    end
    assign hdmi_rst_n = hdmi_rst_n_reg2;
    endmodule

    最后是我们的顶层模块例化代码:

    //局部复位划分模块
    sys_reset sys_reset_inst
    (
    .sys_clk (sys_clk ),
    .pll_clk1 (sys_clk ),
    .pll_clk2 (clk_100m ),
    .vga_clk (clk_74m ),
    .sdram_clk (clk_125m ),
    .hdmi_clk (clk_74m ),
    //输入复位
    .sys_rst_n (sys_rst_n ),
    .rst_n (rst_n ),
    //输出异步复位同步释放后复位信号
    .pll_rst_n1 (pll_rst_n1 ),
    .pll_rst_n2 (pll_rst_n2 ),
    .camera_rst_n (camera_rst_n ),
    .vga_rst_n (vga_rst_n ),
    .sdram_rst_n (sdram_rst_n ),
    .hdmi_rst_n (hdmi_rst_n )
    );

    随后我们就可以将输出的复位信号输入各个模块使用,且理论上由于均为wire型,因此与在每个模块内进行同步毫无区别。同时也避免了需要在顶层模块进行时钟复位信号的同步,保证了顶层模块的简洁。

    ---

    原创教程,转载请注明出处吃豆熊-异步复位同步释放

    参考资料:深入浅出玩转fpga

  • 相关阅读:
    推荐 NestJs 官方教程
    Ansible自动化部署
    在vue项目中使用markdown-it回显markdown文本
    GuavaCache本地缓存(LoadingCache)的使用分析
    visual studio 如何建立 C 语言项目
    学习笔记二十四:K8S四层代理Service
    Redis缓存面临的缓存击穿问题
    使用FCEUX调试器寻找并修改游戏初始物品
    基于Python实现的一个发送程序和接收程序
    计算机体系结构第四章实验——cache模拟器实验
  • 原文地址:https://www.cnblogs.com/chidouxiong/p/16133825.html