Cracking Digital VLSI Verification Interview 第四章

元气小坏坏 提交于 2020-02-08 16:18:52

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

Hardware Description Languages

Verilog

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

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

always  @(posedge clk)
    begin
        x = a|b;
        y = a&b;
        z = x|y;
    end 

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

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

always  @(posedge clk)
    begin
        x <= a|b;
        y <= a&b;
        z <= x|y;
    end 

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

1)
always @(posedge clk)
    begin
        B = A;
        C = B;
    end
2)
always @(posedge clk)
    begin
        B <= A;
        C <= B;
    end

1)一个;2)两个

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

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

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

always @(posedge clk)
    begin
        a = 0;
        a <=1;
        $display("a=%0b", a);
    end

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

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

always @(posedge clk)
    begin
        A<=B;
        B<=A;
    end

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

module test;
    int alpha,beta;
    initial  begin
        alpha = 4;
        beta = 3;
        beta <= beta + alpha;
        alpha <= alpha + beta;
        alpha = alpha - 1;
        $display("Alpha=%0d Beta=%0d", alpha,beta);
    end
endmodule

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

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

1)
initial begin
    a=0;
    b=1;
    c = #5 a+b;
end
2)
initial begin
    a=0;
    b=1;
    #5 c = a+b;
end 
initial begin
    a=0;
    #3 a=1;
end
  1. c=1
  2. c=2

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

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

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

bit a, b, c, d, e;
always @(a, b, c) begin
    e = a & b & c & d;
end

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

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

module mux31_2(inp0,inp1,inp2,sel0,sel1, outres);
    input inp0, inp1, inp2, sel0, sel1;
    output outres;
    assign outres = sel1 ? inp2 : (sel0 ? inp1 : inp0);
endmodule

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

always @(posedge clk or posedge reset)
    if (reset)
        X1 = 0; // reset
    else
        X1 = X2;

always @(posedge clk or posedge reset)
    if (reset)
        X2 = 1;// set
    else
        X2 = X1;

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

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

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

下面的代码为同步复位

always  @ (posedge clk) begin
    if (reset) begin
        ABC <=0;
    end
end 

下面的代码为异步复位

always  @ (posedge clk or posedge reset ) begin
    if (reset) begin
        ABC <=0;
    end
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触发器

always @ (posedge clk) begin
    if(reset) begin
        Q <= 0;
        Qbar <= 1;
    end else begin
        Q <= D;
        Qbar <= ~D;
    end
end

锁存器

always @ (D or Enable) begin
    if(Enable) begin
        Q <= D;
        Qbar <= ~D;
    end
end

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

首先设计状态机。

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

状态机如下

module seq_detector(z,x,clock,reset);
    output z;
    input x,clock;
    input reset; //active high
    reg [2:0] state,nextstate;
    parameter s0=3'b000,s1=3'b001,s2=3'b010,s3=3'b011,s4=3'b100; 
 
  always @ (posedge clock) begin
      if(reset) begin
          state <=s0;
          nextstate<=s0;
      end else begin
          state<=nextstate;
      end
  end
 
  always @ (x or state)
      case(state)
          s0:  if(x) nextstate=s1; else nextstate=s0;
          s1:  if(x) nextstate=s1; else nextstate=s2;
          s2:  if(x) nextstate=s3; else nextstate=s0;
          s3:  if(x) nextstate=s4; else nextstate=s2;
          s4:  if(x) nextstate=s1; else nextstate=s2;
      endcase
    
   always @ (x or state)
       case(state)
           s4: if(x) z=1'b0; else z=1'b1;
           s0,s1,s2,s3: z=1'b0;
       endcase
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)

module fibonacci(input clock, reset, input [7:0] nth_number, output [19:0] fibonacci_number, output number_ready);
    reg [19:0] previous_value, current_value;
    reg [7:0] internal_counter;
    reg number_ready_r;
 
    always @(posedge clock or posedge reset)  begin
        if(reset) begin
            previous_value <='d0; //1st Fibonacci Number
            current_value <='d1; //2nd Fibonacci Number
            internal_counter <='d1;
             number_ready_r <= 0;
        end else begin
            if (internal_counter == (nth_number-2)) begin
                number_ready_r <= 1;
             end else begin
                internal_counter <= internal_counter + 1;
                current_value <= current_value + previous_value;
                previous_value <= current_value;
                number_ready_r <= 0;
             end
        end
    end
 
  assign fibonacci_number = current_value;
  assign number_ready = number_ready_r
    
endmodule

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

module half_adder(input_0, input_1, sum, carry);
    input input_0, input_1;
    output sum, carry;
    assign sum = (input_0)^(input_1);
    assign carry = (input_0)&(input_1);
endmodule
 
module full_adder(input_0, input_1, input_2, sum, carry);
    input input_0, input_1, input_2;
    output sum, carry;
    reg sum_intermediate, carry_intermediate_0, carry_intermediate_1;
    half_adder ha1(input0,input1,sum_intermediate,carry_intermediate_0);
    half_adder ha2(sum_intermediate,input2,sum,carry_intermediate_1);
    assign carry = (carry_intermediate_0)|(carry_intermediate_1);
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的元素

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

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

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

typedef struct {
    bit [7:0] opcode;
    bit [23:0] addr;
} instruction_s;
instruction_s current_instruction;
current_instruction.addr='h100;

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

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

typedef  union {
    int  data;
    real  f_data;
}state_u;
state_u  reg_state;
reg_state.f_data = 'hFFFF_FFFF_FFFF_FFFF;
$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函数都不需要在存储器上创建数据包的副本。

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

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

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

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

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

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

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

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

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

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

logic[31:0] addr; //packed array of logic type
class  record_c;
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。

struct packed {
    int a;
    short int b;
    byte c;
} pack1_s;

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

struct {
    string  name;
    int  age;
    string parent;
} record_s

[186] 下面哪个是对的?

  1. function不能消耗仿真时间
  2. task不能消耗仿真时间

  3. 正确
  4. 错误

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

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

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

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

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

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

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

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

casez (irq)
    3'b1?? : int2 = 1'b1;
    3'b?1? : int1 = 1'b1;
    3'b??1 : int0 = 1'b1;
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代码中有什么错误?

task wait_packet;
    Packet packet;
    event packet_received;
    @packet_received;
    packet = new();
endtask
function void do_print();
    wait_packet();
    $display("packet received");
endfunction

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

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

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

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

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

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

class Statistics;
    Packet p1;
endclass
 
class Packet;  //full definition here
endclass

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

typedef Packet;  //forward declaration

class Statistics;
    Packet p1;
endclass

class Packet;  //full definition here
endclass

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

task gen_packet(Packet pkt);
    pkt = new();
    pkt.dest = 0xABCD;
endtask
 
Packet pkt;
initial begin
    gen_packet(pkt);
    $display(pkt.dest);
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的定义。

class StringList;
    class Node; // Nested class for a node in a linked list.
        string name;
        Node link;
    endclass
endclass 

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

[198] systemverilog中的interface是什么?

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

interface simple_bus; // Define the interface
    logic req, gnt;
    logic [7:0] addr, data;
    logic [1:0] mode;
    logic start, rdy;
endinterface: simple_bus

[199] 什么是modport?

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

interface arb_if(input bit clk);
    logic [1:0] grant, request;
    logic reset;
    modport TEST (output request, reset, input grant, clk);
    modport DUT (input  request, reset, clk, output grant);
    modport MONITOR (input request, grant, reset, clk);
endinterface

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

[200] interface是可综合的吗?

是可综合的。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!