1+1=10

记记笔记,放松一下...

VHDL与Verilog学习小记

接前面Questa Intel FPGA Starter学习小记(一),Questa仿真环境应该可以用了,试着了解一下VHDL与Verilog的基本语法

先列个表格,看看历史:

年份 VHDL Verilog SystemVerilog SystemC
1987 VHDL-87 (IEEE 1076-1987)
1993 VHDL-93 (IEEE 1076-1993)
1995 Verilog-95
2001 VHDL-2000 (IEEE 1076-2000) Verilog-2001 SystemC 1.0
2002 SystemC 2.0
2005 VHDL-2002 (IEEE 1076-2002) Verilog-2005 (IEEE 1364-2005) IEEE 1800-2005
2006 VHDL-2006 (IEEE 1076-2006)
2008 SystemC 2.2
2009 IEEE 1800-2009
2011 VHDL-2008 (IEEE 1076-2008)
2012 IEEE 1800-2012
2016 SystemC 2.3.1
2017 IEEE 1800-2017
2019 VHDL-2019 (IEEE 1076-2019)

Verilog从2005年开始已经融入进SystemVerilog,后面正经学习应该直接看SystemVerilog。但是网络上资源似乎都很老旧,各种写法似乎都停留在Verilog标准化之前Verilog95风格,所以本文仍然Verilog为主。

VHDL与Verilog差异对比?

VHDL(VHSIC Hardware Description Language)和Verilog是两种广泛使用的硬件描述语言(HDL),用于电子设计自动化(EDA)领域,尤其是在集成电路(IC)设计中。

语言对比

特性 VHDL Verilog
起源 由美国国防部于1980年代初发起 由Gateway Design Automation于1984年开发
设计哲学 更偏向于强类型和严格的语法 更灵活,语法较为宽松
类型系统 强类型系统,需要明确指定数据类型 弱类型系统,类型转换较自由
并发机制 使用进程(processes)来描述并发 使用always和initial块来描述并发
可读性 类似Ada,更加注重可读性和维护性 语法类似于C语言,较为简洁
调试和测试 提供强大的断言(assertions)和测试特性 测试和调试功能较为基础
应用领域 在欧洲及军事/航空航天领域更受欢迎 在美国及消费电子领域更普遍
学习曲线 相对陡峭,语法复杂 相对平缓,易于上手

一些术语差异

VHDL Verilog 描述
Entity Module 描述硬件的基本单位,用于定义输入输出端口。
Architecture Implementation Block 描述实体或模块的功能实现。
Signal Wire/Reg 用于在设计中连接不同部分的变量。Wire 是连续赋值,Reg 用于存储。
Process Always Block 描述在特定条件下执行的代码块。
Component Module Instance 用于实例化一个模块或实体,用于设计的结构化。
Configuration Configuration 用来绑定特定的实体和架构,或指定模块的特定实现。
Function/Procedure Function/Task 用于封装并重用代码,Function 返回单个值,Task 可以没有返回值。
Package Package/Library 用于定义和封装一组相关的定义和实现,以便重用。
Generic Parameter 用于在实例化模块或实体时设置模块属性的参数。
Constant Parameter/Localparam 定义在编译时已知且不可变的值。
Variable Variable 用于过程内部存储和操作数据的局部存储元素。
Attribute Attribute 用于为实体或信号添加特定的属性。
Concurrent Statement Concurrent Assignment 在架构或模块级别上同时执行的语句。
Sequential Statement Sequential Block 在过程内部按顺序执行的语句。

数据类型差异

VHDL 数据类型 Verilog 数据类型 描述
STD_LOGIC wire, reg 通用的单比特类型,表示逻辑值。
STD_LOGIC_VECTOR wire [n-1:0], reg [n-1:0] 多比特的向量类型,用于表示一组逻辑线。
BIT wire, reg 单个二进制数(0或1)。
BIT_VECTOR wire [n-1:0], reg [n-1:0] 二进制数字的向量。
BOOLEAN N/A 表示逻辑真或假。
INTEGER integer 表示整数。
UNSIGNED, SIGNED N/A 用于表示无符号和有符号整数。
REAL real 用于表示浮点数。
ENUMERATION TYPE enum 允许用户定义一组命名的常量。
ARRAY array 用于定义具有多个元素的数据结构。
RECORD struct 允许用户定义可以包含不同数据类型的复合数据结构。
ACCESS N/A 指针类型,用于动态内存分配。
FILE N/A 用于文件操作。
TIME time 用于表示时间。

