第四章. 连接设计和测试平台
4.1 测试平台和DUT之间通信

DUT和测试平台(Test)通常是分开的模块(module,描述硬件),可以在顶层(top)中将DUT和Test例化,然后根据对应信号进行连接(注意信号的方向)。

//待测DUT
module arb_port (output logic [1:0] grant,input logic [1:0] request,input logic rst,input logic clk);...always@(posedge clk or posedge rst)beginif(rst)    grant <= 2'b00;else ...end
endmodule                   //测试模块(测试平台定义在另一个模块中,与设计所在的模块相互独立)
module test(input logic[1:0] grant,output logic[1:0] request,output logic rst,input logic clk);initialbegin@(posedge clk)  request <= 2'b01;$display("@%0t:Drove req = 01", $time);repeat (2) @(posedge clk);if(grant != 2'b01)    $display("@%0d: a1: grant != 2'b01", $time);$finish;   end
endmodule//在顶层中例化DUT和Test并进行连接(不使用接口)
module top;logic [1:0] grant, request;bit clk, rst;//系统时钟发生器always #5 clk = ~clk;//clk在DUT和Test都为输入,由顶层clk驱动//DUT中grant为输出,Test中grant为输入,则由顶层模块连接后,驱动由DUT发送到Testarb_port a1 (grant, request, rst, clk);        test t1(grant, request, rst, clk);
endmodule

在一个真实的设计中往往含有数百个端口(port,相对于硬件DUT来说,一个端口就是一个信号)信号,需要数页代码来声明信号和端口。所以这种连接方式是极易出错的,因为一个信号可能流经几个设计层次,它必须一次又一次的被声明和连接。如果相对现有的信号进行拓展新信号,必须在多个文件中定义和连接,所以SV引入接口(interface,相对于Test来说,抽象化概念)来简化连接。

4.2 接口

SV使用接口为程序块之间的通信建模,接口可以看作一捆智能的连线。接口包含了连接,同步,通信的功能。

4.2.1 使用接口简化连接

//定义接口
interface arb_if(input bit clk);logic [1:0] grant, request;logic rst;
endinterface//使用arb_if定义DUT
module arb(arb_if arbif);...always @(posedge arbif.clk or posedge arb_if.rst)beginif(arbif.rst)    arbif.grant <= 2'b00;else          arbif.grant <= next_grant;...end
endmodule//使用arb_if定义Test
module test(arb_if arbif);...initialbegin...@(posedge arbif.clk);arbif.request <= 2'b01;$display("@%0d: Drove req = 01", $time);repeat(2) @(posedge arbif.clk);if(arbif.grant != 2'b01) $display("@%0d: al: grant != 2'b01", $time);$finish;end
endmodule//使用接口在顶层模块中连接DUT与Test
module top;bit clk;always #5 clk = ~clk;arb_if arbif(clk);arb a1 (arbif);test t1(arbif);
endmodule

与端口相比,使用接口连接可以看出来能使代码变得简洁不易出错,如果想拓展接口中的信号,只需在接口中定义和使用这个信号的模块做修改,不需要其他操作,这种特性极大降低了连接出错的几率。

使用接口时需要确保在你的模块和程序块(program block,软件建模)之外声明接口变量(就是说不能将接口定义在module和program内)。有些编译器不支持在模块中定义接口,及时某些编译器支持,接口也只是所在模块的局部变量,对设计的其他部分来说是不可见的。

4.2.2 连接接口和端口

如果DUT中端口使用4.1中的方式定义(不使用接口,端口声明),而Test使用4.2.1中的方式声明(使用接口),此时连接就涉及到如何连接接口与端口,接口与端口的连接是以点对点的连接方式进行的。实际开发过程中常用此方式进行连接。

//顶层模块连接接口与端口
module top;bit clk;always #5 clk = ~clk;arb_if arbif(clk);//DUT实例化过程中'.'代表一个端口,后面名字代表端口名称,括号内为与该端口连接的信号arb_port a1(.grant(arbif.grant),.request(arbif.request),.rst(arbif.rst).clk(arbif.clk));test t1(arbif);
endmodule

