跳转至

基本语法

硬件描述与硬件设计

一般的,我们称 SystemVerilog 为硬件描述语言而非硬件设计语言。这决定了符合语法标准的代码只是对硬件行为的一种描述而非直接的电路设计,因此描述后的语言不一定是可综合的。因此代码描述的抽象程度(底层描述能够保证可综合但更为复杂;高层描述较为简便但不保证可综合)是需要注意的。

基本结构

模块

以简单而输入与非门为例:

module and2x (
    input wire a,
    input wire b,
    output wire r); // 定义输入和输出信号
  assign r = a & b; // 主体部分,功能定义
endmodule

SystemVerilog 的程序是由若干个模块作为基本单元构造而成的。在构建硬件时可以以模块为基本单位,通过多层次的连线,将小的模块连接成大的模块,最终来构造出目标的硬件。每一个模块的内容都包含在 moduleendmodule 之间。

对于一个模块:

  1. 模块的声明和结束,模块以 module 开始,以 endmodule 结束。
  2. 端口的定义:输入端口(input),还是输出端口(output),还是双向端口(inout);端口的信号类型,常见的有 wirereg
  3. 信号类型:寄存器类型 reg 以及连线类型 wire ,如果无法确认使用 reg 还是使用 wire,都可以使用 logic 来代替。
  4. 模块的功能定义。

模块例化

and2x and2x_entity(
    .a (a[0]),
    .b (b[0]),
    .r (out));

如果某些输出端口并不需要在外部连接,例化时可以悬空不连接,甚至删除。一般来说,input 端口在例化时不能删除,否则编译报错,output 端口在例化时可以删除。

数据

常量和整数

整数的常量是按照一定的格式写出的,格式规范为:

+/- <size> ' <base><value>

进制包括了二进制(b 或者 B),十进制(d 或者 D),八进制(o 或者 O)以及十六进制(h 或者 H)。

例如,一个有效的整数例子为 4'd0(4 位宽十进制 1)。

数据取值

  • 0、1:逻辑 01,;
  • x:未知,用于不关心对应信号值的情况,该信号取任意值不影响整个逻辑电路的功能。
  • z:高阻态,典型应用为从内存中获得内存的输入。

数据类型

基本数据类型

基本数据类型包括了 wire 类型和 reg 类型。

wire 类型代表了在硬件电路中的物理连线,其特征就是输出的值紧随着输入值的变化而变化。如果没有驱动元件连接到 wire 型变量,缺省值一般为 z。

reg 数据类型会放到过程语句中,通过过程赋值语句进行赋值。reg 类型不一定必然会对应到硬件的寄存器,综合的时候会依据实际的情况使用连线(组合逻辑)或者寄存器(时序逻辑)来完成 reg 数据类型的功能。

通常情况 regwire 类型都可以通过 logic 类型来统一表达。

向量与标量

向量使用位宽来表达,使用中括号指定,形式为 [msb:lsb]。其中,msb 为最高位(most significant bit),lsb 是最低位(least significant bit)。

索引向量可以直接使用 [] 完成:

l = data[7]; // 获取data的最高位
blob = res[7:0]; // 获取数据res中的最低8位,即最低一个字节

运算

位运算

关系和逻辑运算符

全等运算符和不全等运算符:

相等运算符(==)在进行比较的时候,如果其中的某一位是高阻态(用 Z 表示)或者是不定值(用 X 表示),那么最终的结果是不定值 X。

全等运算符对于这些高阻态或者不定值也需要进行比较,只有完全一致才会获得 true 的结果。

A B A==B A===B
4b1101 4b1101 1 1
4b1100 4b1101 0 0
4b110Z 4b110Z X 1
4b110Z 4b110Z X 1

算术运算符

包括了加减乘除模运算符,这些运算符并不是最基本的运算符,在其背后综合出的电路中,需要使用对应的门电路组织成的组合逻辑来完成。

条件运算符

condition ? result1: result2

位拼接运算符

拼接组合得到新的信号向量:{a[3:0], b[7:6],c}

行为语句

always 过程语句

一个模块中可以有多个 always 过程语句。一个模块的多个 always 过程语句是 并行 执行的。

常用的 always 语句依据组合逻辑和时序逻辑分为两种:

  • always_comb:用来实现 组合逻辑
  • always_ff:用来实现 时钟边沿触发的时序逻辑

always_comb 过程语句

四选一数据选择器示例:

module mux4_1 (
    input din1,
    input din2,
    input din3,
    input din4,
    input se1,
    input se2,
    output reg out);

    always_comb
    begin
        case ({se1,se2})
            2'b00 : out = din1;
            2'b01 : out = din2;
            2'b10 : out = din3;
            2'b11 : out = din4;
        endcase
    end
endmodule

alwayss_ff 过程语句

使用 posedge 指定上升沿,使用 negedge 指定下降沿,并且将边沿敏感类型的信号放置到 always_ff 过程块的敏感信号列表中。

always_ff @(posedge clk)

begin/end 语句块

begin/end 构造出的语句块被称为是串行块,其含义就是在 begin/end 之间的语句是“顺序执行”的。(尽管顺序这一概念在硬件中并不准确)

赋值语句

赋值语句包括了 持续赋值语句过程赋值语句。持续赋值语句在过程外使用,与过程语句并行执行。过程赋值语句在过程内使用,串行执行,用于描述过程的功能。

持续赋值语句

使用 assign 作为持续赋值语句使用,用于对 wire 类型的变量进行赋值。当输入变化时经过一定的延迟输出会根据 assign 指定的规则进行变化。

过程赋值语句

非阻塞赋值语句

非阻塞赋值语句 <=

在赋值语句出现的地方不立即被赋值,等到过程块结束后再进行更新。在执行的过程中被更新的左值保持不变,通常在 always_ff 过程中使用。

阻塞赋值语句

阻塞赋值语句 =

阻塞赋值语句会按照先后顺序关系“执行”。通常用于描述组合逻辑,在 always_comb 过程中使用。

条件语句

1. if(逻辑表达式) 语句1;

2. if(逻辑表达式) 语句1;

    else 语句2;

3. if (逻辑表达式1)语句1;

    else if (逻辑表达式2 语句2;

    else if (逻辑表达式3 语句3;

    ........

    else if (逻辑表达式n 语句n;

    else 语句n+1;
case (敏感表达式)
    条件判断1:语句1
    条件判断2:语句2
    .........
    条件判断n:语句n;
    default:语句n+1;
endcase

注意此处的 case 不需要 break 就可自行跳出。

循环语句

for(循环变量赋初值;循环结束条件;循环变量增值)执行语句;

循环语句较为特殊,由于描述功能比较高层和抽象,因此转化为硬件的难度较大。


最后更新: 2022年9月18日
回到页面顶部