Понимание арифметических операций в VHDL

VHDL (VHSIC Hardware Description Language) обеспечивает надежную поддержку для реализации арифметических операций, что делает его краеугольным камнем для проектирования цифровых систем, таких как процессоры, цифровые сигнальные процессоры (DSP) и блоки управления. Арифметические блоки, такие как аддиторы, субтракторы и множители, являются фундаментальными строительными блоками, и знание того, как их эффективно реализовать, имеет решающее значение как для моделирования, так и для синтеза. Эта статья предлагает тщательное, ориентированное на производство исследование этих операций, охватывающее выбор типа данных, использование оператора, архитектурные стили и общие методы оптимизации.

Прежде чем погрузиться в конкретные компоненты, важно понять два ключевых пакета VHDL, которые позволяют арифметику: ieee.numeric std и ieee.std logic unsigned. Рекомендуемый подход для новых проектов заключается в использовании numeric std, который определяет unsigned и signed типы данных и обеспечивает перегруженных операторов арифметики. Этот пакет обеспечивает последовательное поведение между симуляторами и синтезаторами, избегая ловушек, связанных со старым пакетом std logic arith.

Все примеры кода в этой статье написаны с использованием совместимых VHDL и целевых устройств Xilinx или Intel (Altera) FPGAIEEE 1076-2008, но концепции применимы к любому цифровому потоку проектирования.

Добавки в VHDL

Добавление является наиболее фундаментальной арифметической операцией. В VHDL можно реализовать добавители на различных уровнях абстракции: поведенческие (с использованием оператора +), поток данных (с использованием одновременного назначения сигнала) или структурные (с обоснованием компонентов более низкого уровня). Для большинства практических проектов поведенческое моделирование с numeric std обеспечивает наилучший баланс читаемости и эффективности синтеза.

Простой Ripple-Carry Adder

Добавитель рябь-несущий цепь полноприводных вместе, где перенос каждого бита питает перенос в следующем более высоком бите. Следующий пример показывает 4-битный неподписанный рябь-несущий аддер с использованием поведенческого процесса:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity adder4bit is
 Port (
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 Sum : out unsigned(3 downto 0);
 Cout : out std_logic
 );
end adder4bit;

architecture Behavioral of adder4bit is
begin
 process(A, B)
 variable temp_sum : unsigned(4 downto 0);
 begin
 temp_sum := ('0' & A) + ('0' & B);
 Sum <= temp_sum(3 downto 0);
 Cout <= temp_sum(4);
 end process;
end Behavioral;

Ключевые моменты:

  • Конкатенация ('0' и A) расширяет входы до 5 бит, захватывая вынос.
  • Использование , неподписанного непосредственно с оператором +, является синтезируемым; инструмент выводит соответствующую логику аддитора (риппл-носитель, несущий-лукахед или LUT-основанный в FPGA).
  • Перечень чувствительности процесса включает все входные сигналы, обеспечивая немедленное обновление выходных данных о любых изменениях (совместное поведение).

Carry-Lookahead Adder (CLA)

Для более широких аддиторов (например, 16-битных или 32-битных) аддиторы рябь-носитель вводят значительную задержку из-за распространения переноса. Добавитель переноса-лукахеда уменьшает эту задержку, генерируя сигналы переноса параллельно. Внедрение полного CLA в VHDL требует описания генерации ()gii &biipiiii, затем вычислительная группа переносит сигналы. Однако на практике современные инструменты синтеза автоматически выводят структуры CLA для широких аддиторов, когда используется + оператор показывает 16-битный аддитор, который инструмент, вероятно, будет отображать быстрые несущие

entity adder16bit is
 Port (
 A : in unsigned(15 downto 0);
 B : in unsigned(15 downto 0);
 Sum : out unsigned(15 downto 0);
 CO : out std_logic
 );
end adder16bit;

architecture Behavioral of adder16bit is
 signal temp : unsigned(16 downto 0);
begin
 temp <= ('0' & A) + ('0' & B);
 Sum <= temp(15 downto 0);
 CO <= temp(16);
end Behavioral;

Аддер с обнаружением переполнения

При использовании подписанных чисел перелив происходит, когда знак результата не соответствует ожидаемому знаку на основе входов. Обнаружение перелива имеет важное значение в процессорных ALU. На следующем фрагменте показан подписанный аддер с обнаружением перелива:

