Menu

Port của module Verilog: giải thích input, output và inout

Cách khai báo port của module (input, output và inout), danh sách port kiểu ANSI, và khi nào một output nên là wire hay reg.

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.

Giao diện của một module

Danh sách port của module là giao diện của nó: tất cả những gì thế giới bên ngoài nhìn thấy và chạm vào được. Bên trong module, phần thân tính output từ input. Các port là những sợi dây đi qua ranh giới đó.

Kiểu khai báo ANSI hiện đại đặt hướng, kiểu và độ rộng ngay trên cùng một dòng:

module my_module(
    input  wire        clk,
    input  wire        reset,
    input  wire [7:0]  data_in,
    output reg  [7:0]  data_out,
    output wire        valid
);
    // body
endmodule

Toàn bộ cấu trúc chỉ có vậy. Mỗi port có:

  • Một hướng: input, output hoặc inout.
  • Một kiểu: wire hoặc reg (hoặc logic trong SystemVerilog).
  • Một độ rộng: một khoảng như [7:0], hoặc 1 bit nếu bỏ trống.
  • Một tên: định danh bạn sẽ dùng bên trong phần thân.

Ba hướng port

input: điều khiển từ bên ngoài

Input luôn là wire. Nguồn điều khiển nằm ngoài module: hoặc là tín hiệu của một module cấp cao hơn, hoặc là reg của testbench. Bên trong module này, bạn có thể đọc input trong biểu thức nhưng không bao giờ được gán giá trị cho chúng:

module reader(input wire [7:0] data);
    initial $display("data = %h", data);
endmodule

Viết data = ... bên trong module sẽ gây lỗi.

output: điều khiển từ bên trong

Output được điều khiển bởi thứ gì đó bên trong module này. Nó có thể là wire (điều khiển bởi assign hoặc output của module con) hoặc reg (điều khiển từ always/initial).

module driver(
    input  wire       a,
    input  wire       b,
    input  wire       clk,
    output wire       y,    // wire - driven by assign
    output reg        q     // reg  - driven by always
);
    assign y = a & b;       // OK because y is wire

    always @(posedge clk)
        q <= a;             // OK because q is reg
endmodule

Nếu cố gán cho y bên trong khối always, hoặc điều khiển q bằng assign, bạn sẽ gặp lỗi biên dịch. Từ khóa bạn chọn phải khớp với nguồn điều khiển.

inout: hai chiều

Port inout là dây có thể được điều khiển luân phiên bởi module hoặc bởi thứ được nối ở bên ngoài. Chúng xuất hiện ở ranh giới của chip: đường SDA của I²C, chân GPIO hai chiều, bus dữ liệu dùng chung. Bên trong module, bạn điều khiển hướng bằng mẫu ba trạng thái:

module bidir_pin(
    input  wire data_out,
    input  wire output_enable,
    output wire data_in,
    inout  wire pin
);
    assign pin     = output_enable ? data_out : 1'bz;
    assign data_in = pin;
endmodule

pin là dây dùng chung. Khi output_enable ở mức cao, module đưa data_out ra pin. Khi ở mức thấp, module nhả chân về trạng thái trở kháng cao (z), để một nguồn điều khiển bên ngoài dùng nó. data_in luôn đọc giá trị đang có trên chân.

inout hiếm gặp trong logic thuần nội bộ. Nếu bạn đang xây một bộ điều khiển bộ nhớ, một lõi CPU hay một pipeline xử lý ảnh, có thể bạn sẽ không bao giờ cần đến nó. Đây là tính năng dành cho vòng I/O ở ranh giới chip.

Port 1 bit và port nhiều bit

Khoảng độ rộng là tùy chọn: bỏ nó đi để có port 1 bit:

input wire valid,         // 1 bit
input wire [7:0] data,    // 8 bits
input wire [31:0] addr,   // 32 bits

Bạn có thể dùng tham số cho độ rộng, đó chính là cách các module tham số hóa hoạt động:

module bus #(
    parameter WIDTH = 32
)(
    input  wire [WIDTH-1:0] in,
    output wire [WIDTH-1:0] out
);
    assign out = in;
endmodule

Kiểu ANSI và kiểu Verilog-1995

Thỉnh thoảng bạn sẽ gặp code cũ tách riêng danh sách port và phần khai báo:

// Old Verilog-1995 style - DON'T do this in new code
module foo(clk, data_in, data_out);
    input clk;
    input [7:0] data_in;
    output reg [7:0] data_out;
    // ...
endmodule

Danh sách port chỉ chứa tên; mỗi port sau đó được khai báo riêng trong phần thân. Cách này dài dòng, dễ gặp lỗi "có tên trong danh sách port nhưng chưa khai báo" và phải gõ gấp đôi. Kiểu ANSI-2001 (kiểu được dùng xuyên suốt bộ tài liệu này) thay thế cả hai:

module foo(
    input  wire       clk,
    input  wire [7:0] data_in,
    output reg  [7:0] data_out
);
    // ...
endmodule

Hãy dùng kiểu ANSI. Các công cụ đã hỗ trợ nó từ năm 2001.

Ví dụ hoàn chỉnh

Chỉ một module đó đã có:

  • Một tham số (WIDTH).
  • Các input cho xung clock, reset, cho phép nạp, dữ liệu và cho phép dịch.
  • Một output reg (data_out) được điều khiển bên trong khối always.
  • Một output wire (msb_out) được điều khiển bởi phép gán liên tục.
  • Một khai báo kiểu ANSI đầy đủ liệt kê hướng, kiểu và độ rộng của từng port ngay trên dòng.

Gần như mọi module tổng hợp được mà bạn viết đều có dạng như vậy.

Tiếp theo là gì

Bài tiếp theo, Khởi tạo module, cho bạn thấy cách lấy module này và dùng nó bên trong một thiết kế lớn hơn. Bộ dịch bit bạn vừa viết tự nó chưa làm được gì nhiều; nó chỉ phát huy tác dụng khi có thứ gì đó khởi tạo nó và nối nó vào một hệ thống.

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

Ba hướng port trong Verilog là gì?

input, output và inout. input được điều khiển từ bên ngoài module. output được điều khiển từ bên trong module. inout là hai chiều, hữu ích cho bus ba trạng thái (tri-state), nơi module vừa điều khiển vừa đọc cùng một chân. Phần lớn port bên trong thiết kế là input hoặc output; inout chỉ xuất hiện ở các chân của chip.

output wire và output reg khác nhau thế nào?

output wire nghĩa là output được điều khiển bởi một phép gán liên tục hoặc bởi output của một module con, tức mọi thứ nằm ngoài khối always. output reg nghĩa là nó được điều khiển từ một khối thủ tục (initial hoặc always). Từ khóa này quyết định bạn có thể gán cho tín hiệu từ bên trong always hay không, chứ không quyết định phần cứng sau tổng hợp có chứa flip-flop hay không.

Kiểu ANSI cho port Verilog là gì?

Kiểu ANSI khai báo hướng và kiểu của từng port ngay trong danh sách port: module foo(input wire [7:0] data, output reg [7:0] result);. Kiểu Verilog-1995 cũ hơn chỉ liệt kê tên trong danh sách port rồi khai báo lại bên trong module. Luôn dùng kiểu ANSI cho code mới: ngắn hơn, ít lỗi hơn và là chuẩn ở mọi công cụ hiện đại.

Một module Verilog có thể có nhiều input và output không?

Có. Một module có thể có bao nhiêu port tùy nhu cầu, với bất kỳ tổ hợp hướng nào. Ngăn cách chúng bằng dấu phẩy trong danh sách port. Thứ tự trong danh sách port quyết định thứ tự kết nối theo vị trí khi khởi tạo, nhưng gần như lúc nào bạn cũng dùng kết nối theo tên (.port(signal)) để không phụ thuộc vào thứ tự.

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