liehuf-notes liehuf-notes
首页
K230
Verilog
留言板
我的博客 (opens new window)
GitHub (opens new window)
首页
K230
Verilog
留言板
我的博客 (opens new window)
GitHub (opens new window)
  • 01.VerilogHDL的坤本素养
    • 1、空白符
    • 2、注释
    • 3、标识符(给变量/模块起名字)
    • 4、关键字
    • 5、数字(如何表示0、1、不定、高阻态?)
    • 1. 物理数据类型:硬件电路的“材料”
      • 1.1 连线型(Net Type)—— 相当于“电线”
      • 1.2 寄存器型(Register Type)—— 相当于“存储单元”
    • 2. 存储器型(Memory)—— 相当于“RAM/ROM”
    • 3. 抽象数据类型:辅助设计的“工具”
      • 3.1 整型(`integer`)
      • 3.2 时间型(`time`)
      • 3.3 实型(`real`)
      • 3.4 参数型(`parameter`)—— 相当于“常量”
      • 总结:数据类型的选择指南
    • 1、算术运算符:加减乘除
    • 2. 关系运算符:比较大小
    • 3. 相等运算符:判等
    • 4. 逻辑运算符:真/假判断
    • 5. 按位运算符:逐bit操作
    • 6. 移位运算符:左移/右移
    • 7. 条件运算符:简化的if-else
    • 8. 连接与复制运算符:合并信号
      • 总结:运算符优先级表
    • 1. 模块是什么?
    • 2. 模块的四大组成部分
    • 3. 模块的端口定义
    • 4. 模块的实例化:调用“积木”
    • 5. 模块的层次化设计
    • 6. 模块的测试:Testbench
      • 总结:模块设计要点
  • 02.VerilogHDL程序设计和描述方式
  • Verilog
猎户f
2025-07-10
目录

01.VerilogHDL的坤本素养

# 一、Verilog HAL语言基本要素

==准备好来自这门硬件编程语言的爱吧==~我热烈滴吻~

# 1、空白符

  • 包括空格符(\b)、制表符(\t)、换行符和换页符,编译时被忽略。

  • 示例:

    initial begin a = 3'b100; b = 3'b010; end
    
    1
  • 在加入空白符之后,代码变得更加可读:

    initial 
    begin 
        a = 3'b100; 
        b = 3'b010; 
    end
    
    1
    2
    3
    4
    5

# 2、注释

  • 作用:用来写说明文字,不会被编译,是给可爱的同学们看的笔记,防止一觉睡醒看不懂了(汇编程序员的痛)。

  • 两种写法:

    • 单行注释:用 //,直到行尾都算注释。

      assign a = b & c;  // 这是单行注释:计算a等于b和c的按位与
      
      1
    • 多行注释:用 /* */,中间可以跨多行。

      /* 这是多行注释:
         可以写很长的说明,
         比如这个模块的功能是XXX */**重要规则**:
      
      1
      2
      3
    • 重要规则

      • 多行注释不能嵌套!比如 /* 注释1 /* 注释2 */ 注释1 */ 会报错。

      • 但多行注释里可以包含单行注释:/* // 这是合法的 */。

# 3、标识符(给变量/模块起名字)

作为一名类C语言,verilog十分有素养,所以它的标识符命名方式以及注意点和C语言的变量几乎没有区别

  • 组成:字母、数字、$、_,区分大小写,首字符必须为字母或下划线。

  • 示例:count、_CC_G5(合法);30count、out*(非法)。

  • 转义标识符:以 \ 开头,以空白结尾,可包含任意可打印字符(如 \a+b=c)。

  • 合法示例:

    counter    // 纯字母
    _data_in   // 下划线开头
    $signal    // 美元符号开头(较少用)
    
    1
    2
    3
  • 非法示例:

    3state     // 数字开头
    out#put    // 包含非法字符#
    a+b        // 包含运算符+
    
    1
    2
    3
  • 特殊技巧:转义标识符

    • 如果非要起一个奇怪的名字(比如包含空格、符号),可以用 \ 开头,空格结尾。

    • 示例:

      \7400      // 实际名字是 "7400"(通常用于和传统电路编号兼容)
      \a+b=c     // 实际名字是 "a+b=c"
      \***       // 实际名字是 "***"
      
      1
      2
      3
    • 但不建议滥用,尽量用常规命名(如 and_gate 而不是 \a&b)。

