verilog 始终、开始和结束评估
我正在尝试使用 Pong P. Chu 的书来学习 Verilog。我有一个关于如何评估和实现始终块的问题。作者代码中的风格让我感到困惑。
在此示例中,他编写了一个带有两个输出寄存器“y1”和“y2”的 FSM。我感到困惑的部分是在下一个状态逻辑和输出逻辑总是阻塞,在开始语句和always@*
之后y1和y0被设置为0。我似乎无论状态如何, y1 和 y0 将在每个时钟周期和信号变化时切换为 0。根据书中的状态图,reg y1 在状态 0 或 1 时应该等于 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
I'm trying to learn Verilog using Pong P. Chu's book. I have a question about how an always block is evaluated and implemented. A style in the authors code is confusing me.
In this example he codes an FSM with two output registers 'y1' and 'y2'. The part I'm confused about is in the NEXT STATE LOGIC AND OUTPUT LOGIC always block, where after the begin statement and always@*
y1 and y0 are set to 0. I seems that regardless of state, y1 and y0 will toggle to 0 on every clock cycle and signal change. According to state diagram in the book reg y1 should be equal to 1 while in state 0 or 1.
So does y1 toggle to 0 every clock cycle then back to what ever its value at the present state?? I assume that's not the case and that I'm just confused about how the block is evaluated. Can someone explain what that part of the code is doing. I'm lost. Thanks
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
该表达式
描述了组合逻辑。通常,clk 和 rst 信号不是从这种类型的always块内部读取的,因此它们不会像wisemonkey所说的那样出现在敏感度列表中。最佳实践是使用 @* 作为组合逻辑的敏感度列表,这样您就不会忘记包含一个信号,该信号会推断出一些内存并且不再是组合逻辑。
在组合逻辑块内,您应该使用所谓的阻塞赋值。这些看起来像大多数编程语言中的常规变量赋值并使用单个等于。您分配给组合逻辑块内的变量(reg)的值对于同一组合逻辑块中的其他语句和表达式立即发生,但不会传播到该组合逻辑之外阻止直到到达终点。在块外部看到任何更改之前,always 块必须到达末尾。 Paul S 是对的,每当执行always 块时,您都希望始终为变量分配某些东西,否则您将推断内存。
The expression
describes combinational logic. Typically the clk and rst signals are not read from inside of this type of always block, so they don't appear in the sensitivity list like wisemonkey says. It is best practice to use @* for the sensitivity lists of combinational logic so that you don't forget to include a signal, which would infer some memory and it would no longer be combinational logic.
Inside a combinational logic block, you should use what are called blocking assignments. These look like regular variable assignments in most programming languages and use a single equals. The value that you assign to a variable (a reg) inside of a combinational logic block happens immediately with respect to other statements and expressions in that same combinational logic block, but does not propagate outside of this combinational logic block until you reach the end. The always block must reach the end before any changes are seen outside of the block. Paul S is right that you want to always assign something to your variables whenever the always block is executed, otherwise you will infer memory.
不得不说我不同意aqua。他(和wisemonkey)关于
@*
的说法是正确的,但其余的都是错误的。这两行与空闲状态无关。这些陈述是良好的编码实践。它们确保在评估always块时总是分配这两个输出。让我们看看为什么这很重要:
state_reg = S0
和a = b = 0
y1
a
为零,因此我们不输入 if 语句,并且退出 case,并结束块在块的末尾
y1 == 1
和y0 == ...
呃,等等y0
得到了什么?我想它必须保持它的旧价值。它没有得到一个新的。这意味着 y0 可能必须记住它从一个周期到下一个周期的值。这意味着它需要涉及某种内存,例如寄存器或锁存器。在这种情况下,它将是一个锁存器,因为它的编写风格有时驱动输出,有时保持输出。
...但我们不希望这样。
y1
和y0
本来就是简单的电线。因此,我们必须确保它们中的每一个都始终被分配,无论状态或输入是什么。我们可以通过在逻辑的所有分支中进行分配来做到这一点,但这会导致大量工作。或者,我们可以有一个默认分配,如有必要,我们稍后会覆盖它。这些语句没有将
y1
引入s0
或s1
中的0
的原因是因为内部发生的所有事情始终阻塞会在没有时间流逝的情况下发生。顶部分配的0
与s0
或s1
中的1
之间没有时间经过。所有可见的都是最终状态。您会注意到代码对状态变量执行完全相同的操作。它有一个默认分配,即下一个状态是当前状态,然后在满足正确条件时覆盖该分配。
漂亮干净的状态机。没有什么问题。
Have to say I disagree with aqua. What he (and wisemonkey) says about
@*
is right, but the rest is wrong.Those two lines have nothing to do with an idle state. Those statements are there as good coding practise. They ensure that those two outputs are always assigned to when that always block is evaluated. Let's see why this is important:
state_reg = S0
, anda = b = 0
y1
a
is zero so we don't enter the if statement, and we drop out of the case, and end the blockAt the end of the block
y1 == 1
andy0 == ...
erm, hang on what doesy0
get? I guess it has to keep it's old value. It didn't get a new one.That means it's possible
y0
has to remember it's value from one cycle to the next. That would mean it needs to have some kind of memory involved, like a register or a latch. In this case it would be a latch as it's written in a style that sometimes drives the output and sometimes holds it....but we don't want that.
y1
andy0
were meant to be simple wires. Therefore we must make sure each of them are always assigned to, no matter what the state or inputs are. We could do that by having assignments in all the branches of the logic, but that becomes a lot of work. Alternatively we can have a default assignment which we later override if necessary.The reason these statements don't introduce
y1
going to0
ins0
ors1
is because everything that happens inside an always block happens with no time passing. No time passes between the0
being assigned at the top and the1
ins0
ors1
. All that's visible is the final state.You'll note the code does exactly the same thing with the state variable. It has a default assignment that the next state is the current state, and then overrides that it the correct conditions are met.
Nice clean state machine. Nothing wrong with it.
这是 FSM 的一个糟糕例子。你感到困惑我并不感到惊讶。按照我的理解,
always
块被安排仅在其敏感度列表中的输入发生变化时运行。因此,对于第一个
always
块,它被安排在每次时钟从 0 到 1 的转换时运行,并且复位是异步的。第二个always块具有
@*
符号,它基本上根据块内的逻辑为您创建一个敏感度列表。回想一下,敏感度列表中只有输入很重要。因此,如果a
、b
或state_reg
发生变化,则将调度此始终块。在此示例中,
尝试对 IDLE 状态进行建模,这是一种 FSM 输出 0 的状态。如果您快速研究一下 FSM 的运行方式,您会发现一旦它开始转换通过各州,(案件陈述)它不会回来。
理想情况下,您希望您的 IDLE 信息处于其自己的状态中,而不是漂浮在状态逻辑之外,但我认为这只是一个简单的示例。
This is a poor example of an FSM. I'm not surprised that you are confused. The way I understand it, an
always
block is scheduled to run only when the inputs in its sensitivity list change.So for the first
always
block, it is scheduled to run every clock transition from 0 to 1, and reset is asynchronous.The second always block has the
@*
notation, which basically creates a sensitivity list for you based on the logic within the block. Recall that only inputs matter in a sensitivity list. Therefore, this always block will be scheduled ifa
,b
, orstate_reg
change.In this example, the
is trying to model an IDLE state, a state where the FSM is outputting 0. If you do a quick study of how the FSM operates, you'll see that once it starts transitioning through the states, (the case statements) it won't come back out.
Ideally you want your IDLE information within a state of its own, not floating outside the state logic, but I suppose this works as a trivial example.
我认为其他答案不能直接正确地解决 y0 和 y1 是否在每个时钟周期切换到 0 并返回的问题。
假设状态机从 s0 变为 s1。在这两种状态下,y1 的最终值为 1,但在重新评估时,always 块 y1 首先被分配为 0。这种切换可能每个时钟发生多次,或者根本不会在一个时钟周期发生,具体取决于 a、b 和state_reg 改变。此切换是否传播到连接到输出 y1 的线路取决于模拟器。在 Verilog 中,端口分配被视为连续分配,它们是单独运行的执行线程。对于模拟器来说,在 y1=0 赋值后暂停执行always块,将0分配给连接到输出y1的连线,然后恢复always块的执行是完全合法的。实际上,如果采用良好的编码风格并不重要,因为 y1 的值不会被锁存到任何寄存器中,直到下一个时钟周期,在所有切换完成并且 y1 的最终值可用之后很久。
在模拟中,切换发生在零时间内,但当多个输入发生变化时,在实际硬件中也会发生切换。需要特殊的设计实践来构建不会出现这样“故障”的逻辑。
I don't think other answers directly and correctly addresses the question of whether y0 and y1 toggle to 0 and back on every clock cycle.
Let's say that the state machine changes from s0 to s1. In both states the end value of y1 is 1 but in re-evaluating the always block y1 is first assigned 0. This toggling may happen multiple times per clock, or not at all on a clock cycle depending how many times a, b, and state_reg change. Whether this toggling propagates to the wire attached to output y1 is simulator dependent. Port assignments are treated as continuous assignments in Verilog, which are separately running threads of execution. It is perfectly legal for the simulator to suspend execution of the always block after the y1=0 assignment is made, assign 0 to the wire attached to output y1, and resume execution of the always block afterwards. Practically speaking, it doesn't matter IF good coding styles are practiced because the value of y1 won't get latched into any registers until the next clock cycle, long after all the toggling is done and the final value of y1 is available.
In simulation the toggling happens in zero time but it also happens in real hardware when multiple inputs change. It takes special design practices to build logic that doesn't "glitch" like this.