Menu

Wire và reg trong Verilog: khi nào dùng loại nào (có ví dụ)

Hai kiểu dữ liệu chính trong Verilog: wire cho kết nối liên tục và reg cho lưu trữ trong khối thủ tục, cùng quy tắc giúp bạn luôn chọn đúng giữa hai loại.

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.

Hai cái tên, một nhiệm vụ

Mọi tín hiệu trong Verilog đều có một kiểu. Hai kiểu bạn gặp đầu tiên là wire và reg. Cả hai đều giữ được giá trị. Cả hai đều có thể là một bit hoặc nhiều bit. Điểm khác nhau không nằm ở việc tín hiệu là gì, mà ở việc ai điều khiển nó.

  • Tín hiệu wire được điều khiển bên ngoài khối thủ tục: bởi câu lệnh assign, bởi đầu ra của module con, hoặc bởi cổng đầu vào của module.
  • Tín hiệu reg được điều khiển bên trong khối thủ tục, tức là initial hoặc always.

Đó là toàn bộ quy tắc. Tên các từ khóa nghe khá kỳ vì chúng có từ trước cách viết Verilog hiện đại. reg không phải lúc nào cũng trở thành thanh ghi trong phần cứng. Đọc tiếp để biết vì sao.

Wire: kết nối liên tục

wire là một sợi dây điện. Nó mang bất cứ thứ gì nguồn điều khiển của nó tạo ra, một cách liên tục. Có hai cách để một wire được điều khiển:

Ba điều cần chú ý:

  • y và z được khai báo là wire trong module và lại là wire trong testbench.
  • Chúng được điều khiển bởi assign, tức là dạng phép gán liên tục. Vế phải của = được tính lại mỗi khi có tín hiệu nào trong đó thay đổi.
  • Ta không thể viết y = a & b bên trong khối always nếu vẫn giữ y là wire. Trình biên dịch sẽ từ chối.

Nếu bạn quên từ khóa wire, Verilog sẽ ngầm khai báo tín hiệu đó là một wire một bit. Đôi khi điều này tiện, đôi khi lại thành một bug âm thầm. Hầu hết các nhóm bật một tùy chọn của công cụ để báo lỗi khi gặp wire ngầm định. Hãy khai báo rõ ràng để tránh cái bẫy này.

Reg: lưu trữ bên trong khối thủ tục

reg là tín hiệu bạn gán giá trị bên trong initial hoặc always. Cái tên là di sản từ thời đầu của ngôn ngữ, khi "reg" nghe giống "register"; trong cách dùng hiện đại, reg chỉ đơn giản là kiểu của bất kỳ tín hiệu nào mà code thủ tục ghi vào.

count là reg bên trong module (vì khối always ghi vào nó) và là wire trong testbench (vì cổng đầu ra của DUT điều khiển nó). Cùng một tín hiệu, vai trò khác nhau, kiểu khác nhau trong mỗi phạm vi.

Vì sao "reg" không phải lúc nào cũng là "register"

Đây là cái bẫy phổ biến nhất với người mới. Module này khai báo y là reg, nhưng phần cứng sau tổng hợp lại không có flip-flop nào:

Khối always @(*) nhạy với bất kỳ thay đổi nào ở đầu vào. Đây là khối tổ hợp. Công cụ tổng hợp nhận ra mẫu đó và tạo ra một cổng AND: không flip-flop, không clock, chỉ có logic. Từ khóa reg thuần túy là yêu cầu cú pháp, vì y được gán bên trong always.

Để có một flip-flop thật, khối always phải nhạy với sườn clock:

always @(posedge clk) begin
    q <= d;
end

Đó là mẫu "công cụ tổng hợp ơi, tạo flip-flop giúp mình": sensitivity list theo clock, phép gán non-blocking. Cùng từ khóa reg, nhưng phần cứng hoàn toàn khác. Chúng ta bàn kỹ sự khác biệt này trong Khối always và Blocking và non-blocking.

Quyết định trong thực tế

Mỗi lần khai báo một tín hiệu, hãy tự hỏi: mình sẽ điều khiển nó bằng cách nào?

  • Điều khiển bằng assign? → wire.
  • Nối vào cổng đầu ra của module con? → wire.
  • Cổng đầu vào của module? → wire. (Đầu vào luôn là wire.)
  • Điều khiển từ initial hoặc always? → reg.
  • Cổng đầu ra của module do code thủ tục điều khiển? → output reg.
  • Cổng đầu ra của module do assign điều khiển? → output wire. (Hoặc chỉ output, vì riêng chiều cổng đã mặc định là wire.)

Cây quyết định đó bao quát mọi tình huống bạn sẽ gặp trong Verilog thuần.

