Trong quá trình sử dụng class, việc thao tác trên các biến class và handle đối tượng rất phổ biến. Bài viết này trình bày chi tiết một số thao tác có thể thực hiện với biến class và handle của đối tượng.
Trước khi đọc bài viết này, bạn đọc tham khảo các bài 1 và bài 2 để biết rõ một số khái niệm và thuật ngữ cơ bản về class trong SV.
1) Nhắc lại một số khái niệm
Bài 1 sẽ giúp các bạn có thể hiểu về khái niệm handle. Như đã trình bày, cặp từ khóa class/endclass giúp định nghĩa một loại dữ liệu class chứa các thuộc tính (property) và method (function/task).
Các class sau đây sẽ được sử dụng làm class gốc để sử dụng trong các ví dụ khác trong bài viết này.
Ví dụ 1 - Định nghĩa một class
packet là một class có hai propety là wdata, addr_packet và một method là gen_data. Trong đó, property addr_packet là một property đặc biệt chứa handle của một đối tượng được tạo từ class ctrl_packet. Ngoài ra, packet còn chứa một embedded covergroup tên cov_wdata.
2) Gán giá trị handle (assignment)
class <class_name>Từ loại dữ liệu class đã định nghĩa, chúng ta có thể khai báo một biến class (class variable) thông qua cú pháp sau:
...
endclass
<class_name> <class_variable_name>Một biến class có thể chứa giá trị handle của một đối tượng class (class instance) thông qua cú pháp sau:
<class_variable_name> = new;Handle của class giống như poiter trong ngôn ngữ C/C++, nó là một địa chỉ bộ nhớ trỏ đến đối tượng class được tạo ra. Chú ý, một định nghĩa class có thể được dùng để rạo ra nhiều đối tượng class khác nhau.
Các class sau đây sẽ được sử dụng làm class gốc để sử dụng trong các ví dụ khác trong bài viết này.
Ví dụ 1 - Định nghĩa một class
class ctrl_packet;
//
bit [31:0] addr;
//
function new ();
begin addr[31:0] = 32'd0;
end endfunction //
task gen_addr;
//get a random address and mask 2 LSB bits to 0
addr = $random & 32'hffff_fffc;
$display ("Address 0: %8h", addr);
//Create the 4 next addresses by adding 4
for (int i = 0; i < 4; i++) begin addr = addr + 4;
$display ("Address %2d: %8h", i+1, addr);
end endtask: gen_addr
//
endclass: ctrl_packet
class packet;ctrl_packet có một property là addr và một method là gen_addr. gen_addr sẽ lấy một giá trị ngẫu nhiên với 2 bit cuối luôn bằng 0. Sau đó, tạo thêm 4 giá trị trị địa chỉ liên tiếp nhau, mỗi địa chỉ cách nhau 4 đơn vị.
//Create a class instance inside this class
ctrl_packet addr_packet;
//
bit [31:0] wdata;
//
covergroup cov_wdata; //embedded covergroup
coverpoint wdata
{
bins wdata = {[32'h0000_0001:32'hffff_ffff]};
}
endgroup: cov_wdata
//
function new ();
begin wdata[31:0] = 32'd0;
addr_packet = new;
cov_wdata = new;
end endfunction //
task gen_data;
wdata = $random; $display ("Write data: %8h", wdata);
endtask: gen_data
//
endclass: packet
packet là một class có hai propety là wdata, addr_packet và một method là gen_data. Trong đó, property addr_packet là một property đặc biệt chứa handle của một đối tượng được tạo từ class ctrl_packet. Ngoài ra, packet còn chứa một embedded covergroup tên cov_wdata.
2) Gán giá trị handle (assignment)
Gán một giá trị handle đã có cho một biến class khác. Cả biến class cũ và mới đều chỉ đến cùng một đối tượng. Giá trị của các property của đối tượng có thể được truy cập như nhau thông qua hai tên biến class. Cú pháp:
module class_ex_handle;
ctrl_packet apkt_1;
ctrl_packet apkt_2;
//
initial begin
apkt_1 = new;
apkt_2 = apkt_1; //Assignment
$display ("- The handle value: \n\t 1 - apkt_1 is %d \n\t 2 - apkt_2 is %d", apkt_1, apkt_2);
//
$display ("1/ Change the address value via apkt_1 and check on apkt_2");
apkt_1.gen_addr;
$display ("Address value is accessed via apkt_2: %8h", apkt_2.addr);
//
$display ("2/ Change the address value via apkt_2 and check on apkt_1");
apkt_2.addr = 32'h0a0a_0a0a;
$display ("Address value is accessed via apkt_1: %8h", apkt_1.addr);
end
//
endmodule: class_ex_handle
Kết quả mô phỏng của ví dụ 2:
Kết quả mô phỏng cho thấy,giá trị handle của hai biến apkt_1 và apkt_2 là như nhau. Lần thứ nhất, giá trị addr 12153534 được tạo ra từ method gen_addr thông qua apkt_1 và được kiểm tra lại thông qua apkt_2. Lần thứ hai, giá trị addr được thay đổi thành h0a0a_0a0a thông qua apkt_2 và được lại thông qua apkt_1. Trong ví dụ này, new chỉ được gọi một lần, thực thi một lần nên chỉ tạo ra một đối tượng.
3) Tạo lại đối tượng (re-creation)
Tạo lại đối tượng là tạo một đối tượng mới và gán handle của nó lên một biến class đang lưu giữ một handle khác. Cú pháp:
Trong ví dụ trên, sau khi tạo một đối tượng có handle 65538 và thực thi method gen_addr để tạo ra các giá trị addr, biến class apkt_1 được khởi tạo lại đến một đối tượng mới có handle 131074 (handle cũ là 65538 sẽ bị xóa) nên giá trị biến addr thay đổi từ 12153534 thành 00000000.
4) Shallow copy
Shallow copy, tạm dịch là"sao chép nông" hoặc "sao chép một phần", tên này rất gợi nhớ về đặc điểm của loại sao chép này. Cú pháp:
Trong ví dụ trên, sau khi pkt_1 được khởi tạo. pkt1_1.gen_data được gọi để tạo ra một giá trị cho wdata trước khi thực hiện sao chép đến đối tượng được điều khiển bởi biến class pkt_2.
Chú ý đến các giá trị handle, chúng ta thấy pkt_1 và pkt_2 khác nhau. Điều này chứng minh "một đối tượng mới được tạo ra và được điều khiển bởi pkt_2". hanlde của addr_packet và cov_wdata liên kết với pkt_1 và pkt_2 là như nhau. Điều này chứng minh "không có đối tượng mới được tạo ra và gán handle cho addr_packet và cov_wdata trong pkt_2".
Lần kiểm tra thứ nhất, sau khi sao chép, giá trị "wdata là 12153524" và "addr là 0" của đối tượng được điều khiển bởi pkt_1 và pkt_2 là giống nhau.
Lần kiểm tra thứ hai, sau khi wdata thành trong pkt_2 được cập nhật thành c0895e81 thì chỉ có wdata trong pkt_2 thay đổi vì pkt_1 và pkt_2 điều khiển hai đối tượng khác nhau.
Lần kiểm tra thứ ba, sau khi thay đổi addr thông qua pkt_2 thì cả addr của pkt_1 và pkt_2 có giá trị giống nhau vì chúng đều điều khiển chung một handle trỏ đến một vùng địa chỉ chứa addr.
5) Deep (full) copy
Deep copy, còn gọi là full copy, tạm dịch là "sao chép sâu" hoặc "sao chép toàn phần", là một khái niệm ám chỉ việc sao chép mọi thứ, bao gồm cả các đối tượng chứa bên trong đối tượng khác. Để làm được điều này, bạn phải tự viết một method riêng để sử dụng. Cú pháp:
Ví dụ 5 - Deep (full) copy
<assigned_handle_name> = <handle_name>Ví dụ 2 - Gán giá trị handle
module class_ex_handle;
ctrl_packet apkt_1;
ctrl_packet apkt_2;
//
initial begin
apkt_1 = new;
apkt_2 = apkt_1; //Assignment
$display ("- The handle value: \n\t 1 - apkt_1 is %d \n\t 2 - apkt_2 is %d", apkt_1, apkt_2);
//
$display ("1/ Change the address value via apkt_1 and check on apkt_2");
apkt_1.gen_addr;
$display ("Address value is accessed via apkt_2: %8h", apkt_2.addr);
//
$display ("2/ Change the address value via apkt_2 and check on apkt_1");
apkt_2.addr = 32'h0a0a_0a0a;
$display ("Address value is accessed via apkt_1: %8h", apkt_1.addr);
end
//
endmodule: class_ex_handle
Hình 1: Gán handle (assignment) |
Hình 2: Kết quả mô phỏng của ví dụ 2 |
3) Tạo lại đối tượng (re-creation)
Tạo lại đối tượng là tạo một đối tượng mới và gán handle của nó lên một biến class đang lưu giữ một handle khác. Cú pháp:
<handle_name> = new;Ví dụ 3 - Tạo lại đối tượng (re-creation)
module class_ex_handle;
ctrl_packet apkt_1;
ctrl_packet apkt_2;
initial begin //
apkt_1 = new; $display ("- The handle value: \n\t 1 - apkt_1 is %d", apkt_1);
apkt_1.gen_addr;
apkt_1 = new; $display ("- The handle value: \n\t 2 - apkt_1 is %d", apkt_1);
$display ("Address value is accessed via apkt_1: %8h", apkt_1.addr);
end //
endmodule: class_ex_handle
Hình 3: Kết quả mô phỏng của ví dụ 3 |
Hình 4: Tạo lại đối tượng mới (re-creation) |
Shallow copy, tạm dịch là"sao chép nông" hoặc "sao chép một phần", tên này rất gợi nhớ về đặc điểm của loại sao chép này. Cú pháp:
<new_handle_name> = new <copied_handle_name>Như tên gọi, shallow copy có hai đặc điểm:
- Copy: Tạo một đối tượng mới và sao chép toàn bộ các property, bao gồm cả các handle, của một đối tượng đã có sang đối tượng này.
- Shallow: Trong các property, các handle, như handle của một đối tượng class hoặc handle của covergroup, chỉ được sao chép giá trị handle chứ không sao chép đối tượng (không cấp phát vùng bộ nhớ mới).
module class_ex_handle;
packet pkt_1;
packet pkt_2;
initial begin pkt_1 = new;
pkt_1.gen_data;
pkt_2 = new pkt_1; //shallow copy
$display ("- The handle value: \n\t 1 - pkt_1 is %d \n\t 2 - pkt_2 is %d", pkt_1, pkt_2);
$display ("- The handle value: \n\t 1 - pkt_1.addr_packet is %d \n\t 2 - pkt_2.addr_packet is %d", pkt_1.addr_packet, pkt_2.addr_packet);
$display ("- The handle value: \n\t 1 - pkt_1.cov_wdata is %d \n\t 2 - pkt_2.cov_wdata is %d", pkt_1.cov_wdata, pkt_2.cov_wdata);
//
$display ("1/ Check the write data and address after copying");
$display ("- Write data of pkt_1 is %8h", pkt_1.wdata);
$display ("- Write data of pkt_2 is %8h", pkt_2.wdata);
$display ("- Address of pkt_1 is %8h", pkt_1.addr_packet.addr);
$display ("- Address of pkt_2 is %8h", pkt_2.addr_packet.addr);
//
$display ("2/ Check the write data after changing wdata of pkt_2");
pkt_2.gen_data;
$display ("- Write data of pkt_1 is %8h", pkt_1.wdata);
$display ("- Write data of pkt_2 is %8h", pkt_2.wdata);
//
$display ("3/ Check the address after changing address of pkt_2");
pkt_2.addr_packet.gen_addr;
$display ("- Address of pkt_1 is %8h", pkt_1.addr_packet.addr);
$display ("- Address of pkt_2 is %8h", pkt_2.addr_packet.addr);
//
end //
endmodule: class_ex_handle
Hình 5: Kết quả mô phỏng của ví dụ 4 |
Chú ý đến các giá trị handle, chúng ta thấy pkt_1 và pkt_2 khác nhau. Điều này chứng minh "một đối tượng mới được tạo ra và được điều khiển bởi pkt_2". hanlde của addr_packet và cov_wdata liên kết với pkt_1 và pkt_2 là như nhau. Điều này chứng minh "không có đối tượng mới được tạo ra và gán handle cho addr_packet và cov_wdata trong pkt_2".
Lần kiểm tra thứ nhất, sau khi sao chép, giá trị "wdata là 12153524" và "addr là 0" của đối tượng được điều khiển bởi pkt_1 và pkt_2 là giống nhau.
Lần kiểm tra thứ hai, sau khi wdata thành trong pkt_2 được cập nhật thành c0895e81 thì chỉ có wdata trong pkt_2 thay đổi vì pkt_1 và pkt_2 điều khiển hai đối tượng khác nhau.
Lần kiểm tra thứ ba, sau khi thay đổi addr thông qua pkt_2 thì cả addr của pkt_1 và pkt_2 có giá trị giống nhau vì chúng đều điều khiển chung một handle trỏ đến một vùng địa chỉ chứa addr.
Hình 6: Shallow copy |
Deep copy, còn gọi là full copy, tạm dịch là "sao chép sâu" hoặc "sao chép toàn phần", là một khái niệm ám chỉ việc sao chép mọi thứ, bao gồm cả các đối tượng chứa bên trong đối tượng khác. Để làm được điều này, bạn phải tự viết một method riêng để sử dụng. Cú pháp:
<new_handle_name> = <copied_handle_name>.deep_copy(<arguments>);Chú ý, deep_copy ở đây là một method do người viết code tự xây dựng, không phải là từ khóa được SV hỗ trợ sẵn. Nghĩa là, nó không phải là một method được xây dựng sẵn (built-in).
Ví dụ 5 - Deep (full) copy
class ctrl_packet;
//
bit [31:0] addr;
//
function new ();
begin addr[31:0] = 32'd0;
end endfunction //
task gen_addr;
//get a random address and mask 2 LSB bits to 0
addr = $random & 32'hffff_fffc;
$display ("Address 0: %8h", addr);
//Create the 4 next addresses by adding 4
for (int i = 0; i < 4; i++) begin addr = addr + 4;
$display ("Address %2d: %8h", i+1, addr);
end endtask: gen_addr
//Custom method of deep copy function
function ctrl_packet deep_copy;
//create a new object
deep_copy = new;
//copy the property of current object to the new object
deep_copy.addr = this.addr;
//Return the handle of the new object
return deep_copy;
endfunction: deep_copy
//
endclass: ctrl_packet
class packet;
//Create a class instance inside this class
ctrl_packet addr_packet;
//
bit [31:0] wdata;
//
covergroup cov_wdata @wdata; //embedded covergroup
coverpoint wdata
{
bins wdata = {[32'h0000_0001:32'hffff_ffff]};
}
endgroup: cov_wdata
//
function new ();
begin wdata[31:0] = 32'd0;
addr_packet = new;
cov_wdata = new;
end
endfunction //
task gen_data;
wdata = $random;
$display ("Write data: %8h", wdata);
endtask: gen_data
//
function packet deep_copy;
//create a new object
deep_copy = new;
//deep copy of the object controlled by the handle addr_packet
deep_copy.addr_packet = this.addr_packet.deep_copy;
//copy the property of current object to the new object
deep_copy.wdata = this.wdata;
endfunction: deep_copy
//
endclass: packet
module class_ex_handle;
//
packet pkt_1;
packet pkt_2;
//
initial begin //
pkt_1 = new;
pkt_1.gen_data;
pkt_2 = pkt_1.deep_copy; //deep copy
$display ("- The handle value: \n\t 1 - pkt_1 is %d \n\t 2 - pkt_2 is %d", pkt_1, pkt_2);Trong ví dụ trên, các bạn hãy chú ý đến các đoạn code được tô màu xanh, method deep_copy được viết thêm cho mỗi class để đảm bảo có thể sao chép toàn vẹn các property và đối tượng mong muốn. Ý tưởng tổng quan về method deep_copy là:
$display ("- The handle value: \n\t 1 - pkt_1.addr_packet is %d \n\t 2 - pkt_2.addr_packet is %d", pkt_1.addr_packet, pkt_2.addr_packet);
$display ("- The handle value: \n\t 1 - pkt_1.cov_wdata is %d \n\t 2 - pkt_2.cov_wdata is %d", pkt_1.cov_wdata, pkt_2.cov_wdata);
//
$display ("1/ Check the write data and address after copying");
$display ("- Write data of pkt_1 is %8h", pkt_1.wdata);
$display ("- Write data of pkt_2 is %8h", pkt_2.wdata);
$display ("- Address of pkt_1 is %8h", pkt_1.addr_packet.addr);
$display ("- Address of pkt_2 is %8h", pkt_2.addr_packet.addr);
//
$display ("2/ Check the write data after changing wdata of pkt_2");
pkt_2.gen_data;
$display ("- Write data of pkt_1 is %8h", pkt_1.wdata);
$display ("- Write data of pkt_2 is %8h", pkt_2.wdata);
//
$display ("3/ Check the address after changing address of pkt_2");
pkt_2.addr_packet.gen_addr;
$display ("- Address of pkt_1 is %8h", pkt_1.addr_packet.addr);
$display ("- Address of pkt_2 is %8h", pkt_2.addr_packet.addr);
end //
endmodule: class_ex_handle
- Định nghĩa một function có kiểu dữ liệu trả về chính là kiểu mà class đó định nghĩa. Ví dụ, trong ctrl_packet, thì function được định nghĩa là "function ctrl_packet deep_copy".
- Trong function này, new được gọi để tạo ra đối tượng mới. Sau đó, handle của đối tượng này được gán cho giá trị trả về là deep_copy.
- Thực hiện sao chép các property của đối tượng hiện tại đến đối tượng mới được điều khiển bởi handle deep_copy, ví dụ như "deep_copy.addr = this.addr;" Như vậy, toàn bộ property của đối tượng hiện tại được sao chép đến đối tượng mới.
- Nếu trong class có khai báo một class instance khác, ví dụ như addr_packet trong class packet, thì trong method deep_copy của class này (packet), method deep_copy của addr_packet sẽ được gọi để tạo một đối tượng mới và lấy handle của nó gán cho addr_packet của handle deep_copy hiện tại.
Hình 7: Kết quả mô phỏng của ví dụ 5 |
Hình 8: Deep (full) copy |
- Mỗi handle thuộc về pkt_1 và pkt_2 đều có giá trị khác nhau. Điều này chứng tỏ chúng điều khiển các đối tượng được cấp phát ở các vùng bộ nhớ khác nhau.
- Lần kiểm tra thứ nhất chứng minh pkt_2 được sao chép chính xác từ pkt_1. Lần kiểm tra thứ hai và thứ 3 là để chứng minh việc sao chép này là "deep copy". Các biến của đối tượng được điều khiển bởi pkt_1 và pkt_2 là độc lập. Việc cập nhật giá trị của biến trong đối tượng này sẽ độc lập với đối tượng kia.
0 bình luận:
Đăng nhận xét