Cracking Digital VLSI Verification Interview 第四章


歡迎關注個人公眾號摸魚范式,后台回復pdf獲得全文的pdf文件

)

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是可綜合的嗎?

是可綜合的。

[201] 什么是時鍾塊?在interface中使用時鍾塊有什么好處?

時鍾塊類似於modport,除了具備modport的信號方向指定,還能夠建模信號的時序行為。下面是一個時鍾塊的例子。

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

在上面的示例中,定義了一個名為sample_cb的時鍾塊,關聯的時鍾為clk。default關鍵字定義默認的時鍾偏斜,輸入為2ns,輸出為3ns。輸入偏斜定義了時鍾采樣在時鍾邊沿前多少個時間單位。 輸出偏斜定義了時鍾驅動在時鍾邊沿后的多少個時間單位。

時鍾塊只能在module或者interface中定義

[202] 下面兩種定義時鍾偏斜的方式有什么不同?

1)     input  #1 step req1; 
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] 根據下面的約束,哪一個選項是錯誤的?

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

將約束內容取交集,c可以取27-30之間的任意值。

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

unique約束會令一組成員兩兩各不相同。下面是一個示例。

class Test;
    rand byte a[5];
    rand byte b;
    constraint ab_cons { unique {b, a[0:5]}; }
endclass

[206] 如何選擇性的激活或者關閉一個類中的約束?

//<object>.constraint_mode(0) :: 關閉對象中的所有約束
//<constraint>.constraint_mode(0) :: 選擇性關閉某一個約束
class  ABC;
    rand int length;
    rand  byte SA;
    constraint c_length { length inside [1:64];}
    constraint c_sa {SA inside [1:16];}
endclass ABC abc = new();
abc.constraint_mode(0) // 關閉所有約束
abc.c_length.constraint_mode(0) // 只關閉約束c_length

[207] 現有下面的一個類,如何生成addr大於200的Packet對象?

class  Packet;
    rand bit[31:0] addr;
    constraint c_addr { addr inside [0:100];}
endclass

大於200與內置約束沖突,所以需要先關閉內置約束,然后通過內聯約束進行隨機化

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

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

這顯示systemverilog內建的回調函數。在調用randomize之前會自動調用pre_randomize函數,之后會自動調用post_randomize函數。可以通過定義這兩個函數,完成在隨機化之前或者之后進行某些操作。

[209] 編寫一個約束,為下面對象中的動態數組生成約束,使得每個元素都小於10,數組大小也小於10

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

[210] 編寫約束,創建隨機整數數組,使數組大小在10-16之間,並且數組按照降序排列

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

[211] 如何對一個生成一個隨機動態數組,並且元素兩兩各不相同?參考下面的代碼

class TestClass;
    rand bit[3:0] my_array[];//dynamic array of bit[3:0]
endclass
  1. 使用unique進行約束
constraint c_rand_array_uniq {
    my_array.size == 6;  //or any size constraint
    unique {my_array};   //unique array values
}
  1. 不適用unique進行約束,使用post_randomize回調函數完成
constraint c_rand_array_inc {
    my_array.size == 6 ;// or any size constraint
    foreach (my_array[i])
        if(i >0)
            my_array[i] > my_array[i-1];
}
function post_randomize();
    my_array.shuffle();
endfunction

[212] “fork - join”, “fork - join_any” 和“fork - join_none”之間有什么區別?

systemverilog支持三種類型的動態進程,可以在運行時創建,並作為獨立線程執行。

  1. fork-join:使用“ fork .. join”創建的進程作為單獨的線程運行,但是父進程停滯不前,直到所有進程全部執行完。 如果我們看下面的示例:共有三個進程task1,task2和task3,它們將並行運行,並且只有在這三個進程全部完成之后,join語句之后的$display()才會執行。
initial begin
    fork
        task1; // Process 1
        task2; // Process 2 
        task3; // Process 3   
    join  $display(“All tasks finished”);
