欢迎关注个人公众号摸鱼范式

目录

对Cracking Digital VLSI Verification Interview:Interview Success这本书的汉化,最新更新请关注微信公众号 摸鱼范式

Hardware Description Languages

Verilog

[159] verilog中的阻塞赋值和非阻塞赋值有什么区别?

verilog支持阻塞与非阻塞两种赋值方式。使用阻塞赋值时,计算和赋值会立刻执行。因此但是顺序执行块中由多个阻塞赋值,他们会按照阻塞的方式顺序执行。如下所示。

  1. always @(posedge clk)
  2. begin
  3. x = a|b;
  4. y = a&b;
  5. z = x|y;
  6. end

在这个例子中,所有赋值都采用的时阻塞赋值,执行时会立刻进行计算,然后完成赋值操作。因此在第三条语句中,会将前两条语句中更新后的x和y用于第三条语句的计算并赋值。

在非阻塞赋值中,所有的赋值都将在当前的仿真时刻的最后执行。首先对右侧的表达式进行计算,然后才对左侧进行赋值,因此在下面的例子中,三条语句都先进行右侧语句的计算,然后再一起对左侧进行赋值操作。结果就是,第三条语句所采用的x和y是一个旧值,并不是前两条语句在这个仿真时刻改变后的值。

  1. always @(posedge clk)
  2. begin
  3. x <= a|b;
  4. y <= a&b;
  5. z <= x|y;
  6. end

[160] 下面的两个例子综合时需要多少个Flip-Flop?

  1. 1)
  2. always @(posedge clk)
  3. begin
  4. B = A;
  5. C = B;
  6. end
  7. 2)
  8. always @(posedge clk)
  9. begin
  10. B <= A;
  11. C <= B;
  12. end

1)一个;2)两个

第一种情况下,A的值赋予B,B更新后的值在同一个周期将赋予C,因此只需要一个触发器即可

第二种情况下,B更新后的值,在下一个周期才能赋予C,需要两个触发器实现

[161] 下列代码的输出是什么?

  1. always @(posedge clk)
  2. begin
  3. a = 0;
  4. a <=1;
  5. $display("a=%0b", a);
  6. end

由于非阻塞赋值只能在周期结束生效,而display语句打印的是当前值,所以结果是a=0。

[162] 编写verilog代码,交换两个寄存器的值,并且不使用中间寄存器

  1. always @(posedge clk)
  2. begin
  3. A<=B;
  4. B<=A;
  5. end

[163] 下列代码的输出是?

  1. module test;
  2. int alpha,beta;
  3. initial begin
  4. alpha = 4;
  5. beta = 3;
  6. beta <= beta + alpha;
  7. alpha <= alpha + beta;
  8. alpha = alpha - 1;
  9. $display("Alpha=%0d Beta=%0d", alpha,beta);
  10. end
  11. endmodule

这些赋值在一个没有任何时钟的initial块中,所以非阻塞赋值是不起作用的,因此只关心阻塞赋值。结果是:Alpha=3 Beta=3

[164] 下列两种情况下c的值会是多少(五个时间单位后)?

  1. 1)
  2. initial begin
  3. a=0;
  4. b=1;
  5. c = #5 a+b;
  6. end
  7. 2)
  8. initial begin
  9. a=0;
  10. b=1;
  11. #5 c = a+b;
  12. end
  13. initial begin
  14. a=0;
  15. #3 a=1;
  16. end
  1. c=1
  2. c=2

第一种情况下,c=a+b,但是需要五个时间单位的延迟以后才完成赋值

第二种情况下,在c=a+b赋值完成之前,另一个initial块中,第三个时间单位时,修改了a的值,所以在计算a+b时,a=1,因此最终结果为2

[165] 分析下面的代码,找出代码的错误

  1. bit a, b, c, d, e;
  2. always @(a, b, c) begin
  3. e = a & b & c & d;
  4. end

敏感列表缺失了d,这会导致仿真结果错误,而综合结果可能是正确的

[166] 编写verilog模块,使用“?:”运算符实现3:1mux

  1. module mux31_2(inp0,inp1,inp2,sel0,sel1, outres);
  2. input inp0, inp1, inp2, sel0, sel1;
  3. output outres;
  4. assign outres = sel1 ? inp2 : (sel0 ? inp1 : inp0);
  5. endmodule

[167] 用下列两段代码进行建模,这两种代码风格有什么问题?

  1. always @(posedge clk or posedge reset)
  2. if (reset)
  3. X1 = 0; // reset
  4. else
  5. X1 = X2;
  6. always @(posedge clk or posedge reset)
  7. if (reset)
  8. X2 = 1;// set
  9. else
  10. X2 = X1;

verilog仿真器并不能保证always块的执行顺序,在上面的代码中,由于使用了阻塞赋值,因此会导致竞争现象。如果我们使用不同的仿真器,always块的执行顺序不同可能会导致不同的结果。推荐使用非阻塞赋值。

[168] 同步复位和异步复位之间有什么区别?如何使用verilog进行同步复位和异步复位建模?

上电以后,使用复位进行状态设定为一个确定状态。如果对复位在时钟的边沿进行采样,那么就是同步复位。如果不依赖于时钟边沿进行复位采用,则为异步复位。

下面的代码为同步复位

  1. always @ (posedge clk) begin
  2. if (reset) begin
  3. ABC <=0;
  4. end
  5. end

下面的代码为异步复位

  1. always @ (posedge clk or posedge reset ) begin
  2. if (reset) begin
  3. ABC <=0;
  4. end
  5. end

[169] “==”和“===”有什么区别?

两者都是相等或比较运算符。“==”测检查二值逻辑相等,而“===”运算符测试四值逻辑相等。

使用“==”比较四值逻辑,如果出现X或者Z,则结果为X。

使用“===”比较四值逻辑,如果出现X或Z,则结果为0或1,能够正确的进行比较。

[170] 如果A和B是两个3bit的变量:A = 3'b1x0 B = 3'b1x0 ,那么1)A==B 2)A===B的结果分别是?

  1. A==B只能判断非X/Z,出现X或Z,最后的结果为X
  2. A===B能够判断X/Z,结果为1

[171] 用verilog建模Latch和Flip-Flop,并解释他们的不同

Flip-Flop,在时钟上下沿进行采样。Latch,在使能时,一直进行采样,输出跟随输入

