if, case và loops: chọn nhánh đúng khi có X/Z

Khoảng 40 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 ưu tiên if-else-if với phép so khớp của case
  • Dự đoán tác động của X/Z và wildcard thay vì gọi mọi unknown là0
  • Viết vòng lặp có điều kiện kết thúc và accumulator đúng
  • Phân biệt lặp tính toán với lặp có timing control

Cùng selector, các cấu trúc có thể chọn khác

Một decoder chọn đường theo sel hai bit. Với sel=11, bạn muốn ưu tiên bit cao; với sel có X/Z, bạn muốn phát hiện dữ liệu chưa hợp lệ. Hai mục tiêu ấy cần được nói rõ trước khi đổi if sang case để code trông gọn hơn.

TB của bài dùng cùng một selector cho if ưu tiên, plain case, casez và casex. Với 0z, kết quả lần lượt 0/3/1/1; với 0x là 0/3/3/1. Khác biệt xuất phát từ ngữ nghĩa chọn nhánh, không phải simulator tự “đoán” X/Z thành một giá trị điện áp.

if-else-if xác định ưu tiên

Decoder xét if(sel[1]), tiếp theo else if(sel[0]), cuối cùng else. Khi cả hai bit bằng 1, chỉ nhánh đầu chạy và trả mã 2. Hai if độc lập không có cùng ý nghĩa: cả hai có thể chạy, lần gán sau có thể ghi đè kết quả trước.

Một branch điều khiển một statement; dùng begin/end khi nhiều statement cùng thuộc nhánh. Indentation không tạo scope. Đặt default cho output tổ hợp và gán đủ mọi đường đi giúp diễn đạt rõ ý định không lưu state; phần tổng hợp cụ thể cần kiểm riêng, không suy ra từ tên biến reg. [1]

Điều kiện một bit và vector chứa unknown

Trong if(sel[1]), điều kiện chỉ một bit; 1 chọn then, còn 0/X/Z không chọn then. Vì vậy sel=0x khiến cả hai phép kiểm bit không đúng và decoder vào else 0. Đó là hành vi mô phỏng có thể che unknown, thường gọi là X optimism ở kiểu quyết định này.

Không được rút gọn thành “vector có bất kỳ X thì luôn false”. TB còn kiểm trực tiếp if(sel): sel=1x có bit 1 đã biết nên vector vẫn chắc chắn khác 0, chọn true. Vét cạn 16 selector bằng cặp 0/1/X/Z xác nhận cả priority, plain case và vector truth cho đúng ví dụ.

Nguồn đào tạo mô tả điều kiện dạng ngắn; bài giới hạn và kiểm lại trường hợp vector thay vì mở rộng câu ấy thành mọi quy tắc ngôn ngữ. Chi tiết đầy đủ còn [2].

Plain case so khớp đủ bốn trạng thái

Plain case so khớp từng bit, phân biệt 0,1,X,Z. Decoder chỉ liệt kê 00,01,10,11; mọi selector có X/Z rơi vào default 3. Default ở đây là mã báo không khớp của ví dụ, không phải chữ X và cũng không tự tạo assertion.

Nếu case item chứa X/Z, plain case đòi đúng các giá trị ấy; chúng không tự là wildcard. Bài không dùng item kiểu đó để mô tả phần cứng tổng hợp. Cũng không coi default là bằng chứng toàn bộ input đều hợp lệ: muốn fail trên opcode chưa biết phải có checker riêng. [3]

Wildcard có thể giấu dữ liệu chưa biết

casez bỏ qua vị trí Z hoặc dấu hỏi trong phép match; casex còn bỏ qua X. Wildcard có thể nằm ở expression hoặc item, nên sel 0z có thể match item 01 và trả 1 dù bit thấp chưa biết.

Ở sel 0x, casez của ví dụ về default 3, còn casex match 01 và trả 1. Nhưng “casez thấy X thì luôn default” cũng sai: sel 1x vẫn match item 1?, trả 2. Chính dấu hỏi ở item đã bỏ qua bit thấp chứa X.