大小写

  • VHDL不区分大小写。在 VHDL 中,标识符(例如变量名、信号名和模块名)的大小写不会影响其含义。例如,Signal, signal, 和 SIGNAL 在 VHDL 中被视为同一个标识符。
  • Verilog 区分大小写的语言。在 Verilog 中,所有的标识符(包括变量名、模块名等)的大小写必须保持一致。例如,counter, Counter, 和 COUNTER 在 Verilog 中会被认为是三个不同的标识符。

示例对比

没时间系统学习东西,先用一些简单的例子找找感觉

使用注释

  • VHDL
1
2
3
4
5
6
-- 这是一个单行注释
/*
这是一个多行注释,
但请注意,标准VHDL不支持此种注释方式,
使用时需要确保工具链兼容。
*/
  • Verilog
1
2
3
4
5
// 这是一个单行注释
/*
这是一个多行注释
可以跨越多行
*/

VHDL标准仅官方支持单行注释,使用--。Verilog支持类似C语言的单行(//)和多行(/* ... */)注释,这使得在代码中添加详细注释更灵活。

模块定义与端口声明

  • VHDL
1
2
3
4
5
6
7
8
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity Counter is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           count : out  STD_LOGIC_VECTOR(7 downto 0));
end Counter;
  • Veriog
1
2
3
4
5
module Counter(
    input clk,
    input reset,
    output [7:0] count
);

在VHDL中,模块被称为“实体”(entity),并且需要明确地包括库的引用。每个端口的方向(输入或输出)和类型都需要显式声明。而在Verilog中,模块定义更为简洁,端口类型和方向直接在模块声明中定义。

注意在 VHDL 中,library 和 use 声明是用来引入外部定义的包和库,这些包和库提供了额外的数据类型、子程序和功能,以便在设计中使用。

ieee.std_logic_1164 和 ieee.numeric_std 是非常常用的包。

ieee.std_logic_1164 非常重要,它定义了 std_logic 类型,这是 VHDL 中最常用的数据类型之一,用于表示数字逻辑的单个位。

std_logic 类型是一个枚举类型,包含 'U', 'X', '0', '1', 'Z', 'W', 'L', 'H', '-' 等值,分别代表未初始化、强制未知、强制为0、强制为1、高阻态、弱未知、弱0、弱1 和不关心。此包还包括多位版本std_logic_vector。

Verilog模块声明中的wire可省略,完整的写法如下:

1
2
3
4
5
module Counter(
    input wire clk,
    input wire reset,
    output reg [7:0] count
);

信号赋值

  • VHDL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
process(clk)
begin
    if rising_edge(clk) then
        if reset = '1' then
            count <= (others => '0');
        else
            count <= count + 1;
        end if;
    end if;
end process;
  • Verilog
1
2
3
4
5
6
always @(posedge clk) begin
    if (reset) 
        count <= 0;
    else 
        count <= count + 1;
end

在VHDL中,信号赋值通常发生在过程(process)块内,使用条件语句来检测时钟边沿和重置条件。Verilog使用always块来描述类似的行为,语法更接近于传统的编程语言如C。

在VHDL中,上升沿和下降沿 分别用 rising_edge和falling_edge检测;在Verilog中,posedge和negedge两个关键字直接在always块的敏感列表中使用。

位宽与向量操作

  • VHDL
1
2
signal my_vector : STD_LOGIC_VECTOR(7 downto 0);
my_vector <= "00001111";
  • Verilog
1
2
reg [7:0] my_vector;
my_vector = 8'b00001111;

VHDL和Verilog都支持向量操作,但是在VHDL中,向量的位宽和方向需要明确指定(例如7 downto 0表示从高位到低位)。Verilog中的位宽声明更加类似于C语言数组的声明方式。

对于常量,列个表格:

数据类型 VHDL 示例 Verilog 示例
整数 123 123
二进制 b"1010" 4'b1010
八进制 o"12" 3'o12
十六进制 x"1A" 8'h1A
逻辑向量 std_logic_vector'(b"1010") 4'b1010
字符 'A' (不适用)
字符串 "Hello" "Hello"
实数 1.23 1.23
定义带基数的整数 16#1A# 8'h1A

