--- name: rtl-constraint description: Enforces synthesizable SystemVerilog RTL constraints for DUT code. Requires full assignment in always_comb to avoid latches, synthesizable-only constructs, a single driver per signal, constant loop bounds, no combinational loops, reset via if(!rst_n) not declaration inits, and top modules that are mostly wiring. Use when generating, writing, or editing RTL in 01_RTL or DUT modules. Do not apply to testbench, PATTERN, TESTBED, or CHECKER. --- # rtl-constraint 產生或修改 **DUT / `01_RTL`** 時**全程強制執行**。管的是能不能變成硬體,不管排版(排版走 `design-format`)。timing 優化不在這裡:只有使用者明確要優化時才套 `rtl-opt`。 **不套用**於 `00_TESTBED`、`PATTERN`、`TESTBED`、`CHECKER`。 --- ## 1. always_comb 禁止 Latch `always_comb` 裡**每個輸出在每個分支都要有值**。漏 assign 會推斷 latch。 做法擇一: - 進塊先給 default,再依條件覆蓋 - 每個 `if` / `else` / `case` 分支都寫齊所有輸出,`case` 必有 **`default`** 沒有規格要 latch,就不要寫 `always_latch`。 ```systemverilog // 正確 — 先 default always_comb begin next_state = IDLE; OutValid = 0; if(state == PROCESS) begin next_state = OUT; OutValid = 1; end end // 正確 — 每個分支都寫齊 always_comb begin case(state) IDLE: next_state = (InValid)? PROCESS : IDLE; PROCESS: next_state = OUT; default: next_state = IDLE; endcase end // 錯誤 — PROCESS 沒賦值 next_state → latch always_comb begin if(state == IDLE) next_state = PROCESS; end ``` `always_comb` 用 **`=`**,`always_ff` 用 **`<=`**。同一塊裡不准混。 --- ## 2. 只能寫可合成 RTL DUT 只寫合成器能對應到硬體的語法。 **禁止:** - `#delay`(`#10`、`#(`CLK_PERIOD)` 都算) - `initial`(ROM / memory 用 `$readmemh` 或合成器接受的初始化除外) - `$display`、`$finish`、`$fatal`、`$error` - `force` / `release` - `fork` / `join`、`wait`、`@event` - `real`、`time`、`realtime` - `casex` - `always @(posedge ...)`、`always @(*)`(用 `always_ff` / `always_comb`) ```systemverilog // 錯誤 — sim-only,不能進 01_RTL initial clk = 0; always #5 clk = ~clk; $display("state=%0d", state); ``` --- ## 3. 每個輸出單一驅動 每個 signal **只能被一個 always block(或一個 `assign`)驅動**。兩個 `always_*`、或 `assign` 加 `always` 同時寫同一個 net = 多重驅動,不是合法硬體。 ```systemverilog // 正確 — OutValid 只在這一個 always_ff always_ff @(posedge clk or negedge rst_n) begin if(!rst_n) OutValid <= 0; else OutValid <= next_valid; end // 錯誤 — 兩個 block 寫同一個 OutValid always_ff @(posedge clk) OutValid <= 1; always_comb OutValid = InValid; ``` 讀(右值)可以出現在很多地方;**寫(左值)只能一處**。 --- ## 4. 迴圈邊界必須是常數 `for` 的上下界只能是 **`localparam`**、`` `define ``、**`genvar`**,或數字字面值。合成器要在編譯期展開成固定硬體。 ```systemverilog // 正確 localparam int N = 8; for(int i = 0; i < N; i++) acc = acc + x[i]; for(genvar s = 0; s < `PIPE_STAGE; s++) begin : PIPE_STAGE_GEN ... end // 錯誤 — runtime bound,無法展開 for(int i = 0; i < cnt; i++) acc = acc + x[i]; ``` --- ## 5. 禁止組合迴圈 `always_comb` / `assign` 的依賴不能成環:A 依賴 B、B 又依賴 A。那不是合法 combo 邏輯。 ```systemverilog // 錯誤 always_comb a = b + 1; always_comb b = a + 1; assign x = y & en; assign y = x | d; ``` 資料必須單向流:input / register → combo → register / output。 --- ## 6. 宣告時的 `= 0` 不能當 reset `logic [7:0] x = 0;` 在 FPGA 有時當 power-up,**ASIC 通常不當 reset**。要初值就進 **`if(!rst_n)`**。 ```systemverilog // 正確 logic [7:0] x; always_ff @(posedge clk or negedge rst_n) begin if(!rst_n) x <= 0; else x <= x_n; end // 錯誤 — 宣告賦值當 reset logic [7:0] x = 0; always_ff @(posedge clk) x <= x_n; ``` --- ## 7. Top module 以接線為主 **Top** 盡量只做 submodule instantiate 與 port map,不要塞多餘邏輯(FSM、運算、pipeline)。glue 最多到 signal rename / 串接。 ```systemverilog // 正確 — top 只接線 module EVD ( input clk, input rst_n, input InValid, output logic OutValid ); logic signed [`DATA_WIDTH-1:0] idu_out [0:`MATRIX_SIZE-1]; IDU u_IDU ( .clk(clk), .rst_n(rst_n), .OutData(idu_out) ); QRD u_QRD ( .clk(clk), .rst_n(rst_n), .InData(idu_out), .OutValid(OutValid) ); endmodule // 錯誤 — top 裡直接寫 FSM / datapath module EVD (...); always_ff @(posedge clk or negedge rst_n) begin if(!rst_n) state <= IDLE; else state <= next_state; end always_comb acc = a + b * k; endmodule ```