打印
[Verilog HDL]

状态机实例

[复制链接]
156|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
gaochy1126|  楼主 | 2024-11-30 21:32 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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. 其目标是观察波形图输出.

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:这个社会混好的两种人:一是有权有势,二是没脸没皮的。

1061

主题

11320

帖子

26

粉丝