Chủ Nhật, 3 tháng 2, 2019

[System Verilog][Class]Bài 6 - Toán tử phân giải phạm vi (scope resolution) ::

Toán tử phân giải phạm vi (scope resolution), ký hiệu là "::", là một trong những toán tử được sử dụng rất nhiều để truy xuất các phần tử trong một class. Bài viết này sẽ giải thích rõ cách sử dụng toán tử này. Mỗi đặc điểm sẽ có ví dụ cụ thể để bạn đọc dễ nắm bắt và kiểm chứng.
Chú ý, việc dịch thành tên tiếng việt "toán tử  phân giải phạm vi" là dựa trên cách sử dụng của loại toán tử này. Trong bài viết này, tác giả sẽ dùng cụm từ "scope resolution" hoặc ký hiệu "::" để trình bày.

1) Cú pháp sử dụng toán tử "::"
Toán tử "::" được sử dụng để xác định (truy xuất) một phần tử bên trong một class bằng cách sử dụng tên của class. Nó khác với cách truy xuất thông qua handle của một đối tượng class bằng ký hiệu "." như đã trình bày trong bài 1. Cú pháp sử dụng của toán tử này như sau:
class_type :: { class_type :: } identifier
Trong đó:
  1. class_type chính là tên gốc của class. Nó được gọi là class_type vì một class định nghĩa một loại dữ liệu (data type). Ngoài ra, nó có thể là tên package, tên covergroup, tên coverpoint, tên cross, tên typedef hoặc type parameter nhưng bài viết này chỉ tập trung minh họa cho class.
  2. identifier là tên biến hoặc tên method.
2) Khả năng sử dụng của toán tử "::"
Toán tử scope resolution "::" cho phép các xử lý sau đây:
  1. Truy xuất các thành phần (biến và method) static và public bên ngoài định nghĩa class. Chú ý, thành phần này phải có hai điều kiện là khai báo static và không có khai báo local hoặc protected.
  2. Truy xuất các thành phần public (không có khai báo local hoặc protected) hoặc thành phần được protected của class gốc (base class) ở trong class mở rộng.
  3. Truy xuất các constraint, các khai báo về loại dữ liệu, các hằng số được định nghĩa bằng tên trong enum của một class ở bên ngoài định nghĩa class hoặc bên trong một class mở rộng.
  4. Truy xuất các parameter localparam của một class ở bên ngoài class hoặc bên trong một class mở rộng.
