1 Verilog简介(Verilog语法学习者可跳过该节)
Verilog是一门类C语言
Verilog是一门类C语言,语法与C接近,但Verilog是硬件设计语言,与C实质不同。
- Verilog代码对应硬件实体。比如在Verilog里写的
a+b
,最后会得到由硬件实现的加法器 - Verilog代码到硬件的过程叫综合。综合就是将Verilog代码转化为硬件实现
- Verilog并行执行
HDL: Hardware Description Language
Verilog HDL使硬件设计师们得以专注于逻辑,而不需要考虑硬件层面的实现。同样的代码只要经过不同的库综合,就可以在不同的硬件上运行。因此Verilog的代码具有极高的可复用性。
使用Verilog的绝大多数开发都处于寄存器传输级。
软核、固核与硬核
- 软核(Softcore):功能经过验证的、可综合的、实现后电路结构总门数在5000门以上的Verilog HDL模型。
- 固核:在某一种现场可编程门阵列(FPGA)器件上实现的,经验证是正确的总门数在5000以上的电路结构编码文件。
- 硬核:在某一种专用的半导体集成工艺(ASIC)的器件上实现的,经验证是正确的总门数在5000以上的电路结构掩膜
显然,在具体实现手段和工艺技术尚未确定的逻辑设计阶段,软核具有最大的灵活性。因此,发展软核的设计和推广软核的重用技术是非常有必要的。
自顶向下(Top-Down)设计
(对上图的讲解:P2 | 2:55)
2 Verilog基本语法
Verilog语法概述
- 行为级描述/寄存器传输级(RTL)描述/系统级描述:把module当成黑盒,描述module输入与输出的关系。门级实现由综合工具推断,常常能做到最优。
- 结构级描述/门级描述:把module作为白盒,而描述组成module的门电路(部件)
module <module_name> (in1, in2, ..., out1, out2)
input in1, in2, ...;
output out1, out2, ...;
...
endmodule
// 注释1
/* 注释2 */
基本语法
- Verilog区分大小写
- Verilog的关键字都是小写
-
module, endmodule
定义一个基本模块,两个关键字间的语句是对该模块的描述。定义模块方便复用。 -
parameter
关键字定义一个参数,增强模块的通用性 -
output, input
关键字指定输入输出 -
always @(posedge/negedge clk)
语句,每当clk出现上升/下降沿时,就执行接下来的语句块(begin, end
标志一个语句块)。这种语句构成时序逻辑,当前输出与上一个时刻的值有关 -
reg
类的变量用<=
赋值,赋值符号左侧是寄存器,右侧是更新的值 -
&q
语句输出q
每一位相与的结果,即&q=q[1]&q[2]&...&q[N]
-
assign
关键字描述组合逻辑,输出只与当前输入有关 - input/output如果没有显示声明类型,默认为
wire
类型。如果需要reg
类型以存储值,需要显示声明 -
wire
类型只是一根导线,只能用来组合逻辑。assign
语句等号右侧一旦发生变化,等号左侧立刻得到结果;而always
语句块中的值改变只能发生在每一时刻(由@
符号后的语句描述时刻) -
initial begin, end
语句块指明初始化语句,这部分指令在系统上电后直接执行 -
'timescale 1ns/1ps
指定测试的单位是1ns,精度是1ps -
#100
指令指明延时,单位由之前的代码指定 -
$stop
指令使仿真停止 - 时序逻辑:
<=
赋值;组合逻辑:-
赋值
数据类型及常量、变量
常量
语法:<位宽>'<进制><数值>
- 位宽:对应二进制的宽度
- 进制
变量
有两种类型:
- nets type
- register type
- nets type
wire
- register type
上表的后面三种类型不可以综合,主要用在算法级的开发
reg
运算符
注:==
和===
的区别在于怎么处理z
和x
的位。一般来说用==
就足够了
优先级
【重要】语句
Part 0 概述
- 赋值语句
- 连续赋值语句:
assign
语句 - 过程赋值语句:
always
开头,@
引导一个敏感列表
- 条件语句
if-else
case
(条件语句必须在顺序执行块中使用。所谓顺序执行块是指由 initial 和always语句引导的执行语句集合。除了这两种语句引导的begin_end块中可以编写条件语句外,模块中的其他地方都不能编写。)
- 循环语句
forever
repeat
while
for
- 结构说明语句
initial
always
task
function
- 编译预处理语句
- `define语句
- `include语句
- `timescale语句
Part 1 赋值、条件语句详解
always过程块
always @(<敏感信号表达式>)
begin
...
end
-
always
语句块不能嵌套 - 在敏感信号表达式前加
posedge
或negedge
关键字可以指定上升沿或下降沿触发。否则每当表达式的值发生改变,就会执行语句块
initial过程块
-
initial
过程块模拟上电之后的行为,不可综合,通常用于功能模拟的初始化,写在测试文件(testbench)中 - 同一模块中的
initial
过程块,在上电时并行执行 -
initial
过程块不能嵌套
连续赋值语句assign
-
assign
语句常用于对wire
类型变量进行赋值 - 等式左边的
wire
变量随等式右边的值一起变化
过程赋值语句
- 常用于对
reg
型变量进行赋值 - 阻塞赋值(
=
)与非阻塞赋值(<=
)
- 一个例子
// For e.g.
...
always @(posedge clk)
begin
b <= a
c <= b
end
...
// 上升沿来前,假设b=1,a=2
// 则上升沿来时,b=2,c=1,即c获得b的旧值
// 原因是非阻塞赋值下,两个赋值语句并行执行
// 若将<=全部改为=,则c=2,因为阻塞赋值是顺序执行的
对于过程赋值语句,如果是边沿触发,我们在
always
语句块里用<=
赋值,期望综合后生成寄存器;如果是电平触发,那么这个语句块实际上描述的是一段组合逻辑,我们使用=
赋值,期望综合后不使用寄存器,而生成一个组合逻辑电路
case语句
case (<敏感值表达式>)
value_1: ...;
value_2: ...;
...
default: ...; //可省略
endcase
执行哪一个语句块取决于敏感值表达式的值与哪一个情况(value_i)匹配,如果都不匹配,则执行default后面的语句
- 在敏感信号表达式的位置写
*
,综合器会推断表达式。推断的主要依据是赋值语句的右侧值
小结
语法要点:
(1) always
里面赋值左边必须声明成reg
(Verilog的语法规定。这一条引出了后续的很多要点)
(2) assign
表达式左边必须声明成wire
(3) 阻塞赋值用=
(4) 非阻塞赋值用<=
(1) 边沿触发生成寄存器的时序逻辑
(2) 电平触发且条件完整,生成组合逻辑
(3) 电平触发但条件不完整(写了if没写else,或case不完整),生成锁存器的时序逻辑
结论:
声明成reg
,不一定得到寄存器
声明成reg
,也可能得到锁存器
Coding要点:
-
如果是边沿触发的逻辑
比如always @(posedge clk)
,里面一律用<=
赋值 -
如果是电平触发的逻辑,一律用
=
赋值
逻辑简单用assign
语句;逻辑复杂用always
语句
分支条件写完整,防治出现锁存器
Part 2 循环语句详解
for语句(可综合)
语法(和C一样):
for (<变量赋初值语句>; <条件表达式>; <变量增值语句>)
循环体;
repeat语句
repeat语句常用于仿真
语法:
repeat(<循环次数表达式>)
循环体;
Part 3 结构语句详解
主要讲解function(函数)与task(任务)
function(可综合)
function常用于实现简单的函数,module里可以调用function。而function在一定意义上相当于一个module。
例子
task(不可综合)
task常用于仿真验证,语句块里可以写各种高级语句
例子(例子中的task的作用是等待number_of_edges
个下降沿)
function和task的不同
task中可以使用的高级系统函数
看到这里了?恭喜,你已经学会了Verilog的基本语法,你已经能够handle不少电路设计了!接下来是关于执行顺序的知识。设计一个电路,不仅要知道逻辑,也要知道执行顺序。
3 执行顺序
- 顺序执行块:
begin - end
(例外:always
语句块中用begin - end
和非阻塞赋值<=
时,这时是并行执行) - 并行执行块:
fork - join
举一个例子(假设例子里的时间单位是ns),
由于fork - join
语句块是并行执行的,上图中的两个语句块描述的是同一个过程:
上电(系统启动)后50ns,r='h35'
;再过50ns,r='hE2'
;再过50ns,r='h00'
;以此类推。
延伸该例子,将语句块改为
fork
begin
#50 r='h35';
#100 r='hE2';
end
#150 r='h00';
#200 r='hF7';
#250 r='h00';
end
则依据begin - end
语句块的顺序执行性,r
的值在150ns时是不确定的,因为同时有两个赋值发生。
简单总结,如果想要串行执行,需要满足两个条件:
-
begin - end
语句块 - 阻塞赋值
=
(完结撒花)
网友评论