Do đó cần biết chỗ nào được phép don't-care theo thiết kế và chỗ nào phải báo lỗi. Bài đưa casex vào để làm lộ rủi ro, không khuyến nghị dùng nó để làm regression hết đỏ. Khi nhiều item match, thứ tự viết là điều cần xét. [4]

for và while không tự tạo clock

for có khởi tạo, điều kiện và bước; while kiểm điều kiện trước mỗi lượt. Cả hai có thể chạy hết ở cùng một timestamp nếu thân không chứa timing control. TB tính tổng 0+1+2+3 bằng cả hai cách và kiểm tổng 6, thời gian trước/sau vẫn 275 ns.

Ngược lại, repeat(3) #2 có ba lần chờ 2 ns nên kết thúc 281 ns. Không phải từ khóa repeat tự làm mất sáu ns; timing control trong thân mới làm process chờ.

Popcount của DUT dùng for cộng 8 bit vào accumulator bằng blocking, reset accumulator 0 mỗi lần evaluation. Oracle TB tra số bit 1 của từng nibble rồi cộng hai kết quả, tránh lặp lại nguyên thuật toán DUT. Các loop RTL có biên hằng thường được tool triển khai thành cấu trúc; bài chưa chạy synthesis nên không claim topology hay số gate. [5]

16 selector, 256 byte và hai loại thời gian lặp

Tính trước chạy: if ưu tiên bit cao đã biết 1, plain case chỉ match bốn mã nhị phân; ba ca wildcard nêu ở trên có expected riêng. Mọi byte nhị phân 0..255 được kiểm popcount bằng bảng nibble; ff có 8 bit 1.

if, case, wildcard và vòng lặp có đối chiếu độc lập

Kiểm đủ 16 selector 4-state, các wildcard dễ che X/Z,256 byte popcount và thời gian for/while/repeat.