在 Verilog 中,数值常量前的数字(如 3'o12 中的 3)表示该常量的位宽。3'o12 表示一个 3 位宽的八进制数 12。12在二进制下是1010,但由于这里指定了只有 3 位宽,所以会截取最低的三位,即 010。这样,3'o12 在二进制下实际上表示的是 010。

条件判断

  • VHDL
1
2
3
4
5
6
7
8
process(all)
begin
    if (a = '1' and b = '0') then
        result <= '1';
    else
        result <= '0';
    end if;
end process;
  • Verilog
1
2
3
4
5
6
7
always @(*)
begin
    if (a == 1'b1 && b == 1'b0)
        result = 1'b1;
    else
        result = 1'b0;
end

VHDL使用=作为等于操作符,而Verilog使用==。此外,Verilog明确要求位宽和基数(如1'b1表示一个位的二进制1),而VHDL则相对更灵活。

循环控制

  • VHDL
1
2
3
4
5
6
7
process
begin
    for i in 0 to 7 loop
        my_array(i) <= i;
    end loop;
    wait;
end process;
  • Verilog
1
2
3
4
5
6
integer i;
always @(*)
begin
    for (i = 0; i <= 7; i = i + 1)
        my_array[i] = i;
end

在VHDL中,循环通常用于过程(process)中,loop语句用于迭代。Verilog的循环语法使用for循环。

注意while循环也是存在的,但是不适用于综合,只用于仿真。

函数和过程

  • VHDL
1
2
3
4
5
6
7
8
function add_two_numbers(a, b: integer) return integer is
begin
    return a + b;
end function;

-- 使用
signal result: integer;
result <= add_two_numbers(5, 3);
  • Verilog
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function integer add_two_numbers;
    input integer a, b;
begin
    add_two_numbers = a + b;
end
endfunction

// 使用
reg [31:0] result;
always @(*)
    result = add_two_numbers(5, 3);

VHDL中的函数定义比较形式化,包括详细的参数和返回类型说明。Verilog的函数定义语法上更为紧凑,函数的使用和C语言类似。

模块实例化

  • VHDL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
entity Adder is
    Port ( A : in  INTEGER;
           B : in  INTEGER;
           Sum : out INTEGER);
end Adder;

architecture Behavioral of Adder is
begin
    Sum <= A + B;
end Behavioral;

-- 实例化
signal a, b, sum : INTEGER;
begin
    u_adder: entity work.Adder
        port map (A => a, B => b, Sum => sum);
  • Verilog
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
module Adder(
    input integer A,
    input integer B,
    output integer Sum
);
    assign Sum = A + B;
endmodule

// 实例化
integer a, b, sum;
Adder u_adder (
    .A(a),
    .B(b),
    .Sum(sum)
);

在VHDL中,模块实例化需要明确指出实体和架构,通过port map来映射端口。Verilog端口连接通过.语法直接指定。

时序控制

  • VHDL
1
2
3
4
5
6
7
8
process(clk)
begin
    if rising_edge(clk) then
        if en = '1' then
            reg <= data_in;
        end if;
    end if;
end process;
  • Verilog
1
2
3
4
always @(posedge clk) begin
    if (en)
        reg <= data_in;
end

VHDL使用rising_edge函数来明确指示时钟上升沿,而Verilog直接在always块的敏感列表中使用posedge。

使用数组

  • VHDL
1
2
3
4
5
type my_array_type is array (0 to 7) of INTEGER;
signal my_array : my_array_type;
begin
    my_array(0) <= 10;
    -- 其他元素初始化
  • Verilog
1
2
3
4
5
reg [31:0] my_array [0:7];
initial begin
    my_array[0] = 10;
    // 其他元素初始化
end

在VHDL中,定义数组类型需要先声明一个类型,然后使用该类型定义信号或变量。Verilog则直接在变量声明时指定数组大小和位宽。

描述复杂逻辑

  • VHDL
1
2
3
4
process(a, b, c)
begin
    y <= (a and b) or c;
end process;
  • Verilog
1
assign y = (a & b) | c;

在描述复杂的逻辑运算时,VHDL往往需要更多的结构化代码,如process块和明确的条件语句。Verilog可以使用更简洁的表达式,如使用逻辑运算符(&代表AND,|代表OR)

verilog中,assign语句用于执行连续赋值。如果不用assign,就需要用always块。

1
2
3
always @* begin
     y = (a & b) | c;
end

参数化模块

  • VHDL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
entity GenericAdder is
    generic ( WIDTH : integer := 8 );
    port ( A : in  std_logic_vector(WIDTH-1 downto 0);
           B : in  std_logic_vector(WIDTH-1 downto 0);
           Sum : out std_logic_vector(WIDTH-1 downto 0) );
end GenericAdder;

architecture Behavioral of GenericAdder is
begin
    Sum <= A + B;
end Behavioral;
  • Verilog
1
2
3
4
5
6
7
module GenericAdder #(parameter WIDTH = 8) (
    input [WIDTH-1:0] A,
    input [WIDTH-1:0] B,
    output [WIDTH-1:0] Sum
);
    assign Sum = A + B;
endmodule

参数化模块(泛型)在VHDL中通过generic关键字实现,允许在实例化时定制属性。Verilog使用parameter关键字,语法更接近于C语言的模板或宏定义

状态机实现

  • VHDL
 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
27
type state_type is (IDLE, WORKING, DONE);
signal current_state, next_state: state_type;

process(clk)
begin
    if rising_edge(clk) then
        current_state <= next_state;
    end if;
end process;

process(current_state, input_signal)
begin
    case current_state is
        when IDLE =>
            if input_signal = '1' then
                next_state <= WORKING;
            else
                next_state <= IDLE;
            end if;
        when WORKING =>
            next_state <= DONE;
        when DONE =>
            next_state <= IDLE;
        when others =>
            next_state <= IDLE;
    end case;
end process;
  • Verilog
 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
typedef enum {IDLE, WORKING, DONE} state_type;
reg [1:0] current_state, next_state;

always @(posedge clk) begin
    current_state <= next_state;
end

always @(*) begin
    case (current_state)
        IDLE: begin
            if (input_signal)
                next_state = WORKING;
            else
                next_state = IDLE;
        end
        WORKING: begin
            next_state = DONE;
        end
        DONE: begin
            next_state = IDLE;
        end
        default: begin
            next_state = IDLE;
        end
    endcase
end

VHDL中使用type来定义状态类型,而Verilog使用enum。两者都使用条件语句来描述状态转移

寄存器组的定义和使用

  • VHDL
1
2
type reg_array is array (0 to 7) of std_logic_vector(7 downto 0);
signal registers: reg_array;
  • Verilog
1
reg [7:0] registers [0:7];

定义一组寄存器时,VHDL需要定义一个数组类型然后声明一个信号,而Verilog直接在寄存器声明中定义数组大小和位宽。

触发器实现

  • VHDL
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity DFF is
    Port ( D : in STD_LOGIC;
           CLK : in STD_LOGIC;
           Q : out STD_LOGIC);
end DFF;

architecture Behavioral of DFF is
begin
    process(CLK)
    begin
        if rising_edge(CLK) then
            Q <= D;
        end if;
    end process;
end Behavioral;
  • Verilog
1
2
3
4
5
6
7
8
9
module DFF(
    input D,
    input CLK,
    output reg Q
);
    always @(posedge CLK) begin
        Q <= D;
    end
endmodule

触发器是数字电路中的基本构件。在VHDL和Verilog中都使用了对应的时钟边缘检测机制来描述触发器的行为。Verilog的代码更简洁。

SystemVerilog

先列个表格,后面慢慢了解

特性 Verilog SystemVerilog
语言范围 硬件描述语言(HDL) 硬件描述语言 + 硬件验证语言(HVL)
数据类型 reg, wire 等基本类型 增加logic, bit, enum, struct 等
接口 使用端口连接模块 引入 interface,简化模块连接
抽象层次 RTL 和行为级建模 支持更高级的抽象如 TLM
类和对象 不支持面向对象编程 支持类和对象,具备 OOP 特性
随机化 无内置随机化功能 提供随机化和约束机制
约束和断言 无直接支持 引入断言和约束用于验证
并行处理 使用 fork-join 增强的并行处理,如 fork-join_any
模块 支持模块(module) 还引入了 program 块
覆盖 无覆盖率收集机制 支持代码和功能覆盖

EE FPGA