Assignment và force/release: bỏ ép chưa chắc trở về giá trị cũ

Khoảng 35 phútMức 2/3Bản nháp: một số khẳng định chưa đối chiếu spec

Sau bài này bạn sẽ

  • Phân biệt continuous assignment với cập nhật biến trong process
  • Dự đoán khác biệt khi release net và variable trong ví dụ
  • Đọc legacy assign/deassign mà không đưa tùy tiện vào RTL sản phẩm
  • Kiểm driver và event tiếp theo thay vì dùng force để che lỗi

Hai output theo cùng source, nhưng khác nguồn cập nhật

Trong TB, một net được drive liên tục từ source; một biến được process gán khi source thay đổi. Bình thường cả hai đều theo source, nên nhìn waveform 0/1 ban đầu rất dễ tưởng hai cơ chế hoàn toàn giống nhau.

Ta cố ý force cả hai thành 1 trong khi source đã trở về 0. Sau đó release cả hai và quan sát khác biệt. Điểm cần học là đối tượng nào được ép, ai còn drive nó và sự kiện nào làm nó cập nhật tiếp.

Bài tham khảo nhóm assignment trong tài liệu người dùng cung cấp, viết lại giải thích và TB riêng. Tài liệu website là nguồn đào tạo; quy tắc đầy đủ vẫn [1].

Assign ở module và gán trong process

Hai đường trong ví dụ:

assign continuous_net = source;
always @(source) procedural_variable = source;

Dòng đầu đặt một continuous driver lên net. Dòng thứ hai là process: chờ source thay đổi, thực hiện phép gán biến, rồi quay lại chờ. Khi source đổi 1 rồi 0 và TB chờ các cập nhật ổn định, cả hai output đều được kiểm bằng 0.

Đây là hai cơ chế cụ thể, không phải bảng đầy đủ về mọi kiểu LHS hợp lệ trong mọi phiên bản Verilog/SystemVerilog. Chỉ nhìn từ khóa assign mà bỏ qua vị trí ở module hay bên trong process có thể khiến bạn đọc sai dạng assignment. [2]

Chữ reg/logic cũng không tự chứng minh có flip-flop. Ví dụ này chỉ là TB quan sát ngữ nghĩa, không có claim synthesis.

Force đang thắng không có nghĩa driver đã đổi

TB force net và variable bằng hằng 1'b1, sau đó cho source đổi 1 rồi 0. Hai output vẫn là 1, nhưng source thực sự bằng 0. Dòng FORCE RESULT net=1 variable=1 driver=0 phân biệt giá trị bị ép với dữ liệu đang đi tới nó.

Vì vậy nhìn output đang đúng dưới force không chứng minh logic gốc hoạt động đúng. Force có thể hữu ích để cô lập một đường debug hoặc tạo một tình huống kiểm tra có chủ đích; nó không phải cách sửa một phép nối hoặc driver sai trong thiết kế.

Ví dụ chỉ force hằng một bit trên hai đối tượng đã khai báo. Không suy ra hành vi của mọi RHS động, force xuyên port/hierarchy hoặc force từng bit từ kết quả này.

Release net khác release variable

Lúc release, source đang là 0:

  • Net có continuous driver đang tồn tại, nên sau ổn định nó trở về giá trị driver hiện tại: 0.
  • Biến giữ giá trị đã bị force là 1 cho tới một lần gán procedural tiếp theo trong ví dụ. Release không tự tạo một lần source đổi để đánh thức always @(source).

Vì vậy output thực là RELEASE RESULT net=0 variable=1. Đừng hiểu release như “hoàn tác về snapshot trước force”: thứ quyết định giá trị là loại đối tượng và các nguồn cập nhật đang có, không phải một ngăn lịch sử tự động.

Sau đó source đổi 1 rồi 0; process có sự kiện mới để chạy, và biến trở về 0. TB kiểm NEXT EVENT RESULT net=0 variable=0. [3]

Assign/deassign bên trong process là dạng legacy

TB còn đọc một dạng procedural continuous assignment:

assign legacy_variable = source;
// ...
deassign legacy_variable;

Các statement này nằm bên trong initial, khác continuous assign ở cấp module. Khi assign đang hoạt động, biến theo source tới 1. Deassign dừng cơ chế đó; khi source đổi về 0, biến vẫn giữ 1 trong ví dụ. Một phép gán procedural mới legacy_variable=0 sau đó mới đổi biến về 0.

