"Thời gian" trong Verilog nghĩa là gì
Verilog không có sẵn khái niệm giây. Trình mô phỏng tiến theo "đơn vị thời gian", tức các nhịp số nguyên tùy ý, và code của bạn dùng #N để chờ N nhịp. Chỉ thị `timescale là thứ ánh xạ các nhịp đó sang thời gian thực.
`timescale 1ns / 1ps
module test;
initial begin
$display("t = %0t", $time); // 0
#5;
$display("t = %0t", $time); // 5 ns
#1.5;
$display("t = %0t", $time); // 6500 ps (or 6.5 ns)
$finish;
end
endmodule
Hai tham số:
- Đơn vị (số thứ nhất):
#1nghĩa là gì.1nsnói rằng một nhịp là một nano giây. - Độ chính xác (số thứ hai): mô phỏng theo dõi thời gian mịn đến mức nào bên trong đơn vị đó.
1psnói rằng các độ trễ thập phân được làm tròn đến pico giây.
Độ chính xác không được thô hơn đơn vị (1ps / 1ns là không hợp lệ). Các lựa chọn phổ biến:
1ns / 1ps: chuẩn trên thực tế. Mọi thứ tính bằng nano giây, độ chính xác dưới nano giây cho mọi độ trễ cổng logic.1ps / 1ps: khi mô hình hóa mạch cực nhanh hoặc khi mọi độ trễ đều dưới nano giây.1us / 1ns: cho mô phỏng chậm ở quy mô nhúng (thời gian truyền một byte UART, các giao thức chậm).
Đặt nó ở đâu
`timescale là một chỉ thị trình biên dịch, không phải cấu trúc ở cấp module. Nó nằm ở ngay đầu tệp, trước mọi module:
`timescale 1ns / 1ps
module foo(...);
// ...
endmodule
Phạm vi của nó là "từ điểm này trở đi theo thứ tự biên dịch, cho đến `timescale kế tiếp hoặc khi hết quá trình biên dịch". Điều này có một hệ quả khó thấy: tệp không khai báo `timescale rõ ràng sẽ thừa hưởng giá trị mà tệp được biên dịch trước đó khai báo, và điều này phụ thuộc vào thứ tự trình biên dịch đọc các tệp. Tránh bất ngờ bằng cách đặt `timescale ở đầu mọi tệp.
Trình soạn thảo trên trình duyệt trong bộ tài liệu này đặt sẵn một timescale mặc định cho bạn (thường là 1ns / 1ps), vì thế các ví dụ ở những bài trước chạy được mà không cần khai báo. Trong dự án thật, bạn nên khai báo rõ ràng.
#delay trong thực tế
Sau `timescale 1ns / 1ps:
#5 // wait 5 ns
#100 // wait 100 ns
#1.5 // wait 1.5 ns (precision allows it)
#0.001 // wait 1 ps (just barely above precision)
#0 // zero-delay; useful for ordering events at the same time
Bên trong khối initial hoặc always, #N chặn luồng thủ tục trong N đơn vị thời gian. Trình mô phỏng tạm dừng khối này (các khối chạy đồng thời khác vẫn tiếp tục) và chạy tiếp sau khoảng trễ đó.
Bạn có thể đặt độ trễ trước một phép gán:
#10 a = 1; // wait 10 ns, then assign
data <= #2 new_value; // schedule the non-blocking assignment 2 ns from now
Dạng thứ nhất là thứ không thể thiếu trong testbench. Dạng thứ hai (non-blocking có trễ) được dùng trong mô phỏng mức cổng để mô hình hóa độ trễ lan truyền.
Tạo xung clock
Mẫu kinh điển:
`timescale 1ns / 1ps
module test;
reg clk = 0;
always #5 clk = ~clk;
// ...
endmodule
Với timescale 1ns / 1ps, #5 là 5 ns. Xung clock đảo mỗi 5 ns, cho chu kỳ 10 ns, tức xung clock 100 MHz. Để đổi tần số, hãy đổi độ trễ:
| Nửa chu kỳ | Chu kỳ | Tần số |
|---|---|---|
#1 | 2 ns | 500 MHz |
#2.5 | 5 ns | 200 MHz |
#5 | 10 ns | 100 MHz |
#10 | 20 ns | 50 MHz |
#25 | 50 ns | 20 MHz |
#50 | 100 ns | 10 MHz |
Nếu bạn muốn nửa chu kỳ là số thập phân (#2.5), độ chính xác phải hỗ trợ được: độ chính xác 1ps xử lý được mọi giá trị nhỏ đến 0,001 ns, nên mọi tần số hợp lý đều ổn.
Trộn timescale giữa các tệp
Một thiết kế thật có nhiều tệp, và chúng có thể khai báo timescale khác nhau. Trình mô phỏng dùng timescale của chính từng tệp để hiểu các độ trễ trong tệp đó. Nếu module_a.v khai báo `timescale 1ns / 1ps và dùng #5, đó là 5 ns. Nếu module_b.v khai báo `timescale 1us / 1ns và dùng #5, đó là 5 us.
Điều này phần lớn không lộ ra vì trình mô phỏng dù sao cũng hiển thị một trục thời gian chung, nhưng nó có nghĩa là cùng một #N ở hai tệp có thể mang ý nghĩa khác xa nhau. Cách khắc phục: chọn một timescale (chuẩn trong ngành là 1ns / 1ps) và đặt nó ở đầu mọi tệp. Đừng trộn lẫn.
Độ trễ không tổng hợp được
Điểm then chốt: #delay chỉ tồn tại cho mô phỏng. Công cụ tổng hợp đọc:
// In synthesizable RTL - WRONG
always @(posedge clk) begin
out <= #2 in;
end
…và hoặc bỏ qua #2 (phần lớn công cụ) hoặc từ chối cấu trúc đó (các linter khắt khe hơn). Thời gian trong phần cứng thật được quyết định bởi xung clock và độ trễ lan truyền của cổng logic, cả hai đều không hiện ra trong mã nguồn.
Quy tắc: chỉ dùng # trong testbench. RTL tổng hợp được không có độ trễ #. Nếu bạn thấy mình muốn một độ trễ trong code tổng hợp được, thứ bạn thực sự cần là một bộ đếm lùi theo xung clock: đó là cách phần cứng thật "chờ".
$time và $realtime
Có hai cách đọc thời gian mô phỏng hiện tại:
$timetrả về số nguyên 64 bit tính theo đơn vị của timescale hiện tại.$realtimetrả về giá trịrealtính theo đơn vị của timescale hiện tại, nhưng với đầy đủ độ chính xác.
Để ghi log trong testbench, $time gần như luôn là đủ. Chỉ dùng $realtime khi bạn cần độ chính xác dưới một nhịp trong các câu lệnh in.
Mẹo thực hành
- Luôn khai báo
`timescaleở đầu mọi tệp.1ns / 1pslà mặc định an toàn. - Chỉ dùng
#delaytrong testbench. Coi việc không có nó trong code tổng hợp được là một quy tắc cố định. - Khớp chu kỳ clock với tần số mục tiêu. Nếu bạn đang mô phỏng một thiết kế 50 MHz, hãy dùng chu kỳ clock 20 ns: chu kỳ không khớp có thể che giấu các bug nhạy cảm về thời gian.
- Với tín hiệu kích thích đếm theo chu kỳ, dùng
@(posedge clk)thay cho#. Cách này vẫn đúng khi chu kỳ clock thay đổi.
Tiếp theo là gì
Bạn đã đi qua mọi bài trong bộ hướng dẫn Verilog này. Từ nền tảng ngôn ngữ (wire và reg, module, toán tử), qua các khối thủ tục và luồng điều khiển, đến thiết kế đồng bộ và máy trạng thái, và cuối cùng là công cụ testbench để chứng minh mọi thứ hoạt động. Đã đến lúc xây dựng một thứ gì đó: playground bên cạnh bộ tài liệu này dùng chính trình mô phỏng chúng ta đã dùng suốt từ đầu, sẵn sàng cho bất kỳ module nào bạn phác thảo.
Câu hỏi thường gặp
`timescale 1ns / 1ps trong Verilog nghĩa là gì?
Nó nói với trình mô phỏng: 'một đơn vị thời gian trong tệp này là một nano giây, và thời gian được theo dõi với độ chính xác pico giây.' Sau chỉ thị đó, #5 chờ 5 ns, #1.5 chờ 1,5 ns (làm tròn đến pico giây), và $time được báo theo nano giây. Số thứ nhất là đơn vị; số thứ hai là độ chính xác.
Có cần `timescale trong mọi tệp Verilog không?
Cách làm tốt nhất: có. Phạm vi của chỉ thị kết thúc ở `timescale kế tiếp hoặc khi hết quá trình biên dịch, nên tệp nào không có sẽ thừa hưởng giá trị mà tệp được biên dịch trước đó khai báo. Điều này khiến thời gian không còn tất định giữa các lần build. Đặt `timescale 1ns / 1ps ở đầu mọi tệp nguồn (đây là quy ước phổ biến nhất) và bạn sẽ không bao giờ gặp bất ngờ.
#5 trong Verilog nghĩa là gì?
#5 đẩy thời gian mô phỏng tiến thêm 5 đơn vị thời gian. Đơn vị lấy từ chỉ thị `timescale đang có hiệu lực. Với `timescale 1ns / 1ps, #5 là 5 nano giây. Với `timescale 1us / 1ns, #5 là 5 micro giây. Con số có thể là số thập phân: #1.5 dùng được nếu độ chính xác mịn hơn đơn vị.
#delay trong Verilog có tổng hợp được không?
Không. #delay chỉ ảnh hưởng đến mô phỏng: công cụ tổng hợp bỏ qua hoặc từ chối nó. Thời gian trong phần cứng thật đến từ tín hiệu clock và độ trễ lan truyền của các cổng logic, không phải từ câu lệnh #. Cứ thoải mái dùng # trong testbench; đừng bao giờ viết nó trong RTL tổng hợp được.