tags
type
status
date
slug
summary
category
password
icon

硬件描述语言(HDL)

Analysis: network ⇒ function
Synthesis: function ⇒ network
Verilog HDL或VHDL是通用的硬件描述语言。
一般包含两部分,(1)用于生成硬件(一般不能使用递归、while等)(2)用于测试(TestBench,更像计算语言,可以使用while等)

iverilog

cmd命令:
iverilog -o 1.out 1.v 把1.v输出为1.out,-sv是调用systemverilog
vvp -n 1.out -lxt2vvp 1.out
gtkwave 1.vcd 查看波形
需要在1.v中添加如下命令。在哪个module里加入这个命令就会输出那个module和其调用的module里的波形

编译指令

`define `undef `include
常用:
$finish; 终止仿真
$stop; 将仿真过程暂停在某一时刻
`timescale time_unit/time_precision
`default_nettype none

注释

///* xxx */

模块

实例化

必须给模块取个名字

数值

0低电平,1高电平,x/X未知,z/Z高阻
仿真中出现X往往代表有bug,比如“线与”对烧
4'b1011,4表示bit数,‘DHBO表示基数,最后是数值
可以加入下划线增加可读性,如32'h3022_c0de
负数在最前面加-
都写上长度和基数!不然会有意料之外的bug

数据类型

wire

导线节点,默认的变量类型。信号流向有方向性,因此只能用assign赋值一次。只能在always块外赋值

reg

“寄存器”(不对应生成的电路中的寄存器),类似程序设计语言中的变量
wire用于数据流描述,reg用于行为级描述。reg在always块内赋值。

vector

多位wire或reg组成的bus。
  • 定义
reg [3:0] counter; 表示3-0(含)共4bit,且最高有效位为3。
reg [3:0] counter[3:0]; 则说明counter是4*4bit的数组。
要注意向量(packed/bus)reg[3:0]与数组(unpacked)counter[3:0]是不同的概念。向量作为一种数据类型可以整个赋值,比如counter[0]=4'hA
  • 引用
counter[31-:8]counter[31:24]等价,其中31-/+表示从第31个bit开始递减/增,取几位
  • 合成
temp1 = {data1, data2};其中的常数必须给出长度。 temp2 = {32{1’b0}} 赋值32位0
  • 容易出错的赋值
假设a是一个向量,直接进行b=a;会导致b默认为1bit的wire,从而出现bug。为防止这种情况出现,可以加上`default_nettype none

integer

位宽与编译器有关的有符号数(reg为无符号数)

time

通过$time获取当前仿真时间

parameter/localparam

常量,在module中有效

赋值与运算

❗三种描述方式(实例化,门级结构;数据流,assign,wire;行为级,always,reg)不能互相嵌套!!

style1-门级原始结构

and and1(y,x1,x2)
or or1(y,x1,x2,x3)
not not1(y,x)

style2-数据流

  • wire的赋值
assign f=(~s&x1)|(x&x2);
wire类型只能被赋值一次。
assign语句替代了一个always块,因此不能用在always里面
  • 多变量赋值:assign {o2, o1, o0} = vec;

style3-行为级

  • reg的赋值
b=4'd10
reg不是定义了一个硬件中的寄存器,它未必会体现在最后的硬件中,而只是一个EDA综合时用于运算的中间变量,是我们自己电脑的CPU中的一块内存!
  • 多变量赋值:{o2, o1, o0} = vec;
  • 注意阻塞赋值<= 与非阻塞赋值 = 的区别

运算符

~&|^分别代表非、与、或、异或。注意这些都是bit-wise operator,对向量按位计算且结果也是向量
特别的,如果把&|^作为单目运算符使用,意为reductioon operator,&4'b0111 结果是0;而~4'b0111结果是4'b1000
&& || ! == !== === !==等是logical operator,结果是1bit的值。== != 有X或Z时就返回X;而=== !== 则逐位比较,3'b1XZ===3'b1XZ = 1
notion image
notion image

时延

`timescale time_unit/time_precision
#x 延时x个time_unit

过程结构

initial

每个initial语句都是独立的,从0时刻开始执行,只执行一次。多用于初始化、仿真,不用在设计中

always

  • always @(*)
从0时刻开始执行,执行完最后一条后,如果敏感列表有变化就重复执行。内部赋值采用阻塞赋值x=y
  • always @(posedge clk)
内部采用非阻塞赋值x<=y 。非阻塞赋值同时进行,且采用上一时刻的数据,其结果直到下一个时刻才会生效。如下面代码可以实现reg值的交换。
  • 注意所有always过程结构内部的赋值都不用assign ,只能对reg等赋值(除非是在用generate生成组合逻辑电路)

generate

用来生成组合逻辑电路。控制变量为genvar。
generate中可以嵌套if,case,for等等,需要一个名字
  • 所有的分支和循环(if,case,for,while,repeat,forever,generate)都必须包含在always或者initial或者generate块中

分支

  • if和case必须包含在always块中,因此内部不能对wire赋值。对wire赋值可以采用 out = (sel == 0) ? a:b
  • casez中,当满足多个条件时,采用最前面那个。但最好的方式还是让输入无论如何都只满足一个条件,因此bzz10bzz1z更好。
  • if和case中最好采用互斥条件!这是一个好习惯,利于EDA形成最好的电路
  • 避免latch。如果在一个模块后,某个值没有被确定,那么verilog会自动继承其上一时刻的值,称为latch。除非有意为之,否则latch的警告很有可能是bug。 为避免latch,必须确保在任何情况下所有变量都被赋值 除了使用default之外,也可以在case前赋予默认值:

    循环

    for

    控制变量一般定义为integer,该变量只用于辅助生产代码,而在仿真时会消失。

    repeat

    repeat(N) 代表重复多少次

    while

    forever

    相当于while(1)

    时序电路的Verilog

    • 门级结构描述(RS-Latch)
    理论上这可以描述所有电路,但这太繁琐了
    • posedge/negedge(DFF)
    • 阻塞赋值与非阻塞赋值(移位寄存器)
    阻塞赋值是依次执行,因此赋值顺序非常重要;
    非阻塞赋值是统一计算完所有RHS的结果之后,一起赋值给LHS(类似python),更符合硬件设计的思维
    在同一模块中不要混用阻塞赋值和非阻塞赋值!
    • D-Latch
     
    LatexPython