本文共 1629 字,大约阅读时间需要 5 分钟。
昨天和同学讨论双边沿采样的方法,包括DDR是如何实现的呢?
首先肯定不能在一个always块内用一个时钟的上下边沿,如下:
always@(posedge clk or negedge clk) begin.........end
这种典型的错误原因是FPGA内部(并不仅仅是FPGA)没有这种结构的触发器。之前也写过专门的博文来说过这个问题,这是错误的教训:
FPGA(以及其他任何地方)上的触发器是一个具有一个时钟且仅对该时钟的一个边缘敏感的器件。 因此,当灵敏度列表始终为@(posedge clk)时,综合工具只能映射到此设备。 我们有触发器的变种,可以进行异步预置/清除,因此将映射到始终@(posedge clk或<posedge / negedge> rst),但就是这样。
没有真正的硬件设备可以完成与你所描述的相同的东西 - 总是@(posedge clk or negedge clk)。唯一的例外(种类)是IDDR和ODDR,这些需要实例化 - 它们不能从HDL描述中推断出来。
那么既然不能像上述情况一样来进行双边沿采样,有人会说能不能对时钟进行一个反转,之后用源时钟以及生成时钟上升沿去采同一个数据。
咋一听没问题,可是理想很丰满,现实很骨干,这样会出现另一个问题就是多驱动问题。
assign clk_gen = ~clk;always@(posedge clk) begin q <= d;endalways@(posedge clk_gen) begin q <= d;end
且不说有没有多驱动问题,即使不出现多驱动问题,这种方式也是不能综合的,博文最初接触时钟分频器的时候,请教过别人如何实现奇分频的时钟分频器,就得到过这种答案,用这种方式来实现双边沿计数,仿真完全没问题,但是综合却是不通过的,误人误己。
说了两种方式,都是不可以的,那到底如何实现呢?
难倒只能使用这些垄断公司提供的IP核或者原语吗?
同学提供了我一种思路(什么时候开博客呀,给推荐下大佬):
思路1:
module TwoEdge( input clk, input rst_n, input d, input out);reg p, n;always@(posedge clk or negedge rst_n) begin if(~rst_n) p <= 0; else p <= d ^ n;endalways@(negedge clk or negedge rst_n) begin if(~rst_n) n <= 0; else n <= d ^ p;endassign out = p ^n;endmodule
这个思路很不错了。
那同学又给我一个思路,如下:
module TwoEdge( input clk, input rst_n, input d, output out);reg q, p;always@(posedge clk or negedge rst_n) begin if(~rst_n) begin q <= 0; end else begin q <= d; endendalways@(negedge clk or negedge rst_n) begin if(~rst_n) p <= 0; else p <= d;endassign out = clk ? q : p;endmodule
咋一看,比上一个简单,貌似很好,但是没考虑一个问题(毛刺问题),全靠大佬指导,让我恍然大悟,大佬微信公众号:
为什么会出现毛刺呢?
因为触发器输出会存在一个延迟Tcq,也就是说q和p的输出相对于clk会有一个延迟,如果采用上面的方法会出现毛刺。
暂时溜了。
转载地址:http://amjaf.baihongyu.com/