FPGA学习

一、module :

       定义: 是构建数字系统的基本单元,用于封装电路的结构和行为。它可以表示从简单的逻辑门到复杂的处理器等任何硬件组件。

1. module 的基本定义
module 模块名 (端口列表);// 端口声明input [位宽] 输入端口1;output [位宽] 输出端口1;inout [位宽] 双向端口1;  // 双向I/O(如三态总线)// 内部信号声明wire [位宽] 内部连线;reg [位宽] 寄存器;// 逻辑功能实现// - 连续赋值语句(assign)// - 过程块(always)// - 模块实例化// - 任务(task)和函数(function)endmodule
  • 特点
    • 硬件抽象:每个模块对应实际电路的一个功能单元。
    • 层次性:模块可以嵌套实例化,构建复杂系统。
    • 端口隔离:模块通过端口与外部通信,内部实现对外部透明。

2. 简单模块示例

示例 1:2 输入与门
module and_gate (input a,      // 输入端口ainput b,      // 输入端口boutput y      // 输出端口y
);assign y = a & b;  // 连续赋值语句,实现逻辑与endmodule
示例 2:带寄存器的计数器
module counter (input clk,        // 时钟信号input reset,      // 复位信号(高有效)input enable,     // 使能信号output reg [7:0] count  // 8位计数器输出(声明为reg类型)
);always @(posedge clk) beginif (reset)count <= 8'b0;      // 复位时计数器清零else if (enable)count <= count + 1; // 使能时每个时钟周期加1
endendmodule

3. 模块实例化与连接

模块通过实例化(Instantiation)嵌入到其他模块中,形成层次化设计。

示例 3:使用 and_gate 构建更复杂的电路
module top_module (input x, y, z,output f
);wire w1, w2;  // 内部连线// 实例化and_gate模块(两个与门)and_gate u1 (.a(x),    // 连接到输入x.b(y),    // 连接到输入y.y(w1)    // 输出连接到内部信号w1);and_gate u2 (.a(w1),   // 输入连接到u1的输出w1.b(z),    // 输入连接到输入z.y(w2)    // 输出连接到内部信号w2);// 最终输出assign f = w2;endmodule

4. 参数化模块

使用 parameter 可以定义可配置的常量,增强模块的灵活性。

示例 4:参数化的计数器
module param_counter #(parameter WIDTH = 8,      // 计数器位宽,默认8位parameter INIT = 0        // 初始值,默认0
) (input clk, reset, enable,output reg [WIDTH-1:0] count
);always @(posedge clk) beginif (reset)count <= INIT;        // 复位时加载初始值else if (enable)count <= count + 1;
endendmodule

image

  • assign 是连续赋值语句,用于描述组合逻辑(无记忆,输出实时跟随输入变化)。
  • 功能:将输入 a 和 b 相加,结果赋值给输出 cVerilog 会自动处理位宽扩展(2 位 + 2 位 = 3 位,匹配 c 的位宽)。
  • 模块实例化部分(adder u1(.a(in1), .b(in2), .c(in3));

    这部分是复用已定义的模块adder),并将其端口与外部信号连接,类似 “在电路板上焊接芯片并接线”。

  • adder:要实例化的模块名(必须和前面定义的 module adder 一致)。
  • u1:实例名(自定义,同一模块可实例化多次,如 u2u3 等,用于区分不同实例)。
  • .端口名(外部信号)命名端口映射把模块的内部端口和外部信号一一对应:
    • .a(in1):模块的 a 端口 ↔ 外部信号 in1in1 需是 2 位信号)。
    • .b(in2):模块的 b 端口 ↔ 外部信号 in2in2 需是 2 位信号)。
    • .c(in3):模块的 c 端口 ↔ 外部信号 in3in3 需是 3 位信号)。
  • 外部信号的要求
  • 声明in1in2in3 必须在当前作用域(比如上层模块)中提前声明,例如:

    verilog

    • wire [1:0] in1, in2;  // 2位输入信号(wire类型,因为是组合逻辑连接)
      wire [2:0] in3;       // 3位输出信号(wire类型,因为模块输出是wire)

二、数据类型及常量变量

1、Verilog HDL有四种基本的值

image

(1)其中x和z不区分大小写;

(2)z也可以使用?表示,虽然不懂为什么不是表示未知……

2、Verilog HDL三类常量

 (1)整型:直接用数字序列表示的整数,默认采用十进制。

              1,-2;

  • 特点:书写简单,但无法明确指定数值的位宽和进制。
  • 注意:负数只能用十进制表示,不能用基数表示法表示负数。

B、基数表示:<位宽>’<进制><数字> 

image  

  • b 或 B:二进制
  • o 或 O:八进制
  • d 或 D:十进制
  • h 或 H:十六进制

 image

下划线(_):可以在数字中插入下划线,提高可读性,但不影响数值

image

  • 无符号数:默认情况下,整型常量是无符号数。
  • 有符号数:在常量前加负号(-),但负数只能用十进制表示。
  • -8'd10     // 错误!不能用基数表示法表示负数
    -10        // 正确!十进制负数
  • 不定值(x)和高阻值(z):在二进制、八进制和十六进制中,可以使用 x 或 z 表示不定值或高阻值。

(2)实数型:实数型常量用于表示带有小数部分的数值,有两种表示形式:

1. 十进制形式