control.sv RTL
module control_logic (  input wire [1:0] sel,  input wire [7:0] data,  output reg [1:0] priority_result, exact_result, z_result, x_result,  output reg vector_true,  output reg [3:0] ones);  timeunit 1ns;  timeprecision 1ps;  integer i;  always @* begin    if (sel[1]) priority_result = 2;    else if (sel[0]) priority_result = 1;    else priority_result = 0;    if (sel) vector_true = 1; else vector_true = 0;    case (sel)      2'b00: exact_result = 0;      2'b01: exact_result = 1;      2'b10, 2'b11: exact_result = 2;      default: exact_result = 3;    endcase    casez (sel)      2'b1?: z_result = 2;      2'b01: z_result = 1;      2'b00: z_result = 0;      default: z_result = 3;    endcase    // Deliberately demonstrate why casex can hide an unknown selector.    casex (sel)      2'b1?: x_result = 2;      2'b01: x_result = 1;      2'b00: x_result = 0;      default: x_result = 3;    endcase    ones = 0;    for (i = 0; i < 8; i = i + 1) ones = ones + {3'b0, data[i]};  endendmodule
tb_control.sv testbench
module tb_control;  timeunit 1ns;  timeprecision 1ps;  reg [1:0] sel;  reg [7:0] data = 0;  wire [1:0] priority_result, exact_result, z_result, x_result;  wire vector_true;  wire [3:0] ones;  integer n, hi, lo, want_priority, want_exact, want_vector;  integer for_sum, while_sum, j;  time loop_start;  control_logic dut (.*);  function automatic reg state4(input integer index);    case (index)      0: state4 = 0;      1: state4 = 1;      2: state4 = 1'bx;      default: state4 = 1'bz;    endcase  endfunction  // Independent nibble lookup, rather than duplicating the DUT bit-sum loop.  function automatic integer nibble_ones(input reg [3:0] nibble);    case (nibble)      0: nibble_ones = 0;      1, 2, 4, 8: nibble_ones = 1;      3, 5, 6, 9, 10, 12: nibble_ones = 2;      7, 11, 13, 14: nibble_ones = 3;      15: nibble_ones = 4;      default: nibble_ones = -1;    endcase  endfunction  initial begin    for (n = 0; n < 16; n = n + 1) begin      hi = n / 4; lo = n % 4;      sel = {state4(hi), state4(lo)};      want_priority = (hi == 1) ? 2 : ((lo == 1) ? 1 : 0);      case (n)        0: want_exact = 0;        1: want_exact = 1;        4, 5: want_exact = 2;        default: want_exact = 3;      endcase      want_vector = (hi == 1 || lo == 1) ? 1 : 0;      #1;      if (priority_result !== want_priority[1:0] || exact_result !== want_exact[1:0] ||          vector_true !== want_vector[0]) $fatal(1, "branch mismatch sel=%b", sel);    end    $display("CONTROL CHECK four_state_selectors=16");    sel = 2'b0z; #1;    if (priority_result !== 0 || exact_result !== 3 || z_result !== 1 || x_result !== 1)      $fatal(1, "wildcard0z mismatch");    $display("CONTROL 0z if=%0d case=%0d casez=%0d casex=%0d", priority_result, exact_result, z_result, x_result);    sel = 2'b0x; #1;    if (priority_result !== 0 || exact_result !== 3 || z_result !== 3 || x_result !== 1)      $fatal(1, "wildcard0x mismatch");    $display("CONTROL 0x if=%0d case=%0d casez=%0d casex=%0d", priority_result, exact_result, z_result, x_result);    sel = 2'b1x; #1;    if (priority_result !== 2 || exact_result !== 3 || z_result !== 2 || vector_true !== 1)      $fatal(1, "known-one or wildcard1x mismatch");    $display("CONTROL 1x vector_true=%b case=%0d casez=%0d", vector_true, exact_result, z_result);    for (n = 0; n < 256; n = n + 1) begin      data = n[7:0]; #1;      if (ones !== (nibble_ones(data[7:4]) + nibble_ones(data[3:0])))        $fatal(1, "popcount mismatch data=%h ones=%0d", data, ones);    end    $display("CONTROL POPCOUNT inputs=256 ff_count=%0d", ones);    loop_start = $time;    for_sum = 0;    for (j = 0; j < 4; j = j + 1) for_sum = for_sum + j;    while_sum = 0; j = 0;    while (j < 4) begin while_sum = while_sum + j; j = j + 1; end    if (for_sum != 6 || while_sum != 6 || $time != loop_start || loop_start != 275)      $fatal(1, "zero-time loop mismatch");    repeat (3) #2;    if ($time != 281) $fatal(1, "repeat delay mismatch");    $display("CONTROL LOOPS for_sum=%0d while_sum=%0d start=%0d repeat_done=%0d", for_sum, while_sum, loop_start, $time);    $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: CONTROL CHECK four_state_selectors=16CONTROL 0z if=0 case=3 casez=1 casex=1CONTROL 0x if=0 case=3 casez=3 casex=1CONTROL 1x vector_true=1 case=3 casez=2CONTROL POPCOUNT inputs=256 ff_count=8CONTROL LOOPS for_sum=6 while_sum=6 start=275 repeat_done=281

Không claim synthesis/PPA hoặc coi casez/casex là giải pháp xử lý lỗi input; wildcard có thể che X/Z.

Vét cạn 16 selector cho priority/plain case/vector if; wildcard kiểm ba ca chỉ định, không claim vét cạn mọi pattern wildcard.

Popcount kiểm 256 byte nhị phân, không khái quát các input X/Z của phép cộng.

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.

16 lần chờ selector,3 ca đặc biệt và 256 byte tạo 275 ns trước các loop không delay. Repeat thêm 6 ns. Assertions kiểm cả dữ liệu lẫn thời gian; một vòng for có lỗi biên sẽ không được che bằng dòng output tổng hợp. [6]

Ranh giới cú pháp, mô phỏng và synthesis

