SystemVerilog语法

Tags
SystemVerilog Syntax
Combinational Logic
Hardware Description Language
AI summary
本文介绍了SystemVerilog的语法,包括二进制运算符、always语句、组合逻辑与时序逻辑的区别、模块化设计、以及高级用法如typedef、struct、enum和interface等。强调了电路语句与赋值语句的不同,提供了实例和代码示例,帮助理解如何在硬件描述中实现复杂逻辑和电路设计。
💡
0. 二进制与运算符 1. 常用 always 语句,实现时钟同步原语 2. 一些高级用法 比如 unique_case, enum
一个5级流水线 CPU 示例图。
一个5级流水线 CPU 示例图。

Lecture1

notion image
立即数:位宽+’+进制 如16’habcd
组合/位绑定用法 {a, 1’b1} {a, {16{b}}}
在硬件描述语言中,变量名及其赋值关系被verilog 解读为电路门(与、或、非门)之间的连接,这种关系在复杂电路中可以相当复杂。

Lecture2

逻辑右移
>>
逻辑左移
<<
算术右移
>>>
缩位运算符
&
&x[31:0] → b[0:0]
|x[31:0] → b[0:0]
运算符不能作为向量,因此 (a+b)[3:0] 这种写法是非法的!
赋值语句可以左右两边都可以用位绑定 .
💡
以上都不是电路语句? 谭大爷这句话如何理解,难道这些逻辑运算不会对应到实际电路吗? 只有使用电路语句,语句才会实际地对应到一个电路。

Lecture3

电路语句,谭大爷:写电路图之前一定要先把电路图画出来。
assign 语句:assign + 赋值语句
这是最简单的电路语句。

Lecture4 元件例化

类似于面向对象的《类》
实际上用了 assign 语句就已经能够表达一个 pipelined CPU 绝大部分内容了,但是绝大情况下不会这么做。
 
好处:
  1. 代码易于维护
  1. 代码复用
  1. 黑盒化,便于开发
一个模块应当包含什么?
  1. 模块声明(不包含电路语句)
    1. notion image
  1. 电路代码
比如 assign c = a + b
  1. 模块例化
notion image
传入端口的三种方式:

Lecture5 always_comb 语句 (combination 语句)

  • 内部每一句都是赋值语句,不能有电路语句。
  • always_comb 本身是电路语句。
  • 它描述电路信号行为 (和一般的电路代码不同,内部执行的逻辑是串行的,赋值有覆盖性。
  • 内部可以放置 if, case 等控制语句。
    • 编译器分析每个语句,转换成真值表等,最终还是会成为 assign 语句,但区别在于程序员可以更简单的实现串行逻辑。
      notion image

Lecture6 always_comb 中 unique_case 以及 priority_case

unique_case 是并行比较(每个选项都是unique的
而 priority 则类似 C 语言中 switch 语法。
💡
有一个值得注意的点,unique_case 如果有 default 分支,则会引入锁存器逻辑,而锁存器是十分影响效率的。因此在实际操作中不仅尽量选择 unique_case,而且要避免写 default 分支,避免影响电路效率。
  • system verilog 中的条件语句
notion image
  • 学会 if-else ,然后忘掉它。
为了避免这样写(麻烦),可以考虑这样的写法,编译器对这样的语法有独特的优化(compiler friendly):
notion image

Lecture7 always_comb 中 if 和 for 控制语法

在 always_comb 中,for 会被默认展开(并行操作)
循环变量的上下界都应该是常数。
不应该在循环外面用循环变量,仿真可能能过,但上板会出问题。
notion image

Lecture8 电路语句(4)`always_ff`

`always_ff` 可以描述很复杂的逻辑,但那样写不直观。
值得注意的是:`always_ff` 用 <= 表示非阻塞赋值。
assign 语句中使用 = 表示阻塞赋值。
💡
阻塞赋值和非阻塞赋值的区别是什么?如何理解?
 

状态方程

💡
触发器内部描述比较复杂的逻辑,在写 always_ff 时可以先写状态方程。但是状态方程是什么?
 

组合逻辑 & 时序逻辑

💡
时序逻辑应当尽量简单,组合逻辑用来做脏活累活,实际的电路延迟也大部分由组合逻辑造成。谭大爷建议:对于不涉及触发器里的电路,全部写到组合逻辑中。
 
notion image
notion image
这两张图就描述了组合逻辑(左)与时序逻辑(右)的区别。
时序逻辑的区别在于,结果不会立即存到相应电路中,而是有一个 d 触发器。
时序逻辑描述并列触发器,always_comb 描述电路行为。因此阻塞赋值放在 always_comb,而非阻塞赋值放在 always_ff。
 

Lecture 9 高级用法(1) typedef

 

Lecture 10 高级用法(2)struct enum union

struct 举例

enum 举例

union 举例

避免 union 作为输出时的多驱动现象。
 

Lecture 11 高级用法 (3)parameter

As Template: 增加模块的 flexibility

比如适用同一算法intlong long的加法器,需要写两个。
为了使模块代码具有更高的复用性,引入参数parameter

全局变量

parameter 可以作为全局变量声明,作为一个语句,以分号结尾。
 

Lecture 12 高级用法(4)预编译指令

预编译命令作用和 C 差不多。有了预编译命令,就可以利用头文件提升代码易读性。
C 语言用 # 开头,而 system verilog 用 ` 表示开头。
用预编译可以达到的效果:
  • 配置一些参数(效果类似 parameter)但是parameter 不用反引号。
  • 根据不同参数,生成不同电路。不同于mux。
generate if 粒度为电路语句。而 ifdef 可以是任意粒度的。
  • 使用头文件,类似 package 语句。
  • 使用宏封装一些功能。部分情况下可用 function 语句。
 

Lecture 13 抽象接口(5)interface

电路图清晰地表明了元件的每位输出是从哪个元件的输出得到的,而元件例化没法做到这一点。
  • 例化不标出信号作为输入还是输出。
  • 相关模块的例化代码可能隔得比较远。
同时,模块接口部分的代码,在语法上也可以改进。
  • 不同模块可能复用一部分接口。
  • 添加一个接口,需要修改多处代码。
 
Loading...