Pull to refresh

VHDL. Реконфигурируемый дизайн

Reading time4 min
Views9.8K
Доброго дня.

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

Проблема


Например имеется спроектированный цифровой фильтр, коэффициенты которого представлены числами с плавающей запятой. Но в рамках решаемой задачи необходимо построить систему оперирующую числами с фиксированной запятой, также хотелось бы иметь возможность изменять разрядность без необходимости пересчёта коэффициентов.
Для того чтобы больше углубиться в проблему представим ситуацию когда у нас есть модуль со следующими параметрами:
component FIRCustomizeDesign is
  generic(
    SizeD: integer := 16; -- Размерность входных векторов.
    -- Формат числа предполагает 1 бит на целую часть и SizeD-1 для дробной, т.е. интервал [-1;1].
 
    -- Число параметров уменьшено для наглядности.
    P01 : integer := -32768; -- = (-0.5)*2^SizeD -- (зависим от разрядности SizeD)
    -- Коэффициенты фильтра будем передавать в Integer, 
    -- далее от него будут выполнять преобразования типов . Так удобнее и для того чтобы
    -- подключить модуль нет необходимости дописывать дополнительные библиотеки.
    P02 : integer := 22016; -- = (43/64)*2^SizeD
    P03 : integer := -4352;  -- = -0.0664
    -- и так далее...
  );
  port(
    rst,sclk : in std_logic;
    data_ready: in std_logic; -- готовность входных данных
 
    -- считаем 0-ой бит младшим значащим.
    i_data: in std_logic_vector(SizeD-1 downto 0); 
 
    pipeline_ready: out std_logic; 
    ready: out std_logic; -- готовность выходных данных
 
    o_data: out std_logic_vector(SizeD-1 downto 0);
  );
Где параметры P0(0-n) передают всякие значения, которые могут изменяться с изменением разрядности входных данных, и необходимо их корректно пересчитывать. Оперировать типом integer в данном случае удобно (если его размерности достаточно), в архитектуре модуля он легко конвертируется в signed/unsigned:
VAL <= to_signed(P0, SizeD)+to_signed(P1, SizeD); -- для провердения корректных вычислений
-- в выбранной разрядности,
outdata <= std_logic_vector(VAL); -- а также прозрачному выводу.
Таким образом, когда мы захотим поэкспериментировать с разрядностью и изменять число SizeD, все коэффициенты фильтра придётся пересчитывать. Согласитесь, много раз это делать не очень удобно. Конечно, можно было бы написать внешний генератор, но решение будет не легковесным и потребует каждый раз дополнительных движений.
Моя цель — воспользоваться стандартными библиотеками VHDL и описать параметры таким образом, чтобы при изменении SizeD все P0(1-n) пересчитывались автоматически.

Решение


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

Далее приводиться пример подключения к тесту, принципиальных отличий от использования в топовом модуле тут нету.
LIBRARY ieee;
  use ieee.std_logic_1164.all;
  use ieee.numeric_std.all;
  use ieee.std_logic_textio.all;
  use ieee.math_real.all;
LIBRARY floatfixlib;
  use floatfixlib.fixed_pkg.all;
LIBRARY std;
  use std.textio.all;
 
entity FIRCustomizeDesign_tb is
  -- empty
end FIRCustomizeDesign_tb;
 
architecture DUT of FIRCustomizeDesign_tb is
 
-- Функция для преобрзарования типа sfixed (Signed Fixed Point) в Interger
  function sFix2Int(: UNRESOLVED_sfixed)
    return integer is
  begin
    -- Преобразуем sfixed, выбрав тип UNRESOLVED_sfixed для однозначности значения,
    -- далее переводим его в std_logic_vector, затем указывая что он знаковый в SIGNED,
    -- уже получено нужное значение, но всё равно необходимо привести к integer. 
    return (to_integer(SIGNED(to_Std_Logic_Vector(r))));
  end function sFix2Int;
 
  constant SizeD: integer := 16;
  constant clk_period: time := 100 ns; 
  -- Определим коэффициенты фильтра.
 
  -- Для того чтобы поделить числа нужно явно определить их как real,
  -- так регламентирует синтаксис VHDL.
  constant P01: real := -real(17)/real(128); 
  constant P02: real :=  sqrt(real(0.675));  -- Производим любые 
                         --математические действия с высокой точностью.
  constant P03: real :=  real(0); -- Всё жестко типизировано.
  -- и т.д.
  -- Знаковые числа с фиксированной точкой
  constant Decimal_Length: integer := 1; -- Длина целой части.
  -- В рамках примера формат фиксированный:
  constant Fractional_Length: integer := SizeD-Decimal_Length;
  -- Приведём границы числа в формат понятный для функции to_sfixed.
  constant fx_left : integer := Decimal_Length-1;
  constant fx_right : integer := -(Fractional_Length);
  -- Оперделим подтип для более элегантной записи.
  subtype qfixed is sfixed( fx_left downto fx_right);
  -- Теперь всё приведено к целевому формату.
  constant fP01: qfixed := to_sfixed( aP21 ,fx_left,fx_right);
  constant fP02: qfixed := to_sfixed( aP22 ,fx_left,fx_right);
  constant fP03: qfixed := to_sfixed( aP23 ,fx_left,fx_right);
begin
 
FIRDUT: FIRCustomizeDesign generic map(
    SizeD => SizeD,
    P01 => sFix2Int(fP01)-- переведём sfixed в integer
    P02 => sFix2Int(fP02),
    P03 => sFix2Int(fP03)
    )
    port map(
      rst => rst, clk => clk, data_ready => data_ready,
      i_data => i_data, ready => ready,pipeline_ready => pipeline_ready,
      o_data0 => o_data0
      );
 
-- Далее всё стандартно.
test_reactor:
  process …
 
end DUT;

Достоинства:

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

Недостатки:

  • Конструкции по приведению типов выглядят достаточно громоздко, но в этом и особенность VHDL, выраженная в жесткой типизации.
  • Использование промежуточного типа integer накладывает дополнительные ограничения, но в рамках моей задачи всего было достаточно.

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

Спасибо за внимание.
Tags:
Hubs:
+15
Comments6

Articles