数电与Verilog基础知识之always关键词及其相关

Verilog语言中三种固定的always关键词,它们分别是always_ff、always_latch和always_comb。它们都是用来描述电路元件的行为的,但是它们有不同的用途和限制。它们的工作原理如下:

  • always_ff是用来描述触发器的行为的,它只能包含时钟信号和复位信号的边沿触发事件,不能包含其他类型的事件。它只能使用非阻塞赋值(<=)来赋值输出信号,不能使用阻塞赋值(=)。它不能被其他进程写入相同的变量,也就是说输出信号只能由一个always_ff语句控制。
  • always_latch是用来描述锁存器的行为的,它可以包含任何类型的事件,但是必须有一个完整的if-else语句来控制输出信号。它只能使用阻塞赋值(=)来赋值输出信号,不能使用非阻塞赋值(<=)。它不能被其他进程写入相同的变量,也就是说输出信号只能由一个always_latch语句控制。
  • always_comb是用来描述组合逻辑电路的行为的,它不需要指定敏感列表,也就是说不需要列出触发语句块执行的事件,因为它会自动推断出所有相关的输入信号,并在任何一个输入信号发生变化时重新执行语句块。它只能使用阻塞赋值(=)来赋值输出信号,不能使用非阻塞赋值(<=)。它不能被其他进程写入相同的变量,也就是说输出信号只能由一个always_comb语句控制。

详细介绍如下:

always_comb

用来描述组合逻辑电路的行为。组合逻辑电路是一种没有存储元件的电路,它的输出信号只取决于当前的输入信号。always_comb语句的特点是:

  • 它不需要指定敏感列表,也就是说不需要列出触发语句块执行的事件,因为它会自动推断出所有相关的输入信号,并在任何一个输入信号发生变化时重新执行语句块。
  • 它会在仿真开始时自动执行一次,也就是说在时间0时,它会根据初始的输入信号计算出输出信号的值。
  • 它只能包含组合逻辑元件,也就是说不能包含触发器、锁存器、延时、事件控制或分支等时序逻辑元件。
  • 它只能使用阻塞赋值(=)来赋值输出信号,不能使用非阻塞赋值(<=)。
  • 它不能被其他进程写入相同的变量,也就是说输出信号只能由一个always_comb语句控制。

举个例子,假设有一个四位的全加器电路,它有两个四位的输入信号a和b,一个一位的进位输入信号cin,一个四位的输出信号s和一个一位的进位输出信号cout。可以用以下SystemVerilog代码来描述:

module adder(input logic [3:0] a, input logic [3:0] b, input logic cin, output logic [3:0] s, output logic cout);
  always_comb begin
    {cout, s} = a + b + cin; // 阻塞赋值·
  end
endmodule

上述代码第三行表示一个阻塞赋值,也就是说把右边的表达式的值赋给左边的变量,并且立即生效。左边的变量是一个连接符号{},它表示把两个或多个变量连接成一个变量。例如,{cout, s}表示一个五位的变量,它由cout和s两个变量组成,cout是最高位,s是低四位。右边的表达式是一个加法运算符+,它表示把两个或多个变量按位相加,并返回一个相同长度的结果。例如,a + b + cin表示把a、b和cin三个变量按位相加,并返回一个五位的结果。所以,这个语句的意思是把a、b和cin三个变量按位相加,并把结果的最高位赋给cout,低四位赋给s。

always_ff

用来描述触发器的行为。触发器是一种可以存储数据的电路元件,它有两个端口:时钟端口clk和数据端口d。触发器的输出端口q的值会在每个时钟信号的有效沿(通常是上升沿)时更新为数据端口d的值。always_ff语句的工作流程如下:

  • 首先,需要在always_ff语句后面用括号指定敏感列表,也就是触发语句块执行的事件。敏感列表只能包含时钟信号和复位信号的边沿触发事件,不能包含其他类型的事件。例如,@(posedge clk)表示只有在时钟信号clk的上升沿时才会执行语句块;@(posedge clk or negedge rst_n)表示只有在时钟信号clk的上升沿或复位信号rst_n的下降沿时才会执行语句块。
  • 然后,需要用begin和end包围一个语句块,也就是描述触发器功能的一组语句。语句块中只能使用非阻塞赋值(<=)来赋值输出信号,不能使用阻塞赋值(=)。非阻塞赋值表示赋值操作会在一个时间单位后才生效,并且不会影响其他赋值操作执行。例如,q <= d表示把数据信号d的值赋给输出信号q,并且延时一个时间单位。
  • 最后,需要注意不要被其他进程写入相同的变量,也就是说输出信号只能由一个always_ff语句控制。如果有多个always_ff语句控制同一个变量,那么可能会产生冲突或不确定的结果。

举个例子,假设有一个四位的D触发器电路,它有一个时钟输入端口clk,一个数据输入端口d,一个数据输出端口q,和一个异步复位输入端口rst_n。可以用以下SystemVerilog代码来描述:

module flop(input logic clk, input logic rst_n, input logic [3:0] d, output logic [3:0] q);
  always_ff @(posedge clk or negedge rst_n) begin
    if (rst_n == 0) q <= 4'b0; // 异步清零
    else q <= d; // 同步更新
  end
endmodule

always_latch

它用来描述锁存器的行为。锁存器是一种可以存储数据的电路元件,它有两个端口:使能端口en和数据端口d。锁存器的输出端口q的值会在使能端口en为高电平时更新为数据端口d的值,否则保持原来的状态。always_latch语句的工作流程如下:

  • 首先,需要在always_latch语句后面用括号指定敏感列表,也就是触发语句块执行的事件。敏感列表可以包含任何类型的事件,例如时钟信号的上升沿或下降沿,或者输入信号的变化。
  • 然后,需要用begin和end包围一个语句块,也就是描述锁存器功能的一组语句。语句块中必须有一个完整的if-else语句来控制输出信号,也就是说必须覆盖所有可能的输入情况。否则,如果有一些输入情况没有被处理,那么就会产生一个隐含的锁存器,这可能会导致错误或不确定的结果。
  • 最后,需要注意不要被其他进程写入相同的变量,也就是说输出信号只能由一个always_latch语句控制。如果有多个always_latch语句控制同一个变量,那么可能会产生冲突或不确定的结果。

举个例子,假设有一个四位的D锁存器电路,它有一个使能输入端口en,一个数据输入端口d,和一个数据输出端口q。可以用以下SystemVerilog代码来描述:

module latch(input logic en, input logic [3:0] d, output logic [3:0] q);
  always_latch @(en or d) begin
    if (en == 1) q = d; // 使能更新
    else q = q; // 保持状态
  end
endmodule

猜你喜欢

转载自blog.csdn.net/qq_52505851/article/details/132064847