Compiler directives: macro tồn tại khác tín hiệu bằng1

Khoảng 30 phútMức 1/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 conditional compilation với nhánh if chạy trong mô phỏng
  • Đọc macro như text expansion và kiểm precedence
  • Giải thích include guard, undef và phạm vi compilation
  • Dùng default_nettype để giảm lỗi implicit net với giới hạn rõ

Có những nhánh không bao giờ được đưa vào simulation

Bạn tắt một feature bằng cách define macro thành 0 nhưng log vẫn cho biết nhánh feature được chọn. Nguyên nhân thường nằm ở câu hỏi mà ifdef kiểm: macro có tồn tại không, chứ không phải giá trị số của nó có khác 0 không.

Thí nghiệm đặt cùng hai con số 7/3 cho compile-time và runtime để làm khác biệt rõ: macro tồn tại dù mang text 0 chọn 7; runtime flag 0 chọn else 3. Đây là hai cơ chế khác nhau.

Preprocess, elaboration và runtime

Directive bắt đầu bằng backtick và tác động tới cách compiler xử lý source. Macro được thay và conditional compilation chọn phần source trước khi process mô phỏng chạy. Parameter/localparam cấu hình theo phạm vi và elaboration; if procedural chọn statement lúc process thực thi.

Vì vậy một tín hiệu thay đổi trong simulation không thể làm phần code bị ifdef loại bỏ tự xuất hiện lại. Khi debug, xác định câu hỏi thuộc giai đoạn nào trước khi nhìn waveform. [1]

Macro được define 0 vẫn là đã định nghĩa

Source define RDVA_FEATURE thành 0 rồi khai báo feature_on. Điều kiện ifdef đúng nên module này có assign 7. Sau undef macro, feature_off dùng else và assign 3. Hai module được instantiate trong cùng TB nhưng giữ phần source đã chọn ở nơi định nghĩa.

Runtime flag cũng bằng 0; procedural if lấy else 3. Muốn kiểm một giá trị số, đừng đọc ifdef như biểu thức if. Tên macro và nội dung expansion cần được xem tách nhau. [2]

Macro là thay thế văn bản, phải giữ ngoặc

Macro an toàn của bài bọc cả argument và toàn biểu thức: ((v) + (v)). Khi dùng argument 1+2 rồi nhân 3, kết quả là 18. Macro cố ý lỗi chỉ có v + v; expansion trở thành 1 + 2 + 1 + 2 * 3, kết quả 10 theo precedence.

Không phải simulator tính nhầm phép nhân; ta đã đưa cho nó một biểu thức khác. Macro có tác dụng lặp argument cũng không nên nhận biểu thức có side effect nếu chưa xét số lần evaluation. TB chỉ dùng hằng số, không kiểm side effect.

Parameter W 5 trong TB độc lập với macro width 4. Macro không tự có kiểu, phạm vi instance hay cơ chế override giống parameter. [3]

Include guard và cleanup có mục tiêu khác nhau

Include đưa nội dung file vào chỗ include. Header config.svh được include hai lần; guard ifndef/define ngăn phần định nghĩa được đưa lại khi guard còn tồn tại. Nó không tạo package hay scope riêng cho macro.

Cuối ví dụ, các macro demo được undef để hạn chế ảnh hưởng tới source đọc tiếp. Guard bảo vệ khỏi include lặp; cleanup giới hạn phần tiếp theo còn nhìn thấy macro. Nếu undef guard rồi include lại, guard không còn ngăn lần include ấy.

Tác động giữa các file phụ thuộc compilation-unit/tool invocation; bài chỉ kiểm cấu hình pipeline đã chạy. Không suy “macro luôn global qua mọi lần compiler chạy”. [4]

Default nettype kiểm lỗi implicit net

Ví dụ dùng default_nettype none và khai báo đầy đủ dây/port. Mục tiêu là không để một số lỗi gõ tên tạo implicit wire mà không có chủ ý. Sau vùng demo, source phục hồi wire để không áp chính sách lên code tiếp theo một cách bất ngờ.

Đây không phải spell checker cho mọi identifier. Gõ nhầm sang một tên đã khai báo vẫn có thể hợp lệ nhưng sai chức năng. Cần cả compile diagnostic lẫn test dữ liệu. Các quy tắc chính xác của directive còn [5]. [6]

Đối chiếu nhánh 7/3 và biểu thức 18/10

Kỳ vọng trước chạy: defined-zero chọn 7, sau undef chọn 3, runtime-zero chọn 3; macro width 4, parameter 5; expression có ngoặc 18 và thiếu ngoặc 10.

Macro, include guard và ifdef kiểm sự tồn tại

Kỳ vọng trước chạy: macro FEATURE được define 0 vẫn có tồn tại nên chọn 7; sau undef chọn 3; runtime flag 0 chọnelse 3.

