我正在研究需要合成专门用于ASIC开发的RTL代码的项目。鉴于这种情况,在设计RTL时将顺序逻辑与差分逻辑分开有多重要?如果它很重要,那么在设计时应该采用什么方法,好像我应该如何区分我的顺序和组合逻辑设计呢?
答案 0 :(得分:2)
我通常会尽可能地将顺序和组合分开,直到它导致过多的代码(通常很少见,可能表示设计不佳),或者当某些事情变得更有意义时(这又是罕见的,但是发生了。)
这种隔离通常有助于初始设计,大脑 - > 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上。
答案 1 :(得分:2)
总的来说,我会毫不犹豫地将组合与顺序逻辑混合在一起。我来自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输出上:
在顺序过程中驱动的任何信号都会推断触发器。因此,如果您希望实现驱动实体输出的组合逻辑,那么这个组合逻辑必须处于一个单独的过程中
*通常不是一个好主意 - 小心。
答案 2 :(得分:0)
如果可以,我会发表评论作为评论,因为我没有写完整的答案,但是给你一个消息来源,而且我也没有完全参考这个问题(我不知道任何关于ASIC)中。但是关于这个问题,一般here都有很好的pdf。通常,您不必将顺序逻辑与差分逻辑完全分开,但编写更易读和可维护的代码会很有帮助。
答案 3 :(得分:0)
按顺序和组合的混合压缩了代码,几乎总是使代码更易于理解。
分离使ECO更容易。
您选择的是个人风格以及组织编码约定和标准。