D触发器

  1. always @ (posedge clk) begin
  2. if(reset) begin
  3. Q <= 0;
  4. Qbar <= 1;
  5. end else begin
  6. Q <= D;
  7. Qbar <= ~D;
  8. end
  9. end

锁存器

  1. always @ (D or Enable) begin
  2. if(Enable) begin
  3. Q <= D;
  4. Qbar <= ~D;
  5. end
  6. end

[172] 编写verilog代码,检测序列10110

首先设计状态机。

  1. 没有检测到序列
  2. 检测到1
  3. 检测到10
  4. 检测到101
  5. 检测到1011

状态机如下

  1. module seq_detector(z,x,clock,reset);
  2. output z;
  3. input x,clock;
  4. input reset; //active high
  5. reg [2:0] state,nextstate;
  6. parameter s0=3'b000,s1=3'b001,s2=3'b010,s3=3'b011,s4=3'b100;
  7. always @ (posedge clock) begin
  8. if(reset) begin
  9. state <=s0;
  10. nextstate<=s0;
  11. end else begin
  12. state<=nextstate;
  13. end
  14. end
  15. always @ (x or state)
  16. case(state)
  17. s0: if(x) nextstate=s1; else nextstate=s0;
  18. s1: if(x) nextstate=s1; else nextstate=s2;
  19. s2: if(x) nextstate=s3; else nextstate=s0;
  20. s3: if(x) nextstate=s4; else nextstate=s2;
  21. s4: if(x) nextstate=s1; else nextstate=s2;
  22. endcase
  23. always @ (x or state)
  24. case(state)
  25. s4: if(x) z=1'b0; else z=1'b1;
  26. s0,s1,s2,s3: z=1'b0;
  27. endcase
  28. endmodule

[173] 写一段verilog代码,根据输入的n计算斐波那契数列

斐波那契数列是一种数列,每一项是通过将前两项相加而得到的。 从0和1开始,顺序为0、1、1、2、3、5、8、13、21、34,依此类推。 通常,表达式为xn = xn-1 + xn-2。 假设最大值n = 256,以下代码将生成第n个斐波那契数。 值“n”作为输入传递给模块(nth_number)

  1. module fibonacci(input clock, reset, input [7:0] nth_number, output [19:0] fibonacci_number, output number_ready);
  2. reg [19:0] previous_value, current_value;
  3. reg [7:0] internal_counter;
  4. reg number_ready_r;
  5. always @(posedge clock or posedge reset) begin
  6. if(reset) begin
  7. previous_value <='d0; //1st Fibonacci Number
  8. current_value <='d1; //2nd Fibonacci Number
  9. internal_counter <='d1;
  10. number_ready_r <= 0;
  11. end else begin
  12. if (internal_counter == (nth_number-2)) begin
  13. number_ready_r <= 1;
  14. end else begin
  15. internal_counter <= internal_counter + 1;
  16. current_value <= current_value + previous_value;
  17. previous_value <= current_value;
  18. number_ready_r <= 0;
  19. end
  20. end
  21. end
  22. assign fibonacci_number = current_value;
  23. assign number_ready = number_ready_r
  24. endmodule

[174] 写一段verilog代码,用半加器组成全加器

  1. module half_adder(input_0, input_1, sum, carry);
  2. input input_0, input_1;
  3. output sum, carry;
  4. assign sum = (input_0)^(input_1);
  5. assign carry = (input_0)&(input_1);
  6. endmodule
  7. module full_adder(input_0, input_1, input_2, sum, carry);
  8. input input_0, input_1, input_2;
  9. output sum, carry;
  10. reg sum_intermediate, carry_intermediate_0, carry_intermediate_1;
  11. half_adder ha1(input0,input1,sum_intermediate,carry_intermediate_0);
  12. half_adder ha2(sum_intermediate,input2,sum,carry_intermediate_1);
  13. assign carry = (carry_intermediate_0)|(carry_intermediate_1);
  14. endmodule

[175] verilog中的task和function有什么区别?

  1. function不能使用任何延时语句,task可以
  2. 由于1中的原因,function可以调用function,但是不能调用task。task可以调用task和funtion
  3. funtion可以综合,task不可以
  4. function需要有通过返回参数作为输出,但是有多个输入输出参数。task不能返回任何值,但是具有多个输入或者输出参数。

SystemVerilog

[176] systemverilog中的reg,wire和logic有什么区别?

reg和wire是Verilog中就存在的两种数据类型,而logic是SystemVerilog中引入的新数据类型。

  1. wire是一种数据类型,可以对物理导线进行建模以连接两个元素。 导线只能由连续赋值语句驱动,如果不驱动,则无法保持值。 因此,wire只能用于对组合逻辑进行建模。
  2. reg是可以为存储数据或状态建模的数据类型。 它们需要由always块驱动,而不能由连续赋值语句驱动。 reg可用于建模顺序逻辑和组合逻辑。
  3. logic是SystemVerilog中的一种新数据类型,可用于wire和reg建模,也是四值逻辑,可以被用作reg也可以wire。

[177] bit和logic有什么区别?

bit是只能存储0和1的二值逻辑,而logic能够储存0、1、X和Z的四值逻辑。

二值逻辑能够加速仿真速度,而如果用二值逻辑用于驱动或者采样来自RTL的信号,会导致错误采样X和Z

[178] logic[7:0] 和 byte 有什么区别?

byte是有符号类型,最大为127,而logic可以被声明为无符号,最大可以达到255.

[179] 动态数组和关联数组,哪个更加适合模拟大型数组?例如32KB的巨大内存数组

关联数组更加适合用于大型数组的建模,因为只有在讲元素写入数组时才分配内存。而动态数组需要在使用之前分配和初始化内存。例如:如果要使用动态数组对32KB的内存数组进行建模,则首先需要分配32K的空间用于读/写。但是由于关联数组内部使用哈希进行元素的搜索,所以速度也是最慢的。

[180] 有一个动态数组通过下列代码进行初始化,写一段代码找出数组中所有大于3的元素

  1. int myvalues [] = '{9,1,8,3,2,4,6},
  2. int match_q[$];
  3. match_q = myvalues.find with (item > 3);

[181] systemverilog中的union和struct有什么区别?

struct表示不同数据类型的集合。 例如:在下面的示例中,我们定义了一个名为instruction_s的struct,该struct由24位地址和8位操作码构成。

  1. typedef struct {
  2. bit [7:0] opcode;
  3. bit [23:0] addr;
  4. } instruction_s;
  5. instruction_s current_instruction;
  6. current_instruction.addr='h100;