entity signed_adder is
 Port (
 A : in signed(7 downto 0);
 B : in signed(7 downto 0);
 Sum : out signed(7 downto 0);
 Overflow: out std_logic
 );
end signed_adder;

architecture Behavioral of signed_adder is
 signal extended_sum : signed(8 downto 0);
begin
 extended_sum <= (A(7) & A) + (B(7) & B); -- sign extend
 Sum <= extended_sum(7 downto 0);
 Overflow <= extended_sum(8) XOR extended_sum(7); -- sign mismatch
end Behavioral;

Вычитатели в VHDL

Вычитание может быть осуществлено либо прямым использованием оператора -, либо добавлением двух комплементов вычитания к минуэнду.В то время как поведенческое моделирование является простым, понимание распространения заимствований и обработки отрицательных результатов имеет важное значение.

Прямой поведенческий субтрактор

Простейший субтрактор использует оператор - с неподписанными или подписанными типами.Для неподписанного вычитания результат может стать отрицательным, если B > A; в таких случаях нам нужно обнаружить недоток (заимствование). Следующий пример возвращает как разницу, так и флаг заимствования:

entity subtractor4bit is
 Port (
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 Diff : out unsigned(3 downto 0);
 Borrow : out std_logic
 );
end subtractor4bit;

architecture Behavioral of subtractor4bit is
 signal temp_diff : signed(4 downto 0);
begin
 process(A, B)
 begin
 temp_diff <= signed('0' & A) - signed('0' & B);
 if temp_diff(4) = '1' then
 Borrow <= '1';
 else
 Borrow <= '0';
 end if;
 Diff <= unsigned(temp_diff(3 downto 0));
 end process;
end Behavioral;

Обратите внимание на преобразование в , подписанное для промежуточного вычисления; это позволяет правильно обрабатывать отрицательные различия.Наиболее значительный бит (temp diff(4)) действует как флаг заимствования.

Субтрактор, использующий комплемент двух

Альтернативно, вы можете реализовать вычитание, добавив дополнение двух B. Этот метод распространен при повторном использовании существующего аддитора в ALU. Комплект двух B вычисляется как (не B) + 1 .

signal B_comp : unsigned(3 downto 0);
signal sum_with_borrow : unsigned(4 downto 0);

B_comp <= (not B) + 1; -- two's complement
sum_with_borrow <= ('0' & A) + ('0' & B_comp);
Diff <= sum_with_borrow(3 downto 0);
Borrow <= not sum_with_borrow(4); -- borrow asserted if carry out is 0

Оба метода являются дружественными к синтезу; выберите тот, который соответствует архитектурным предпочтениям вашего дизайна.

Сравнение и вычитание

Вычитатели часто используются для реализации компараторов. Изучая заем или знак разницы, можно определить, является ли A>B, A<B, или A=B без выделенного компаратора. Например, после вычитания, если результат равен нулю (все биты 0), входы равны. Если заем/знак равен 1, то A<B.

Мультипликаторы в VHDL

Умножение является более ресурсоемким, чем сложение или вычитание. VHDL поддерживает оператор * для неподписанных и подписанных типов, что выводит комбинационный множитель. Однако для больших битовых ширин комбинационные множители могут потреблять значительную логику и иметь длительные задержки распространения. Последовательность и трубопроводные реализации часто необходимы для высокоскоростных конструкций.

Комбинированный множитель

4-битный множитель с использованием оператора * является тривиальным:

entity multiplier4bit is
 Port (
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 Product : out unsigned(7 downto 0)
 );
end multiplier4bit;

architecture Behavioral of multiplier4bit is
begin
 Product <= A * B;
end Behavioral;

Это выводит комбинационный множитель, который в FPGA обычно реализуется с использованием выделенных срезов DSP (например, блоков Xilinx DSP48) или логики на основе LUT. Для ширины до 18 бит большинство инструментов FPGA могут отображать умножение на один срез DSP. Для более широких множителей инструмент синтеза может объединять несколько срезов DSP или использовать мягкую логику.

Последовательный мультипликатор (Shift-and-Add)

