Verilog模型可以是实际电路的不同级别的抽象。这些抽象的级别和它们所对应的模型类型共有以下5种:系统级、算法级、RTL级、门级、开关级
‘bz :表示高阻态, ’bx表示不定值(0或1均可)
`include "muxtwo.v" 将文件引进
{$ random} 为系统任务,会产生一个随机数,
in = {$random} % 2; //产生0到2(0或1) 的随机数in = $random % b; //b为十进制整数,in为范围在 -(b-1):(b-1) 中的随机数
而“always”块既可用于描述组合逻辑,也可描述时序逻辑,always块中的语句称为顺序语句,因为他们是顺序执行的
verilog hdl 共有19种数据类型,最基本的4种为reg、wire、integer、parameter
其他的数据类型是:larger, medium,scalared, time, small, tri, trio, tril, triand, trior, trireg、vectored, wand, wor型
利用parameter 来传递参数
//这是子模块//在子模块里面定义了常量width 和height
module decoder(
input wire in,
output wire out)
parameter width = 1;
parameter height = 2;
endmodule
module top (
input wire a,
output wire b
);
//调用子模块并更改里面的常量width 和heigh
decoder
#(
.width (10), //使decoder里面的width = 10
.height (8)
)
decoder_inst(
.in(),
.out()
);
endmodule
用defparam来重定义模块种的值,如defparam module1.module2.module3.a = 0; module1,module2和module3是例化模块的名字
wire型变量通常是用来表示单个门驱动或连续赋值语句驱动的网络型数据,tri型变量则用来表示多驱动器驱动的网络型数据
原语:相当于库中的module,直接调用
符号优先级:
时序分析:时序分析的方法主要有两种,一种是静态时序分析,另一种是动态时序分析
静态分析:通过设计好的电路中的已知参数 利用EDA的工具模型分析出时钟和数据的关系。
动态分析: 把设计好的电路中的所有延时都考虑进来,加上EDA中的延时参数,然后观测Ts, Th是否满足要求,需要电路模拟跑起来。
Ts(建立时间:采样时钟的上升沿 到达 数据起始位置的时间)
Th(保持时间:采样时钟的上升沿 到达 数据结束位置的时间)
时序分析需要关心的路径:
1、从输入到输出的路径;
2、从输入到寄存器的路径;
3、从寄存器到输出的路径;
4、从寄存器到寄存器的路径;
5、异步清零信号和时钟存在异步的进入和退出问题。
编辑
图 中的 T1 为数据路径的延迟,,它包括 寄存器 REG1 时钟端 到 寄存器 REG1 数据输出端 Q 的延迟(Tco) 与 两级寄存器之间的组合逻辑与布线延迟( Tdata) 之和。
图 中的△T 为时钟偏斜,等于时钟到第二级寄存器时钟端的延迟 与 时钟到第一级寄存器的时钟端的延迟 之差。注:△T 的值可以为正值也可以为负 值,一般情况它们之间的差就为一个正值,更容易计算。
另外Ts 为建立时间,Th 为保持时间,T_cycle 为时钟周期
根据波形图所表示的内容可以得出建立保持时间公式的推导,如下所示:
建立时间的公式为:Ts = T_cycle – T1 + △T = T_cycle –(Tco + Tdata)+ △T
保持时间的公式为:T1 – △T = (Tco + Tdata)– △T
第5章:条件语句、循环语句、块语句和生成语句
块语句:
1、顺序块: begin end
(1)块内的语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。
(2)每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
(3)直到最后一条语句执行完,程序流程控制才跳出该语句块。
2、并行块: fork join
(1)块内语句是同时执行的,即程序流程控制一进入到该并行块,块内语句则开始同时并
行地执行。
(2)块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间的。
(3)延迟时间是用来给赋值语句提供执行时序的。
(4)当按时间时序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制
跳出该程序块。
3、生成块 generate endgenerate
3种生成语句的方法: 循环生成、条件生成、case生成
生成范围内允许声明下列数据类型:
(1)net(线网)、reg(寄存器):
(2)integer(整型数),real(实型数)、time(时间型)、realtime(实数时间型);
(3)event(事件)
不允许出现在生成范围之中的模块项声明包括:
(1)参数、局部参数:
(2)输入、输出和输入/输出声明;
(3)指定块。
生成块的本质是使用循环内的一条语句来代替多条重复的Verilog语句,简化用户的编程。
条件语句
case语句的所有表达式值的位宽必须相等,只有这样,控制表达式和分支表达式才能进
行对应位的比较。一个经常犯的错误是用'bx,'bz来替代nbx,nbz,这样写是不对的,因为信
号x,z的默认宽度是机器的字节宽度,通常是32位(此处n是case控制表达式的位宽)。
编辑
其中casez语句用来处理不考虑高阻值z的比较过程,casex语句则将高阻值z和不定值都视为不必关心的情况。所谓不必关心的情况,即在表达式进行比较时,不将该位的状态考虑在内。
循环语句:
1、forever 连续执行 forever begin end forever 必须写在initial块中。
2、repeat 连续执行一条语句n次。 repeat (n)begin end
3、while
while(?) begin end
4、for for(i=0; i<n; i=i+1) begin end
第6章 结构语句、系统任务、函数语句和显示系统任务
1、initial 语句
initial 语句在仿真开始时对变量初始化,初始化并不需要任何仿真时间。
2、always语句
同步复位和异步复位
//同步复位
always @ (posedge sys_clk)
if(sys_rst_n == 1'b0) //在时钟的上升沿才复位
a <= 0;
else
a <= 1'b1;
//异步复位
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0) //当复位信号为0时触发,和时钟无关
a <= 0;
else
a <= 1'b1;
wait 关键字 (可以理解为if)
always
wait (count_enable) #20 count = count + 1;
3、task(任务)
任务和函数的不同:
(1)函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。
(2)函数不能启动任务,而任务能启动其他任务和函数。
(3)函数至少要有一个输入变量,而任务可以没有或有多个任何类型的变量。
(4)函数返回一个值,而任务则不返回值。
任务的调用源码: caculation(data_in, data_out);
函数调用源码: data_out = caculation(data_in);
task和function中是不能使用initial和always的
任务定义和调用:
//任务定义
task my_task;
input a,b;
inout c;
output d,e;
begin
d = a + b;
e = a + b;
end
endtask
//任务调用my_task(m,n,o,p,q);
常用的系统任务
$display (p1, p2, p3);
$write (p1, p2, p3);
这两个函数和系统任务的作用是用来输出信息,即将参数p2到pn按参数p1给定的格式
输出。参数p1通常称为“格式控制”,参数p2至pn通常称为“输出表列”。
文件输出任务:
打开文件: $fopen("a.txt");
任务$fopen返回一个被称为多通道描述符(multichannel descriptor)的32位值。
integer a;
a = $fopen("a.txt");
写文件 :$fdisplay、 $fmonitor、$fwrite、$fstrobe
$fdisplay(a,"hello")
关闭文件
$fclose(a);
integer handle1, handle2, handle3; //整数为32位
integer desc1, desc2, desc3;
intial
begin
handle1 = $fopen("1.out"); //handle1 = 32'b0010; 第一位为1
handle2 = $fopen("2.out"); //handle2 = 32'b00100;
handle3 = $fopen("3.out"); //handle3 = 32'b001000;
desc1 = handle1 | 1; //desc1 = 32'b0010
$fdisplay(desc1, "aaa"); //写到文件1.out
desc2 = handle2 | handle1; //desc2 = 32'b00110
$fdisplay(desc1, "aaa"); //写到文件1.out 和2.out
desc3 = handle3;
$fdisplay(desc3, "aaa"); //只写到文件3.out
$fclose(handle1); //关闭文件1
end
4、function (函数) 函数的目的是返回一个用于表达式的值。
函数的定义:
①函数的定义不能包含有任何的时间控制语句,即任何用#、@、或wait来标识的语句。
②函数不能启动任务。
③定义函数时至少要有一个输入参量。
④在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该
内部变量具有和函数名相同的名字。
//函数的定义//至少有一个输入变量
function [7:0] return_data; //return_data 既作为函数名,也作为返回的寄存器变量
input [3:0] a;
reg[3:0] index;
begin
return_data = {a, index};
end
endfunction
//调用
out = return_data(b);
第7章 调试用系统任务和常用编译预处理语句
1、$monitor(p1, p2, ..., pn);
$monitor提供了监控和输出参数列表中的表达式或变量值的功能。
$monitoron 和 $monitoroff任务是通过打开和关闭监控标志来控制监控任务$monitor的启动和停止。
$monitor ( $time,,“rxd=%b txd=%b“,rxd,txd );
2、$time
$time可以返回一个64位的整数来表示的当前仿真时刻值, $time总是输出整数
$realtime 返回的时间数字是一个实型数,无须进行取整操作。
3、$finish,退出仿真器
4、$stop 是把EDA工具(例如仿真器)置成暂停模式
5、$readmemb和$readmemh,并用来从文件中读取数据到存储器中。这两个系统任务可以在仿真的任何时刻被执行使用。
initial
readmemh("mem.data",mem); //装数据到起始地址为1的存储器单元中
initial
readmemh("mem.data",mem,16); //装数据到起始地址为16的存储器单元中,一直到地址是256的单元为止
initial
readmemh("mem.data",mem,128,1); //装数据到起始地址为128的存储器单元中,一直到地址是1的单元为止
6、$random 当函数被调用时返回一个32位的随机数。
$random一般的用法是:$ramdom%b,其中b>0。它给出了一个范围在(-b+1):
(b - 1)中的随机数。下面给出一个产生随机数的例子:
reg [23:0] rand:
rand = $random % 60;
上面的例子给出了一个范围在 -59 ~ 59之间的随机数,下面的例子通过位拼接操作产生
一个值在0一59之间的数。
reg【23:0】rand;
rand= { $random} % 60;
7、编译预处理
`define
`define signal string
module
`define WoRDSIZE 8
endmodule
`include 文件包含 `include "a.v"
`timescale <时间单位> /<时间精度>
timescale命令用来说明跟在该命令后的模块的时间单位和时间精度。
timescale 1ns/1ps:
//在这个命令之后,模块中所有的时间值都表示是1ns的整数倍。这是因为在timescale命令中,定义了时间单位是1ns。模块中的延迟时间可表达为带3位小数的实型数,因为timescale命令定义时间精度为1ps。
//例
timescale 10 ns/1 ns
module test;
reg set;
parameter d=1.55:
initial
begin
#d set=0: //因为是整数倍,精度为1ns,所以是16ns #d set=l; //也是16ns
end
endmodule
条件编译 `ifdef `else `endif
`ifdef 宏名(标识符)
程序1
`else
程序2
`endif
//例
`ifdef TEST //当程序中`define TEST 调用module a
endmodule
`elsemodule b
endmodule
`endif
条件执行 $test $plusargs
if($test$plusargs ("AAA”)) //需要`define AAA 才触发
可以使用系统任务关键字$value$plusargs来进一步控制条件执行。该系统任务用于
测试调用选项的参数值。如果没有找到匹配的调用选项,那么$value$plusargs返回0:如果
找到匹配的选项,那么$value$plusargs返回非0值。
if($value $pluargs("test = %s", test_string))
elseif($value $pluargs("clk_t = %d", clk_priod))
else//用test = "test1.vcc" clk_t = 10 来启动
第8章 语法概念总复习练习
1、定义4位的输入矢量可这么定义:
input [3:0] P, Q, R; // P,Q,R都是4位
2、b = 8'bZ0 =8'bZZZZ_ZZZ0;
3、integer 定义的整数可为负数
4、
编辑
5、位拼接运算符必须指明位数,若不指明则隐含着为32位的二进制数,
例{1, 0} = 64'h00000001_00000000
第二部分 设计和验证部分
第9章 Verilog HDL模型的不同抽象级别
1、分为行为模块和结构模块
2、5种不同描述方法:
(1)系统级(system); (行为级)
(2)算法级(algorithmic).;(行为级)
(3)RTL(Register-Transfer-Level);(行为级)
(4)门级(gate-level); (结构级)
(5)开关级(switch-level)
3、and 与门;
nand 与非门;
nor 或非门;
or 或门
xor 异或门;
xnor 异或非门;
buf 缓冲器;
not 非门。
4、所谓逻辑综合就其实质而言是设计流程中的一个阶段,在这一阶段中将较高级抽象层次
的描述自动地转换成较低层次描述。就现在达到的水平而言,所谓逻辑综合就是通过综合器
把HDL程序转换成标准的门级结构网表,而并非真实具体的门级电路。真实的电路需要布局布线。
5、用户定义的原语UDP(user defined primitives)
//定义UDP的语法
primitive 元件名 (输出端口名, 输入端口名, 输入端口名2...)
output reg 输出端口名;
input 输入端口名1, 输入端口名2...
initial
begin
end
table
endtable
endprimitive
(1)UDP只能有一个输出端,而且必定是端口说明列表的第一项。
(2)UDP可以有多个输入端,最多允许有10个输入端。
(3)UDP所有端口变量必须是标量,也就是必须是1位的。
(4)在UDP的真值表项中,只允许出现0,1,X的3种逻辑值,高阻值状态Z是不允许出
现的。
(5)只有输出端才可以被定义为寄存器类型变量。
(6)initial语句用于为时序电路内部寄存器赋初值,只允许赋0,1,X的3种逻辑值,默认值
为X
第10章 如何编写和验证简单的纯组合逻辑模块
1、用Verilog HDL来描述加法器
module add4(X,Y,sum,C);
input [3 :0]X,Y;
output [3:0]sum;
output C;
assign {C,Sum} = X + Y;
endmodule
2、乘法器
两个4位2进制数X 和 Y相乘的延时时间
2.1 逐位进位乘法器
编辑
逐位进位乘法器所需时间: 俩个4位的二进制数,结果是8位, 4 X 8 + 1 =33个门的传输延时。
2.2 超前进位加法器
需要1个与门 + 4个全加器 + 3位超前进位加法器的传输延时
3、总线: 总线是运算部件之间数据流通的公共通道, 各运算部件和数据寄存器组可以通过带控制端的三态门与总线的连接。通过对控制端电平的控制来确定在某一时间片段内,总线归哪两个或哪几个部件使用。
4、流水线
所谓流水线设计实际上是把规模较大、层次较多的组合逻辑电路分为几个级,在每一级插
入寄存器组并暂存中间数据。K级的流水线就是从组合逻辑的输入到输出恰好有K个寄存
器组(分为K级,每一级都有一个寄存器组),上一级的输出是下一级的输入而又无反馈的
电路。
优点:能提高吞吐量
流水线加法器(或乘法器)与组合逻辑加法器(或乘法器)的比较:
采用流水线技术可以在相同的半导体工艺的前提下通过电路结构的改进来大幅度地提高
重复多次使用的复杂组合逻辑计算电路的吞吐量。下面是一个位全加器的例子。为实现该加法功能需要3级电路:
(1)加法器输入的数据产生器和传送器;
(2)数据产生器和传送器的超前进位部分,
(3)数据产生、传送功能和超前进位三者求和电路。
编辑
6*10流水线乘法器原理图
流水线操作(面积换取速度):
流水线处理是提高组合逻辑设计的处理速度和吞吐量的最常用手段。如果某个组合逻辑
设计的处理流程可以分为若干个步骤,而且整个数据处理过程是“单流向”的,即没有反馈或者
迭代运算,前一个步骤的输出是下一个步骤的输入,则可以考虑采用流水线设计方法提高系统
的数据处理频率,即吞吐量。
第11章 复杂数字系统的构成
1、数字逻辑种类
组合逻辑: 输出只是当前输人逻辑电平的函数(有延时),与电路的原始状态无关的逻辑电路.
时序逻辑:输出不只是当前输入的逻辑电平的函数,还与电路目前所处的状态有关的逻辑电路
2、在verilog 中启用同步时序逻辑
用Verilog HDL设计的可综合模块,必须避免使用异步时序逻辑,
利用上一个时钟为下一个时钟创造触发条件:确定下一个状态所使用的组合电路的延迟与时钟到各触发器的差值必须小于一个时钟周期的宽度。采用的措施:
(1)全局时钟网络布线时尽量使各分支的时钟一致。
(2)采用平衡树结构,在每一级加入缓冲器,使到达每个触发器时钟端的时钟同步。
3、跨时钟域处理
为了避免由异步时钟域产生的错误,经常使用双口RAM(DPRAM),FIFO缓存的方法完
成异步时钟域之间的数据传送
第12章:同步状态机的原理、结构和设计
mealy 状态机 :如果时序逻辑的输出不但取决于状态还取决于输入。
moore 状态机: 有些时序逻辑电路的输出只取决于当前状态
独热编码: 1000 0100 0010 0001
格雷编码: 00 01 10 11
第13章 设计可综合的状态机的指导原则
综合的一般原则
通常,综合的一般原则为:
(1)综合之前一定要进行仿真,这是因为仿真会暴露逻辑错误,所以建议大家这样做。如
果不做仿真,没有发现的逻辑错误会进人综合器,使综合的结果产生同样的逻辑错误。
(2)每一次布局布线之后都要进行仿真,在器件编程或流片之前要做最后的仿真。
(3)用Verilog HDL描述的异步状态机是不能综合的,因此应该避免用综合器来设计:如
果一定要设计异步状态机,则可用电路图输人的方法来设计。
(4)如果要为电平敏感的锁存器建模,使用连续赋值语句是最简单的方法。
always块可以表示时序逻辑或者组合逻辑,也可以用always块既表示电平敏感的透
明锁存器又同时表示组合逻辑,但是不推荐使用这种描述方法,因为这容易产生错误和多余的
电平敏感的透明锁存器。
(I)对一个寄存器型(reg)和整型(integer)变量给定位的赋值,只允许在一个always块内
进行,如在另一always块中也对其赋值,这是非法的。
(2)把某一信号值赋为‘bx,综合器就把它解释成无关状态,因而综合器为其生成的硬件
电路最简洁。
(3)带有posedge或negedge关键字的事件表达式表示沿触发的时序逻辑,没有posedge
或negedge关键字的表示组合逻辑或电平敏感的锁存器,或者两种都表示。
4)每个在always块中赋值的信号都必须定义成reg型或整型。整型变量默认为32位,
使用Verilog操作符可对其进行二进制求补的算术运算。综合器还支持整型变量的范围说