可以直接引用instruction_s,也可以单独引用成员。存储struct所需的内存空间为成员之和,例如instruction_s需要32bit的空间。

union是一种数据类型,可以使用有且只有一种命名成员数据类型来访问它。 与struct不同,不能访问所有成员数据类型。 分配给union的内存将是成员数据类型所需的最大内存。 如果要对诸如寄存器之类的硬件资源建模,该寄存器可以存储不同类型的值,则union很有用。 例如:如果寄存器可以存储整数和实数值,则可以按以下方式定义union:

  1. typedef union {
  2. int data;
  3. real f_data;
  4. }state_u;
  5. state_u reg_state;
  6. reg_state.f_data = 'hFFFF_FFFF_FFFF_FFFF;
  7. $display(" int_data =%h", reg_state.data);

在此示例中,state_u可以保存32位整数数据,也可以保存64位实数数据。 因此,为reg_state分配的内存将为64bit(两种数据类型中的较大者)。 由于所有成员数据类型都有共享内存,因此在上面的示例中,如果我们将64位值分配给reg_state.f_data,则我们也可以使用其他数据类型引用相同的32位。

[182] systemverilog的function和task中“ref”和“const ref”是什么意思?

ref关键字用于通过引用而不是值的方式传递参数。 子例程/函数与调用者共享句柄以访问值。 这是传递诸如类对象或对象数组之类的参数的有效方法,否则创建副本将消耗更多内存。 同样,由于调用方和function/task共享相同的引用,因此使用ref在函数内部完成的任何更改也对调用方可见。

例如:这是一个CRC函数的示例,该函数需要一个大数据包作为参数来计算CRC。 通过作为参考传递,每次调用CRC函数都不需要在存储器上创建数据包的副本。

  1. function automatic int crc(ref byte packet [1000:1] );
  2. for( int j= 1; j <= 1000; j++ ) begin
  3. crc ^= packet[j];
  4. end
  5. endfunction

const关键字用于禁止函数修改传递的参数。例如:在同一个CRC函数中,可以将参数声明为“const ref”参数,如下所示,以确保原始数据包内容不会被CRC函数意外修改。

  1. function automatic int crc(const ref byte packet [1000:1] );
  2. for( int j= 1; j <= 1000; j++ ) begin
  3. crc ^= packet[j];
  4. end
  5. endfunction

[183] 下面代码中参数a和b的方向是什么?

  1. task sticky(ref int array[50], int a, b);

function和task的每一个参数都有他的方向,input,ouput,inout或者ref。如果没有显式声明,则默认与前面的参数保持一致,如果前面没有参数,则默认为输入。上述代码中,第一个参数array的方向为ref,而后续a和b没有显式声明,所以将延续前者的方向,即为ref。

[184] 压缩数组和非压缩数组的区别是?

压缩数组表示一组连续的bit,而非压缩数组不是。在声明上,有下面的区别

  1. bit [7:0] data ; // packed array of scalar bit types
  2. real latency [7:0]; // unpacked array of real types

压缩数组只能由一位数据类型(bit,logic,reg)或枚举类型组成。非压缩数组能够由任意类型组成

  1. logic[31:0] addr; //packed array of logic type
  2. class record_c;
  3. record_c table[7:0]; //unpacked array of record objects

[185] packed struct和unpacked struct的区别是什么?

压缩结构体是一种可以将压缩位向量作为结构成员进行访问的方法。 换句话说,如果struct的所有成员仅由位字段组成并且可以无间隙地打包在内存中,则它可以是压缩结构。 例如:在下面的struct定义中,所有成员都可以表示为位向量(int等于32位,short int到16位,byte到8位),并且一个struct可以打包到单个连续内存56bit。

  1. struct packed {
  2. int a;
  3. short int b;
  4. byte c;
  5. } pack1_s;

非压缩结构体不需要打包到连续的bit位中,因此在不同成员之间可以存在空隙。下面是一个无法压缩的结构体。

  1. struct {
  2. string name;
  3. int age;
  4. string parent;
  5. } record_s

[186] 下面哪个是对的?

  1. function不能消耗仿真时间

  2. task不能消耗仿真时间

  3. 正确

  4. 错误

[187] 现有一个动态数组的大小为100,如何把他的大小定义为200,并且前100个元素为原来的数组?

动态数组使用new[]进行保留元素的内存分配。

  1. integer addr[]; // Declare the dynamic array.
  2. addr = new[100]; // Create a 100-element array.
  3. ………
  4. // Double the array size, preserving previous values.
  5. addr = new[200](addr);

[188] systemverilog中case,casex和casez的区别是?

case语句是选择语句,匹配表达式并执行对应的语句。下面是一个3:1MUX

  1. case (select[1:0])
  2. 2'b00: out_sig = in0;
  3. 2'b01: out_sig = in1;
  4. 2'b10: out_sig = in2;
  5. default: out_sig = 'x;
  6. endcase

上面的例子中,如果表达式与指定的内容完全匹配,则执行后续语句,如果出现x或者z,将执行默认语句。

casez是case的一个特殊版本,通常在处理较少位的译码器中使用。下面的示例中,将3位中断申请队列解码位三个独立的中断引脚,只关心对应位,而其他位无关紧要。带有优先级。

  1. casez (irq)
  2. 3'b1?? : int2 = 1'b1;
  3. 3'b?1? : int1 = 1'b1;
  4. 3'b??1 : int0 = 1'b1;
  5. endcase

“ casex”是另一个特殊版本,除了无关项,在比较中它也忽略X和Z值。

[189] 在case、casez、casex中使用的是==还是===?

三者使用的都是===

[190] systemverilog中的display,write,monitor和strobe用什么区别?

  1. $display:执行时立刻打印内容
  2. $strobe:在当前的timestep结束时打印内容
  3. $monitor:在timestep结束时,如果内容改变了,则进行打印。如果多次调用,则新的覆盖旧的。
  4. $write:和\$display一样,但是不会在结尾打印换行符

[191] 下面的systemverilog代码中有什么错误?

  1. task wait_packet;
  2. Packet packet;
  3. event packet_received;
  4. @packet_received;
  5. packet = new();
  6. endtask
  7. function void do_print();
  8. wait_packet();
  9. $display("packet received");
  10. endfunction

