Thursday, April 25, 2019

[Bài 12][ UVM ] Cấu trúc cơ bản của một môi trường UVM

1. Transaction.

class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction)

[Khai báo các biến giao tiếp với DUT ở đây]

[Khai báo các constraint cho các biến bên trên ở đây]

function new (string name = "");
    super.new(name);
  endfunction

endclass: my_transaction

Như vậy với transaction ta chỉ cần khai báo các biến giao tiếp với DUT và contrainst của nó.

2. Sequence.

class my_sequence extends uvm_sequence#(my_transaction);

  `uvm_object_utils(my_sequence)
`uvm_declare_p_sequencer (my_sequencer)


  function new (string name = "");
    super.new(name);
  endfunction
task pre_body ();
[Code kiểm tra tín hiệu cờ]
endtask

  task body;
[Tạo tín hiệu random ở đây]
  endtask: body

task post_body();
[Code kiểm tra tín hiệu cờ sau “task body”]
endtask

endclass: my_sequence

Với bộ sequence ta cần code trong task body, pre_body, post_body.
Trong đó task pre_body và post_body là không bắt buộc. Thậm chí nếu bạn sử dụng macros uvm_do* trong “task body” thì “pre_body” và “post_body” là không cần thiết.  Trong thực tế người verify sẽ làm việc nhiều với sequence. Nó thật ra là công việc tạo test pattern trong verify.

3. Driver.

class my_driver extends uvm_driver #(my_transaction);

  `uvm_component_utils(my_driver)

  virtual dut_if dut_vif;

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction

  function void build_phase(uvm_phase phase);
[Add code của bạn ở đây]
  endfunction 

  task run_phase(uvm_phase phase);
[Add code của bạn ở đây]
  endtask

endclass: my_driver

4. Monitor.

class my_monitor extends uvm_monitor;
   `uvm_component_utils (my_monitor)

   virtual dut_if   vif;

   uvm_analysis_port #(my_data)   mon_analysis_port;

   function new (string name, uvm_component parent= null);
      super.new (name, parent);
   endfunction


   virtual function void build_phase (uvm_phase phase);
     [Thêm code tại đây]
   endfunction

   virtual task run_phase (uvm_phase phase);
      [Thêm code tại đây]
   endtask

endclass

5. Scoreboard.

class my_scoreboard extends uvm_scoreboard;
  `uvm_component_utils (my_scoreboard)

  function new (string name = "my_scoreboard", uvm_component parent);
    super.new (name, parent);
  endfunction

  uvm_analysis_imp #(apb_pkt, my_scoreboard) apb_export;

  function void build_phase (uvm_phase phase);
[Thêm code bạn ở đây]
  endfunction

  virtual function void write (apb_pkt data);
[In dữ liệu ra để quan sát]
  endfunction

  virtual task run_phase (uvm_phase phase);
    [Nhận data và gòi hàm so sánh kết quả]
  endtask

  virtual function void check_phase (uvm_phase phase);
    [So sánh kết quả output từ DUT và kết quả dự đoán]
  endfunction
endclass


6. Agent.

class my_agent extends uvm_agent;
      `uvm_component_utils (my_agent)

      my_driver                  m_drv0;
      my_monitor                 m_mon0;
      uvm_sequencer #(my_data)   m_seqr0;
      agent_cfg                  m_agt_cfg;

      function new (string name = "my_agent", uvm_component parent=null);
         super.new (name, parent);
      endfunction

      virtual function void build_phase (uvm_phase phase);
[Thêm code tại đây]
      endfunction



      virtual function void connect_phase (uvm_phase phase);
      [Thêm code tại đây để connect 3 thành phần driver, sequencer, monitor lại với nhau]
      endfunction
   endclass

[Bài 11][ UVM ] Tìm hiểu về UVM package


Tôi nghĩ rằng hầu hết chúng ta đều nhìn thấy một vài dòng code sau trong môi trường UVM rồi đúng không.