end
  1. fork-join_any:使用“ fork…join_any”創建的進程作為單獨的進程運行,但是父進程會在任何一個子進程完成后繼續。其余進程和父進程可以並行運行。 如果我們看下面的示例:有三個進程-task1,task2和task3將並行運行。 當task1 / task2 / task3之一完成時,join_any將完成,並在其他線程可能仍在運行時執行$ display()。
initial begin
    fork
        task1; // Process 1 
        task2; // Process 2  
        task3; // Process 3 
    join_any 
    $display(“Any one of task1/2/3 finished”);
end
  1. fork-join_none:使用“ fork…join_none”創建的進程將作為單獨的進程運行,但是父進程不會停滯並且也將並行進行。 參考以下示例,其中有三個進程-task1,task2和task3,它們將與父進程並行運行。
initial begin
    fork
        task1; // Process 1 
        task2; // Process 2  
        task3; // Process 3 
    join_none 
    $display(“All tasks launched and running”);
end

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

在使用“ fork..join_none”或“ fork..join_any”時,有時候需要父進程和子進程進行同步,這可以通過wait fork完成。

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

類似的,disable fork可以提前將子進程停止。

initial begin
    fork   
        task1; // Process 1 
        task2; // Process 2  
    join_any 
    $display(“One of task1/2 completed ”);
    disable  fork;
    $display(“All other tasks disable now”); 
end

[214] 硬約束和軟約束有什么區別?

systemverilog中編寫的常規約束為硬約束,對成員的隨機化必須始終滿足約束,如果約束無解,則會導致錯誤。如果將約束定義為軟約束,在沒有外部約束的條件下,和硬約束一樣,外部約束的優先級比軟約束高。軟約束通常用於指定隨機變量的默認值和分布,並且可以被外部特定的約束覆蓋。

class Packet;
    rand int length;
    constraint length_default_c {
        soft length inside {32,1024};
    }
endclass
 
Packet p = new();
p.randomize() with { length == 1512; }

上例中,如果約束沒有定義為軟約束,則隨機化會失敗。

[215] 下面每個線程的輸出是什么?

initial begin
 for (int j=0; j<3; j++) begin
     fork
         automatic int result;
         begin
             result= j*j;
             $display("Thread=%0d value=%0d", j, value);
         end
     join_none
     wait
     fork;
 end
end

由於“ j”不是每個線程的動態變量,因此在生成每個線程后它會不斷遞增,並且當所有線程開始執行時,每個線程將看到的j都是3。因此,每個線程都將輸出9。 如果每個線程打算使用不同的值,則應將“ j”的值復制到一個自動變量,如下所示:

automatic int  k = j;
begin
    result = k*k;
end

[216] 下面的代碼會產生多少個並行的線程?

fork
    for (int i=0; i < 10; i++ ) begin 
        ABC();
    end
join

for循環在fork join內,所以只有一個線程。

[217] 下面的約束有什么錯誤?

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

約束表達式中,至多只有一個關系運算符(<,<=,==,>=或>)。如果要實現變量的順序約束,需要多個表達式。

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

[218] systemverilog中的虛方法和純虛方法的區別是?

在類中將方法定義為虛方法,則在派生類中可以重寫這個方法。基類可以定義具有實現或不實現的虛函數,在派生類中也可以選擇覆蓋或不覆蓋。而純虛函數只具備函數聲明,沒有具體實現,在派生類中必須有具體實現。純虛函數常用在抽象類中使用,下面是一個示例。

virtual class BasePacket;  // No implementation
    pure virtual function integer send(bit[31:0] data);
endclass
 
class EtherPacket extends BasePacket;
    virtual function integer send(bit[31:0] data);
        // body of the function
        // that implements the send
        ….… 
    endfunction
endclass

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

