我正在尝试使用Pong P. Chu的书来学习Verilog。我有一个关于如何评估和实现always块的问题。作者代码中的一种风格令我感到困惑。
在此示例中,他使用两个输出寄存器'y1'和'y2'对FSM进行编码。我困惑的部分是在NEXT STATE LOGIC和OUTPUT LOGIC总是阻塞,在begin语句之后,always@*
y1和y0被设置为0.我似乎无论状态如何,y1和y0都会切换每个时钟周期为0,信号发生变化。根据书中的状态图,在状态0或1时,reg y1应该等于1。
那么y1在每个时钟周期切换到0然后回到它在当前状态下的值吗?我认为情况并非如此,我只是对如何评估块感到困惑。有人可以解释代码的这一部分正在做什么。我迷路了。感谢
module fsm_eg_2_seg
(
input wire clk, reset, a, b,
output reg y0, y1
);
//STATE DECLARATION
localparam [1:0] s0 =2'b00,
s1=2'b01,
s2=2'b10;
// SIGNAL DECLARATION
reg [1:0] state_reg, state_next ;
//STATE REGISTER
always @(posedge clk, posedge reset)
if (reset)
state_reg <= s0;
else
state_reg <= state_next;
//NEXT STATE LOGIC AND OUTPUT LOGIC
always @*
begin
state_next = state_reg; // default next state: the same
y1 = 1'b0; // default output: 0
y0 = 1'b0; // default output: 0
case (state_reg)
s0: begin
y1 = 1'b1;
if (a)
if(b)
begin
state_next = s2;
y0 = 1'b1;
end
else
state_next = s1;
end
s1: begin
y1 = 1'b1;
if (a)
state_next = s0;
end
s2: state_next = s0;
default: state_next = s0;
endcase
end
endmodule
答案 0 :(得分:4)
不得不说我不同意aqua。他(以及智者)对@*
所说的是正确的,但其余的都是错的。
这两行与空闲状态无关。这些陈述是良好的编码实践。它们确保在评估始终块时始终将这两个输出分配给它们。让我们看看为什么这很重要:
state_reg = S0
和a = b = 0
y1
a
为零所以我们不输入if语句,我们退出案例,结束块在阻止y1 == 1
和y0 == ...
的结尾处,依靠y0
得到的内容?我想它必须保持它的旧价值。它没有得到一个新的。
这意味着y0
可能必须记住它从一个周期到下一个周期的值。这意味着它需要涉及某种内存,如寄存器或锁存器。在这种情况下,它将是一个闩锁,因为它以一种有时驱动输出并有时保持输出的样式编写。
......但我们不希望这样。 y1
和y0
意味着简单的电线。因此,无论状态或输入是什么,我们都必须确保每个人都被分配。我们可以通过在逻辑的所有分支中进行分配来做到这一点,但这会成为很多工作。或者,我们可以使用默认分配,如有必要,我们稍后会覆盖它。
这些陈述未在y1
或0
中引入s0
的原因是因为在始终阻止内发生的所有内容都会在没有时间过去的情况下发生。在顶部分配的s1
和0
或1
中的s0
之间没有时间。所有可见的都是最终状态。
您会注意到代码与状态变量完全相同。它有一个默认的赋值,即下一个状态是当前状态,然后覆盖它是否满足正确的条件。
漂亮干净的状态机。它没有错。
答案 1 :(得分:4)
表达式
always @* begin : name_of_my_combinational_logic_block
// code
end
描述了组合逻辑。通常,clk和rst信号不会从这种类型的always块内部读取,因此它们不会像wisemonkey所说的那样出现在灵敏度列表中。最佳做法是使用@ *作为组合逻辑的灵敏度列表,这样您就不会忘记包含一个信号,这将推断出一些内存,它将不再是组合逻辑。
在组合逻辑块中,您应该使用所谓的阻止分配。这些看起来像大多数编程语言中的常规变量赋值,并使用单个等号。分配给组合逻辑块内的变量(reg)的值相对于同一组合逻辑块中的其他语句和表达式发生立即,但不会传播到此组合逻辑之外阻止你到达终点。始终块必须在块外部看到任何更改之前到达结尾。 Paul S是正确的,你总是希望在执行always块时总是将某些赋给变量,否则你将推断出内存。
答案 2 :(得分:1)
这是FSM的一个不好的例子。你很困惑我并不感到惊讶。我理解它的方式,always
块计划仅在其灵敏度列表中的输入发生变化时运行。
因此对于第一个always
块,它被安排在从0到1的每个时钟转换运行,并且reset是异步的。
第二个always块具有@*
符号,它基本上根据块内的逻辑为您创建一个敏感列表。回想一下,只有输入在敏感列表中很重要。因此,如果a
,b
或state_reg
发生更改,则会计划此始终阻止。
在这个例子中,
y1 = 1'b0; // default output: 0
y0 = 1'b0; // default output: 0
正在尝试建模 IDLE 状态,这是FSM输出0的状态。如果你快速研究FSM如何运作,你会看到它一旦开始转换通过国家,(案例陈述)它不会退出。
理想情况下,您希望 IDLE 信息处于自己的状态,而不是在状态逻辑之外浮动,但我认为这可以作为一个简单的例子。
答案 3 :(得分:1)
我不认为其他答案直接正确地解决了y0和y1是否在每个时钟周期切换为0并返回的问题。
假设状态机从s0变为s1。在两种状态下,y1的结束值为1,但在重新评估时,始终首先将块y1分配为0.这种切换可能每个时钟发生多次,或者根据时钟周期完全不发生,这取决于a,b和a的次数。 state_reg改变。这种切换是否传播到连接到输出y1的导线是否依赖于模拟器。端口分配在Verilog中被视为连续分配,它们分别运行执行线程。在进行y1 = 0分配后,模拟器暂停执行always块是完全合法的,将0分配给连接到输出y1的导线,然后继续执行always块。实际上,如果实现了良好的编码风格并不重要,因为在完成所有切换并且y1的最终值可用之后很长时间,y1的值将不会被锁存到任何寄存器中,直到下一个时钟周期。
在仿真中,切换发生在零时间,但当多个输入发生变化时,它也会发生在真实硬件中。它需要特殊的设计实践来构建不会像这样“故障”的逻辑。