Chúng ta cùng phân tích ví dụ sau đây để kiểm chứng các đặc điểm đã nêu về toán tử "::".
Ví dụ 1 - Toán tử "::" trong việc sử dụng class
class packet;
  parameter ADDR_WIDTH = 8; //parameter
  localparam TEST = "THIS IS THE BASE CLASS"; //local parameter
  typedef enum {IDLE, BUSY, FINISH} state; //enum
  static int count;    //static variable
  bit [ADDR_WIDTH-1:0] addr; //non-static variable
  protected bit [ADDR_WIDTH-1:0] addr_mask = {{ADDR_WIDTH-4{1'b1}}, 4'd0}; //protected variable
  //
  task addr_gen; //non-static method
    int i = 1;
    while (i) begin      addr[ADDR_WIDTH-1:2] = $random(count); //Get the random value
      addr[1:0] = 2'b00;   //mask 2 LSB bits to 0
      if (addr[ADDR_WIDTH-1:2] != 0) begin        i = 0;
        $display ("[addr_gen] Address is %h", addr);
      end      count++;
    end  endtask: addr_gen
  //
  static task count_reset; //static method
    count = 0; //Reset count
  endtask: count_reset
  //
endclass: packet
//
class packet_ext extends packet;
  bit [ADDR_WIDTH-1:0] first_addr;
  localparam TEST_EXT = "THIS IS THE EXTENDED CLASS";
  //
  task first_gen;
    packet::addr_gen; //Access to a public method of superclass (baseclass)
    packet::addr_mask = {{ADDR_WIDTH-6{1'b1}}, 6'd0}; //Access to a protected property of superclass
    first_addr        = addr & packet::addr_mask; //mask 6 LSB bits
  endtask: first_gen
  //
endclass: packet_ext
//
module class_scope_resolution;
  packet_ext pkt_ext;
  initial begin    pkt_ext = new;
    //
    $display ("1/ Access to static public members");
    packet::count = 5; //Access to static public property
    $display ("-- count = %d", packet::count);
    packet::count_reset; //Access to static public method
    $display ("-- count = %d", packet::count);
    //
    $display ("2/ Access to public or protected members of superclass");
    pkt_ext.first_gen;
    $display ("-- first_addr = %h",pkt_ext.first_addr);
    //
    $display ("3/ Access to constraints, type declarations, and enumeration named constants");
    $display ("-- state: IDLE=%2d, BUSY=%2d, FINISH=%2d", packet::IDLE, packet::BUSY, packet::FINISH);
    //
    $display ("4/ Access to parameters and local parameters");
    $display ("-- ADDR_WIDTH: %d", packet::ADDR_WIDTH);
    $display ("-- TEST: %s", packet::TEST);
    $display ("-- TEST: %s", packet_ext::TEST);
    $display ("-- TEST_EXT: %s", packet_ext::TEST_EXT);
  end//endmodule: class_scope_resolution
Ví dụ trên định nghĩa một class gốc tên packet và một class mở rộng tên packet_ext. Module tên  class_scope_resolution dùng để kiểm chứng các đặc điểm đã nêu về toán tử "::".
Xét đặc điểm thứ nhất, để kiểm chứng đặc điểm này bạn đọc chú ý đến các thành phần sau:

  1. Trong class packet, biến count được khai báo static và là loại public
  2. Trong class packet, method count_reset là một method static và là loại public

Đoạn code sau đây trong module class_scope_resolution kiểm chứng việc truy xuất biến tĩnh count và method tĩnh count_reset thông qua tên class packet bằng toán tử "::".
    $display ("1/ Access to static public members");
    packet::count = 5; //Access to static public property
    $display ("-- count = %d", packet::count);
    packet::count_reset; //Access to static public method
    $display ("-- count = %d", packet::count);
Biến count được gọi và gán giá trị 5. count_reset được gọi để reset lại  biến count về 0. Kết quả mô phỏng như sau:
Hình 1: Kết quả kiểm chứng đặc điểm 1
Xét đặc điểm thứ hai, để kiểm chứng đặc điểm này bạn đọc chú ý đến các method first_gen trong class mở rộng packet_ext. Trong method này, toán tử "::" được sử dụng để truy xuất đến method public addr_gen và biến protected addr_mask. Trong module class_scope_resolution, đoạn code sau đây dùng để kiểm chứng kết quả:
    $display ("2/ Access to public or protected members of superclass");
    pkt_ext.first_gen;
    $display ("-- first_addr = %h",pkt_ext.first_addr);
method first_gen được gọi để thực thi. Nó sẽ gọi addr_gen của class gốc để tạo một giá trị cho biến addr. Sau đó, biến addr được che 6 bit cuối lại bởi biến addr_mask. Kết quả mô phỏng như sau:
Hình 2: Kết quả mô phỏng kiểm chứng đặc điểm 2
Xét đặc điểm thứ 3, để kiểm chứng đặc điểm này bạn đọc chú ý đến định nghĩa biến enum state trong class packet. Các hằng số của nó sẽ được truy xuất bởi toán tử "::" trong đoạn code sau:
    $display ("3/ Access to constraints, type declarations, and enumeration named constants");
    $display ("-- state: IDLE=%2d, BUSY=%2d, FINISH=%2d", packet::IDLE, packet::BUSY, packet::FINISH);
Kết quả kiểm chứng như sau:
Hình 3: Kết quả mô phỏng kiểm chứng đặc điểm 3
Xét đặc điểm thứ 4, để kiểm chứng đặc điểm này bạn đọc chú ý đến parameter ADDR_WIDTH, localparam trong class packet parameter TEST_EXT trong class packet_ext. Đoạn code sau sẽ truy xuất các parameter localparam đã nêu:
    $display ("4/ Access to parameters and local parameters");
    $display ("-- ADDR_WIDTH: %d", packet::ADDR_WIDTH);
    $display ("-- TEST: %s", packet::TEST);
    $display ("-- TEST: %s", packet_ext::TEST);
    $display ("-- TEST_EXT: %s", packet_ext::TEST_EXT);
Hình 4: Kết quả mô phỏng kiểm chứng đặc điểm 4
Trên đây là những đặc điểm cơ bản khi sử dụng toán tử "::" truy xuất các thành phần trong class.

0 bình luận:

Đăng nhận xét