4.2.3 使用modport将接口中的信号分组并指定方向

在4.1中端口信号的声明包含了方向,编辑器会依次来检查连线方向是否发生错误。在接口中使用modport结构能够将信号分组并指定方向。

//带有modport的接口
interface arb_if(input bit clk);logic[1:0] grant, request;logic rst;modport TEST(output request, rst,input grant, clk);modport DUT(input request, rst, clk,output grant);modport  MONITOR(input request, grant, rst, clk);
endinterface//在接口中使用modport
module arb(arb_if.DUT arbif);...
endmodulemodule test(arb_if.TEST arbif);...
endmodule//顶层例化与4.2中相同,因modport只需要在模块定义时指明,而模块例化时不需要指明。使用modport更确切得代表了一个真实的设计,尤其是信号的方向

在设计中可以通过两种方法使用modport名:一种是在接口信号的程序和模块中使用modport名;另外也可以在顶层模块中使用modport名,然后把接口放到程序和模块的端口表中(module,program中使用interface声明,top层使用interface.modport声明)。正常情况下,使用第一种连接方式,因为modport是接口实现的细节,不应该出现在顶层模块中。特殊情况,如果一个模块需要多次例化,每次例化需要连接到不同的modport,那么此时应该使用第二种连接方式。

并非接口中的每个信号都是必须连接的,某些模块可能只关注接口的某些信号而忽略其他信号。

4.2.4 接口的优缺点

优点:便于设计重用减少连接错误可拓展性强,modport允许多个信号捆绑到一起,也可以通过定义方向借助编译器进行自动检查

缺点:对于点对点连接,使用modport接口描述跟使用信号列表的端口一样冗余。必须同时使用信号名和接口,可能会使模块变得冗长。如两个连接的模块是一个不会被重用的专用协议,使用接口需要做比端口连接更多的工作。连接两个不同的接口很困难,需正确拆分独立的信号并正确的驱动。

4.3 激励时序(难点)

4.3.1 使用时钟块控制同步信号的时序

**接口块可以使用时钟块来指定同步信号相对于时钟的时序。时钟块中的任何信号都将同步地驱动或采样,这就保证了测试平台在正确的时间点与信号交互。**一个接口可以包含多个时钟块,因为每个块中都只有一个时钟表达式,所以每一个对应一个时钟域。可以在时钟块中使用default语句指定一个时钟偏移。一旦定义了时钟块,测试平台就可以用@arbif.cb表达式等待时钟,而不需要描述确切的式中信号和边沿@(posedge arbif.cb)。这样即使改变了时钟块中的时钟或者边沿,也不需要修改测试平台的代码。

//带时钟块的接口
interface arb_if(input bit clk);logic [1:0] grant, request;logic rst;clocking cb @(posedge clk);output request;input grant;endclockingmodport TEST (clocking cb, output rst);modport DUT (input request, rst, output grant);endinterface//测试平台
module test(arb_if.TEST arbif);initialbeginarbif_cb_request <= 0;@arbif.cb;$display("@%0t: Grant=%b", $time, arbif.cb.grant);end
endmodule

4.3.2 接口中的logic和wire对比

cb块中声明了块中的信号在时钟的上升沿有效,**信号的方向是相对于modport(也就说方向相对于采样的模块)的,接口中的信号建议定义为logic,因logic易用。如果测试平台在接口中使用过程赋值语句驱动一个异步信号,那么该信号必须是logic类型。wire类型变量只能被连续赋值语句驱动,时钟块中的信号始终是同步的,可以定义为logic或者wire。logic信号可以直接被驱动,而wire需要使用额外的代码。**建议使用logic的另一个原因是,如果你无意中使用了多个元件的驱动源,编译器会自动报错。

//接口中驱动logic和wire信号
interface asynch_if();logic l;wire w;
endinterfacemodule test(asynch_if ifc);logic local_wire;//接口中的wire类型不能直接驱动,需在module中定义logic类型驱动信号,使用assign赋值语句将信号线连接,module中通过logic类型驱动assign ifc.w = local_wire;initialbeginifc.l <= 0;          //直接驱动异步logic信号local_wire <= 1; //借助logic驱动wire类型end
endmodule