# 4、关键字

就是说verilog里面已经用于设计的“标识符”不能让它换工作,这就是关键字,这点和C语言没有任何区别。总之有点:咱们是兄弟,你的就是我的,我的还是我的,那种味儿……==~emmmm~==

  • 特点:

    • 全部是小写,比如 always 是关键字,但 ALWAYS 不是。
    • 不能用来起名字!比如定义一个变量叫 module 会报错。
  • 常见关键字列表(不止这点,因为我用的框架不行):

    关键字 作用
    module 定义模块开头
    input 声明输入端口
    output 声明输出端口
    reg 声明寄存器变量
    wire 声明连线
    always 描述时序/组合逻辑

# 5、数字(如何表示0、1、不定、高阻态?)

四种基本逻辑状态:

状态 含义
0 低电平、逻辑0或假
1 高电平、逻辑1或真
x/X 不确定或未知状态
z/Z 高阻态
  • 整数表示法:<位宽>'<基数><数值>

    • 位宽:二进制位的总数(如 4'b1011 表示4位二进制数)。

    • 基数:

      基数符号 进制 合法字符
      b/B 二进制 0,1,x,X,z,Z,?, _(下划线可忽略)
      o/O 八进制 0-7,x,X,z,Z,?, _
      d/D 十进制 0-9, _
      h/H 十六进制 0-9, a-f, A-F, x,X,z,Z,?, _
    • 示例:

      8'b1010_1101    // 8位二进制,下划线提高可读性
      16'hFF00        // 16位十六进制,等于65535
      4'd10           // 4位十进制,实际存储为1010
      3'b1x0          // 3位二进制,第二位未知
      
      1
      2
      3
      4
    • 易错点:

      • 位宽不能是表达式:(2+2)'b11 ❌
      • 负号必须在最左边:-4'd3 ✅,4'd-3 ❌
  • 实数(浮点数)表示法:
    • 两种写法:
      1. 直接写小数:3.14、0.5(注意:.5 是错的,必须写 0.5)。
      2. 科学计数法:2.5e3(=2500)、1E-6(=0.000001)。

# 二、Verilog HDL基本数据类型

# 1. 物理数据类型:硬件电路的“材料”

核心思想:Verilog的数据类型是对实际硬件电路的抽象,比如电线、寄存器、存储器等。


# 1.1 连线型(Net Type)—— 相当于“电线”

  • 作用:表示电路中的物理连接,不能存储数据,只能传递信号。

  • 常见类型:

    类型 功能说明 现实类比
    wire 普通导线(默认类型) 铜线
    tri 三态导线(可高阻态) 带开关的线
    wor/trior 多驱动时,实现“线或”逻辑 多个开关并联
    wand/triand 多驱动时,实现“线与”逻辑 多个开关串联
    supply1 电源线(恒定高电平) VCC(+5V)
    supply0 地线(恒定低电平) GND(0V)
  • 声明格式:

    wire [位宽] 变量名;       // 普通连线
    tri [7:0] bus;          // 8位三态总线
    supply1 vdd;            // 电源线
    
    1
    2
    3
  • 关键特性:

    • 默认初值为 z(高阻态)。

    • 需要用 assign 或模块输出驱动:

      wire a;
      assign a = 1'b1;  // 给电线a赋值高电平
      
      1
      2

# 1.2 寄存器型(Register Type)—— 相当于“存储单元”

  • 作用:表示可以存储数据的硬件元件(如触发器、锁存器)。

  • 关键字:reg

  • 声明格式:

    reg [位宽] 变量名;  
    
    1
  • 示例:

    reg q;          // 1位寄存器(存储1bit数据)
    reg [7:0] data; // 8位寄存器(存储一个字节)
    
    1
    2
  • 关键特性:

    • 默认初值为 x(未知状态)。

    • 必须在 always 或 initial 块中赋值:

      always @(posedge clk) begin
        q <= din;  // 在时钟上升沿存储din的值
      end
      
      1
      2
      3
    • 注意:

      • reg 不一定是实际的寄存器!综合工具可能将其优化为组合逻辑。

      • 若需要明确符号,可用 reg signed:

        reg signed [3:0] num; // 4位有符号数(范围-8到7)
        num = -2;             // 存储为1110(补码)
        
        1
        2

# 2. 存储器型(Memory)—— 相当于“RAM/ROM”

  • 作用:描述硬件中的存储阵列(如内存、寄存器堆)。

  • 本质:一组 reg 的集合。

  • 声明格式:

    reg [数据位宽] 存储器名 [地址数量];  
    
    1
  • 示例:

    reg [7:0] ram [0:255]; // 256个8位存储单元(地址0~255)
    reg [31:0] rom [0:1023]; // 1KB的32位ROM
    
    1
    2
  • 操作方式:

    • 按地址读写:

      ram[0] = 8'hFF;  // 给地址0写入255
      data_out = rom[5]; // 读取地址5的数据
      
      1
      2
    • 重要限制:

      • 不能一次性读写整个存储器!必须逐个地址操作。
      • 综合时可能被映射为FPGA的Block RAM或寄存器堆。

# 3. 抽象数据类型:辅助设计的“工具”

# 3.1 整型(integer)

  • 作用:用于仿真时的数学运算(不直接对应硬件)。

  • 特点:

    • 32位有符号整数(范围 -2^31 到 2^31-1)。
    • 常用于循环计数器、临时计算。
  • 示例:

    integer i;
    for (i=0; i<10; i=i+1) begin // 循环10次
      // 执行操作
    end
    
    1
    2
    3
    4

# 3.2 时间型(time)

  • 作用:记录仿真时间(如测试延迟)。

  • 特点:

    • 64位无符号整数,单位由 ``timescale定义(如1ns`)。
    • 常与 $time 系统函数配合使用。
  • 示例

    time start_time;
    initial begin
      start_time = $time; // 记录当前仿真时间
      #10;               // 延迟10个时间单位
      $display("耗时:%t", $time - start_time);
    end
    
    1
    2
    3
    4
    5
    6

# 3.3 实型(real)

  • 作用:仿真时的浮点数计算(如模拟电路参数)。

  • 示例:

    real voltage;
    voltage = 3.3; // 表示3.3V电压
    
    1
    2

# 3.4 参数型(parameter)—— 相当于“常量”

  • 作用:定义模块中的固定值(如位宽、延迟时间)。

  • 特点:

    • 仿真前确定,运行时不可修改。
    • 提高代码可读性和可维护性。
  • 示例:

    parameter WIDTH = 8;       // 定义位宽为8
    parameter DELAY = 10;      // 定义延迟10ns
    reg [WIDTH-1:0] data;      // 实际声明为reg [7:0] data
    
    1
    2
    3

# 总结:数据类型的选择指南

类型 关键字 适用场景 硬件对应物
连线型 wire 模块间信号连接 导线
寄存器型 reg 时序逻辑存储 触发器/锁存器
存储器型 reg [] [] RAM/ROM建模 存储阵列
整型/实型 integer/real 仿真计算 无直接对应
参数型 parameter 常量定义(如位宽) 硬件配置参数

黄金法则:

  1. 连线用 wire,存储用 reg。
  2. 存储器本质是 reg 数组,按地址访问。
  3. integer/real 仅用于仿真,不生成实际电路。

常见错误:

  • 错误1:对 wire 用过程赋值(=)。

    wire a;
    always @(*) a = b; // 错!wire必须用assign驱动
    
    1
    2
  • 错误2:试图整体操作存储器。

    reg [7:0] mem [0:255];
    mem = 0; // 错!必须逐个地址初始化
    
    1
    2

# 三、运算符和表达式

# 1、算术运算符:加减乘除

符号:+、-、*、/、%(取模) 作用:数学运算,注意位宽规则!

module arithmetic;
  reg [3:0] a = 4'b1100; // 12
  reg [2:0] b = 3'b011;  // 3
  
  initial begin
    $display("a + b = %b", a + b); // 12+3=15 → 4'b1111
    $display("a - b = %b", a - b); // 12-3=9 → 4'b1001
    $display("a * b = %b", a * b); // 12*3=36 → 但a只有4位,截断后=4'b0100(36%16=4)
    $display("a / b = %b", a / b); // 12/3=4 → 4'b0100
    $display("a %% b = %b", a % b); // 12%3=0 → 4'b0000
  end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12
  • 结果位宽 = 操作数的最大位宽(如4位+3位=4位结果)。
  • 除法/取模会舍去小数(如 7/3=2)

# 2. 关系运算符:比较大小

符号:>、<、>=、<= 输出:1(真)、0(假)、x(未知)

module compare;
  reg [3:0] x = 4'b1010; // 10
  reg [3:0] y = 4'b0011; // 3
  
  initial begin
    $display("x > y? %b", x > y);  // 10>3 → 1
    $display("x < y? %b", x < y);  // 10<3 → 0
    $display("x >=10? %b", x >= 4'd10); // 10>=10 → 1
  end
endmodule
1
2
3
4
5
6
7
8
9
10

注意:若操作数含 x,结果可能是 x(如 4'b101x > 4'b0000 → x)。

# 3. 相等运算符:判等

符号:

  • ==(等于)、!=(不等)→ 可能返回 x
  • ===(全等)、!==(非全等)→ 严格比较(包括 x 和 z)
module equality;
  reg [3:0] p = 4'b101x;
  reg [3:0] q = 4'b1010;
  
  initial begin
    $display("p == q? %b", p == q);  // 101x == 1010 → x(不确定)
    $display("p === q? %b", p === q); // 101x === 1010 → 0(严格不等)
    $display("p != q? %b", p != q);  // 101x != 1010 → x
    $display("p !== q? %b", p !== q); // 101x !== 1010 → 1(确实不等)
  end
endmodule
1
2
3
4
5
6
7
8
9
10
11

何时用 ===? 测试中精确匹配高阻态 z 或未知态 x。

# 4. 逻辑运算符:真/假判断

符号:&&(与)、||(或)、!(非) 规则:非0即真(0=假,1/x/z=真)

module logical;
  reg a = 1'b1;
  reg b = 1'b0;
  reg c = 1'bx;
  
  initial begin
    $display("a && b = %b", a && b); // 1 && 0 → 0
    $display("a || b = %b", a || b); // 1 || 0 → 1
    $display("!a = %b", !a);         // !1 → 0
    $display("a && c = %b", a && c); // 1 && x → x(不确定)
  end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12

易错点:逻辑运算符会先化简操作数为1位(如 4'b1011 当作 1'b1)。


# 5. 按位运算符:逐bit操作

符号:&(与)、|(或)、^(异或)、~(非)

module bitwise;
  reg [3:0] m = 4'b1100;
  reg [3:0] n = 4'b1010;
  
  initial begin
    $display("m & n = %b", m & n); // 1100 & 1010 → 1000
    $display("m | n = %b", m | n); // 1100 | 1010 → 1110
    $display("m ^ n = %b", m ^ n); // 1100 ^ 1010 → 0110(相同为0,不同为1)
    $display("~m = %b", ~m);       // ~1100 → 0011
  end
endmodule
1
2
3
4
5
6
7
8
9
10
11

对比逻辑运算符:

  • & 是逐位与,&& 是整体逻辑与。
  • 示例:4'b1100 && 4'b1010 → 1(非0即真),但 4'b1100 & 4'b1010 → 4'b1000。

# 6. 移位运算符:左移/右移

符号:<<(左移)、>>(右移) 规则:空位补 0,不循环移位!

module shift;
  reg [3:0] num = 4'b1101; // 13
  
  initial begin
    $display("num << 1 = %b", num << 1); // 1101→1010(高位1丢弃,低位补0)
    $display("num >> 2 = %b", num >> 2); // 1101→0011(低位01丢弃,高位补0)
  end
endmodule
1
2
3
4
5
6
7
8

应用场景:

  • 左移1位 ≈ 乘以2(4'b0011<<1 → 4'b0110,即3→6)。
  • 右移2位 ≈ 除以4(4'b1100>>2 → 4'b0011,即12→3)。

# 7. 条件运算符:简化的if-else

符号:条件 ? 表达式1 : 表达式2

module conditional;
  reg sel = 1'b1;
  reg [3:0] in1 = 4'b1010;
  reg [3:0] in2 = 4'b0101;
  wire [3:0] out;
  
  assign out = sel ? in1 : in2; // sel为1选in1,否则选in2
  
  initial begin
    $display("out = %b", out); // sel=1 → out=1010
  end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12

等效代码:

if (sel) out = in1;
else out = in2;
1
2

# 8. 连接与复制运算符:合并信号

符号:

  • 连接 {a, b}:将多个信号拼接。
  • 复制 {n{a}}:重复信号n次。
module concat;
  reg [1:0] a = 2'b10;
  reg [2:0] b = 3'b110;
  
  initial begin
    $display("{a, b} = %b", {a, b}); // 10 + 110 → 5'b10110
    $display("{3{a}} = %b", {3{a}}); // 10重复3次 → 6'b101010
    $display("{2{a}, 1'b0, b} = %b", {2{a}, 1'b0, b}); // 1010 + 0 + 110 → 7'b1010110
  end
endmodule
1
2
3
4
5
6
7
8
9
10

典型用途:

  • 扩展位宽:{4{1'b1}} 生成 4'b1111。
  • 组合总线:{addr, data} 合并地址和数据。

# 总结:运算符优先级表

优先级 运算符 描述
最高 ! ~ 逻辑非、按位非
* / % 乘、除、取模
+ - 加、减
<< >> 移位
< <= > >= 关系比较
== != === !== 相等判断
& 按位与
^ ^~ 按位异或、同或
| 按位或
&& 逻辑与
最低 | | 逻辑或

黄金法则:

  1. 不确定优先级时,加括号!如 (a & b) || c。
  2. 按位操作 vs 逻辑操作:看是否需要逐bit处理。
  3. 移位运算的空位永远补 0。

# 四、模块(Module)—— Verilog的“积木块”

别怕,就是C语言的函数,有输入返回,就是他换了衣服

# 1. 模块是什么?

核心概念:模块是Verilog的基本设计单元,相当于电路中的一个功能盒子。

  • 现实类比:就像乐高积木,每个模块实现特定功能(如计数器、加法器),通过拼接构建复杂系统。

# 2. 模块的四大组成部分

每个模块都包含以下结构(以D触发器为例):

// 1. 模块声明(定义"盒子"的名字和接口)
module dff (
  input      clk,    // 2. 端口定义:输入时钟
  input      din,    //         输入数据
  output reg q       //         输出数据(用reg存储)
);

  // 3. 逻辑功能描述:当时钟上升沿到来时,存储din的值
  always @(posedge clk) begin
    q <= din;  
  end

endmodule // 4. 模块结束
1
2
3
4
5
6
7
8
9
10
11
12
13

# 3. 模块的端口定义

端口类型:

类型 方向 示例
input 输入信号 input clk;
output 输出信号 output q;
inout 双向信号(少见) inout data_bus;

端口数据类型:

  • 默认是 wire 型(如 input clk 等价于 input wire clk)。

  • 若输出需存储(如时序逻辑),需显式声明为 reg:

    output reg q;  // 输出q需要在always块中赋值
    
    1

# 4. 模块的实例化:调用“积木”

场景:在顶层模块中调用子模块(如用两个D触发器构建移位寄存器)。

# 方法1:顺序连接(按位置对应)
module top;
  wire clk, data_in;
  wire stage1, stage2;
  
  // 实例化第一个D触发器(端口顺序必须与模块定义一致!)
  dff dff1 (clk, data_in, stage1); 
  
  // 实例化第二个D触发器
  dff dff2 (clk, stage1, stage2);  
endmodule
1
2
3
4
5
6
7
8
9
10

风险:若模块端口顺序变更,所有实例化需同步修改!

# 方法2:命名连接(推荐!)
module top;
  wire clk, data_in;
  wire stage1, stage2;
  
  // 通过.端口名(信号名)明确对应关系
  dff dff1 (
    .clk(clk), 
    .din(data_in), 
    .q(stage1)
  );
  
  dff dff2 (
    .clk(clk), 
    .din(stage1), 
    .q(stage2)
  );
endmodule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

优势:

  • 顺序无关,可读性强。
  • 避免因模块定义修改导致错误。

# 5. 模块的层次化设计

关键思想:自顶向下拆分功能,逐步细化。

示例:构建一个4位加法器

  1. 顶层模块:定义输入输出

    module adder_4bit (
      input  [3:0] a, b,
      output [3:0] sum,
      output carry
    );
      // 调用子模块(全加器)
      full_adder fa0 (a[0], b[0], 1'b0, sum[0], c1);
      full_adder fa1 (a[1], b[1], c1,  sum[1], c2);
      full_adder fa2 (a[2], b[2], c2,  sum[2], c3);
      full_adder fa3 (a[3], b[3], c3,  sum[3], carry);
    endmodule
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  2. 子模块:实现全加器

    module full_adder (
      input  a, b, cin,
      output sum, cout
    );
      assign sum  = a ^ b ^ cin;
      assign cout = (a & b) | (cin & (a ^ b));
    endmodule
    
    1
    2
    3
    4
    5
    6
    7

# 6. 模块的测试:Testbench

作用:模拟输入信号,验证模块功能。

示例:测试D触发器

module testbench;
  reg  clk, din;  // 测试输入(用reg驱动)
  wire q;         // 测试输出
  
  // 1. 实例化被测模块
  dff uut (.clk(clk), .din(din), .q(q));
  
  // 2. 生成时钟(周期=10ns)
  initial begin
    clk = 0;
    forever #5 clk = ~clk; // 每5ns翻转一次
  end
  
  // 3. 提供测试激励
  initial begin
    din = 0;
    #10 din = 1;  // 10ns后输入变1
    #10 din = 0;
    #20 $finish;  // 40ns后结束仿真
  end
  
  // 4. 打印结果
  initial begin
    $monitor("Time=%t, din=%b, q=%b", $time, din, q);
  end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

输出结果:

Time=0, din=0, q=x  
Time=5, din=0, q=0  // 第一个时钟上升沿  
Time=15, din=1, q=1  // 第二个时钟上升沿  
Time=25, din=0, q=0  // 第三个时钟上升沿  
1
2
3
4

# 总结:模块设计要点

  1. 模块声明:module 模块名(端口列表);
  2. 端口方向:input/output/inout,输出可声明为 reg。
  3. 实例化:推荐用命名连接(.端口名(信号名))。
  4. 测试方法:
    • 用Testbench生成时钟和激励。
    • 通过 $monitor 或波形图观察输出。

常见错误:

  • 错误1:在 always 块中对 wire 赋值 → 应改用 reg。
  • 错误2:模块实例化时端口顺序不匹配 → 用命名连接避免。
帮助我们改善此页面 (opens new window)
上次更新: 2025/07/12, 16:01:22
02.VerilogHDL程序设计和描述方式

02.VerilogHDL程序设计和描述方式→

Theme by Vdoing | Copyright © 2025-2025 Eryajf | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式