1. 什么是HDL ?
HDL, hardware description language, 硬件描述语言
其分为两部分:
a. 逻辑功能设计.
用HDL 语言在不同层次描述对数字电路的功能、结构和行为进行描述.
b. 电路实现
通过综合工具将逻辑设计转化为门级电路网表,再将其与某种工艺的
基本元件逐一对应起来,再通过布局,布线工艺转换为电路的布局,布线
美国,中国,日本多使用verilog, 欧洲多使用vhdl
2. verilog 是什么?
verilog 是一种类C 语言, 它的词法和语法借鉴了C, 减小了它的入门门槛.
但verilog 是硬件描述语言,数字电路是它的基础,运算是并行的,这是与C不同之处.
1. 基础知识:(词法)
1.1: 逻辑值, 0,1,X,Z(低,高,不定,高阻)
1.2: 数据的表示方式:
表示方式是<位宽’类型数值>
常见类型: b,d,h (二进制,十进制,16进制)
例如:
4’b0101
4’d9
4’hb
16’b0101_1010_0101_1010 = 16’h5a5a
1.3: 标识符
标识符用来表示模块名,端口名,信号名
1.4: 数据类型(3种), 寄存器类型,线网类型,参数类型(非线路类型)
1.4.1: reg 关键字说明寄存器类型.
该类型代表一个抽象的数据存储单元.
通过赋值语句可以改变寄存器中储存的值.
它可以在 always 语句和 initial 语句中被赋值.
如果该过程语句描述的是时序逻辑,即 always 语句带有时钟信号,则该寄存器变量对应为寄存器;
如果该过程语句描述的是组合逻辑,即 always 语句不带有时钟信号,则该寄存器变量对应为硬件连线;
寄存器类型的缺省值是 x(未知状态)
reg [31:0] delay_cnt; //位宽32位的寄存器
1.4.2: wire 关键字说明线网类型
它代表元件间的物理连线。它的值由驱动元件的值决定
如果没有驱动元件连接到线网,线网的缺省值为 z(高阻态)
wire[7:0] data; // 8bits 数据
1.4.3: 由parameter 关键字说明的变量是参数类型
参数类型实际上是定义一个常数.
例:
parameter DATA_WITH = 3‘d8; //跟c语言中的常数变量定义类似
外部调用模块时,还可以传递参数.
类型后边跟一个标识符,说明这个变量时什么类型.
1.5: 注释
与c 一样, //注释单行, /* */注释多行
1.6: 运算符
与c 一致.
算术运算符: ±*/%
关系运算符: >,=,<,>=,<=,==,!=
逻辑运算符: &&,||,!
条件运算符: ?:
位运算符: ~,^,&,|
移位运算符: <<,>>
新增一条强势组合操作符,拼接运算符
例如: c={a,b} 把a,b 拼成c信号
1.7: 关键字.
关键字用来表示特定的意义,指导编译程序完成特定功能. 掌握常用关键字即可.
2. 常用关键字介绍
if - else 语句
if - else if -else 语句
for 语句
while 语句
以上与c 一致
case 语句
c 中是switch … case 形如: switch(var) case val1: do1;break…;default: do_defaut;
v 中是case … endcase 形如: case(var) val1: do1; val2:do2; default: do_default; endcase;
与c不同的地方
c 用{} 表示语句块, v用begin, end 表示语句块
c 用 = 直接给变量赋值,v 叠加了assign 或者 always 关键字给变量赋值
assign 用于简单的组合逻辑赋值语句
always 即可用于组合逻辑,此时功能与assign 一样, 也可用于时序逻辑.
always 语句常常带有敏感信号列表,带begin,end 语句块,看起来像一个c中的无名函数
= 是阻塞赋值, 用在组合电路中
<=是非阻塞赋值, 用在时序带路中
always 语句块, 其内部可以认为是顺序执行的,而always 块与块之间是并行执行的.
注: 硬件语句一定要注意语法的完整性,有if一定要跟else, 有case 一定要有default,不能有不周全的地方.
否则可能会有glitch 毛刺的产生.
3. 模块化设计
一个模块对应一种功能,上层模块调用下层模块.
每调用一次模块,需要先对模块实例化.
模块相当于c++中的类, 模块的参数相当于带参对象的构造函数
4.实战: 4位密码锁设计
当然,改成8位也是很简单的.
状态机的编写
典型的状态机例子就是一个密码锁. 它有10个按键输入
假如实现4位密码锁,其密码是1217,输入正确开锁,不正确不开锁.
c语言的状态机只需要一个switch-case 块.
v语言常用三段式状态机. 因为它有组合逻辑部分和时序逻辑部分,需要区别对待.
基本格式是:
第一个 always 语句实现同步状态跳转;(时序电路), 在时钟上升沿把下一状态付给当前状态
第二个 always 语句采用组合逻辑判断状态转移条件;//类似于c语言的switch-case块, 找到下一个状态
第三个 always 语句描述状态输出(可以用组合电路输出,也可以时序电路输出)。对应moore 和 mealy状态机
代码:
module Cipher(
input sys_clk,
input reset,
input key0,
input key1,
input key2,
input key3,
input key4,
input key5,
input key6,
input key7,
input key8,
input key9,
output reg result
);
//parameter define
parameter S0 = 3'b000 ;
parameter S1 = 3'b001 ;
parameter S2 = 3'b010 ;
parameter S3 = 3'b011 ;
parameter S4 = 3'b100 ;
//reg define, 当前状态和下一状态,3bits
reg [2:0] curr_st;
reg [2:0] next_st;
//main code
//状态机的第一段采用同步时序描述状态转移
always @(posedge sys_clk or negedge reset)
begin
if (!reset)
curr_st <= S0;
else
curr_st <= next_st;
end
//状态机的第二段根据当前状态及输入采用组合逻辑确定下一状态
//按键低有效
always @(
negedge key0,
negedge key1,
negedge key2,
negedge key3,
negedge key4,
negedge key5,
negedge key6,
negedge key7,
negedge key8,
negedge key9
)
begin
case (curr_st)
S0:
begin
if(key1 == 1'b0) next_st = S1;
else next_st = S0;
end
S1:
begin
if(key2 == 1'b0) next_st = S2;
else next_st = S0;
end
S2:
begin
if(key1 == 1'b0) next_st = S3;
else next_st = S0;
end
S3:
begin
if(key7 == 1'b0) next_st = S4;
else next_st = S0;
end
default:
next_st = S0;
endcase
end
//状态机的第三段描述状态输出(这里采用时序电路输出)
always @(posedge sys_clk or negedge reset)
begin
if (!reset)
result <= 1'b0;
else if (curr_st == S4)
result <= 1'b1;
else
result <= 1'b0;
end
endmodule
测试代码:(test_bench)
//时间精度/显示精度
`timescale 1ns/1ns
module tb_basic();
//regeister and wire
reg sys_clk;
reg reset;
reg key0;
reg key1;
reg key2;
reg key3;
reg key4;
reg key5;
reg key6;
reg key7;
reg key8;
reg key9;
wire result;
//initial, 输入信号激励及仿真命令
initial
begin
$dumpfile("basic.vcd"); //定义仿真波形文件,值变转储文件(.vcd)
$dumpvars(0,u_Cipher); //导出变量u_Cipher, 密码锁单元
sys_clk = 1'b1;
reset = 1'b0;
key0=1'b1;
key1=1'b1;
key2=1'b1;
key3=1'b1;
key4=1'b1;
key5=1'b1;
key6=1'b1;
key7=1'b1;
key8=1'b1;
key9=1'b1;
#100;
reset = 1'b1;
key1=1'b0; // 1 press
#100;
key1=1'b1;
key2=1'b0; // 2 press
#100;
key2=1'b1;
key1=1'b0; // 1 press
#100;
key1=1'b1;
key7=1'b0; // 7 press
#100;
// $stop;
$finish; //退出仿真
end
//时钟激励, 25M 时钟
always #20 sys_clk=~sys_clk;
Cipher u_Cipher(
.sys_clk(sys_clk),
.reset(reset),
.key0(key0),
.key1(key1),
.key2(key2),
.key3(key3),
.key4(key4),
.key5(key5),
.key6(key6),
.key7(key7),
.key8(key8),
.key9(key9),
.result(result)
);
endmodule
仿真结果:
在这里插入图片描述
测试输入密码1217, 给出了结果result=1. 测试通过.
附录: 测试环境(Makefile)
$ cat Makefile
SRC = basic.v tb_basic.v
TARGET = basic.vvp
#在tb_basic.v 中指定了波形文件(值变转储文件)为basic.vcd
VCDFILE = basic.vcd
#############################################
#简化目标
all: $(TARGET)
#生成target 文件,排除源码书写错误等
$(TARGET): $(SRC)
iverilog -o $@ $^
# 生成vcd 文件, 供gtkwave 使用
$(VCDFILE) : $(TARGET)
vvp $(TARGET)
#简化目标
build: $(VCDFILE)
#简化目标,用run 目标来查看波形
run: $(VCDFILE)
gtkwave $(VCDFILE)
clean:
rm $(TARGET) $(VCDFILE)
3个目标, default, build, run.
make 其目标是编译,主要查询源码中的书写错误.
make build. 其目标是生成仿真波形图 .vcd文件
make run. 其目标是观察波形图输出.
|