*1 模块结构
** 端口: module 模块名(端口1, 端口2, 端口3)** 内容:*** I/O说明:
input 端口名;
output 端口名;
*** 内部信号:
reg [width-1:0] r变量1,r变量2;
wire [width-1:0] w变量1,w变量2;
*** 功能定义:**** a. assign 连线
assign a = b&c;
**** b. 实例化其他元件
and and_inst(q, a, b);
**** c. always模块
always @(posedge clk or posedge clr)
begin
*2.数据类型 常量 变量
** 常量:*** 整数:<位宽 num'><进制 b|o|d|h><数字>,例如 4'b1010
x值(不定值)和z值(高阻值,也可用?代替)
x和z可以标识某一位或者某一个数字
4'b10x0,4'bx,4'b101z,4'bz,4'b?
*** 负数:整数最前面加-*** 下划线:分割数字部分,更加易读(8'b1000_1000)** 参数:parameter
parameter 参数名=表达式;
表达式只能是数字或者定义过的参数
** 变量:*** wire型:wire [n-1:0] 数据名;
wire表示信号,常用来表示assign关键字指定的组合逻辑信号
wire型信号可以用作输入,输出
*** reg型:reg [n-1:0] 数据名;
对存储单元的抽象
常用来表示always模块内的指定信号,常代表触发器
always块内被赋值的每一个信号都必须定义为reg型
*** memory型:reg [n-1:0] 存储器名[m-1:0];
reg [n-1:0]表示基本存储单元的大小
存储器名[m-1:0]表示基本存储单元的个数,存储空间的容量
对存储器进行地址索引的表达式必须是常数表达式
一个n位寄存器可以在一条赋值语句里进行赋值,而一个完整的存储器不行
** 运算符及表达式:*** 基本运算符:+ - * / %*** 位运算符:~ & | ^ ^~*** 逻辑运算符:&& || !*** 关系运算符:< > <= >=*** 等式运算符:== != (不管x、z,结果可能是不定值)
=== !==(对参数的x、z都进行比较)
*** 移位运算符:<< >>*** 位拼接运算符:{ },将几个信号拼接起来,例如{a,b[3:0],w,3'b100}*** 缩减运算符:C =&B;C =|B;C =^B;*** 优先级别:和c语言差不多,加括号** 赋值语句:*** 1)非阻塞赋值方式(b <= a)**** a.块结束才完成赋值**** b.b的值不是立刻就改变的**** c.在可综合的模块中常用*** 2)阻塞赋值方式(b = a)**** a.赋值语句执行完成后,块才结束**** b.b的值在赋值语句执行后立刻改变**** c.可能会产生意想不到的结果**** 简单理解:
非阻塞赋值用了多个触发器,每次时钟到达,所有触发器都触发一次
阻塞赋值连到同一个触发器上,时钟到达,导致所有寄存器被赋值
** 块语句:*** 顺序块:**** 1)块内顺序执行**** 2)每条语句的延迟是相对于前一条语句的仿真时间(语句前#num)**** 3)直到最后一句执行完,流程控制才跳出该块***** begin***** 语句1;***** ...***** 语句n;***** end**** 或***** begin:块名:***** 块内声明;***** 语句1;***** ...***** 语句n;***** end*** 并行块:**** 1)块内是同时执行的**** 2)语句的延迟是相对于程序流程控制进入块内时的仿真时间**** 3)延迟时间是用来给赋值语句提供时序的**** 4)时序最后的语句执行完,或者disable语句执行时,跳出程序块***** fork***** 语句1; ***** ... ***** 语句n; ***** join **** 或 ***** fork:块名: ***** 块内声明; ***** 语句1; ***** ... ***** 语句n; ***** join***** ** 块名:可以给每一块取名,将名字加在begin和fork之后*** 1)可以在块内定义局部变量*** 2)可以被其他语句调用*** 3)在verilog中,所有变量静态(都有唯一地址)** 起始时间和结束时间:*** 并行块和顺序块中有起始时间和结束时间** 条件语句:*** 1)if...else 语句*** if(表达式)*** 语句|语句块*** else*** 语句|语句块*** *** 2)case语句*** case|casez(case?)|casex,最常用的casez*** case(控制表达式)*** 分支表达式1:语句|语句块*** ...*** 分支表达式n:语句|语句块*** default: 语句*** endcase*** 分支表达式的值的位宽必须相等,需要指明位宽*** 使用if时,最好也要使用else*** 使用case时,最好用上default
块名:可以给每一块取名,将名字加在begin和fork之后 1)可以在块内定义局部变量 2)可以被其他语句调用 3)在verilog中,所有变量静态(都有唯一地址) 起始时间和结束时间: 并行块和顺序块中有起始时间和结束时间 条件语句: 1)if...else 语句 if(表达式) 语句|语句块 else 语句|语句块 2)case语句 case|casez(case?)|casex,最常用的casez case(控制表达式) 分支表达式1:语句|语句块 ... 分支表达式n:语句|语句块 default: 语句 endcase 分支表达式的值的位宽必须相等,需要指明位宽 使用if时,最好也要使用else 使用case时,最好用上default ** 循环语句:*** 1)forever 连续的执行语句*** forever *** begin*** 多条语句 *** end*** 常用来生成周期型波形,用来作为仿真测试信号*** 不能独立写在程序中,必须写在initial块中*** 2)repeat 执行一条语句n次*** repeat(常量表达式)*** begin*** 多条语句*** end*** *** 3)while:执行一条语句直到某个条件不满足(如果一开始条件不满足,则一次也不执行)*** while(表达式)*** begin*** 多条语句*** end*** 4)for*** a) 循环次数变量初始值*** b) 循环表达式判断*** c) 执行语句修正循环次数变量*** 使用方法和c语言基本一致*** for(表达式1;表达式2;表达式3)*** begin*** 多条语句*** end
** 结构说明:*** initial语句:
initial在仿真一开始就执行,但是只执行一次
*** initial *** begin*** 多条语句*** end*** always语句:*** always <控制时序> <语句>*** always 在仿真一开始就执行,always语句会不断重复执行,所以需要时序的控制*** 不加时序控制,会不停重复执行,形成仿真死锁*** //边沿触发*** always @(postedge clock or postedge reset)*** begin*** 多条语句*** end*** //电平触发*** always @(postedge clock or postedge reset)*** begin*** 多条语句*** end*** 边沿触发的always块常常用来描述时序逻辑*** 一个模块中可以有多个always块,他们并行执行*** task语句: *** 任务定义: *** task <任务名>;*** <端口及数据类型声明语句>*** <语句1>*** ...*** <语句n>*** endtask*** 任务调用:*** <任务名> (端口1,...端口n)*** 例子:*** //定义*** task mytask;*** input a,b;*** inout c;*** output d,e;*** ...*** <语句> //执行任务工作相应的语句*** ...*** c=foo1;*** d=foo2;*** e=foo3;*** endtask*** //调用*** mytask(v,w,x,y,z);*** function语句:*** function定义:*** function <返回值的类型或范围> (函数名);*** <端口说明语句>*** <变量类型说明语句>*** begin*** 多条语句;*** end*** endfunction*** 函数调用:*** <函数名> (<表达式1>,...,<表达式n>)*** 函数的使用规则:*** 1)函数定义中不能包含有任何的时间控制语句*** 2)函数不能启动任务*** 3)定义函数时至少有一个输入参数*** 4)在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值*** 该函数变量具有和函数名相同的名字*** 例子:*** //函数定义*** function [7:0] getbyte;*** input [15:0] address;*** begin*** <说明语句>*** getbyte = result_expression;*** end*** endfunction*** //函数调用:*** word = control ? {getbyte(mybyte),getbyte(mybyte)} : 0;
系统函数和任务:
verilog语言中每个系统函数和任务前面都用一个标识符$来加以确认
notice:参数为",,",表示空参数,输出时显示空格
1)$display 和 $write
作用:按照指定格式输出,p1给出格式,p2...pn按序输出
$display输出后会自动换行,$write不换行
用法基本和c语言的printf一致
格式:
$display(p1,p2,...pn);
$write(p1,p2,...pn);
例子:
$display("rval=%h hex %d decimal",rval,rval);
2)系统任务 $monitor
用处:
提供了监控和输出参数列表中的表达式或变量值的功能
仿真器建立了一种机制,使得每当参数列表中变量或者表达式的值发生变化时,
整个参数列表中变量或表达式的值都将输出显示。
任何时刻只能有一个$monitor起作用,因此需配合$monitoron和$monitoroff使用,
把需要监视的模块用$monitoron打开,在监视完毕后及时用$monitoroff关闭
格式:
$monitor(p1,p2,...pn);
$monitor;
$monitoron;
$monitoroff;
例子:
$monitor($time,,"rxd=%b txd=%b",rxd,txd);
3)时间度量系统函数$time
verilog语言中支持两种时间函数$time和$realtime
$time返回一个64比特值的整数来表示当前仿真时刻值
返回的总是时间尺度的倍数,并且会取整数
例子:
`timescale 10ns/1ns
module test
reg set;
parameter p=1.6;
initial
begin
$monitor($time,,"set=",set);
#p set=0;
#p set=1;
end
endmodule
$realtime返回的时间数字是个一个实型数
和$time一样都是以时间尺度为单位
4)系统任务 $finish
$finish的作用是退出仿真器,返回主操作系统
$finish有3个参数,输出的特征信息一次变多
参数:
0:不输出信息
1:输出当前的仿真时刻和位置
2:输出当前的仿真时刻和位置,在仿真过程中
所用的memory及CPU时间的统计
5)系统任务 $stop
把EDA工具(如仿真器)设置成暂停模式,在仿真环境中给出一个交互式的
命令提示符,将控制权交给用户
和$finish一样,参数为0,1,2,数越大,信息越多
6)系统任务 $readmemb,$readmemh
用来从文件中读取数据到寄存器
读取的文件有格式要求(具体查书)
数据文件的每个被读取的数字都被存放到地址连续的存储器单元中
每个数据的存放地址在数据文件中进行说明
二进制数字读取
$readmemb("<数据文件名>",<存储器名>);
$readmemb("<数据文件名>",<存储器名>,<起始地址>);
$readmemb("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);
16进制数字读取
$readmemh("<数据文件名>",<存储器名>);
$readmemh("<数据文件名>",<存储器名>,<起始地址>);
$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);
例子:
reg [7:0] mem[1:256];
initial $readmemh("mem.data",mem);
initial $readmemh("mem.data",mem,16);
initial $readmemh("mem.data",mem,128,1);
7)系统任务 $random
一个产生随机数的手段,函数返回一个32bit的随机数
$random的一般用法 $random % num,返回一个在(-num+1,num-1)范围
内的随机数
reg [23:0] rand;
rand = {$random} % 60; //利用拼接返回一个0到59之间的数
编译预处理:
宏定义 `define
`define 标识符(宏名) 字符串内容(宏内容)
“文件包含”处理 `include
一个源文件可以将另外一个源文件全部包含起来
时间尺度:
`timescale <时间单位>/<时间尺度>
条件编译:
`ifdef
`else
`endif