Для проектов с ограниченными областями или когда комбинационная задержка неприемлема, можно использовать последовательный множитель, который итерирует над битами. Классический алгоритм сдвига и добавления умножает два числа N-битов на N тактовых циклов. Ниже приведен упрощенный пример (4-битный множитель, неподписанный, с управляющими сигналами, опущенными для ясности):

entity sequential_multiplier is
 Port (
 clk : in std_logic;
 reset : in std_logic;
 start : in std_logic;
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 done : out std_logic;
 Product : out unsigned(7 downto 0)
 );
end sequential_multiplier;

architecture Behavioral of sequential_multiplier is
 signal multiplicand : unsigned(7 downto 0);
 signal multiplier : unsigned(3 downto 0);
 signal product_reg : unsigned(7 downto 0);
 signal count : integer range 0 to 4;
 signal busy : std_logic;
begin
 process(clk)
 begin
 if rising_edge(clk) then
 if reset = '1' then
 count <= 0;
 busy <= '0';
 product_reg <= (others => '0');
 done <= '0';
 elsif start = '1' and busy = '0' then
 multiplicand <= "0000" & A; -- left-aligned 4-bit multiplicand
 multiplier <= B;
 product_reg <= (others => '0');
 count <= 0;
 busy <= '1';
 done <= '0';
 elsif busy = '1' then
 if multiplier(0) = '1' then
 product_reg <= product_reg + multiplicand;
 end if;
 multiplicand <= multiplicand(6 downto 0) & '0'; -- shift left
 multiplier <= '0' & multiplier(3 downto 1); -- shift right
 count <= count + 1;
 if count = 3 then
 busy <= '0';
 done <= '1';
 end if;
 end if;
 end if;
 end process;
 Product <= product_reg;
end Behavioral;

В этой конструкции используется одно L-битное дополнение на тактовый цикл (4 цикла для 4-битных входов). Это экономит площадь, но жертвует пропускной способностью и задержкой.

Пипелиновый множитель

Для высокопроизводительных приложений вставки с конвейерным мультипликатором регистрируются между стадиями комбинационного умножения. Многие инструменты синтеза FPGA могут автоматически пропускать мультипликатор при добавлении регистров трубопроводов. Например, используя для ... генерации петли или ручной вставки этапа:

-- Pipelined unsigned 4x4 multiplier (2-stage pipeline)
architecture Pipelined of multiplier4bit is
 signal stage1_prod : unsigned(7 downto 0);
 signal stage1_A, stage1_B : unsigned(3 downto 0);
 signal stage2_prod : unsigned(7 downto 0);
begin
 process(clk)
 begin
 if rising_edge(clk) then
 stage1_A <= A;
 stage1_B <= B;
 stage1_prod <= stage1_A * stage1_B; -- first stage
 stage2_prod <= stage1_prod; -- second stage
 Product <= stage2_prod;
 end if;
 end process;
end Pipelined;

Этот простой двухступенчатый подход удваивает пропускную способность (один результат на часы после начальной задержки) при добавлении только одного дополнительного регистрового слоя.

Использование DSP-срезов

Современные FPGA содержат закаленные срезы DSP, сконфигурированные для умножения и накопления. В VHDL, используя оператор *, часто автоматически делают выводы об этих блоках. Для обеспечения вывода DSP следуйте рекомендациям поставщика: держите операнды в пределах ширины среза (например, 18x18, 18x25), избегайте крупных реляционных операторов на результате и используйте соответствующую ширину. Для Xilinx 7-й серии вы также можете непосредственно инстанцировать примитивный DSP48E1, но использование оператора предпочтительно для переносимости.

Синтезный путеводитель по силинксу Вивадо

Методы оптимизации и соображения синтеза