function中不能使用任何延时语句。上面的例子中,function调用了一个耗时的task,这是非法的。

[192] systemverilog中new()和new[]有什么区别?

new()时systemverilog中类的构造函数。他在类中定义,并初始化对象。

new[]用于动态数组的内存分配。

[193] 什么是systemverilog中的前置声明?

有时候,一个类有可能引用另一个尚未编译的类,这会导致编译错误。例如,如果按照下面的顺序编译Statistics和Packet,由于在编译Statistics时,Packet尚未定义,编译器将为报错。

  1. class Statistics;
  2. Packet p1;
  3. endclass
  4. class Packet; //full definition here
  5. endclass

为了避免这个问题,类在完全定义之前,可以先进行前置声明。

  1. typedef Packet; //forward declaration
  2. class Statistics;
  3. Packet p1;
  4. endclass
  5. class Packet; //full definition here
  6. endclass

[194] 下面代码有什么问题?

  1. task gen_packet(Packet pkt);
  2. pkt = new();
  3. pkt.dest = 0xABCD;
  4. endtask
  5. Packet pkt;
  6. initial begin
  7. gen_packet(pkt);
  8. $display(pkt.dest);
  9. end

这段代码在运行时,会产生空指针错误。task传入pkt句柄,而在内部为句柄创建了一个对象。在initial块中,调用了gen_packet,并修改了pkt.dest,但是对于task来说这些都是局部的。task的默认方向是input,在内部的修改句柄的指向并不能影响外部,尽管在task内部进行了对象例化并且修改了值,而实际上外部的pkt始终是空句柄。

[195] systemverilog中,类成员的private、public和protect属性是什么意思?

  1. private成员只允许在class内部进行访问。这些成员在派生类中也是不可见的。
  2. public成员在class内外都可以进行访问。这些成员在派生类中是可见的。
  3. protect成员只允许在class内部进行访问。这些成员在派生类中是可见的。

[196] systemverilog的类中,成员变量默认是public还是private?

与C++和Java中不一样,systemverilog的类成员默认为public。

[197] 什么是嵌套类?何时使用他?

当一个类的定义包含另一个类的定义,则该类为嵌套类。例如,下面代码中,StringList类定义包含另一个类Node的定义。

  1. class StringList;
  2. class Node; // Nested class for a node in a linked list.
  3. string name;
  4. Node link;
  5. endclass
  6. endclass

嵌套允许隐藏本地名称和资源的本地分配。 当需要新的类作为类实现的一部分时很有用。

[198] systemverilog中的interface是什么?

systemverilog中的接口用于将多个线网进行捆绑,有助于封装设计块之间的接口通信。接口可以在设计中实例化,并且可以使用单个名称进行连接,不需要显式声明所有的端口和连接。除了用于连接,接口内部还可以定义function,可以通过实例化接口调用function。接口还支持过程块和assign,这些对于协议检查和断言非常有用。下例是一个接口的简单示例:

  1. interface simple_bus; // Define the interface
  2. logic req, gnt;
  3. logic [7:0] addr, data;
  4. logic [1:0] mode;
  5. logic start, rdy;
  6. endinterface: simple_bus

[199] 什么是modport?

modport(模块端口的缩写)是接口中的一种构造,可用于分组信号并指定方向。 以下是如何使用Modport将接口进一步分组以连接到不同组件的示例。

  1. interface arb_if(input bit clk);
  2. logic [1:0] grant, request;
  3. logic reset;
  4. modport TEST (output request, reset, input grant, clk);
  5. modport DUT (input request, reset, clk, output grant);
  6. modport MONITOR (input request, grant, reset, clk);
  7. endinterface

上面的示例中,相同的信号在不同的modport中具有不同的方向。

[200] interface是可综合的吗?

是可综合的。

[201] 什么是时钟块?在interface中使用时钟块有什么好处?

时钟块类似于modport,除了具备modport的信号方向指定,还能够建模信号的时序行为。下面是一个时钟块的例子。

  1. clocking sample_cb @(posedge clk);
  2. default input #2ns output #3ns;
  3. input a1, a2;
  4. output b1;
  5. endclocking

在上面的示例中,定义了一个名为sample_cb的时钟块,关联的时钟为clk。default关键字定义默认的时钟偏斜,输入为2ns,输出为3ns。输入偏斜定义了时钟采样在时钟边沿前多少个时间单位。 输出偏斜定义了时钟驱动在时钟边沿后的多少个时间单位。

时钟块只能在module或者interface中定义

[202] 下面两种定义时钟偏斜的方式有什么不同?

  1. 1) input #1 step req1;
  2. 2) input #1ns req1;

