Menu

Vòng lặp for trong Verilog: được trải phẳng khi biên dịch

Vòng lặp for trong Verilog khác vòng lặp trong phần mềm ra sao: công cụ tổng hợp trải phẳng nó thành phần cứng song song, chứ không chạy lặp từng bước lúc thực thi.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Trông giống hệt phần mềm

Vòng lặp for của Verilog là bản sao của C:

for (i = 0; i < 8; i = i + 1) begin
    // body
end

Vẫn ba phần quen thuộc: khởi tạo, điều kiện, bước tăng. Thân vòng lặp chạy lại chừng nào điều kiện còn đúng.

Trong testbench, nó hoạt động đúng như bạn chờ đợi từ phần mềm. Trình mô phỏng lần lượt đi qua từng lần lặp:

Bốn lần lặp, bốn dòng kết quả. Không có gì bất ngờ.

Điều bất ngờ đến khi bạn đặt vòng lặp for vào code tổng hợp được.

Trải phẳng vòng lặp

Vòng lặp for trong một khối always tổng hợp được không trở thành vòng lặp lúc chạy trong phần cứng. Công cụ tổng hợp trải phẳng nó ở bước elaboration: khai triển vòng lặp thành N bản sao của thân, với N là số lần lặp:

Trông như một vòng lặp. Khi mô phỏng, trình mô phỏng đúng là lặp qua tám lần. Khi tổng hợp, vòng lặp được trải phẳng thành tám phép kiểm tra song song từ data[0] đến data[7], tất cả diễn ra cùng lúc. Công cụ tổng hợp thấy:

count = 0;
if (data[0]) count = count + 1;
if (data[1]) count = count + 1;
if (data[2]) count = count + 1;
...
if (data[7]) count = count + 1;

…rồi biến chuỗi đó thành một cây bộ cộng. Hành vi lúc chạy là "nhìn cả 8 bit cùng lúc và đếm có bao nhiêu bit bằng 1", chỉ trong một lượt tổ hợp.

Hệ quả là: vòng lặp for trong Verilog tổng hợp được không hề miễn phí. Một vòng lặp 64 lần thành 64 bản sao của thân trong phần cứng. Nếu thân phức tạp, bạn vừa dựng một khối tổ hợp rất lớn. Hãy dùng vòng lặp khi N nhỏ (vài lần đến vài chục lần). Với số lần lớn hơn, bạn thường cần một bộ đếm theo clock và một máy trạng thái.

Giới hạn phải là hằng số

Công cụ tổng hợp chỉ trải phẳng được vòng lặp nếu biết N ở bước elaboration. Nghĩa là giới hạn vòng lặp phải là hằng số:

// Works - bound is constant
for (i = 0; i < 8; i = i + 1) ...

// Works - bound is a parameter
for (i = 0; i < WIDTH; i = i + 1) ...

// Doesn't synthesize - bound depends on a runtime signal
for (i = 0; i < dynamic_count; i = i + 1) ...

Dạng cuối cùng có thể vẫn chạy khi mô phỏng, nhưng công cụ tổng hợp sẽ từ chối. Nếu bạn thật sự cần một vòng lặp đếm theo giá trị lúc chạy, hãy dựng nó bằng một máy trạng thái theo clock và một thanh ghi đếm: phần cứng không có vòng lặp với số lần lặp thay đổi theo kiểu phần mềm.

generate for và for thủ tục

Một cấu trúc riêng nhưng có liên quan là generate for, dùng genvar và nằm bên ngoài các khối always:

genvar i;
generate
    for (i = 0; i < 8; i = i + 1) begin : g
        bit_inverter inv(.x(in[i]), .y(out[i]));
    end
endgenerate

Đoạn này tạo ra 8 instance của bit_inverter (xem bài Khởi tạo module). Đây là mô tả cấu trúc, tức bạn đang nói "hãy tạo 8 bản sao của module con này", chứ không phải mô tả hành vi.

Phân biệt nhanh:

  • for thủ tục (bên trong always): trải phẳng các câu lệnh trong một khối hành vi duy nhất.
  • generate for (bên ngoài always): nhân bản toàn bộ cấu trúc: instance, câu lệnh assign, khối có tên.

Hãy dùng loại khớp với thứ bạn đang nhân bản.

Khi for tỏa sáng: thao tác trên vector

Vòng lặp phát huy tốt nhất khi bạn làm cùng một thao tác với mọi bit của một vector. Đếm số bit 1, tính bit chẵn lẻ, đảo thứ tự byte, tạo bảng tra:

32 lần lặp, mỗi lần gán một bit: dễ đọc hơn nhiều so với tự viết tay 32 phép gán wire. Công cụ tổng hợp trải phẳng nó rất gọn.