  • Đổi if-else-if thành hai if rồi vô tình đổi ưu tiên.
  • Quên begin/end hoặc default cho output tổ hợp.
  • Dùng biến đếm quá hẹp làm nó quay vòng trước khi đạt điều kiện dừng.
  • Cộng dồn bằng NBA và mong mỗi iteration thấy accumulator mới ngay.
  • Nghĩ for 8 lượt luôn cần 8 clock, dù không có event/delay trong thân.
  • Coi wildcard che X/Z là sửa đúng nguyên nhân khởi tạo/reset.
  • Dùng kết quả simulation để tuyên bố synthesis/PPA chưa thực chạy.

Trong RTL sản phẩm, kiểu loop và điều kiện phải phù hợp synthesis flow; không khái quát rằng mọi while đều tổng hợp được hoặc mọi loop trong TB đều tự tiến thời gian. Các quy tắc cú pháp Verilog và SystemVerilog cũng khác: bộ ví dụ này biên dịch ở chế độ SystemVerilog.

Tự kiểm tra

Tự kiểm tra: control

  1. Decoder if(sel[1]) trả 2, else if(sel[0]) trả 1, else trả 0. Sel 11 chọn gì?
  2. Với sel 0x, plain case và casex của ví dụ lần lượt trả gì?
  3. Trong thí nghiệm, if(sel) với sel 1x chọn true hay false?
  4. for và while tính 0+1+2+3 không có timing control; bắt đầu 275 ns thì kết thúc khi nào?
  5. Trong popcount tổ hợp, tại sao reset accumulator 0 rồi dùng blocking ở từng iteration?

Kiểm nhánh biên trước khi tin biểu thức ngắn

Viết bảng quyết định cho trường hợp hai điều kiện cùng đúng, tất cả sai và input unknown. Với loop, kiểm giá trị khởi đầu, điều kiện thoát, độ rộng biến đếm và có hay không timing control.

Nguồn đào tạo đã đọc hỗ trợ cách phân loại; bằng chứng tool phủ các ca nêu rõ. Bài giữ draft/LRM needs-spec-check.

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

  1. Verilog if-else-if — bản chụp trong resources.zip, Syntax và ưu tiên. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. 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ở).
  3. Verilog case statement — bản chụp trong resources.zip, x and z in the case expression. Trích dẫn; nguồn đã đọc các mục liên quan.
  4. Verilog case statement — bản chụp trong resources.zip, casez and casex. Trích dẫn; nguồn đã đọc các mục liên quan.
  5. Verilog for Loop — bản chụp trong resources.zip, for loop. Trích dẫn; nguồn đã đọc các mục liên quan.
  6. Thí nghiệm nguyên bản ex.a7.control, Bằng chứng nguyên bản. Trích dẫn; nguồn đã đọc toàn văn.
  7. IEEE 1800-2023 SystemVerilog LRM, Conditional, case/casez/casex and loop statements; truth values. Chưa đối chiếu với văn bản nguồn (chưa mở).
  8. Thí nghiệm nguyên bản ex.a7.control, TB nguyên bản, assertions và verification.json. Trích dẫn; nguồn đã đọc toàn văn.
  9. IEEE 1800-2023 SystemVerilog LRM, Conditional/case/loop statements; logical truth values; wildcard matching. Chưa đối chiếu với văn bản nguồn (chưa mở).
  10. Verilog if-else-if — bản chụp trong resources.zip, Syntax; Priority in an if-else-if Chain. Trích dẫn; nguồn đã đọc các mục liên quan.
  11. Verilog case statement — bản chụp trong resources.zip, x and z in the case expression; casez and casex; How is a case different from if-else?. Trích dẫn; nguồn đã đọc các mục liên quan.
  12. Verilog for Loop — bản chụp trong resources.zip, Syntax; How Synthesis Handles a for Loop; for Loop in Combinational Logic. Trích dẫn; nguồn đã đọc các mục liên quan.
  13. Thí nghiệm nguyên bản ex.a7.control, control.sv, tb_control.sv và verification.json. Trích dẫn; nguồn đã đọc toàn văn.