Xung đột nguồn điều khiển

Về lý thuyết, tín hiệu wire có thể có nhiều nguồn điều khiển. Đó là cách bus ba trạng thái hoạt động: nhiều module cùng điều khiển một dây, và các module không hoạt động chuyển sang trở kháng cao (z). Với logic thông thường, hai câu lệnh assign cùng ghi vào một wire sẽ cho hành vi không xác định:

assign y = a;
assign y = b;   // BAD - y has two drivers

Trình mô phỏng có thể chọn một trong hai, có thể biến tín hiệu thành X, tùy công cụ. Dù thế nào đó cũng là bug. Mỗi wire chỉ một nguồn điều khiển, trừ khi bạn chủ động xây dựng một bus.

Tín hiệu reg chỉ có thể được điều khiển từ một khối thủ tục. Hai khối always cùng gán cho một reg là lỗi tổng hợp và cho kết quả kỳ quặc khi mô phỏng. Quy tắc vẫn vậy: một nguồn điều khiển.

Cập nhật từ SystemVerilog: logic

SystemVerilog (phiên bản mở rộng mà Verilog phát triển thành) thêm một từ khóa duy nhất thay cho cả hai: logic. Tín hiệu logic có thể được điều khiển bởi assign hoặc bởi khối thủ tục (nhưng không phải cả hai), và trình biên dịch ngăn bạn vô tình tạo ra bug nhiều nguồn điều khiển.

module modern(input logic a, input logic b, output logic y);
    assign y = a ^ b;
endmodule

Nếu bạn bắt đầu một dự án từ đầu và công cụ hỗ trợ SystemVerilog (editor trên trang này hỗ trợ, với -g2012), dùng logic ở mọi nơi sẽ đơn giản hóa các quy tắc. Tệp .v thuần vẫn cần tách wire/reg, và bạn sẽ còn thấy cả hai phong cách ngoài thực tế rất lâu nữa.

Tiếp theo

Bài tiếp theo, Vector và mảng, lấy chính hai kiểu này và chỉ cách làm chúng thành nhiều bit. Độ rộng bus, khoảng chỉ số, chiều packed, sự khác nhau giữa một vector bit và một mảng vector. Tất cả những gì bạn cần trước khi xây được thứ gì lớn hơn một bộ cộng một bit.

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

Wire và reg trong Verilog khác nhau thế nào?

wire và reg đều giữ giá trị của tín hiệu tại mọi thời điểm trong mô phỏng, nhưng bạn chọn giữa hai loại dựa trên ai điều khiển tín hiệu đó. wire được điều khiển từ bên ngoài khối thủ tục, bởi một lệnh assign hoặc đầu ra của một module con. reg được điều khiển từ bên trong khối initial hoặc always. Hai cái tên này có từ lịch sử và dễ gây hiểu lầm; reg không phải lúc nào cũng có nghĩa là 'thanh ghi'.

Reg trong Verilog có phải là flip-flop không?

Chưa chắc. Một reg chỉ được tổng hợp thành flip-flop khi khối always của nó nhạy với sườn clock và dùng phép gán non-blocking. Một reg được gán trong khối tổ hợp always @(*) sẽ được tổng hợp thành logic tổ hợp thông thường. Từ khóa này chọn kiểu dữ liệu Verilog, không chọn phần cứng.

Khi nào nên dùng wire, khi nào dùng reg?

Quy tắc nhanh: nếu bạn điều khiển tín hiệu bằng assign hoặc nối nó vào cổng đầu ra của một module con, hãy dùng wire. Nếu bạn gán giá trị cho nó bên trong khối always hoặc initial, hãy dùng reg. Đầu vào của module luôn là wire. Đầu ra là wire nếu được điều khiển bởi assign và là reg nếu được điều khiển bởi khối thủ tục.

Có thể gán giá trị cho wire bên trong khối always không?

Không, đó là lỗi cú pháp. Tín hiệu wire chỉ có thể được điều khiển bởi phép gán liên tục (assign) hoặc bằng cách nối làm đầu ra của một instance module con. Mọi phép gán bên trong initial hoặc always phải nhắm vào reg (hoặc logic trong SystemVerilog). Trình biên dịch sẽ phát hiện và báo lỗi 'left-hand side type mismatch'.

Logic trong SystemVerilog là gì?

logic là kiểu hợp nhất wire và reg của SystemVerilog. Nó có thể được điều khiển bởi phép gán liên tục hoặc bởi khối thủ tục (nhưng không phải cả hai cùng lúc). Code hiện đại ngày càng dùng logic ở mọi nơi và bỏ qua sự phân biệt wire/reg. Tệp Verilog thuần vẫn phải chọn một trong hai.

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