Dạng sóng mà bạn thật sự cần
$display in ra văn bản. Như vậy là đủ cho một lần kiểm tra nhanh, nhưng ngay khi bạn phải gỡ lỗi một bộ đếm bị lỗi timing hay một máy trạng thái bị kẹt, bạn sẽ muốn một bức tranh: tín hiệu được vẽ như điện áp theo thời gian, cuộn được, phóng to được, có con trỏ để đặt lên bất kỳ lần chuyển trạng thái nào.
Bức tranh đó là một tệp VCD, viết tắt của "value change dump", định dạng văn bản mà IEEE chuẩn hóa ngay trong đặc tả Verilog đầu tiên. Mọi trình mô phỏng hiện đại đều ghi được, mọi trình xem dạng sóng hiện đại đều đọc được. Cái giá để tạo ra nó chỉ là hai dòng trong testbench.
Hai dòng lệnh
Trong một khối initial (thường là chính khối chứa kích thích), hãy thêm:
$dumpfile("dump.vcd");
$dumpvars(0, test);
$dumpfileđặt tên tệp đầu ra. Truyền vào đường dẫn nào cũng được; nếu không có thư mục, tệp sẽ nằm trong thư mục làm việc của trình mô phỏng.$dumpvars(0, test)nghĩa là "ghi lại mọi tín hiệu trong phạm vitestvà mọi phạm vi con, theo kiểu đệ quy, không giới hạn độ sâu".
Thiết lập chỉ có vậy. Chạy mô phỏng và bạn sẽ có tệp dump.vcd để mở trong GTKWave hoặc tab Waveform của trình soạn thảo trên trình duyệt.
Ví dụ hoàn chỉnh
Hãy chạy thử. Tab Waveform lúc này hiển thị ba tín hiệu clk, reset, count, được vẽ suốt toàn bộ thời gian mô phỏng. Bạn có thể đặt con trỏ ở bất kỳ điểm nào và đọc giá trị tín hiệu tại thời điểm đó. Bạn có thể kéo để phóng to và xem kỹ một chu kỳ clock.
Đối số đầu tiên của $dumpvars làm gì
$dumpvars(depth, scope) duyệt qua scope và mọi instance con bên trong, ghi lại tín hiệu sâu tối đa depth cấp. Các giá trị độ sâu:
0: không giới hạn. Mọi tín hiệu trongscopevà mọi module con lồng nhau đều được ghi.1: chỉ các tín hiệu khai báo trực tiếp trongscope. Module con không được ghi.2:scopecộng thêm một cấp module con.N:scopecộng thêm N-1 cấp module con.
Trên thực tế, hầu như testbench nào cũng dùng $dumpvars(0, test). Ghi lại mọi thứ rất rẻ (VCD chỉ lưu các lần chuyển trạng thái, không lưu trạng thái ổn định), và bạn không muốn đang gỡ lỗi giữa chừng mới phát hiện tín hiệu cần xem lại không được ghi.
Nếu thiết kế thật sự lớn và tệp VCD quá nặng, bạn có thể ghi có chọn lọc:
$dumpvars(0, dut.inner_module); // just the interesting sub-module
$dumpvars(0, dut.regs); // just the register file
Bạn có thể gọi $dumpvars nhiều lần để cộng dồn các phạm vi.
Ghi từng tín hiệu cụ thể
$dumpvars cũng có thể nhận một danh sách tín hiệu cụ thể thay vì cả một phạm vi:
$dumpvars(0, test.clk, test.reset, dut.count);
Lệnh này chỉ ghi ba tín hiệu đó. Hữu ích khi thiết kế cực lớn và bạn chỉ quan tâm vài tín hiệu. Với thiết kế nhỏ, ghi tất cả đơn giản hơn.
Điều khiển việc ghi giữa chừng
Có thêm hai lệnh đi cùng $dumpvars:
$dumpoff: tạm dừng ghi. Các thay đổi sau đó không được đưa vào VCD.$dumpon: ghi tiếp.
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test);
// ... interesting region ...
#1000 $dumpoff;
// ... long boring region we don't want in the VCD ...
#5000 $dumpon;
// ... another interesting region ...
end
Đó là cách bỏ qua đoạn "chạy một triệu chu kỳ" nhàm chán của một bài test dài mà không tạo ra tệp VCD nặng 5GB.
Xem kết quả
Có hai cách chính:
Trong trình soạn thảo trên trình duyệt
Trình soạn thảo trên trang này hiển thị VCD ngay tại chỗ. Chạy mô phỏng, chuyển sang tab Waveform, các tín hiệu hiện ra với cấu trúc phân cấp ở một bên và con trỏ kéo được ở bên kia. Bấm vào bất kỳ đâu trên một đường tín hiệu để đặt con trỏ; giá trị tín hiệu tại thời điểm đó hiện thành các nhãn nhỏ cạnh tên từng tín hiệu.
Trong GTKWave
Nếu bạn chạy mô phỏng trên máy (iverilog -o sim test.v rồi vvp sim), hãy mở tệp VCD tạo ra bằng:
gtkwave dump.vcd
GTKWave nạp tệp, hiển thị cây phạm vi ở bên trái và chờ bạn kéo tín hiệu vào vùng dạng sóng ở bên phải. Bấm chuột phải vào một tín hiệu nhiều bit để đổi cách hiển thị (nhị phân, thập lục phân, thập phân, analog). Dùng ô tìm kiếm ở dưới cùng để nhảy đến thời điểm hoặc lần chuyển trạng thái cụ thể.
Các mẫu thường gặp
Một đoạn hỗ trợ testbench nhỏ
Phần lớn tệp testbench mở đầu bằng đúng khối này. Bạn có thể giữ nó ngắn gọn bằng cách gộp mọi thứ vào một khối initial:
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test);
// ... stimulus ...
$finish;
end
Ghi có điều kiện
Trong môi trường kiểm thử mà bạn chỉ muốn có VCD cho các bài test thất bại:
initial begin
if (DUMP_VCD) begin
$dumpfile("dump.vcd");
$dumpvars(0, test);
end
// ... stimulus ...
end
DUMP_VCD là một parameter bạn đặt từ dòng lệnh hoặc định nghĩa tùy chế độ kiểm thử. Cách này tiết kiệm dung lượng đĩa trong các bộ kiểm thử hồi quy.
Ghi bộ nhớ
Theo mặc định, $dumpvars không ghi bộ nhớ (mảng unpacked) vì chúng có thể rất lớn. Hãy dùng $dumpvars(0, dut.memory) một cách tường minh nếu bạn cần, hoặc $dumpmem ở một số trình mô phỏng.
Lỗi thường gặp
Tệp VCD rỗng. Bạn quên $dumpfile hoặc $dumpvars, hoặc mô phỏng gọi $finish trước khi có tín hiệu nào thay đổi. Hãy chạy thêm ít nhất vài đơn vị thời gian sau phần thiết lập.
Tín hiệu bị thiếu trong dạng sóng. Phạm vi bạn truyền vào không bao gồm chúng. $dumpvars(0, dut) chỉ ghi những gì bên trong dut; nếu testbench điều khiển tín hiệu ở cấp test, các tín hiệu đó sẽ không xuất hiện. Luôn ghi từ phạm vi testbench: $dumpvars(0, test).
Tệp VCD quá lớn. Một lần mô phỏng dài với các tín hiệu rộng thay đổi liên tục sẽ tạo ra rất nhiều dòng VCD. Có ba cách khắc phục: ghi một phạm vi hẹp hơn, dùng $dumpoff/$dumpon quanh các đoạn nhàm chán, hoặc chuyển sang định dạng gọn hơn như FST (cả iverilog lẫn GTKWave đều hỗ trợ qua cờ -fst).
Tiếp theo
Bài cuối của chương này, Timescale và độ trễ, giải thích chỉ thị `timescale và cách #delay thật sự quy đổi ra thời gian thực. Sau đó bạn đã đi hết bộ tài liệu từ đầu đến cuối.
Câu hỏi thường gặp
Tệp VCD trong Verilog là gì?
VCD là viết tắt của Value Change Dump, một định dạng văn bản ghi lại mọi lần chuyển trạng thái của tín hiệu trong quá trình mô phỏng. Trình mô phỏng ghi giá trị ban đầu của từng tín hiệu tại thời điểm 0, rồi ghi mọi thay đổi kèm dấu thời gian. Các trình xem dạng sóng như GTKWave đọc tệp này và hiển thị thành giản đồ thời gian dạng đồ họa mà bạn có thể cuộn và phóng to.
Làm sao tạo tệp VCD trong Verilog?
Thêm hai lệnh hệ thống vào một khối initial trong testbench: $dumpfile("dump.vcd"); đặt tên tệp đầu ra, và $dumpvars(0, top_module); ghi lại mọi tín hiệu trong top_module và các cấp bên dưới. Khi mô phỏng kết thúc, bạn sẽ có tệp dump.vcd mở được bằng bất kỳ trình xem dạng sóng nào.
$dumpvars(0, ...) trong Verilog nghĩa là gì?
$dumpvars(depth, instance) ghi lại tín hiệu bắt đầu từ instance và đi sâu xuống depth cấp. $dumpvars(0, test) nghĩa là 'mọi tín hiệu trong phạm vi test và mọi phạm vi con, theo kiểu đệ quy': độ sâu 0 là trường hợp đặc biệt, nghĩa là không giới hạn. $dumpvars(1, test) chỉ ghi các tín hiệu khai báo trực tiếp trong test, không ghi trong module con nào được khởi tạo.
Vì sao tệp VCD trong Verilog bị rỗng?
Ba nguyên nhân thường gặp: bạn chưa hề gọi $dumpfile/$dumpvars; mô phỏng chạy đến $finish trước khi có tín hiệu nào thay đổi (hãy chạy thêm ít nhất vài đơn vị thời gian sau lệnh dump); hoặc phạm vi bạn truyền cho $dumpvars không khớp với cấu trúc phân cấp thật. Một chuỗi lệnh tối thiểu chạy được là $dumpfile("dump.vcd"); $dumpvars(0, test); #10; $finish;.