4.3.3 V的时序问题

在实际的硬件设计中,DUT中的存储单元在时钟的有效沿锁存输入信号,这些数值由存储单元输出,然后通过逻辑块到达下一个存储单元。从上一个存储单元的输入到下一个存储单元的输入延时必须小于一个时钟周期。所以测试仪需要在时钟沿之后驱动芯片的输入,然后在下一个时钟之前读取输出。测试平台应该模拟测试仪这种行为,应该在有效时钟边沿或边沿之后驱动待测设计,然后在有效时钟沿到达之前,在满足协议时序的前提下,尽可能晚地采样。

如果DUT和测试程序仅由V模块构成,这几乎是不可能实现的。如果测试平台在时钟边沿驱动DUT,就会存在竞争状态。如果时钟到达一个DUT的时间快于测试平台的激励,但是到达另一个DUT的时钟又晚于这个激励,这种情况会导致DUT的外部时钟沿都是在相同的仿真时间达到,DUT内部有一些输入在上个时钟周期采样,但是其他的输入却在当前时钟周期采样。

解决此问题的一种方法是给系统添加延时。比如#0,0时延会强迫V代码的线程停止并在所有其他代码完成之后被重新调度执行。但在一个大型的设计中,往往不可避免地存在多个线程都想最后执行,#0带来的结果是每次运行结果不确定,所以要避免使用#0以免代码不稳定并且不可移植。

另外一个解决方法是使用一个较大的延时#1。 RTL代码除了时钟沿之外没有其他时序信息,所以逻辑电路在时钟沿之后一个时间单位就会稳定。但是如果一个模块使用1ns时间精度,而其他仅使用10ps的时间精度呢,那么#1意味着1ns10ps还是其他的时间长度呢。你需要在时钟的有效沿之后,并且是在任何事件发生之前,而非在一段时间之内,尽快地驱动设计。所以应当避免使用#1延时解决时序问题。

对DUT输出信号的采样存在着相同的问题,希望在时钟有效沿到来之前的最后时刻捕获信号的值,你可能直到下个时钟沿会出现在100ns,但是不能在100ns出现时钟边沿的时钟采样,因为设计的输出值可能已经改变了,应当在时钟沿到达之前的Tsetup时间上采样。

4.3.4 程序块和时序区域(难点)

竞争问题的根源在于设计和测试平台的事件混合在同一个时间片(time slot)内,即使在纯RTL程序中也会发生同样的问题。如果存在一种可以将时间轴上分开这些时间的方法,如在100ns时刻,测试平台可以在时钟信号变化或者设计产生任何活动之前采样设计的输出信号。在所有的事件执行完毕后,测试平台开始下一个动作。在SV中,测试平台的代码在一个程序块中,但是程序块中不能有任何的层次级别,例如模块的实例,接口或者其他程序。

SV引入了一种新的时间片的划分方式,在V中,大多数的时间在有效区域执行。在一个时间片首先执行Active区域,在这个区域中运行设计事件,包括RTL,门级代码和时钟发生器。第二个区域是Oberved区域,执行断言。接下来就是执行测试平台的Reactive区域。注意时间并不是单向向前流动,ObservedReactive区域的事件可以触发本时钟周期内Active区域中进一步的设计事件。最后是Postponed区域,他将在时间片的最后,所有设计活动都结束后的只读时间段采样信号。

4.3.5 仿真的结束

在V中,仿真在调度事件存在的时候会继续执行,直到遇到$finish。SV中增加了一种结束仿真的方法。SV把任何一个程序块都视为含有一个测试,如果仅有一个程序块,那么当完成所有的initial块中的最后一个语句时,仿真就结束了,因为编译器认为这就是测试的结尾即使还有模块或者程序块的线程在运行,仿真也会结束。所以,当测试结束时无需关闭所有的monitor和driver。如果存在多个程序块,仿真在最后一个程序块结束时结束,这样最后一个测试完成时仿真就会结束。可以执行$exit提前中断任何一个程序块,也可以使用$finish来结束仿真。