Dòng kết quả LEGACY RESULT tracked=1 held=1 next_write=0 là ba giá trị được ghi nhận/kiểm ở ba giai đoạn, không phải ba driver đồng thời.

Ta học dạng này để đọc code cũ. Việc simulator hỗ trợ không phải lý do tự chọn nó cho RTL sản phẩm mới. Hãy dùng coding guideline và kiểm khả năng tổng hợp của flow cụ thể; bài không chạy synthesis cho các thao tác override này.

Kiểm 0/0, ép 1/1 rồi bỏ ép

Kỳ vọng tính trước khi chạy: bình thường 0/0; bị ép 1/1 dù source 0; sau release net 0/variable 1; sau event mới cả hai 0. Dạng legacy theo source 1, giữ 1 sau deassign và nhận 0 khi được ghi lại.

Release net khác release variable

Kiểm continuous assignment, process gán biến và force/release; đọc legacy assign/deassign bằng Icarus.

tb_assign.sv testbench
module tb_assign;  timeunit 1ns;  timeprecision 1ps;  reg source = 0;  wire continuous_net;  reg procedural_variable = 0;  reg legacy_variable = 0;  reg tracked, held;  assign continuous_net = source;  always @(source) procedural_variable = source;  initial begin    #1;    source = 1;    #1;    if (continuous_net !== 1 || procedural_variable !== 1) $fatal(1, "normal update failed");    source = 0;    #1;    if (continuous_net !== 0 || procedural_variable !== 0) $fatal(1, "normal clear failed");    $display("ASSIGN RESULT continuous=%b procedural=%b", continuous_net, procedural_variable);    force continuous_net = 1'b1;    force procedural_variable = 1'b1;    source = 1;    #1;    source = 0;    #1;    if (continuous_net !== 1 || procedural_variable !== 1 || source !== 0)      $fatal(1, "force failed");    $display("FORCE RESULT net=%b variable=%b driver=%b", continuous_net, procedural_variable, source);    release continuous_net;    release procedural_variable;    #1;    if (continuous_net !== 0 || procedural_variable !== 1) $fatal(1, "release semantics failed");    $display("RELEASE RESULT net=%b variable=%b", continuous_net, procedural_variable);    source = 1;    #1;    source = 0;    #1;    if (continuous_net !== 0 || procedural_variable !== 0) $fatal(1, "next event failed");    $display("NEXT EVENT RESULT net=%b variable=%b", continuous_net, procedural_variable);    assign legacy_variable = source;    source = 1;    #1;    if (legacy_variable !== 1) $fatal(1, "legacy assign did not track source");    tracked = legacy_variable;    deassign legacy_variable;    source = 0;    #1;    if (legacy_variable !== 1) $fatal(1, "deassign did not retain value");    held = legacy_variable;    legacy_variable = 0;    #1;    if (legacy_variable !== 0) $fatal(1, "procedural write after deassign failed");    $display("LEGACY RESULT tracked=%b held=%b next_write=%b", tracked, held, legacy_variable);    $finish;  endendmodule

Đã kiểm chứng thế nào

  • Phân tích và elaborate theo LRM (pyslang 12.1.0)
  • Mô phỏng, exit 0 và output khớp kỳ vọng tính tay (Icarus Verilog 11.0, 4-state)

Output phải khớp: ASSIGN RESULT continuous=0 procedural=0FORCE RESULT net=1 variable=1 driver=0RELEASE RESULT net=0 variable=1NEXT EVENT RESULT net=0 variable=0LEGACY RESULT tracked=1 held=1 next_write=0

TB dùng force/release và procedural assign/deassign để quan sát; không claim synthesis hoặc coi đây là RTL fix trong sản phẩm.

RHS legacy assign chỉ là source đơn; force dùng hằng 1. Không kết luận về mọi RHS động, port coercion hoặc force qua hierarchy.

Process của ví dụ gán biến khi source đổi; release không tự tạo source event. Các phép kiểm đợi 1 ns để quan sát ổn định.

Các kiểm tra chạy lúc xây dựng nội dung (2026-11-06), không chạy trong trình duyệt. Kỳ vọng được tính tay trước khi chạy.