`include "uvm_macros.svh"
  import uvm_pkg::*;

Thế nhưng bạn có biết chức năng thực sự của các package này là gì không ?
Trước khi phân tích về chức năng chính của các package chúng ta sẽ tìm hiểu vị trí các định nghĩa class được thực hiện.

Tôi có hai giả thiết, Thứ nhất bạn phải định nghĩa class bên trong một module như bên dưới:

module A;
   class B;
      ….
   endclass: B
   class C;
      …
   endclass: C
   …
endmodule

Đối với cách này, bạn không cần package, tuy nhiên nếu số lượng class lớn và phức tạp thì việc viết như thế này sẽ làm ảnh hưởng đến khả năng quản lí các class.

Cách thứ hai là bạn định nghĩa các class bên ngoài module nhưng nằm trong cùng 1 file:

class B;
   ….
endclass: B

module A;
   ….
endmodule


Việc này cũng được chắp nhận tuy nhiên có một vài lưu ý nhỏ về vị trí định nghĩa các class, Ta xét ví dụ bên dưới:

  class test1;
     logic [1:0] sw;
     test tes;
     function new ();
         sw[1:0] = 2'b00;
     endfunction
  task show;
        sw = 2'b11;
        $display("sw = %b", sw);
  endtask
endclass: test1


class test;
     rand logic [1:0];
endclass: test

module tb;
  initial begin
    test1 te;
    te = new();
    te.show;
  end
endmodule

Trường hợp này sẽ báo lỗi vì class test1 được biên dịch trước class test nhưng trong class test1 có 1 biến được khai báo kiểu test. Như vậy khi khai báo các class cần phải theo thứ tự nghĩa là có một class khai báo 1 biến có kiểu dữ liệu là 1 class khác thì class khác đó phải được compile trước.
Trong môi trường uvm các class rất phức tạp vì vậy chúng sẽ được chia ra thành các file riêng lẽ mà không nằm trong cùng 1 file đễ dễ quản lí. Tối lấy ví dụ một môi trường uvm cho bộ cộng 4 bit sẽ có các thành phần như bên dưới:

add4bit_agent.sv
add4bit_config.sv
add4bit_driver.sv
add4bit_env.sv
add4bit_if.sv
add4bit_monitor.sv
add4bit_scoreboard.sv
add4bit_sequense.sv
add4bit_tb_top.sv
add4bit_test.sv

Bạn có hai cách để gọi các thành phần này trong module testbench top, để biên dịch chúng.
Thêm các dòng code sau:

    `include "add4bit_sequense.sv"
    `include "add4bit_monitor.sv"
    `include "add4bit_driver.sv"
    `include "add4bit_agent.sv"
    `include "add4bit_scoreboard.sv"
    `include "add4bit_config.sv"
    `include "add4bit_env.sv"
    `include "add4bit_test.sv"

Điều này cũng được đấy. Tuy nhiên cứ mỗi lần gọi các class này bạn lại cứ tốn nhiều dòng code để include chúng. Đôi khi bạn include thiếu, include không đúng thứ tự ưu tiên của class dẫn đến error xảy ra trong quá trình compile. Từ đó khái niệm package sẽ giúp bạn giải quyết vấn đề này.
Cấu trúc của một package:

package add4bit_pkg;
    `include "add4bit_sequense.sv"
      ………….
    `include "add4bit_test.sv"
endpackage: add4bit_pkg


Khi bạn muốn gọi các file định nghĩa class. Bạn chỉ cần 2 dòng code sau là có thể định nghĩa được chúng.

`include “tên_file_package”
 import tên_package::*;

Việc này ngắn hơn, đơn giản hơn các cách trước và tránh được các lỗi không đáng có khiến bạn mất thời gian để debug.
Một cách định nghĩa khác bạn có thể thực hiện là định nghĩa các class  trực tiếp trong cấu trúc package như bên dưới:

package A;
   class C;
      …
   endclass: C
   class B;
      …
   endclass: B
   ….
endpackage: A


Cách gọi package thì tương tự như 2 dòng lệnh trên.
Đến đây thì bạn đã rõ về cách sử dụng package rồi phải không nào.
Dưới đây là một số cấu trúc package bạn có thể tham khảo:

//Author: SCD/BUS/TrongTran
//Date  : 03/29/2019
`ifndef _UVM_PKG_
`define _UVM_PKG_
`include "uvm_macros.svh"
package add4bit_pkg;
    import uvm_pkg::*;
    `include "add4bit_sequense.sv"
    `include "add4bit_monitor.sv"
    `include "add4bit_driver.sv"
    `include "add4bit_agent.sv"
    `include "add4bit_scoreboard.sv"
    `include "add4bit_config.sv"
    `include "add4bit_env.sv"
    `include "add4bit_test.sv"
endpackage: add4bit_pkg
`endif

Một mẫu cho cách khai báo thứ hai là:


package my_testbench_pkg;
  import uvm_pkg::*;
  // The UVM sequence, transaction item, and driver are in these files:
  `include "my_sequence.svh"
  `include "my_driver.svh"
  // The agent contains sequencer, driver, and monitor (not included)
  class my_agent extends uvm_agent;
    `uvm_component_utils(my_agent)
    my_driver driver;
    uvm_sequencer#(my_transaction) sequencer;
    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
      driver = my_driver ::type_id::create("driver", this);
      sequencer =
        uvm_sequencer#(my_transaction)::type_id::create("sequencer", this);
    endfunction   
    // In UVM connect phase, we connect the sequencer to the driver.
    function void connect_phase(uvm_phase phase);
      driver.seq_item_port.connect(sequencer.seq_item_export);
    endfunction
   
    task run_phase(uvm_phase phase);
      // We raise objection to keep the test from completing
      phase.raise_objection(this);
      begin
        my_sequence seq;
        seq = my_sequence::type_id::create("seq");
        seq.start(sequencer);
      end
      // We drop objection to allow the test to complete
      phase.drop_objection(this);
    endtask

  endclass
 
  class my_env extends uvm_env;
    `uvm_component_utils(my_env)
   
    my_agent agent;

    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
   
    function void build_phase(uvm_phase phase);
      agent = my_agent::type_id::create("agent", this);
    endfunction

  endclass
 
  class my_test extends uvm_test;
    `uvm_component_utils(my_test)
   
    my_env env;

    function new(string name, uvm_component parent);
      super.new(name, parent);
    endfunction
   
    function void build_phase(uvm_phase phase);
      env = my_env::type_id::create("env", this);
    endfunction
   
    task run_phase(uvm_phase phase);
      // We raise objection to keep the test from completing
      phase.raise_objection(this);
      #10;
      `uvm_warning("", "Hello World!")
      // We drop objection to allow the test to complete
      phase.drop_objection(this);
    endtask

  endclass
 
endpackage


Wednesday, April 24, 2019

[Verification] Tìm hiểu về combine test (CT), unit test (UT) và system test (ST) trong thiết kế SoC


1. Combine test.

Không sử dụng model, Môi trường combine test thường rất lớn, Bao gồm toàn bộ các rtl (Verilog hoặc system Verilog) được kết nối lại với nhau. Combine test thường cho việc kiểm tra đọc/ghi thanh ghi, kiểm tra connection giữa các thành phần.
Chúng ta thường tạo pattern bằng cách viết ngôn ngữ assembly để CPU có thể access đến các thanh ghi cụ thể (Thông qua địa chỉ của các thanh ghi này).


2. Unit test.

Dùng để kiểm tra tính đúng đắn của function. Môi trường unit test thường rất nhỏ vì chỉ test cho một function nhất định. Chúng ta thường dùng thư viện uvm (build môi trường random test) để tạo môi trường.
Nó không cần toàn bộ rtl của hệ thống mà các chỉ cần một DUT (Design under test) cụ thể cần được test. Sau đó , các connection đến nó sẽ được giả lập bởi các model (Tùy theo chuẩn giao tiếp mà bạn đang dùng). Model là một code được viết bằng ngôn ngữ Verilog hoặc system Verilog được cung cấp bởi cadence hoặc synopsys. Một model sẽ có đầy đủ các input/output cần connect đến DUT nhầm thực hiện chức năng nhận data và trả data (giả lập như một rtl hoàn chỉnh).
Với môi trường unit test, kết quả được đánh giá là “PASS” nếu như function của bạn chạy đúng.