4.3.6 指定设计和测试平台之间的时延

时钟块的默认时序是在#1step延时之后采样输入信号,在#0延时之后驱动输出信号。#1step延时规定了信号在前一个时间片的Postponed区域,在设计有任何新的动作之前被采样,这样就可以在时钟改变之前捕获输出值。因为时钟模块的原因,测试平台的输出信号是同步的,所以他们直接被送入设计中,在Reactive区域运行的程序块在同一个时间片再一次触发Actie区域。可以想象时钟块在设计和测试平台中插入了一个同步器来理解。(不理解)

4.4 接口的驱动和采样

测试平台需要驱动和采样设计的信号,这主要是通过带有时钟块的接口实现的。异步信号通过接口时没有任何时延,比如rst,而时钟块中的信号将得到同步。

4.4.1 接口同步

可以使用V中的@wait来同步测试平台中的信号。

//信号同步
program automatic test(bus_if.TB bus);initialbegin@bus.cb;                     //时钟块的有效时钟沿(posedge,negedge)继续repeat(3) @bus.cb;              //等待3个有效时钟沿@bus.cb.grant;                  //在任何时钟沿继续@(posedge bus.cb.grant)          //上升沿继续@(negedge bus.cb.grant)         //下降沿继续wait (bus.cb.grant == 1);      //等待表达式被执行,如果已经是真,不做任何延时@(posedge bus.cb.grant or negedge bus.rst);      //等待几个信号end
endprogram

4.4.2 接口信号采样

当从时钟块读取一个信号时,是在时钟沿之前得到采样值。

