Вычислить e^x для значений с плавающей точкой в ​​System Verilog?

Я строю нейронную сеть, работающую на ПЛИС, и последняя часть головоломки - это аппаратная функция сигмоида. Это либо:

1/(1 + e^-x)

или же

(atan(x) + 1) / 2

К сожалению, х здесь является значением с плавающей запятой (real значение в SystemVerilog).

Есть ли какие-либо советы о том, как реализовать любую из этих функций в SystemVerilog?

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

6 ответов

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

Для аппаратных реализаций любую нелинейную функцию можно аппроксимировать как кусочно-линейную функцию и использовать подход к реализации на основе ПЗУ, как описано в предыдущих ответах. Количество точек выборки, которые вы берете из нелинейной функции, определяет вашу точность. Чем больше сэмплов вы сохраните, тем лучше будет аппроксимация функции, которую вы получите. Часто в аппаратном обеспечении количество сэмплов, которые вы можете хранить, может быть ограничено объемом доступной вам быстрой/локальной памяти. В этом случае для оптимизации ресурсов памяти можно добавить немного дополнительных вычислительных ресурсов и выполнить линейную интерполяцию для вычисления необходимых значений.

Я только что закончил это с помощью Vivado HLS, который позволяет писать схемы на C. Вот мой C-код.

#include math.h

void exp(float a[10],b[10])

{
    int i;
    for(i=0;i<10;i++)
    {
        b[i] = exp(a[i]);
    }
}

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

Один более простой способ для этого - создать память / массив для этой функции. Однако этот вариант может быть крайне неэффективным.

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

Предположим, значение вашей функции заключается в следующем. (Это всего лишь пример)

x = 0 => f(0) = 1
x = 1 => f(0) = 2
x = 2 => f(0) = 3
x = 3 => f(0) = 4

Таким образом, вы можете создать для этого массив, в котором будут храниться выходные значения.

int a[4] = `{1, 2, 3, 4};

Вам действительно нужно для этого плавающее число? Достаточно ли фиксированной точки?

Учитывая (atan(x) + 1) / 2, вполне вероятно, что единственными полезными значениями x являются те, где показатель степени довольно мал. (если показатель большой, ваш ответ - пи / 2).

atan числа с фиксированной точкой может быть довольно легко вычислено в HW; есть сердечные методы (см. https://zipcpu.com/dsp/2017/08/30/cordic.html) и прямые методы; см., например, https://dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization/

Как вы, похоже, понимаете, тип real не синтезируется. вам нужно работать с целочисленной мантиссой типа и показателем целочисленного типа отдельно и объединять их, когда вы закончите, отслеживая знак. Как только вы позаботитесь о (e^-x), все остальное должно быть простым.

попробуйте эту страницу для быстрого объяснения: https://www.geeksforgeeks.org/floating-point-representation-digital-logic/

и поиск по "цифровому дизайну с плавающей запятой" для получения дополнительных объяснений / примеров.

Потоки проектирования ПЛИС, в которых нацелено оборудование (ПЛИС), обычно не поддерживают числа с плавающей запятой в матрице ПЛИС. Чаще используется фиксированная точка с ограниченной точностью.

Подход с фиксированной точкой ограниченной точности:
используйте Matlab для создания массива выборок для вашей математической функции, чтобы максимальное значение было +/- .99999. Для 8-битной точности (на самом деле 7 со знаковым битом) умножьте эти числа на 128, округлите до десятичной точки и опустите дробную часть. Запишите эти числа в текстовый файл в шестнадцатеричном формате с дополнением до двух. В SystemVerilog вы можете реализовать ПЗУ, используя этот текстовый файл. Используйте $readmemh() для считывания этих чисел в переменную стиля памяти (имеющую как упакованное, так и распакованное измерение). Ссылка на руководство:
https://projectf.io/posts/initialize-memory-in-verilog/ .
Теперь у вас есть ПЗУ с образцами вашей функции с ограниченной точностью.

Раздел 21.4 Загрузка данных массива памяти из файла в спецификации SystemVerilog предоставляет определение для $readmh(). Вот этот документ:
https://ieeexplore.ieee.org/document/8299595

Если вам нужна плавающая запятая, одна из возможностей - использовать программное ядро ​​процессора с модулем с плавающей запятой, реализованным в матрице FPGA, и запускать программное обеспечение на этом ядре. Основной интерфейс с остальной структурой FPGA через физическую шину, такую ​​как обработка пары axi4. См.
Https://www.xilinx.com/products/design-tools/microblaze.html, чтобы начать работу.
Этот рабочий процесс сильно отличается от обычного проектирования FPGA и использует другие инструменты. Компилятор C или C++ с математическими библиотеками (tan, exp, div и т. Д.) Будет использоваться вместе с ядром процессора.

Еще одна возможность для фиксированной точки - это ПЛИС с жестким ядром процессора. Xilinx Zynq - один из них. Это сложный и мощный подход. Бесплатная бесплатная книга содержит информацию о том, как использовать Zynq
http://www.zynqbook.com/ .
Этот рабочий процесс еще более сложен, чем подход с программным ядром, потому что Zynq - более сложная платформа (жесткий процессор и FPGA интегрированы в один чип).

Другие вопросы по тегам