while, repeat, forever

Ngoài for, Verilog còn ba cấu trúc lặp khác, chủ yếu dùng trong testbench:

// Run until a condition fails
while (~done) begin
    @(posedge clk);
    cycles = cycles + 1;
end

// Run N times - simpler than for when you don't need a counter
repeat (8) @(posedge clk);

// Run forever - clock generators, monitoring loops
always #5 clk = ~clk;
forever begin
    @(posedge clk);
    $display("count=%0d", count);
end

while, repeat và forever chỉ tổng hợp được trong một số trường hợp hẹp (đáng chú ý là repeat với số lần là hằng số và thân theo clock). Trong testbench chúng là công cụ hữu ích; trong RTL tổng hợp được, hãy ưu tiên vòng lặp for có đếm cộng với một máy trạng thái tường minh.

for thủ tục trong testbench

Trong testbench, vòng lặp for hoạt động giống phần mềm. Cứ thoải mái dùng:

Hai vòng lặp lồng nhau quét mọi tổ hợp của hai đầu vào 2 bit. Trình mô phỏng chạy các lần lặp tuần tự. Không cần lo chuyện trải phẳng, vì testbench không được tổng hợp.

Lỗi thường gặp

Dùng vòng lặp for với giới hạn không phải hằng số trong code tổng hợp được. Công cụ tổng hợp sẽ từ chối. Nếu giới hạn chỉ biết lúc chạy, hãy dựng một bộ đếm và một máy trạng thái.

Quên rằng thân vòng lặp trở thành phần cứng song song. Một vòng lặp 64 lần có phép nhân trong thân là 64 bộ nhân song song, có lẽ không phải điều bạn muốn. Với đường dữ liệu rộng, hãy dựng một bộ nhân duy nhất và cấp dữ liệu cho nó tuần tự.

Trộn integer i với một reg cũng tên i. Hai thứ này thuộc hai phạm vi khác nhau; bên trong vòng lặp, biến integer được ưu tiên. Hãy đặt tên rõ ràng để tránh nhầm lẫn.

Tiếp theo

Giờ bạn đã có mọi cấu trúc thủ tục mà Verilog cung cấp. Chương tiếp theo ghép tất cả lại thành những mẫu mà người thiết kế số thật sự dùng trong sản phẩm: Logic theo clock với flip-flop, thanh ghi và pipeline, cùng Máy trạng thái hữu hạn, cách viết chuẩn cho mọi bộ điều khiển có nhiều chế độ hoạt động.

Câu hỏi thường gặp

Vòng lặp for trong Verilog hoạt động thế nào?

Về cú pháp, nó trông giống C: for (i = 0; i < N; i = i + 1) statement;. Nhưng với code tổng hợp được, vòng lặp được trải phẳng ở bước elaboration: công cụ tổng hợp khai triển nó thành N bản sao của thân vòng lặp. Không có bộ đếm vòng lặp lúc chạy và phần cứng cũng không lặp gì cả. Trong testbench, vòng lặp for hoạt động như vòng lặp phần mềm vì trình mô phỏng có thể đi qua từng lần lặp một cách tuần tự.

Vòng lặp for trong Verilog có tổng hợp được không?

Có, nhưng chỉ khi giới hạn vòng lặp là hằng số đã biết ở bước elaboration. Công cụ tổng hợp trải phẳng vòng lặp thành N bản sao song song của thân vòng lặp. Nếu giới hạn phụ thuộc vào một tín hiệu lúc chạy, vòng lặp không tổng hợp được: bạn phải chuyển nó thành một thiết kế tuần tự theo clock.

for và generate for trong Verilog khác nhau thế nào?

Vòng lặp for bên trong khối always là cấu trúc thủ tục, được tổng hợp bằng cách trải phẳng. Vòng lặp generate for (dùng genvar) là cấu trúc tường minh ở bước elaboration, dùng để nhân bản phần cứng cấu trúc: nhiều instance module, nhiều wire, nhiều câu lệnh assign. Dùng for bên trong khối thủ tục; dùng generate for bên ngoài chúng để nhân bản cấu trúc.

Verilog có vòng lặp while không?

Có: while (condition) statement;. Nó chỉ tổng hợp được khi công cụ tổng hợp chứng minh được vòng lặp kết thúc sau một số lần lặp có giới hạn. Trên thực tế điều này hiếm gặp, nên while chủ yếu xuất hiện trong testbench và code chỉ dùng cho mô phỏng. Để lặp trong code tổng hợp được, hãy dùng vòng lặp for có đếm.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu