DocsTech
/
Проекты ПЛИС на VERILOG
/

~ cd 10 лучших примеров кода verilog

Сводка

В данной статье мы рассмотрим примеры кода на языке Verilog с отчетами о компиляции и RTL. Всего мы представим 10 лучших примеров, начиная от простых операций с логическими элементами и заканчивая моделированием микропроцессоров и цифровых фильтров. Каждый пример сопровождается отчетом о его успешной компиляции, что позволяет убедиться в корректности представленного кода. Мы охватили широкий спектр тем для того, чтобы помочь разработчикам глубже понять синтаксис и семантику из документации Verilog, а также улучшить свои навыки в области моделирования электронных схем.

Пример 1. Двухпортовая Однотактовая Память RAM

Пример 1 — Объявления модуля памяти с использованием памяти ПЛИС c помощью Verilog(см. массивы и память). Этот модуль не использует регистры, а напрямую обращается к IP-блоку памяти. Здесь имеется в виду ресурс, заложенный в микрочип, отделенный от логических ячеек.
...
Копировать
module TWISE_PORTS_RAM(data, addr_we, addr_re, we, clk, q);

    parameter BITS_DATA = 8; // кол-во битов в линии данных
    parameter BITS_ADDR = 6; // кол-во битов в линии адреса

    input [(BITS_DATA - 1): 0] data;  // линия данных
    input [(BITS_ADDR - 1): 0] addr_we;  // линия адреса для записи
    input [(BITS_ADDR - 1): 0] addr_re;  // линия адреса для чтения
    input we;  // линия включения записи
    input clk; // тактовый сигнал
    output reg [(BITS_DATA - 1): 0] q;  // линия выхода

    // регистр, использующий примитив FPGA
    reg [(BITS_DATA - 1): 0] ram [((2**BITS_ADDR) - 1): 0];

    always @(posedge clk) begin
        // запись
        if (we)
            ram[addr_we] <= data;

        // чтение
        q <= ram[addr_re];
    end
endmodule

При приходе восходящего фронта в тактовом сигнале clk и высоком сигнале записи write, происходит запись в память по адресу addr_we. При одинаковой ситуации тактовым сигналом происходит чтение с памяти по адресу addr_re. RTL модель показана на рисунке 1.

RTL однотакотовой двухпортовой памяти и отчет о компиляции
Рисунок 1 — RTL однотакотовой двухпортовой памяти.

Пример 2. FIFO

