FSM với enum: chuyển trạng thái theo hợp đồng và kiểm không làm mất X

Khoảng 40 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ẽ

  • Viết next-state/output/state-register có ownership và reset rõ
  • Kiểm đủ bảng chuyển của ba state hợp lệ qua interface thực
  • Giữ4-state khi so output và phân biệt legal-state tests với fault recovery

Tên state giúp đọc, contract quyết định chuyển

Controller có IDLE 0, BUSY 1, DONE 2. Start chỉ làm IDLE sangBUSY; finish chỉ làm BUSY sangDONE; ack chỉ làm DONE vềIDLE. Tín hiệu cùng tên có thể mang contract khác trong thiết kế khác; đừng suy global priority từ thứ tự bit control.

State type làm encoding và nhãn dễ review

Ví dụ khai enum base logic 2 bit, gán nhãn 0/1/2 rõ. Không dùng arithmetic cộng state để giả làm trình tự giao dịch. Explicit labels thể hiện RTL encoding, nhưng synthesis vẫn có thể re-encode theo flow. Example dùng attribute riêng của Yosys để giữ binary cho cell check, không hứa mọi tool hoặc target giữ nguyên bits. [1]

Next-state và register làm hai việc khác nhau

Always_comb đặt next_state=state rồi chọn transition; always_ff lấy mẫu next_state hoặc reset đồng bộ vềIDLE. Done là decode của current state, không phải nhớ độc lập thêm một chu kỳ. Mỗi state register có một owner, output state_out là view để TB kiểm qua port.

Control không dùng trong state hiện tại phải không gây chuyển

Ở BUSY, start hoặc ack không thay điều kiện finish. Ở DONE, ack quyết định trở vềIDLE dù start/finish có giá trị nào. Vì có 3 state và 8 pattern control,24 ca normal phủ từng hàng của bảng bước đơn hợp lệ; một reset_mode khác kiểm thêm 24 ca ưu tiên reset.

Preload bằng reset và giao dịch thật

TB không force vào biến state. Muốn BUSY thì reset rồi phát start; muốn DONE thì thêm finish. Nó kiểm state/done sau từng setup trước khi áp pattern cần thử. Cách này tránh bỏ qua logic để đi tới state mà vẫn gọi là coverage.

Int cast trong checker có thể biến X thành 0

State_out là 4-state, nên checker dùng case inequality với expected 2 bit. Probe cố ý làm output ởBUSY thành X1: checker cũ castint có thể nhìn thành 1 và exit 0, còn checker mới runtime từ chối ngay lần chuyển vàoBUSY. Đây là lỗi của cách quan sát, không phải lý do đổi simulator 2-state hoặc sửa expected theo DUT.

Default recovery chưa được fault-inject không phải proof

Source có default next=IDLE, nhưng main không inject internal encoding 3/X hoặc mô hình metastability. Output-X mutation chỉ kiểm độ chặt của checker, không chứng minh mọi internal fault recovery. Dòng invalid_fault_injected=0 cố tình nhắc giới hạn ấy; không tắt warning hoặc bảo đảm enum khiến invalid state bất khả xảy ra.

48 ca bước đơn và cell count binary

Expected trước chạy: 24 normal/24 reset,12 advance/12 hold, done đúng ởDONE; generic 2 FF/0 latch với attribute đã nêu.

Thí nghiệm fsm-enum

Tính trước:3reachablestate x8control=24 normalvà 24 reset, mỗi state 4 advance 4 hold→12/12. Preloadqua reset/start/finish interface, không forceinternal.

