Verilog多驱动错误求助:同频异相时钟驱动寄存器的解决方案
我明白你遇到的问题了——Verilog里绝对不允许同一个寄存器被多个always块独立驱动,这就是工具报多驱动错误的核心原因,本质上这相当于硬件里两个不同的电路同时往同一个节点输出信号,要么产生不确定电平,要么直接损坏电路。
你的需求是利用同频但相位偏移的两个时钟,在不同的时钟沿位置切换信号电平,这个需求完全可以实现,下面给你几个可行的解决方案,按推荐程度排序:
方案1:用主时钟同步所有事件(最稳健,推荐)
既然两个时钟同频,我们可以选择其中一个作为主时钟(比如Clock_A),把另一个时钟的上升沿同步到主时钟域,然后在主时钟的always块里统一处理两种触发逻辑,完全避免多驱动问题:
wire Signal_On_Wire; reg SigValue; // 第一步:把Clock_B同步到Clock_A的时钟域,避免亚稳态 reg clk_b_sync1, clk_b_sync2; always @(posedge Clock_A) begin clk_b_sync1 <= Clock_B; clk_b_sync2 <= clk_b_sync1; end // 第二步:在Clock_A域内检测Clock_B的上升沿 assign clk_b_posedge = clk_b_sync1 & ~clk_b_sync2; // 第三步:在主时钟的always块里统一更新SigValue always @(posedge Clock_A) begin if (clk_b_posedge) begin // 当检测到Clock_B的上升沿时,设置为1 SigValue <= 1'd1; end else begin // 每次Clock_A的上升沿(且不是Clock_B的上升沿时),设置为0 SigValue <= 1'd0; end end assign Signal_On_Wire = SigValue;
这种方法的优势在于所有逻辑都在同一个时钟域下,符合数字设计的时序规范,几乎不会有毛刺或亚稳态问题,不管两个时钟的相位偏移是多少(只要不同时触发)都能稳定工作。
方案2:合并触发条件到单个always块
如果你的工具支持多时钟触发的always块,也可以把两个时钟的上升沿合并到同一个触发列表里,通过条件判断区分不同的触发源:
wire Signal_On_Wire; reg SigValue; always @(posedge Clock_A or posedge Clock_B) begin if (posedge Clock_A) begin SigValue <= 1'd0; end else if (posedge Clock_B) begin SigValue <= 1'd1; end end assign Signal_On_Wire = SigValue;
不过要注意:这种方法要求两个时钟的上升沿绝对不能同时发生(也就是相位偏移不能是0或180度,或者说不能出现两个沿同时触发的情况),否则条件判断会出现竞争,导致输出不确定。而且不是所有综合工具都支持这种多时钟触发的写法,所以通用性不如方案1。
方案3:用组合逻辑+脉冲检测(适合简单场景)
你也可以把两个时钟的上升沿转化为窄脉冲,然后用组合逻辑选择输出,但这种方法容易产生毛刺,需要额外处理:
wire Signal_On_Wire; // 生成Clock_A上升沿的脉冲 reg clk_a_pulse; always @(posedge Clock_A or negedge Clock_A) begin clk_a_pulse <= (posedge Clock_A) ? 1'b1 : 1'b0; end // 生成Clock_B上升沿的脉冲 reg clk_b_pulse; always @(posedge Clock_B or negedge Clock_B) begin clk_b_pulse <= (posedge Clock_B) ? 1'b1 : 1'b0; end // 组合逻辑选择输出,注意这里用Signal_On_Wire反馈保持电平 assign Signal_On_Wire = clk_a_pulse ? 1'b0 : (clk_b_pulse ? 1'b1 : Signal_On_Wire);
这种写法的问题是组合逻辑的反馈路径可能导致时序问题,而且脉冲宽度很窄,容易被噪声干扰,所以只适合非常简单的场景,不推荐在复杂设计中使用。
总结一下,方案1是最可靠的选择,完全遵循数字设计的最佳实践,能完美实现你的需求同时避免多驱动和时序问题。
内容的提问来源于stack exchange,提问作者JHinkle