Пример 2 — Данный FIFO написан для заполнения слова буфера по битно. Модуль использует регистровую память на логических клетках. Вставьте экземпляр памяти из примера 1, чтоб использовать ресурсы(память) чипа. Объяснение по портам и параметрам не требуется, потому что код приведен с комментариями. Более подробно описано в статье про FIFO. Процедурный блок always «реагирует» на восходящий фронт тактового сигнала clk и асинхронного сигнала reset. Когда появляется восходящего фронта сигнала reset, сбрасывается все управляющие регистры FIFO и выставляется на логическую 1 сигнал пустоты буфера empty. Если буфер полон, выставляется сигнал full. Когда происходит переход тактового сигнала к логической 1, при высоком сигнале write_enable и не полном буфере осуществляется запись в буфер и увеличение счетчика и указателя. Если clk восходящий фронт, при высоком сигнале read_enable и при не полном буфере читаются данные из буфера, уменьшается счетчика и увеличивается указатель. Для заполнения буфера FIFO по другой величине битов, необходимо увеличить длину битов линии data_in, data_out на одинаковое кол-во и переделать счетчик и указателей write_pointer и read_pointer.
...
Копировать
module fifo (
  input wire clk,        // тактовый сигнал
  input wire reset,      // сигнал сброса

  input wire write_enable,  // сигнал разрешения записи
  input wire read_enable,   // сигнал разрешения чтения
  input wire data_in,       // входные данные
  output reg data_out,     // выходные данные

  output wire empty,       // сигнал пустого буфера
  output wire full         // сигнал заполненного буфера
);

  // Параметры FIFO
  parameter FIFO_DEPTH = 8;     // глубина FIFO

  // Внутренние сигналы и регистры
  reg [7:0] fifo[FIFO_DEPTH-1:0];  // регистры FIFO
  reg [3:0] write_pointer;         // указатель записи
  reg [3:0] read_pointer;          // указатель чтения
  reg [3:0] count;                 // счетчик элементов
  reg empty_reg, full_reg;         // регистры состояния

  // Логика FIFO
  always @(posedge clk or posedge reset) begin
    if (reset) begin
      // Сброс FIFO
      write_pointer <= 0;
      read_pointer <= 0;
      count <= 0;
      empty_reg <= 1;
      full_reg <= 0;
    end else begin
      // Запись данных
      if (write_enable && !full_reg) begin
        fifo[write_pointer] <= data_in;
        write_pointer <= write_pointer + 1;
        count <= count + 1;
        empty_reg <= 0;
        if (count == FIFO_DEPTH - 1)
          full_reg <= 1;
      end

      // Чтение данных
      if (read_enable && !empty_reg) begin
        data_out <= fifo[read_pointer];
        read_pointer <= read_pointer + 1;
        count <= count - 1;
        full_reg <= 0;
        if (count == 1'h0)
          empty_reg <= 1;
      end
    end
  end

  // Выходные сигналы состояния FIFO
  assign empty = empty_reg;
  assign full = full_reg;

endmodule

RTL FIFO показано на рисунке 2.

RTL примера модуля FIFO и отчет о компиляции

Рисунок 2 — RTL примера модуля FIFO.

Пример 3. Интегратор

Пример 3Интегратор — это устройство или элемент, который выполняет функцию интегрирования. Данный интегратор реализован с выбором коэффициента дискретизации или без его наличия. Точечная нотация фиксированной точки равна 1.15. Именно, на такой нотации работает сигнальные процессоры. Код создавался и тестировался Quartus9.1, поэтому была необходимость прописывать умножение отдельным модулем. Иным образом программа искажала generate RTL(см generate).
...
Копировать
module integr(data, clk, reset, out);
    // 1 - включение коэф дискретизации, 0 - отключение
    parameter KOEF_ENA = 1;
    // мой пример... Частота дискретизации 500 КГц. 1/500 = 0.002
    // В данном случае точечная нотация с фиксированой точкой 1.15
    // следовательно. 0.002*(2**16) = 65.536 => PERIOD = 65
    parameter [15: 0] PERIOD = 82;
    parameter DATA_WIDTH = 16;

    input [(DATA_WIDTH - 1): 0] data;
    input reset, clk;
    output [(DATA_WIDTH - 1): 0] out;

    wire [(DATA_WIDTH*2 - 1): 0] koef_after;
    wire [(DATA_WIDTH - 1): 0] add1;

    reg [(DATA_WIDTH - 1): 0] z1 = 0;

    generate
        if (KOEF_ENA) begin
            unsigned_multiply #(DATA_WIDTH - 1) mult1(PERIOD, data, koef_after);
        end else begin
            assign koef_after = {data, 15'h00};
        end
    endgenerate

    assign add1 = (koef_after >> 15) + z1;
    assign out = add1;

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            z1 <= 0;
        end else begin
            z1 <= add1;
        end
    end

endmodule

module unsigned_multiply
#(parameter WIDTH=8)
(
    input [WIDTH-1:0] dataa,
    input [WIDTH-1:0] datab,
    output [2*WIDTH-1:0] dataout
);

    assign dataout = dataa * datab;

endmodule

RTL интегратора состоит из триггера, сумматора и умножителя и показан на рисунке 3.

RTL примера модуля интегратора и отчет о компиляции
Рисунок 3 — RTL примера модуля интегратора.

Пример 4. Дифференциатор

Пример 4Дифференциатор — это устройство, которое позволяет получить производную сигнала. Дифференциатор представляет собой нерекурсивный фильтр 1 порядка с коэффициентом -1.
...
Копировать
module diff(data, clk, reset, out);

    parameter DATA_WIDTH = 16; // Битовая длина линии данных

    input signed [(DATA_WIDTH - 1): 0] data; // Линия данных
    input reset, clk; // асихронный сброс и тактовый сигнал
    output signed [(DATA_WIDTH - 1): 0] out; // линия выхода
    // z(-1) запоминает входной сигнал
    reg [(DATA_WIDTH - 1): 0] z1 = 0;

    // вычетание входных даных и предыдуших данных
    assign out = data - z1;

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            z1 <= 0;
        end else begin
            z1 <= data;
        end
    end

endmodule

RTL Дифференциатора состоит из триггера и разнатора и показан на рисунке 4.

RTL примера модуля дифференциатора и отчет о компиляция
Рисунок 4 — RTL примера модуля дифференциатора.

Пример 5. Фазовый накопитель

Пример 5Фазовый накопитель — это БИХ-фильтр(рекурсивный фильтр) 1 порядка. Фазовый накопитель применяется во многих схема, но чаще его используется в схеме NCO генератора, управляемый числовой последовательность. Входной 16 битный сигнал складывается с предыдущим значением сумматора. Если значение сумматора выше заложенных битов, регистр запоминает лишь первые 16 бит(см. регистры).
...
Копировать
module FN(out, sig, clk, reset);

    parameter DATA_WIDTH = 16;

    input signed [(DATA_WIDTH - 1): 0] sig; // Линия данных
    input reset, clk; // асихронный сброс и тактовый сигнал
    output signed [(DATA_WIDTH - 1): 0] out; // линия выхода

    wire [(DATA_WIDTH - 1): 0] add1; //Линия сумматора

    reg signed [(DATA_WIDTH - 1): 0] z1 = 0; // буфер

    // суммированые входного и предидущего выходного сигнала
    assign add1 = sig + z1;
    assign out = add1;

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            z1 <= 0;
        end else begin
            z1 <= add1;
        end
    end
endmodule

RTL Фазового накопителя состоит из триггера и сумматора и показан на рисунке 5.

RTL примера модуля Фазового накопителя и отчет о компиляции
Рисунок 5 — RTL примера модуля Фазового накопителя.

Пример 6. КИХ-фильтр 5 порядка

Пример 6КИХ-фильтр 5 порядка(FIR) — нерекурсивный фильтр 5 порядка. КИХ-фильтры используют для фильтрации данных. С помощью КИХ-фильтров строят ФНЧ, ФВЧ, режекторный и полосовый фильтры. Данный код использует нотацию для фиксированной запятой 1.15.
...
Копировать
module fir(data, clk, reset, out);

    parameter DN = 5; // порядок фильтра
    parameter K0 = 655; // коэф равный 0,02
    parameter K1 = 16_384; // коэф равный 0,5
    parameter K2 = 21_299; // коэф равный 0,65
    parameter K3 = 36; // коэф равный 0,0011
    parameter K4 = 3276; // коэф равный 0,1
    parameter DATA_WIDTH = 16; // Битовая длина линии данных

    input signed [(DATA_WIDTH - 1): 0] data; // линия данных
    input reset, clk; // асихронный сброс и тактовый сигнал
    output signed [(DATA_WIDTH - 1): 0] out; // выходной сигнал

    reg [(DATA_WIDTH - 1): 0] z [(DN - 1): 0]; // память филтра

    wire [(DATA_WIDTH*2 - 1): 0] k_w [(DN - 1): 0];

    // Задание начальных значений памяти фильтра
    initial begin
        z[0] = 0;
        z[1] = 0;
        z[2] = 0;
        z[3] = 0;
        z[4] = 0;
    end

    // перемножение z^(-1) регистра на коэф
    assign k_w[0] = z[0] * K0;
    assign k_w[1] = z[1] * K1;
    assign k_w[2] = z[2] * K2;
    assign k_w[3] = z[3] * K3;
    assign k_w[4] = z[4] * K4;

    assign out = (k_w[0] + k_w[1] + k_w[2] + k_w[3] + k_w[4]) >> (DATA_WIDTH - 1);

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            // очистка памяти
            z[0] <= 0;
            z[1] <= 0;
            z[2] <= 0;
            z[3] <= 0;
            z[4] <= 0;
        end else begin
            z[0] <= data;
            z[1] <= z[0];
            z[2] <= z[1];
            z[3] <= z[2];
            z[4] <= z[3];
        end
    end

endmodule

RTL КИХ-фильтр 5 порядка состоит из 10 триггеров(5 памяти константных чисел и 5 коэф. фильтра), 5 сумматоров и 5 умножителей и показан на рисунке 6.

RTL примера модуля КИХ-фильтр 5 порядка и отчет о компиляции
Рисунок 6 — RTL примера модуля КИХ-фильтр 5 порядка.

Пример 7. LUT 6-ти входная

Пример 7LUT — это таблица, которая выдает заранее определенные значения. LUT бывает 2-х типов: функциональная и хеш-таблица. В данном случае применяется функциональная LUT. Функциональная LUT — это комбинационная схема с определенным кол-во входов. Хорошим примером является LUT логической клетки семейства FPGA Cyclone. В примере используется директива `default_nettype, которая задает объявление по умолчанию не объявленных цепей.
...
Копировать
`default_nettype wire

module LUT6(out, sig0, sig1, sig2, sig3, sig4, sig5);

    input sig0, sig1, sig2, sig3, sig4, sig5;
    output out;

    assign w1 = (sig1 & sig3) | sig0;
    assign w2 = w1 & sig4 & (sig5 ^ sig2);

    assign out = w2;

endmodule

RTL LUT 6-ти входная состоит из 2 конъюнкций, дизъюнкции и суммы по модуля 2 и показан на рисунке 7.

RTL примера модуля LUT 6-ти входная и отчет о компиляции
Рисунок 7 — RTL примера модуля LUT 6-ти входная.

Пример 8. Полный N-битный сумматор

Пример 8Полный сумматор — это логический элемент, который суммирования числа с переносом и осуществляет перенос. N-битный сумматор — это сумматор с длинной линиями данных равной N. В этом примере шестнадцатибитный.
...
Копировать
module sum(out, cout, dataa, datab, cin);

    parameter DATA_WIDTH = 16; // битовая длина

    input [(DATA_WIDTH - 1): 0] dataa, datab; // линии данных
    input cin; // входной перенос
    output [(DATA_WIDTH - 1): 0] out;  // результат суммирования
    output cout;  // выходной перенос

    assign {cout, out} = dataa + datab + cin;

endmodule

RTL Полного шестнадцатибитного сумматора состоит из 2-х примитивов сложения и показан на рисунке 8.

Полный N-битный сумматор и отчет о компиляции
Рисунок 8 — RTL примера модуля полного сумматора.

Пример 9. 16-входной мультиплексор

Пример 9Мультиплексор — это логический элемент, который осуществляет выборку сигналов из всех входных информационных сигналов через адресные управляющие входы.
...
Копировать
module mux16
#(
    parameter DATA_WIDTH = 16 // битовая длина
)
(
    output [(DATA_WIDTH - 1): 0] out, // результат
    // линии данных
    input [(DATA_WIDTH - 1): 0] sig0, sig1, sig2, sig3,
    input [(DATA_WIDTH - 1): 0] sig4, sig5, sig6, sig7,
    input [(DATA_WIDTH - 1): 0] sig8, sig9, sig10, sig11,
    input [(DATA_WIDTH - 1): 0] sig12, sig13, sig14, sig15,
    input [3:0] addr // линия адреса
);



    wire [(DATA_WIDTH - 1): 0] con_mux [15: 0];

    assign  con_mux[0] = (0 == addr)? sig0: 0,
                con_mux[1] = (1 == addr)? sig1: 0,
                con_mux[2] = (2 == addr)? sig2: 0,
                con_mux[3] = (3 == addr)? sig3: 0,
                con_mux[4] = (4 == addr)? sig4: 0,
                con_mux[5] = (5 == addr)? sig5: 0,
                con_mux[6] = (6 == addr)? sig6: 0,
                con_mux[7] = (7 == addr)? sig7: 0,
                con_mux[8] = (8 == addr)? sig8: 0,
                con_mux[9] = (9 == addr)? sig9: 0,
                con_mux[10] = (10 == addr)? sig10: 0,
                con_mux[11] = (11 == addr)? sig11: 0,
                con_mux[12] = (12 == addr)? sig12: 0,
                con_mux[13] = (13 == addr)? sig13: 0,
                con_mux[14] = (14 == addr)? sig14: 0,
                con_mux[15] = (15 == addr)? sig15: 0;

    assign out = con_mux[0]
            | con_mux[1]
            | con_mux[2]
            | con_mux[3]
            | con_mux[4]
            | con_mux[5]
            | con_mux[6]
            | con_mux[7]
            | con_mux[8]
            | con_mux[9]
            | con_mux[10]
            | con_mux[11]
            | con_mux[12]
            | con_mux[13]
            | con_mux[14]
            | con_mux[15];

endmodule

RTL 16-входного мультиплексора на рисунке 9.

16-входной мультиплексор и отчет о компиляции
Рисунок 9 — RTL примера модуля 16-входного мультиплексора.

Пример 10. D-триггер

Пример 10 — Реализован 8-ми битный D-триггер с сигналом предустановки preload, синхронным сбросом reset и включения ena.
...
Копировать
module triggers (data, data_load, clk, ena, sreset, preload, q);
    // длина информационных сигналов
    parameter DATA_WIDTH = 8;
    // информационный сигнал
    input [(DATA_WIDTH - 1): 0] data, data_load;
    // тактовый сигнал, сигнал включения, сброса и предустанвоки
    input clk, ena, sreset, preload;
    // выход
    output [(DATA_WIDTH - 1): 0] q;
    // объявление его регистровым
    reg [(DATA_WIDTH - 1): 0] q;

    always @(posedge clk) begin
        if (sreset) begin
            q <= 0;
        end else begin
            if (preload) begin
                q <= data_load;
            end else begin
                q <= data;
            end
        end
    end

endmodule

RTL 8-ми битного D-триггера показано на рисунке 10.

RTL D-триггер и отчет о компиляции
Рисунок 10- RTL примера модуля 8-ми битного D-триггера.
Главная
Курсы
Вебинары
Квадратный корень методом CORDIC на FPGA
Алгоритмы квадратного корня на FPGA
Делители частоты на Verilog: дробные и целые коэффициенты деления
Расчет фильтра Баттерворта MATLAB и Verilog
Интегратор: цифровой и аналоговый элемент схемы
Примеры Сдвиговых регистров в Verilog
5 Уникальных Примера Testbench на Verilog
Сдвиги в Verilog: логический, арифметический и циклический
Память RAM Verilog ПЛИС
Триггеры в Verilog: JK, RS, D и T
10 Лучших примеров кода Verilog
Закрыть