controller.sv RTL
`timescale 1ns/1psmodule controller(input logic clk,rst_n,start,finish,ack,output logic done,output logic[1:0] state_out);  typedef enum logic[1:0] {IDLE=0,BUSY=1,DONE=2} state_t;  (* fsm_encoding="none" *) state_t state;  state_t next_state;  always_comb begin    next_state=state;    case(state)      IDLE:if(start)next_state=BUSY;      BUSY:if(finish)next_state=DONE;      DONE:if(ack)next_state=IDLE;      default:next_state=IDLE;    endcase  end  always_ff @(posedge clk)    if(!rst_n)state<=IDLE;else state<=next_state;  assign state_out=state;  assign done=(state==DONE);endmodule
tb_fsm.sv testbench
`timescale 1ns/1psmodule tb_fsm;  logic clk=0,rst_n=0,start=0,finish=0,ack=0;  wire done;  wire[1:0] state_out;  int target,control,reset_mode,expected_state,normal_cases=0,reset_cases=0,advances=0,holds=0;  controller dut(.*);  task automatic tick;    #1;clk=1;#1;clk=0;  endtask  task automatic preload(input int requested);    rst_n=0;start=0;finish=0;ack=0;tick();    if(state_out!==0||done!==0)$fatal(1,"FSM preload reset");    rst_n=1;    if(requested>=1)begin start=1;tick();start=0;end    if(requested==2)begin finish=1;tick();finish=0;end    if(state_out!==2'(requested)||done!==(requested==2))$fatal(1,"FSM reachable preload");  endtask  initial begin    for(reset_mode=0;reset_mode<2;reset_mode++)for(target=0;target<3;target++)for(control=0;control<8;control++)begin      preload(target);      start=control[2];finish=control[1];ack=control[0];rst_n=!reset_mode;      if(reset_mode)begin expected_state=0;reset_cases++;end      else begin        case(target)          0:expected_state=(control>=4)?1:0;          1:expected_state=((control/2)%2)?2:1;          default:expected_state=(control%2)?0:2;        endcase        normal_cases++;        if(expected_state==target)holds++;else advances++;      end      tick();      if(state_out!==2'(expected_state)||done!==(expected_state==2))$fatal(1,"FSM transition/reset target=%0d control=%0d",target,control);    end    if(normal_cases!=24||reset_cases!=24||advances!=12||holds!=12)$fatal(1,"FSM coverage accounting");    $display("FSM CHECK normal=24 reset=24 advances=12 holds=12 invalid_fault_injected=0");    $finish;  end  initial begin #1000;$fatal(1,"FSM watchdog");endendmodule

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

  • Phân tích và elaborate theo LRM (pyslang 12.1.0)
  • Lint -Wall (Verilator 5.050)
  • Mô phỏng, exit 0 và output khớp kỳ vọng tính tay (Icarus Verilog 11.0, 4-state)
  • Tổng hợp, số cell khớp kỳ vọng (Yosys 0.70)

Output phải khớp: FSM CHECK normal=24 reset=24 advances=12 holds=12 invalid_fault_injected=0

Kỳ vọng sau tổng hợp: controller: $_*DFF* = 2controller: $_*DLATCH* = 0

Primary 48 case trên legalstate 0/1/2 và 8 control, reset đồng bộ; preload qua interface. Không fault-inject internalencoding 3/X hoặc chứng minh fault recovery/formal liveness.

Yosys-specific fsm_encoding=none giữ binary 2 FF/0 latch cho generic check; không hứa encoding mọi target. State_out dùng case inequality, output X1 mutation bị bắt; không cast 2-state trước checker.

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.

Icarus giữ 4-state ở interface/checker; sample sau NBA và có watchdog. Không claim formal liveness, fault recovery hay physical reset sign-off. [2]

Tự kiểm tra

Tự kiểm tra: fsm-enum

  1. Ở BUSY, start 1/finish 0/ack 1: next state là gì?
  2. Có 3 state và 8 controlpattern: normal/reset mỗi loại bao nhiêu ca?
  3. Tổng normal advance/hold trong 24 ca là bao nhiêu?
  4. Vì sao không cast state_out thành int trước so sánh?
  5. Dòng invalid_fault_injected=0 nhắc điều gì?

Kiểm bảng chuyển và kiểm chính checker

Ghi state hợp lệ, control có ý nghĩa, reset và latency output. Dùng interface thật để setup, giữ X/Z tới phép so sánh, rồi thử một mutation làm dữ liệu quan sát sai. Một dòng PASS không đủ nếu checker đã xoá thông tin cần bắt.

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

  1. SystemVerilog FSM Enum Modeling — bản chụp resources.zip, Enum state. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. Thí nghiệm nguyên bản ex.b3.fsm-enum, Bằng chứng. Trích dẫn; nguồn đã đọc toàn văn.
  3. IEEE 1800-2023 SystemVerilog LRM, fsm-enum procedural/package semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  4. Thí nghiệm nguyên bản ex.b3.fsm-enum, Source, self-checks, metadata, verification.json và mutation probes. Trích dẫn; nguồn đã đọc toàn văn.
  5. IEEE 1800-2023 SystemVerilog LRM, fsm-enum procedural semantics. Chưa đối chiếu với văn bản nguồn (chưa mở).
  6. SystemVerilog FSM Enum Modeling — bản chụp resources.zip, Declaring States as an Enumerated Type; Decoding State with unique case; Casting Into the State Variable. Trích dẫn; nguồn đã đọc các mục liên quan.
  7. SystemVerilog always\_comb, always\_ff — bản chụp resources.zip, always_comb; always_ff. Trích dẫn; nguồn đã đọc các mục liên quan.