定义时钟偏斜有两种方式

  1. 以time step为单位,这是由全局的时间精度定义的,即`timescale
  2. 之间指明具体的时间长度

[203] systemveirlog仿真里在一个timestep中有哪些主要的阶段?

systemverilog仿真器是事件驱动的仿真器,明确定义了不同的阶段来计划和执行所有事件。在仿真中,所有的事件都以timeslot为单位进行。timeslot被划分为一组有序阶段,提供设计和测试代码之间的可预测交互。如下图所示,一个timeslot可以划分为五个主要阶段,每个阶段都可以进一步的被划分为细分的子阶段。

  1. Prepone:这是timeslot最先执行的阶段,并且只执行一次。来自测试平台对设计信号的采样发生在这个阶段。
  2. Active:Active阶段包括三个子阶段:Active,Inactive和NBA(Nonblocking assignment)阶段.RTL代码和行为代码在Active阶段执行。阻塞赋值在Active阶段执行。非阻塞赋值,RHS的计算在Active阶段执行,赋值操作在NBA阶段执行。如果由#0的赋值行为,则在inactive阶段执行。
  3. Observed:Observed用于计算property(并发断言中)表达式的触发行为。在property计算期间,通过或者失败的调度会在当前timeslot的Reactive阶段进行。
  4. Reactive:Reactive阶段包括三个子阶段:Re-Active,Re-Inactive和Re-NBA(Re-Nonblocking assignment)阶段。用于执行systemverilog中program块的阻塞赋值,#0阻塞赋值,非阻塞赋值。这个独立的响应阶段确保在测试代码执行之前,设计代码的状态已经稳定。使用OVM/UVM不需要程序块,因此Reactive阶段可能使用得不多。
  5. Postponed:Postponed阶段是当前timeslot的最后一个阶段。$monitor,$strobe和其他类似的事件在此区域执行。而$dislpay事件在Active 和 Reactive(如果在program块中调用)阶段执行

[204] 根据下面的约束,哪一个选项是错误的?

  1. rand logic [15:0] a, b, c;
  2. constraint c_abc {
  3. a < c;
  4. b == a;
  5. c < 30;
  6. b > 25;
  7. }
  1. b可以取26-29之间的任意值
  2. c可以取0-29之间的任意值
  3. c可以取26-29之间的任意值

将约束内容取交集,c可以取27-30之间的任意值。

[205] 什么是systemverilog中的unique约束?

unique约束会令一组成员两两各不相同。下面是一个示例。

  1. class Test;
  2. rand byte a[5];
  3. rand byte b;
  4. constraint ab_cons { unique {b, a[0:5]}; }
  5. endclass

[206] 如何选择性的激活或者关闭一个类中的约束?

  1. //<object>.constraint_mode(0) :: 关闭对象中的所有约束
  2. //<constraint>.constraint_mode(0) :: 选择性关闭某一个约束
  3. class ABC;
  4. rand int length;
  5. rand byte SA;
  6. constraint c_length { length inside [1:64];}
  7. constraint c_sa {SA inside [1:16];}
  8. endclass ABC abc = new();
  9. abc.constraint_mode(0) // 关闭所有约束
  10. abc.c_length.constraint_mode(0) // 只关闭约束c_length

[207] 现有下面的一个类,如何生成addr大于200的Packet对象?

  1. class Packet;
  2. rand bit[31:0] addr;
  3. constraint c_addr { addr inside [0:100];}
  4. endclass

大于200与内置约束冲突,所以需要先关闭内置约束,然后通过内联约束进行随机化

  1. Packet p = new();
  2. p.c_addr.constraint_mode(0);
  3. p.randomize with {addr > 200;};

[208] 什么是pre_randomize()和post_randomize()函数?

这显示systemverilog内建的回调函数。在调用randomize之前会自动调用pre_randomize函数,之后会自动调用post_randomize函数。可以通过定义这两个函数,完成在随机化之前或者之后进行某些操作。

[209] 编写一个约束,为下面对象中的动态数组生成约束,使得每个元素都小于10,数组大小也小于10

  1. class dynamic_array;
  2. rand unsigned int abc[];
  3. endclass
  1. constraint c_abc_len {
  2. abc.size() < 10;
  3. foreach (abc[i])
  4. abc[i] < 10;
  5. }

[210] 编写约束,创建随机整数数组,使数组大小在10-16之间,并且数组按照降序排列

  1. class array_abc;
  2. rand unsigned int myarray[];
  3. endclass
  1. constraint c_abc_val {
  2. myarray.size inside { [10:16] };
  3. foreach (myarray[i])
  4. if (i>0) myarray[i] < myarray[i-1];
  5. }

[211] 如何对一个生成一个随机动态数组,并且元素两两各不相同?参考下面的代码

  1. class TestClass;
  2. rand bit[3:0] my_array[];//dynamic array of bit[3:0]
  3. endclass
  1. 使用unique进行约束
  1. constraint c_rand_array_uniq {
  2. my_array.size == 6; //or any size constraint
  3. unique {my_array}; //unique array values
  4. }
  1. 不适用unique进行约束,使用post_randomize回调函数完成
  1. constraint c_rand_array_inc {
  2. my_array.size == 6 ;// or any size constraint
  3. foreach (my_array[i])
  4. if(i >0)
  5. my_array[i] > my_array[i-1];
  6. }
  7. function post_randomize();
  8. my_array.shuffle();
  9. endfunction

[212] “fork - join”, “fork - join_any” 和“fork - join_none”之间有什么区别?

systemverilog支持三种类型的动态进程,可以在运行时创建,并作为独立线程执行。

  1. fork-join:使用“ fork .. join”创建的进程作为单独的线程运行,但是父进程停滞不前,直到所有进程全部执行完。 如果我们看下面的示例:共有三个进程task1,task2和task3,它们将并行运行,并且只有在这三个进程全部完成之后,join语句之后的$display()才会执行。
  1. initial begin
  2. fork
  3. task1; // Process 1
  4. task2; // Process 2
  5. task3; // Process 3
  6. join $display(“All tasks finished”);
  7. end
  1. fork-join_any:使用“ fork…join_any”创建的进程作为单独的进程运行,但是父进程会在任何一个子进程完成后继续。其余进程和父进程可以并行运行。 如果我们看下面的示例:有三个进程-task1,task2和task3将并行运行。 当task1 / task2 / task3之一完成时,join_any将完成,并在其他线程可能仍在运行时执行$ display()。
  1. initial begin
  2. fork
  3. task1; // Process 1
  4. task2; // Process 2
  5. task3; // Process 3
  6. join_any
  7. $display(“Any one of task1/2/3 finished”);
  8. end
  1. fork-join_none:使用“ fork…join_none”创建的进程将作为单独的进程运行,但是父进程不会停滞并且也将并行进行。 参考以下示例,其中有三个进程-task1,task2和task3,它们将与父进程并行运行。
  1. initial begin
  2. fork
  3. task1; // Process 1
  4. task2; // Process 2
  5. task3; // Process 3
  6. join_none
  7. $display(“All tasks launched and running”);
  8. end

[213] “wait fork”和“disable fork”的作用是什么?

在使用“ fork..join_none”或“ fork..join_any”时,有时候需要父进程和子进程进行同步,这可以通过wait fork完成。

  1. initial begin
  2. fork task1; // Process 1
  3. task2; // Process 2
  4. join_none
  5. $display(“All tasks launched and running”);
  6. wait fork;
  7. $display(“All sub-tasks finished now”);
  8. end

类似的,disable fork可以提前将子进程停止。

  1. initial begin
  2. fork
  3. task1; // Process 1
  4. task2; // Process 2
  5. join_any
  6. $display(“One of task1/2 completed ”);
  7. disable fork;
  8. $display(“All other tasks disable now”);
  9. end

[214] 硬约束和软约束有什么区别?

systemverilog中编写的常规约束为硬约束,对成员的随机化必须始终满足约束,如果约束无解,则会导致错误。如果将约束定义为软约束,在没有外部约束的条件下,和硬约束一样,外部约束的优先级比软约束高。软约束通常用于指定随机变量的默认值和分布,并且可以被外部特定的约束覆盖。

  1. class Packet;
  2. rand int length;
  3. constraint length_default_c {
  4. soft length inside {32,1024};
  5. }
  6. endclass
  7. Packet p = new();
  8. p.randomize() with { length == 1512; }

上例中,如果约束没有定义为软约束,则随机化会失败。

[215] 下面每个线程的输出是什么?

  1. initial begin
  2. for (int j=0; j<3; j++) begin
  3. fork
  4. automatic int result;
  5. begin
  6. result= j*j;
  7. $display("Thread=%0d value=%0d", j, value);
  8. end
  9. join_none
  10. wait
  11. fork;
  12. end
  13. end

由于“ j”不是每个线程的动态变量,因此在生成每个线程后它会不断递增,并且当所有线程开始执行时,每个线程将看到的j都是3。因此,每个线程都将输出9。 如果每个线程打算使用不同的值,则应将“ j”的值复制到一个自动变量,如下所示:

  1. automatic int k = j;
  2. begin
  3. result = k*k;
  4. end

[216] 下面的代码会产生多少个并行的线程?

  1. fork
  2. for (int i=0; i < 10; i++ ) begin
  3. ABC();
  4. end
  5. join

for循环在fork join内,所以只有一个线程。

[217] 下面的约束有什么错误?

  1. class packet;
  2. rand bit [15:0] a, b, c;
  3. constraint pkt_c { 0 < a < b < c; }
  4. endclass

约束表达式中,至多只有一个关系运算符(<,<=,==,>=或>)。如果要实现变量的顺序约束,需要多个表达式。

  1. constraint pkt_c { 0 < a; a < b ; b < c; }

[218] systemverilog中的虚方法和纯虚方法的区别是?

在类中将方法定义为虚方法,则在派生类中可以重写这个方法。基类可以定义具有实现或不实现的虚函数,在派生类中也可以选择覆盖或不覆盖。而纯虚函数只具备函数声明,没有具体实现,在派生类中必须有具体实现。纯虚函数常用在抽象类中使用,下面是一个示例。

  1. virtual class BasePacket; // No implementation
  2. pure virtual function integer send(bit[31:0] data);
  3. endclass
  4. class EtherPacket extends BasePacket;
  5. virtual function integer send(bit[31:0] data);
  6. // body of the function
  7. // that implements the send
  8. ….…
  9. endfunction
  10. endclass

[219] 什么是Semaphores?何时使用?

Semaphores是用于控制对公用资源的机制。Semaphores可以视为在创建时具有多个钥匙的存储池,使用Semaphores访问资源时,首先要取得要是,然后才能够继续执行。通过这种机制,可以确保没有要是的进程一直等待到获取钥匙。Semaphores通常用于相互排斥,对公用资源进行访问控制以及简单同步。下面是简单的Semaphores的创建方法。

  1. semaphore smTx;
  2. smTx = new(1); //create the semaphore with 1 keys.

get()和try_get()分别是阻塞和非阻塞的获取钥匙的方法,put()用于返还钥匙。

[220] 下面两个约束有什么不同?

  1. 1)
  2. class ABSolveBefore;
  3. rand bit A;
  4. rand bit [1:0] B;
  5. constraint c_ab { (A==0) -> B==0; solve A before B; }
  6. endclass
  7. 2)
  8. class ABSolveBefore;
  9. rand bit A;
  10. rand bit [1:0] B;
  11. constraint c_ab { (A==0) -> B==0; solve B before A; }
  12. endclass

两种情况下,A都能取到0和1,B能取到0123,并且A为0时B都为0。但是求解顺序会导致两者的分布会不一致。

如果先求解A再求解B,那么概率分布表为

A B 概率
0 0 0.5
0 1 0
0 2 0
0 3 0
1 0 0.5*0.25
1 1 0.5*0.25
1 2 0.5*0.25
1 3 0.5*0.25

如果先求解B再求解A,那么概率分布表为

A B 概率
0 0 0.5*0.25
0 1 0
0 2 0
0 3 0
1 0 0.5*0.25
1 1 0.25
1 2 0.25
1 3 0.25

[221] 什么是mailbox?如何使用mailbox?

mailbox是一种通信机制,用于线程之间的数据交换。数据在一个线程存入mailbox中,在另一个线程中检索。下面是mailbox的声明与创建的示例:

  1. mailbox mbxRcv;
  2. mbxRcv = new();

将数据存入mailbox中可以使用put(阻塞)和peek(非阻塞)实现,从mailbox中取出数据可以使用get(阻塞)和try_get(非阻塞)方法,查询mailbox中的数据数量可以使用num()方法。

[222] 有限容量和无限容量的mailbox有什么区别?如何创建?

有限容量的mailbox是指邮箱在创建时就指定容量。

  1. mailbox mbxRcv;
  2. mbxRcv = new(10); //size bounded to 10

无限容量的mailbox是指邮箱再创建时不指定容量。

  1. mailbox mbxRcv;
  2. mbxRcv = new(); //size is unbounded or infinite

有限容量的mailbox如果存满,线程将无法继续存入数据,只到mailbox有空间。

[223] 什么是systemverilog中的event?如何触发event?

event类型的变量不用于存储数据,用于线程同步。可以使用"->"显式触发事件,而线程可以通过"@"来等待事件的触发,阻断线程只到事件被触发。event为两个或者多个同时运行的进程的同步提供强大而有效的手段。

下面的示例中,两个线程通过event进行同步。一旦发送请求,send_req()将会触发一个事件,而receive_response()检测到事件被触发以后就会继续执行任务。

  1. module test;
  2. event req_send;
  3. initial begin
  4. fork
  5. send_req();
  6. receive_response);
  7. join
  8. end
  9. task send_req(); //create and send a req
  10. -> req_send; //trigger event
  11. endtask
  12. task receive_response();
  13. @req_send; //wait until a send event is triggered
  14. //collect response
  15. endtask
  16. endmodule

[224] 如何合并两个event?

可以指将将一个event变量赋值给另一个event变量,此时两个event变量都指向同一个同步对象,可以认为两者合并。

[225] 什么是systemverilog中的std::randomize()方法?何时使用它?

std::randomize()是作用域随机化函数,无需定义类或者实例化类对象仅能对当前作用域中的数据进行随机化。如果某些需要随机化的变量不是类的成员,则需要使用std::randomize()。下面是一个示例。

  1. module stim;
  2. bit [15:0] addr;
  3. bit [31:0] data;
  4. function bit gen_stim();
  5. bit success, rd_wr;
  6. success = std::randomize(addr, data, rd_wr);
  7. return rd_wr ;
  8. endfunction

  9. endmodule

std::randomize()和类的randomize()具有相似的功能,也可以使用约束,下面是一个通过with添加约束的示例。

  1. success = std::randomize( addr, data, rd_wr ) with {rd_wr -> addr > 'hFF00;};

[226] 在派生类中可以覆盖基类中的约束嘛?如果可以,如何实现?

可以通过使用相同的约束名称在派生类中重写基类定义的约束。下面是一个示例

  1. class Base;
  2. rand int a ;
  3. rand int b;
  4. constraint c_a_b_const {a < b;}
  5. endclass
  6. class Derived extends Base;
  7. constraint c_a_b_const {a > b;}
  8. endclass

[227] 下面的systemverilog代码的调用有什么问题?

  1. function int count_ones ( ref bit [9:0] vec );
  2. for( count_ones = 0; vec != 0; vec = vec >> 1 )
  3. begin
  4. count_ones += vec & 1'b1;
  5. end
  6. endfunction
  7. constraint C1 { length == count_ones( myvec ) ; }

在约束中,不允许调用方向为ref的函数,除非使用“const ref”,这保证函数在内部不会修改参数。

[228] 下面两个派生类有什么不同?

  1. class Base;
  2. virtual function printA();
  3. endfunction
  4. endclass
  5. 1)
  6. class Derived extends Base;
  7. function printA();
  8. //new print implementation
  9. endfunction
  10. endclass
  11. 2)
  12. class Derived extends Base;
  13. virtual function printA();
  14. //new print implementation
  15. endfunction
  16. endclass

两者并没有区别,在基类中如果定义了virtual关键字,那么派生类也会继承该属性,无论有没有显式的二次声明。

[229] 找出下面代码中的问题(如果有的话)

  1. class Packet;
  2. bit [31:0] addr;
  3. endclass
  4. class ErrPacket extends Packet;
  5. bit err;
  6. endclass
  7. module Test;
  8. initial begin
  9. Packet p;
  10. ErrPacket ep;
  11. ep = new();
  12. p = ep;
  13. $display("packet addr=%h err=%b", p.addr, p.err);
  14. end
  15. endmodule

没有问题,基类句柄可以指向派生类对象,但是反过来是不允许的。

[230] 现有下面两个类,请问在示例代码中compute_crc函数的调用顺序是?

  1. class Packet; //Base Class
  2. rand bit [31:0] src, dst, data[8]; // Variables
  3. bit [31:0] crc;
  4. virtual function void compute_crc;
  5. crc = src ^ dst ^ data.xor;
  6. endfunction
  7. endclass : Packet
  8. class BadPacket extends Packet; //Derived class
  9. rand bit bad_crc;
  10. virtual function void compute_crc; //overriding definition
  11. super.compute_crc(); // Compute good CRC
  12. if (bad_crc) crc = ~crc; // Corrupt the CRC bits
  13. endfunction
  14. endclass : BadPacket

示例

  1. Packet pkt;
  2. BadPacket badPkt;
  3. initial begin
  4. pkt = new;
  5. pkt.compute_crc; // 1) Which of compute_crc() gets called?
  6. badPkt = new;
  7. badPkt.compute_crc; // 2) Which of compute_crc() gets called?
  8. pkt = badPkt; // Base handle points to ext obj
  9. pkt.compute_crc; // 3) Which of compute_crc() gets called ?
  10. end
  1. 调用了基类的compute_crc
  2. 调用了派生类的compute_crc
  3. 调用了派生类的compute_crc,虽然使用的是基类的句柄,但是方法定义为虚方法,所以要根据对象的类型进行调用

[231] 下面两种代码风格哪种更加好?为什么?

  1. 1)
  2. for (i=0; i < length*count; i++) begin
  3. a[i] = b[i];
  4. end
  5. 2)
  6. l_end = length * count;
  7. for (i=0; i < l_end; i++) begin
  8. a[i] = b[i]
  9. end

2比1更好,在1中,每次迭代都需要计算length*count,2中只需要计算一次

[232] 下面的代码有什么错误?

  1. class ABC;
  2. local int var;
  3. endclass
  4. class DEF extends ABC;
  5. function new();
  6. var = 10;
  7. endfunction
  8. endclass

变量var具备local关键字,在派生类中是不可用的。

[233] 什么是虚接口,何时使用它?

虚接口是指向实际结构的变量。他在类中用于提供接口的连接点,通过虚接口可以访问接口中的信号。在下面的示例中,接口bus_if将多个信号整合起来。然后,BusTransactor类中定义了这一接口类型的虚接口,这个虚接口用于访问来自this.b_if的所有驱动或检测。实例化物理接口以后,通过构造函数将句柄传递给BusTransactor类。

  1. interface bus_if; // A bus interface
  2. logic req, grant;
  3. logic [7:0] addr, data;
  4. endinterface
  5. class BusTransactor; // Bus transactor class
  6. virtual bus_if bus; // virtual interface of type bus_if
  7. function new( virtual bus_if b_if );
  8. bus = b_if; // initialize the virtual interface
  9. endfunction
  10. task request(); // request the bus
  11. bus.req <= 1'b1;
  12. endtask
  13. task wait_for_bus(); // wait for the bus to be granted
  14. @(posedge bus.grant);
  15. endtask
  16. endclass
  17. module top;
  18. bus_if bif(); // instantiate interfaces, connect signals etc
  19. initial begin
  20. BusTransactor xactor;
  21. xactor = new(bif); //pass interface to constructor
  22. end
  23. endmodule

[234] 工厂和工厂模式的意思是?

在面向对象编程中,工厂是用于创建原型或类的不同对象的方法或函数。不同的类在工厂中注册后,工厂方法可以通过调用相应的构造函数来创建任何已注册类类型的对象。创建对象不直接调用构造函数的模式称为工厂模式。使用基于工厂的对象创建而不是直接调用构造函数,允许在对象创建中使用多态性。这个概念是在UVM (Univers)中实现的。

[235] 回调函数(callback)的意义是什么?

“回调”是由另一个函数调用的任何函数,它以第一个函数为参数。大多数情况下,当某个“事件”发生时,会调用回调函数。在验证平台中,回调函数很多优点:

  1. 注入从驱动程序发送的事务错误
  2. 当一个模拟阶段准备结束时,调用一个函数来关闭所有序列/驱动程序中所有挂起的事务。
  3. 在一个特定的事件上调用一个覆盖率采样函数。

大多数情况下,回调函数是通过将它们注册到一个组件/对象中来实现的,该组件/对象会在某些定义的条件下回调。UVM中的phase_ready_to_end()就是回调函数,它在基类中实现,并注册到UVM_component类中。当当前仿真阶段准备结束时,将调用该函数。因此,用户可以通过覆盖此函数定义来实现需要在仿真阶段结束时执行的任何功能。

[236] 什么是DPI调用?

DPI是直接编程接口的缩写,它是SystemVerilog和C/C++等外语编程语言之间的接口。DPI允许在接口两边的语言之间直接进行跨语言函数调用。在C语言中实现的函数可以在SystemVerilog中调用(import),在SystemVerilog中实现的函数可以使用DPI层在C语言中调用(export)。DPI支持跨语言边界的function(零时间执行)和task(耗时执行)。SystemVerilog数据类型是惟一能够在任何方向上跨越SystemVerilog和外部语言之间的边界的数据类型。

[237] “DPI import” 和“DPI export”有什么区别?

import的DPI函数是用C语言实现并在SystemVerilog代码中调用的函数。

export的DPI函数是用SystemVerilog语言实现并导出到C语言的函数,这样就可以从C语言调用它。

函数和任务都可以导入或导出。

[238] 什么是系统函数?举例说明他们的作用

SystemVerilog语言支持许多不同的内置系统任务和函数,通常在任务/函数名称前加上“$”前缀。此外,语言还支持添加用户定义的系统任务和功能。下面是一些系统任务和功能的例子(根据功能分类)。对于完整的列表,可以参考LRM。

  1. 仿真控制函数 - \(\$finish\), \(\$stop\), \(\$exit\)
  2. 转换函数 - \(\$bitstoreal\), \(\$itor\), \(\$cast\)
  3. bit向量系统函数 - \(\$countones\), \(\$onehot\), \(\$isunknown\)
  4. 安全系统函数 - \(\$error\), \(\$fatal\), \(\$warning\)
  5. 采样值控制函数 - \(\$rose\), \(\$fell\), \(\$changed\)
  6. 断言控制函数 - \(\$asserton\), \(\$assertoff\)

Cracking Digital VLSI Verification Interview 第四章的更多相关文章

  1. Cracking Digital VLSI Verification Interview 第三章

    目录 Programming Basics Basic Programming Concepts Object Oriented Programming Concepts UNIX/Linux Pro ...

  2. Cracking Digital VLSI Verification Interview 第一章

    目录 Digital Logic Design Number Systems, Arithmetic and Codes Basic Gates Combinational Logic Circuit ...

  3. Cracking Digital VLSI Verification Interview 第二章

    Computer Architecture 对Cracking Digital VLSI Verification Interview:Interview Success这本书的汉化,最新更新请关注微 ...

  4. ROS机器人程序设计(原书第2版)补充资料 (肆) 第四章 在ROS下使用传感器和执行器

    ROS机器人程序设计(原书第2版)补充资料 (肆) 第四章 在ROS使用传感器和执行器 书中,大部分出现hydro的地方,直接替换为indigo或jade或kinetic,即可在对应版本中使用. 第四 ...

  5. 4 Visual Effects 视觉效果 读书笔记 第四章

    4   Visual Effects    视觉效果        读书笔记 第四章 Well, circles and ovals are good, but how about drawing r ...

  6. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  7. 《Linux内核设计与实现》读书笔记 第四章 进程调度

    第四章进程调度 进程调度程序可看做在可运行太进程之间分配有限的处理器时间资源的内核子系统.调度程序是多任务操作系统的基础.通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的 ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (20) -----第四章 ASP.NET MVC中使用实体框架之在MVC中构建一个CRUD示例

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第四章  ASP.NET MVC中使用实体框架 ASP.NET是一个免费的Web框架 ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 4.2. 构建一个搜索查询 搜索数据是几乎所有应用的一个基本功能.它一般是动态的,因 ...

随机推荐

  1. 降维之奇异值分解(SVD)

    看了几篇关于奇异值分解(Singular Value Decomposition,SVD)的博客,大部分都是从坐标变换(线性变换)的角度来阐述,讲了一堆坐标变换的东西,整了一大堆图,试图“通俗易懂”地 ...

  2. pip制作离线安装包

    有的电脑无法连接外网(金融行业你懂的),上网下载包又下载不到 anaconda安装一个包(联网情况下)很简单 pip install python-docx 如何把python-docx做成离线包安装 ...

  3. web前端知识点

    一.CSS问题 1.flex布局 display:flex; 在父元素设置,子元素受弹性盒影响,默认排成一行,如果超出一行,按比例压缩 flex:1; 子元素设置,设置子元素如何分配父元素的空间,fl ...

  4. css布局 -双飞翼布局&圣杯布局

    一,双飞翼布局 左右两边固定,中间可以随着浏览器放大和缩小 <!DOCTYPE html> <html lang="en"> <head> &l ...

  5. spring源码 BeanFactory根接口

    /* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Vers ...

  6. 通过while循环一步步实现九九乘法表

    # 打印#做出@列的效果height = int(input("height: ")) #用户输入一个高度 num_height = heightwhile num_height ...

  7. c++ opencv显示对话框

    IplImage *pl = cvLoadImage("e:\\3.bmp", 1); cvNamedWindow("123", 1); cvShowImage ...

  8. dmesg 显示开机信息。

    功能说明:显示开机信息. 语 法:dmesg [-cn][-s <缓冲区大小>] 补充说明:kernel会将开机信息存储在ring buffer中.您若是开机时来不及查看信息,可利用dme ...

  9. SpringCloud学习之Sleuth服务链路跟踪(十二)

    一.为什么需要Spring Cloud Sleuth 微服务架构是一个分布式架构,它按业务划分服务单元,一个分布式系统往往有很多个服务单元.由于服务单元数量众多,业务的复杂性,如果出现了错误和异常,很 ...

  10. css div框加小箭头

    <div class="con">    <div class="arrow"> </div></div> .c ...