文章目录
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开发)
【Python】一文详细介绍 py格式 文件
高斯小哥
Python基础【高质量合集】 python 新手入门 学习
【Python】一文详细介绍py格式文件个人主页:高斯小哥高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程希望得到您的订阅和支持~创作高质量博文(平均质量分92+),分享更多关于深度学习、PyTorch、Python领域的优质内容!(希望得到您的关注~)文章目录一、py格式文件简介二、如何创建和编辑py格式文件三、如何运行py
大学播音主持都学什么内容?播音主持专业学什么?
配音新手圈
有些喜欢播音主持并且犹豫要不要报考这个大学专业的小伙伴们就会想要了解大学播音主持都学什么内容吧,毕竟如果不够了解就直接选择这个专业真的等选择完进去学习以后才知道这个专业并不是自己想要学习的东西那就来不及了。下面是小编为大家整理出来的一些播音主持专业学习的内容,请往下看吧。大学播音主持专业主要学习的课程有:播音发声、播音创作基础、广播播音主持、电视播音主持、文艺作品演播学概论、新闻学概论、新闻采编、
Android和IOS应用开发-Flutter应用让屏幕在 app 运行期间保持常亮的方法
江上清风山间明月
Flutter android ios flutter KeepAlive 屏幕常亮 wakelock 熄屏
文章目录Flutter应用让屏幕在app运行期间保持常亮的方法方法一:使用系统插件方法二:使用Widgets注意事项Flutter应用让屏幕在app运行期间保持常亮的方法在Flutter开发中,可以使用以下两种方法让屏幕在app运行期间保持常亮:方法一:使用系统插件Flutter社区中已经有很多相关插件可供使用,比如wakelock:https://pub.dev/packages/wakeloc
新网师的精神肤色(幕布笔记)
悦读书香
王子老师的《极简100小妙招》收到已经几天了,之前大概的浏览了全书,今天起给自己定了一个计划,必须每天学习极简小妙招里面的一个妙招,并加以运用。一、今天要打卡什么内容因有完成每天学习极简小妙招的计划,所以今天晚饭吃的比较简单,草草吃完以后带着小宝到广场溜达一圈,急忙赶回来学习极简小妙招。再重看的时候不知道自己要学点什么,打卡哪一招,感觉哪个都简单,就看这一环节像王子老师说的“一看就会”,但做这一环
学习JavaEE的日子 Day32 线程池
A 北枝
学习JavaEE 学习 java-ee java 线程池
Day32线程池1.引入一个线程完成一项任务所需时间为:创建线程时间-Time1线程中执行任务的时间-Time2销毁线程时间-Time32.为什么需要线程池(重要)线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time1,T3分别安排在项目的启动和结束的时间段或者一些空闲的时间段线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建
以前开发MFC界面如何快速转成QT界面
广州视觉芯软件有限公司
mfc qt c++
将MFC界面快速转换为Qt界面可能需要进行一些手动工作,因为MFC和Qt是两个不同的界面框架,它们具有不同的设计和实现原理。但是,以下步骤可以帮助你快速进行转换:创建一个新的Qt项目:使用QtCreator创建一个新的Qt项目。分析MFC界面:仔细分析你的MFC界面,包括窗口、对话框、控件等的布局、样式和行为。重新设计界面:使用Qt的可视化设计器重新设计界面。在QtCreator的设计器中,你可以
没有如释重负
君远近
虽然只有短短的一个多月的努力复习时间,但今天的整个考试经过,还是发现了效果的,题目做的比较自如,没有慌里慌张,而且提前五分钟完成。至于考试成绩,没有实足的把握,60分都不敢保证。但绝对相信自己,比去年肯定要好!今天早早的赶到考场,见到了刘老师,谈起来学习情况,坦率的说,真的是自己不够重视。总以为会很难,没有信心。其实不是的,只要认真对待,树立足够的信心,绝对可以通过考试的。还向老师询问了,后续再报
C++学习笔记(lambda函数)
__TAT__
C&C++ c++ 学习 笔记
C++learningnote1、lambda函数的语法2、lambda函数的几种用法1、lambda函数的语法lambda函数的一般语法如下:[capture_clause](parameters)->return_type{function_body}capture_clause:需要捕获的变量,但要求该变量必须在这个作用域中。通常的捕获方式有以下几种:[]:不捕获任何变量[&]:按引用捕获变
心赏(2018.10.8)
六一节_3928
1.上班第一天,同事彤休完产假,回来上班,给我带了酸奶和水果。她生小孩时,我给她发了一个小红包贺喜,哪知她就记在心里了。心赏这个有心的90后。2.女儿放学回来,说自己当了小组长。一边说不想当,一边得意的样子。心赏老师给了孩子这个锻炼的机会。3.老妈今天做了"蚂蚁上树"的菜,得到女儿的高度肯定。心赏老妈还在不断学习。
springboot集成logback-spring.xml文件
RT_0114
SpringBoot spring boot spring logback
彩色日志日志分debug和error文件输出,方便开发人员运维日志限制最大保管天数日志限制总量大小占用量GB日志限制单个文件大小MB日志显示最大保留天数屏蔽没用的日志${CONSOLE_LOG_PATTERN}${log.path}/debug.log${log.path}/%d{yyyy-MM-dd,aux}/debug.%d{yyyy-MM-dd}.%i.log.gz1024MB50GB365
2022-2-13晨间日记
越亮也打烊
今天是什么日子起床:7:00就寝:12:08天气:晴心情:糟糕纪念日:无任务清单昨日完成的任务,最重要的三件事:寒假作业,网课,画画改进:作业时间剪短习惯养成:网课不逃~周目标·完成进度数学卷子100%学习·信息·阅读《傅雷家书》《钢铁是怎样炼成的》健康·饮食·锻炼我终于不喝饮料啦,喝茶~人际·家人·朋友邝姐姐带我吃火锅工作·思考啥时候开学,我还有几天赶完作业最美好的三件事1.卷子写完了2.我有冰
中原焦点团队38期王芳芳坚持分享第236天,20230630总约练134次,来访113次,咨8次,观察员13次
芳芳王
学习焦点的初心是想拯救孩子,孩子由于沉迷游戏,成绩下滑,在学习的过程中发现是自己的教育方式出了状况。经过半年的学习,一些焦点的基本技巧,如接纳、欣赏、倾听、同理心、尊重等都有了一定的了解。但在实际应用时仍然存在很多问题,感觉自己仍然没有放下对孩子成绩的期望,仍然把握不住对孩子管理的度。我该如何去陪伴好孩子?多用心去听课,并加强反思,多约练。去思考如何让自己快乐起来?
请简单介绍一下Shiro框架是什么?Shiro在Java安全领域的主要作用是什么?Shiro主要提供了哪些安全功能?
AaronWang94
shiro java java 安全 开发语言
请简单介绍一下Shiro框架是什么?Shiro框架是一个强大且灵活的开源安全框架,为Java应用程序提供了全面的安全解决方案。它主要用于身份验证、授权、加密和会话管理等功能,可以轻松地集成到任何JavaWeb应用程序中,并提供了易于理解和使用的API,使开发人员能够快速实现安全特性。Shiro的核心组件包括Subject、SecurityManager和Realms。Subject代表了当前与应用
【转载】SSD测试第一神器——FIO
running_sheep
转自:[http://www.ssdfans.com]对于SSD性能测试来说,最好的工具莫过于FIO了。FIO是Jens开发的一个开源测试工具,功能非常强大,本文就只介绍其中一些基本功能。线程,队列深度,Offset,同步异步,DirectIO,BIO使用FIO之前,首先要有一些SSD性能测试的基础知识。线程指的是同时有多少个读或写任务在并行执行,一般来说,CPU里面的一个核心同一时间只能运行一个
虚拟 DOM 的优缺点有哪些
咕噜签名分发
前端 javascript 开发语言
虚拟DOM(VirtualDOM)技术作为现代前端开发中的重要组成部分,已经成为了众多流行前端框架的核心特性。它的引入为前端开发带来了诸多优势,同时也需要我们认真思考其潜在的考量。下面简单的介绍一下虚拟DOM技术的优势与缺点,深入探讨其在实际应用中的影响。提升性能虚拟DOM的最大优势之一是提升页面性能。通过比较前后两次虚拟DOM树的差异,最小化实际DOM操作,从而减少页面重渲染时的性能消耗。这种优
大创项目推荐 深度学习 opencv python 公式识别(图像识别 机器视觉)
laafeer
python
文章目录0前言1课题说明2效果展示3具体实现4关键代码实现5算法综合效果6最后0前言优质竞赛项目系列,今天要分享的是基于深度学习的数学公式识别算法实现该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!学长这里给一个题目综合评分(每项满分5分)难度系数:3分工作量:4分创新点:4分更多资料,项目分享:https://gitee.com/dancheng-senior/postgraduate1课题
#D174-读书会作业-《财务自由之路》3
白洲笔记
最近沉迷于写作营,一直就没时间去弄读书会的作业,书的第二遍也就看了个开头,趁着日更的时间,赶紧把作业做了,这次是15到21课。【1.印象最深刻的部分】(本周所读内容中印象最深刻的部分)*活在未来,最正确的方法是什么?用正确的方法做正确的事情,判断什么是正确的?逻辑。学会思考。"作对事情"永远比“把事情作对“重要的多。”长远思考,耐心验证,小心总结提炼“证明自己正确并不是学习的任务和目标,时刻成长,
docker基础(一)
运维搬运工
容器-docker docker 容器 运维
相关概念介绍Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖到一个可移植的容器中,然后发布到任何流行的linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,互相之间不会有任何接口。Docker有几个重要概念:dockerfile,配置文件,用来生成dockerimagedockerimage,交付部署的最小单元docker命令与API,定义命令与接口,支持第三方系统集
Flutter运行flutter doctor 命令长时间未响应如何解决
咕噜签名分发-淼淼
flutter
Hello大家好!我是咕噜铁蛋!在移动应用开发领域,Flutter以其高效、跨平台的特性吸引了众多开发者的关注。然而,在使用Flutter进行项目开发时,开发者可能会遇到各种问题,其中之一就是运行flutterdoctor命令时长时间未响应。今天铁蛋将深入探讨这一问题的成因、解决方案以及相关的Flutter环境配置知识。一、Flutter与flutterdoctor命令简介Flutter是Goog
账务处理又出错?资深会计来教你,学会效率翻倍!共同学习
小橘子要努力吖
作为一名会计,在实际工作中会遇到各种麻烦的账务处理问题。那么,最常用的会计处理方法都有哪些呢?今天小编为大家带来了从业二十六年的资深老会计分享的十四中会计常用的账务处理问题的解决方案,快来看看吧!一、促销品的账务处理在促销时公司经常会把一些商品按进价赠送给消费者使用二、款已付清但发票未到的账务处理三、购买材料发生不合理损耗的账务处理问题公司在购买材料时,常常会发生一些不合理的损耗,那么这种问题该怎
【真诚子】通晓鬼谷第七篇读书日记。
真诚子l通晓鬼谷
今天把个人品牌,从193读到208页,书的内容质量出奇的高,尤其是这一段。对标学习法,找一个比自己强,或者你期望成为的人进行模仿性学习,对标学习,不是到处,去找人对标兵学习很多人的优点,或是学习自己认为好的方面,而是找准一个对标高手,然后全方位的学习这个人。我在做品牌咨询时就对标,学习了一个在国内很有名的行业顶尖大咖。我先找到他公司的方案,进行完全模仿,连PPT的排版都一样,而且我只参照他一个人的
ES-LTR粗排模块
poins
jenkins 运维
ES-LTR粗排模块官方资源:https://github.com/HeiBoWang/elasticsearch-learning-to-rankElasticsearch学习排名插件使用机器学习提高搜索相关性排名。它为维基媒体基金会和Snagajob等地方的搜索提供了动力!这个插件有什么功能此插件:允许您在Elasticsearch中存储特征(Elasticsearch查询模板)记录特征得分(
2018-11-18成长小组学习笔记
实验中学45
因为嗓子“罢工”,我面对众人只能借“微笑”代言。在开始授课前,绣霞老师先反馈上次作业的情况,提到“接纳”需是真正发自内心的完全接纳,而不是口头上的接纳,内心却是排斥的。提到一个“问题”孩子恰恰对家爱的更加“深沉”,夫妻间的问题不能影响到孩子,对孩子更好的爱不是你为他做的更多,而是给他自由、健康成长的空间。图片发自App一、孩子:家庭的一面镜子夫妻成了彼此的“投射”,婚姻便“吵的不可开交”,婚姻便成
flutter boost 如何从native跳转到flutter页面
Icarus_
flutter flutter
FlutterBoost是一个Flutter插件,它可以帮助开发者在原生应用和Flutter应用之间无缝跳转。以下是一些基本步骤,展示了如何使用FlutterBoost从原生(Native)页面跳转到Flutter页面。1.配置FlutterBoost在你的Flutter项目中集成FlutterBoost插件。这通常涉及到修改`pubspec.yaml`文件来添加依赖项,并根据FlutterBoo
掌握Flutter底部导航栏:畅游导航之旅
繁依Fanyi
xml json sql flutter 开发语言 前端 git
1.引言在移动应用开发中,底部导航栏是一种常见且非常实用的用户界面元素。它提供了快速导航至不同功能模块或页面的便捷方式,使用户可以轻松访问应用程序的各个部分。在Flutter中,底部导航栏也是一项强大的功能,开发者可以利用Flutter框架提供的丰富组件和灵活性,轻松实现各种样式和交互效果的底部导航栏。本文将深入探讨Flutter中底部导航栏的实现方法,从基础的结构搭建到高级功能的应用,带领读者逐
【鸿蒙HarmonyOS开发笔记】ArkUI常用组件介绍汇总(更新中)
温、
鸿蒙HarmonyOS开发笔记 学习记录 harmonyos 笔记 华为
概述此文总结开发中用到的一些常用组件,便于查阅,此文持续更新,闲的没事就更线性布局(Row/Column)不多介绍了,最常用的布局组件,两者除了方向不一样,别的都一样方便起见下面只写Column常用属性排列方向上的间距:spaceColumn({space:20}){Row().width('90%').height(50).backgroundColor(0xF5DEB3)Row().width
python转码
Desamond
python 开发语言
转码在许多场景中都有应用,以下是一些常见的场景:网页开发:当用户在网页上输入文本时,可能需要将特殊字符(如空格、引号、特殊符号等)进行转码,以防止这些字符对URL或HTML代码产生干扰。文件名处理:在处理文件名时,可能需要将特殊字符进行转码,以避免文件名被错误地解析或显示。数据传输:在数据传输过程中,为了确保数据的完整性和正确性,可能需要将数据中的特殊字符进行转码。数据存储:在数据库或数据存储中,
2019-07-16
振华老凤祥店长崔宁宁
大爱的李老师,智慧的教授,亲爱的跃友们:大家好!我是莱州鑫和金店李总的人~崔宁宁今天是我的日精进行动第56天,我分享一下今天的改变,我们相互勉励,每天进步一点点,离成功便不远。1、比学习:人这一生最主要的就是信念,坚定不移的信念是成功路上的重要基石!2、比改变:我是一切的根源,我变了世界就变了!改变自己的心态!3、比付出:承担才能成长,付出才会杰出!4、比谦卑:学习每位优秀店长身上的优点!5、比感
python清华大学出版社答案_Python机器学习及实践
weixin_39805119
python清华大学出版社答案
第1章机器学习的基础知识1.1何谓机器学习1.1.1传感器和海量数据1.1.2机器学习的重要性1.1.3机器学习的表现1.1.4机器学习的主要任务1.1.5选择合适的算法1.1.6机器学习程序的步骤1.2综合分类1.3推荐系统和深度学习1.3.1推荐系统1.3.2深度学习1.4何为Python1.4.1使用Python软件的由来1.4.2为什么使用Python1.4.3Python设计定位1.4.
微信小程序修改checkbox和radio的样式
叶落无痕123
微信小程序 小程序
我们在开发小程序的时候,有时候需要修改小程序中checkbox和radio的原生样式,如何修改呢?这里给大家提供了一份代码,大家可以试试。首先是修改checkbox样式的代码:/*重写checkbox样式*//*未选中的背景样式*/checkbox.wx-checkbox-input{border-radius:50%;/*圆角*/width:40rpx;/*背景的宽*/height:40rpx;
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