Ví dụ:

Bạn có 1 DUT thực hiện tính năng cộng giữa hai số. Bạn đánh giá kết quả là “PASS” khi bạn đưa input vào và kết quả gửi ra đúng như mong đợi (Kết quả mong đợi dựa vào spec).
Người chạy unit test chỉ cần tạo pattern. Partern ở đây không giống với pattern của combine test vì nó là đưa vào các input cụ thể hoặc các tín hiệu kích thích ngẫu nhiên. Từ các tín hiệu đó chúng ta sẽ quan sát kết quả đầu ra. Nếu đúng với kết quả chúng ta mong đợi thì đó là “PASS”. Tôi lấy ví dụ ta có bộ cộng như bên dưới:

Nhiệm vụ của ta là tạo ra 2 tín hiệu input chứa tất cả các trường hợp có thể có. Đưa vào DUT và quan sát kết quả đầu ra.
Giả sử nếu ta đưa vào a = 2 và b = 3 các trường hợp có thể xẩy ra:

  • Kết quả đầu ra c= 4 => Fail vì 2+3 # 4.
  • Kết quả đầu ra c = 5 => Pass vì 2+3 = 5.
  • Quá trình mô phỏng treo.


Bên cạnh việc kiểm tra kết quả ngõ ra, Bạn còn phải đảm bảo rằng bạn đưa vào đủ các trường hợp có thể có của a và b. Điều này được tính toán bởi quá trình lấy coverage. Nếu coverage đạt trên bao nhiêu phần % sẽ được chấp nhận. Vậy tại sao chúng ta cần coverage. Đơn giản là vì nếu bạn sử dụng môi trường random test các tín hiệu a và b sẽ được tạo ra một cách ngẫu nhiên. Khi đó bạn không thể đảm bảo rằng nó đưa vào đủ hết các trường hợp. Việc tính toán coverage là cần thiết để biết chắc bạn đã “PASS” hết tất cả các trường hợp có thể xảy ra.

3. System test.

System test là cách để bạn kiểm tra hiệu xuất làm việc của toàn bộ hệ thống.
Tôi lấy ví dụ với unit test bạn chỉ cần kết quả gửi ra đúng (Hay nói đúng hơn miễn sao function của bạn đúng) là bạn có thể đánh giá kết quả là “PASS”. Tuy nhiên, thực tế có một số trường hợp đặc biệt bạn không thể đoán trước được. Tôi lấy ví dụ master A gửi quá nhiều transaction đến slave B. Đôi khi slave B không thể xử lí kịp và trả về kết quả . Điều này dẫn đến kết quả dữ liệu bạn nhận được khi read/write có thể bị mất, hoặc hệ thống bị treo…
Để kiểm tra cho việc này chúng ta cần phải có một môi trường để kiểm tra nó. Và system test ra đời.
Một lưu ý quan trọng là system test rất khó debug vì vậy nó chỉ nên được chạy tại các giai đoạn sau cùng.
Nghĩa là khi combine test (CT) và unit (UT) đã “PASS”. Việc đảm bảo function đúng để tránh lãng phí thời gian debug cho system test.
Về cơ bản System test là việc kiểm tra các bus, hay các interconnection có đáp ứng được các điều kiện hoạt động hay không.
Có đảm bảo performance hay không. Vì hầu như các master hay slave đều được thay thế bằng model.
Nhiệm vụ của người chạy là tạo các patern (Hay sequence) bằng cách thiết lập các thông số cho model. Ví dụ bandwidth là bao nhiêu,data width…v…v
(Còn tiếp).

Monday, April 22, 2019

[System verilog][ Bài 3] Tìm hiểu về mảng đóng gói và mảng không đóng gói (Packed array và unpacked array)


