文章目录
1.Verilog电路仿真和验证概述
2.Verilog测试程序设计基础
2.1Testbench及其结构
2.2测试平台举例
2.3Verilog仿真结果确认
2.4Verilog仿真效率
3.与仿真相关的系统任务
3.1$display和 $write
3.2$monitor与 $stobe
3.3$time和 $reltime
3.4$finish和 $stop
3.5$readmemh和 $readmem
3.6$random
4.信号时间赋值语句
4.1时间延迟的语法说明
4.2时间延迟的描述形式
4.3边沿触发时间控制
4.4电平敏感事件控制
5.任务和函数
6.典型测试向量的设计
6.1变量初始化
6.2数据信号测试向量产生
6.3时钟信号测试向量产生
6.4总线信号测试向量产生
7.用户自定义元件模型UDP
8.基本门级元件和模块的延时建模
8.2模块延时建模
9.编译预处理语句
9.1宏定义
9.2文件包含处理
9.3仿真时间标度
9.4条件编译命令
9.5其他语句
10.Verilog测试方法简介
来源:蔡觉平老师的Verilog课程
1.Verilog电路仿真和验证概述
仿真,也叫模拟,是通过使用EDA仿真工具,通过输入测试信号,比对输出信号(波形、文本或者VCD文件)和期望值,来确认是否得到与期望所一致的正确的设计结果,验证设计的正确性。 验证是一个证明设计思路如何实现,保证设计在功能上正确的一个过程。
验证在Verilog设计的整个流程中分为4个阶段:
阶段1:功能验证;
阶段2:综合后验证;
阶段3:时序验证;
阶段4:板级验证。
2.Verilog测试程序设计基础
2.1Testbench及其结构
在仿真的时候Testbench用来产生测试激励给待验证设计( Design Under Verification, DUV),或者称为待测设计(Design UnderTest, DUT) 。 测试程序的一般结构: Testbench是一个测试平台,信号集成在模块内部,没有输入输出。在Testbench模块内,例化待测设计的顶层模块,并把测试行为的代码封装在内,直接对待测系统提供测试激励。
例:T触发器测试程序
module TFF_tb;
reg clk, rst_n, T;
wire data_out;
TFF U1(.data_out(data_out), .T(T), .clk(clk), .rst_n(rst_n)); //对被测模块实例化
always
#5 clk = ~clk;
Initial
begin
clk = 0;
#3 rst_n = 0;
#5 rst_n = 1;
T = 1;
#30 T = 0;
#20 T = 1;
end
Initial
begin
$monitor($time,"T=%b, clk=%b,rst_n=%b,data_out=%b", T, clk, rst_n,data_out);
end
endmodule
T触发器的波形仿真和文本输出: 从下图可以清晰地看出Testbench的主要功能: (1)为DUT提供激励信号0。 (2)正确实例化DUT。 (3)将仿真数据显示在终端或者存为文件,也可以显示在波形窗口中以供分析检查。 (4)复杂设计可以使用EDA工具,或者通过用户接口自动比较仿真结果与理想值,实现结果的自动检查。
在编写Testbench时需要注意的问题: (1)testbench代码不需要可综合 Testbench代码只是硬件行为描述不是硬件设计。 (2)行为级描述效率高 Verilog HDL语言具备5个描述层次,分别为开关级、门级、RTL级、算法级和系统级。 (3)掌握结构化、程式化的描述方式 结构化的描述有利于设计维护,可通过initial、always以及assign语句将不同的测试激励划分开来。 一般不要将所有的测试都放在一个语句块中
2.2测试平台举例
测试平台需要产生时钟信号、复位信号和一系列的仿真向量,观察DUT的响应,确认仿真结果。 (1)组合逻辑电路仿真环境搭建 全加器真值表:
module adder1 (a, b, ci, so, co);
input a, b, ci ;
output so, co ;
assign { co , so } = a + b + ci ;
endmodule
根据全加器的真值表编写的全加器测试程序如下:
module adder1_tb ;
wire so, co;
reg a, b, ci;
adder1 U1(a, b, ci, so, co); //模块例化
initial //测试信号产生
begin
a = 0; b = 0; ci = 0;
#20 a = 0; b = 0; ci = 1;
#20 a = 0; b = 1; ci = 0;
#20 a = 0; b = 1; ci = 1;
#20 a = 1; b = 0; ci = 0;
#20 a = 1; b = 0; ci = 1;
#20 a = 1; b = 1; ci = 0;
#20 a = 1; b = 1; ci = 1;
#200 $finish;
end
endmodule
全加器的输入a、b和ci定义为reg型变量,把输出so和co定义为wire型变量;用模块例化语句 "adder1 U1(a,b,ci,so,co);"把全加器设计电路例化到测试仿真环境中;用initial块语句改变输入的变化并生成测试条件,输入的变化语句完全根据全加器的真值表编写 (2)时序逻辑电路仿真环境的搭建 在于时序逻辑电路仿真环境中,需要考虑时序、定时信息和全局复位、置位等信号,并定义这些信号。
用Verilog编写的十进制加法计数器:
module cnt10(cIk ,rst, ena, q, cout);
input clk,rst,ena;
output [3:0] q;
output cout;
reg [3:0] q;
always@(posedge clk or posedge rst)
begin
if(rst)q=4’b0000;
else if(ena)
begin
if(q<9)
q=q+1;
else
q=0;
end
end
assign cout=q[3]&q[0];
endmodule
测试程序代码:
module cnt10_tb;
reg clk, rst, ena;
wire [3:0] q;
wire cout;
cnt10 U1(clk ,rst, ena, q, cout);
always #50 clk = ~clk;
initial
begin
clk=0;rst=0;ena=1;
#1200 rst=1;
#120 rst=0;
#2000 ena=0;
#200 ena=1;
#20000 $finish;
end
endmodule
实例化语句"cnt10 U1(clk,rst,ena,q,cout);“把十进制计数模块例化到仿真环境中; 在always中用语句”#50 clk=~clk;"产生周期为100(标准时间单位)的时钟方波; 用initial块生成复位信号rst和使能控制信号ena的测试条件。 测试结果如图:
2.3Verilog仿真结果确认
(1)直接观察波形 通过直接观察各信号波形的输出,比较测试值和期望值的大小,来确定仿真结果的正确性。
(2)打印文本输出法
module adder1_tb;
wire so,co;
reg a,b,ci;
adderl U1(a,b,ci,so,co);
initial
begin
a=0;b=0;ci=0;
#20 a=0;b=0;ci=1;
#20 a=0;b=1 ;ci=0;
#20 a=0;b=1 ;ci=1;
#20 a=1 ;b=0;ci=0;
#20 a=1 ;b=0;ci=1;
#20 a=1 ;b=1 ;ci=0;
#20 a=1;b=1;ci=1;
#200 $finish;
end
$monitor($time, "%b %b %b -> %b %b”,a, b, ci, so, co);
endmodule
输出结果:
0 0 0 0 -> 0 0 20 0 0 1 -> 1 0 40 0 1 0 -> 1 0 60 0 1 1 -> 0 1 80 1 0 0-> 1 0
系统任务打印任务: $display:直接输出到标准输出设备; $monitor:监控参数变化; $fdisplay:输出到文件等。
(3)自动检查仿真结果 自动检查仿真结果是通过在设计代码中的关键节点添加断言监控器,形成对电路逻辑综合的注释或是对设计特点的说明,以提高设计模块的观察性。
(4)使用VCD文件 Verilog提供一系列系统任务用于记录信号值变化保存到标准的VCD(Value Change Dump)格式数据库中。VCD文件是一种标准格式的波形记录文件,只记录发生变化的波形。
2.4Verilog仿真效率
因为要通过串行软件代码完成并行语义的转化,Verilog行为级仿真代码的执行时间比较长。
提高Verilog HDL代码的仿真代码执行时间: (1)减小层次结构 仿真代码的层次越少,执行时间就越短。 (2) 减少门级代码的使用 由于门级建模属于结构级建模,建议仿真代码尽量使用行为级语句,建模层次越抽象,执行时间就越短。 (3) 仿真精度越高,效率越低 计时单位值与计时精度值的差距越大,则模拟时间越长。`timescale仿真时间标度。 (4) 进程越少,效率越高 代码中的语句块越少仿真越快,这是因为仿真器在不同进程之间进行切换也需要时间。 (5) 减少仿真器的输出显小 Verilog语言包含一些系统任务,可以在仿真器的控制台显示窗口输出一些提示信息,但会降低仿真器的执行效率。
3.与仿真相关的系统任务
3.1$display和 $write
语法格式: $display("", );
$write("", );
""通常称为“格式控制” ""为”信号输出列表“ $display自动地在输出后进行换行 $write输出特定信息是不换行
输出格式说明,由“%"和格式字符组成,其作用是将输出的数据转换成指定的格式输出。 常用的几种输出格式如下表: 一些特殊的字符可以通过表中的转换序列来输出: 例:$display和 $write语句
module disp_tb;
reg[31 :0] rval;
pulldown(pd);
initial
begin
rval=101;
$display("\\\t%%\n\"\123”);
$display("rval=%h hex %d decimal" rval, rval);
$display("rval=%o otal %b binary", rval, rval);
$display("rval has %c ascii character value",rval);
$display("pd strength value is %v", pd);
$display("current scope is %m");
$display("%s is ascii value for 101",101);
$write("simulation time is");
$write("%t\n", $time);
end
endmodule
在$display中,输出列表中数据的显示宽度是自动按照输出格式进行调整的,总是用表达式的最大值所占的位数来显示表达式的当前值。
3.2$monitor与 $stobe
$monitor与$stobe都提供了监控和输出参数列表中字符或变量的值的功能 (1)$monitor语法格式: $monitor("", );
任务$monitor提供了监控和输出参数列表中的表达式或变量值的功能。每当参数列表中变量或表达式的值发生变化时,整个参数列表中变量或表达式的值都将输出显示。 例如:$monitor($time, , “rxd=%b txd=%b”,rxd txd); 注意在上面的语句中,", ,"代表一个空参数。空参数在输出时显示为空格。
$monitoron和$monitoroff任务的作用是通过打开和关闭监控标志来控制监控任务$monitor的启动和停止,这样使得程序员可以很容易的控制$monitor何时发生。 $monitor与$display的不同处在于$monitor往往在initial块中调用,只要不调用$monitoroff,$monitor便不间断地对所设定的信号进行监视。
例:$monitor系统任务的应用
module monitor_tb;
integer a,b;
initial
begin
a = 2;
b = 2;
forever
begin
#5 a = a + b;
#5 b = a - 1;
end
end
initial $monitor($time,"a = %d,b = %d",a, b);
endmodule
(2)$strobe语法格式: $strobe();
$strobe("",);
探测任务用于在某时刻所有时间处理完后,在这个时间步的结尾输出一行格式化的文本。常用的系统任务如下: $stobe:在所有时间处理完后,以十进制格式输出一行格式化的文本; $strobeb:在所有时间处理完后,以二进制格式输出一行格式化的文本; $strobeo:在所有时间处理完后,以八进制格式输出一行格式化的文本; $strobeh:在所有时间处理完后,以十六进制格式输出一行格式化的文本。
$strobe任务在被调用的时刻所有的赋值语句都完成了,才输出相应的文字信息。$strobe任务提供了另一种数据显示机制,可以保证数据只在所有赋值语句被执行完毕后才被显示。
3.3$time和 $reltime
用这两个时间系统函数可以得到当前的仿真时刻,不同的是,$time函数以64位整数值的形式返回仿真时间,而 $realtime函数则以实数型数据返回仿真时间。 (1)系统函数 $time 例:$time系统任务的应用实例
`timescale 1ns/1ns
module time_tb;
reg ts;
parameter dalay = 2;
initial
begin
#delay ts = 1;
#delay ts = 0;
#delay ts = 1;
#delay ts = 0;
end
initial
$monitor($time,,"ts = %b",ts);
$time
endmodule
输出结果:
0 ts = x 3 ts = 1 5 ts = 0 8 ts = 1 10 ts = 0
(2) $realtime系统函数
$realtime返回的时间数字是一个实型数,该数字也是以时间尺度为基准的。 例:$realtime 系统任务的应用实例:
`timescalel 1ns/1ns
module realtime_tb;
reg set;
parameter p=2;
initial
begin
$monitor($realtime, ,"set=b%" ,set);
#p set=0;
#p set=1;
end
endmodule
输出结果:
0 set=x 2 set=0 4 set=1
3.4$finish和 $stop
系统任务$finish和$stop是用于对仿真过程进行控制,分别表示结束仿真和中断仿真。其语法格式: $finish; $fimsh(n); $stop; $stop(n); 其中,n是$finish和$stop的参数,n可以取0、1或2几个值,分别表示如下含义,如下表所示。
$finish的作用是退出仿真器,返回主操作系统,也就是结束仿真过程。任务$finish可以带参数,根据参数的值输出不同的特征信息。如果不带参数,默认$finish的参数值为1。 $stop任务的作用是把EDA工具(例如仿真器)置成暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。这个任务可以带有参数表达式。根据参数值(0,1或2)的不同,输出不同的信息。参数值越大,输出的信息越多。
$finish的实例
module finish_tb;
integer a,b;
initial
begin
a = 2;
b = 4;
forever
begin
#5 a = a + b;
#5 b = a - 1;
end
end
initial #40 $finish; //程序执行到40个时间单位时退出仿真器
initial
begin
$monitor($time,"a = %d,b = %d",a,b);
end
endmodule
$stop的实例
module stop_tb;
integer a,b;
initial
begin
a = 2;
b = 4;
forever
begin
#5 a = a + b;
#5 b = a - 1;
end
end
initial #40 $stop; //程序执行到40个时间单位时停止仿真
initial
begin
$monitor($time,"a = %d,b = %d",a,b);
end
endmodule
3.5$readmemh和 $readmem
在Verilog程序中有两个系统任务$readmemh和$readmem用来从文件读取数据到存储器中。在两个系统任务可以在仿真的任何时刻被执行使用,其语法格式共有一下: (1) $readmemb(“”,); (2) $readmemb(“”,,); (3) $readmemb(“”,,,); (4) $readmemh(“” ,); (5) $readmemh(“”,,); (6) $readmemh(“”,,,);
例:$readmemh和$readmem系统任务实例
module read_mem_tb;
reg [7:0] memory_b [0:7];
reg [7:0] memory_h [0:31];
integer i;
initial
begin
$readmemb("init_b.txt",memory_b); //把数据文件init_b.txt读入存储器中的指定地址
$readmemh("init_h.txt",memory_h);
for(i = 0;i < 8; i=i+1)
begin
$display("memory_b[%0d] = %b",i.memory_b[i]);
$display("memory_h[%0h] = %h",i.memory_h[i]);
end
end
endmodule
文件init_b.txt和init_h.txt包含初始化数据。用@
在数据文件中指定地址。其中,"init_b.txt"指定二进制数据从第二位地址开始写入;而"init_h.txt"指定十六进制数据从地一位地址写入。样本文件如下:
init_b.txt:
@002
11111111 01010101
00000000 10101010
@001
1111zzzz 00001111
init_h.txt:
@001
00000000000000000000000000000011
00000000000000000000000000000111
00000000000000000000000000001111
00000000000000000000000000011111
3.6$random
$random是产生随机数的系统函数,每次调用该函数将返回一个32位的随机数,该随机数是一个带符号的整数。语法格式: $random%;
这个系统函数提供了一个产生随机数的手段。当函数被调用时返回一个32bit 的随机数。它是一个带符号的整形数。 $random一般的用法是:$ramdom%b,其中b>0,它给出了一个范围在(-b + 1):(b - 1)中的随机数。
实例:
`timescale 1ns/1ns
module random_pulse(dout);
output [9:0] dout;
reg dout;
integer delay1,delay2,k;
initial
begin
#10 dout=0;
for(k=0;k<100;k=k+1)
begin
delay1 = 20 * ({$random} % 6);
delay2 = 20 * (1 +{$random} % 3);
#delay1 dout= 1 << ({$random}%10);
$delay2 dout = 0;
end
end
endmodule
4.信号时间赋值语句
4.1时间延迟的语法说明
延迟语句用于对各条语句的执行时间进行控制,从而决速满足用户的时序要求。Verilog语言中延时控制的语法格式有两类: (1)#<延迟时间>行为语句; (2)#<延迟时间>; 其中,符号"#"是延迟控制的关键字符,<延迟时间>可以是直接指定的延迟时间量,并以多少个仿真时间单位的形式给出。在仿真过程中,所有时延都根据时间单位定义。
下面是带时延的赋值语句示例。 #2 Sum = A ^ B; //#2指定2个时间单位后,将A异或B的值赋值给Sum
根据时间控制部分在过程赋值语句中出现的位置,可以把过程赋值语句中的时间控制方式分为外部时间控制方式和内部时间控制方式。 (1)外部时间控制方式是时间控制出现在整个过程赋值语句的最左端,也就是出现赋值目标变量的左边的时间控制方式,其语法结构如: #5 a=b;
在仿真执行时就相当于如下几条语句:
initial
begin
#5;
a = b;
end
(2)内部时间控制方式是过程赋值语句中的时间控制部分还可以出现在“赋值操作符"和“赋值表达式"之间的时间控制方式。其语法结构如: a=#5b;
其中时间控制部分"#5"就出现在赋值操作符":"和赋值表达式"b"的中间,因此在这条过程赋值语句内带有内部时间控制方式的时间控制。它在执行时就相当于如下几条语句的执行:
initial
begin
temp=b; //先求b的值
#5;
a=temp;
end
4.2时间延迟的描述形式
此处时间延迟的描述形式是指延时控制的描述形式,其分为串行延迟控制、并行延迟控制、阻塞式延迟控制和非阻塞式延迟控制四种形式。以实现两组不同波形的信号为例(如图q0_out和q1_out),说明四种不同时间延迟的描述形式。 (1)串行延迟控制 串行延迟控制是最为常见的信号延迟控制,它是由begin、end过程块加上延迟赋值语句构成,其中延迟赋值语句可以为外部时间控制方式,也可以为内部时间控制方式。在<延迟时间>之后也可根据情况来确定是否执行相应的行为语句。 在<延迟时间>后面有相应的行为语句,则仿真进程遇到这条带有延迟控制的行为语句后并不立即执行行为语句指定的操作,而是要延迟等待到“<延迟时间> ’ 所指定的时间量过去后才真正开始执行行为语句指定的操作。
例:Verilog串行延迟控制方式设计上图的信号
`timescale 1ns/1ns
module serial_delay(q0_out, q1_out);
output q0_out, q1 _out;
reg q0_out, q1_out;
initial
begin
q0_out=1'b0;
#50 q0_out=l'b1;
#100 q0_out=1'b0;
#100 q0_out=1'b1;
#50 q0_out=1'b0;
#100 q0_out=1'b1;
#50 q0_out=1'b0;
#50 q0_out=1'b1;
#50 q0_out=1'b0;
end
initial
begin
q1_out=1'b0;
#100 q1_out=1'b1;
#100 q1_out=1'b0;
#50 q1_out=1'b1;
#100 q1_out=1'b0:
#50 q1_out=1'b1;
#100 q1_out=1'b0;
#50 q1_out=1'b1:
#50 q1_out=1'b0;
end
endmodule
(2)并行延迟控制 并行延迟控制方式是通过fork-join过程块加上延迟赋值语句构成,其中延迟赋值语句同串行延迟控制方式一样,既可以是外部时间控制方式,也可以是内部时间控制方式。在<延迟时间>之后也可根据情况来确定是否执行相应的行力语句。 在<延迟时间>后面有相应的行为语句,则仿真进程遇到这条带有延迟控制的行为语句后并不立即执行行为语句指定的操作,而是要延迟等待到"<延迟时间>"所指定的时间量过去后才真正开始执行行为语句指定的操作。但并行延迟控制方式与串行延迟控制方式不同在于并行延迟控制方式中的多条延迟语句时并行执行的,并不需要等待上一条语句的执行完成才开始执行当前的语句。
例:Verilog并行延迟控制方式设计上例波形图
`timescale 1ns/1ns
module parallel_delay (q0_out,q1_out);
output q0_out,q1_out;
reg q0_out,q1_out;
initial
fork
q0_out=1'b0;
#50 q0_out=1'b1;
#150 q0_out=1'b0;
#250 q0_out=1'b1;
#300 q0_out=1'b0;
#400 q0_out=1'b1;
#450 q0_out=1'b0;
#500 q0_out=1'b1;
#600 q0_out=1'b0;
join
initial
fork
ql_out=1'b0;
#100 q1_out=1'b1;
#200 q1_out=1'b0;
#250 q1_out=1'b1;
#350 q1_out=1'b0;
#400 q1_out=1'b1;
#500 q1_out=1'b0;
#550 q1_out=1'b1;
#600 q1_out=1'b0;
join
endmodule
(3)阻塞式延迟控制 以赋值操作符"=“来标识的赋值操作称为“阻塞式过程赋值”。阻塞式延迟控制是在阻塞式过程赋值基础上带有延时控制的情况,例如:
initial
begin
a = 0;
a = #5 1;
a = #10 0;
a = #15 1;
end
各条阻塞式赋值语句将依次得到执行,并且在第一条语句所指定的赋值操作没有完成之前第二条语句不会开始执行。因此在仿真进程开始时刻将"0"值赋给a,此条赋值语句完成之后才开始执行第二条赋值语句;在完成第一条赋值语句之后,延迟5个时间单位将"1"赋值给a;同理第三条赋值语句是在第二条赋值语句完成之后延迟10个时间单位才开始执行,将"0"赋值给a;最后一条赋值语句是在前三条语句都完成的时刻,延迟15个时间单位,将"1"赋值给a。下图给出了上例中信号a的波形。上述两例都采用的是阻塞式赋值语句。 4)非阻塞式延迟控制 以赋值操作符"<=“来标识的赋值操作称为"非阻塞式过程赋值”。非阻塞式延迟控制是在非阻塞式过程赋值基础上带有延时控制的情况。如下例所示:
initial
begin
a <= 0;
a <= #5 1;
a <= #10 0;
a <= #15 1;
end
在上例中各条非阻塞式赋值语句均以并行方式执行,虽然执行语句在begin-end串行块中,但其执行方式与并行延迟控制方式一致,在仿真进程开始时刻同时执行四条延迟赋值语句。在仿真进程开始时,将"0"值赋值给a;在离仿真开始时刻5个时间单位时,将"1"值赋值给a;在离仿真开始时刻10个时间单位时,将"0"值赋值给a;最后在离仿真开始时刻15个时间单位时,将"1"值赋值给a。下图给出了上例中信号a的波形。 例:Verilog非阻塞延迟控制方式设计
`timescale 1ns/1ns
module non_blocking_delay(q0_out,q1_out);
output q0_out,q1_out;
reg q0_out,q1_out;
initial
begin
q0_out <= 1'b0;
q0.out <= #50 1'b1;
q0_out <= #150 1'b0;
q0_out <= #250 1'b1;
q0_out <= #300 1'b0;
q0_out <= #400 1'b1;
q0_out <= #450 1'b0;
q0_out <= #500 1'b1;
q0_out <= #600 1'b0;
end
initial
begin
q1_out <= 1'b0;
q1_out <= #100 1'b1;
q1_out <= #200 1'b0;
q1_out <= #250 1'b1;
q1_out <= #350 1'b0;
q1_out <= #400 1'b1;
q1_out <= #500 1'b0;
q1_out <= #550 1'b1;
q1_out <= #600 1'b0;
end
endmodule
4.3边沿触发时间控制
边沿触发事件控制的语法格式可以为如下四种形式: 形式1:@(<事件表达式>)行为语句; 形式2:@(<事件表达式>); 形式3:@(<事件表达式1>or<事件表达式2>or…or<事件表达式n>) 行为语句; 形式4:@(<事件表达式1>or<事件表达式2>or…or<事件表达式n>);
1,事件表达式 在事件表达式中,可以以三种形式出现: 形式1:<信号名> 形式2:posedge<信号名> 形式3:negedge<信号名>
其中,"<信号名>“可以是任何数据类型的标量或矢量。 形式1中,代表触发事件的”<信号名>"在指定的信号发生逻辑变化时,执行下面的语句,如下例: @(in) out=in; 当敏感事件In发生逻辑变化时(包括正跳变和负跳变),执行对应的赋值语句,将in的值赋值给out。
形式2中,代表触发事件的"posedge <信号名>"在指定的信号发生了正跳变时,执行下面的语句,如下例: @(posedge(n) out=in; 当敏感事件in发生正跳变时,执行对应的赋值语句,将in的值赋值给out。
形式3中,代表触发事件的"negedge <信号名>"在指定的信号发生了负跳变时,执行下面的语句,如下例: @(negedge in) out=in; 当敏感事件in发生负跳变时,执行对应的赋值语句,将in的值赋值给out。
在信号发生逻辑变化(正跳变或负跳变)的过程中,信号的值是从0、1、x、 z四个值中的一个值变化到另一个值;而信号发生正跳变的过程是信号山低电平向高电平的转变,负跳变是信号山高电平向低电平的转变。下表为 Verilog HDL中规定的正跳变和负跳变:
正跳变
负跳变
0→x
1→x
0→z
1→z
0→1
1→0
x→1
x→0
z→1
z→0
2.边沿触发语法格式 形式1:@(<事件表达式>) 行为语句; 这种语法格式的敏感事件列表内只包含了一个触发事件,只有当这个指定的触发事件发生之后,后面的行为语句才能启动执行。在仿真进程中遇到这种带有事件控制的行为语句时,如果指定的触发事件还没有发生,则仿真进程就会停留在此处等待,直到指定触发事件发生之后再启动执行后面的行为语句,仿真进程继续向下进行。 例:时钟脉冲计数器
module clk_counter(clk, count_out);
input clk;
output count_out;
reg[3:0] count_out;
initial
count_out = 0;
always@(posedge clk)
count_out = count_out + 1; //在clk的每个正跳变边沿count_out增加1
endmodule
形式2:@(<事件表达式>); 这种语法格式的敏感事件列表内也只包含了一个触发事件,没有行为语句来指定触发事件发生时要执行的操作。这种格式的事件控制语句的执行过程与延时控制语句中没有行为语句的情况类似,仿真进程在遇到这条事件控制语句后会进入等待状态,直到指定的触发事件发生后才结束等待状态,退出该事件控制语句的执行并开始下一条语句的执行。
例:用于测定输入时钟正电平,负电平持续时间以及时钟周期的模块
module clk_time(clk);
input clk;
time posedge_time,negegde_time;
time high_last_time,low_last_time,last_time;
initial
begin
@(posedge clk);
posedge_time = $time;
@(negedge clk);
negedge_time = $time;
@(negedge clk);
last_time = $time - posedge_time;
high_last_time = negedge_time - posedge_time;
low_last_time = last_time - high_last_time;
$display("The clk stay in High level for: %t",high_last_time);
$display("The clk stay in Low level for: %t",low_last_time);
$display("The clk stay in signal Period for: %t",last_time);
end
endmodule
形式3:@(<事件表达式1>or<事件表达式2>…or<事件表达式n>) 行为语句; 这种语法格式的“敏感事件列表”内指定了由不同"<事件表达式>“代表的多个触发事件,这些”<事件表达式>"之间要用关键词”or“组合起来。只要这些触发事件中的任何一个得到发生,就启动行为语句的执行。在仿真进程遇到这种格式的边沿触发事件控制语句时,如果所有的触发事件都没有发生,则仿真进程就会进入等待状态,直到其中的某一个出发事件发生后才启动执行后面给出的行为语句,仿真进程继续向下进行。
形式4:@(<事件表达式1>or<事件表达式2>or…or<事件表达式n>) 同第三种语法格式一样,这种语法格式内指定了多个触发事件。但是在这种格式中没有行为语句。在这种情况下,该语句的执行过程与第二种语法格式的执行过程类似,仿真进程在遇到这条事件控制语句后会进入等待状态,直到敏感事件列表包含的多个触发事件中的任何一个得到发生后才结束等待状态,退出该事件控制语句并开始执行该事件控制语句后的下一条语句。 例:在触发事件发生后退出事件控制语句
module display(a,b);
input a,b;
wire a,b;
always@(posedge a or negndge b);
begin
$display("One of a and b changed in time: %t",$time);
end
endmodule
4.4电平敏感事件控制
电平敏感时间控制是另一种事件控制方式,与边沿触发事件控制不同,它是在指定的条件表达式为真时启动需要执行的语句。电平敏感时间控制是用关键词“wait"来表示。 电平触发事件控制的语法格式可以为如下两种: 形式1:wait(条件表达式)行为语句; 形式2:wait(条件表达式); 电平敏感事件控制的第一种形式中包含了行为语句,它可以是串行块(begin-end)语句或并行块(fork-join)语句,也可以是单条行为语句。在这种事件控制语句形式下,行为语句启动执行的触发条件是:条件表达式的值为"真(逻辑1)“。如果当仿真进程执行到这条电平敏感控制语句时条件表达式的值是"真”,那么语句块立即得到执行,否则语句块要一直等到条件表达式变为“真"时再开始执行。
例如:
wait(enable == 1)
begin
d = a & b;
d = d | c;
end
wait语句的作用是根据条件表达式的真假来控制下面begin-end语句块的执行,在使能信号enable变为高电平后,也就是enable==1的语句为真时进行a,b, c 之间的与或操作;若使能信号enable未变为高电平,则begin-end语句块的执行需要等到enable变为高电平之后才开始执行。
电平敏感事件控制的第2种形式中没有包含行为语句。在这种电平敏感事件控制语句形式下,如果当仿真进程执行到该wait控制语句时条件表达式的值是"真",那么立即结束该wait事件控制语句的执行,仿真进程继续往下进行;而如果当仿真进程执行到这条wait控制语句时条件表达式的值是"假",则仿真进程进入等待状态,一直等到条件表达式取值变为"真"时才退出等待状态同时结束该wait语句的执行,仿真进程继续往下进行。这种形式的电平敏感时间控制常常用来对串行块中各条语句的执行时序进行控制。
begin
wait(enable == 1)
d = a & b;
d = d | c;
end
5.任务和函数
在Verilog语言中提供了任务和函数,可以将较大的行为级设计划分为较小的代码段,允许设计者将需要在多个地方重复使用的相同代码提取出来,编写成任务和函数,这样可以使代码更加简洁和易懂。
5.1任务
任务的定义 任务定义的语法格式: task<任务名>;
端口和类型声明
局部变量声明
begin
语句1;
语句1;
......
语句n;
end
endtask
任务定义是嵌入在关键字task和endtask之间的,其中关键词task标志着一个任务定义结构的开端,endtask标志着一个任务定义结构的结束。“<任务名>” 是所定义任务的名称。在"<任务名>"后面不能出现输入输入端口列表。
例以读存储器数据为例说明任务定义的操作
task read_memory;
input[15:0] address;
output[31:0] data;
reg[3:0] counter;
reg[7:0] temp[1:4];
begin
for(counter=1;counter<=4;counter=counter+1)
temp[counter]=mem[address+counter-1];
data={temp[1],temp[2],temp[3],temp[4]};
end
endtask
任务定义时需注意以下事项: (1)在第一行"task"语句中不能列出端口名列表。 (2)任务中可以有延时语句、敏感事件控制语句等事件控制语句。 (3)任务可以没有或可以有一个或多个输入、输出和双向端口。 (4)任务可以没有返回值,也可以通过输出端口或双向端口返回一个或多个返回值。 (5)任务可以调用其它的任务或函数,也可以调用该任务本身。 (6)任务定义结构内不允许出现过程块(initial或always过程块)。 (7)任务定义结构内可以出现disable终止语句,这条语句的执行将中断正 在执行的任务。在任务被中断后,程序流程将返回到调用任务的地方继续向下执行 。
2.任务调用 任务的调用是通过"任务调用语句"来实现的。任务调用语句列出了传入任务的参数值和接收结果的变量值,任务的调用格式如下: <任务名>(端口1,端口,...,端口n);
例:以测试仿真中常用的方式来说明任务的调用
module task_tb;
reg[7:0] mem[127:0];
reg[15:0] a;
reg[31:0] b;
initial
begin
a = 0;
read_mem(a,b);//任务的第一次任务调用
#10;
a = 64;
read_mem(a,b);
end
task read_memory;
input[15:0] address;
output[31:0] data;
reg[3:0] counter;
reg[7:0] temp[1:4];
begin
for(counter=1;counter<=4;counter=counter+1)
temp[counter]=mem[address+counter-1];
data={temp[1],temp[2],temp[3],temp[4]};
end
endtask
使用任务可以使程序更加简洁易懂,以实际中的交通控制灯为例说明任务的定义,调用的特点。
module traffic_lights(red, amber, green);
output red, amber, green;
reg [2:1] order;
reg clock, red, amber, green;
parameter ON=1, OFF=0, RED_TICS=350,AMBER_TICS=30,GREEN_TICS=200;//产生时钟脉冲
always
begin
#100 clock = 0;
#100 clock= 1;
end
//任务的定义,该任务用于实现交通灯的开启
task light;
output red;
output amber;
output green;
input [31:0] tic_time;
input [2:1] order;
begin
red = OFF;
green = OFF;
amber = OFF;
case(order)
2'1b01: red = ON;
2'b10: green = ON;
2'b11: amber = ON;
endcase
repeat(tic_time)@(posedge clock);
red = OFF;
green = OFF;
amber = OFF;
end
endtask
//任务调用,交通灯初始化
initial
begin
order = 2'b00;
light(red, amber, green, 0, order);
end
//任务调用,交通灯控制时序
always
begin
order=2’b01;
light(red, amber, green,RED_TICS,order);//调用开灯任务,开红灯
order=2'b10;
light(red, amber, green,GREEN_TICS,order);//调用开灯任务,开绿灯
order=2'b11;
light(red, amber, green,AMBER_TICS,order);//调用开灯任务,开黄灯
end
endmodule
5.2函数
1.函数的定义 function<返回值类型或位宽><函数名>;
<输入参量与类型声明>
<局部变量声明>
begin
语句1;
语句;
...
语句n;
end
endfunction
函数定义是嵌入在关键字function和endfunction之间的,其中关键词function标志着一个函数定义结构的开端,endfunction标志着一个函数定义结构的结束。"<函数名>"是给被定义函数取的名称。这个函数名在函数定义结构内部还代表着一个内部变量,函数调用后的返回值是通过这个函数名变量传递给调用语句的。
<返回值类型或位宽>是一个可选项,它是用来对函数调用返回数据的类型或宽度进行说明,它可以有如下三种形式: (1)“[msb Isb]”:这种形式说明函数名所代表的返回数据变量时一个多位的寄存器变量,它的位宽由[ msb:lsb]指定,比函数定义语句:
function[7:0] adder;
就定义了一个函数"adder",它的函数名"adder"还代表着一个8位宽的寄存器变量,其中最高位为第7位,最低位为第0位。 (2)“integer”:这种形式说明函数名代表的返回变量是一个整数型变量。 (3)“real”:这种形式说明函数名代表的返回变量是一个实数型变量。
"<输入参量与类型声明> "是对函数各个输入端口的宽度和类型进行说明,在函数定义中,必须至少有一个输入端口(input)的声明,不能有输出端口(output)的声明。数据类型声明语句用来对函数内用到的局部变量进行宽度和类型说明,这个说明语句的语法与进行模块定义时的相应说明语句语法是一致的。 "<局部变量说明>"是对函数内部局部变量进行宽度和类型的说明。 由"begin与"end"关键词界定的一系列语句和任务一样,用来指明函数被调用时要执行的操作,在函数被调用时,这些语句将以串行方式得到执行。
例:统计输入数据中"0"的个数
function[3:0] out;
input[7:0] x;
reg[3:0] count;
integer i;
begin
count=0;
for(i=0;i<=7;i=i+1;)
if(x[i]==1'b0)
count=count+1;
out0=count;
end
endmodule
在进行函数定义时需要注意以下几点 (1)与任务一样,函数定义结构只能出现在模块中,而不能出现在过程块内。 (2)函数至少必须有一个输入端口。 (3)函数不能有任何类型的输出端口(output端口)和双向端口(inout端口)。 (4)在函数定义结构中的行为语句部分内不能出现任何类型的时间控制描述,也不允许使用disable终止语句。 (5)与任务定义一样,函数定义结构内部不能出现过程块。 (6)在一个函数内可以对其它函数进行调用,但是函数不能调用其它任务。 (7)在第一行"function"语句中不能出现端口名列表。 (8)在函数声明的时候,在Verilog的内部隐含地声明了一个名为function_identifier(函数标识符)的寄存器类型变量,函数的输出结果将通过这个寄存器类型变量被传递回来。
2.函数的调用 函数的调用是通过将函数作为表达式中的操作数来实现的。函数的调用格式如下: <函数名>(<输入表达式1>,<输入表达式2>,...,<输入表达式n>);
其中,输入表达式应与函数定义结构中说明的输入端口一 一对应,它们代表着各个输入端口的输入数据。 函数调用时要注意以下几点: (1)函数的调用不能单独作为一条语句出现,它只能作为一个操作数出现在调用语句内。 (2)函数的调用既能出现在过程块中,也能出现在assign连续赋值语句中。 (3)函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。
module tryfact_tb;
function[31:0] factorial;
input[3:0] operand;
reg[3:0] index;
begin
factorial = 1;
for(index=1;index<=operand;index=index+1)
factorial = index * factorial;
end
endfunction
reg[31:0] result;
reg[3:O] n;
initial
begin
result=1;
for(n=1;n<=9;n=n+1)
begin
result = factorial(n);
$display("n= %d result= %d", n, result);
end
end
endmodule
上例由函数定义和initia|过程块构成,其中定义了一个名为factorial函数,该函数是一个进行阶乘运算的函数,具有一个4位的输入端口,同时返回一个32位的寄存器类型的值;在initial块中定义了两个寄存器变量分别为32位的result和4位的n,initial块对1至9进行阶乘运算,并打印出结果值。
5.3任务与函数的区别
6.典型测试向量的设计
6.1变量初始化
在Verilog语言中,有两种方法可以初始化变量 一种是利用初始化变量,另一种就是在定义变量时直接赋值初始化。这两种初始化任务是不可综合的,主要用于仿真过程。 (1)initial初始化方式 在大多数情况下,Testbench中变量初始化的工作通过initial过程块来完成,可以产生丰富的仿真激励。 initial语句只执行一次,即在设计被开始模拟执行时开始(0时刻)直到过程结束,专门用于对输入信号进行初始化和产生特定的信号波形。一个Testbench可以包含多initial过程语句块,所有的initia|过程都同时执行需要注意的是,initial语句中的变量必须为reg类型。
例:利用initial初始化方式的测试向量产出
module counter(clk,cnt);
output [3:0] cnt;
reg clk;
reg [3:0] temp;
initial temp = 0;
initial clk = 0;
endmodule
(2)定义变量时初始化在定义变量时初始化的语法非常简单,直接用"="在变量右端赋值即可,如: reg [7:0] cnt=8’b00000000; 就将8比特的寄存器变量cnt初始化为0。
6.2数据信号测试向量产生
数据信号的产生有两种形式:其一是初始化和产生都在单个initial块中进行;其一是初始化在initial语句中完成,而产生在always语句块中完成。前者适合不规则数据序列,并且要求长度较短;后者适合具有一定规律的数据序列,长度不限。
例:产生位宽为4的质数序列{1、2、3、5、7、11、13 },并且重复两次,其中样值间隔为4个仿真时间单位 由于该序列无明显规律,因此利用Initial语句最为合适。
`timescale 1ns/1ps
module sequence_tb;
reg [3:0] qout;
parameter sample_period = 4;
parameter queue_num = 2;
initial
begin
q_out =0;
repeat( queue_num)
begin
# sample_period q_out = 1;
# sample_period q_out = 2;
# sample_period q_out = 3;
# sample_period q out = 5;
# sample_period q_out = 7;
# sample_period q_out= 11;
# sample_period q_out = 13;
end
end
e ndmodule
6.3时钟信号测试向量产生
例:产生占空比为50%的时钟信号 (1)基于initial语句的方法
module clk1(clk);
output clk;
parameter clk_period = 10;
reg clk;
initial
begin
clk = 0;
forever #(clk_period/2) clk = ~clk;
end
endmodule
(2)基于always语句的方法
module clk2(clk);
output clk;
parameter clk_period = 10;
reg clk;
initial clk = 0;
always #(clk_period/2) clk = ~clk;
endmodule
6.4总线信号测试向量产生
总线是运算部件之间数据流通的公共通道。在RTL级描述中,总线指的是由逻辑单元、寄存器、存储器、电路输入或其它总线驱动的一个共享向量。而总线功能模型则是一种将物理的接口时序操作转化成更高抽象层次接口的总线模型,如图: 在总线中,对于每个请求端,有一个输入来选择驱动该总线所对应的请求端。选择多个请求端会产生总线冲突,根据总线的类型,冲突会产生不同的结果。当有多个请求端发出请求时,相应的操作由总线的类型决定。在Verilog测试中,总线测试信号通常是通过将片选信号,读(或者写)使能信号、地址信号以及数据信号以task任务的形式来描述,通过调用以task形式的总线信号测试向量来完成相应的总线功能 。 下面以工作频率为100MHz的AHB总线写操作为例,说明以task方式产生总线信号测试向量的方式。下图是AHB总线写操作的时序图,其中,在完成数据的写操作后将片选和写使能信号置为无效(低电平有效)。
例:产生一组具有写操作AHB总线功能模型
module bus_wr_tb;
reg clk;
reg cs;
reg wr;
reg [31:0] addr;
reg [31:0] data:
initial
begin
cs=1'b1;
wr=1'b1;
#30;
bus_wr(32'h1100008a, 32’h11113000);
bus_wr(32’h1100009a, 32'h11113001);
bus_wr(32'h110000aa, 32'h11113002);
bus_wr(32'h110000ba, 32’h11113003);
bus_wr(32'h110000ca, 32’h11113004);
addr=32’bx;
data=32’bx;
end
initial clk=1;
always #5 clk=~clk;
task bus_wr;
input [31:0] ADDR;
input [31:0] DATA;
begin
cs=1'b0;
wr=1'b0;
addr = ADDR;
data = DATA;
#30 cs=1'b1;
wr=1'b1;
end
endtask
endmodule
7.用户自定义元件模型UDP
通过UDP,可以把一块组合逻辑电路或时序逻辑电路封装在一个UDP内,并把这个UDP作为一个基本门元件来使用。需要注意的是,UDP是不能综合的,只能用于仿真。
7.1UDP的定义与调用
UDP的定义格式如下:
primitive <元件名称>(<输出端口名>,<输入端口名1 >,<输入端口名2>,...,<输入端口名n>)
输出端口类型声明(output);
输入端口类型声明(input);
输出端口寄存器变量说明(reg);
元件初始状态说明(initial);
table
;
;
...
;
endtable
endprimitive
和Verilog中的模块相比,UDP具备以下特点。 (1)UDP的输出端口只能有一个,且必须位于端口列表的第一项。只有输出端口能定义为reg类型。 (2)UDP的输入端可有多个,一般时序电路UDP的输入端口最多9个,组合电路UDP 的输入端口可多至10个。 (3)所有端口变量的位宽必须是1比特。 (4)在table表项中,只能出现0、1、x这三种状态,z将被认为是x状态。
根据UDP包含的基本逻辑功能,可以将UDP分为组合电路UDP、时序电路UDP及混合电路UDP,这几类UDP的差别主要体现在table表项的描述上。 UDP的调用和Verilog中模块的调用方法相似,通过位置映射,其语法格式如下: UDP名 例化名(连接端口1信号名,连接端口2信号名,连接端口3信号名,...);
例:用UDP方式定义一个全加器仿真模型
primitive summ(sum, cin, a, b);//本位和
output sum;
input a,b,cin;
table
//cin a b : sum
0 0 0 : 0;
0 0 1 : 1;
0 1 0 : 1;
0 1 1 : 0;
1 0 0 : 1;
1 0 1 : 0;
1 1 0 : 0;
1 1 1 : 1;
endtable
endprimitive
primitive summ(sum, cin, a, b);//进位
output cout;
input a,b,cin;
table
//cin a b : sum
0 0 0 : 0;
0 0 1 : 0;
0 1 0 : 0;
0 1 1 : 1;
1 0 0 : 0;
1 0 1 : 1;
1 1 0 : 1;
1 1 1 : 1;
endtable
endprimitive
7.2UDP应用实例
(1)组合电路UDP元件 组合逻辑电路的功能列表类似真值表,就是规定了不同的输入值和对应的输出值,表中每一行形式是output,input1,input2,…,排列顺序和端口列表中的顺序相同,合电路UDP的输入端口可多至10个。如果某个输入组合没有定义的输出,那么就把这种情况的输出置为x。
例:3选1多路选择器
primitive mux3_1(Y,in0,in1,in2,s2,s1);
input in0,in1,in2,s2,s1;
output Y;
table
//in0 in1 in2 s2 s1 : Y
0 ? ? 0 0 : 0;//当s2s1=00时,Y=in0
1 ? ? 0 0 : 1;
? 0 ? 0 1 : 0;//当s2s1=01时,Y=in1
? 1 ? 0 1 : 1;
? ? 0 1 ? : 0;//当s2s1=1?时,Y=in2
? ? 1 1 ? : 1;
0 0 ? 0 ? : 0;
1 1 ? 0 ? : 1;
0 ? 0 ? 0 : 0;
1 ? 1 ? 0 : 1;
? 0 0 ? 1 : 0;
? 1 1 ? 1 : 1;
endtable
endprimitive
(2)时序电路UDP元件 UDP还可以描述具有电平触发和边沿触发特性的时序电路。时序电路拥有内部状态序列,其内部状态必须用寄存器变量进行建模,该寄存器的值就是时序电路的当前状态,它的下一个状态是山放在基元功能列表中的状态转换表决定的,而且寄存器的下一个状态就是这个时序电路UDP的输出值。所以,时序电路UDP由两部分组成一状态寄存器和状态列表。定义时序UDP的工 作也分为两部分一初始化状态寄存器和描述状态列表。 在时序电路的UDP描述中,01、Ox、xl代表着信号的上升沿。下面给出一个上升沿D触发器的UDP开发实例。
例:通过Verilog给出D触发器UDP描述,并在模块中调用UDP组件
primitive D_Edge(Q,Clk,Data);
output Q;
reg Q;
input Data,Clk;
initial Q = 0;
table
//Clk Data : Q(Stata): Q(next)
(01) 0 : ? : 0;
(01) 1 : ? : 1;
(0x) 1 : 1 : 1;
(0x) 0 : 0 : 0;
(?0) ? : ? : -;// 忽略时钟负边沿
? (??) : ? : -;// 忽略在稳定时钟上的数据变化
endtable
endprimitive;
表项(01)表示从0转换到1,表项(0x)表示从0转换到x,表项(?0)表示从任意值(0、1或x)转换到0,表项(??)表示任意转换,输出默认为x。假定D_Edge为UDP定义,它现在就能像基本门一样在模块中使用。
(3)混合电路UDP元件 在同一个表中能够混合电平触发和边沿触发项。在这种情况下,边沿变化在电平触发之前处理,即电平触发项覆盖边沿触发项。下面给出一段带异步清空的D触发器的UDP描述。 例:利用Verilog语言完成异步清零D触发器的UDP描述
primitive D_Async_FF(0, Clk, Clr, Data);
output Q;
reg Q;
input Clk, Data, Clr;
table
//Clk Clr Data : (SQtate) : Q(next)
(01) 0 0 : ? : 0;
(01) 0 0 : ? : 0;
(0x) 0 1 : 1 : 1;
(0x) 0 0 : 0 : 0;
(?0) 0 ? : ? : -;
(??) 0 ? : ? : -;
? 1 ? : ? : 0;
endtable
endprimitive
8.基本门级元件和模块的延时建模
8.1门级延时建模
门级延时可以分为如下几类: (1)“上升延时”:表示信号由"x"或"z"状态变化到"1"状态时受到的门传输延时。 (2)“下降延时”:表示信号由"1",“x"或"z"状态变化到"0"状态时受到的门传输延时。 (3)“到不定态的延时”:表示信号由"0”、“1"或"z"状态变化到"x"状态时受到的门传输延时。 (4)“截止延时”:表示信号由"0”、"1"或"x"状态变化到"z"状态时受到的门传输延时。
1.门级延时的基本延时表达形式 在门级延时的基本表达形式下,“delay"内可以0至3个延时值,下表给出了指定的不同延时值个数时,delay的4种表达形式。 (1)当"delay"没有指定门延时值,则默认的延时值为0。这意味着元件实例的"上升延时值”、“下降延时值”、“截止延时值"和"到不定态的延时值"均为0。 在实例:notif1 U0(out, in, ctrl); 门级延时值为"0”,因为没有定义延迟,则元件实例UO的"上升延时值"、“下降延时值”、“截止延时值"和"到不定态的延时值"均为0。 (2)当"delay"内只包含1个延时值时,给定的延时值"d"将同时代表着元件实例的"上升延时值"、“下降延时值”、“截止延时值"和"到不定态的延时值”。在下面的实例引用中, notif1 #20 U1(out, in, ctrl); 门级延时值为"20",且只包含1个延时值,说明元件实例U1的所有类型的门级延时都是20个单位时间。 (3)当"delay"内包含了2个延时值时,元件实例的"上升延时值"由给定的“d1"指定;“下降延时值"由给定的"d2"指定;而"截止延时值"和"到不定态的延时值”“将由"dl"和"d2"中的最小值指定。在实例中: notif1 #(10,20)U2(out, in, ctrl); 门级延时值为”(10,20)“,包含了2个延时值10和20,这表明元件实例U2将具有10个单位时间的"上升延时"和20个单位时间的"下降延时”,而它的"截止延时"和"到不定态的延时"将是10和20中的最小值指定,即为10个单位时间。 (4)当"delay"内包含了3个延时值,元件实例的"上升延时值"由给定的"dA"指定;“下降延时值"由给定的“dB"指定;“截止延时"由给定的"dC '指定;而它的"到不定态的延时"将由dA、dB和dC中的最小值指定。在实例: notif1 #(10,20,30)U3(out, in, ctrl); 门级延时值为“ 00,20,30)”,包含了3个延时值10、20和30,这表明元件实例IJ3具有10个单位时间的“上升延时”、20个单位时间的“下降延时"和30个单位时间的“截止延时",而它的“到不定态的延时"将有 10、20和30中的最小值指定,即为10个单位时间。
2.门级延时的最小、典型、最大延时表达形式 除了基本延时表达形式外,门级延时量还可以采用”最小、典型、最大“延时表达形式,在这种表示方式下,门级延时量中的每一项将由“最小延时"、“典型延时"和”最大延时"二个值来表示,其语法格式如下: #(d min:d_typ:d_max)
采用"最小、典型、最大"延时表达形式时,delay"内可以包含为1至3个延时值,如下列几种情况:
#(dA_min: dA_typ: dA_max) #(dA_min: dA_typ: dA_max, dB_min: dB_typ: dB_max) #(dA_min :dA_typ: dA_max, dB_min: dB_typ: dB_max, dC_min: dC_typ: dC_max)
在实例:and #(4:5:6) U1(out,i1,i2); “delay"中只包含1个延迟值,其最小值为4、典型值为5、最大值为6。元件实例UI的"上升延时值”、“下降延时值”、"截止延时值"和“到不定态的延时值"如下表:
最小延时
上升延时=4
下降延时:4
到不定态的延时 =4
关断延时:4
典型延时
上升延时:5
下降延时=5
到不定态的延时
关断延时:5
最大延时
上升延时=6
下降延时=6
到不定态的延时
关断延时= 6
在实例:and #(3:4:5,5:6:7)U2(out, i1,i2); "delay"中包含了2个延迟值,第一个延迟值的最小值为3、典型值为4、最大值为5,第二个延迟值的最小值为5、典型值为6、最大值为7。元件实例U2的"上升延时值"由第一个延时值指定,下降延时值"山第二个延时值指定,"到不定态的延时值"和"截止延时值"均由两个延时值的最小值指定。各值的取值情况如下表:
最小延时
上升延时=3
下降延时=5
到不定态的延时=min(3,5)
关断延时=min(3,5)
典型延时
上升延时=4
下降延时=6
到不定态的延时=min(4,6)
关断延时=min(4,6)
最大延时
上升延时=5
下降延时=7
到不定态的延时=min(5,7)
关断延时=min(5,7)
and # (2:3:4,3:4:5,4:5:6) U3(out,i1,i2);
"delay"中包含了三个延迟值,第一个延迟值的最小值为2、典型值为3、最大值为4,第二个延迟值的最小值为3、典型值为4、最大值为5,第三个延迟值的最小值为4、典型值为5、最大值为6。元件实例U3的"上升延时值"由第一个延时值指定,"下降延时值"由第二个延时值指定,"截止延时值"由第三个延时值指定,而它的"到不定态的延时值"由三个延时值的最小值指定。各值的取值情况如下表。
最小延时
上升延时=2
下降延时=3
到不定态的延时=min(2,3,4)
关断延时=4
典型延时
上升延时=3
下降延时=4
到不定态的延时=min(3,4,5)
关断延时= 5
最大延时
上升延时=4
下降延时=5
到不定态的延时=min(4,5,6)
关断延时= 6
例:用Verilog建立模块D的延迟仿真模块 其门级实现如模块D的逻辑图如下,其中包含了延时时间为5个单位时间的与门和一个延时时间为4个单位时间的或门。 带有延迟的模块D代码:
module D(out,a,b,c);
output out;
input a,b,c;
wire e;
and #(5) a1(e,a,b);
or #(4) o1(out, e,c);
endmodule
带有延时的模块D的测试激励模块:
module D_tb;
reg A,B,C;
wire OUT;
D d1(.out(OUT), .a(A), .b(B), .c(C));
initial
begin
A=1'b0;B=1'b0;C=1'b0;
#10 A=1'b1;B=1'b1;C=1'b1;
#10 A=1'b1;B=1'b0;C=1'b0;
#20 $finish;
end
endmodule
8.2模块延时建模
1.延时说明块Specify Block 在模块输入和输出引脚之间的延迟称为模块路径延迟。在Verilog中,在关键字specify和endspecify之间给路径延迟赋值,关键字之间的语句组成specify块(即指定块)。"specify"与"endspeclfy"分别是延时说明块的起始标识符和终止标识符。
Specify块包含下列操作语句: (1)定义穿过模块的所有路径延迟 (2)在电路中设置时序检查 (2)定义specparam常量
例:上图为例,用specify块来描述途中M模块的路径延迟。代码如下:
module M(out,a,b,c,d);
input a,b,c,d;
output out;
wire e,f;
assign out=(a&b)|(c&b);//逻辑功能
specify //包含路径延迟语句的specify块
(a=>out)=9;
(b=>out)=9;
(c=>out)=11;
(d=>out)=11;
endspecify;
endmodule
2.径延迟描述方式 (1)并行连接 每条路径延迟语句都有一个源域或一个目标域。在上例的路径延迟语句中,a、b、c和d在源域位置,而out是目标域。 在specify块中,用符号"=> "说明并行连接,其语法格式如下: (=>)=;
其中可以包含1至3个延时量,也可以采用"最小、典型、最大"延时表达形式。在延时量由多个值组成的情况下,应在延时量的外面加上一对括号例如 (a=>out)=(8,9,10); 表示的是输入a到输出b的延迟最小、典型和最大延迟分别是8、9、10个时间单位。
在并行连接中,源域中的每一位与目标域中相应的位连接。如果源和目标域是向量,必须有相同的位数,否则会出现不匹配。因此,并行连接说明了源域的每一位到目标域的每一位之间的延迟。 下图显示了源域和目标域之间的位是如何并行连接的。同时例给出了并行连接的Verilog描述。
(2)全连接 在specify块中,用符号"*>"表示全连接,其语法格式如下: (*>)=;
在全连接中,源域中的每一位与目标域中的每一位相连接。如果源和目标是向量,则它们不必位数相同。全连接描述源中的每一位和目标中的每一位之间的延迟,如下图所示:
3.spacparam声明语句 spacparam用来定义specify块中的参数,如下例使用spacparam语句的specify块示例
module parallel_connected(out, a, b);
input a, b;
output out;
wire out;
assign out=a&b; //逻辑功能
//在指定块内部定义参数
specify
specparam a_to_out = 9;
specparam b_to_out = 11;
(a => out) = a_to_out;
(b => out) = b_to_out;
endspecify
endmodule
specparam语句的格式和作用都类似于parameter参数说明语句,但两者又有不同: (1)specparam语句只能在延时说明块(specify块)中出现;而parameter 语句则不能在延时说明块内出现。 (2)由specparam语句进行定义的参数只能是延时参数;而由parameter语句定义的参数可以是任何数据类型的常数参数。 (3)由specparam语句定义的延时参数只能在延时说明块内使用;而 parameter语句定义的参数则可以在模块内的任意位置处使用。 在模块中提供specify参数是为了方便给延迟赋值,建议用specify参数而不是数值来表示延迟。这样,如果电路的时序说明变化了,用户只需要改变 specify参数值,而不必逐个修改每条路径的延迟值。
8.3与时序检查相关的系统任务
9.编译预处理语句
编译预处理是Verilog编译系统的一个组成部分,指编译系统会对一些特殊命令进行预处理,然后将预处理结果和源程序一起再进行通常的编译处理。以"`"(反引号)开始的某些标识符是编译预处理语句。在verilog语言编译时,特定的编译器指令在整个编译过程中有效(编译过程可跨越多个文件),直到遇到其它的不同编译程序指令。常用的编译预处理语句如下:
9.1宏定义
`define指令是一个宏定义命令,通过一个指定的标识符来代表一个字符串,可以增加Verilog HDL代码的可读性和可维护性,找出参数或函数不正确或不允许的地方。 `define指令像C语言中的#define指令,可以在模块的内部或外部定义,编译器在编译过程中,遇到该语句将把宏文本替换为宏的名字。 `define的声明语法格式如下: `define 对于己声明的语法,在代码中的应用格式如下所示,不要漏掉宏名称前的"`"。 `macro_name 例如: `define MAX_BUS_SIZE 32 … reg [`MAX_BUS_SIZE-1:0] AddReg;
一旦`define指令被编译,其在整个编译过程中都有效。例如,通过另一个文件中的`define指令MAX_BUS_SIZE能被多个文件使用。 `undef指令取消前面定义的宏。例如: `define WORD 16 //建立一个文本宏替代 … wire[`WORD:1] Bus; … `undef WORD //在`undef编译指令后,WORD的宏定义不再有
关于宏定义指令,有下面8条规则需要注意。 (1)宏定义的名称可以是大写,也可以是小写,但要注意不要和变量名重复。 (2)和所有编译器伪指令一样,宏定义在超过单个文件边界时仍有效(对工程中的其它源文件),除非被后面的`define,`undef或`resetall伪指令覆盖,否则`define不受范围限制。 (3)当用变量定义宏时,变量可以在宏正文使用,并且在使用宏的时候可以用实际的变量表达式代替。 (4)通过用反斜杠"\"转义中间换行符,宏定义可以跨越几行,新的行是宏正文的一部分。 (5)宏定义行末不需要添加分号表示结束。 (6)宏正文不能分离的语言记号包括注释、数字、字符串、保留的关键字、运算符。 (7)编译器伪指令不允许作为宏的名字。 (8)宏定义的文本也可以是一个表达式。
`define和parameter是有区别的,`define和parameter都可以用于完成文本替换,但其存在本质上的不同,前者是编译之前就预处理,而后者是在正常编译过程中完成替换的。此外,`define和parameter存在下列两点不同之处。 (1)作用域不同:parameter作用于声明的那个文件;`define从编译器读到这条指令开始到编译结束都有效,或者遇到`undef命令使之失效,可以应用于整个工程。如果要让parameter作用于整个项目,可以将声明语句写于单独文件,并用`include让每个文件都包含声明文件。 `define也可以写在代码的任何位置,而parameter则必须在应用之前定义。通常编译器都可以定义编译顺序,或者从最底层模块开始编译,因此写在最底层就可以了。 (2)传递功能不同:parameter可以用作模块例化时的参数传递,实现参数化调用:`define语句则没有此作用。`define语句可以定义表达式,而parameter只能用于定义变量。
9.2文件包含处理
所谓“文件包含处理"是一个源文件可以将另外一个源文件的全部内容包含进来,即将另外的文件包含到本文件之中。Verilog语言提供了`include命令用来实现"文件包含"的操作。其一般形式为:include “文件名”。
9.3仿真时间标度
在这条命令中,时间单位参量是用来定义模块中仿真时间和延迟时间的基准单位的。时间精度参量是用来声明该模块的仿真时间的精确程度的,该参量被用来对延迟时间值进行取整操作(仿真前),因此该参量又可以被称为取整精度。如果在同一个程序设计里,存在多个`timescale命令,则用最小的时间精度值来决定仿真的时间单位。另外,时间精度至少要和时间单位一样精确,时间精度值不能大于时间单位值。 在`timescale命令中,用于说明时间单位和时间精度参量值的数字必须是整数,其有效数字为1、10、100,单位为秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(p司、亳皮秒〈fs)。下面举例说明`timescale命令的用法。 例仿真时间标度举例 (1)`timescale 1ns/1ps 模块中所有的时间值都表示是1ns的整数倍。这是因为在`timescale命令中,定义了时间单位是1ns。模因为`timescale命令定义时间精度为1ps,块中的延迟时间可表达为带三位小数的实型数。 (2)`timescale 10us/100ns 在这个例子中,模块中时间值均为10us的整数倍。因为`timesacle命令定义的时间单位是10us。延迟时间的最小分辨度为十分之一微秒(100ns),即延迟时间可表达为带一位小数的实型数。
9.4条件编译命令
9.5其他语句
10.Verilog测试方法简介
在集成电路测试领域,常用的测试方法有完全测试法、随机测试法和自动测试法。 (1)完全测试法对于复杂的设计来说,常常通过检查代码的覆盖率来检查验证工作是否完成的一种重要方法。代码覆盖率可以指示Verilog代码描述的功能有多少在仿真过程中被验证过。通常代码覆盖率包括以下内容: ·语句覆盖率 ·路径覆盖率 ·状态机覆盖率 ·触发覆盖率 ·表达式覆盖率 (2)随机测试法 在Verilog中提供了多个用于随机测试的系统命令,通常使用随机测试的系统函数来仿真真实应用的情况,如在通信领域中常用的帧同步搜索电路需要从接收的数据流中检测发送端固定插入的某个特殊的码型,而数据本身也有可能包括该码型,在这种情况下进行随机化测试就更接近于真实应用的情况。其中最常用的系统命令是随机数产生系统任务$random。 (3)自动测试法 在集成电路测试领域,全面准确的测试才能保证大规模集成电路的正常工作,而确保一个设计能够得到全面测试的唯一途径就是实现任务的自动化。通常通过创建一个检验表,使用相应个数的采样值。当修改过源代码后,所有的测试程序都自动被再次执行。但需要注意,使用自动测试可能会存在截断误差。
来源:蔡觉平老师的Verilog课程
你可能感兴趣的:(Verilog学习笔记,学习,fpga开发)
零基础怎么开始学网络安全(非常详细)零基础入门到精通,收藏这一篇就够了
程序员羊羊
web安全 安全 网络 php 学习
一、学习建议1.了解基础概念:开始之前,了解网络安全的基本概念和术语是很重要的。你可以查找网络安全入门教程或在线课程,了解网络安全领域的基本概念,如黑客、漏洞、攻击类型等。2.网络基础知识:学习计算机网络基础知识,了解网络通信原理,不同网络协议(如TCP/IP)的工作方式,以及网络拓扑结构等。3.操作系统知识:了解常见的操作系统,特别是Windows和Linux。掌握基本的命令行操作和系统管理技能
Linux篇1-初识Linux
逃跑的机械工
Linux linux
1.Linux能干什么Linux能够进行各种语言的开发工作,基本主要以后端语言为主C++,JAVA,python;Linux能进行各种指令操作,从而完成各种的文件相关的管理工作2.Linux基本指令2.1ls指令在Linux中,以.开头的文件,叫做隐藏文件;ls-a显示隐藏文件隐藏文件:Linux配置文件,可以隐藏起来,防止误操作,起到保护作用;ls-l列出文件的详细信息-d将目录象文件一样显示,
【C++篇】排队的艺术:用生活场景讲解优先级队列的实现
far away4002
C++ c++ stl 优先级队列 向下(向上)调整算法
文章目录须知欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步!深入理解与实现:C++优先级队列的模拟实现1.引言在算法和数据结构中
【C++篇】深入剖析C++ Vector底层源码及实现机制
far away4002
C++ c++ 开发语言 vector visual studio vscode
文章目录须知欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步!全面剖析vector底层及实现机制接上篇:【C++篇】探索STL之美
实战LLM强化学习——使用GRPO(DeepSeek R1出圈算法)
大富大贵7
程序员知识储备1 程序员知识储备2 程序员知识储备3 经验分享
引言近年来,深度强化学习(DRL)已经成为解决复杂决策问题的一个强有力工具,尤其是在自然语言处理(NLP)领域的广泛应用。通过不断优化决策策略,DRL能在大量数据中学习最佳行为,尤其是大型语言模型(LLM)在任务中展现出的巨大潜力。然而,随着模型规模的扩大和任务复杂性的增加,传统的强化学习算法开始暴露出训练效率低、收敛速度慢等问题。为了解决这些挑战,DeepSeek公司提出了一个新的强化学习算法—
人生建议往死里学网络安全!零基础也能跨行学习!!漏洞挖掘还能做副业
黑客老哥
web安全 学习 安全 网络 系统安全
一、网络安全的重要性:从‘不学会被黑’到‘学会保护别人’网络安全的概念现在不再是技术圈的独立话题,它已经渗透到社会的各个领域。从个人的隐私保护、企业的数据安全,到国家的信息防护,网络安全几乎影响了每一个人的生活。无论是黑客攻击、勒索病毒、数据泄露,还是国家间的信息战,网络安全已经成为现代社会的基础设施之一。所以,首先要明白学习网络安全的重要性:你不仅是在学习技术,更多的是在为自己和他人的安全“筑城
AI学习指南RAG篇(24)-RAGFlow的社区与开源贡献
俞兆鹏
AI学习指南 人工智能
一、引言RAGFlow是一款基于深度文档理解的开源RAG(Retrieval-AugmentedGeneration,检索增强生成)引擎,旨在解决现有RAG技术在数据处理和生成答案方面的挑战。RAGFlow通过结合大型语言模型(LLMs)的强大生成能力和高效的信息检索系统,为用户提供了一种全新的交互体验。本文将鼓励读者参与到RAGFlow的开源社区中,共同推动技术的发展和创新。二、RAGFlow的
10 分钟学会SpringValidation数据校验和全局异常处理
ohn.yu
spring spring boot java
以下是一个使用Spring开发的简单RESTAPI小程序,通过对一张user表进行操作,代码演示如何RestAPI开发中实现数据校验、全局异常处理和返回Json格式数据。使用的核心框架包括SpringBootSpringWebSpringDataJPABeanValidation(JSR-303)Lombok1.项目依赖(pom.xml)创建一个Maven项目,添加以下依赖:"xmlns:xsi=
【Vue+TypeScript实战指南:提高代码质量和开发效率】
小怪兽9699
typescript vue.js 前端
前言在现代前端开发中,Vue.js是一个非常流行且强大的框架,而TypeScript则是增强代码类型安全性和可维护性的利器。本文将详细介绍如何结合Vue和TypeScript来构建高质量的应用程序。无论你是有一定基础的开发者还是希望进一步提升技能的高手,本文都将为你提供详细的步骤和代码示例。1.环境搭建首先,确保你已经安装了Node.js和npm。然后,全局安装VueCLI:npminstall-
若依框架入门指南:快速上手SpringBoot+前后端分离版
小小鸭程序员
spring java spring boot 后端 intellij-idea
若依(RuoYi)是一款基于SpringBoot的快速开发平台,集成了权限管理、代码生成、监控管理等功能。本文将以SpringBoot+Vue前后端分离版本为例,带你快速上手若依框架。一、环境准备基础环境:JDK1.8+MySQL5.7+Redis5.0+Maven3.6+Node.js14+(前端)下载项目:#后端项目gitclonehttps://gitee.com/y_project/Ruo
Deno入门教程:Node.js 的替代品
xiaoweids
编程语言 JavaScript node.js javascript 开发语言
转自:微点阅读https://www.weidianyuedu.com这几天假期,我学习了一下Deno[1]。它是Node.js的替代品。有了它,将来可能就不需要Node.js了。这篇文章就是Deno的一个初步介绍,尝试回答为什么Node.js不能满足需要,以及Deno能够带给我们什么?以下内容主要基于BertBelder[2]和RyanDahl[3]的最新演讲。0、进入主题之前,先说一下Deno
进入Tokio的异步世界
lipicoder
rust 开发语言 后端
Tokio是一个基于Rust语言开发的异步运行时。初接触的开发者可能会存在两个疑问,为什么要异步,什么要基于Rust来做异步?简单的说,异步更符合计算机的运行机制,能够更大的发挥计算能力。当然,这个是针对IO密集型的任务。如果是CPU密集型的,长耗时的纯计算,那还是同步机制好从通常的场景来看,大部分的应用都是IO密集型的。长耗时的纯CPU计算只需要写一个脚本跑就可以了,比较简单为什么采用Rust来
在Ubuntu上安装MEAN Stack的4个步骤
ubuntu
在Ubuntu上安装MEANStack的4个步骤为:1.安装MEAN;2.安装MongoDB;3.安装NodeJS,Git和NPM;4.安装剩余的依赖项。什么是MEANStack?平均堆栈一直在很大程度上升高为基于稳健的基于JavaScript的开发堆栈。名称的意思是指其组件;MongoDB,ExpressJS,Angularjs和NodeJS。第1步:安装MEAN对于此安装,我们将在本指南中使用
Vue.js的watch监听
阿珊和她的猫
vue.js 前端 javascript
前端开发工程师、技术日更博主、已过CET6阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1牛客高级专题作者、打造专栏《前端面试必备》、《2024面试高频手撕题》、《前端求职突破计划》蓝桥云课签约作者、上架课程《Vue.js和Egg.js开发企业级健康管理项目》、《带你从入门到实战全面掌握uni-app》文章目录引言`watch`选项的基本概念`watch`选项的基本语法`watch
黑客最恨的安全武器!开发者必知的代码签名证书终极指南
ssl证书数字证书
为什么黑客害怕代码签名证书?当用户下载未签名的软件时,系统会弹出“未知发布者”红色警告——这正是黑客的突破口。超过62%的用户会因此放弃安装,而剩下的用户可能因忽略警告而中招。代码签名证书通过加密技术为软件赋予“数字身份证”,让用户瞬间识别开发者身份,直接切断黑客伪装合法软件的传播链。这是开发者对抗黑产的终极防线。代码签名证书申请流程↓1、快速申请入口2、直接访问JoySSL官方网站,注册一个新账
基于图像比对的跨平台UI一致性校验工具开发全流程指南——Android/iOS/Web三端自动化测试实战
追寻向上
ui android ios
一、需求背景与方案概述1.1为什么需要跨平台UI校验?在移动互联网时代,同一产品需覆盖Android、iOS和Web三端。由于不同平台的开发框架(如Android的MaterialDesign与iOS的Cupertino风格)及渲染引擎差异,UI界面易出现以下问题:布局错位:按钮位置偏移、文本换行不一致视觉差异:颜色色差、字体粗细不同交互逻辑冲突:滑动方向、弹窗动画不一致传统人工测试效率低且易遗漏
移动端IOS的H5页面被键盘顶起后,底部有一大片空白区域的解决方法
不怕麻烦的鹿丸
浏览器 HTML5 JavaScript 前端 html5 javascript
在移动端开发中,当使用HTML5(特别是在Vue.js框架下)构建应用时,经常会遇到键盘弹出导致页面内容被顶起的问题。当键盘收起后,页面未能自动恢复到原来的位置。当键盘弹出时,你可以通过JavaScript监听键盘的显示和隐藏事件,并相应地调整页面的滚动位置。exportdefault{mounted(){window.addEventListener('focusin',this.handleF
设计模式-单一职责原则
qq_26920109
java java 设计模式 深圳
单一职责原则(SRP:Singleresponsibilityprinciple)又称单一功能原则,原话解释是:thereshouldneverbemorethanareasonofaclasstochange,也就是引起类的变化原因不能超过一个,面向对象五个基本原则(SOLID)之一。该原则由罗伯特·C·马丁(RobertC.Martin)于《敏捷软件开发:原则、模式和实践》一书中给出的。马丁表
QR二维码开发实战:生成、管理与扫描的最佳实践
34号树洞
javascript 二维码开发 Python Javascript URL QRCode
目录一、QR二维码是什么?1.QR二维码的基础知识2.QR二维码的生成3.QR二维码的应用场景4.QR二维码的管理二、开发QR二维码1.生成二维码(支持移动端+网页)2.生成“活码”(可修改目标URL的二维码)3.扫描二维码4.嵌入二维码功能到App5.高级功能6.推荐技术栈7.开发注意事项一、QR二维码是什么?1.QR二维码的基础知识QR码结构:了解QR码的组成部分,如定位图案、校正图案、数据区
【unity&Node.js篇】多人联机游戏开发代码规范
雅鸦
unity node.js 代码规范
多人联机游戏前端(Unity)与后端(Node.js)代码规范说明书这份代码规范旨在帮助多人联机游戏的开发团队建立一致性和高质量的代码标准,涵盖前端(Unity)和后端(Node.js)开发部分。无论是游戏逻辑的实现、多人同步机制、网络通信还是错误处理,都需要清晰的规范来确保代码的可维护性、可扩展性与高效性。1.Unity前端代码规范1.1命名规范变量、函数命名:使用PascalCase(大驼峰)
Rust为Node.js开发者设计:入门到实战
平依佩Ula
Rust为Node.js开发者设计:入门到实战rust-for-node-developersAnintroductiontotheRustprogramminglanguageforNodedevelopers.项目地址:https://gitcode.com/gh_mirrors/ru/rust-for-node-developers项目介绍欢迎来到《Rust为Node.js开发者设计》的实践
如何用AI轻松制作完美PPT,节省时间又提升效率
HUIPPT剑盾ai
人工智能 powerpoint 软件 aigc 科技
如何用AI轻松制作完美PPT,节省时间又提升效率!在这个快节奏的时代,做PPT成了大家工作和学习的必备技能。无论是准备一场重要的商务演讲,还是一份课题报告,PPT的质量往往决定了成败。然而,传统制作PPT的方式费时又费力,如何快速、高效地制作出吸引人的PPT呢?别担心,AI的出现为我们提供了全新的解决方案!AI制作PPT,让你摆脱繁琐的排版如今你只需提供一些基础的内容,剩下的工作交给AI。AI制作
Node.js系列(5)--数据库操作指南
一进制ᅟᅠ
Node.js node.js 数据库
Node.js数据库操作指南引言数据库操作是Node.js应用开发中的关键环节。本文将深入探讨Node.js数据库操作的实现方案,包括连接管理、查询优化、事务处理等方面,帮助开发者构建高效可靠的数据访问层。数据库操作概述Node.js数据库操作主要包括以下方面:连接管理:连接池、故障恢复、负载均衡查询处理:SQL构建、参数绑定、结果映射事务管理:事务控制、隔离级别、一致性保证性能优化:查询优化、缓
多学科视角下探索开源&Github、Git初步学习
Bulestar_xx
开源 github git
Think1.Github作为现今最主流的代码托管平台、协作平台甚至是“社交平台”,本身是闭源的。一方面,它是和大多数开发者连接最紧密的开源阵地,另一方面,拥有传统“黑客精神”的人认为将用户身份绑定这样一个闭源平台上恰恰与开源背道而驰。请从早期自由软件运动与现代开源模式变迁的视角,谈一谈你对上述两种认知的理解。2.在拓展阅读《开放式协作》第二章中国根据用户增长和贡献者增长将所有项目分为了四种类型,
【ES6】05-Promise + Fetch + Axios + 模块化 + 同步异步
beibeibeiooo
ES6【已完结】 es6 javascript 前端
本文介绍Promise+Fetch+Axios+模块化+同步异步目录1.Promise1.1Promise简介1.2resovle1.3reject&finally2.Fetch2.1get请求2.2post请求3.Axios4.模块化开发5.同步异步同步异步asyncawait1.Promise1.1Promise简介Promise是一个对象,表示承诺在未来的某个时刻可能会完成并返回结果对于某些
31天Python入门——第7天:集合·字典你真的懂了吗?
安然无虞
Python手把手教程 python 开发语言 后端
你好,我是安然无虞。文章目录1.集合1.1集合的定义1.2集合的常用操作1.3集合练习2.字典2.1字典的定义2.2嵌套字典和字典的取值2.3字典的常用操作补充知识:字典的优势是查找值效率高2.4字典推导式2.5字典练习很重要的补充练习:希望你能掌握练习一练习二1.集合在之前的章节中,我们学习了列表,元组,字符串.已经可以覆盖七成的使用场景了.那么为什么还要学习集合类型呢.列表:有序可变,元素可重
电子工程师转战汽车OEM主机厂之路
上层精灵的赞美诗
行业杂谈 汽车 单片机 嵌入式硬件 eclipse mcu
文章目录1电子工程师2汽车系统工程师第一篇分享一个笔者2018年的一个心得文章,回头想想从事汽车行业也小8年了,从懵懂稚嫩到所谓的老油条,也是难忘的经历,希望我的经历对从事电子行业和汽车行业的小伙伴有所帮助。1电子工程师2013年电气工程及其自动化专业毕业,由于家里条件的原因,我不能选择继续读研深造,所以本科毕业必须出来工作,由于本科生的就业压力也是非常大的,所以当时想,在大学的时候要学习一些真正
技术债务的隐患:何时重构,何时妥协?
测试者家园
测试开发和测试 质量效能 软技能 软件测试 质量效能 AI赋能 人工智能 项目管理 研发管理 技术债务
在快节奏的软件开发环境中,企业为了抢占市场或满足紧迫需求,往往不得不在短期内采取“捷径”来加速产品交付,这便引入了“技术债务”。短期内看似能迅速交付,但随着时间推移,这些未优化的代码和架构缺陷会逐渐累积,成为制约团队敏捷性、影响系统稳定性和增加后期维护成本的隐患。如何在“重构”和“妥协”之间找到平衡,是每个技术团队必须面对的难题。一、技术债务概述1.定义与来源技术债务(TechnicalDebt)
基于 Docker 和 Flask 构建高并发微服务架构
TechStack 创行者
# 服务器容器 Linux 架构 docker flask 容器 微服务
基于Docker和Flask构建高并发微服务架构一、微服务架构概述(一)微服务架构的优点微服务架构是一种将应用程序拆分为多个小型、自治服务的架构风格,在当今的软件开发领域具有显著的优势。高度可扩展性:每个微服务可以独立进行扩展。例如,在电商系统中,订单服务在促销活动期间可能会面临高并发的订单处理需求,此时可以仅对订单服务进行横向扩展,增加服务实例数量,而无需对整个系统进行大规模的扩容,从而提高资源
UI自动化测试与性能测试
scratchpads
ui 鸿蒙 harmonyos
在HarmonyOSNEXT应用的开发过程中,除了单元测试和集成测试外,UI自动化测试和性能测试也是不可或缺的环节。UI自动化测试用于确保应用的用户界面能够正确响应用户操作并提供预期的交互体验,而性能测试则评估应用在不同负载条件下的表现,确保其具备良好的响应速度和稳定性。本文将重点讨论如何在鸿蒙操作系统中进行UI自动化测试和性能测试,帮助开发人员提升应用质量。一、UI自动化测试UI自动化测试(UI
sql统计相同项个数并按名次显示
朱辉辉33
java oracle
现在有如下这样一个表:
A表
ID Name time
------------------------------
0001 aaa 2006-11-18
0002 ccc 2006-11-18
0003 eee 2006-11-18
0004 aaa 2006-11-18
0005 eee 2006-11-18
0004 aaa 2006-11-18
0002 ccc 20
Android+Jquery Mobile学习系列-目录
白糖_
JQuery Mobile
最近在研究学习基于Android的移动应用开发,准备给家里人做一个应用程序用用。向公司手机移动团队咨询了下,觉得使用Android的WebView上手最快,因为WebView等于是一个内置浏览器,可以基于html页面开发,不用去学习Android自带的七七八八的控件。然后加上Jquery mobile的样式渲染和事件等,就能非常方便的做动态应用了。
从现在起,往后一段时间,我打算
如何给线程池命名
daysinsun
线程池
在系统运行后,在线程快照里总是看到线程池的名字为pool-xx,这样导致很不好定位,怎么给线程池一个有意义的名字呢。参照ThreadPoolExecutor类的ThreadFactory,自己实现ThreadFactory接口,重写newThread方法即可。参考代码如下:
public class Named
IE 中"HTML Parsing Error:Unable to modify the parent container element before the
周凡杨
html 解析 error readyState
错误: IE 中"HTML Parsing Error:Unable to modify the parent container element before the child element is closed"
现象: 同事之间几个IE 测试情况下,有的报这个错,有的不报。经查询资料后,可归纳以下原因。
java上传
g21121
java
我们在做web项目中通常会遇到上传文件的情况,用struts等框架的会直接用的自带的标签和组件,今天说的是利用servlet来完成上传。
我们这里利用到commons-fileupload组件,相关jar包可以取apache官网下载:http://commons.apache.org/
下面是servlet的代码:
//定义一个磁盘文件工厂
DiskFileItemFactory fact
SpringMVC配置学习
510888780
spring mvc
spring MVC配置详解
现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了。不过要想灵活运用Spring MVC来应对大多数的Web开发,就必须要掌握它的配置及原理。
一、Spring MVC环境搭建:(Spring 2.5.6 + Hi
spring mvc-jfreeChart 柱图(1)
布衣凌宇
jfreechart
第一步:下载jfreeChart包,注意是jfreeChart文件lib目录下的,jcommon-1.0.23.jar和jfreechart-1.0.19.jar两个包即可;
第二步:配置web.xml;
web.xml代码如下
<servlet>
<servlet-name>jfreechart</servlet-nam
我的spring学习笔记13-容器扩展点之PropertyPlaceholderConfigurer
aijuans
Spring3
PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。关于BeanFactoryPostProcessor和BeanPostProcessor类似。我会在其他地方介绍。PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java P
java 线程池使用 Runnable&Callable&Future
antlove
java thread Runnable callable future
1. 创建线程池
ExecutorService executorService = Executors.newCachedThreadPool();
2. 执行一次线程,调用Runnable接口实现
Future<?> future = executorService.submit(new DefaultRunnable());
System.out.prin
XML语法元素结构的总结
百合不是茶
xml 树结构
1.XML介绍1969年 gml (主要目的是要在不同的机器进行通信的数据规范)1985年 sgml standard generralized markup language1993年 html(www网)1998年 xml extensible markup language
改变eclipse编码格式
bijian1013
eclipse 编码格式
1.改变整个工作空间的编码格式
改变整个工作空间的编码格式,这样以后新建的文件也是新设置的编码格式。
Eclipse->window->preferences->General->workspace-
javascript中return的设计缺陷
bijian1013
JavaScript AngularJS
代码1:
<script>
var gisService = (function(window)
{
return
{
name:function ()
{
alert(1);
}
};
})(this);
gisService.name();
&l
【持久化框架MyBatis3八】Spring集成MyBatis3
bit1129
Mybatis3
pom.xml配置
Maven的pom中主要包括:
MyBatis
MyBatis-Spring
Spring
MySQL-Connector-Java
Druid
applicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?>
&
java web项目启动时自动加载自定义properties文件
bitray
java Web 监听器 相对路径
创建一个类
public class ContextInitListener implements ServletContextListener
使得该类成为一个监听器。用于监听整个容器生命周期的,主要是初始化和销毁的。
类创建后要在web.xml配置文件中增加一个简单的监听器配置,即刚才我们定义的类。
<listener>
<des
用nginx区分文件大小做出不同响应
ronin47
昨晚和前21v的同事聊天,说到我离职后一些技术上的更新。其中有个给某大客户(游戏下载类)的特殊需求设计,因为文件大小差距很大——估计是大版本和补丁的区别——又走的是同一个域名,而squid在响应比较大的文件时,尤其是初次下载的时候,性能比较差,所以拆成两组服务器,squid服务于较小的文件,通过pull方式从peer层获取,nginx服务于较大的文件,通过push方式由peer层分发同步。外部发布
java-67-扑克牌的顺子.从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的.2-10为数字本身,A为1,J为11,Q为12,K为13,而大
bylijinnan
java
package com.ljn.base;
import java.util.Arrays;
import java.util.Random;
public class ContinuousPoker {
/**
* Q67 扑克牌的顺子 从扑克牌中随机抽5张牌,判断是不是一个顺子,即这5张牌是不是连续的。
* 2-10为数字本身,A为1,J为1
翟鸿燊老师语录
ccii
翟鸿燊
一、国学应用智慧TAT之亮剑精神A
1. 角色就是人格
就像你一回家的时候,你一进屋里面,你已经是儿子,是姑娘啦,给老爸老妈倒怀水吧,你还觉得你是老总呢?还拿派呢?就像今天一样,你们往这儿一坐,你们之间是什么,同学,是朋友。
还有下属最忌讳的就是领导向他询问情况的时候,什么我不知道,我不清楚,该你知道的你凭什么不知道
[光速与宇宙]进行光速飞行的一些问题
comsci
问题
在人类整体进入宇宙时代,即将开展深空宇宙探索之前,我有几个猜想想告诉大家
仅仅是猜想。。。未经官方证实
1:要在宇宙中进行光速飞行,必须首先获得宇宙中的航行通行证,而这个航行通行证并不是我们平常认为的那种带钢印的证书,是什么呢? 下面我来告诉
oracle undo解析
cwqcwqmax9
oracle
oracle undo解析2012-09-24 09:02:01 我来说两句 作者:虫师收藏 我要投稿
Undo是干嘛用的? &nb
java中各种集合的详细介绍
dashuaifu
java 集合
一,java中各种集合的关系图 Collection 接口的接口 对象的集合 ├ List 子接口 &n
卸载windows服务的方法
dcj3sjt126com
windows service
卸载Windows服务的方法
在Windows中,有一类程序称为服务,在操作系统内核加载完成后就开始加载。这里程序往往运行在操作系统的底层,因此资源占用比较大、执行效率比较高,比较有代表性的就是杀毒软件。但是一旦因为特殊原因不能正确卸载这些程序了,其加载在Windows内的服务就不容易删除了。即便是删除注册表中的相 应项目,虽然不启动了,但是系统中仍然存在此项服务,只是没有加载而已。如果安装其他
Warning: The Copy Bundle Resources build phase contains this target's Info.plist
dcj3sjt126com
ios xcode
http://developer.apple.com/iphone/library/qa/qa2009/qa1649.html
Excerpt:
You are getting this warning because you probably added your Info.plist file to your Copy Bundle
2014之C++学习笔记(一)
Etwo
C++ Etwo Etwo iterator 迭代器
已经有很长一段时间没有写博客了,可能大家已经淡忘了Etwo这个人的存在,这一年多以来,本人从事了AS的相关开发工作,但最近一段时间,AS在天朝的没落,相信有很多码农也都清楚,现在的页游基本上达到饱和,手机上的游戏基本被unity3D与cocos占据,AS基本没有容身之处。so。。。最近我并不打算直接转型
js跨越获取数据问题记录
haifengwuch
jsonp json Ajax
js的跨越问题,普通的ajax无法获取服务器返回的值。
第一种解决方案,通过getson,后台配合方式,实现。
Java后台代码:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String ca
蓝色jQuery导航条
ini
JavaScript html jquery Web html5
效果体验:http://keleyi.com/keleyi/phtml/jqtexiao/39.htmHTML文件代码:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>jQuery鼠标悬停上下滑动导航条 - 柯乐义<
linux部署jdk,tomcat,mysql
kerryg
jdk tomcat linux mysql
1、安装java环境jdk:
一般系统都会默认自带的JDK,但是不太好用,都会卸载了,然后重新安装。
1.1)、卸载:
(rpm -qa :查询已经安装哪些软件包;
rmp -q 软件包:查询指定包是否已
DOMContentLoaded VS onload VS onreadystatechange
mutongwu
jquery js
1. DOMContentLoaded 在页面html、script、style加载完毕即可触发,无需等待所有资源(image/iframe)加载完毕。(IE9+)
2. onload是最早支持的事件,要求所有资源加载完毕触发。
3. onreadystatechange 开始在IE引入,后来其它浏览器也有一定的实现。涉及以下 document , applet, embed, fra
sql批量插入数据
qifeifei
批量插入
hi,
自己在做工程的时候,遇到批量插入数据的数据修复场景。我的思路是在插入前准备一个临时表,临时表的整理就看当时的选择条件了,临时表就是要插入的数据集,最后再批量插入到数据库中。
WITH tempT AS (
SELECT
item_id AS combo_id,
item_id,
now() AS create_date
FROM
a
log4j打印日志文件 如何实现相对路径到 项目工程下
thinkfreer
Web log4j 应用服务器 日志
最近为了实现统计一个网站的访问量,记录用户的登录信息,以方便站长实时了解自己网站的访问情况,选择了Apache 的log4j,但是在选择相对路径那块 卡主了,X度了好多方法(其实大多都是一样的内用,还一个字都不差的),都没有能解决问题,无奈搞了2天终于解决了,与大家分享一下
需求:
用户登录该网站时,把用户的登录名,ip,时间。统计到一个txt文档里,以方便其他系统调用此txt。项目名
linux下mysql-5.6.23.tar.gz安装与配置
笑我痴狂
mysql linux unix
1.卸载系统默认的mysql
[root@localhost ~]# rpm -qa | grep mysql
mysql-libs-5.1.66-2.el6_3.x86_64
mysql-devel-5.1.66-2.el6_3.x86_64
mysql-5.1.66-2.el6_3.x86_64
[root@localhost ~]# rpm -e mysql-libs-5.1