Function new là một chức năng đã được build sẵn (built-in) trong System Verilog (SV). Function này là một phần không thể thiếu khi xây dựng các class. Bài viết này sẽ trình bày về các khía cạnh sử dụng khác nhau của function new.
1) new() là gì?
new là một function được xây dựng sẵn của SV nên nó là một keyword của SV. new không xác định loại dữ liệu trả về (type) và các phép gán sử dụng trong new phải là phép gán blocking, ký hiệu là "=" (Xem lại ví dụ bài 2).
Ví dụ 1 - So sánh new và function khác
function new;
begin
num = 1;
i = 0;
end
endfunction//
function integer get_size();
begin
get_size = num;
end
endfunction: get_size
Trong ví dụ trên, get_size() có xác định loại dữ liệu trả về là integer còn new thì không. new là một function nên cũng chỉ sử dụng phép gán blocking như get_size().
Nếu cố khai báo loại dữ liệu trả về cho function new thi phần mềm biên dịch sẽ báo lỗi.
Ví dụ 2 - Khai báo loại dữ liệu trả về cho new (vi phạm)
function integer new;
begin num = 1;
i = 0;
end
endfunction
Ví dụ trên cố tình khai báo integer cho function new. Một lỗi biên dịch code sẽ sinh ra:
Hình 1: Lỗi biên dịch code khi khai báo loại dữ liệu trả về cho function new |
2) Chức năng của new() đối với class
Liên quan đến class, new có hai ngữ cảnh sử dụng:
- Tạo đối tượng class (class instance) và gán handle của đối tượng đến một biến class (xem bài 1)
- Class constructor: Gán giá trị ban đầu cho các biến hoặc thực hiện một số tác vụ ban đầu khi một đối tượng class được khởi tạo.
Ngữ cảnh sử dụng thứ nhất, các bạn đọc mục 4 của bài 1.
Ngữ cảnh sử dụng thứ hai, các bạn đọc mục 3 của bài 1.
Ví dụ 3 - new là một constructor
class rand_packet;
//
// 1: Declare the class properties
//
integer num = 1;
integer rand_data []; //A dynamic array
integer i;
//
// 2: Constructor
//
function new;
begin
num = 5;
i = 0;
$display ("This is rand_packet with num = %d and i = %d", num, i);
end
endfunction //
// 3: Build the class methods (tasks or functions)
//
// Method 1: Task in class (object method)
// Create the random data and store to an array element
task build_data ();
begin
rand_data = new[num]; //Set the size of dynamic array
for (i = 0; i < num; i++) begin
rand_data[i] = $random;
end
//$display ("This is a base class");
end
endtask: build_data
//
// Method 2: Task in class (object method)
// Display all values of array
task print ();
begin
for (i=0; i < num; i ++) begin
$display("rand_data[%d] %x",i, rand_data[i]);
end
end
endtask: print
//
// Method 3: Function in class (object method)
// Get the size of the array
function integer get_size();
begin get_size = num;
end
endfunction: get_size
endclass: rand_packet
Trong ví dụ trên, khi một đối tượng class được tạo, ví dụ như bởi code sau:
rand_packet pkt = new;
function new trong class sẽ được gọi. Nó thực hiện gán num = 5, i = 1 và hiển thị dòng thông báo về giá trị của num và i thông qua $display.
Hình 2: Kết quả thực thi constructor new trong ví dụ 3 |
Khi class là một constructor, function new có thể được truyền thêm các đối số (argument) để sử dụng linh động class khi khởi tạo các đối tượng (class instance).
Ví dụ 4 - function new có đối số
function new (integer exp_num = 0);
begin
if (exp_num != 0) begin
num = exp_num;
end
else begin
num = 1;
end
i = 0;
$display ("This is rand_packet with num = %d and i = %d", num, i);
end
endfunction
Trong ví dụ trên, một đối số exp_num được thêm vào function new. Đối số này được sử dụng để xử lý việc gán giá trị ban đầu cho biến num. Nếu đối số truyền vào lớn hơn 0 thì num sẽ lấy giá trị của exp_num. Nếu đối số không được truyền cho new hoặc <= 0 thì num = 1. Thay function new của ví dụ 4 vào class của ví dụ 3 rồi sử dụng đoạn code sau để chạy mô phỏng kiểm chứng:
rand_packet pkt;
pkt = new(8);
pkt.build_data();
$display ("Size of packet pkt: %0d",pkt.get_size());
pkt.print();
Chú ý dòng pkt = new(8), đối số được truyền cho function new là 8. Kết quả:
Hình 3: Kết quả mô phỏng của function new với đối số là 8 |
Một class luôn tồn tại function new cho dù nó có được khai báo hay không. Nếu một class không khai báo function new thì nó sẽ sử dụng function new mặc định của hệ thống. Việc không khai báo function new cho class tương đương với việc khai báo một function new rỗng.
function new();
endfunction
3) new() trong class mở rộng
Như đã trình bày trong mục 4 của bài 2, new là một method đặc biệt của class. Nó không bị "ghi đè" (overridden) hoàn toàn trong class mở rộng.
Ví dụ 5 - new của class mở rộng
class rand_packet_mdf extends rand_packet;
function new;
begin
num = 3;
$display ("This is rand_packet_mdf with num = %d and i = %d", num, i);
end
endfunction
endclass: rand_packet_mdf
rand_packet_mdf pkt_mdf;
pkt_mdf = new;
pkt_mdf.build_data();
$display ("Size of packet pkt_mdf: %0d",pkt_mdf.get_size());
pkt_mdf.print();
Hình 4: Kết quả mô phỏng với một đối tượng của class mở rộng rand_packet_mdf |
Kết quả cho thấy new của class gốc rand_packet vẫn được thực thi và hiển thị ở dòng đầu tiên hình 4. Điều này là bởi vì new trong class mở rộng rand_packet_mdf sẽ thực thi mặc định lệnh super.new trước khi thực thi các dòng code nào khác mô tả trong new. super.new sẽ gọi và thực thi new của class gốc rand_packet.
Hình 5: Sự tương đương trong mô tả function new trong class mở rộng và class tạo mới (không được mở rộng từ class khác) |
Một số lưu ý về super.new:
1. super.new luôn được thực thi đầu tiên trong function new của class mở rộng dù nó có được mô tả hay không. Vì vậy, nếu mô tả super.new sau các dòng code khác trong function new, lỗi biên dịch sẽ xảy ra.
Ví dụ 6 - mô tả sai vị trí super.new trong function new (sai)
function new;
begin
num = 3;
super.new;
$display ("This is rand_packet_mdf with num = %d and i = %d", num, i);
end
endfunction
Hình 6: Lỗi biên dịch code khi mô tả sai vị trí super.new trong fucntion new của class mở rộng |
2. Không mô tả super.new trong class không mở rộng (không có extends)
Ví dụ 7 - Mô tả super.new trong một class không mở rộng từ class khác (sai)
class rand_packet;
function new;
begin
super.new;
num = 3;
$display ("This is rand_packet with num = %d and i = %d", num, i);
end
endfunction
endclass: rand_packet
Hình 7: Lỗi biên dịch khi mô tả super.class trong một class không mở rộng |
4) Khai báo local và protected cho new()
4.1) local
Khi new là một constructor của class, nó có thể được khai báo là local hoặc protected.
local là một từ khóa dùng để xác định một thành phần chỉ được truy xuất nội bộ. Trong class, một property hoặc method được khai báo local sẽ chỉ sử dụng trong phạm vi class đó. Nó không thể được truy xuất ở phạm vi ngoài class nên không thể được truy xuất thông qua handle của đối tượng. Các thành phần được khai báo local gọi là "bị ẩn" (hiding) đối với tất cả các phạm vi ngoài class, bao gồm cả subclass được suy ra từ class này. Chú ý, hiding (data hiding) là một thuật ngữ hay sử dụng để nói về phương pháp ẩn và giới hạn truy cập các thành phần của class từ bên ngoài.
new là một method, nó có thể được khai báo local. Việc khai báo local cho new sẽ làm cho một class không thể được mở rộng, nói cách khác nó không thể làm base class cho một class mở rộng.
Ví dụ 8 - Khai báo thêm local cho new trong class rand_packet
Như đã trình bày ở trên, function new của class mở rộng (rand_packet_mdf) luôn gọi new của class gốc thông qua super.new. Việc khai báo local cho new trong class gốc (rand_packet) làm cho super.new trong subclass (rand_packet_mdf) không thể thực hiện được vì new mang thuộc tính local nên không thể truy xuất new bên ngoài class gốc (rand_packet).
Ngoài ra, chúng ta không thể tạo đối tượng (một class instance) từ một class có new được khai báo local vì new không được phép gọi ngoài phạm vi class. Ví dụ, giả sử class rand_packet có new được khai báo local như ví dụ 8. Sau đó, một đối tượng được tạo từ rand_packet như sau:
Có thể thấy, một class có constructor được khai báo local không sai cú pháp nhưng nó không thể mở rộng được và không thể tạo đối tượng để sử dụng nên chúng ta không dùng khai báo local cho constructor của class.
4.2) protected
protected là từ khóa có tác dụng tương tự local ở đặc điểm "không cho phép truy xuất các thành phần được khai báo protected bên ngoài class" và khác ở đặc điểm "cho phép truy xuất các thành phần được khai báo protected trong class mở rộng (subclass)".
Nếu khai báo protected cho constructor của class thì class sẽ không thể tạo trực tiếp một đối tượng từ class này.
Ví dụ 9 - Khai báo thêm protected cho new trong class rand_packet
Tuy nhiên, việc mở rộng class và truy xuất các thành phần protected thông qua class mở rộng là được phép, ví dụ như class rand_packet_mdf được mở rộng từ class rand_packet được chấp nhận.
Lịch sử cập nhật:
1) 2019.01.30 - Thêm mục "Khai báo local và protected cho new()"
4.1) local
Khi new là một constructor của class, nó có thể được khai báo là local hoặc protected.
local là một từ khóa dùng để xác định một thành phần chỉ được truy xuất nội bộ. Trong class, một property hoặc method được khai báo local sẽ chỉ sử dụng trong phạm vi class đó. Nó không thể được truy xuất ở phạm vi ngoài class nên không thể được truy xuất thông qua handle của đối tượng. Các thành phần được khai báo local gọi là "bị ẩn" (hiding) đối với tất cả các phạm vi ngoài class, bao gồm cả subclass được suy ra từ class này. Chú ý, hiding (data hiding) là một thuật ngữ hay sử dụng để nói về phương pháp ẩn và giới hạn truy cập các thành phần của class từ bên ngoài.
new là một method, nó có thể được khai báo local. Việc khai báo local cho new sẽ làm cho một class không thể được mở rộng, nói cách khác nó không thể làm base class cho một class mở rộng.
Ví dụ 8 - Khai báo thêm local cho new trong class rand_packet
local function new;Khi khai báo thêm local cho function new của class rand_packet trong ví dụ 3, trình biên dịch sẽ báo lỗi đối với class mở rộng rand_packet_mdf.
begin num = 1;
i = 0;
$display ("This is rand_packet with num = %d and i = %d", num, i);
end
endfunction
Hình 8: Lỗi biên dịch khi mở rộng class rand_packet vì khai báo local cho new |
Ngoài ra, chúng ta không thể tạo đối tượng (một class instance) từ một class có new được khai báo local vì new không được phép gọi ngoài phạm vi class. Ví dụ, giả sử class rand_packet có new được khai báo local như ví dụ 8. Sau đó, một đối tượng được tạo từ rand_packet như sau:
rand_packet pkt = new;
Hình 9: Lỗi biên dịch khi tạo một đối tượng từ class có new được khai báo local |
4.2) protected
protected là từ khóa có tác dụng tương tự local ở đặc điểm "không cho phép truy xuất các thành phần được khai báo protected bên ngoài class" và khác ở đặc điểm "cho phép truy xuất các thành phần được khai báo protected trong class mở rộng (subclass)".
Nếu khai báo protected cho constructor của class thì class sẽ không thể tạo trực tiếp một đối tượng từ class này.
Ví dụ 9 - Khai báo thêm protected cho new trong class rand_packet
protected function new;Khi khai báo protected cho new, việc tạo đối tượng trực tiếp từ rand_packet sẽ bị lỗi biên dịch:
begin num = 1;
i = 0;
$display ("This is rand_packet with num = %d and i = %d", num, i);
end
endfunction
rand_packet pkt = new;
Hình 10: Lỗi biên dịch khi tạo đối tượng trực tiếp từ class có new được khai báo protected |
Lịch sử cập nhật:
1) 2019.01.30 - Thêm mục "Khai báo local và protected cho new()"
2) 2022.10.30 - Sửa các từ khóa nonblocking thành blocking
Ad ơi, nonblocking kí hiệu là <= chứ ạ.
Trả lờiXóaCảm ơn em góp ý, đó là một lỗi, anh đã cập nhật lại
Xóa