`timesacle 1ns/1ns
program test(arb_if.TEST arbif);initialbegin$monitor("@%0t: grant = %h", $time, arbif.cb.grant);# 50ns $display("End of test");end
endprogrammodule arb(arb_if.DUT arbif);initialbegin# 7  arbif.grant = 1;   //@7ns# 10 arbif.grant = 2;   //@17ns# 8  arbif.grant = 2;  //@25nsend
endmodule

时序图如下,在仿真时刻25ns时刻,采样值为2而不是3,说明信号的新值不是在下一个时钟周期(25ns~35ns)传递给测试平台的。

4.4.3 接口信号驱动

当在时钟块中使用modport时任何同步接口信号多必须加上接口名(arbif)和时钟块名(cb)的前缀

4.4.5 通过时钟块驱动接口信号

时钟块中应当使用同步驱动接口信号必须使用非阻塞赋值来驱动。信号在赋值后并不会立刻改变(在时钟沿赋值除外)。例如测试平台在100ns的时候同时产生了arbif.cb.request和arbif.cb,那么request信号在100ns的时候就会改变。如果测试平台在101改变arbif.cb.request信号,那么该变化直到下一个时钟沿才会传递给设计。

busif.cb.request <= 1;       //同步驱动
busif.cb.cmd <= cmd_buf;    //同步驱动

如果测试平台在时钟的有效沿改变接口信号,那么其值会立刻传递到设计中,这是因为时钟块的默认输出延时是#0如果测试平台在时钟有效沿之后驱动输出,那么该值直到时钟的下一个有效沿才会传递到设计中。

program test(arb_if.TEST arbif);initialbegin#7   arbif.cb.request <= 3;  //@7ns#10 arbif.cb.request <= 2;   //@17ns#8  arbif.cb.request <= 1;  //@25ns#25 finish;end
endprogrammodule arb(arb_if.DUT arbif);initial$monitor("@%0t: req = %h", $time, arbif.request);
endmodule

时序图如上,第二个周期产生的值2永远不会被DUT捕获,因为第三个周期结束时测试平台产生了值1。异步的驱动时钟块信号会导致数值丢失,应该使用时钟延时前缀以保证在时钟沿驱动信号。

##2 arbif.cb.request <= 0;   //等待两个时钟周期后赋值
##3     //非法,必须跟赋值语句同时使用

如果想在驱动一个信号前等待两个时钟周期,可以使用repeat(2)@bus.cb;或将时钟周期延时##2.。后一种方式只能在时钟块里作为驱动信号的前缀来使用,因为它需要知道使用哪个时钟来做延时。

4.4.6 接口中的双向信号

V中,如果想要驱动一个双向信号,比如一个过程代码中的双向端口,需要用一个连续赋值语句来将reg连接到wire。

SV中,当你在程序对线网赋值时,SV实际上将值写到了一个驱动该线网的临时变量中,所有驱动器输出经过判断后程序可以直接通过连线读取该值。

interface master_if(input bit clk);wire [7:0] data;          //双向信号clocking cb @(posedge clk);inout data;endclockingmodport TEST (clocking cb);
endinterfaceprogram test (master_if mif);initialbeginmif.cb.data <= 'z;        //三态总线@mif.cb;$display(mif.cb.data);   //从总线读取@mif.cb;mif.cb.data <= 7'h5a;  //驱动总线@mif.cb;mif.cb.data <= 'z;      //释放总线end
endprogram

SV中没有明确定义如何驱动接口中的异步双向信号,驱动双向信号有两种解决方式:使用一个跨模块引用或连续赋值语句。

4.6.7 为什么在program中不允许使用always块

在一个设计中,一个always块可能从仿真的开始就会在每一个时钟的上升沿触发执行。但是一个测试平台的执行过程是经过初始化驱动和响应设计行为等步骤后结束仿真的在测试平台中,一个连续执行的always模块不能正常工作。当program中最后一个initial块结束的时候,仿真实际上也默认结束了,就像执行了$finish一样,如果加入了一个always块,它将永远不会结束,这样就不得不显式调用$exit来发出程序块结束的信号。如果确实需要一个always块,可以使用initial forever来完成。

4.6.8 时钟发生器

时钟发生器跟设计结合地更加紧密,所以时钟发生器应当定义一个模块。不应该把时钟发生器放在程序块里。如下clkout_sig信号都从Reactive区域开始传递,在Active区域进入设计,根据这两个信号到达的先后不同可能会引起竞争状态。将时钟发生器放在一个模块中可以避免竞争状态。

program bad_generator(output bit clk, out_sig);initial   forever #5 clk <= ~clk;initialbeginforever @(posedge clk)  out_sig <= ~out_sig;end
endprogram

如下是一个正确的时钟发生器,他有意避免了0时刻的边沿以免竞争情形的发生。所有的时钟边沿使用阻塞赋值生成,他们将在Active区域触发事件的发生。如果确实需要0时刻产生一个时钟边沿,那么可以使用非阻塞赋值语句设置初始值,这样所有的时钟敏感逻辑电路比如always块都会在时钟变换之前执行。

module clock_generator (output bit clk);initialalways #5 clk = ~clk     //在时间0之后生成时钟沿
endmodule
4.5 连接模块
module top;bit clk;always #4 clk = ~clk;arb_if arbif(.*);arb al(.*);test t1(.*);
endmodule

快捷符号.*(隐式端口连接),能自动在当前级别自动连接模块实例的端口到具体信号,只要端口和信号的名字和数据类型相同端口列表中的接口必须连接,否则SV编译器不通过

4.6 顶层作用域

有时需要在仿真过程中创建程序或者模块之外的对象,以便参与仿真的所有对象都可以访问他们。在V中,只有宏定义可以跨越模块的边界而且经常被用来创建全局变量

SV中引入了编译单元,它是仪器编译的源文件的一个组合。任何module,macromodule,interface,program,package或者primitive边界之外的作用域被称为编译单元作用域,也称为$unit。这个作用域内的任何成员,比如parameter,都类似于全局变量,因为它可以被所有低一级的块访问。但是它们又不同于真正的全局成员,例如parameter在编译时其他源文件不可见。

有些工具,比如Synopsys VCS,它同时编译所有的SV代码,所以$unit是全局的。但是Synopsys Design Compiler一次编译一个模块或者一组模块,这时$unit可能只包含了一个或者几个文件的内容。其他供应商的EDA工具可能一次编译所有的文件或者只是一个子集,结果导致**$unit是不可移植的。**

实例名$root允许你从顶层作用域开始明确地引用系统中的成员名。此时,$root类似于Unix文件系统中的根目录/。对于VCS这样一次编译所有文件的工具。$root$unit是等价的。当你的代码引用另一个模块中的成员时,编译器首先在本作用域内查找,然后在上一层作用域内查找,如此往复直到到达顶层作用域。可以通过使用$root指定绝对路径明确地引用跨模块的变量

4.7 程序-模块交互

程序块可以读写模块中的所有信号,可以调用模块中的所有例程,但是模块看不到程序块。程序可以调用模块中的例程执行不同的动作,这个例程可以改变内部信号的值。因为当前SV标准没有定义怎样在程序块内改变信号的值,所以需要在设计中写一个任务来改变信号的值,然后在程序中调用这个任务。

4.8 SVA断言

可以使用SVA在设计或者能够创建时序断言。断言的例化跟其他设计块的例化相似,而且在整个仿真过程中都是有效的。仿真器会跟踪哪些断言被激活,这样就可以在此基础上收集功能覆盖率的数据。

4.8.1 立即断言

测试平台的过程代码可以检查待测设计的信号值和测试平台的信号值,并且在存在问题时采取相应的行动。

bus.cb.request <= 1;
repeat (2) @bus.cb;
if(bus.cb.grant != 2'b01) $display("Error, grant!=1");

断言比if语句更加紧凑,设计期望断言表达式为真,否则输出后面的错误。

bus.cb.request <= 1;
repeat (2) @bus.cb;
a1: assert(bus.cb.grant == 2'b01);

如果产生了一个grant信号,那么测试继续执行,如果信号不符合期望值,仿真将给出断言失败信息。断言是声明性代码,他的执行过程和过程性代码有很大差异。使用几行断言,可以验证复杂的时序关系,等价的过程代码可能远比这些断言要复杂和冗长。

//一个断言有可选的then和else分句。
a1:assert(bus.cb.grant == 2'b01)
else $error("Grant not asserted");

SV有四个输出消息的函数:$info$warning$error$fatal。这些函数仅允许在断言内部使用,不允许在过程代码中使用

4.8.2 并行断言

可以理解并行断言是一个连续运行的模块,它为整个仿真过程检查信号的值。需要在断言内指定一个采样时钟。

//并行断言,检查request在非复位状态下没有X/Z信号
interface arb_if(input bit clk);logic [1:0] grant, request;logic rst;property request_2state;@(posedge clk) disable iff (rst);$isunknown(request) == 0;endpropertyassert_request_2state:assert property (request_2state);
endinterface

断言还有许多其他的用法。例如,可以在接口中使用断言,这样接口不仅可以传送信号值也可以检查协议的正确性。

4.9 ref端口的方向

SV引入了一种新的端口方向ref,ref是对变量的引用,它的值是该变量最后一次赋的值。如果将一个变量连接到多个ref端口,就可能产生竞争,因为多个模块的端口都可能更新同一个变量

4.10 仿真的结束

仿真在程序块中的最后一个initial块结束是结束。其实,在最后一个initial块完成时,它隐式地调用$exit以标志程序的结束。当所有的程序块都退出了,$finish函数的隐性调用也就结束了。也可以在需要的时候直接调用$finish来结束仿真。

但是,程序并没有完全结束。模块或者程序块可以定义一个或者多个finial块来执行仿真器退出前的代码。此块可以放置清理任务,在finial块中不能调度事件,或者有任何时延信息。

program test;int errors, warnings;initialbegin...endfinial $display("Test done with %0d errors and %0d warnings", errors, warnings);
endprogram

参考文献:

SystemVerilog验证 测试平台编写指南(原书第二版)张春 麦宋平 赵益新 译

SV绿皮书笔记(四)相关推荐

  1. SV绿皮书笔记(九)暂时完结

    第九章. 功能覆盖率 9.1覆盖率类型 功能覆盖率:功能覆盖率和设计意图是紧密相连的.用来衡量哪些设计特性已经被测试程序测试过的一个指标. 代码覆盖率:包括行覆盖率,路径覆盖率,翻转覆盖率,状态机覆盖 ...

  2. SV绿皮书笔记(六)

    第六章. 随机化 6.1 哪些对象需要随机化 随机时需考虑设计输入的各个方面,器件配置,环境配置,原始输入数据,封装后的输出数据,协议异常,延时,事务状态,错误和违例等情况. 6.2 SV中的随机化 ...

  3. SV绿皮书笔记(二)

    第二章. 数据类型 2.1 基本数据类型的两个属性 双状态/四状态:根据存储数据类型中的每一bit位的可能数分为双状态类型和四值状态类型. 双状态:可能值0,1,双状态默认初始值为0.双状态值具有更低 ...

  4. SV绿皮书笔记(三)

    第三章. 过程语句和子程序 3.1 循环语句 continue:在循环中跳出本轮循环剩下的语句直接进入下一轮循环. break:用于终止并跳出循环 bit[127:0] cmd; int file, ...

  5. SV绿皮书笔记(七)

    第七章. 线程以及线程间的通信 在实际硬件中,时序逻辑通过时钟沿来激活,而组合逻辑的输出则随着输入的变化而变化.所有这些并发的活动在V的寄存器传输级上是通过initial和always块语句,实例化和 ...

  6. SV绿皮书笔记(五)

    第五章. 面向对象编程基础 5.1 OOP概述 V属于过程性编程语言(代码逐行执行,无数据结构,类似C语言),V中没有结构,只有位向量和数组.而在对总线事务建模时往往需要数据结构,使用过程性语言不够便 ...

  7. SV绿皮书笔记(一)

    第一章. 验证导论 1.1 验证概述 验证的目的:保证设计文件能够完成预期的功能,寻找设计漏洞. 验证计划:参考设计文档,描述需要验证哪些特性,采用什么样的技术. 验证的流程:设计文档(自然语言)→设 ...

  8. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  9. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

最新文章

  1. C++——虚函数(Virtual Member Functions) 【functions语意学】
  2. vimrc-20201028重新配置
  3. java 执行jar_windows下如何用java指令运行jar包?
  4. 开源 Asp.net mvc 用户中心(nUserCenter) 产品机会评估
  5. 标准库函数和系统调用的区别
  6. 1208. 尽可能使字符串相等
  7. java long 唯一_Java工具类:获取long型唯一ID
  8. Atitit.故障排除系列---NoClassDefFoundError  NoClassDefFoundError ClassNotFoundException
  9. cad填充密度怎么调整_CAD填充图案放在哪里,这几步你要了解
  10. 《深入浅出WPF》——模板学习
  11. Maix_Bit官方烧录软件和IDE的使用
  12. 什么是胖客户端和瘦客户端?什么是哑终端?
  13. 安卓图片轮播(banner)
  14. 科学计算机感叹号,电脑网络黄三角感叹号怎么解决
  15. 成也苹果败也苹果,曾经女首富身价缩水一半
  16. 关于虚拟机VMware中打开Ubuntu20.04提示AMD-V处于禁用状态解决办法(基于MSI微星主板)
  17. Android逆向学习之如何在抖音发链接
  18. 无限级分类之迭代查找家谱树
  19. HDMI 2.0概述
  20. 毕业薪酬行业第一?计算机+金融交叉学科真香

热门文章

  1. 老马 Markov 其人
  2. 巨蟹座---永远的伤
  3. php fgetc,PHP 文件读取 fread、fgets、fgetc、file_get_contents 与 file 函数
  4. 文员应该要学计算机哪些内容,文员的基本电脑知识是什么? 文员的基本要求有哪些?...
  5. OpenWRT安装SIM7600CE 4G模块驱动
  6. 物体识别全流程(Ubuntu16.04)结合ROS
  7. 轻言壁纸微信小程序源码
  8. 【记录】用Python webdriver图书馆抢座自动预约的正确姿势
  9. 推荐几个2021年App数据统计平台,分析数据运营必备工具
  10. 某客服热线呼叫中心话务分析