При осуществлении арифметических операций в ВГДЛ на качество результатов влияют несколько факторов:

  • Ширина данных: Используйте наименьшую необходимую ширину для уменьшения логики. Например, если входы 5-битные, используйте неподписанный(4 до 0).
  • Атрибуты синтеза: Применять атрибуты, такие как , , use dsp или mult style, чтобы влиять на отображение.
  • Обмен ресурсами: Если несколько операций используют один и тот же аддитор или множитель, рассмотрите возможность повторного использования аппаратного обеспечения через общий компонент или один арифметический блок с мультиплексированными входами.
  • Пипелинирование: Включить регистры для соответствия временным ограничениям. Для аддитивных цепей сбалансировать размещение регистра, чтобы избежать длинных комбинаторных путей.
  • Подписанный против неподписанного: Используйте подписанный для подписанных операций; инструмент синтеза будет рассматривать MSB как бит знака, влияя на выведенную арифметическую логику.
  • Цепи переноса: Для широких добавителей цепь переноса является выделенным ресурсом в FPGA. Убедитесь, что ваш инструмент синтеза не нарушает цепь, используя неподходящие промежуточные сигналы.

Комбинация операций: пример ALU

Чтобы проиллюстрировать, как аддиторы, вычитатели и множители интегрируются в более крупный дизайн, рассмотрим простой арифметический логический блок (ALU), который может добавлять, вычитать или умножать два 8-битных значения на основе выбранного сигнала:

entity alu is
 Port (
 A, B : in signed(7 downto 0);
 op : in std_logic_vector(1 downto 0); -- "00": add, "01": sub, "10": mul
 result : out signed(15 downto 0)
 );
end alu;

architecture Behavioral of alu is
begin
 process(A, B, op)
 begin
 case op is
 when "00" => result <= resize(A + B, 16); -- sign extend
 when "01" => result <= resize(A - B, 16);
 when "10" => result <= A * B;
 when others => result <= (others => '0');
 end case;
 end process;
end Behavioral;

Этот ALU повторно использует один и тот же регистр результатов и объединяет три операции. В синтезе каждая операция реализуется как отдельный блок, с выходом, выбранным мультиплексором. В зависимости от целевого устройства множитель может быть критическим путем.

Использование IP-ядер для комплексной арифметики

Для продвинутых операций (например, плавающей точки, квадратного корня, модуля) или когда требуется максимальная производительность, рекомендуется использовать IP-ядра, предоставляемые поставщиком. Они высоко оптимизированы и имеют проверенные модели моделирования. В VHDL вы инстанцируете IP-ядро в качестве компонента, отображая свои сигналы на его порты. Общие ядра включают:

  • Xilinx Floating-Point Operator для добавления/подмножения/разделяния в формате IEEE 754.
  • Altera (Intel) ALTMULT ADD для операций с множественным добавлением.
  • Разделитель латиницы для фиксированного точечного деления.

Использование IP-ядер минимизирует риск и часто приводит к лучшей производительности, чем ручные эквиваленты.

Intel FPGA IP Cores Guide

Испытания и проверка

Моделирование имеет решающее значение для арифметических конструкций. Напишите тест-системы, которые выполняют угловые случаи: переполнение, ноль, максимальные значения и смешанные знаки (для подписанных типов). Для множителей проверьте все комбинации наименьших входов для проверки алгоритма. Используйте утверждение , чтобы проверить ожидаемые результаты. Пример фрагмента тест-системы для 4-битного добавщика:

signal A, B : unsigned(3 downto 0);
signal Sum : unsigned(3 downto 0);
signal Cout : std_logic;
...
A <= "1100"; B <= "0011"; wait for 10 ns;
assert (Sum = "1111" and Cout = '0')
 report "Adder failed for 12 + 3" severity error;

Для более крупных проектов рассмотрите возможность использования случайных стимулов и золотых моделей в языках сценариев (Python, Tcl) для генерации тестовых векторов.

Техники Testbench (SynthWorks)

Заключение

Внедрение арифметических операций в VHDL представляет собой смесь понимания цифровой арифметики, умелого использования типов данных и операторов и осведомленности о поведении инструмента синтеза. Добавители и вычитатели просты при использовании numeric std, в то время как множители требуют тщательного рассмотрения производительности и области. Используя поведенческие описания, вы быстро достигаете рабочих конструкций и, применяя методы, такие как конвейерирование, совместное использование ресурсов и вывод DSP, вы оптимизируете для реального оборудования. С приведенными примерами и руководящими принципами, вы оснащены для создания надежных арифметических единиц для любой цифровой системы.

Для дальнейшего чтения, обратитесь к справочному руководству по языку IEEE VHDL и документации по арифметическому выводу для конкретного поставщика.

IEEE Std 1076-2008 VHDL Language Reference Manual