一.相关概念
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.条件执行系统任务:
44.event的定义与触发:
45.数字电路系统的5种描述方式:行为级(系统级、算法级、RTL级)、门级、开关级。
46.流水线处理
47.数据接口同步的问题?如:采集前级模块的信号时,时钟为跨时钟域,怎么保证不出差
48.为什么要使用全局时钟?
49.什么是竞争冒险现象?
50.有限状态机的两种描述方式:
51.阻塞赋值与非阻塞赋值
52.如何避免出现冒险竞争现象?
二.概念解释
1.
四种结构语句分别为:initial,always,task,function。
2.
两种都是仿真开始时同时立即开始执行,在同一模块中可包含多个,并行运行;而initial只执行一次,always只要达到触发条件就执行。Initial常用于测试文件和虚拟模块的编写;而always常与时序控制相结合,包含沿触发(常常描述时序逻辑)和电平触发(常常描述组合逻辑)。
3.
①task可自定义自己的仿真单位时间,而function则跟随主模块;
②task可有任意数量的输入输出,function可有大于等于1的输入,但只能有一个返回值;
③task里可引用task和function,而function里不能启动task;
4.
definition:
task <task_name>; task my_task;
<in/out>; input a,b; inout c ; output d,e;
begin begin
< … >; c = a ;…
end end
endtask endtask
call:
task_name(port…); my_task(v,w,x,y,z);
5.
definition
function <type and range of return value><function_name>; function [1:0]my_function
<input>; input [1:0] my_input;
begin begin
<…>; my_function =my_input
end end
endfunction endfunction
call:
function_name(input); my_function(x);
6.
必须有一个赋值语句为跟函数名同名的变量赋值;
必须不能有时序控制语句,即不能包含@、wait、#三种语句。
7.
verilog不能递归调用,因为并行;若在函数声明时使用关键字automatic ,则可递归,每一个函数调用动态地分配新地址空间。
8.
$display() and $write() and $strobe() and $monitor()
9.
类似于c或c++那种,
$display(“ 字符,格式控制 ”,p1,p2,…,pn);
如:$display(“my age is %d , %h”, age,age);
- my age is 15 , f ; //输出会在终端窗口显示
输出格式用到时查一下即可。
10.
①输出部分位为不定值:X;
②输出部分位为高阻值:Z;
③输出全部位为不定值:x;
④输出全部位为高阻值:z;
11.
display输出后自动换行,write一行输出多个信息,除此之外无差别。
12.
$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()系统函数常用于打印当前非阻塞赋值的变量的值。
13.
$monitor用法与$display一致,用于持续检测变量,只要变量发生变化就打印。
14.
用法:$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
15.
用法:$fclose(handle1);
关闭handle1对应为1的通道,不能再写入。
16.
四种文件输出的系统任务:$fdislplay,$fwrite,$strobe,$monitor;
四种普通的显示/打印类系统任务:$display,$write,$strobe,$monitor;
区别在于:
①普通显示/打印系统任务输出到终端窗口上,另一种输出到文件中;
②文件操作需要先打开文件,存下句柄,在用法上需要加入句柄,如:
$fdisplay(handle1,”%d”,data);
$display(”%d”,data);
除此之外,在效果上都相同。
17.
VCD是ASCII文件,包含仿真时间、范围和信号定义、信号值变化等信息。用于存储仿真过程中的数据,后处理工具可以把VCD文件作为输入,显示仿真波形等信息。
VCD相关系统函数:
$dumpfile(“filename.dmp”);//指定文件
$dumpvars(n,module);//指定要转储的变量
$dumpon;//启动转储
$dumpoff;//停止转储
$dumpall;//生成一个监测点,转储。
18.
$finish 和 $stop 都可以终止仿真,一般用于测试模块的initial块中。其中,$finish可以选择退出modelsim仿真器。一般使用$stop即可。
这两个函数可以带参数:$stop(n);
n = :
0:不输出任何信息
1: 输出当前仿真时刻和位置;
2: 输出当前仿真时刻、位置和仿真过程中所用的memory及CPU时间的统计。
默认带参数1。
19.
forever ; repeat ; while ; for ;
20.
repeat(size)begin <…> end
21.
while(condition) begin <…> end
22.
for() 跟C语言一致
23.
forever begin <…> end 无限循环,可用于产生周期性波形,与always不同的是,必须写在initia块中。
24.
顺序块:begin <…> end 如果没有时序控制语句,如wait、#、@等,则执行这些语句虽有顺序,但不需要执行时间。
并行块:fork <…> join 从仿真的角度看,如果在同一时刻对同一个变量产生影响,就会引入竞争。
25.
块可以具有自己的名字,称为命名块。
命名块里可声明局部变量,可通过层次名引用,可通过disable block_name 禁止。
26.
作用:实现重复赋值/例化
本质:用一条代码来代替多条重复的语句。
方法:
①
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]) ); //在这里,多次例化的例化名称可以相同
27.
① 可编程输入输出单元(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:触发器
28.
单目运算符,如下:
reg [3:0]B ;
reg C;
C = &B ; 等价于 C = ((B[0]&B[1])&B[2])&B[3];
即所有位进行与/或/非运算,得到一个一位的二进制数值。
&:判断一个数是不是所有位都为1;
| :判断一个数是不是所有位都为0;
~:判断一个数所有位中1或0的个数的奇偶性。
29.
always@(posedge clk)
begin
b <= a ;
c <= b ;
end
always@(posedge clk)
begin
b = a ;
c = b ;
end
30.
verilog模块中所有过程块(initial块、always块)、连续赋值语句、实例例化引用都是并行的;
它们表示的是一种通过变量名相互连接;
这三者出现的先后顺序不影响;
只有assign和例化可以独立于过程块。
31.
参数型常量:parameter par_name = xxx;
改变参数型常量有两种方式:
①模块名 #( .参数名(新值)) 例化名(端口); //如果不写参数名,则按照参数定义顺序修改数值。
②模块名 例化名(端口)defparam 例化名.参数名 = 新值;(例化名可以嵌套多个)
32.
定义:
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。
33.
模运算符/求余运算符,求两个的余数,要求两个数均为整型数据,符号采用模运算的第一个操作数的符号位。
34.
^ : 按位异或;(XOR)
^~:按位同或;(XNOR)
加个N即表示非。
35.
必须为reg类型
36.
$time 返回一个64位的整数来表示当前仿真时刻值。
$realtime 返回实型数。
常用于监控变量:(用法)
$monitor($time, ,” value = %d ”,value);
37.
可以在仿真的任意时刻被执行使用
其中,$readmemb读取二进制数字;$readmemh读取十六进制数字。使用语法如下:
$readmemb(“<数据文件名>”,<存储器名>);默认1为起始地址
$readmemb(“<数据文件名>”,<存储器名>, <起始地址>);
$readmemb(“<数据文件名>”,<存储器名> ,<起始地址>, <结束地址>);
38.
$random返回一个32位的随机数。
reg [23:0]rand ;
rand = $random % max ;//产生一个 -max ~ max 的随机数
rand = {$random } %max ;//产生一个 0 ~ max 的随机数
39.
编译预处理命令标识为:`
系统任务/函数标识为:$
40.
宏定义:`define 宏名 宏内容
用一个指定的标识符来代表一个字符串。宏定义结尾不加分号,加了会被视为字符串
如:`define wordsize 8 ;使用时为:`wordsize
宏定义可以层层置换:
`define aa a+b;
`define cc c+`aa;
则`cc = c+a+b ;
41.
文件包含处理:`include “filename.v”
作用:将filename.v文件中的所有内容复制并插入当前行。可以嵌套使用。
42.
与其他语言一样,verilog包含条件编译命令:
`ifdef 宏名
<…>
`else
<…>
`endif
当然还有:`ifndef,用法相同。<>不需要加begin end
定义宏名时可以直接: `define 宏名 来控制是否编译,后面无需加内容。
43.
系统任务:$test$plusargs用于条件执行,如:
if($test$plusargs(“displayvar”))
$display(“var”);如果定义了标志displayvar,则执行该语句
系统任务:$value$plusargs用于条件执行,找到匹配选项则返回非0值,如:
if($value$plusargs(“test name = %s”,test_string))
$display(“var”);如果找到匹配项,则执行该语句
44.
event var;
event触发为:->var;
捕获触发为:@(var);
定义了event之后,就可以在需要的地方触发event:->var
触发event后,就会跳到@(var)处继续往下执行。举例如下:
event reset_event , reset_done_event ;
initial begin
forever begin
@(reset_event);
reset = 1 ;
#20 ;
reset = 0 ;
->reset_done_event;
end
end
//本来initial块只执行一次,但里面用了循环语句forever,所以会一直等待复位事件到来。
initial begin
#100 ;
->reset_event;
@(reset_done_event);
end
45.
门级描述方法可以构建诸如与门、或门等等,如:
nand #10 ndl(a,data,clock,clear);
说明在模块中使用了一个名为ndl的与非门,输入输出延时为10个时间单位。
门级描述表示的是电路结构,他是电路布局布线的依据,而综合器可以把行为级描述的verilog模块转换为门级结构;然后布局布线器再据此进行布局布线。
46.
流水线设计是把规模较大、层次较多的组合逻辑电路分为K级,每一级插入寄存器组并暂存中间数据,上一级的输出是下一级的输入,且无反馈。(就是插入寄存器大法)
如果不插入寄存器,每次完整的组合逻辑运算都要K个GLB层的时间。插入了寄存器后,每次计算所有寄存器都会暂存上一次的结果,所以执行一次计算只需要1个GLB层的时间(当然,第一次运算结果需要K个GLB层的时间),增加了约K倍吞吐量。即流水线设计提高了组合逻辑设计的处理速度和吞吐量。
代价是使用更多的寄存器,一定程度上是面积换取速度。
流水线设计也需要考虑时序问题。
47.
前级(如另一个芯片、pcb布线、驱动接口元件)输出的延时时随机的,且可能是异步时钟域,想要保证采集到的数据时正确的,且不出现亚稳态,一是要用寄存器打两拍,防止数据状态不稳定的传播,使后级处理的数据都是有效电平(虽然不一定是正确的);二是用前级时钟先往双端口RAM、FIFO中缓存数据,再用本级时钟读取数据即可,其中需要空/满的信号来管理数据的读写,以避免数据丢失。,从而完成跨时钟域的数据交换。
48.
使用全局时钟可以使得时钟到达每个触发器的时钟端的时钟沿偏差非常小,防止组合逻辑中由于竞争冒险产生的不稳定信号值存入寄存器中,躲避竞争冒险现象。
49.
在组合逻辑电路中,某个输入变量通过两条或以上的途径传到输出端,由于每条途径延迟时间不同,到达输出门的时间有先有后,这种现象称为竞争;在信号变化的瞬间,组合逻辑的输出有先后顺序,并不是同时变化,往往会出现一些不正确的尖峰信号,这些尖峰信号称为"毛刺"。如果一个组合逻辑电路中有"毛刺"出现,就说明该电路存在冒险。
可以通过代数法和卡诺图来判断。代数法:如果我们对电路化简后得到了 F = A+A' 或者 F = A*A' 的表达式,则电路中存在竞争与冒险。卡诺图:卡诺图存在“相切”的卡诺圈,则电路中存在竞争与冒险。
50.
①经典描述方式:
所有变化在一个always块中。
②复杂多输出状态机:
一个always块处理一个变量。
独热编码:一个状态就用一位来表示,如有4个状态,则定义状态:reg [3:0]state;
51.
阻塞赋值:在赋值时先计算右手方向(RHS)的值,此时不允许其他verilog语句执行。阻塞赋值操作在RHS不能设定有延迟(零延迟也不可),如果加了延迟,延迟期间会阻止赋值语句的执行,但是这种语句不可综合(没有这样的电路给你延时)。
非阻塞赋值:计算非阻塞赋值的RHS表达式和更新LHS期间,其他的verilog语句、包括非阻塞赋值语句都能同时计算RHS和更新LHS。只能对寄存器类型变量赋值,在initial和always块中。
52.
在always块中建立组合逻辑模型时用阻塞赋值;
在同一个always块中建立时序和组合逻辑电路时用非阻塞赋值;
在同一个always块中不要既用阻塞又用非阻塞;
不要在多个always块中为同一变量赋值;
在赋值时不要用#延时;
用$strobe系统任务来显示非阻塞赋值的变量值。