TB dùng các phép so case inequality và $fatal sau từng giai đoạn, không chỉ in các con số rồi tự coi là pass. Đợi một bước thời gian để quan sát ổn định, nên không dùng thứ tự tùy ý giữa process source-change và phép kiểm làm đáp án.

Pyslang parse và Icarus simulation đã đạt; exit 0 và đủ output mới có claim simulated. [4]

Nếu source không đổi sau release thì sao?

Với đúng process always @(source) của ví dụ, biến không có một lịch clock riêng để tự ghi lại. Khi không có source event mới, không thể giả định nó sẽ trở về giá trị driver như net continuous.

Nếu đổi process thành cập nhật theo clock, quan hệ tiếp theo cần phân tích theo clock và loại phép gán. Nếu thêm một writer khác, cần phân tích cả tranh chấp. Bài không dùng một kết quả release để đoán mọi biến trong mọi thiết kế.

Trong debug thực tế, ghi lại đối tượng đã force, thời điểm bỏ force và điều kiện để driver bình thường cập nhật. Sau khi bỏ override, kiểm lại đường chức năng gốc trước khi kết luận bug đã hết.

Đừng sửa triệu chứng bằng một force bị quên

  • Dùng force làm output đúng rồi xem đó là bằng chứng DUT đã sửa.
  • Quên release, khiến phần kiểm sau không còn tác động tới output.
  • Mong mọi variable lập tức nhận lại dữ liệu nguồn sau release.
  • Nhầm assign cấp module với assign/deassign trong process.
  • Mở rộng kết quả hằng một bit của bài sang mọi force expression/port.
  • Gắn nhãn synthesizable vì TB đã parse và chạy được.

Tự kiểm tra

Tự kiểm tra: assign

  1. Source đang 0, net và variable đã bị force 1. Sau release và ổn định, output của ví dụ là gì?
  2. Khi hai output bị force 1 nhưng source 0, có thể kết luận driver gốc đã đúng không?
  3. Vì sao procedural_variable trở về 0 ở giai đoạn NEXT EVENT của bài?
  4. Legacy assign đã theo source tới 1, rồi deassign và source đổi 0. Biến giữ gì trước lần ghi procedural mới?
  5. TB parse và simulate được force/release có chứng minh nó là RTL synthesizable không?

Đọc loại đối tượng và driver trước khi release

Hỏi ba câu: đang ép net hay variable, driver bình thường là gì, và lúc nào có lần cập nhật tiếp theo? Sau đó kiểm output khi đang force, ngay sau release và sau sự kiện cập nhật bình thường.

Bài đã đối chiếu các phần nguồn đào tạo liên quan bằng thí nghiệm riêng, chưa thay cho kiểm toàn bộ quy tắc LRM. Trạng thái vẫn draft/needs-spec-check; không sao chép code/đáp án website vào bài rồi coi là bằng chứng chạy.

Nguồn và mức độ đối chiếu

  1. IEEE 1800-2023 SystemVerilog LRM, cần đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).
  2. Verilog Assignments — bản chụp trong resources.zip, Ba nhóm assignment trong nguồn đào tạo. Trích dẫn; nguồn đã đọc các mục liên quan.
  3. Procedural continuous assignments — bản chụp trong resources.zip, Giải thích net/reg khi release trong nguồn đào tạo. Trích dẫn; nguồn đã đọc các mục liên quan.
  4. Thử nghiệm nguyên bản ex.a7.assign, Bằng chứng thí nghiệm nguyên bản. Trích dẫn; nguồn đã đọc toàn văn.
  5. IEEE 1800-2023 SystemVerilog LRM, Continuous/procedural assignments; procedural continuous assignment; force/release. Chưa đối chiếu với văn bản nguồn (chưa mở).
  6. Thử nghiệm nguyên bản ex.a7.assign, TB nguyên bản và verification.json đã chạy. Trích dẫn; nguồn đã đọc toàn văn.
  7. Procedural continuous assignments — bản chụp trong resources.zip, assign and deassign; force and release. Trích dẫn; nguồn đã đọc các mục liên quan.
  8. Verilog Assignments — bản chụp trong resources.zip, Three Basic Assignment Forms; Procedural Assignment. Trích dẫn; nguồn đã đọc các mục liên quan.
  9. Thử nghiệm nguyên bản ex.a7.assign, content/code/ex.a7.assign/verification.json và tb_assign.sv. Trích dẫn; nguồn đã đọc toàn văn.