5

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

 2 years ago
source link: https://www.cnblogs.com/chidouxiong/p/16133825.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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

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

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

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

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

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

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;    endend 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;    endend 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;    endend 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;    endend 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;    endend 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;    endend 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK