学习:
Since digital circuits are composed of logic gates connected with wires, any circuit can be expressed as some combination of modules and assign statements. However, sometimes this is not the most convenient way to describe the circuit. Procedures (of which always blocks are one example) provide an alternative syntax for describing circuits.
For synthesizing hardware, two types of always blocks are relevant:
- Combinational: always @(*)
- Clocked: always @(posedge clk)
Combinational always blocks are equivalent to assign statements, thus there is always a way to express a combinational circuit both ways. The choice between which to use is mainly an issue of which syntax is more convenient. The syntax for code inside a procedural block is different from code that is outside. Procedural blocks have a richer set of statements (e.g., if-then, case), cannot contain continuous assignments*, but also introduces many new non-intuitive ways of making errors. (*Procedural continuous assignments do exist, but are somewhat different from continuous assignments, and are not synthesizable.)
For example, the assign and combinational always block describe the same circuit. Both create the same blob of combinational logic. Both will recompute the output whenever any of the inputs (right side) changes value.
译:
由于数字电路是由用导线连接的逻辑门组成的,因此任何电路都可以表示为模块和赋值语句的某种组合。然而,有时这不是最方便的方式来描述电路。Procedures(以 always blocks为例)为描述电路提供了另一种语法。
对于合成硬件,有两种类型的always块是相关的:
组合: always @(*)
时钟: always @(posedge clk)
组合always块相当于赋值语句,因此总有一种方法可以用两种方式表示组合电路。选择使用哪一种主要是哪种语法更方便的问题。过程块内部代码的语法与外部代码不同。过程块具有更丰富的语句集(例如,if-then, case),不能包含连续赋值*,但也引入了许多新的非直观的出错方式。(*程序连续分配确实存在,但与连续分配有些不同,并且不可综合。)
例如,赋值和组合总是块描述相同的电路。两者都创建了相同的组合逻辑。每当任何输入(右侧)改变值时,两者都将重新计算输出。
assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;
组合逻辑的always块应该总是使用一个敏感性列表()。明确地列出信号是容易出错的(如果你漏掉了一个信号),并且对于硬件综合来说这是被忽略的。如果你明确指定了敏感性列表并漏掉了一个信号,综合出的硬件仍然会表现得像是指定了()一样,但是仿真不会,并且不会与硬件的行为匹配。(在SystemVerilog中,使用always_comb。)
关于线网(wire)与寄存器(reg)的说明:assign语句的左侧必须是网类型(例如,wire),而always块中的程序化赋值的左侧必须是变量类型(例如,reg)。这些类型(线网对比寄存器)与综合出的硬件无关,只是Verilog作为硬件仿真语言时留下的语法。
练习:
Build an AND gate using both an assign statement and a combinational always block. (Since assign statements and combinational always blocks function identically, there is no way to enforce that you're using both methods. But you're here for practice, right?...)
使用赋值语句和组合always块构建AND门。(因为赋值语句和组合语句总是相同地阻塞函数,所以没有办法强制使用这两种方法。但你是来练习的,对吧?)
// synthesis verilog_input_version verilog_2001
module top_module(
input a,
input b,
output wire out_assign,
output reg out_alwaysblock
);
assign out_assign = a&b;
always@(*) out_alwaysblock = a&b;
endmodule
运行结果:
注释:
关于always@(*)的使用
always
块是硬件描述语言(如Verilog和SystemVerilog)中用于描述硬件电路行为的一种结构。它主要用于模拟和综合时序逻辑和组合逻辑的行为。以下是 always
块的基本用法和注意事项:
时序逻辑(Sequential Logic)
时序逻辑 always
块用于描述那些依赖于时钟信号或其他寄存器值的状态变化。这些 always
块通常包含一个敏感性列表,用于指定哪些信号的变化会触发块内的代码执行。
解释
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
reg_value <= initial_value;
end else begin
reg_value <= next_value;
end
en
在这个例子中,always
块对时钟信号 clk
的上升沿(posedge
)和复位信号 rst_n
的下降沿(negedge
)敏感。当这些信号变化时,块内的代码会执行。如果复位信号被激活,寄存器 reg_value
将被设置为初始值。否则,它会根据下一个值 next_value
更新。
组合逻辑(Combinational Logic)
组合逻辑 always
块用于描述那些仅依赖于当前输入值的逻辑行为。在这种情况下,敏感性列表包含了所有影响输出的输入信号。在SystemVerilog中,可以使用 always_comb
关键字来明确指出这是一个组合逻辑块。
always @(*) begin
output = input_A & input_B;
end
在这个例子中,always
块对所有列出的信号(input_A
和 input_B
)敏感。这意味着,只要这些信号中的任何一个发生变化,块内的代码就会执行。这里没有明确列出敏感性列表,而是使用了 (*)
表示对所有可能影响输出的信号都敏感。
注意事项
- 敏感性列表:在时序逻辑中,敏感性列表是必须的,用于指定触发
always
块执行的信号。在组合逻辑中,可以使用(*)
来表示对所有信号敏感。 - 阻塞赋值:
always
块内部通常使用阻塞赋值(例如reg_value <= value;
),这样可以在单个时钟周期内完成赋值。 - 非阻塞赋值:在某些情况下,特别是在时钟边沿触发的
always
块中,使用非阻塞赋值(例如reg_value = value;
)来避免数据冒险和竞争条件。 - 仿真与综合:
always
块的行为在仿真和综合时可能有所不同。在仿真时,明确列出敏感性列表有助于提高仿真的准确性。然而,在综合时,硬件综合工具通常会忽略显式列出的信号,而是依赖于它们内部的分析来确定敏感性。因此,对于组合逻辑,使用(*)
是推荐的做法。
总之,always
块是描述数字电路行为的重要工具,它在Verilog和SystemVerilog中被广泛使用。正确理解和使用 always
块对于设计正确和高效的硬件电路至关重要。