assign mô tả một sự thật cố định
Khai báo wire tạo ra một tín hiệu. Câu lệnh assign mô tả thứ gì điều khiển nó. Mối quan hệ là liên tục: vế phải là gì thì vế trái bằng đúng thứ đó, tại mọi thời điểm trong thời gian mô phỏng:
wire y;
assign y = a & b;
Cặp dòng đó ngụ ý hai điều:
ytồn tại như một wire trong mạch.yluôn luôn là phép AND từng bit củaavàb. Đổi một trong hai đầu vào thìythay đổi theo.
Không có clock, cũng không có sự kiện nào kích hoạt việc cập nhật. Trình mô phỏng thấy a hoặc b thay đổi, đánh dấu y cần tính lại và tính lại biểu thức. Trong phần cứng, đoạn này ứng với vài cổng AND: tổ hợp, không trạng thái, tức thời.
Dạng gộp
Bạn có thể gộp khai báo và phép gán trên một dòng:
wire y = a & b;
Dòng này tương đương hai dòng ở trên. Rất tiện cho các wire nội bộ mà không ai bên ngoài phạm vi này quan tâm. Nhiều bộ quy tắc viết code thậm chí thích dạng gộp hơn vì nó đặt khai báo ngay cạnh phương trình.
assign điều khiển được gì
Đích của assign phải là một kiểu net: trong Verilog thuần, đó là wire (hoặc một vài họ hàng hiếm gặp hơn như tri, wand, wor). Nó không thể là reg. Nếu bạn lỡ khai báo đích là reg:
reg y;
assign y = a & b; // ERROR: cannot drive reg with assign
Trình biên dịch sẽ báo lỗi. Hãy đổi đích thành wire, hoặc chuyển logic vào một khối always @(*), nơi reg là đích hợp lệ.
Vế phải có thể là bất cứ thứ gì tính ra một giá trị: hằng số, tín hiệu, parameter, biểu thức toán tử, lời gọi hàm. Nó có thể trộn các đầu vào có độ rộng khác nhau; các quy tắc mở rộng độ rộng thông thường được áp dụng.
Khi nào dùng assign, khi nào dùng always
Cả hai đều tạo được logic tổ hợp. Lựa chọn chủ yếu phụ thuộc vào việc code đọc ra sao:
assignhợp nhất khi mối quan hệ là một biểu thức duy nhất. Bộ cộng, mux đơn giản viết bằng?:, mặt nạ, bit chẵn lẻ, mọi thứ viết được trong một dòng.always @(*)hợp nhất khi bạn cần câu lệnh thủ tục. Câu lệnhcasenhiều nhánh,if/else iflồng nhau, mọi thứ có lợi khi có cácregtrung gian được đặt tên. Nội dung này nằm trong bài Khối always.
Đây là cùng một mux 4 sang 1 viết theo cả hai cách:
Cả hai module đều được tổng hợp thành gần như cùng một bộ ghép kênh. Bản assign chỉ một dòng code; bản always thì sáu dòng. Với bốn trường hợp thì hai bên ngang nhau; với mười sáu trường hợp thì khối case rõ ràng dễ đọc hơn.
Các mẫu thường gặp
Logic tổ hợp thuần
assign sum = a + b;
assign carry = a[7] & b[7];
assign equal = (data == 8'hFF);
Một biểu thức, một wire. Đây là công việc hằng ngày của assign.
Mux 2 sang 1
assign out = sel ? a : b;
Một biểu thức điều kiện duy nhất: công cụ tổng hợp biến nó thành một mux 2 sang 1. Đây là cách viết gọn nhất cho ý "chọn giữa a và b".
Gói bit
assign status = {error, overflow, ready, busy, 4'b0};
Ghép nối ở vế phải của assign là cách gói các cờ vào một byte trạng thái. Kết quả được tính và điều khiển liên tục.
Đầu ra ba trạng thái
assign data_pin = output_enable ? data_out : 1'bz;
Khi output_enable ở mức cao, chân được điều khiển. Khi ở mức thấp, chân được nhả về trạng thái trở kháng cao. Đây là mẫu kinh điển ở các chân chip, nơi nhiều nguồn điều khiển có thể dùng chung một wire.
Tính đồng thời: nhiều assign không chạy tuần tự
Một lời nhắc không bao giờ thừa: nhiều câu lệnh assign trong cùng một module đều chạy song song. Chúng không phải là một chuỗi tuần tự:
assign y = a & b; // exists at all times
assign z = a | b; // also exists at all times, independently
Thứ tự trong tệp không quan trọng. Cả hai phương trình đều đúng đồng thời. Công cụ tổng hợp có thể đặt cổng AND trước hay sau cổng OR; điều đó không quan trọng, cả hai cổng đều hoạt động liên tục.
Nếu bạn muốn hành vi trông có vẻ tuần tự, bạn cần đến khối always (và có lẽ cả clock). Đó là chuyện của một chương khác.
Nhiều nguồn điều khiển: mẫu bus
Một wire có thể là đích của nhiều câu lệnh assign, nhưng bạn gần như không bao giờ muốn điều này, trừ bus ba trạng thái. Hai nguồn tranh nhau điều khiển một wire tạo ra hành vi không xác định:
assign y = a;
assign y = b; // BAD - two drivers, simulator picks one or x's it out
Mẫu hợp lệ: mỗi nguồn nhả về z khi không hoạt động, và tại mỗi thời điểm chỉ có tối đa một nguồn hoạt động.
assign bus = device_a_active ? data_from_a : 1'bz;
assign bus = device_b_active ? data_from_b : 1'bz;
Cách này chạy được vì tại bất kỳ thời điểm nào, chỉ tối đa một trong hai biểu thức điều kiện cho ra giá trị khác z. Giá trị thực của wire là giá trị của nguồn đang không nhả.
Với logic bên trong (bất cứ đâu không phải chân chip hay bus dùng chung trên chip), mỗi wire chỉ một nguồn điều khiển. Bug do nhiều nguồn điều khiển rất khó gỡ.
Những gì assign không làm được
Có những việc assign không phải công cụ phù hợp:
- Lưu trữ.
assignmô tả quan hệ tổ hợp; nó không thể tạo ra flip-flop. Nếu bạn cần một giá trị được ghi nhớ qua các chu kỳ clock, đó là việc của khốialways @(posedge clk). - Logic thủ tục nhiều bước. Bạn không thể viết
if/elsehaycasebên trongassign. Gần nhất là chuỗi?:, và nó trở nên rối mắt khi quá ba nhánh. - Điều khiển thanh ghi từ bên trong khối thủ tục. Đích
regcần phép gán thủ tục, không phảiassign.
Hiểu giới hạn là cách để biết khi nào nên chuyển sang always.
Tiếp theo
Giờ bạn đã xem toàn bộ phần cấu trúc của Verilog: khai báo module, khởi tạo module và nối logic tổ hợp bằng assign. Chương tiếp theo chuyển sang các khối thủ tục: cấu trúc initial và always, nơi thời gian và thứ tự bắt đầu có ý nghĩa.
Câu hỏi thường gặp
Phép gán liên tục trong Verilog là gì?
assign target = expression; khai báo một mối quan hệ cố định và liên tục: target luôn bằng expression. Mỗi khi bất kỳ tín hiệu nào trong biểu thức thay đổi, trình mô phỏng tính lại vế phải và cập nhật target. Không có clock, không có sự kiện nào: mối quan hệ đúng tại mọi thời điểm.
assign trong Verilog gán được cho đối tượng nào?
assign có thể điều khiển một wire, nhưng không bao giờ là reg. Đích phải là kiểu net. Nếu bạn cần gán cho thứ gì đó trong khối always, hãy khai báo nó là reg. Trình biên dịch sẽ từ chối assign x = ... nếu x là reg, và từ chối x = ... trong always nếu x là wire.
Khi nào dùng assign, khi nào dùng khối always?
Dùng assign cho logic tổ hợp đơn giản: một biểu thức vào, một tín hiệu ra, không cần if/else. Dùng always @(*) khi logic cần câu lệnh thủ tục (một case, một chuỗi if/else if, một vòng lặp for). Cả hai đều tạo ra phần cứng tổ hợp; lựa chọn chỉ là chuyện dễ đọc.
Có thể dùng nhiều assign cho cùng một wire trong Verilog không?
Chỉ khi bạn mô hình hóa một bus ba trạng thái, trong đó mỗi nguồn điều khiển nhả wire về z khi không hoạt động. Hai lệnh assign cùng cố điều khiển wire về các giá trị xác định một lúc sẽ gây xung đột: trình mô phỏng có thể chọn một bên, có thể biến tín hiệu thành X, tùy công cụ. Với logic tổ hợp thông thường, mỗi wire chỉ có một nguồn điều khiển.