Virtual interface: class truy cập đúng instance, đổi binding có kiểm tra
Sau bài này bạn sẽ
- Phân biệt interface instance tĩnh và virtual handle trong class
- Truyền modport view qua constructor và chặn null trước truy cập
- Kiểm rebind bằng oracle độc lập bên ngoài class
Driver là object, chân tín hiệu thuộc hierarchy
Một driver class cần điều khiển a/b của DUT nhưng class không tạo interface instance bên trong như module top. Top tạo left/right cùng hai DUT, rồi đưa một tham chiếu tới instance có sẵn cho driver. Virtual interface là cầu nối giữa object runtime và tín hiệu trong hierarchy.
Từ virtual ở đây không có nghĩa có một mạch mô phỏng khác hoặc driver tự clone tín hiệu. Nó mô tả biến tham chiếu có thể giữ binding tới một interface instance. Cũng đừng lẫn với virtual method trong OOP: dynamic dispatch của method và việc đổi đích truy cập tín hiệu giải hai vấn đề khác nhau.
Khai báo virtual handle chưa tạo tín hiệu
Property local virtual sum_link.driver vif lưu view driver của một instance sum_link. Khai báo này không tạo a/b/total và không thể thay cho sum_link left() ở top. Constructor của SumDriver tạo object driver; actual left.driver xác định bộ tín hiệu mà object sử dụng.
Vì giữ tham chiếu, nhiều object có thể được truyền cùng một physical interface. Chúng không tự có storage độc lập. Nếu cùng drive a/b, cần thiết kế ownership hoặc arbitration; gọi mỗi object bằng một tên khác không tránh tranh chấp. Main chỉ có một driver object để tập trung kiểm binding.
Constructor nhận đúng view trước khi chạy task
Interface định nghĩa calc cho DUT và driver cho class. Với driver, a/b là output và total là input. Constructor nhận virtual sum_link.driver link, kiểm null rồi gán property vif. Local property giúp code bên ngoài không tùy tiện thay binding, còn method bind_link là nơi đổi đích có kiểm tra.
Top gọi driver=new(left.driver). Đây là truyền một tham chiếu đã có, không phải gọi new để cấp phát interface. Nếu đổi W hoặc modport ở các bên, type của virtual interface phải tương thích. Bài chỉ kiểm một definition 4 bit và view driver; không dùng PASS này để tuyên bố mọi specialization đều gán lẫn nhau được.
Dừng ở cấu hình thiếu, trước khi ghi tín hiệu
Cả constructor và bind_link từ chối null bằng $fatal(1,"null virtual interface"). Guard báo lỗi ở điểm nhận cấu hình thay vì đợi một truy cập member xa hơn. Probe âm gọi new(null) kiểm đúng đường này, giữ message do ví dụ tự viết để không phụ thuộc diagnostic dereference của simulator.
Guard không chữa một handle hợp lệ nhưng trỏ nhầm instance. Left và right đều có type phù hợp nên compiler có thể chấp nhận việc dùng nhầm. Cần oracle kiểm routing ở top, không chỉ kiểm “vif khác null”. Khi làm UVM sau này, kiểm config_db get phải thành công cũng mới là một phần của bài toán kết nối.
Task có chờ thời gian, function chỉ đổi binding
Task drive_and_check ghi a/b qua vif, chờ #1 rồi kiểm tổng với hai argument int nhận từ caller. Task được phép tiêu thụ thời gian; constructor và function bind_link không chờ cạnh hoặc delay. Hai loại method này có trách nhiệm khác nhau.
Argument trong main luôn từ 0 tới 15 nên cast 4 bit giữ nguyên giá trị. Nếu mở API nhận số ngoài miền, phải thêm contract/guard hoặc định nghĩa rõ hành vi cắt bit; không giả định int tùy ý vẫn tương đương chân 4 bit. Main dùng mạch tổ hợp, không claim quy tắc drive/sample cho DUT tuần tự.
Đổi handle không sao chép hoặc xoá tín hiệu cũ
Driver quét 256 cặp vào left rồi gọi bind_link(right.driver) một lần và quét tiếp 256 cặp vào right. Khi xong left, giá trị cuối là a=15,b=15,total=30. Đổi binding chỉ đổi nơi các lần truy cập vif tiếp theo đi tới; nó không tự reset left.
Sau mỗi call vào right, top kiểm left vẫn giữ ba giá trị cuối. Với 256 call, có thêm 256 isolation check. Nếu cần dừng nhiều task đang chạy trước rebind, phải thêm đồng bộ riêng: ví dụ tuần tự ở đây không chứng minh rebind trong khi driver/monitor đồng thời hoạt động là an toàn.
Checker nằm trong class có thể cùng đi nhầm đường
Nếu bind_link quên gán vif, driver tiếp tục ghi left. Phép kiểm bên trong class vẫn đọc left và so với argument nên có thể hoàn toàn đúng dù mục tiêu right chưa được chạm tới. Do đó PASS của self-check bên trong chưa đủ chứng minh routing.
Oracle isolation ở top đọc trực tiếp left và bắt thay đổi ngay khi lượt right bắt đầu. Top cũng kiểm right.total cuối bằng 30, số call=512, số bind=1 và thời gian 512 ns. Mutant bỏ assignment trong bind_link bị oracle bên ngoài bắt. Đây là lý do cần kiểm cả dữ liệu lẫn đường đi của dữ liệu, thay vì thêm nhiều phép tính giống nhau vào cùng một class.
512 call, một lần rebind, 256 kiểm tra isolation
Expected trước chạy: VIF CHECK calls=512 bindings=1 isolated=256 right_last=30 time_ns=512. Main đã parse/simulate với Verilator; không dùng clocking block, không claim UVM config_db, null-dereference runtime hoặc độ chính xác four-state.
Hai đường âm tách riêng: bỏ đổi binding phải bị checker isolation bắt; đưa null vào constructor phải dừng bởi guard của ví dụ. Không sửa checker để chấp nhận driver còn ghi vào instance cũ.
Tự kiểm tra
Kiểm type, binding và ownership trước giá trị
Vẽ instance do top tạo, object do new tạo và virtual handle nối hai miền ấy. Kiểm null ở cấu hình, kiểm destination bằng quan sát độc lập, rồi kiểm dữ liệu sau thời điểm ổn định phù hợp. Handle đúng type không đủ bảo đảm đúng DUT; self-check đúng giá trị cũng chưa đủ bảo đảm đúng đường kết nối.
Nguồn và mức độ đối chiếu
- SystemVerilog Virtual Interface — bản chụp resources.zip, Why Do We Need Virtual Interfaces?; Basic Syntax; A Complete Example; The Most Common Pitfall: An Uninitialized Virtual Interface. Trích dẫn; nguồn đã đọc các mục liên quan.
- Thí nghiệm nguyên bản ex.b4.vif, Source, self-checks, verification.json và fault probes. Trích dẫn; nguồn đã đọc toàn văn.
- IEEE 1800-2023 SystemVerilog LRM, Interfaces, modports, parameterization and virtual interfaces — chưa đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).