config.svh testbench
`ifndef RDVA_DEMO_CONFIG_SVH`define RDVA_DEMO_CONFIG_SVH`define RDVA_WIDTH 4`define RDVA_TWICE(v) ((v) + (v))// Intentionally unsafe expansion for a precedence experiment.`define RDVA_BAD_TWICE(v) v + v`endif
tb_directives.sv testbench
`default_nettype none`include "config.svh"`include "config.svh"`define RDVA_FEATURE 0module feature_on(output wire [3:0] value);  timeunit 1ns;  timeprecision 1ps;`ifdef RDVA_FEATURE  assign value = 7;`else  assign value = 3;`endifendmodule`undef RDVA_FEATUREmodule feature_off(output wire [3:0] value);  timeunit 1ns;  timeprecision 1ps;`ifdef RDVA_FEATURE  assign value = 7;`else  assign value = 3;`endifendmodulemodule tb_directives;  timeunit 1ns;  timeprecision 1ps;  localparam W = 5;  reg [`RDVA_WIDTH-1:0] word;  wire [3:0] on_value, off_value;  integer good, bad, runtime_value;  reg runtime_flag = 0;  feature_on on_dut(on_value);  feature_off off_dut(off_value);  initial begin    word = '1;    good = `RDVA_TWICE(1 + 2) * 3;    bad = `RDVA_BAD_TWICE(1 + 2) * 3;    if (runtime_flag) runtime_value = 7; else runtime_value = 3;    #1;    if ($bits(word) != 4 || W != 5 || word !== 4'hf || good != 18 || bad != 10 ||        on_value !== 7 || off_value !== 3 || runtime_value != 3)      $fatal(1, "preprocessing or expression mismatch");    $display("DIRECTIVE CHECK defined_zero=%0d after_undef=%0d runtime_zero=%0d", on_value, off_value, runtime_value);    $display("MACRO CHECK width=%0d parameter=%0d safe=%0d unsafe=%0d", $bits(word), W, good, bad);    $finish;  endendmodule`undef RDVA_WIDTH`undef RDVA_TWICE`undef RDVA_BAD_TWICE`undef RDVA_DEMO_CONFIG_SVH`default_nettype wire

Đã 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: DIRECTIVE CHECK defined_zero=7 after_undef=3 runtime_zero=3MACRO CHECK width=4 parameter=5 safe=18 unsafe=10

Macro có ngoặc(1+2)*2*3=18; expansion thiếu ngoặc 1+2+1+2*3=10. Width macro 4 độc lập localparam 5.

Không claim mọi macro/include compilation-unit configuration, synthesis hoặc default_nettype bắt mọi lỗi typo.

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 assert mọi giá trị thay vì chỉ in lời “feature enabled”. Parser và Icarus đã thực chạy với header; không claim synthesis hoặc mọi tùy chọn preprocess của simulator khác. [7]

Đừng dùng macro thay mọi cấu hình có kiểu

  • Define FEATURE 0 rồi mong ifdef false.
  • Quên ngoặc quanh argument hoặc toàn macro biểu thức.
  • Nghĩ include giống instantiate module và tự tạo scope.
  • Coi include guard là cơ chế khóa phiên bản header.
  • Để macro demo hoặc default_nettype ảnh hưởng source tiếp theo ngoài ý định.
  • Dùng macro cho cấu hình từng instance rồi ngạc nhiên vì chúng không độc lập.
  • Cho rằng compile được chứng minh đúng thứ tự ưu tiên hay đúng chức năng.

Khi cần debug, xem expansion và file list thực tế, giảm ví dụ xuống một biểu thức có expected tính tay như 18/10. Với cấu hình số theo instance, ưu tiên xem parameter phù hợp hơn không.

Tự kiểm tra

Tự kiểm tra: directives

  1. RDVA_FEATURE được define 0; ifdef của feature_on trong ví dụ chọn giá trị nào?
  2. Macro safe và unsafe của bài, với argument 1+2 rồi nhân 3, lần lượt cho gì?
  3. Sau undef FEATURE, feature_off chọn giá trị nào?
  4. Include guard có tác dụng gì khi include cùng header lần nữa mà guard vẫn được định nghĩa?
  5. Default_nettype none có thay được checker chức năng không?

Xem code sau preprocess khi nghi macro

Hỏi macro có được định nghĩa không, expansion thành text gì, và phần text ấy được đọc trong compilation context nào. Chỉ sau đó mới phân tích process runtime.

Bài giữ draft/LRM needs-spec-check; ví dụ cố ý có macro lỗi để so sánh, không dùng macro ấy trong RTL sản phẩm.

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

  1. Verilog Compiler Directives — bản chụp trong resources.zip, Compiler directives. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. Verilog Compiler Directives — bản chụp trong resources.zip, ifdef/ifndef và undef. Trích dẫn; nguồn đã đọc các mục liên quan.
  3. Verilog Compiler Directives — bản chụp trong resources.zip, define. Trích dẫn; nguồn đã đọc các mục liên quan.
  4. Verilog Compiler Directives — bản chụp trong resources.zip, include và undef. Trích dẫn; nguồn đã đọc các mục liên quan.
  5. 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ở).
  6. Verilog Compiler Directives — bản chụp trong resources.zip, default_nettype. Trích dẫn; nguồn đã đọc các mục liên quan.
  7. Thí nghiệm nguyên bản ex.a7.directives, Bằng chứng nguyên bản. Trích dẫn; nguồn đã đọc toàn văn.
  8. IEEE 1800-2023 SystemVerilog LRM, directives language and system task semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  9. Thí nghiệm nguyên bản ex.a7.directives, Source, assertions và verification.json của TB nguyên bản. Trích dẫn; nguồn đã đọc toàn văn.
  10. IEEE 1800-2023 SystemVerilog LRM, Compiler directives; text macros; conditional compilation; default nettype. Chưa đối chiếu với văn bản nguồn (chưa mở).
  11. Verilog Compiler Directives — bản chụp trong resources.zip, define; include; ifdef/ifndef; undef; default_nettype. Trích dẫn; nguồn đã đọc các mục liên quan.
  12. Thí nghiệm nguyên bản ex.a7.directives, config.svh, tb_directives.sv và verification.json. Trích dẫn; nguồn đã đọc toàn văn.