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
-- 这是一个单行注释
/*
这是一个多行注释,
但请注意,标准VHDL不支持此种注释方式
使用时需要确保工具链兼容。
*/
  • Verilog
// 这是一个单行注释
/*
这是一个多行注释
可以跨越多行
*/

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

模块定义与端口声明

  • VHDL
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
module Counter(
    input clk,
    input reset,
    output [7:0] count
);

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

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

ieee.std_logic_1164ieee.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可省略,完整的写法如下:

module Counter(
    input wire clk,
    input wire reset,
    output reg [7:0] count
);

信号赋值

  • VHDL
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
always @(posedge clk) begin
    if (reset) 
        count <= 0;
    else 
        count <= count + 1;
end

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

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

位宽与向量操作

  • VHDL
signal my_vector : STD_LOGIC_VECTOR(7 downto 0);
my_vector <= "00001111";
  • Verilog
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
process(all)
begin
    if (a = '1' and b = '0') then
        result <= '1';
    else
        result <= '0';
    end if;
end process;
  • Verilog
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
process
begin
    for i in 0 to 7 loop
        my_array(i) <= i;
    end loop;
    wait;
end process;
  • Verilog
integer i;
always @(*)
begin
    for (i = 0; i <= 7; i = i + 1)
        my_array[i] = i;
end

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

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

函数和过程

  • VHDL
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
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
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
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
process(clk)
begin
    if rising_edge(clk) then
        if en = '1' then
            reg <= data_in;
        end if;
    end if;
end process;
  • Verilog
always @(posedge clk) begin
    if (en)
        reg <= data_in;
end

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

使用数组

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

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

描述复杂逻辑

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

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

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

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

参数化模块

  • VHDL
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
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
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
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
type reg_array is array (0 to 7) of std_logic_vector(7 downto 0);
signal registers: reg_array;
  • Verilog
reg [7:0] registers [0:7];

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

触发器实现

  • VHDL
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
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