我通常会尽可能地将顺序和组合分开,直到它导致过多的代码(通常很少见,并且可能表明设计不佳),或者当某些东西变得更有意义时(这种情况再次罕见,但却发生) 。
这种隔离通常有助于初始设计,大脑 - > rtl->合成(您认为您正在制作的实际上是合成的内容),CDC对多锁设计的评估,验证以及其他内容。我很难给出一个很糟糕的例子,而不是我称之为好的东西,但这里有一个很好的例子。
假设我有一个计数器,我想重置某个值。我能做到这一点(我倾向于从具有强大软件和/或FPGA背景的人那里看到)
always @(posedge clk or posedge reset) begin if(reset) begin count <= 0; end else begin if((count == reset_count_val) || (~enable)) count <= 0; else count <= count + 1; end end
或者我会这样做(这是我个人会做的):
//Combinational Path assign count_in = enable ? ((count == reset_count_val) ? 4'd0 : count_in + 4'd1) : 4'd0; //Sequential Path always @(posedge clk or posedge reset) begin if(reset) count <= 4'd0; else count <= count_in; end
上面我同意的是更多的打字,有些人更难以阅读。然而,它确实以一种允许我更容易看到每个时钟边沿发生的事情的方式分割电路。我知道在clk的构成之前正在设置“count_in”。我可以很容易地看到(以及其他任何查看代码的人)我希望MUX可以根据reset_count_val重置或添加计数,最后的MUX用于根据使能信号选通计数。现在你可以看到第一段代码相同,但IMO并不那么明确。当你查看一个sim时,你可以看到在clk的上升沿之前count_in的样子。如果count_in的条件语句相当复杂,这可以帮助您。
假设您通过综合和布局布线发送了这个,并且您在计数寄存器的Q和计数寄存器的D之间得到时序违规(因为您有基于添加的环回)。它会 通常 更容易看到哪个路径导致第二批代码的问题。这取决于工具(Primetime很可能)。 CDC也可以更容易,因为假设reset_count_val来自另一个时钟域中的静态寄存器。工具 可以 尝试合成/详细说明第一批代码中的OR,认为reset_count_val和enable有些相关,给你一个奇怪的CDC违规。同样,有时候很难想出一个例子来演练所有“为什么你不应该这样做”的案例。
作为一个关于拆分组合和顺序的例子,我继承了一个设计,其中有人有一个状态机,用同一个块中的组合和顺序编写(总是@(posedge clk),其中if / else会变深并具有下一个状态其中的逻辑)。我不是天才,但经过几天盯着那件事并运行sims,我可能无法弄清楚它在做什么。它也很大。我简单地重新设计了设计,保留了相同的算法,但是按照我在这里描述的格式拆分逻辑。即使我添加了一些功能,大小也下降了~15%。其他工程师遇到与其他设计相同的问题,现在可以理解它发生了什么。情况并非总是如此,但往往更多。
TLDR; 在设计用于ASIC的RTL时,我尝试极具描述性。代码越抽象,构建您不想要的东西的可能性就越大,或者比需要的东西更复杂。验证通常要容易得多,特别是当你进入门禁模拟器时。虽然我在“代码越少越好”的阵营中,但对于verilog来说并非总是如此,特别是在ASIC上。
总的来说,我会毫不犹豫地将组合与顺序逻辑混合在一起。我来自IC设计背景,总是将组合逻辑与顺序混合在一起。如果你不这样做,并且没有充分利用逻辑合成器的强大功能,我认为你是在限制自己。
例如,以下是我如何在VHDL中设计一个简单的异步复位计数器:
process (Clock, Reset) begin if Reset = '1' then Cnt <= (others => '0'); elsif Rising_edge(Clock) then if Enable = '1' then Cnt <= Cnt + 1; end if; end if; end process;
这种用VHDL编写计数器的方式无处不在。我个人认为将代码分成两个独立的进程没有任何优势,一个顺序是另一个组合。我刚刚教过一个满是工程师的房间,正是以这种方式设计了一个柜台。
以下是一些例外情况。 的 如果符合以下情况,我会从顺序逻辑中拆分组合逻辑: 强>
的 i)我正在设计一台状态机: 强>
我认为有一种非常优雅的编码状态机的方法,你可以从顺序中拆分组合逻辑:
Registers: process (Clock, Reset) begin if Reset = '1' then State <= Idle; elsif Rising_edge(Clock) then State <= NextState; end if; end process Registers; Combinational: process (State, Inputs) begin NextState <= State; Output1 <= '0'; Output2 <= '0'; -- etc case State is when Idle => if Inputs(1) = '1' then NextState <= State2; end if; when State2=> Output1 <= '1'; if Inputs = "00" then NextState <= State3; end if; -- etc end case; end process Combinational;
编码这样的状态机的优点是组合过程看起来非常像状态图。它不易出错,易于修改,不易出错。
的 ii)组合逻辑很复杂: 强>
对于真正大块的组合逻辑,我会分开。 “真正大”的确切定义是一个判断问题。
的 iii)组合逻辑在触发器的Q输出上: 强>
在顺序过程中驱动的任何信号都会推断触发器。 因此,如果您希望实现驱动实体*的输出的组合逻辑,则该组合逻辑必须处于单独的过程中。
*通常不是一个好主意 - 小心。
如果可以的话,我会发表评论,因为我没有写完整的答案,但是给你一个消息来源,而且我也没有完全参考这个问题(我对ASIC没有任何了解)。但总的来说,关于这个问题有很好的pdf 这里 。通常,您不必将顺序逻辑与差分逻辑完全分开,但编写更易读和可维护的代码会很有帮助。