直接用数字和小数点表示的实数。

3.14159      // 圆周率
-0.001       // 负数
0.5          // 小数
5.           // 等同于5.0
2. 科学计数法形式

使用指数表示的实数,格式为:<数字>e<指数> 或 <数字>E<指数>

3.6e4        // 3.6×10⁴,即36000
-1.23e-2     // -1.23×10⁻²,即-0.0123
5E7          // 5×10⁷,即50000000
3. 实数与整数的转换
  • 实数赋值给整数变量时,会自动截断小数部分,只保留整数部分。
integer a;
a = 3.9;     // a的值为3(截断小数部分)

  • 整数赋值给实数变量时,会自动转换为实数。
real b;
b = 10;      // b的值为10.0(转换为实数)

三、字符串型常量

字符串型常量用于表示一串字符,用双引号(")括起来。

"Hello, World!"    // 字符串常量
"Verilog HDL"      // 包含空格的字符串
"12345"            // 包含数字的字符串
1. 转义字符

在字符串中,可以使用转义字符表示特殊字符。

"\n"        // 换行符
"\t"        // 制表符
"\""        // 双引号
"\\"        // 反斜杠
2. 字符串的存储
  • 字符串在 Verilog 中被存储为一系列的 ASCII 字符,每个字符占用 8 位(1 字节)。
  • 字符串的长度由字符的个数决定,不包括双引号。
"ABC"       // 长度为3的字符串,占用24位(3×8)
3. 字符串操作
  • 字符串可以赋值给 reg 类型的变量,但变量的位宽必须足够容纳字符串的所有字符。
reg [7:0] str [0:2];    // 声明一个3个字符的字符串数组
initial beginstr[0] = "A";  // 或 8'd65 或 8'h41str[1] = "B";  // 或 8'd66 或 8'h42str[2] = "C";  // 或 8'd67 或 8'h43
endinitial begin$sformatf(str, "%s", "ABC");  // 格式化字符串到数组$display("str = %s", str);  // 输出:str = ABC
end
  • reg [7:0]:每个元素是 8 位宽的寄存器(对应 1 个 ASCII 字符)。
  • str [0:2]:数组索引范围是 0 到 2,共 3 个元素
  • 等效理解:这是一个 3 行 8 列 的二维数组,可存储 3 个 ASCII 字符。

  • Verilog 不直接支持字符串的拼接、比较等操作,需要使用系统任务或函数。
$display("The value is %d", 10);    // 系统任务输出字符串和变量

四、WIRE类型

(1)wire类型是一种基本的数据类型,用于表示硬件电路中的物理连线。它是组合逻辑设计的核心元素,下面详细介绍其定义、用法和注意事项:

一、核心定义

  wire 用于建模信号连接类似电路板上的导线,主要特点:

  1. 信号载体:用于传递和连接模块、门电路之间的信号。
  2. 被动赋值:不能存储值,必须由驱动源(如 assign、门级实例、模块输出)持续驱动。
  3. 默认状态:若无驱动,值为高阻态 z
  4. 硬件对应:直接映射到物理连线或组合逻辑输出。

二、声明语法

wire [位宽] 信号名1, 信号名2, ...;
  • 位宽(可选):指定信号的二进制位数,默认为 1 位。
  • 示例
wire clk;                // 1位wire信号(时钟)
wire [7:0] data;         // 8位向量(数据总线)
wire [3:0] addr, enable; // 同时声明多个wire

三、常见用法

1. 连续赋值(assign 语句)

通过表达式持续驱动 wire

wire a, b, c;
assign c = a & b;        // c等于a和b的逻辑与wire [3:0] x, y, z;
assign z = x + y;        // z等于x和y的和(组合逻辑加法)
2. 门级实例化连接

作为门电路的输入 / 输出:

wire a, b, c;
and gate1(c, a, b);      // 与门实例化,输出到c
3. 模块间信号连接

连接不同模块的端口:

module top;wire clk, rst, data_in, data_out;// 实例化时钟生成模块clock_gen clk_gen (.clk(clk), .rst(rst));// 实例化数据处理模块data_proc proc (.clk(clk), .rst(rst), .in(data_in), .out(data_out));
endmodule
4. 三态门驱动

用于总线共享场景:

wire data_bus;
wire enable;// 三态缓冲器
assign data_bus = enable ? data_out : 1'bz; // 使能时输出数据,否则高阻

五、REG类型

   REG 是一种基本的数据类型,主要用于存储数值,对应硬件中的触发器(Flip-Flop)或锁存器(Latch)。下面详细介绍其定义、用法和注意事项:

一、核心定义

reg 用于表示存储元件,具有以下特点:

  1. 存储能力:能保持当前值,直到被新值覆盖。
  2. 主动赋值:通过过程块(initial 或 always)赋值。
  3. 默认状态:初始值为不定态 x(除非显式初始化)。
  4. 硬件对应:综合后通常映射为触发器(时序逻辑)或锁存器(组合逻辑)。

 二、声明语法

reg [位宽] 信号名1, 信号名2, ...;
  • 位宽(可选):指定信号的二进制位数,默认为 1 位。

示例

reg clk;                // 1位reg信号
reg [7:0] data;         // 8位向量(可存储0~255)
reg [3:0] count;        // 4位计数器

三、常见用法

1. 时序逻辑(触发器)

在时钟边沿触发的 always 块中使用:

reg [3:0] counter;always @(posedge clk or negedge rst_n) beginif (!rst_n)         // 异步复位(低电平有效)counter <= 4'b0;else if (en)        // 使能时计数counter <= counter + 1;
end
2. 组合逻辑(锁存器)

在电平敏感的 always 块中使用(需谨慎,易产生锁存器):

reg data_out;always @(*) beginif (sel)data_out = data_in;  // 组合逻辑赋值(=)// 缺少else分支 → 综合出锁存器(不推荐)
end
3. 初始化(initial 块)

仅用于仿真,不可综合:

reg [7:0] mem [0:15];  // 存储器数组initial begin// 初始化存储器内容mem[0] = 8'hAA;mem[1] = 8'h55;// ...
end

 4. 函数和任务中的变量

function [7:0] multiply(input [3:0] a, b);reg [7:0] result;  // 函数内部变量beginresult = a * b;multiply = result;end
endfunction

 四、与 wire 的对比

特性regwire
存储能力有存储能力(保持值直到下一次赋值)无存储,仅传递信号
赋值方式只能在 initial 或 always 块中赋值由 assign、门或模块驱动
适用场景时序逻辑(如触发器)、变量存储组合逻辑连接、模块间通信
默认值不定值 x高阻态 z
赋值符号非阻塞赋值(<=)或阻塞赋值(=仅连续赋值(assign
硬件对应触发器、锁存器等存储元件物理连线、组合逻辑输出

五、使用注意事项

(1)在always语句和initial语句中的赋值对象只能是reg类型,reg类型信号也只能在always语句和initial语句中被赋值,

(2)所以,always、initial块外的赋值对象和连线用wire型信号,always、initial块内的赋值对象用reg型

六、运算符与运算表达式

image

1、算术运算符

用于数值计算,支持整数和定点数运算。

运算符描述示例
+加法c = a + b;
-减法c = a - b;
*乘法c = a * b;
/除法c = a / b;(整数除法)
%取模(取余)c = a % b;
**乘方(SystemVerilog)c = a ** b;(a 的 b 次方)

2、逻辑运算符

符号=(阻塞赋值)、<=(非阻塞赋值)
功能:将值赋给变量,核心区别在于执行时序

类型语法应用场景关键特性
阻塞赋值reg_a = reg_b;组合逻辑(always @(*)立即赋值,语句顺序影响结果(如 a=1; b=a; 中b会立即等于 1)。
非阻塞赋值reg_a <= reg_b;时序逻辑(always @(posedge clk)

3、关系运算符

符号>(大于)、<(小于)、>=(大于等于)、<=(小于等于)
功能:比较两数大小,结果为1 位布尔值1/0/X)。

示例说明
a > b无符号数比较(Verilog 默认,SystemVerilog 可通过signed声明有符号比较)。
a <= b若操作数含X/Z,结果可能为X(如 4'b10x0 <= 4'b1000 结果为X)。

4、相等运算符

符号==(逻辑相等)、!=(逻辑不等)
功能:比较两数是否相等(==)或不等(!=),不严格匹配X/Z

示例说明
a == b仅比较0/1位,若含X/Z,结果可能为X(如 4'b10x0 == 4'b1000 结果为X)。
a != b只要有一位0/1不同,结果为1;含X时结果可能为X
扩展===(全等,严格匹配X/Z)和!==(非全等),图中未列但常用。

5、逻辑运算符

符号&&(逻辑与)、||(逻辑或)、!(逻辑非)
功能:对布尔值(非零视为,零为)进行逻辑运算,结果为1 位

示例说明
(a>0) && (b<5)两边均为(非零)时,结果为1;否则为0
!(a==0)a非零,结果为1;否则为0
与位运算符的区别
  • 逻辑运算:结果 1 位;位运算(如&/|)逐位处理,结果位宽同操作数。
  • 示例:wire log = (a && b);(逻辑与) vs wire bit = a & b;(位与)。

6、位运算符

符号~(按位取反)、&(按位与)、|(按位或)、^(按位异或)
功能:对操作数逐位进行逻辑运算,结果位宽与操作数一致。

运算符示例说明
~~4'b1010 = 4'b0101每一位取反(1→00→1)。
&4'b1010 & 4'b1100 = 4'b1000逐位与(1&1=1,否则0)。
```4'b10104'b1100 = 4'b1110`逐位或(`00=0,否则1`)。
^4'b1010 ^ 4'b1100 = 4'b0110逐位异或(相同为0,不同为1)。
扩展:同或(~^ 或 ^~),即异或非(如 4'b1010 ~^ 4'b1100 = 4'b1001)。

7、移位运算符

符号<<(逻辑左移)、>>(逻辑右移)
功能:将操作数按位移动,空位补 0(逻辑移位)。

运算符示例说明
<<4'b1010 << 1 = 4'b0100左移 1 位,低位补 0(值变为原来的 2 倍,注意位宽截断)。
>>4'b1010 >> 1 = 4'b0101右移 1 位,高位补 0(值变为原来的 1/2,整数除法)。
注意
  • Verilog 的>>对有符号数也会补 0(逻辑移位),SystemVerilog 的>>>才是算术移位(补符号位)。
  • 移位位数需为常量(综合要求),如 a << 2(合法),a << b(非法,b需是常量)。

8、条件运算符

符号?:(三元运算符)
语法condition ? expr1 : expr2
功能:条件为(非零)时,结果为expr1;否则为expr2

示例说明
assign mux = sel ? a : b;sel=1asel=0b(实现多路选择器)。
assign max = (a>b) ? a : b;ab中的较大者(支持嵌套,如三数取最大)。
注意expr1expr2需位宽兼容,否则会截断(如16'b08'b1拼接会报错,需显式扩展位宽)。

9、连接和复制操作符

符号{}(大括号)
功能

  • 连接(Concatenation):将多个信号按位拼接。
  • 复制(Replication):将信号重复拼接(格式:{n{expr}}n为常量)。
1. 连接示例
// 示例1:简单拼接
wire [3:0] a = 4'b1010, b = 4'b0011;
wire [7:0] c = {a, b};  // 结果:8'b1010_0011(a是高4位,b是低4位)// 示例2:位逆序(图中案例)
assign bus[3:0] = {bus[0], bus[1], bus[2], bus[3]};  
// 原bus[3:0] = 4'b1010(位3=1,位2=0,位1=1,位0=0)→ 拼接后为{0,1,0,1} → 4'b0101(位序反转)。

2. 复制示例 

// 示例1:信号复制
wire [1:0] a = 2'b10;
wire [5:0] b = {3{a}};  // 结果:6'b10_10_10(a重复3次)// 示例2:图中案例
assign bus[3:0] = {2{bus[0]}, 2{bus[3]}};  
// 假设bus[0]=1(1位),bus[3]=0(1位)→ 复制后:{1,1,0,0} → 4'b1100。

九、begin_end

1、定义:

    用于将多条语句组合成一个顺序执行的代码块,类似于 C 语言中的花括号{}。它主要用于alwaysinitialtaskfunction等过程块中,确保语句按顺序执行。

begin语句1;语句2;// ... 更多语句
end
  • 顺序执行:块内语句按书写顺序依次执行(仅适用于过程块)。
  • 命名块:可通过begin: 块名为块命名,用于调试或声明局部变量。

 2. 在always块中的应用

示例 1:组合逻辑(阻塞赋值)

always @(*) beginif (sel == 1'b0)out = a;elseout = b;
end

  • 说明
    • begin-endif-else语句组合为一个整体。
    • 使用阻塞赋值=),确保语句按顺序执行。
示例 2:时序逻辑(非阻塞赋值)
always @(posedge clk or posedge reset) beginif (reset)q <= 1'b0;      // 异步复位elseq <= d;         // 时钟上升沿更新
end
  • 说明
    • 使用非阻塞赋值<=),避免竞争冒险。
    • begin-end包裹复位和赋值操作。

3. 在initial块中的应用

示例:测试平台的初始化
initial begin// 初始化信号clk = 0;reset = 1;data = 8'h00;// 控制时序#10 reset = 0;      // 10个时间单位后释放复位#20 data = 8'h55;   // 再20个时间单位后写入数据#30 $finish;        // 结束仿真
end
  • initial块仅执行一次,常用于测试平台(Testbench)。
  • begin-end组合多条初始化和延时语句。

4. 在taskfunction中的应用

示例:任务(task)中的顺序操作

​​​​​​​

2、语句:顺序块

(1)块内的语句顺序执行

(2)每条语句的延时为相对前一句

(3)最后一句执行完,才能跳出该块

image

十、fork_join语句:并行块

(1)块内语句同时执行

(2)每条语句的延时为相对于进入块仿真的时间

(较为少用)

image

十一、if else语句(需要在always块中使用)

if(表达式)   语句;
else if(表达式)  语句;
else   语句;
(多个语句需放在begin end间)

image

十二、case语句:多分支语句(需要在always块中使用)

case(表达式)分支:语句……default:语句;endcase

十三、forever连续执行,常用于产生时钟信号

image

十四、while执行语句

image

十五、repeat

    连续执行语句n次

    repeat(表达式),在此表达式通常为常量表达式,表示重复次数。

    begin语句;end

十六、for

image

十七、initial

        initial:是一种用于初始化和仿真控制的过程块,主要用于测试平台(Testbench)和仿真场景。initial 块的特点如下:
  1. 执行一次:在仿真开始时执行一次,执行完毕后不再执行。
  2. 仿真专用:不可综合为硬件,仅用于仿真验证。
  3. 并行执行:多个 initial 块并行执行(与代码顺序无关)。
  4. 行为描述:用于控制信号时序、生成激励或监控输出。

二、语法格式

initial begin// 语句序列
end
  • begin-end:可选,若有多条语句则必须使用;单条语句可省略。

示例

initial begina = 1'b0;      // 初始化信号a为0#10 a = 1'b1;  // 延迟10个时间单位后,a置为1#20 $finish;   // 再延迟20个时间单位后,结束仿真
end
  • #10 是 Verilog 的延迟语句,表示暂停执行 10 个时间单位(时间单位由仿真器或 timescale 指令定义)。
  • 延迟结束后,执行 a = 1'b1
#20 $finish;
  • 功能:再延迟 20 个时间单位后,调用系统任务 $finish 结束仿真。
  • 语法
    • $finish 是 Verilog 的系统任务,用于终止当前仿真。
    • 整个 initial 块的执行时间为 30 个时间单位(10 + 20)。

三、常见用法

1. 信号初始化

在仿真开始时设置信号初始值:

reg clk, rst_n;
reg [7:0] data;initial beginclk = 1'b0;      // 初始化时钟为0rst_n = 1'b0;    // 初始化复位信号为低(复位状态)data = 8'h00;    // 初始化数据为0
end
  • clk = 1'b0  1'b0 表示 1 位二进制数(1'b 是 Verilog 的二进制字面量格式)
    将时钟信号初始化为低电平。通常配合后续的时钟生成代码(如forever #5 clk = ~clk)。
  • rst_n = 1'b0
    将复位信号初始化为低电平。大多数设计采用异步低电平复位,即rst_n=0时电路进入复位状态。
  • data = 8'h00
    将数据信号初始化为 0。8'h00表示 8 位十六进制数00(二进制0000_0000)。
2. 生成时钟信号

通过循环生成周期性时钟:

initial beginforever begin#5 clk = ~clk;  // 每5个时间单位翻转一次,生成周期为10的时钟end
end
3. 产生测试激励

按特定时序提供输入信号:

initial begin// 复位序列rst_n = 1'b0;#20 rst_n = 1'b1;  // 20个时间单位后释放复位// 输入数据序列#10 data = 8'hAA;  // 释放复位后10个时间单位,发送数据AA#20 data = 8'h55;  // 再20个时间单位后,发送数据55#30 $finish;       // 结束仿真
end
4. 文件操作

读取测试数据或写入仿真结果:

integer file;
reg [7:0] data;initial beginfile = $fopen("input.txt", "r");  // 打开输入文件if (file == 0) begin$display("Error: Cannot open file!");$finish;endwhile (!$feof(file)) begin$fscanf(file, "%h", data);  // 从文件读取16进制数据#10;                        // 等待10个时间单位end$fclose(file);
end
5. 仿真控制

使用系统任务控制仿真流程:

initial begin#1000 $finish;  // 1000个时间单位后自动结束仿真// 打印关键信息$display("Simulation started at time %0t", $time);$monitor("At time %0t: a=%b, b=%b, sum=%b", $time, a, b, sum);
end

十八、always 块

        是用于描述时序逻辑或组合逻辑的核心结构。它允许代码在特定条件下重复执行,是构建硬件电路行为模型的基础。

1. always 块的基本定义

always @(触发条件) begin// 过程化语句
end
  • 功能always 块会在触发条件满足时执行,执行完毕后等待下一次触发。
  • 触发条件(敏感列表):
    • 边沿触发(时序逻辑):@(posedge clk)(上升沿)或 @(negedge clk)(下降沿)。
    • 电平触发(组合逻辑):@(a or b) 或 @(*)(自动推断所有输入信号)。

                1. @(a or b) 的含义

                        触发条件:当信号ab的值发生变化时,always块内的代码会立即执行。  

     2、@(*) 的含义

                        自动推断:编译器会自动分析always块内的代码,将所有读操作的输入信号添加到敏感列表中。

always @(*) begin  // 等价于 @(a or b or c)out = a & b | c;  // 自动推断敏感信号为a、b、c
end

2. 时序逻辑中的 always 块

用途:描述触发器、寄存器等时序元件。

示例 1:同步复位的 D 触发器
always @(posedge clk) beginif (reset)        // 同步复位(高电平有效)q <= 1'b0;    // 复位时输出清零elseq <= d;       // 时钟上升沿时,d的值赋给q
end
  • 特点
    • 使用非阻塞赋值<=)保证正确的时序行为。
    • 仅在时钟上升沿触发,复位信号 reset 必须与时钟同步。
示例 2:带使能的计数器
reg [7:0] count;
always @(posedge clk) beginif (reset)count <= 8'b0;else if (enable)count <= count + 1;  // 每个时钟周期加1
end

 3. 组合逻辑中的 always 块

用途:描述逻辑门、多路选择器等组合电路。

示例 3:4 选 1 多路选择器
always @(*) begin  // 敏感列表为*,自动包含所有输入case (sel)2'b00: out = a;2'b01: out = b;2'b10: out = c;2'b11: out = d;default: out = 1'bx;  // 避免锁存器生成endcase
end
第 1 行:always @(*)
  • 触发条件*表示敏感列表自动包含所有在块内被读取的信号(即selabcd)。
  • 组合逻辑标志always @(*)是 Verilog 中定义组合逻辑的标准写法。
第 2-7 行:case语句
  • 选择逻辑
    • sel2'b00时,输出a
    • sel2'b01时,输出b
    • sel2'b10时,输出c
    • sel2'b11时,输出d
第 8 行:default: out = 1'bx
  • 默认分支:处理sel为非法值(如2'bx2'bz)的情况。
  • 避免锁存器:明确指定所有可能的输入场景,防止综合工具生成不必要的锁存器。
  • 特点
    • 使用阻塞赋值=)保证语句按顺序执行。
    • 敏感列表需包含所有输入信号(或用 * 自动推断),否则可能导致仿真与综合结果不一致。
示例 4:组合逻辑实现的加法器

always @(a or b) begin  // 等价于 @(*)sum = a + b;
end

4. 混合逻辑中的 always 块

用途:同时包含时序和组合逻辑的复杂电路。

示例 5:带保持功能的寄存器
always @(posedge clk or posedge reset) beginif (reset)q <= 1'b0;else if (load)q <= d;         // 加载新数据elseq <= q;         // 保持原数据(等价于不操作)
end

5. 无限循环的 always 块

用途:生成时钟信号(仅用于仿真)。

示例 6:时钟生成
reg clk;
always #5 clk = ~clk;  // 生成10个时间单位周期的时钟(50%占空比)

6. 敏感列表的注意事项

  1. 组合逻辑必须包含所有输入

// 错误示例:敏感列表遗漏b,可能导致仿真错误
always @(a) beginout = a & b;  // 当b变化时,out不会更新
end// 正确写法:
always @(a or b) begin ... end  // 或使用 @(*)

时序逻辑通常仅对时钟边沿敏感: 

// 同步复位的正确写法
always @(posedge clk) beginif (reset) ...
end// 异步复位的写法(复位信号独立触发)
always @(posedge clk or posedge reset) beginif (reset) ...
end

7. 阻塞赋值和非阻塞赋值类型对比

特性阻塞赋值(=)非阻塞赋值(<=)
执行顺序立即执行,按顺序执行并行执行,在时间步结束时更新
适用场景组合逻辑时序逻辑
硬件对应逻辑门触发器 / 寄存器

 image

十九、function

用于定义可重复使用的组合逻辑单元,类似于软件中的函数。它接收输入参数,执行计算,并返回单个结果。

1. function 的基本定义

function [返回值位宽] 函数名;input [参数位宽] 参数1;input [参数位宽] 参数2;// 局部变量声明reg [位宽] 变量名;// 函数体begin// 计算逻辑函数名 = 表达式;  // 将结果赋给函数名本身end
endfunction
  • 特点
    • 组合逻辑:函数内不能包含时序控制(如 #延迟@事件)。
    • 立即返回:执行完毕后立即返回结果。
    • 单一返回值:通过函数名本身赋值返回结果。

2. 简单函数示例

 示例 1:计算两个数的和

function [7:0] add_numbers;input [7:0] a;input [7:0] b;
beginadd_numbers = a + b;  // 返回a和b的和
end
endfunction

调用方式

result = add_numbers(10, 20);  // result = 30

示例 2:判断奇偶性 

function bit is_odd;input [7:0] num;
beginis_odd = (num % 2 == 1);  // 返回1(奇数)或0(偶数)
end
endfunction

调用方式:  

 result = is_odd(number);  // result = 1'b1

3. 带局部变量的函数

function [7:0] max_of_three;input [7:0] a, b, c;reg [7:0] temp;  // 局部变量
begintemp = (a > b) ? a : b;max_of_three = (temp > c) ? temp : c;  // 返回三者中的最大值
end
endfunction

调用方式: 

max_value = max_of_three(a, b, c);  // max_value = 25

 4. 多维数组作为参数

function [7:0] sum_array;input [7:0] array [0:3];  // 4元素的数组参数reg [7:0] i, sum;
beginsum = 0;for (i = 0; i < 4; i = i + 1) beginsum = sum + array[i];endsum_array = sum;  // 返回数组元素的和
end
endfunction

 调用方式

  initial begin// 初始化数组元素my_array[0] = 8'd10;my_array[1] = 8'd20;my_array[2] = 8'd30;my_array[3] = 8'd40;// 调用函数计算数组总和result = sum_array(my_array);  // result = 100 (8'd100)

5. 函数的递归调用

// 计算阶乘(n!)
function [31:0] factorial;input [7:0] n;
beginif (n == 0)factorial = 1;elsefactorial = n * factorial(n-1);  // 递归调用
end
endfunction

  调用方式

module test_factorial;reg [7:0] input_n;     // 输入值reg [31:0] result;     // 存储结果initial begininput_n = 5;       // 计算 5!// 调用函数result = factorial(input_n);  // result = 120 (5! = 120)$display("%d的阶乘是: %d", input_n, result);  // 输出: 120end

二十、task:

        用于定义可复用的代码块,类似于function,但功能更强大。与function只能实现组合逻辑不同,task可以包含时序控制(如延时、事件触发),支持多输出参数,且不要求立即返回结果。

1. task的基本定义

task 任务名;input [位宽] 输入参数1;    // 输入参数(可选)output [位宽] 输出参数1;   // 输出参数(可选)inout [位宽] 双向参数1;    // 双向参数(可选)reg [位宽] 局部变量;       // 局部变量(可选)// 任务体begin// 可包含时序控制和过程化语句#延迟;                 // 延时语句@(事件);               // 事件触发wait(条件);            // 等待条件满足// 逻辑操作输出参数1 = 表达式;     // 赋值给输出参数end
endtask
  • 特点
    • 时序支持:可包含#延迟@事件wait等时序控制语句。
    • 多参数传递:通过outputinout参数返回多个值。
    • 过程化执行:任务内的语句按顺序执行。

2. 简单任务示例

示例 1:带延时的信号生成
task generate_pulse;input [7:0] width;  // 脉冲宽度(时间单位)output pulse;       // 输出脉冲信号
beginpulse = 1'b1;       // 脉冲置高#width;             // 保持width个时间单位pulse = 1'b0;       // 脉冲置低
end
endtask

调用方式

reg my_pulse;
generate_pulse(10, my_pulse);  // 生成宽度为10的脉冲
示例 2:带输入输出的任务
task add_numbers;input [7:0] a, b;   // 输入两个数output [8:0] sum;   // 输出和(9位以避免溢出)
beginsum = a + b;        // 计算和
end
endtask

 调用方式

reg [7:0] x = 5, y = 10;
reg [8:0] result;
add_numbers(x, y, result);  // result = 15 (9'd15)

 3. 包含时序控制的任务

task wait_and_check;input signal;       // 待监测的信号input [7:0] timeout; // 超时时间output success;     // 检查结果(成功/失败)
beginsuccess = 1'b0;     // 默认失败// 等待信号变高或超时forkbegin@(posedge signal);  // 等待信号上升沿success = 1'b1;     // 成功endbegin#timeout;           // 超时等待endjoin_anydisable fork;       // 终止未完成的线程
end
endtask

调用方式

reg flag, check_result;
wait_and_check(flag, 100, check_result);  // 等待flag上升沿,最多100个时间单位

4. 在模块中调用任务

module test_task;reg clk, reset, enable;reg [7:0] data_in, data_out;initial begin// 初始化信号clk = 0;reset = 1;enable = 0;// 调用任务initialize_system();  // 初始化系统generate_clock(10);   // 生成周期为10的时钟// 执行操作#50;reset = 0;  // 释放复位#20;process_data(8'd42, data_out);  // 处理数据end// 任务定义task initialize_system;begin// 系统初始化操作reset = 1;enable = 0;#20;  // 保持复位20个时间单位endendtasktask generate_clock;input period;beginforever begin#(period/2) clk = ~clk;  // 生成时钟endendendtasktask process_data;input [7:0] input_data;output [7:0] output_data;begin@(posedge clk);  // 等待时钟上升沿if (!reset && enable) begin// 数据处理逻辑output_data = input_data * 2;endendendtask
endmodule

    函数与任务(task)的对比

    特性函数(function)任务(task)
    返回值必须有一个返回值(通过函数名赋值)可以没有返回值,或通过 output 参数返回
    时序控制不能包含(如 #@wait可以包含时序控制语句
    调用可在表达式中直接调用必须单独作为一条语句调用
    适用场景组合逻辑计算时序逻辑或复杂操作

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/pingmian/87291.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87291.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    26-计组-存储器与Cache机制

    一、存储器与局部性原理 1. 局部性原理 基础概念&#xff1a; 时间局部性&#xff1a;一个存储单元被访问后&#xff0c;短时间内可能再次被访问&#xff08;例如循环变量&#xff09;。空间局部性&#xff1a;一个存储单元被访问后&#xff0c;其附近单元可能在短时间内被访…

    I/O 线程 7.3

    前言 以下&#xff1a; 概述 1.基础 2.代码演示 3.练习 4.分析题 1.基础 一、线程基础概念 并发执行原理 通过时间片轮转实现多任务"并行"效果 实际为CPU快速切换执行不同线程 线程 vs 进程 线程共享进程地址空间&#xff0c;切换开销更小 进程拥有独立资源&am…

    MySQL JSON数据类型完全指南:从版本演进到企业实践的深度对话

    &#x1f4ca; MySQL JSON数据类型完全指南&#xff1a;从版本演进到企业实践的深度对话 在当今数据驱动的时代&#xff0c;MySQL作为最受欢迎的关系型数据库之一&#xff0c;不断演进以满足现代应用的需求。JSON数据类型的引入&#xff0c;让MySQL在保持关系型数据库优势的同时…

    BI × 餐饮行业 | 以数据应用重塑全链路业务增长路径

    在竞争激烈的餐饮行业中&#xff0c;数据已成为企业保持竞争力的关键资产。通过深入分析顾客数据&#xff0c;餐饮企业能够洞察消费者的需求和偏好&#xff0c;从而提供更加精准和个性化的服务。此外&#xff0c;利用数据优化业务管理&#xff0c;降低成本&#xff0c;并提高运…

    【学习线路】机器学习线路概述与内容关键点说明

    文章目录 零、机器学习的企业价值一、基础概念1. 机器学习定义2. 学习类型3. 学习范式 二、核心算法与技术1. 监督学习2. 无监督学习3. 模型评估与优化 三、深度学习与神经网络1. 神经网络基础2. 深度学习框架3. 应用场景 四、工具与实践1. 数据处理2. 模型部署3. 机器学习的生…

    Linux 命令:cp

    Linux cp 命令详细教程 cp 是 Linux 系统中最常用的命令之一&#xff0c;用于复制文件或目录。它可以将源文件/目录复制到指定的目标位置&#xff0c;支持批量复制、强制覆盖、保留文件属性等功能。下面详细介绍其用法。资料已经分类整理好&#xff1a;https://pan.quark.cn/s…

    java分页插件| MyBatis-Plus分页 vs PageHelper分页:全面对比与最佳实践

    MyBatis-Plus分页 vs PageHelper分页&#xff1a;全面对比与最佳实践 一、分页技术概述 在Java持久层框架中&#xff0c;分页是高频使用的功能。主流方案有&#xff1a; MyBatis-Plus分页&#xff1a;MyBatis增强工具的内置分页方案PageHelper分页&#xff1a;独立的MyBatis…

    PROFINET转MODBUS TCP网关在机械臂通信操作中的应用研究

    在特定的汽车零部件生产工厂焊接生产线上&#xff0c;机械臂被应用于焊接作业&#xff0c;其控制体系基于Profinet协议。同时&#xff0c;工厂的自动化控制体系以西门子S7-1200PLC为核心&#xff0c;通过ModbusTCP协议实现数据交换。为实现焊接过程的自动化控制以及生产数据的实…

    Mac中如何Chrome禁用更新[update chflags macos]

    写在前面 在 macOS 系统中&#xff0c;系统更新提示的小红点常常让人不胜其扰。 尤其是当你希望保持现有系统的稳定性&#xff0c;或因兼容性问题暂不想升级时&#xff0c;这个小红点就像一个顽固的提醒。 - windowsMac版直接删除更新程序, 有效 cd ~/Library/Google/Googl…

    LoRA使用-多个LoRA

    LoRA的风格分类 不用去记它有什么很特别的风格&#xff0c;简单来说基础模型就像一个全能画手&#xff0c;什么都能画&#xff0c;而LoRA是在某个风格中经过特训的它的一个分身。使得它更精通该风格。 关于LoR风格分类&#xff1a;提示词撰写公式 Checkpoint&LoRA对比 训…

    牛客刷题 — 【排序】[NOIP2012] 国王的游戏(高精度结构体排序)

    1.题面&#xff1a;传送门 2. 思路&#xff1a; 相邻的两个大臣的先后顺序只会互相影响&#xff0c;并不会影响其他人的金币数。 假设前 i-1 个人左手上的数乘积为 s 。 ① 若 A 大臣排在B 大臣的前面&#xff0c;则&#xff1a; s 此时的金币数最大值为 。 ② 若B大臣排…

    grpc 和限流Sentinel

    基于gRPC的微服务通信模块技术方案书 1. 总体架构设计 #mermaid-svg-TiN9cudEfW5mCWHm {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-TiN9cudEfW5mCWHm .error-icon{fill:#552222;}#mermaid-svg-TiN9cudEfW5mCWHm…

    经典灰狼算法+编码器+双向长短期记忆神经网络,GWO-Transformer-BiLSTM多变量回归预测,作者:机器学习之心!

    经典灰狼算法编码器双向长短期记忆神经网络&#xff0c;GWO-Transformer-BiLSTM多变量回归预测&#xff0c;作者&#xff1a;机器学习之心&#xff01; 目录 经典灰狼算法编码器双向长短期记忆神经网络&#xff0c;GWO-Transformer-BiLSTM多变量回归预测&#xff0c;作者&#…

    VGG Image Annotator (VIA):一款免费的数据标注软件介绍与使用

    VGG Image Annotator (VIA)&#xff1a;一款免费的数据标注软件介绍与使用 在计算机视觉领域&#xff0c;数据标注是训练机器学习模型的基础步骤之一&#xff0c;而标注工具的选择直接影响标注的效率和准确性。众多标注工具中&#xff0c;VGG Image Annotator (VIA) 是一个开源…

    CSS实现百分比水柱图

    背景 在echarts没发现有可以直接使用的展示百分比的柱形图,只好自己封装一个组件使用 实现思路 一、图形拆解 要实现的组件是一个 可配置的圆柱形液柱图组件&#xff0c;常用于展示比例进度&#xff0c;比如任务完成度、指标达成率等。把图拆成最小单元然后拼接起来&#x…

    详解 rzsz 工具:Windows 与 Linux 文件传输

    &#xff08;Linux之软件包管理器&#xff08;CentOS系统&#xff09; —— yum-CSDN博客&#xff09;rzsz工具之前我在这篇文章中介绍过&#xff0c;现在重新详细介绍一下该工具。rzsz 是一个用于在 Windows 和 Linux 系统之间传输文件的工具集&#xff0c;通常通过终端模拟器…

    网络编程1(UDP)

    网络编程套接字&#xff08;socket api&#xff09; 了解了网络的一些概念&#xff0c;接下来就要进行网络中的跨主机通信&#xff0c;了解网络中的一些API&#xff0c;这里谈到的API都是针对传输层进行的&#xff0c;这是因为我们编写的代码是在应用层&#xff0c;而传输层就…

    【电机】定点线性映射

    这是一个定点数线性映射的问题&#xff0c;通常用于将浮点型的物理量&#xff08;如速度、位置、扭矩&#xff09;转换为嵌入式系统中使用的整型数据格式&#xff0c;便于通过 CAN 总线或其它通信协议发送给电机控制器。 我们来逐步解析这个过程&#xff0c;并以“速度”为例说…

    Spring Cloud 微服务(远程调用与熔断机制深度解析)

    &#x1f4cc; 摘要 在微服务架构中&#xff0c;服务之间的远程调用是构建分布式系统的核心环节。然而&#xff0c;随着服务数量的增加和网络复杂度的提升&#xff0c;调用失败、延迟高、异常等问题变得越来越频繁。 为此&#xff0c;Spring Cloud 提供了强大的远程调用组件 …

    electron-vite 抽离config.js

    1、将config.js 放到resources下的config目录下 module.exports {url: http://192.168.1.17:8000,wsUrl: ws://192.168.1.17:8000, }2、在preload.js 暴露读取API src/preload/index.js(或你的preload入口) const fs require(fs); const path require(path);function getCo…