4.18层次建模的概念
典型设计中,二者同时存在。设计人员定义顶层模块,逻辑设计者将计划划分为子模块,电路设计者对底层功能块电路进行优化设计。
电路设计者——开关级原语——底层功能块库。
逻辑设计者——库单元——结构描述设计。
RTL描述:寄存器传输级描述,是指行为级与数据流级的一种混合描述(能被逻辑综合工具接受的)。
module *模块名*(*模块端口列表*)//模块声明过程
*模块内容*//该部分内容不允许再嵌套模块
endmodule
T_FF tff0(q[0],clk,reset);//调用D_FF,取名dff0
逻辑仿真的构成
在设计完成之后,需要对其逻辑设计的正确性验证,这样的测试过程叫做逻辑仿真。
过程实现:对模块施加激励,检查其输出。用于测试模块功能的块儿,称为激励块,也可称为测试台。可以选择多种测试台对设计块进行全面测试。
激励块的设计方式:1.包含关系:在激励块的声明中直接调用并驱动设计块(在此顶层块为激励块,由他控制对设计块的输入信号,以及检查设计块的输出信号); 2.并列关系:在虚拟的顶层模块(顶层模块的作用在此只是调用二者)中调用激励块和设计块(二者通过接口进行交互)。
实例
设计块:选择自顶向下的方法进行设计。
step 1:顶层描述。-step 2:定义设计块。- step 3:定义激励块。- step 4:检查仿真结果。
/***************************step 1*********************************/
module ripple_carry_counter(q,clk,reset);//声明顶层模块
output [3:0] q;
input clk,reset;
T_FF tff0(q[0],clk,reset);//调用T_FF模块
T_FF tff1(q[1],q[0],reset);
T_FF tff2(q[2],q[1],reset);
T_FF tff3(q[3],q[2],reset);
endmodule
/**************************step 2********************************/
module T_FF(q,clk,reset);//声明T_FF模块
output q;
input clk,reset;
wird d;
D_FF dff0(q,d,clk,reset);//调用D_FF模块
not n1(d,q);//非门
endmodule
module D_FF(q,d,clk,reset);//声明D_FF模块
output q;
input d,clk,reset;
reg q;
always @(posedge reset or negedge clk)
if(reset)
q<=1'b0;
else
q<=d;
endmodule
/**************************step 3******************************/
module stimulus;
reg clk;
reg reset;
wire[3:0] q;
ripple_carry_counter r1(q,clk,reset);//此处引用设计模块(采用包含关系的设计模块)
initial
clk = 1'b0;//初始clk为0
always
#5 clk = ~clk;//每5个时间单位时钟翻转一次,时钟信号的时钟周期为10个时间单位
initial
begin
reset = 1'b1;//初始reset为1
#15 reset = 1'b0;//在第15个时间单位时,reset翻转为0
#180 reset = 1'b1;//在第180+15个时间单位时,reset翻转为1
#10 reset = 1'b0;//在第10+180+15个时间单位时,reset翻转为0
#20 $finish;//在第20+10+180+15个时间单位时,终止仿真
end
initial
$monitor($time,"Output q=%d",q);
endmodule
4.22基本概念
词法约定
Verilog描述包含一个单词流。
【空白符】仅用于分割标识符,编译时被忽略。
【注释】单行注释// 多行注释/(内容)/
【操作符】单目、双目、三目。(单目操作符置于操作数前,双目操作符置于两操作符之间,三目操作符是两个单独的操作符,用来分割三个操作数。)
【数字声明】指明位数的数字<数字位宽度>’<基数格式><数字> :其中数字位宽度用十进制数来表示,合法的基数格式包括十进制('d或’D)十六进制('h或’H)二进制('b或’B)八进制('o或’O),数字用连续的阿拉伯数字0~10以及a、b、c、d、e、f来表示(其中不同的基数只能相应的使用其中的一部分)。 不指明位数的数字:没有指定基数则默认为十进制,没有指定宽度则默认为位宽度与仿真器和使用的计算机有关(最小32位)。
【X和Z值】不确定值用X表示,高阻值用Z表示:如果某数的最高位为0/x/z,Verilog语言约定将分别使用0/x/z自动对这个数进行扩展以填满余下的更高位。如果某数的最高位为1,则Verilog语言约定将使用0来扩展余下的更高位。举例如下:
12'h13x//这是一个12位的十六进制数,四个最低位不确定(在以十六进制为基数的表示中x/z代表4位) 32'bz //这是一个32位的高阻值(在以二进制为基数的表示中x/z代表1位) b'ox//这是一个6位的八进制数,所有位都不确定(在意八进制为基数的表示中x/z代表3位)
【复数】在表示位宽的数字前面加一个减号,来表明这个常数是个复数。
【下划线和问号】除了第一个字符,下划线可以出现在数字中的任何位置以提高可续性,编译阶段被忽略掉。而“?”是高阻值Z的另一种表示,可以表示“不必关心”的情况。
【字符串】“(内容)”:这样的一个用双引号括起来的字符队列叫做字符串。必须在一行中书写完,不能书写在多行中,亦不可包含回车符。
【关键字】用于定义语言结构的预留语言,实际上是一种特殊的标识符。Verilog中的关键字全部小写,关键字还包括系统任务和编译指令。
【标识符】程序代码中对象的名字,是程序员访问对象的手段。Verilog中的标识符由字母数字字符、下划线_、美元符号$组成,(标识符不可以以数字或美元符号为首,美元符号开始的标识符是为系统函数保留的)区分大小写。
【转义标识符】以反斜线(/)开始,以空白符( )结束。
数据类型
【值的种类】
值的级别 | 硬件电路中的条件 |
---|---|
0 | 逻辑0,条件为假 |
1 | 逻辑1,条件为真 |
x | 逻辑值不确定 |
z | 高阻,浮动状态 |
除此之外,不同强度的驱动源之间的赋值冲突还使用强度值来解决。当同一个线网有两个不同强度信号驱动,则竞争结果值为高强度的值;如果两个信号强度相同,则竞争结果值为不确定值。
强度等级 | 类 型 | 程 度 |
---|---|---|
supply | 驱动 | 最强 |
strong | 驱动 | ↑ |
pull | 驱动 | 丨 |
large | 存储 | 丨 |
weak | 驱动 | 丨 |
medium | 存储 | 丨 |
small | 存储 | 丨 |
highz | 高阻 | 最弱 |
其中在各类线网中,只有trireg类型的线网可以具有存储强度。
wire [31:0] busA,busB,busc;//3条32位宽的总线
reg [0:40] virtual_addr;//向量寄存器,41位宽的虚拟地址,最高有效位是第0位
5.13
real delta;//定义一个名为delta的实型变量
initial
delta = 2.13;
integer i;//定义一个名为i的整型变量
initial
i = delta;//i得到值2
时间寄存器是一种在仿真时保存仿真时间的特殊时间寄存器数据类型,关键字time。其宽度与具体实现有关,最小64位。通过调用系统函数$time可以得到当前的仿真时间。(仿真时间以秒为单位,与真实时间的对应关系需要用户来定义)
time save_sim_time;//定义时间类型的变量save_sim_time
initial
save_sim_time = $timr;//把当前的仿真时间记录下来
-【数组】
数组中每个元素都可以作为一个标量或向量,使用方式<数组名>[<下标>],数组的维数没有限制,可以声明任意维数的数组,但是要注意多维数组需要说明每一维的索引。
数组≠线网≠寄存器向量 向量是一个单独的元件位宽为n;数组由多个元件组成,其中每个元件的位宽为n或1
time chk_point[1:100];//由100个时间检查变量组成的数组
reg[63:0] array_4d [15:0][7:0][7:0][255:0];//四维64位寄存器型数组.
count[5]=0;//把count数组中第5个整数型单元(32位)复位
array_4d[0][0][0][0][15:0] = 0;//把四维数组中索引号为[0][0][0][0]的寄存器型单元的0~15位都置为0
-【存储器】
使用寄存器的一位数组来表示存储器,对寄存器文件、RAM、ROM建模。如果需要访问存储器中的一个特定的字,可以通过将字的地址作为数组的下标来完成:一个字/元素就是数组的一个元素,由数组的索引来指定,位宽为1位或多位。
reg [7:0] membyte[0:1023];//八位的1k字节的存储器membyte
membyte[511];//取出存储器membyte中地址511所存的字节
-【参数】
参数代表常数,不能被赋值,但是可以通过参数重载语句(defparam)使得用户对模块实例进行定制。其类型和范围是可以进行定义的。关键字parameter。
parameter port_id = 5;//定义常数port_id为5
parameter signed [15:0] WIDTH;//把参数width规定为有正负号,宽度为16位
局部参数使用关键字localparam定义,其值不可以通过defparam或有序参数列表或命名参数赋值来直接修改。
-【字符串】
保存在reg类型的变量中,每个字符占用一个字节(8 bit),故声明保存字符串的reg变量时,其位宽应比字符串的位长稍大。(若寄存器变量的宽度大于字符串的大小,则自动用0来填充左边空余位;若寄存器变量的宽度小于字符串大小,则自动截掉字符串最左边的位。)
reg[8*18:1] string_value;//声明变量string_value,~~宽度是18个字节(?)~~
initial
string_value = "Hello Verilog World";//17个字符存在18个字节的寄存器变量中
特殊字符表格
转义字符 | 显示的字符 |
---|---|
\n | 换行 |
\t | TAB |
%% | % |
\ | \ |
" | " |
\ooo | 1~3个八进制数字字符(?) |
5.14
$display("Hello Verilog World");//显示小括号中的字符串
--Hello Vrilog World
%display("ID of the port is %b",port_id);//用二进制数字符显示port_id 5
--ID of the port is 00101
字符串 | 格式说明 |
---|---|
%d或%D | 用十进制显示变量 |
%b或%B | 用二进制显示变量 |
%h或%H | 用十六进制显示变量 |
%o或%O | 用八进制显示变量 |
%s或%S | 显示字符串 |
%c或%C | 显示ASCII字符 |
%m或%M | 显示层次名(?) |
%v或%V | 显示强度 |
%t或%T | 显示当前时间格式 |
%e或%E | 用科学记数法格式显示实数 |
%f或%F | 用十进制浮点数格式显示实数 |
%g或%G | 用科学计数法或十进制格式显示实数,显示较短格式 |
initial
begin
$monitor($time,"Value of signals clock = %b reset = %b",clock,reset);//监视时钟和复位信号的时间和值
end
//监视语句部分输出
-- 0 Value of signals clock = 0 reset = 1
-- 5 Value of signals clock = 1 reset = 1
-- 10 Value of signals clock = 0 reset = 0
#100 $stop;//在仿真时刻为100单位时,暂停仿真
#900 $finish;//在仿真时刻1000是,终止仿真
-【编译指令】使用方式`< keyword >,下面介绍两种:
` define用于定义文本宏(可理解为一种快捷方式),在模块中使用预定义的常数或文本宏时,在宏名前加上 define的之前相同的前缀号(此处不好打出,意会即可)。
`define S $stop;//定义别名,可用`S来代替$stop
`define WORD_REG reg [31:0]//定义常用字符串,可用`WORD_REG reg32来定义一个32位的寄存器变量
·include用于将内含全局或公用定义的头文件包含在设计文件中
`include "header.v"//包含header.v文件,在该文件中主文件需要的内容
5.21
模块与端口
//ANSI C风格的端口声明
module fulladd4( output reg [3:0] sum,
output reg c_out,
input [3:0] a,b//默认为wire数据类型
input c_in//默认为wire数据类型);
<模块的内容>
endmodule
模块内部 | 模块外部 | |
---|---|---|
输入端口input | wire数据类型 | reg/wire数据类型 |
输出端口output | reg/wire数据类型 | wire数据类型 |
输入/输出端口inout | wire数据类型 | wire数据类型 |
module Top;
//下面声明连接变量
reg [3:0]A,B;
reg C_IN;
wire [3:0] SUM;
wire C_OUT;
//方法一:调用模块,按端口列表中的次序连接
fulladd4 fa_orderd(SUM,C_OUT,A,B,C_IN);
//方法二:调用模块,通过端口名誉外部信号连接
fulladd4 fa_byname(.c_out(C_OUT), .sum(SUM), .b(B), .c_in(C_IN), .a(A), );//在这里不需要连接的端口只需要忽略即可
...
endmodule
module fulladd4(sum, c_out, a, b, c_in);
output[3:0] sum;
output c_cout;
input[3:0] a,b;
input c_in;
...
endmodule
5.23
数据流建模
在数字设计领域,RTL通常是指数据流建模和行为级建模的结合。
//连续赋值特点举例
assign out = i1 & i2;//连续赋值语句,out是线网,i1和i2也是线网
---------------------------
assign addr[15:0] = addr1_bits[15:0]^addr2_bits[15:0];//向量线网的连续赋值:左边是16位的向量线网,右边是16位的向量寄存器
------------------------------------------------------
assign {
c_out, sum[3:0]} = a[3:0] + b{
3:0} + c_in;//拼接操作:左边是标量线网和向量线网的拼接
//对比普通的连续赋值和隐式连续赋值
wire out;
assign out = in1 & in2;//普通的连续赋值
-----------------------
wire out = in1 & in2;//隐式连续赋值
assign #10 out = in1 & in2;//连续赋值语句中的延迟
-------------------------
wire #10 out = in1 & in2;//隐式连续赋值延迟=先把out定义为wire类型后再用连续赋值语句为out赋值
-------------------------
wire #10 out;
assign out = in1 & in2;//线网声明延迟