Mảng đóng gói (Hay còn gọi là packed array) và mảng không đóng gói ( Hay còn gọi là unpacked array).
Trong bài này chúng ta sẽ cùng tìm hiểu về mảng packed và mảng unpacked.
Đây là 2 kiểu mảng được sử sụng khá phổ biến nhưng chúng ta có thể chưa nhận ra nó. Tôi lấy ví dụ khi bạn định nghĩa kiểu dữ liệu như bên dưới:
bit [3:0] var;
logic var [3:0];
Bạn đang sử dụng mảng packed và unpacked rồi đấy.
Vậy tại sao ta gọi đó là 1 mảng mà không gọi là 1 biến.
Đầu tiên bạn có thể xem “logic” hay “bit” là các kiểu dữ liệu 1 bit và như vậy với biến var ta đang có 4 phần tử 1 bit kết hợp lại. Điều này có thể xem xét như là mảng 1 chiều có 4 phần tử, mỗi phần tử 1 bit.
Bây giờ ta sẽ đi vào phân tích hai loại mảng trên để thấy được điểm khác biệt giữa chúng.

1.Mảng đóng gói (Hay còn gọi là  packed array).

Kích thước mảng được khai báo trước tên biến. Packed array có thể được xem xét như một vector.

Ví dụ:
bit [7:0] led;
bit [3:0] sw;

Để xác định kích thước của mảng ta dùng hàm $size(tên mảng).

Ta xét một số ví dụ bên dưới:

module tb;
  bit [3:0]sw;
  initial begin
    sw = 4'b1011;
    for (int i = 0; i < $size(sw); i++) begin
      $display ("sw[%0d] = %b", i, sw[i]);
    end
  end
endmodule

Ta xem xét ví dụ như bên trên và dưới đây là kết quả:
ncsim> run
sw[0] = 1
sw[1] = 1
sw[2] = 0
sw[3] = 1

Thêm một ví dụ khác về packed array hai chiều:

module tb;
  bit [1:0][3:0]   sw;  
  initial begin
    sw = 8'hAB;
    $display ("sw = 0x%0h", sw);
    for (int i = 0; i < $size(sw); i++) begin
      $display ("sw[%0d] = %b (0x%0h)", i, sw[i], sw[i]);
    end
  end
endmodule

Và đây là kết quả:

ncsim> run
sw = 0xab
sw[0] = 1011 (0xb)
sw[1] = 1010 (0xa)




 Dưới đây là ví dụ về việc gán từng phần tử trong mảng:

module tb;
  bit [1:0][3:0]   sw;  

  initial begin
    sw[0] = 4'hB;
    sw[1] = 4'hA;

    $display ("sw = 0x%0h", sw);

    for (int i = 0; i < $size(sw); i++) begin
      $display ("sw[%0d] = %b (0x%0h)", i, sw[i], sw[i]);
      for (int j = 0; j < $size(sw[i]); j++) begin
        $display ("sw[%0d][%0d] = %b (0x%0h)", i ,j , sw[i][j], sw[i][j]);
      end
    end
  end
endmodule

Ta có được kết quả như bên dưới:

ncsim> run
sw = 0xab
sw[0] = 1011 (0xb)
sw[0][0] = 1 (0x1)
sw[0][1] = 1 (0x1)
sw[0][2] = 0 (0x0)
sw[0][3] = 1 (0x1)
sw[1] = 1010 (0xa)
sw[1][0] = 0 (0x0)
sw[1][1] = 1 (0x1)
sw[1][2] = 0 (0x0)
sw[1][3] = 1 (0x1)

2.Mảng không đóng gói (Hay còn gọi là unpacked array)

Bây giờ chúng ta sẽ cùng nhau tìm hiểu về unpacked array.
Khác với packed array, Unpacked array có kích thước được định nghĩa sau tên biến.
Ví dụ:
logic var [3:0];
bit sw [1:0];
Unpacked array có thể là fixed-size arrays, dynamic arrays, associative arrays hoặc là queues.

Ta xét một số ví dụ như bên dưới để có thể thấy rõ sự khác biệt giữa packed array và unpacked array.

