if-else-if 条件语句 用于决定是否执行特定语句,类似C语言。表达式为真执行第一语句,为假且有else则执行else部分。多语句需用begin-end包围。嵌套if省略else易混淆,建议总将else关联最近缺else的if,或用begin-end明确界定。最后else处理默认情况。 四种循环语句 forever 循环 持续执行块内语句,慎用避免仿真挂起。 repeat 循环 执行固定次数,表达式为X/Z视为零不执行。 while 循环 条件为真持续执行,条件变假退出。初始为假则不执行。 for 循环 三步控制:初始化计数器、评估条件、更新计数器。有明确始终,适合已知迭代次数场景。V
阻塞赋值 阻塞赋值使用=符号,在过程块中按顺序依次执行。但不会阻止并行块中语句的执行。 module tb; reg [7:0] a, b, c, d, e; initial begin a = 8hDA; $display ([%0t] a=0x%0h b=0x%0h c=0x%0h, $time, a, b, c); b = 8hF1; $display ([%0t] a=0x%0h b=0x%0h c=0x%0h, $time, a, b, c); c = 8h30; $display ([%0t] a=0x%0h b=0x%0h
将值赋给网络和变量称为赋值。Verilog 中有三种基本形式:过程赋值连续赋值过程连续赋值合法的左值(LHS)赋值语句包含右侧表达式(RHS)和左侧目标(LHS),中间用等号(=)或非阻塞赋值符号(<=)连接。赋值类型左侧允许的对象过程赋值变量(向量/标量)向量 reg、integer 或 time 变量的位选或部分选存储单元上述任意项的拼接连续赋值网络(向量/标量)向量网络的位选或部分选位选和部分选的拼接过程连续赋值网络或变量(向量/标量)向量网络的位选或部分选RHS 可以是任意求值表达式,LHS 指明接收该值的网络或变量。module tb; reg clk; wire a, b,
有方法将一组语句分组,使其在语法上等价于单条语句,称为 块语句。块语句分两种:顺序和并行。 顺序块 语句用 begin 和 end 关键字包裹,按给定顺序依次执行。延迟值相对于前一条语句执行时间。块内所有语句执行完后控制权可转移别处。 module design0; bit [31:0] data; // initial 块在时间 0 启动 initial begin // 10 时间单位后 data 变为 0xfe #10 data = 8hfe; $display ([Time=%0t] data=0x%0h, $time, data); // 20 时间
前例探讨了简单序列检测器。这里是另一个检测稍长模式的模式检测器示例。 设计 module det_110101 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S11 = 2, S110 = 3, S1101 = 4, S11010 = 5, S110101 = 6; reg [2:0] cur_state, next_state; assign out = cur_state == S110101 ? 1 : 0; always @ (posedge cl
FSM 的常见例子是序列检测器,硬件设计需检测输入二进制位流中何时出现固定模式。 示例 module det_1011 ( input clk, input rstn, input in, output out ); parameter IDLE = 0, S1 = 1, S10 = 2, S101 = 3, S1011 = 4; reg [2:0] cur_state, next_state; assign out = cur_state == S1011 ? 1 : 0; always @ (posedge clk) begin if (!rstn)
generate 块允许批量实例化模块或有条件实例化任意模块。它提供基于 Verilog 参数构建设计的能力。当同一操作或模块实例需重复多次,或某些代码需根据 Verilog 参数有条件包含时,这些语句特别方便。 generate 块不能包含端口、参数、specparam 声明或 specify 块。但允许其他模块项和其他 generate 块。所有生成实例化编写在 module 内,位于 generate 和 endgenerate 关键字之间。 生成的实例化可包含模块、连续赋值、always 或 initial 块及用户定义基元。generate 构造有两类:循环和条件。 Generate
所有行为代码写在 module 和 endmodule 内。无论打算创建什么数字设计,都放在 module 块中。可定义端口允许信号作为 input 进入或作为 output 离开。 模块 下面空模块叫 testbench。命名随意,但须字母数字且可含下划线 _。 module testbench; endmodule 再看另一模块。有几个信号 (d, clk, rstb) 声明为输入,q 声明为输出。 module dff (input d, clk, rstb, output q); endmodule 数据类型 看过模块样子,再看模块内可放什么,以 testbench 模块为例。Ve
一组 Verilog 语句通常在仿真中顺序执行。这些语句放在 过程 块中。Verilog 中主要有两类 过程 块:initial 和 always。 语法 initial [single statement] initial begin [multiple statements] end initial 块用途 initial 块不可综合,无法转换为包含数字元件的硬件原理图。因此 initial 块除仿真外用途不大。主要用于初始化变量和驱动设计端口特定值。 initial 块何时开始和结束? initial 块在仿真开始时刻 0 启动。整个仿真期间仅执行一次。执行完块内所有语句后结束。 上图
前文展示了用 always 块实现组合逻辑的示例。always 块也主要用于实现时序逻辑,包含可保持值的触发器等存储元件。 JK 触发器 JK 触发器是存储值的多种触发器之一,有两个数据输入 j 和 k,以及复位 rstn 和时钟 clk。真值表如下,通常用与非门实现。 rstnjkq注释 0000复位有效时输出恒为 0 100保持值j=k=0 时输出保持不变 1011k=1 时输出变 1 1100k=0 时输出变 0 111翻转值j=k=1 时输出翻转当前值 JK 触发器行为 Verilog 代码如下: module jk_ff ( input j, // Input
Verilog always 块可用于时序和组合逻辑。前文用 assign 语句展示了几个设计示例。接下来用 always 块探索相同设计集。 示例 #1:简单组合逻辑 下面代码实现简单数字组合逻辑,输出信号 z 类型为 reg,在敏感列表中任一信号值变化时更新。敏感列表声明在 @ 运算符后的括号内。 module combo ( input a, b, c, d, e, output reg z ); always @ (a or b or c or d or e) begin z = ((a & b) | (c ^ d) & ~e); end endmodule 模
always 块是 Verilog 中的 过程 块之一。always 块内语句按顺序执行。 语法 always @ (event) [statement] always @ (event) begin [multiple statements] end always 块在特定事件执行。事件由敏感列表定义。 什么是敏感列表? 敏感 列表定义 always 块何时执行,指定在 @ 运算符后的括号 ( ) 内。列表可包含一个或一组信号,其值变化将触发 always 块执行。 下面代码中,always 块内所有语句在信号 a 或 b 值变化时执行。 // 当 a 或 b 值变化时执行 always 块
多位 Verilog 线网和变量可通过 拼接 运算符 { 和 } 用逗号分隔组合成更宽的多线网线网或变量。拼接操作数除线网和变量外,还允许表达式和带位宽常数。 必须知道每个操作数尺寸以计算完整拼接尺寸。 Verilog 拼接示例 wire a, b; // 1-bit wire wire [1:0] res; // 2-bit wire to store a and b // res[1] follows a, and res[0] follows b assign res = {a, b}; wire [2:0] c; wire [7:0] res1; // res[0] follows c
无法处理的数据毫无用处,数字电路和计算机系统总需要某种计算。看看 Verilog 中哪些运算符能让综合工具实例化合适的硬件元件。 Verilog 算术运算符 若除法或取模运算符的第二个操作数为零,结果为 X。若幂运算符任一操作数为实数,结果也为实数。幂运算符第二个操作数为 0 时结果为 1 (a0)。 运算符描述 a + ba 加 b a - ba 减 b a * ba 乘 b a / ba 除以 b a % ba 模 b a ** ba 的 b 次幂 算术运算符使用示例: module des; reg [7:0] data1; reg [7:0] data2; initial begi
Verilog assign 语句通常用于持续驱动 wire 数据类型信号,综合为组合逻辑。以下是更多使用 assign 语句的设计示例。 示例 #1:简单组合逻辑 下面代码实现简单数字组合逻辑,输出线网 z 通过 assign 语句持续驱动以实现数字方程。 module combo ( input a, b, c, d, e, output z ); assign z = ((a & b) | (c ^ d) & ~e); endmodule 模块 combo 经综合工具展开为以下硬件原理图,可见组合逻辑用数字门实现。 测试平台 测试平台用于仿真验证设计行为是否符合预期。使
wire 类型或类似线网数据类型的信号需要连续赋值。例如面包板上连接元件的电线,只要一端接 +5V 电池,另一端连接的元件即获得所需电压。 Verilog 中通过 assign 语句实现此概念:任何 wire 或类似线网数据类型可被表达式持续驱动。值可以是常量或由一组信号组成的表达式。 assign 语法 赋值语法以关键字 assign 开始,后跟信号名(可以是单个信号或不同信号线网的拼接)。驱动强度 和 延迟 可选,主要用于数据流建模而非综合成真实硬件。右侧表达式或信号求值后赋给左侧线网或线网表达式。 assign = [drive_strength] [delay] 延迟值用于指定门
如前文所述,复杂设计通过层次化集成多个模块构建。模块可在其他模块内实例化,这些实例的端口可与父模块内其他信号连接。 端口连接可通过有序列表或按名称进行。 按有序列表连接端口 模块实例化中端口表达式列表与父模块内信号连接的一种方法是有序列表。 mydesign 模块在另一模块 tb_top 中实例化为 d0。端口按特定顺序连接,顺序由模块声明中端口列表位置决定。例如 tb_top 中的 b 连接到设计的 y,仅因两者在端口列表中均处于第二位置。 o endmodule 必须知道设计模块端口顺序才能正确连接。 这非常不便,因为新增端口会改变顺序,且端口数量大时极易出错。 按名称连接端口 更好的方
端口是作为特定模块输入和输出的一组信号,是与模块通信的主要方式。将模块视为放置在 PCB 上的芯片,很明显与芯片通信只能通过其引脚。端口如同引脚,设计用于向外部发送和接收信号。 端口类型 端口描述 Input设计模块仅通过 input 端口从外部接收值 Output设计模块仅通过 output 端口向外部发送值 Inout设计模块可通过 inout 端口发送或接收值 端口默认视为 wire 类型线网。 语法 inout 声明的端口可同时作为输入和输出。 input [net_type] [range] list_of_names; // 输入端口 inout [net_type] [ra
module 是实现特定功能的 Verilog 代码块。模块可嵌套在其他模块内,高层模块通过输入输出端口与低层模块通信。 语法 module 必须包含在 module 和 endmodule 关键字之间。模块名紧跟 module 关键字后,可选端口列表。注意:端口列表中声明的端口不能在模块体内重复声明。 module ([port_list]); // Contents of the module endmodule // 模块可有空端口列表 module name; // Contents of the module endmodule 所有变量声明、数据流语句、函数/任务及下层模块实例(
数组 声明的线网或变量可以是标量或向量。通过在标识符名后指定地址范围可创建任意维度数组,称为多维数组。Verilog 允许 reg、wire、integer 和 real 数据类型形成数组。 reg y1 [11:0]; // y1 是标量 reg 数组,深度=12,每个 1 位宽 wire [0:7] y2 [3:0] // y2 是 8位向量线网,深度=4 reg [7:0] y3 [0:1][0:3]; // y3 是二维数组,行=2,列=4,每个 8位宽 访问数组特定元素需为每个维度指定索引,索引可以是其他变量的表达式。数组可用于 Verilog 支持的所有数据类型。 注意:n 个 1
Verilog