Semaphores是用於控制對公用資源的機制。Semaphores可以視為在創建時具有多個鑰匙的存儲池,使用Semaphores訪問資源時,首先要取得要是,然后才能夠繼續執行。通過這種機制,可以確保沒有要是的進程一直等待到獲取鑰匙。Semaphores通常用於相互排斥,對公用資源進行訪問控制以及簡單同步。下面是簡單的Semaphores的創建方法。

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

get()和try_get()分別是阻塞和非阻塞的獲取鑰匙的方法,put()用於返還鑰匙。

[220] 下面兩個約束有什么不同?

1) 
class ABSolveBefore; 
    rand bit A;
    rand bit [1:0] B;
    constraint c_ab {    (A==0) -> B==0;    solve A before B;  } 
endclass 
2)      
class ABSolveBefore; 
    rand bit A; 
    rand bit [1:0] B; 
    constraint c_ab {    (A==0) -> B==0;    solve B before A;  }
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的聲明與創建的示例:

mailbox mbxRcv; 
mbxRcv = new(); 

將數據存入mailbox中可以使用put(阻塞)和peek(非阻塞)實現,從mailbox中取出數據可以使用get(阻塞)和try_get(非阻塞)方法,查詢mailbox中的數據數量可以使用num()方法。

[222] 有限容量和無限容量的mailbox有什么區別?如何創建?

有限容量的mailbox是指郵箱在創建時就指定容量。

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

無限容量的mailbox是指郵箱再創建時不指定容量。

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

有限容量的mailbox如果存滿,線程將無法繼續存入數據,只到mailbox有空間。

[223] 什么是systemverilog中的event?如何觸發event?

event類型的變量不用於存儲數據,用於線程同步。可以使用"->"顯式觸發事件,而線程可以通過"@"來等待事件的觸發,阻斷線程只到事件被觸發。event為兩個或者多個同時運行的進程的同步提供強大而有效的手段。

下面的示例中,兩個線程通過event進行同步。一旦發送請求,send_req()將會觸發一個事件,而receive_response()檢測到事件被觸發以后就會繼續執行任務。

module test; 
    event req_send;
 
    initial begin
        fork
            send_req(); 
            receive_response);
        join 
    end

    task send_req();    //create and send a req
        -> req_send; //trigger event
    endtask

    task receive_response(); 
        @req_send; //wait until a send event is triggered 
        //collect response 
    endtask 
endmodule

[224] 如何合並兩個event?

可以指將將一個event變量賦值給另一個event變量,此時兩個event變量都指向同一個同步對象,可以認為兩者合並。

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

std::randomize()是作用域隨機化函數,無需定義類或者實例化類對象僅能對當前作用域中的數據進行隨機化。如果某些需要隨機化的變量不是類的成員,則需要使用std::randomize()。下面是一個示例。

module stim; 
    bit [15:0] addr; 
    bit [31:0] data;
    function bit gen_stim(); 
        bit success, rd_wr; 
        success = std::randomize(addr, data, rd_wr);  
        return rd_wr ;
    endfunction
    … 
endmodule

std::randomize()和類的randomize()具有相似的功能,也可以使用約束,下面是一個通過with添加約束的示例。

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

[226] 在派生類中可以覆蓋基類中的約束嘛?如果可以,如何實現?

可以通過使用相同的約束名稱在派生類中重寫基類定義的約束。下面是一個示例

class Base; 
    rand int a ; 
    rand int b; 
    constraint  c_a_b_const {a < b;} 
endclass
 
class  Derived extends Base; 
    constraint c_a_b_const {a > b;}
endclass

[227] 下面的systemverilog代碼的調用有什么問題?

function int count_ones ( ref bit [9:0] vec ); 
    for( count_ones = 0; vec != 0; vec = vec >> 1 )
        begin
            count_ones += vec & 1'b1; 
        end
endfunction

constraint C1 { length == count_ones( myvec ) ; }

在約束中,不允許調用方向為ref的函數,除非使用“const ref”,這保證函數在內部不會修改參數。