module tb;
  byte   sw [4];    
  initial begin
    foreach (sw[i]) begin
      sw[i] = $random;
      $display ("Assign 0x%h to index %d", sw[i], i);
    end
    $display ("sw = %p", sw);
  end
endmodule

Ta có kết quả như bên dưới:

ncsim> run
Assign 0x24 to index 0
Assign 0x81 to index 1
Assign 0x09 to index 2
Assign 0x63 to index 3
sw = '{'h24, 'h81, 'h9, 'h63}

Như vậy có thể thấy, unpacked array là một mảng thật sự. Các phần tử tách biệt nhau.

module tb;
  byte   sw [2][2];
  initial begin
    foreach (sw[i]) begin
      foreach (sw[i][j]) begin
            sw[i][j] = $random;
        $display ("sw[%d][%d] = 0x%h", i, j, sw[i][j]);
      end
    end
    $display ("sw = %p", sw);
  end
endmodule

Kết quả in ra màn hình của chương trình trên:

ncsim> run
sw[          0][          0] = 0x24
sw[          0][          1] = 0x81
sw[          1][          0] = 0x09
sw[          1][          1] = 0x63
sw = '{'{'h24, 'h81}, '{'h9, 'h63}}

Thursday, April 18, 2019

[Synthesis][ Bài 1] Giới thiệu cơ bản về việc thay thế FF (flip flop) hậu tổng hợp (Synthesis)

Tìm hiểu về việc thay thế FF (Flip flop) trong quá trình hậu synthesis.
Synthesis là một bước khá quan trọng và cần rất nhiều nỗ lực để kiểm tra, test (timing, power…). Trong bài này tôi sẽ trình bày cho các bạn về việc thay thế các FF trên gate nestlist (Sau khi synthesis).

I. Meta FF

Đây là một công việc của Backend team. Nhiệm vụ của họ là thay thế các FF đã được liệt kê trong một danh sách được cung cấp bởi designer hoặc người synthesis (Frontend team) trên gate nestlist bởi 1 flip flop thuộc loại khác.
Với phương pháp này ta thấy thông thường nhất là việc thay các flip flop trong mạch đồng bộ tín hiệu async  2 flip flop.
Ta có sơ đồ mạch như hình bên dưới:

Hình 1. Mạch đồng bộ tín hiệu async 2 flip flop và vị trí thay thế Meta FF 

Như vậy ta có cấu trúc FF1 -> FF2. Nhiệm vụ của designer hay nói đúng hơn là người thực hiện quá trình tổng hợp (synthesis) là sẽ liệt kê ra tên của FF1 trong mạch đồng bộ tín hiệu async. Có nhiều cách để liệt kê, ở đây tôi đề cập đến 2 cách: 

Cách 1: Là cách phổ biến nhất, đó là dựa vào report của quá trình STAcheck. Đối với mạch async, STAcheck sẽ báo ra các lỗi để người dùng confirm. Đồng thời tên của FF1 cũng sẽ được liệt kê ra trong report. Nhiệm vụ của người synthesis chỉ là thu thập chúng, tạo file và gửi cho Backend team.

Cách 2: Dành cho những người hiểu rõ về thiết kế của mạch. Người thu thập thông tin cần biết được các port input nào hoạt động với clock async (Nghĩa là clock khác với clock của module). Từ các port đó ta cần lần theo các connection trong gate nestlist để xác định được cập flip flop nào có chức năng đồng bộ tín hiệu async. Với cách làm này, chỉ có thể áp dụng cho module có rất ít port async và module nhỏ. Với một module lớn và có nhiều port async bạn sẽ phải tốn thời gian để liệt kê đầy đủ thông tin.
Vậy mục đích của việc thay thế này là gì. Đó là các FF1 sẽ được thay thế bởi các flip flop có tuổi thọ lâu hơn, tốt hơn. Hãy tưởng tượng khi FF1 bị die thì mạch chúng ta sẽ làm việc không đúng nữa. Làm việc với mạch async mang lại rất nhiều rủi ro nếu ta không kiểm tra đầy đủ.

II. TMR.

