1.四种结构说明语句
2.initial 与 always 的异同点
3.task 与 function 的3个不同点
4.task的语法结构(定义及调用)
5.function的语法结构(定义及调用)
6.function 的一个必须有和一个必须没有,使用规则
7.自动(递归)函数是什么?
8.四种用来输出信息的系统任务是?
9.$display()的格式?
10.$display()输出不定值和高阻值的规则?
11.$display和$write的异同点?
12.$display 和 $strobe 区别?
13.$display 和 $monitor 区别?
14.文件操作函数$fopen使用规则?
15.文件操作函数$fclose使用规则?
16.四种文件输出的系统任务与四种普通的显示/打印类系统任务有何区别?
17.值变转储文件(VCD)
18.$stop 与 $finish
19.四种循环语句
20.repeat用法
21.while用法
22.for用法
23.forever用法
24.顺序块与并行块
25.命名块
26.生成语句与生成块:generate ,三种用法,
27.FPGA分布结构(6种),底层资源。IOB包含什么,CLB包含哪四个部分,
28.缩减运算符及其运算规则、作用。
29.用一张图解释阻塞赋值与非阻塞赋值
30.verilog(硬件描述语言)与其他C语言等的区别?
31.参数型常量及两种实例化参数重定义方法
32.多维数组的定义和使用。
33.运算符 %
34.位运算符:^ ; ^~
35.always里的赋值信号类型
36.时间度量系统函数:$time 和 $realtime
系统函数/任务大部分只能在仿真中使用
37.系统函数$redmemb 和 $readmemh
38.系统任务$random
39. 预编译处理命令:
40.宏定义:
41.文件包含处理:
42.条件编译命令:
43.条件执行系统任务:
二.概念解释
四种结构语句分别为:initial,always,task,function。
两种都是仿真开始时同时立即开始执行,在同一模块中可包含多个,并行运行;而initial只执行一次,always只要达到触发条件就执行。Initial常用于测试文件和虚拟模块的编写;而always常与时序控制相结合,包含沿触发(常常描述时序逻辑)和电平触发(常常描述组合逻辑)。
①task可自定义自己的仿真单位时间,而function则跟随主模块;
②task可有任意数量的输入输出,function可有大于等于1的输入,但只能有一个返回值;
③task里可引用task和function,而function里不能启动task;
definition:
task
begin begin
< … >; c = a ;…
end end
endtask endtask
call:
task_name(port…); my_task(v,w,x,y,z);
definition
function
; input [1:0] my_input;
begin begin
<…>; my_function =my_input
end end
endfunction endfunction
call:
function_name(input); my_function(x);
必须有一个赋值语句为跟函数名同名的变量赋值;
必须不能有时序控制语句,即不能包含@、wait、#三种语句。
verilog不能递归调用,因为并行;若在函数声明时使用关键字automatic ,则可递归,每一个函数调用动态地分配新地址空间。
$display() and $write() and $strobe() and $monitor()
类似于c或c++那种,
$display(“ 字符,格式控制 ”,p1,p2,…,pn);
如:$display(“my age is %d , %h”, age,age);
输出格式用到时查一下即可。
①输出部分位为不定值:X;
②输出部分位为高阻值:Z;
③输出全部位为不定值:x;
④输出全部位为高阻值:z;
display输出后自动换行,write一行输出多个信息,除此之外无差别。
$strobe() 用法与 $display() 一致,区别在于打印的时间点:当程序执行到到当前行时(假设当前行为display 或 strobe),display会立刻显示(所以显示内容与前面语句的顺序是不确定的),而strobe则会等待前面的语句全部执行完毕(包括需要花时间的非阻塞赋值)(确保在同一时钟沿赋值的其他语句执行完毕)(显示变量的时刻更确定)才显示。如:
reg [1:0]a;
initial begin
a = 1 ;
#1 ;
a = a + 1 ;
$display(“ display result : a = %d ”,a);
$strobe(“ strobe result : a = %d ”,a);
#1 ;
$display(“ display result : a = %d ”,a);
end
结果为:
display result : a = 1 ;(非阻塞赋值未完成)
strobe result : a = 2;
display result : a = 2 ;(延迟1s后非阻塞赋值完成)
所以$strobe()系统函数常用于打印当前非阻塞赋值的变量的值。
$monitor用法与$display一致,用于持续检测变量,只要变量发生变化就打印。
用法:$fopen(“filename”,type);返回一个32位值的多通道描述符,可以用integer类型变量存储。如:
integer handle1 ;
handle1 = $fopen(“D:/filename”,“type”);
其中,type指定打开的类型,如下:
文件句柄的32位代表32个通道,最低位用于标准输出通道stdout,0表示关闭,1代表打开;每$fopen一次打开一个通道;可以同时打开多个通道,如:
interger handle1 , handle2, desc1 , desc2 ;
handle1 = $fopen(“file1”,”w”); // handle1 = 32’h0000_0002;(倒数第二位置1)
handle2 = $fopen(“file2”,”w”); // handle1 = 32’h0000_0004;(倒数第三位置1)
initial begin
$fdisplay(handle1,”display 1”);//把display 1 写到 file1.out中;
$fdisplay(handle1 | 1 ,”diplay 2”);//把display 2 写到 file1.out 和 stdout 中;
$fdisplay(handle1 | handle2,”display 3”);//把display 3写到 file1.out 和file2.out中
end
用法:$fclose(handle1);
关闭handle1对应为1的通道,不能再写入。
四种文件输出的系统任务:$fdislplay,$fwrite,$strobe,$monitor;
四种普通的显示/打印类系统任务:$display,$write,$strobe,$monitor;
区别在于:
①普通显示/打印系统任务输出到终端窗口上,另一种输出到文件中;
②文件操作需要先打开文件,存下句柄,在用法上需要加入句柄,如:
$fdisplay(handle1,”%d”,data);
$display(”%d”,data);
除此之外,在效果上都相同。
VCD是ASCII文件,包含仿真时间、范围和信号定义、信号值变化等信息。用于存储仿真过程中的数据,后处理工具可以把VCD文件作为输入,显示仿真波形等信息。
VCD相关系统函数:
$dumpfile(“filename.dmp”);//指定文件
$dumpvars(n,module);//指定要转储的变量
$dumpon;//启动转储
$dumpoff;//停止转储
$dumpall;//生成一个监测点,转储。
$finish 和 $stop 都可以终止仿真,一般用于测试模块的initial块中。其中,$finish可以选择退出modelsim仿真器。一般使用$stop即可。
这两个函数可以带参数:$stop(n);
n = :
0:不输出任何信息
1: 输出当前仿真时刻和位置;
2: 输出当前仿真时刻、位置和仿真过程中所用的memory及CPU时间的统计。
默认带参数1。
forever ; repeat ; while ; for ;
repeat(size)begin <…> end
while(condition) begin <…> end
for() 跟C语言一致
forever begin <…> end 无限循环,可用于产生周期性波形,与always不同的是,必须写在initia块中。
顺序块:begin <…> end 如果没有时序控制语句,如wait、#、@等,则执行这些语句虽有顺序,但不需要执行时间。
并行块:fork <…> join 从仿真的角度看,如果在同一时刻对同一个变量产生影响,就会引入竞争。
块可以具有自己的名字,称为命名块。
命名块里可声明局部变量,可通过层次名引用,可通过disable block_name 禁止。
作用:实现重复赋值/例化
本质:用一条代码来代替多条重复的语句。
方法:
①
genevar i;//定义循环变量,用于判断
generate for(;;)
begin:name//要起名字,必须有begin-end
<…>
end
endgenerate
②
generate
if()
<…>
else
<…>
endgenerate
③
generate
case(N)
<…>
<…>
endcase
endgenerate
用途:
① 重复赋值: assign xxx
② 多次例化: module_name inst_name( x.(x[i]) ); //在这里,多次例化的例化名称可以相同
① 可编程输入输出单元(IOB input output block)
② 可编程基本逻辑单元(CLB configurable logic block : 查找表LUT ,触发器Flip-Flop,复用器MUX,进位链Carry Chain)
③ 嵌入块状RAM(BRAM block ram: 用于生成ram、fifo等)
④ 丰富的布线资源
⑤ 底层内嵌的专用功能单元
⑥ 完整的时钟管理
IOB:
分为两种 :
1.HP bank(high performance bank 高性能)
2.HR bank (high range bank 高范围bank)
一个IOB中包含IPAD、IBUF、OBUF。PAD是与外界连接焊盘的引脚;当IOB配置为input时,需要连接IBUF;当IOB配置为output时,需要连接OBUF。HR支持更大的电压范围;IOB可以通过配置调节驱动电流,上下拉电阻等,适应不同电器标准的IO物理特性。
CLB:
1.CLB LM: SLCIEL + SLCIEM (LUT + MEMORY)
2.CLB LL: SLICEL + SLICEL ( LUT + LUT )
M是memory的意思,有存储功能,可以配置为DRAM/LUTRAM等;LUT可以配置为ROM使用;SLCIEM中的LUT还可配置为移位寄存器;
SLICEL 结构:4个 6-LUT ; 3个MUX ; 进位链 ;8个FF(触发器)
①LUT
通过真值表存放在内存单元中来实现组合逻辑电路功能的模块称为LUT,LUT本质上是一个RAM;所以自然也可以配置为RAM/ROM等;在 FPGA中,只要逻辑表达式是6位以内输入1位输出,综合后的结果通常都会是一个6-LUT。对于更多位的输入,FPGA会采用级联6-LUT的方式实现。(在决定逻辑块的结构时,除了查找表的输入大小之外,评测所用的面积模型,延迟,制程也是重要的考量因素。6-LUT面积和速度方面的性能最好,具备更高的逻辑密度)
6-LUT 是由两个5-LUT和一个MUX2:1构成的,如图:
同理,更高输入的LUT依旧采用级联的方式来实现。
FPGA会选择使用LUT代替传统门电路实现,主要是由于传统门电路存在的一些缺点:
传统门电路的复杂度与输入逻辑变量的个数有关。输入逻辑变量的个数越多,逻辑函数的组合和变化就会更多,这会增加电路的复杂度。
逻辑门的延迟与传输线的延迟不可避免。复杂的门电路通常包含更多的逻辑门和信号路径,因而延迟较大。将延迟不相同的逻辑电路直接拼接在一起可能会导致电路的时钟频率下降,并引起时序相关问题。
使用LUT不会存在上述的问题,因为LUT本质上是一个RAM。它将输入数据作为RAM的地址,然后通过该地址找到对应的值,将该值作为结果输出。当输入变量为0、0,1时,就会将LUT中地址为0、0,1的存储单元中设定的INIT值输出,依此类推,每一次查找的延迟都是固定的。
②MUX
MUX 是一种从多个输入信号中选择单个输出信号的组合逻辑电路。
实现方式主要有两种,一种是使用LUT实现,另一种是直接使用MUX基本逻辑单元实现。
使用6-LUT实现MUX4:1(4输入多路选择器)的方式,它将6个输入分为两组,4个输入(C0,C1,C2,C3)作为输入信号,另外两个输入(S1,S2)作为输入地址:
当输入信号大于4时,一个6-LUT就不够用了,这时候会用到FPGA内部的MUX基本逻辑单元。
③进位链
进位链用于实现加法和减法运行。
④FF:触发器
单目运算符,如下:
reg [3:0]B ;
reg C;
C = &B ; 等价于 C = ((B[0]&B[1])&B[2])&B[3];
即所有位进行与/或/非运算,得到一个一位的二进制数值。
&:判断一个数是不是所有位都为1;
| :判断一个数是不是所有位都为0;
~:判断一个数所有位中1或0的个数的奇偶性。
always@(posedge clk)
begin
b <= a ;
c <= b ;
end
always@(posedge clk)
begin
b = a ;
c = b ;
end
verilog模块中所有过程块(initial块、always块)、连续赋值语句、实例例化引用都是并行的;
它们表示的是一种通过变量名相互连接;
这三者出现的先后顺序不影响;
只有assign和例化可以独立于过程块。
参数型常量:parameter par_name = xxx;
改变参数型常量有两种方式:
①模块名 #( .参数名(新值)) 例化名(端口); //如果不写参数名,则按照参数定义顺序修改数值。
②模块名 例化名(端口)defparam 例化名.参数名 = 新值;(例化名可以嵌套多个)
定义:
reg [n-1]reg_name[m-1];
定义m个n位的存储器,如reg [7:0] mema[255:0] 定义了256个8位的存储器。前面位宽,后面深度(或称个数);
reg y1 [11:0]; // reg型数组,深度为12,位宽为1
wire [7:0] y2 [3:0] // wire型数组,深度为4,位宽为8
reg [7:0] y3 [0:1][0:3]; // reg型三维数组,2行(rows = 2)3列(cols = 3),每个单元数据位宽8bit
赋值:
y3[1][2] = 8’hac ; //第1行第2列的数据置为ac。
模运算符/求余运算符,求两个的余数,要求两个数均为整型数据,符号采用模运算的第一个操作数的符号位。
^ : 按位异或;(XOR)
^~:按位同或;(XNOR)
加个N即表示非。
必须为reg类型
$time 返回一个64位的整数来表示当前仿真时刻值。
$realtime 返回实型数。
常用于监控变量:(用法)
$monitor($time, ,” value = %d ”,value);
可以在仿真的任意时刻被执行使用
其中,$readmemb读取二进制数字;$readmemh读取十六进制数字。使用语法如下:
$readmemb(“<数据文件名>”,<存储器名>);默认1为起始地址
$readmemb(“<数据文件名>”,<存储器名>, <起始地址>);
$readmemb(“<数据文件名>”,<存储器名> ,<起始地址>, <结束地址>);
$random返回一个32位的随机数。
reg [23:0]rand ;
rand = $random % max ;//产生一个 -max ~ max 的随机数
rand = {$random } %max ;//产生一个 0 ~ max 的随机数
编译预处理命令标识为:`
系统任务/函数标识为:$
宏定义:`define 宏名 宏内容
用一个指定的标识符来代表一个字符串。宏定义结尾不加分号,加了会被视为字符串
如:`define wordsize 8 ;使用时为:`wordsize
宏定义可以层层置换:
`define aa a+b;
`define cc c+`aa;
则`cc = c+a+b ;
文件包含处理:`include “filename.v”
作用:将filename.v文件中的所有内容复制并插入当前行。可以嵌套使用。
与其他语言一样,verilog包含条件编译命令:
`ifdef 宏名
<…>
`else
<…>
`endif
当然还有:`ifndef,用法相同。<>不需要加begin end
定义宏名时可以直接: `define 宏名 来控制是否编译,后面无需加内容。
系统任务:$test$plusargs用于条件执行,如:
if($test$plusargs(“displayvar”))
$display(“var”);如果定义了标志displayvar,则执行该语句
系统任务:$value$plusargs用于条件执行,找到匹配选项则返回非0值,如:
if($value$plusargs(“test name = %s”,test_string))
$display(“var”);如果找到匹配项,则执行该语句