Plusargs và file I/O: kiểm cấu hình trước khi dùng dữ liệu

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

  • Tách kiểm có đối số, parse giá trị và kiểm miền hợp lệ
  • Đọc file theo return code, không dùng EOF như một bản ghi
  • Đối chiếu thứ tự hàng và giá trị bằng oracle độc lập

Đọc được một số chưa chắc đã có cấu hình hợp lệ

Regression muốn thay số mẫu mà không sửa và compile lại HDL. Plusargs đưa cấu hình runtime cho simulator; file I/O đưa stimulus hoặc kết quả qua file. Cả hai đều cần hợp đồng rõ: tên nào được nhận, giá trị nào hợp lệ và một bản ghi cần có những trường nào.

Ví dụ có COUNT mặc định 4, chỉ nhận 1..8. Mỗi hàng chứa index và giá trị 3+2×index, sau đó file có một hàng cố ý malformed. Checker không coi việc mở được file hay có đủ số dòng là đủ; nó kiểm cả return code, thứ tự và từng giá trị.

$test$plusargs kiểm prefix, không parse số

$test$plusargs("COUNT") trả khác 0 nếu có plusarg bắt đầu bằng COUNT. Nó có thể match cả +COUNT=8, +COUNT hoặc +COUNTDOWN=3; đây không phải equality tên đầy đủ. Return value cũng không phải số 8 được truyền vào.

Main kết hợp has_count với kết quả parse: nếu prefix xuất hiện mà không có giá trị COUNT hợp lệ thì dừng. Hợp đồng ví dụ dành prefix COUNT cho tùy chọn này, nên +COUNTDOWN bị từ chối thay vì âm thầm đổi số mẫu. Trong một ứng dụng có nhiều flag chung prefix, phải thiết kế tên/parser riêng, không xem guard đơn giản này là parser CLI tổng quát.

[1]

$value$plusargs lấy giá trị, kiểm miền là bước khác

$value$plusargs("COUNT=%d",n) thử parse số decimal. Khi không có match, n vẫn giữ mặc định 4. Khi có số, code vẫn phải kiểm n trong 1..8; parse +COUNT=0 thành công không làm cấu hình 0 hợp lệ.

Main được pipeline chạy không có đối số: supplied=0, count=4. Runner riêng chạy cùng executable với +COUNT=1 và +COUNT=8, đồng thời kiểm bốn nhánh từ chối. Không ghi các nhánh có đối số như đã được pipeline main mặc định tự chạy; bằng chứng bổ sung có command và oracle riêng.

Kiểm handle bằng khác 0, đóng trước khi mở đọc

Main gọi $fopen("samples.txt","w"), dừng nếu handle bằng 0, rồi ghi từng hàng bằng $fdisplay. Sau $fclose, nó mở lại mode r để đọc. Không kiểm handle phải dương: cách biểu diễn int của descriptor có thể có bit cao, còn sentinel thất bại là 0.

Mode w có thể tạo hoặc ghi đè file; vì thế runner thực thi trong thư mục tạm riêng, không ghi một đường dẫn dữ liệu người dùng. Không gán số handle cố định hoặc coi nó là index portable. File I/O này là của simulator trên máy chạy tool, khác với JSON backup của web app/Android.

[2]

Chỉ parse khi fgets thực sự đọc được một hàng

Vòng while dựa vào $fgets(line,fd)!=0. Khi không đọc thêm được dữ liệu, nó dừng trước phần xử lý hàng. Đây là cách tránh dùng nội dung cũ hoặc in thêm một bản ghi rỗng khi chỉ kiểm !$feof trước lần đọc.

Sau khi đọc được line, $sscanf(line,"%d %d",index,value) phải trả 2 mới có đủ hai field để checker tiêu thụ. Hàng bad-row trả không đủ nên tăng rejected và bỏ qua. Main cố ý không dùng một fscanf thất bại trong vòng lặp không tiến file; đọc nguyên dòng giúp luôn tiến qua hàng lỗi của fixture này.

[3]

Đúng số hàng chưa bảo đảm đúng dữ liệu

Oracle yêu cầu index bằng số hàng hợp lệ đã thấy và value bằng 3+2×rows. Nó không lấy index vừa parse để tự tính expected rồi bỏ qua thứ tự; nếu file đảo hàng, lỗi vẫn bị bắt. Chỉ khi qua hai điều kiện, rows tăng và sum cộng value.

Sau EOF, rows phải bằng n, rejected phải bằng 1 và sum phải bằng n×3+n×(n−1). Với n 4, các giá trị 3,5,7,9 cho tổng 24; với n 1 tổng 3; với n 8 tổng 80. Mutant ghi bước 3 thay bước 2 bị row oracle bắt, không đợi đến cuối mới nhìn một tổng có thể trùng.

Hai field parse được không có nghĩa grammar tùy ý đã hợp lệ

Fixture chỉ gồm hàng hai số đúng và một dòng bad-row. Sscanf trả 2 chưa chứng minh không có field thứ ba, trailing text, số quá lớn hoặc dòng vượt giới hạn mà một parser sản phẩm cần xử lý. Bài không hứa kiểm chặt mọi grammar CSV/text hoặc mọi input độc hại.

Nếu hợp đồng yêu cầu đúng hai token, phải thêm kiểm phần dư/giới hạn và test riêng trước khi dùng parser này cho file do người khác biên soạn. Tương tự, duplicate plusargs hay format malformed khác chưa được kiểm không được suy từ hai case n 1/n 8. Scope rõ giúp ví dụ nhỏ hữu ích mà không tạo cảm giác an toàn sai.

Mặc định 4 hàng, một hàng lỗi và tổng 24

Expected trước chạy: CMDLINE FILE CHECK count=4 supplied=0 rows=4 rejected=1 sum=24. Main parsed/simulated trên Verilator, tạo file tạm và đọc lại thật; không phải dữ liệu file được giả lập trong JavaScript.

Plusargs và file I/O: kiểm cấu hình trước khi dùng dữ liệu

Expected tính trước chạy: CMDLINE FILE CHECK count=4 supplied=0 rows=4 rejected=1 sum=24

tb.sv testbench
`timescale 1ns/1ps// Default n4, base3 ->values3/5/7/9,sum24. Prefix +COUNT requires separate parse/domain checks.module tb_cmdline_fileio; int n=4,base=3,fd,rc,index,value,rows=0,sum=0,rejected=0; bit has_count,got_count; string line; initial begin  has_count=$test$plusargs("COUNT");  got_count=$value$plusargs("COUNT=%d",n);  if(has_count&&!got_count)$fatal(1,"COUNT prefix without valid value");  if(n<1||n>8)$fatal(1,"COUNT outside 1..8");  fd=$fopen("samples.txt","w");if(fd==0)$fatal(1,"file write open");  for(int k=0;k<n;k++)$fdisplay(fd,"%0d %0d",k,base+2*k);  $fdisplay(fd,"bad-row");$fclose(fd);  fd=$fopen("samples.txt","r");if(fd==0)$fatal(1,"file read open");  while($fgets(line,fd)!=0)begin   rc=$sscanf(line,"%d %d",index,value);   if(rc!=2)begin rejected++;continue;end   if(index!=rows||value!=base+2*rows)$fatal(1,"file row oracle");   sum+=value;rows++;  end  $fclose(fd);  if(rows!=n||sum!=n*base+n*(n-1)||rejected!=1)$fatal(1,"file accounting");  $display("CMDLINE FILE CHECK count=%0d supplied=%0d rows=%0d rejected=%0d sum=%0d",n,got_count,rows,rejected,sum);$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 (Verilator 5.050, 2-state)

Output phải khớp: CMDLINE FILE CHECK count=4 supplied=0 rows=4 rejected=1 sum=24

Pipeline main không có plusargs: count 4. Probes riêng chạy n 1/n 8 và 4 case từ chối.

Fixture hai integer/hàng và 1 bad-row; không claim strict grammar, trailing tokens, overflow, duplicate args hoặc mọi input.

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.

Runner cùng executable: n 1/8 đạt với sum 3/80; n 0/9 bị domain guard, +COUNT và +COUNTDOWN=3 bị prefix-without-value guard. Đây là 2 positive/4 negative probe riêng, không phải sáu lần main đều exit 0.

[4]

Tự kiểm tra

Tự kiểm tra: cmdline-fileio

  1. COUNT mặc định 4 tạo các giá trị 3,5,7,9. Tổng là bao nhiêu?
  2. $test$plusargs("COUNT") có thể match những đối số nào?
  3. $value$plusargs đọc được COUNT=0. Cấu hình có hợp lệ cho main không?
  4. Điều kiện nào của main ngăn xử lý một hàng chưa đọc được?
  5. Sscanf trả 2 trong fixture có chứng minh mọi grammar text được kiểm chặt không?

Validate cấu hình, validate hàng, rồi mới tiêu thụ

Tách ba bước ở command line: nhận diện, chuyển kiểu, kiểm miền. Tách ba bước ở file: đọc thành công, parse đủ field, kiểm semantic theo oracle. Giữ summary đếm accepted/rejected và expected tính trước, chạy trong thư mục tạm, rồi mở rộng grammar có test khi yêu cầu thực tế tăng lên.

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

  1. SystemVerilog Command Line Arguments — bản chụp resources.zip, $test$plusargs: Checking Whether an Argument Was Passed; $value$plusargs: Extracting a Value. Trích dẫn; nguồn đã đọc các mục liên quan.
  2. SystemVerilog file operations — bản chụp resources.zip, How to Open and Close a File?; File Access Modes; How to Read and Write to a File?. Trích dẫn; nguồn đã đọc các mục liên quan.
  3. SystemVerilog file operations — bản chụp resources.zip, How to Read Until End of File?; How to Parse a Line for Values?. Trích dẫn; nguồn đã đọc các mục liên quan.
  4. Thí nghiệm nguyên bản ex.b4.cmdline-fileio, Source, self-checks, verification.json và timing/argument probes. Trích dẫn; nguồn đã đọc toàn văn.
  5. IEEE 1800-2023 SystemVerilog LRM, Clocking, program, stratified scheduling and simulation I/O — chưa đối chiếu LRM. Chưa đối chiếu với văn bản nguồn (chưa mở).