Việc thay thế số 2 tôi nói đến trong bài này là TMR (Triple modular redundancy): Đây cũng là việc thay thế của Backend team và Fronend team cũng là những người cần cung cấp các list cần thay thế.
Việc thông thường là ta sẽ thay thế 1 flip flop bởi nhiều flip flop mắc song song.

Tôi lấy ví dụ. Khi bạn có một tín hiệu để xuất kết quả error ra đến module interrupt. Giả sử trên đường truyền tín hiệu error này, vì một lí do nào đó tín hiệu của bạn bị sai (tín hiệu error thường 1 bit vì vậy xác xuất sai của bạn khá cao) khi đó hệ thống của bạn dù không lỗi cũng có thể bị interrupt (tín hiệu error truyền sai từ giá trị 0 -> 1). Hoặc dù có lỗi nhưng vẩn không bị interrupt (Tín hiệu error truyền sai từ giá trị 1 ->0). Điều này thật sự nghiêm trọng nhất là đối với việc các module connect với nhau. TMR sẽ giúp chúng ta giải quyết vấn đề này bằng cách nhân 3 mạch hay FF lên  (Hay nói đúng hơn là thay thế mạch FF hiện tại bằng 3 mạch tương tự mắc song song với nhau, 3 mạch này có output đi vào bộ compare) sau đó so sánh kết quả của 3 mạch hay FF này nếu có tối thiểu 2 tín hiệu trong 3 tín hiệu này mang giá tri 0 thì giá trị ra mang giá trị 0 và ngược lại.
Hình bên dưới biểu thị rõ nét mạch nhân 3 để check tín hiệu ra.

Hình 2. Thay thế mạch 1 flip flop bởi mạch 3 flip flop mắc song song.

Tương tự ta có thể nhân 5 mạch để xác xuất lỗi giảm xuống. Nếu 2 tín hiệu bị lỗi và 3 tín hiệu chính xác thì tín hiệu ngõ ra sẽ mang giá trị 3 tín hiệu đó.

Tuy nhiên lựa chọn nhân 3 mạch là lựa chọn khả dĩ nhất vì nếu bạn tăng nhiều mạch gate size của bạn sẽ tăng lên đáng kể mà xác xuất chính xác cũng không hiệu quả hơn là bao so với nhân 3 mạch.

Thursday, April 11, 2019

[Bài 10][ UVM ] Tạo env trong môi trường UVM

Về khối env, tôi thấy nó cũng khá giống với khối agent. Chức năng chính của khối này là tiến hành connect hai khối scoreboard và khối agent lại với nhau thông qua chuẩn giao tiếp TLM. Tuy nhiên, vì chúng ta có hai luồng dữ liệu khác nhau nên ta phải khai báo 2 biến TLM. Dùng để truyền output của DUT và truyền kết quả dự đoán từ khối agent đến khối scoreboard.

Dưới đây là cấu trúc cơ bản của khối env:

`ifndef _UVM_ENV_
`define _UVM_ENV_

class add4bit_env extends uvm_env;
         `uvm_component_utils(add4bit_env)

          add4bit_scoreboard score_trans;
          add4bit_agent agent_trans;

         function new(string name = "", uvm_component parent);
            super.new(name, parent);
        endfunction: new

       function void build_phase(uvm_phase phase);
            super.build_phase(phase);
           score_trans =                  add4bit_scoreboard::type_id::create(.name("score_trans"), .parent(this));
        agent_trans = add4bit_agent::type_id::create(.name("agent_trans"), .parent(this));
    endfunction: build_phase

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        agent_trans.agent_before.connect(score_trans.monitor_before);
        agent_trans.agent_after.connect(score_trans.monitor_after);
    endfunction: connect_phase

endclass: add4bit_env

`endif

Cách tính BW và latency trong 1 hệ thống SoC sử dụng chuẩn giao tiếp AXI protocol

Tác giả:  TrongTran Ngày:  31/12/2019 Nếu bạn nào đang làm về verification cho system performance (ST) thì bài này sẽ bổ ích cho bạn. Ngày ...