Программная инженерия и программирование
Создание пользовательских пакетов Vhdl для модульных и поддерживаемых структур кода
Table of Contents
Введение в пользовательские пакеты VHDL
VHDL (VHSIC Hardware Description Language) является краеугольным камнем цифрового дизайна, позволяя инженерам моделировать, имитировать и синтезировать сложные электронные системы. По мере роста размеров и сложности дизайны становятся критически важными, поддержание качества кода становится критическим. Одним из наиболее эффективных способов достижения модульного, многоразового и поддерживающего кода в VHDL является использование пользовательских пакетов. Пакет VHDL инкапсулирует связанные декларации, такие как типы, подтипы, константы, функции и процедуры, позволяя дизайнерам делиться общей функциональностью в нескольких единицах дизайна. Эта статья предоставляет всеобъемлющее руководство по созданию и использованию пользовательских пакетов VHDL, с практическими примерами, передовыми методами и передовыми практиками для профессиональной разработки оборудования.
Независимо от того, строите ли вы небольшой проект FPGA или большой дизайн ASIC, мастеринг пакетов упростит ваш рабочий процесс, уменьшит ошибки и облегчит чтение и обслуживание вашего кода.
Что такое VHDL пакет?
Пакет VHDL является конструктивным блоком, который обеспечивает способ группирования логически связанных деклараций. Он состоит из двух частей:
- Декларация пакета (FLT:0) определяет интерфейс — типы, подтипы, константы, сигналы, функции и процедуры, которые видны внешнему миру.
- Корпус пакета (факультативно): Содержит реализацию любых подпрограмм (функций и процедур), заявленных в пакетной декларации.
Сам пакет хранится в библиотеке (обычно библиотеке «работа» или библиотеке, определяемой пользователем) и может ссылаться на другие блоки проектирования с использованием пункта . Это разделение интерфейса и реализация является фундаментальным принципом модульного дизайна в VHDL.
Декларация пакета Основы
Декларация пакета начинается с ключевого слова , за которым следует название пакета и заканчивается (или ).
- Типы и подтипы — например, перечисленные типы, массивы, записи или подтипы стандартных типов.
- Константы — глобальные значения, которые остаются фиксированными при моделировании или синтезе.
- Функции и процедуры — подпрограммы, работающие по заявленным типам.
- Атрибуты и псевдонимы (менее распространенные, но полезные в продвинутых контекстах).
Вот структура скелета:
package My_Package is
-- Type declarations
type State_Type is (idle, read, write, error);
subtype Byte is std_logic_vector(7 downto 0);
-- Constant declarations
constant Clock_Period : time := 10 ns;
constant Data_Width : integer := 16;
-- Function prototype
function Max(A, B : integer) return integer;
-- Procedure prototype
procedure Reset_Counters(signal count1, count2 : inout integer);
end package My_Package;
Реализация пакетного корпуса
Корпус упаковки содержит полное осуществление любых подпрограмм, заявленных в заголовке упаковки. Он должен иметь то же название, что и пакет, и должен быть объявлен как .
package body My_Package is
function Max(A, B : integer) return integer is
begin
if A > B then
return A;
else
return B;
end if;
end function Max;
procedure Reset_Counters(signal count1, count2 : inout integer) is
begin
count1 <= 0;
count2 <= 0;
end procedure Reset_Counters;
end package body My_Package;
Обратите внимание, что типы, константы и другие неподпрограммные декларации не фигурируют в корпусе упаковки; они только в части декларации.
Зачем использовать пользовательские пакеты VHDL?
Пользовательские пакеты предлагают многочисленные преимущества, которые согласуются с современными методами разработки программного обеспечения, применяемыми к описанию оборудования:
- Модульность: Разбивайте сложные конструкции на автономные блоки с четкими интерфейсами. Каждый пакет обрабатывает определенный домен (математические утилиты, шинные протоколы, типы памяти и т. д.).
- Многоразовая возможность: Хорошо продуманный пакет может использоваться в десятках проектов. Например, пакет, определяющий общие сигналы шины (например, AXI-Stream), может быть разделен между ядрами IP, экономя недели переделки.
- Устойчивость : При изменении требований пакет обновляется в одном месте, и все используемые им конструкторские блоки автоматически получают обновление — без изменения каждого объекта.
- Согласованность: Централизованные определения типов предотвращают несоответствия.Если каждый файл определяет свой собственный , более позднее изменение на 9 битов требует поиска каждого события.
- Читаемость: Описательные названия пакетов и хорошо документированные декларации делают код самообъясняющимся.Новые члены команды могут быстро понять строительные блоки дизайна.
- Дружественное управление версиями : Изменения в пакете отслеживаются только как изменения в этом файле, упрощая обзоры кода и слияние.
Профессиональные потоки проектирования VHDL, такие как те, которые используются в оборонных подрядчиках, аэрокосмических компаниях и полупроводниковых фирмах, требуют использования пакетов для любого нетривиального проекта. Стандарт IEEE VHDL (1076) явно поддерживает пакеты, и их использование рекомендуется руководящими принципами передовой практики, такими как рекомендации из группы анализа и стандартизации VHDL (VASG) [[FLT: 1]].
Пошаговое руководство: Создание и использование пользовательского пакета
Давайте пройдемся по полному процессу создания надежного пользовательского пакета и интеграции его в дизайн VHDL.
Шаг 1: Определите декларацию пакета
Откройте новый файл VHDL (например, ]) и напишите декларацию пакета. Начните с библиотечного пункта, чтобы включить стандартные библиотеки IEEE, если вы используете их типы (например, ]. Однако сам пакет может быть чистым VHDL - это зависит только от выбранных вами типов. Для максимальной портативности избегайте полагаться на конкретные пакеты поставщиков, если это не требуется.
-- math_utils.vhd
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
package Math_Utils is
-- Type for signed integers (if needed for synthesis)
subtype Slv16 is std_logic_vector(15 downto 0);
-- Constants
constant PI : real := 3.141592653589793;
constant E : real := 2.718281828459045;
constant MAX_DIM : integer := 256;
-- Function prototypes
function Add(A, B : integer) return integer;
function Multiply(A, B : integer) return integer;
function Clamp(Value, Low, High : integer) return integer;
function Is_Power_Of_Two(Value : positive) return boolean;
function Log2(Value : positive) return natural;
-- Procedure prototypes
procedure Swap(signal X, Y : inout integer);
end package Math_Utils;
Обратите внимание, что мы включили функцию Clamp (насыщает целое число между границами) и функцию Log2 (обычно используется для расчетов ширины бита адреса).
Шаг 2: Напишите тело упаковки
В одном и том же файле (или отдельном файле) реализован корпус пакета.Большинство конструкторов держат оба в одном файле, потому что корпус может быть длинным, но синтаксически они могут быть отдельными.
package body Math_Utils is
function Add(A, B : integer) return integer is
begin
return A + B;
end function Add;
function Multiply(A, B : integer) return integer is
begin
return A * B;
end function Multiply;
function Clamp(Value, Low, High : integer) return integer is
begin
if Value < Low then
return Low;
elsif Value > High then
return High;
else
return Value;
end if;
end function Clamp;
function Is_Power_Of_Two(Value : positive) return boolean is
begin
return (Value and (Value - 1)) = 0;
end function Is_Power_Of_Two;
function Log2(Value : positive) return natural is
variable result : natural := 0;
variable temp : natural := Value;
begin
while temp > 1 loop
temp := temp / 2;
result := result + 1;
end loop;
return result;
end function Log2;
procedure Swap(signal X, Y : inout integer) is
variable tmp : integer;
begin
tmp := X;
X <= Y;
Y <= tmp;
end procedure Swap;
end package body Math_Utils;
Примечание по синтезу: Процедуры и синтезируемы (зажим производит компараторы и мультиплексоры; своп использует промежуточную переменную. и используются в тестбенчах или для генерации конфигурационных параметров. Если вам нужны синтезируемые версии, убедитесь, что корпус упаковки не использует конструкции, такие как рекурсия или неограниченные петли, которые не поддерживаются вашим инструментом.
Шаг 3: Используйте пакет в своем дизайне
Чтобы использовать пакет, добавьте пункт в свою сущность или архитектуру. Типичный синтаксис:
library work;
use work.Math_Utils.all;
Ключевое слово делает все объявления видимыми. Вы также можете выборочно импортировать определенные элементы, используя и т. Д.
Вот полный пример организации, которая использует наш пакет:
-- calculator.vhd
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.Math_Utils.all;
entity Calculator is
port (
A, B : in integer;
Operation: in std_logic_vector(1 downto 0);
Result : out integer;
Error : out std_logic
);
end entity Calculator;
architecture Behavioral of Calculator is
begin
process(A, B, Operation)
variable temp : integer;
begin
Error <= '0';
case Operation is
when "00" => Result <= Add(A, B);
when "01" => Result <= Multiply(A, B);
when "10" =>
temp := A - B;
-- Clamp the result to 0..255
Result <= Clamp(temp, 0, 255);
when "11" =>
if B = 0 then
Error <= '1';
Result <= -1;
else
Result <= A / B;
end if;
when others =>
Error <= '1';
Result <= 0;
end case;
end process;
end architecture Behavioral;
В этой конструкции мы используем , и из пакета. процедура не используется здесь, но может быть вызвана в тестбенч для обмена значениями.
Расширенные возможности пакета
Помимо основных функций и констант, пакеты VHDL поддерживают несколько усовершенствованных конструкций, которые повышают модульность и абстракцию.
Перегрузка в пакетах
VHDL позволяет перегружать функции и процедуры различными типами параметров или числами. Это особенно полезно, когда вам нужно обрабатывать различные представления данных (например, , , ).
package Overload_Example is
function Add(A, B : integer) return integer;
function Add(A, B : std_logic_vector) return std_logic_vector;
function Add(A, B : signed) return signed;
end package Overload_Example;
package body Overload_Example is
function Add(A, B : integer) return integer is
begin
return A + B;
end function;
function Add(A, B : std_logic_vector) return std_logic_vector is
variable result : std_logic_vector(A'length-1 downto 0);
begin
result := std_logic_vector(unsigned(A) + unsigned(B));
return result;
end function;
function Add(A, B : signed) return signed is
begin
return A + B;
end function;
end package body Overload_Example;
Правильная версия решается во время компиляции на основе типов аргументов. Это позволяет избежать загромождения вашего дизайна именами функций, такими как , и т. Д.
Общие пакеты (VHDL-2008 и более поздние версии)
Начиная с VHDL-2008, пакеты могут быть параметризированы с помощью дженериков, похожих на сущности и компоненты. Общий пакет позволяет определять типы, константы или подпрограммы, зависящие от общего параметра. Например, пакет для FIFO любой глубины и ширины данных:
generic type data_type is private;
package Generic_FIFO is
type FIFO_Array is array (natural range <>) of data_type;
function Is_Empty(f : FIFO_Array) return boolean;
-- ...
end package Generic_FIFO;
package body Generic_FIFO is
function Is_Empty(f : FIFO_Array) return boolean is
begin
return f'length = 0;
end function;
end package body Generic_FIFO;
Чтобы создать универсальный пакет, вы используете пункт с общей картой:
package my_fifo_pkg is new work.Generic_FIFO
generic map (data_type => std_logic_vector(7 downto 0));
Это мощная функция для создания многоразовых библиотек. Однако поддержка общих пакетов ограничена в некоторых старых инструментах синтеза; проверьте документацию поставщика (Xilinx, Intel, Lattice). Стандартное справочное руководство по языку VHDL (1076-2019) IEEE включает в себя полные детали по общим пакетам.
Защищенные типы (VHDL-2002+)
Защищенные типы позволяют использовать общие переменные в VHDL, полезные для тестбенчей и моделирования высокого уровня (например, табло). Они объявлены внутри пакета в качестве защищенного определения типа. Это скорее функция проверки, чем синтез, но она помогает при создании сложных утилит тестбенча.
package Scoreboard_Pkg is
type Scoreboard_Type is protected
procedure Push(item : integer);
function Pop return integer;
function Count return natural;
end protected Scoreboard_Type;
end package Scoreboard_Pkg;
Реализация защищенного типа должна появиться в корпусе пакета, похожем на подпрограммы.Защищенные типы являются наиболее близкими VHDL к объектно-ориентированному программированию.
Лучшие практики для дизайна пакетов
Чтобы сделать ваши пакеты действительно ремонтопригодными и многоразовыми, следуйте этим рекомендациям:
1.Названия пакетов четко
Используйте описательные имена, которые отражают домен пакета: , , . Избегайте общих имен, таких как или .
2.Сосредоточьтесь на пакетах
Не сбрасывайте каждую функцию в один огромный пакет. Вместо этого создайте несколько небольших, сплоченных пакетов. Например, отделите математические процедуры от типов интерфейса шины. Это улучшает читаемость и уменьшает зависимости от рекомпиляции.
3.Документировать интерфейс пакета
Включите комментарии, объясняющие цель каждого типа, константу и подпрограмму. Упомяните любые ограничения (например, функции, не синтезируемые). Используйте последовательный стиль комментариев (инструментально-агностический). Пример:
-- Clamp: Returns Value saturated within [Low, High].
-- If Low > High, returns Low.
-- This function is synthesizable.
4. использовать стандартные типы, где это возможно;
Определите подтипы или от , а не изобретайте новые типы оснований. Это поддерживает совместимость с большинством IP-адресов и библиотек.
5.Избегать скрытых зависимостей
Если ваш пакет использует типы из другого пакета, явно их в декларации пакета.Не полагайтесь на родительский блок дизайна, который включил другой пакет - это вызовет проблемы с заказом компиляции.
6. Версия пакетов
Включите константу, которая отслеживает версию пакета (major.minor). Это помогает при отладке проблем интеграции.
constant PKG_VERSION : string := "1.2";
Обычные подводные камни и как их избежать
Даже опытные VHDL-дизайнеры время от времени попадают в ловушки с упаковками. Вот наиболее частые проблемы:
- Отсутствующий корпус пакета для функций: Если вы объявляете функцию в пакете, вы должны предоставить реализацию в корпусе пакета, даже если это тривиально.
- Заказ компиляции: Пакет должен быть составлен перед любым блоком проектирования, который его использует. Используйте настройки порядка компиляции симулятора или инструмента синтеза или компиляцию пакетов в библиотеку, отдельную от объектов дизайна.
- Конфликты имен: Если два пакета объявляют одно и то же имя, VHDL не может разрешить ссылку. Используйте с явными именами (например, ) или переименуйте импорт с использованием .
- Орган упаковки без деклараций: Законно иметь декларацию пакета без подпрограмм (только типы/составляющие). В этом случае вам не нужен орган пакета. Но если вы позже добавите подпрограмму, вы должны добавить тело.
- Каскады рекомпилирования: Изменение декларации пакета заставляет перекомпилировать все конструктивные блоки, которые его используют. Для минимизации этого в декларации помещают только стабильные интерфейсы; помещают детали реализации (например, локальные вспомогательные функции) в корпус пакета, если они не нужны другим блокам.
- Несинтезируемые подпрограммы: Функции, такие как ввод/вывод файла, рекурсия или динамическое распределение, не синтезируемы. Отметьте их четко в комментариях, чтобы инструменты синтеза могли быть настроены на игнорирование пакетов, предназначенных только для моделирования.
Пример из реального мира: простой пакет UART
Чтобы проиллюстрировать практическое применение, давайте разработаем пакет для UART (универсальный асинхронный приемник / передатчик). Этот пакет определяет константы и функции для генерации скорости бод и форматирования данных.
package UART_Utils is
constant DEFAULT_BAUD : positive := 115200;
constant CLK_FREQ : positive := 50000000; -- 50 MHz
-- Calculate divisor for baud rate generator
function Baud_Divider(Baud : positive; Clock_Freq : positive) return natural;
-- Serialize a byte (LSB first)
function To_Serial(B : std_logic_vector(7 downto 0)) return std_logic_vector;
-- Deserialize a 10-bit UART frame (start, 8 data, stop)
function From_Serial(frame : std_logic_vector(9 downto 0)) return std_logic_vector;
-- Type for UART state machine
type UART_State is (Idle, Start, Data, Stop, Error);
end package UART_Utils;
package body UART_Utils is
function Baud_Divider(Baud : positive; Clock_Freq : positive) return natural is
begin
return (Clock_Freq / Baud) - 1;
end function;
function To_Serial(B : std_logic_vector(7 downto 0)) return std_logic_vector is
variable result : std_logic_vector(9 downto 0);
begin
result(0) := '0'; -- start bit
for i in 0 to 7 loop
result(i+1) := B(i);
end loop;
result(9) := '1'; -- stop bit
return result;
end function;
function From_Serial(frame : std_logic_vector(9 downto 0)) return std_logic_vector is
variable data : std_logic_vector(7 downto 0);
begin
for i in 0 to 7 loop
data(i) := frame(i+1);
end loop;
return data;
end function;
end package body UART_Utils;
Этот пакет может использоваться как в передатчиках, так и в приемниках UART, обеспечивая последовательные вычисления параметров.
Заключение
Настраиваемые пакеты VHDL являются краеугольным камнем профессионального аппаратного проектирования. Они способствуют модульности, многоразовости и ремонтопригодности - качествам, которые становятся все более важными по мере масштабирования проектов. Инкапсулируя общие типы, константы и подпрограммы, вы уменьшаете дублирование, предотвращаете ошибки и облегчаете навигацию и обновление вашей кодовой базы.
В этой статье мы рассмотрели синтаксис и структуру пакетов, пошаговое создание, расширенные функции, такие как перегрузка и дженерики, лучшие практики и общие подводные камни. Независимо от того, разрабатываете ли вы небольшой проект FPGA или многочиповую систему, принятие дисциплинированной стратегии пакетов принесет дивиденды в сжатые сроки разработки и улучшенное качество дизайна.
Для дальнейшего чтения обратитесь к справочному руководству по языку IEEE Standard VHDL (1076-2019) и рекомендациям для поставщиков от Xilinx или Intel. EDA Playground является хорошим онлайн-инструментом для экспериментов с дизайнами на основе пакетов.