Professional Documents
Culture Documents
Xin lưu ý với các bạn, đây chỉ là cấu trúc cơ bản. Hiện nay, với sự phức tạp ngày càng tăng của các thiết
kế môi trường mô phỏng cũng được xây dựng dựa trên các giao thức và phương pháp được chuẩn hóa
ví dụ như OVM, VMM, UVM,... Các giao thức này phân chia môi trường thành nhiều thành phần với tên
gọi khác nhau nhưng vẫn không nằm ngoài 2 mục đích là tạo dữ liệu đầu vào và giám sát dữ liệu đầu ra
của thiết kế.
2. Mô tả DUT
Phần này mô tả DUT sẽ sử dụng trong bài viết này. DUT là một bộ đếm Johnson được mô tả chi tiết ở
link sau:
http://nguyenquanicd.blogspot.com/2017/08/verilog-rtl-code-mo-ta-cac-loai-bo-em.html
http://nguyenquanicd.blogspot.com/2017/08/questa-sim-huong-dan-cai-at-va-chay-mo.html
Trong đó:
N là số bit của DUT bộ đếm Johnson
VALUE_NUM là số giá trị cần phải kiểm tra của DUT
END_TIME là thời gian giới hạn của testbench khi chạy mô phỏng tính theo số chu kỳ xung
clock clk. Ở đây, sau 100 xung clock clk thì mô phỏng sẽ kết thúc.
3.4 Khai báo giao tiếp với DUT
reg clk;
reg rst_n;
wire [N-1:0] js_count;
Đây chính là các ngõ vào và ngõ ra của DUT, Trong đó:
Ngõ vào được khai báo kiểu biến (variable) để gán giá trị lái DUT
Ngõ ra khai báo kiểu net chỉ để giám sát
3.5 Gọi DUT
johnson_counter #(.N(N)) dut (.clk(clk), .rst_n(rst_n), .js_count(js_count));
Dòng code trên gọi module johnson_counter và thiết lập số bit cho bộ đếm là N = 4 bit.
reg [N-1:0] test_value[2*N-1:0];
integer i,j;
Trong đó:
test_value là một mảng dùng để lưu các giá trị cần kiểm tra, giá trị này sẽ được sử dụng để
so sánh với ngõ ra DUT để biết DUT có hoạt động đúng hay không.
i,j là hai biến lặp dùng để xây dựng Stimulus và Monitor
3.7 Tạo Stimulus
Tạo clock với chu kỳ 20 đơn vị thời gian. Đơn vị thời gian ở đây là 1ns, được quy định bởi 3.1. Nếu
không có định nghĩa trước về đơn vị thời gian thì testbench sẽ chạy theo đơn vị thời gian mặc định của
trình mô phỏng:
initial begin
clk = 0;
forever #10 clk = !clk;
end
Tạo reset tích cực trong 20ns ở hai điểm là thời điểm bắt đầu mô phỏng 0ns và sau khi
chạy 200ns. Điểm thứ 2 là để kiểm tra DUT có hoạt động chính xác khi bị reset trong lúc hoạt động hay
không.
initial begin
rst_n = 0;
#20
rst_n = 1;
#200
rst_n = 0;
#20
rst_n = 1;
end
initial begin
j = VALUE_NUM;
test_value[0] = 0;
for (i = 1; i < VALUE_NUM; i=i+1) begin
test_value[i] = {test_value[i-1][N-2:0], ~test_value[i-1][N-1]};
end
end
Chú ý, số lượng giá trị cần kiểm tra là VALUE_NUM sẽ được lưu theo thứ tự từ ô thứ 0 đến ô
thứ VALUE_NUM-1. Tuy nhiên, biến j được gán giá trị ban đầu ngoài tầm này, j = VALUE_NUM là để sử
dụng xác định thời điểm testbench bắt đầu kiểm tra sau này. Cụ thể, testbench chỉ kiểm tra bắt đầu tính
từ khi DUT được reset. Thời điểm bắt đầu mô phỏng cho đến trước khi reset, giá trị DUT là không xác
định nên không kiểm tra.
always @ (posedge clk) begin
if (~rst_n) j <= 0;
else if (j == VALUE_NUM-1) j <= 0;
else j <= j + 1;
end
always @ (*) begin
if (!rst_n && (j != VALUE_NUM) && (js_count[N-1:0] != test_value[j])) begin
$display ("--------------- SIMULATION FAIL ---------------");
$display ("[%t]The reset value of DUT: %b\tThe TEST value: %b", $time, js_count[N-1:0], test_value[j]);
$stop;
end
end
So sánh và kiểm tra ngõ ra DUT trong lúc hoạt động và không có reset:
always @ (posedge clk) begin
if (rst_n && (js_count[N-1:0] != test_value[j])) begin
$display ("--------------- SIMULATION FAIL ---------------");
$display ("[%t]The DUT value: %b\tThe TEST value: %b", $time, js_count[N-1:0], test_value[j]);
$stop;
end
end
Khối kiểm tra trên chỉ lấy mẫu kiểm tra tại các vị trí cạnh lên xung clock và báo lỗi bất cứ khi nào giá trị
ngõ ra DUT khác với giá trị kiểm tra. Mô phỏng cũng dừng ngay khi phát hiện lỗi.
initial begin
repeat (END_TIME) begin
@ (posedge clk);
end
$display ("[%t]--------------- SIMULATION PASS ---------------", $time);
$stop;
end
Kết thúc thời gian chạy mô phỏng, sau số cạnh lên xung clock clk được quy định
bởi END_TIME, testbench sẽ báo mô phỏng thành công với thông điệp "SIMULATION PASS". Điều kiện
PASS ở đây không cần vì chỉ cần xuất hiện 1 lỗi thì testbench đã dừng và báo FAIL ngay lập tức.
http://nguyenquanicd.blogspot.com/2017/08/questa-sim-huong-dan-cai-at-va-chay-mo.html
Ở đây, tôi chỉ phân tích kết quả mô phỏng. Nếu mô phỏng PASS, các bạn sẽ quan sát trên cửa sổ
Transcript hiện thông báo "SIMULATION PASS" và thời điểm kết thúc mô phỏng ngay trước thông điệp.
Đồng thời, một dòng báo vị trị code trong testbench làm ngắt mô phỏng, chính là dòng $stop trong
mục 3.9.
Hình 2. Thông điệp báo PASS trên cửa sổ transcript
Nếu giá trị reset bị sai, tác giả đã sửa giá trị reset trong RTL code của DUT thành 1 (lưu ý phải biên dịch
lại DUT), kết quả mô phỏng như sau:
always @ (posedge clk) begin
if (~rst_n)
js_count[N-1:0] <= 0;
else
js_count[N-1:0] <= js_count + 1;
end
Việc tạo một testbench kiểm tra cho phép kiểm tra nhiều trường hợp trong thời gian mô phỏng dài mà
không cần tốn công dò dạng sóng. Việc xem waveform chỉ thực hiện khi bạn mới xây dựng testbench để
kiểm tra tính đúng đắn của testbench và khi phát hiện lỗi để gỡ lỗi (debug).
Trên đây chỉ là một hướng dẫn cơ bản giúp bạn nhanh chóng viết một testbench. Để một DUT được
kiểm tra toàn diện và đảm bảo tính đúng đắn cao thì testbench và môi trường mô phỏng có thể cần thêm
nhiều thành phần khác.