Menu

Porty modułu w Verilogu: input, output i inout

Jak deklarować porty modułu (input, output i inout), jak wygląda lista portów w stylu ANSI i kiedy wyjście powinno być wire, a kiedy reg.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Interfejs modułu

Lista portów modułu to jego interfejs, czyli wszystko, co świat zewnętrzny może zobaczyć i czego może dotknąć. Wewnątrz modułu ciało oblicza wyjścia na podstawie wejść. Porty to przewody, które przekraczają granicę.

Współczesna deklaracja w stylu ANSI podaje kierunek, typ i szerokość w jednym miejscu:

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 cały kształt. Każdy port ma:

  • Kierunek: input, output albo inout.
  • Typ: wire albo reg (albo logic w SystemVerilogu).
  • Szerokość: zakres taki jak [7:0] albo jeden bit, gdy zakres jest pominięty.
  • Nazwę: identyfikator, którego użyjesz w ciele modułu.

Trzy kierunki

input: sterowany z zewnątrz

Wejścia są zawsze typu wire. Źródło sterowania jest poza modułem: to sygnał modułu wyższego poziomu albo reg z testbencha. Wewnątrz tego modułu możesz odczytywać wejścia w wyrażeniach, ale nigdy do nich nie przypisujesz:

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

Zapis data = ... wewnątrz modułu byłby błędem.

output: sterowany od środka

Wyjścia są sterowane przez coś wewnątrz tego modułu. Mogą być typu wire (sterowane przez assign albo wyjście podmodułu) lub reg (sterowane z always/initial).

module driver(
    input  wire       a,
    input  wire       b,
    input  wire       clk,
    output wire       y,    // wire: sterowane przez assign
    output reg        q     // reg: sterowane przez always
);
    assign y = a & b;       // OK, bo y to wire

    always @(posedge clk)
        q <= a;             // OK, bo q to reg
endmodule

Próba przypisania do y wewnątrz bloku always albo sterowania q przez assign to błąd kompilacji. Słowo kluczowe musi pasować do źródła sterowania.

inout: dwukierunkowy

Porty inout to przewody, którymi na zmianę może sterować moduł albo to, co jest podłączone z zewnątrz. Pojawiają się na granicach układu: linie SDA w I²C, dwukierunkowe piny GPIO, współdzielone magistrale danych. Wewnątrz modułu kierunkiem steruje się wzorcem trójstanowym:

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 to współdzielony przewód. Gdy output_enable jest w stanie wysokim, moduł ustawia na pin wartość data_out. Gdy w niskim, zwalnia pin do stanu wysokiej impedancji (z), pozwalając korzystać z niego zewnętrznemu źródłu. data_in zawsze widzi to, co aktualnie jest na pinie.

inout rzadko występuje w czysto wewnętrznej logice. Budując kontroler pamięci, rdzeń procesora albo potok przetwarzania obrazu, możesz go nigdy nie potrzebować. To funkcja dla pierścienia wejść/wyjść na granicach układu.

Porty jednobitowe i wielobitowe

Zakres szerokości jest opcjonalny. Pomiń go dla portu jednobitowego:

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

Do określania szerokości możesz używać parametrów, i tak właśnie działają moduły sparametryzowane:

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

Style ANSI i Verilog-1995

Czasem zobaczysz starszy kod, który rozdziela listę portów i deklaracje:

// Stary styl Verilog-1995: NIE rób tak w nowym kodzie
module foo(clk, data_in, data_out);
    input clk;
    input [7:0] data_in;
    output reg [7:0] data_out;
    // ...
endmodule

Lista portów zawiera tylko nazwy, a każdy port jest potem deklarowany osobno w ciele. To rozwlekłe, podatne na błędy typu "nazwa jest na liście portów, ale nigdy nie została zadeklarowana" i wymaga dwa razy więcej pisania. Styl ANSI-2001 (używany w całej tej dokumentacji) zastępuje oba elementy:

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

Używaj stylu ANSI. Narzędzia obsługują go od 2001 roku.

Kompletny przykład

Ten jeden moduł ma:

  • Parametr (WIDTH).
  • Wejścia zegara, resetu, zezwolenia na ładowanie, danych i zezwolenia na przesuwanie.
  • Wyjście reg (data_out) sterowane wewnątrz bloku always.
  • Wyjście wire (msb_out) sterowane przypisaniem ciągłym.
  • Kompletną deklarację w stylu ANSI, która dla każdego portu podaje kierunek, typ i szerokość.

Tak wygląda prawie każdy syntezowalny moduł, jaki napiszesz.

Co dalej

Następny artykuł, Tworzenie instancji modułów, pokazuje, jak wziąć ten moduł i użyć go w większym projekcie. Napisany właśnie shifter sam w sobie nie jest przydatny. Zaczyna się przydawać, gdy coś tworzy jego instancję i włącza go do systemu.

Najczęściej zadawane pytania

Jakie są trzy kierunki portów w Verilogu?

input, output i inout. input jest sterowany spoza modułu. output jest sterowany z wnętrza modułu. inout jest dwukierunkowy i przydaje się na magistralach trójstanowych, gdzie moduł zarówno steruje tym samym pinem, jak i go odczytuje. Zdecydowana większość portów wewnętrznych to input albo output, a inout pojawia się tylko na pinach układu.

Jaka jest różnica między output wire a output reg?

output wire oznacza, że wyjście jest sterowane przypisaniem ciągłym albo wyjściem podmodułu, czyli wszystkim poza blokiem always. output reg oznacza, że jest sterowane z bloku proceduralnego (initial albo always). Słowo kluczowe decyduje o tym, czy możesz przypisywać do sygnału wewnątrz always, a nie o tym, czy zsyntezowany sprzęt zawiera przerzutnik.

Czym jest styl ANSI dla portów w Verilogu?

Styl ANSI deklaruje kierunek i typ każdego portu bezpośrednio na liście portów: module foo(input wire [7:0] data, output reg [7:0] result);. Starszy styl z Verilog-1995 wypisywał na liście portów tylko nazwy i deklarował je ponownie wewnątrz modułu. W nowym kodzie zawsze używaj stylu ANSI: jest krótszy, mniej podatny na błędy i standardowy we wszystkich współczesnych narzędziach.

Czy moduł w Verilogu może mieć wiele wejść i wyjść?

Tak. Moduł może mieć tyle portów, ile potrzebujesz, w dowolnej kombinacji kierunków. Oddzielaj je przecinkami na liście portów. Kolejność na liście portów decyduje o kolejności połączeń pozycyjnych przy tworzeniu instancji, ale prawie zawsze używa się połączeń po nazwie (.port(signal)), żeby nie polegać na kolejności.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