[228] 下面兩個派生類有什么不同?

class Base; 
    virtual function printA(); 
    endfunction
endclass
1)     
class Derived extends Base; 
    function printA(); 
        //new print implementation 
    endfunction 
endclass 
2)
class Derived extends Base; 
    virtual function printA(); 
        //new print implementation
    endfunction
endclass

兩者並沒有區別,在基類中如果定義了virtual關鍵字,那么派生類也會繼承該屬性,無論有沒有顯式的二次聲明。

[229] 找出下面代碼中的問題(如果有的話)

class Packet;
    bit [31:0] addr;
endclass

class ErrPacket extends Packet;
    bit err;
endclass
 
module Test;
    initial begin
        Packet p;
        ErrPacket  ep;
        ep = new(); 
        p = ep;
        $display("packet addr=%h err=%b", p.addr, p.err);
    end
endmodule

沒有問題,基類句柄可以指向派生類對象,但是反過來是不允許的。

[230] 現有下面兩個類,請問在示例代碼中compute_crc函數的調用順序是?

class Packet; //Base Class
    rand bit [31:0] src, dst, data[8]; // Variables
    bit [31:0] crc;
    virtual function void compute_crc;
        crc = src ^ dst ^ data.xor;
    endfunction
endclass : Packet
 
class BadPacket extends Packet;  //Derived class
    rand bit bad_crc;
    virtual function void compute_crc;  //overriding definition
        super.compute_crc();  // Compute good CRC
        if (bad_crc) crc = ~crc; // Corrupt the CRC bits
    endfunction
endclass : BadPacket

示例

Packet pkt;
BadPacket  badPkt;
initial begin
    pkt  = new; 
    pkt.compute_crc; // 1) Which of compute_crc() gets called? 
    badPkt = new;
    badPkt.compute_crc; // 2) Which of compute_crc() gets called?
    pkt = badPkt; // Base handle points to ext obj
    pkt.compute_crc; // 3) Which of compute_crc() gets called ?
end
  1. 調用了基類的compute_crc
  2. 調用了派生類的compute_crc
  3. 調用了派生類的compute_crc,雖然使用的是基類的句柄,但是方法定義為虛方法,所以要根據對象的類型進行調用

[231] 下面兩種代碼風格哪種更加好?為什么?

1)
for (i=0; i < length*count; i++) begin
    a[i] = b[i];
end
2) 
l_end = length * count;
for (i=0; i < l_end; i++) begin
    a[i] = b[i]
end

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

[232] 下面的代碼有什么錯誤?

class ABC;
    local int var;
endclass

class DEF extends ABC;
    function new();
        var = 10;
    endfunction
endclass

變量var具備local關鍵字,在派生類中是不可用的。

[233] 什么是虛接口,何時使用它?

虛接口是指向實際結構的變量。他在類中用於提供接口的連接點,通過虛接口可以訪問接口中的信號。在下面的示例中,接口bus_if將多個信號整合起來。然后,BusTransactor類中定義了這一接口類型的虛接口,這個虛接口用於訪問來自this.b_if的所有驅動或檢測。實例化物理接口以后,通過構造函數將句柄傳遞給BusTransactor類。

interface bus_if; // A bus interface
    logic req, grant;
    logic [7:0] addr, data;
endinterface
 
class BusTransactor; // Bus transactor class
    virtual bus_if bus; // virtual interface of type bus_if
    function new( virtual bus_if b_if );
        bus = b_if; // initialize the virtual interface
    endfunction
    
    task request(); // request the bus
        bus.req <= 1'b1;
    endtask
    
    task wait_for_bus(); // wait for the bus to be granted
        @(posedge bus.grant);
    endtask
endclass
 
module top;
    bus_if bif(); // instantiate interfaces, connect signals etc
    initial begin
        BusTransactor xactor;
        xactor = new(bif);   //pass interface to constructor
    end
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\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM