Plusargs và file I/O: kiểm cấu hình trước khi dùng dữ liệu
Tiên quyết
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.
$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.
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.
Đú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.
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.
Tự kiểm tra
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
- 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.
- 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.
- 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.
- 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.
- 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ở).