一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情。
第一章·语法
1 数值
verilog中数值有以下表示方法
(1) 逻辑值
(2) 整数
整数可以标明位数也可不标明位数,通用表示方法为:
其中位数表明该数用二进制的几位来表示;基数为数值使用的码制——可以是二(b)、八(o)、十(d)或十六(h)进制;数值可以是所选基数的任何合法值包括不定值和高阻态。
例如:8’b11010001、32’H080A_FFFF、48
(3) 位扩展
对于所有小于位宽的数值,在其位区间内用0填充高位;除非该数值为不定值或高阻态,此时高位用相应的或扩展。所谓位区间就是基数码制中,一个数占用的比特数,例如二进制位区间为1,十六进制为4。
2 字符串
字符串常用于系统测试时,表示命令内需要显示的信息;通常不用于硬件建模。
字符串是用“”括起来的一行字符,其内可以用C语言中的各种格式控制符(转义符),如“\n”、“\t”等;也可以用C语言中的各种数值型控制符,如:%b、%o、%d、%h、%t、%s等。
3 标识符
所谓标识别符就是用户为程序描述中的Verilog对象——如模块名、端口名、实例名等,所起的名字。标识符必须以英文字母或下划线起头,最长可以达到1023个字符。此外,Verilog语言是大小写敏感的。
4 关键字
关键字是Verilog语言中预设的用于描述架构的词,所有关键字都是小写字母,如:module、endmodule、always、reg、wire、if、else等。
5 任务和函数
任务和函数具备将程序中反复调用的语句结构聚合起来的能力,因其可在过程块中被调用(而模块不行),因此可以有效的简化程序结构。利用任务和函数可以把一个大的程序模块分解成多个小的任务和函数,利于调试。
任务和函数的共同点是:语句中不能出现过程引导的语句,且其中无法描述时序电路,只能描述组合电路。
- 任务结构
基本格式
task <任务名>;
端口及数据类型声明语句;
过程语句;
endtask
调用格式
<任务名> (端口1,端口2,…,端口N)
- 函数结构
基本格式
function <位宽范围声明> 函数名;
输入端口及数据类型声明语句;
过程语句;
函数名= ***;
//该句是作为函数的返回值,不可缺
endfunction
调用格式
X = <函数名> (端口1,端口2,…,端口N)
任务与函数的不同点在于:
(a) 函数只能与主模块共用同一个仿真时间,而任务可以定义自己的仿真时间; (b) 函数不能调用任务,但任务能调用其他任务和函数; (c) 函数至少要有一个输入变量,而任务可以没有或有多个输入变量; (d) 函数显式返回一个值,而任务则隐式返回值——或理解为处理值; (e) 任务需要定义输入和输出,而函数只需定义输入,函数名就是输出
下面给出两个实例:
module TASKDEMO (S,D,C1,D1,C2,D2);
input S;
input[3:0] C1,D1,C2,D2;
output [3:0] D;
reg[3:0] out1,out2;
task CMP; //任务定义,任务名CMP
input [3:0] A,B;
output[3:0] DOUT;
begin
if(A>B) DOUT=A;
end
endtask
always @(C1)
begin
CMP(C1,D1,out1); //一次调用任务
CMP(C2,D2,out2); //二次调用任务
endmodule
module CN(
input [3:0]A,
output [2:0]OUT
);
function[2:0] GP;
input[3:0] M;
reg [2:0]CNT,N;
begin
CNT = 0;
for(N=0;N<=3;N=N+1)
if(M[N]==1) CNT=CNT+1;
GP = CNT;
end
endfunction
assign OUT = (~|A) ? 0:GP(A);
endmodule
此外还有一系列系统任务和函数,以符号$起头。
| 系统任务/函数 | 含义 |
|---|---|
| $time | 找到当前的仿真时间 |
| $display、$monitor | 显示和监视信号值的变化 |
| $stop | 暂停仿真 |
| $finish | 结束仿真 |
6 编译引导语句
编译引导语句用主键盘左上角小写键`起头,用于指导仿真编译器在编译时采取一些特殊处理,编译引导语句一直保持有效,直到被取消或重写。编译引导语句不会生成硬件电路。
| 常用编译引导 | 含义 |
|---|---|
| `define | 用于宏定义 |
| `include | 用于包含其它模块,便于层次化系统设计 |
| `timescale | 用于说明程序中的时间单位和仿真精度,必须放在模块边界前面。例如:`timescale 1ns/100ps表示下面模块中所有的时间单位都是1ns的整数倍。尽可能使仿真精度与时间单位接近,前者是由所有参加仿真模块中由`timescale指定的精度最高(即时间最短)的决定。特殊符号“#”常用来表示延迟,其延迟单位由时间单位决定。如上例中指定的模块若有# 10,则表示延迟10ns |
| `uselib | 用于定义仿真器到哪里去找库元件 |
| `resetall | 用于把所有设置的编译引导恢复到缺省状态 |
7 基本数据类型
(1) Nets(网络连接)
表示器件之间的物理连接
wire(缺省)、tri:对应于标准的互连线supply1、supply2:对应于电源线或接地线wor、trior:对应于有多个驱动源的线或逻辑连接wand、triand:对应于有多个驱动源的线与逻辑连接trireg:对应于有电容存在能暂时存储电平的连接tri1、tri0:对应于需要上拉或下拉的连接
(2) Register(寄存器/变量)
表示抽象的储存单元
reg:无符号整数变量,可以选择不同位宽integer:有符号32位宽整数变量,算术运算可产生2的补码real:有符号双精度浮点数time:无符号64位宽整数变量(Verilog-XL仿真工具用64位正数记录仿真时刻)
(3) Parameters(参数)
常用参数来声明运行时的常数,参数是本地的,其定义只在本模块内有效 可用字符串表示的任何地方,都可以用定义的参数来代替。
第二章·建模
1 结构建模
描述系统的具体结构组成、由哪些子模块组合而成以及如何互连。具体而言,采用模块互相调用、组合的方式进行建模。
2 数据流建模
描述系统中信号/数据在线路中的流向,即一个或几个信号、数据如何从一个线路传递到另一个线路上。具体而言,采用assign连续赋值方式建模。
3 行为建模
按高级语言的思路对一个系统的功能或工作行为过程进行描述。具体而言,采用always或initial引导过程块方式建模。
4 FSM建模
在Verilog中引入FSM建模的优势在于:
(a) FSM是高效的顺序控制模型——克服了纯硬件数字系统顺序方式控制不灵活的缺点; (b) 容易利用现成的EDA优化工具; (c) 性能稳定。状态机容易构成性能良好的同步时序逻辑模块; (d) 设计实现效率高。状态机的HDL表述丰富多样、程序层次分明、易读易懂; (e) 高速性能及高可靠性。
第三章·案例
1 循环彩灯控制器
问题描述:设计一个循环彩灯控制器。要求控制红、绿、黄发光管循环发亮。红发光管亮2秒,绿发光管亮3秒,黄发光管亮1秒。设系统时钟为10MHz。
//@ Attention:时钟频率10MHz
module circle_lights(
input clk,
input rst,
output reg [1:0] lights
);
/******************* 变量定义区 **********************/
reg [1:0] state;
reg [1:0] next_state;
reg [25:0] cnt;
`define S_red 2'b00 //红灯
`define S_green 2'b01 //绿灯
`define S_yellow 2'b10 //黄灯
`define sec_2 26'd2000_0000 //2s
`define sec_5 26'd5000_0000 //5s
`define sec_6 26'd6000_0000 //6s
/***************************************************/
//state register block
always @ (posedge clk)
begin
if(!rst||(cnt==`sec_6))
begin
state<=`S_red;
cnt<=0;
end
else
begin
state <= next_state;
cnt <= cnt+1;
end
end
//next state logic
always @(state or cnt)
begin
case(state)
`S_red:
begin
if(cnt==`sec_2) next_state = `S_green; //持续2s
else next_state = `S_red;
end
`S_green:
begin
if(cnt==`sec_5) next_state = `S_yellow; //持续3s
else next_state = `S_green;
end
`S_yellow:
begin
if(cnt==`sec_6) next_state = `S_red; //持续1s
else next_state = `S_yellow;
end
endcase
end
//output logic
always@(state or cnt)
begin
case(state)
`S_red: lights = 2'b00;
`S_green: lights = 2'b01;
`S_yellow: lights = 2'b10;
endcase
end
endmodule
2 红外入侵传感器
用FSM设计一个入侵警报系统 Verilog 代码: 启用键,进入防护状态 解除键,从其他状态返回到解除防护状态 测试键,进入测试状态,报警 当传感器输出为“1”持续5秒,警报器报警。
//@ Attention:时钟频率10MHz
module alarm(
input clk,
input [3:0] btn,
//0->进入防护状态 1->解除防护 2->报警 3->复位
//one_hot编码
output reg beep
);
/******************* 变量定义区 **********************/
reg [1:0] state;
reg [1:0] next_state;
reg [25:0] cnt;
`define S_init 2'b00 //解除防护状态
`define S_defence 2'b01 //防护状态
`define S_alarm 2'b10 //报警状态
`define sec_5 26'd5000_0000 //5s
/***************************************************/
//state register block
always @ (posedge clk)
begin
if(btn == 4'b1000)
begin
state<=`S_init;
cnt<=0;
end
else
begin
state <= next_state;
cnt <= cnt+1;
end
end
//next state logic
always @(state or btn)
begin
if(btn == 4'b0001) next_state = `S_defence;
else if(btn == 4'b0100)
begin
next_state = `S_alarm;
cnt = 0; //开始5s计时
end
else next_state = `S_init;
end
//output logic
always@(state or btn)
begin
if(state == `S_alarm)
begin
if(cnt == `sec_5)
begin
beep = 0;
next_state = `S_defence;
end
else beep = 1;
end
else beep = 0;
end
endmodule
测试模块代码:
module test_alarm(
);
reg clk;
reg [3:0] btn;
wire beep;
alarm my_test(clk,btn,beep);
always #50 clk <= ~clk;
initial
begin
clk = 1'b0;
btn <= 4'b1000;
#55
btn <= 4'b0001;
end
endmodule
更多内容欢迎关注我的AI频道"AI技术社"