tags
type
status
date
slug
summary
category
password
icon
硬件描述语言(HDL)
Analysis: network ⇒ function
Synthesis: function ⇒ network
Verilog HDL或VHDL是通用的硬件描述语言。
一般包含两部分,(1)用于生成硬件(一般不能使用递归、while等)(2)用于测试(TestBench,更像计算语言,可以使用while等)
FPGA
- 板子型号
Family: Artix-7
Speed grade: 2
Package: fgg484
xc7a35tfgg484-2
- source
包括两部分,verilog和constrains
- 流程
run synthesis
run implementation
generate bitstream
tool → program device(此时需要的连线是带大转接器的线,且FPGA需要接电源)
如果需要uart串口通信,需要uart→USB的线
iverilog
cmd命令:
iverilog -o 1.out 1.v 把1.v输出为1.out,-sv是调用systemverilogvvp -n 1.out -lxt2 或 vvp 1.outgtkwave 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
不要混用无符号数和有符号数,加法、乘法的结果均可能出错。以加法为例,如果8位无符号数+8位有符号数,而无符号数首位为1,结果就会出错!
数据类型
wire
导线节点,默认的变量类型。信号流向有方向性,因此只能用assign赋值一次。只能在always块外赋值
reg
“寄存器”(不对应生成的电路中的寄存器),类似程序设计语言中的变量
wire用于数据流描述,reg用于行为级描述。reg在always块内赋值。(在always块外,reg可以出现在等号右边,但不能出现在等号左边)
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 noneinteger
位宽与编译器有关的有符号数(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

时延
`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)都必须包含在always或者initial或者generate块中
分支
- if和case必须包含在always块中,因此内部不能对wire赋值。对wire赋值可以采用
out = (sel == 0) ? a:b
- casez中,当满足多个条件时,采用最前面那个。但最好的方式还是让输入无论如何都只满足一个条件,因此
bzz10比bzz1z更好。
- if和case中最好采用互斥条件!这是一个好习惯,利于EDA形成最好的电路
- 避免latch。如果在一个模块后,某个值没有被确定,那么verilog会自动继承其上一时刻的值,称为latch。除非有意为之,否则latch的警告很有可能是bug。 为避免latch,必须确保在任何情况下所有变量都被赋值 除了使用default之外,也可以在case前赋予默认值:
循环
for
控制变量一般定义为integer,该变量只用于辅助生产代码,而在仿真时会消失。
repeat
repeat(N) 代表重复多少次while
forever
相当于
while(1)时序电路的Verilog
注意before/after clock edge
当前周期rhs均为before edge的值,而lhs在after edge变化
- 门级结构描述(RS-Latch)
理论上这可以描述所有电路,但这太繁琐了
- posedge/negedge(DFF)
- 阻塞赋值与非阻塞赋值(移位寄存器)
阻塞赋值是依次执行,因此赋值顺序非常重要;
非阻塞赋值是统一计算完所有RHS的结果之后,一起赋值给LHS(类似python),更符合硬件设计的思维
在同一模块中不要混用阻塞赋值和非阻塞赋值!
- D-Latch
- 作者:Tianyao Xiao
- 链接:https://www.xty27.top/article/Verilog
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

