打工人日报#20250929
打工人日报#20250929
入职一周年了
知识点
Verilog 程序框架
- 模块(Module)定义:Verilog 设计的基本单元是模块,一个模块描述了一个特定的硬件功能。模块定义包含模块名、端口列表以及模块内容。
module module_name (// 端口声明input wire [width - 1:0] input_port1,input wire [width - 1:0] input_port2,output reg [width - 1:0] output_port
);// 模块内部声明(可选)reg [width - 1:0] internal_signal;// 逻辑描述always @(*) begininternal_signal = input_port1 + input_port2;output_port = internal_signal;end
endmodule
- 端口声明:模块通过端口与外部进行通信。端口可以是输入(input)、输出(output)或双向(inout)类型。端口可以声明为wire或reg类型,一般来说,input端口通常声明为wire类型,因为它们是由外部驱动的;output端口如果是组合逻辑输出,通常声明为wire,如果是需要存储状态的时序逻辑输出,通常声明为reg。
- 模块内部声明:在模块内部,可以声明各种信号、变量和参数。例如,reg类型的信号可用于存储数据,wire类型信号用于连接不同的逻辑块。还可以声明parameter来定义常量,方便在整个模块中使用。
- 逻辑描述:模块的逻辑功能通过各种语句来实现,如always块、assign语句等。always块可用于描述时序逻辑和组合逻辑,敏感列表决定了always块何时执行。assign语句通常用于描述组合逻辑,它会在其右侧表达式的值发生变化时立即更新左侧信号的值。
注释
单行注释:使用//来注释单行内容。
// 这是一个单行注释,解释下面这行代码的作用
assign result = a + b;
多行注释:使用/* */来注释多行内容。
/*
这是一个多行注释,
可以用来解释一段代码块的功能,
这里描述了一个加法器的实现。
*/
module adder (input wire [3:0] a,input wire [3:0] b,output wire [4:0] result
);assign result = a + b;
endmodule
关键字
- 模块相关关键字
- module:用于定义一个模块,标志着模块定义的开始。
- endmodule:标志着模块定义的结束。
- 端口相关关键字
- input:声明输入端口。
- output:声明输出端口。
- inout:声明双向端口。
- 数据类型相关关键字
- wire:定义线网类型,用于连接不同的逻辑元件。
- reg:定义寄存器类型,常用于存储数据,可在always块中赋值。
- parameter:定义参数,即常量值,在模块实例化时可以进行参数传递。
module counter #(parameter COUNT_WIDTH = 8) (input wire clk,input wire rst,output reg [COUNT_WIDTH - 1:0] count
);always @(posedge clk or posedge rst) beginif (rst)count <= {COUNT_WIDTH{1'b0}};elsecount <= count + 1;end
endmodule
过程块相关关键字
- always:定义一个过程块,在敏感列表中的信号发生变化时执行块内代码,可用于描述时序逻辑和组合逻辑。
- initial:定义一个只在仿真开始时执行一次的过程块,常用于初始化变量。
initial beginreg [3:0] temp;temp = 4'b0000;// 其他初始化操作
end
- 赋值相关关键字
- assign:用于对wire类型信号进行连续赋值,常用于描述组合逻辑。
- <=:非阻塞赋值,常用于always块内的时序逻辑中,先计算右侧表达式的值,在当前时间步结束时再更新左侧信号的值,避免竞争冒险。
- =:阻塞赋值,在always块内使用时,先计算右侧表达式的值并立即更新左侧信号的值,常用于组合逻辑。
- 条件和循环相关关键字
- if - else:条件判断语句,根据条件执行不同的代码块。
always @(*) beginif (a > b)result = a;elseresult = b;
end
case - endcase:多路选择语句,根据表达式的值选择执行不同的分支。
always @(*) begincase (sel)2'b00: result = a;2'b01: result = b;2'b10: result = c;2'b11: result = d;default: result = 4'b0000;endcase
end
for:循环语句,用于重复执行一段代码。
reg [7:0] array [0:3];
integer i;
initial beginfor (i = 0; i < 4; i = i + 1) beginarray[i] = i * 2;end
end
阻塞赋值(Blocking)与非阻塞赋值(Non - Blocking)
阻塞赋值(=):
- 执行方式:在always块内遇到阻塞赋值时,先计算右侧表达式的值,然后立即更新左侧变量的值。这意味着在阻塞赋值语句执行期间,后续语句会被 “阻塞”,直到该赋值完成。
always @(*) begina = b + c;d = a * 2;
end
首先计算
b + c的值并赋给a,然后使用更新后的a值计算a * 2并赋给d。如果b、c的值在a = b + c执行过程中发生变化,a的值会立即反映这种变化,并且d的计算也会基于变化后的a值。
非阻塞赋值(<=):
- 执行方式:在always块内遇到非阻塞赋值时,先计算右侧表达式的值,但不会立即更新左侧变量的值。而是在当前时间步结束时,同时更新所有非阻塞赋值语句左侧变量的值。
always @(posedge clk) begina <= b + c;d <= a * 2;
end
在时钟上升沿到来时,先计算b + c和a * 2的值,但a和d的值不会立即更新。直到当前时间步结束,才将计算好的值分别赋给a和d。所以d的计算基于a在时钟上升沿到来前的值,而不是更新后的a值。
assign和always区别
功能特性:
- assign:主要用于描述组合逻辑。它是一种连续赋值语句,只要右侧表达式中的任何操作数发生变化,就会立即重新计算并更新左侧信号的值。
assign result = a + b;
当a或b的值发生变化时,result会立即更新。
always:既可以描述组合逻辑,也可以描述时序逻辑。通过敏感列表来决定何时执行块内代码。对于组合逻辑,敏感列表包含所有输入信号;对于时序逻辑,敏感列表通常是时钟信号的边沿(如posedge clk或negedge clk)。
// 组合逻辑
always @(*) beginresult = a + b;
end
// 时序逻辑
always @(posedge clk) beginq <= d;
end
- 数据类型:
- assign:左侧信号必须是wire类型,因为它用于连接不同的逻辑元件,代表硬件中的连线。
- always:如果在always块内使用阻塞赋值(=),左侧信号通常是reg类型;如果使用非阻塞赋值(<=),也常用于reg类型信号,特别是在时序逻辑中。但在描述组合逻辑时,always块也可以通过assign语句对wire类型信号进行赋值。
- 使用场景:
- assign:适用于简单的组合逻辑,如加法器、多路选择器等,代码简洁明了。
- always:在描述复杂的组合逻辑或任何时序逻辑时更为灵活,能够实现状态机、计数器等复杂功能。
带时钟和不带时钟的always
- 带时钟的always:
- 功能:主要用于描述时序逻辑电路,如触发器、寄存器、计数器和状态机等。敏感列表通常是时钟信号的边沿(上升沿posedge clk或下降沿negedge clk)。在时钟边沿到来时,always块内的语句才会执行,从而实现状态的同步更新。
always @(posedge clk or posedge rst) beginif (rst)counter <= 0;elsecounter <= counter + 1;
end
counter是一个计数器。当时钟clk的上升沿到来时,如果复位信号rst有效(高电平),counter会被清零;否则counter会递增。
不带时钟的always:
- 功能:用于描述组合逻辑。敏感列表通常使用@(*),表示只要块内涉及的任何输入信号发生变化,always块就会执行。
result的值取决于a和b的比较结果,只要a或b的值发生变化,result就会重新计算。
什么是 Latch(锁存器)
- 定义:Latch 是一种对电平敏感的存储元件,它在输入信号电平有效期间保持输出跟随输入变化,当输入信号电平无效时,输出保持当前值。与触发器(对时钟边沿敏感)不同,Latch 在电平有效期间是透明的,即输出会实时反映输入的变化。
- 产生原因:在 Verilog 代码中,如果组合逻辑的描述不完整,就可能会综合出 Latch。例如,在always块内描述组合逻辑时,没有对所有可能的输入情况进行赋值,就可能导致 Latch 的产生。
always @(*) beginif (enable)out = in;
end
- 当enable为低电平时,out的值没有被明确赋值,综合工具会认为需要一个 Latch 来保持out的当前值,从而产生 Latch。
- 特点与应用:Latch 的优点是结构简单、速度快,但由于它对电平敏感,容易受到毛刺等干扰信号的影响,导致数据错误。因此,在设计中应尽量避免无意产生的 Latch,除非在特定场景下(如高速数据采样)有意使用。
状态机
- 定义:状态机是一种抽象的数学模型,用于描述在不同状态之间进行转换的系统。在数字电路设计中,状态机通过硬件电路实现,能够根据当前状态和输入信号决定下一个状态以及输出。
- 组成部分:
- 状态(States):表示系统在某一时刻所处的条件或情况。每个状态都有一个唯一的标识,可以用枚举类型、二进制编码或其他方式表示。例如,在一个简单的交通灯状态机中,可能有 “红灯”、“绿灯”、“黄灯” 等状态。
- 输入(Inputs):影响状态机状态转换的外部信号。例如,在交通灯状态机中,可能有一个 “定时时间到” 的输入信号,用于触发状态从 “绿灯” 转换到 “黄灯”。
- 输出(Outputs):根据当前状态产生的信号,用于控制外部设备或提供反馈。在交通灯状态机中,输出信号可以直接控制交通灯的亮灭。
- 状态转换逻辑(Transition Logic):决定在不同输入条件下,状态机如何从一个状态转换到另一个状态的逻辑。
- 类型:
- 摩尔(Moore)型状态机:输出仅取决于当前状态,与输入信号无关。其状态转换图中,输出值标注在状态节点上。
- 米利(Mealy)型状态机:输出不仅取决于当前状态,还取决于输入信号。在状态转换图中,输出值标注在状态转换箭头上。
- Verilog 实现:通常使用always块结合case语句来实现状态机。例如,一个简单的摩尔型状态机示例:
module simple_fsm (input wire clk,input wire rst,input wire in,output reg out
);typedef enum {STATE0, STATE1, STATE2} state_type;state_type current_state, next_state;always @(posedge clk or posedge rst) beginif (rst)current_state <= STATE0;elsecurrent_state <= next_state;endalways @(*) beginnext_state = current_state;out = 0;case (current_state)STATE0: beginif (in)next_state = STATE1;endSTATE1: beginout = 1;if (!in)next_state = STATE2;endSTATE2: beginif (in)next_state = STATE0;endendcaseend
endmodule
current_state表示当前状态,next_state表示下一个状态。第一个always块在时钟上升沿更新当前状态,第二个always块根据当前状态和输入信号确定下一个状态和输出值。
阅读
《杀死一只知更鸟》
第五章结束