AFON // радиотехник affonya@mail.ru
← Проекты на VHDL

Проект 28. Прямой цифровой синтез (DDS)

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

Задача

Сформировать отсчёты синусоиды с задаваемой частотой.

Тип схемы: Синхронная схема: накопитель фазы и таблица отсчётов.
Модуль: dds. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования. Теорию по языку смотрите в статье VHDL — язык описания аппаратуры, по самим микросхемам — на странице ПЛИС.

Алгоритм

  1. Каждый такт прибавлять к накопителю фазы код приращения.
  2. Старшие разряды фазы использовать как адрес таблицы.
  3. Два самых старших разряда определяют четверть периода.
  4. По симметрии синуса развернуть четверть таблицы в полный период.

Код на VHDL

Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v28.vhd

--=====================================================
-- Проект 28. Прямой цифровой синтез (DDS)
-- Накопитель фазы адресует таблицу синуса:
-- частота задаётся приращением фазы.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity dds is
    generic (
        PHASE_W : integer := 24                         -- разрядность накопителя
    );
    port (
        clk       : in  std_logic;
        rst       : in  std_logic;
        phase_inc : in  std_logic_vector(PHASE_W-1 downto 0);   -- приращение
        wave      : out std_logic_vector(7 downto 0)            -- отсчёт синуса
    );
end entity dds;

architecture rtl of dds is
    signal phase : unsigned(PHASE_W-1 downto 0) := (others => '0');

    -- четверть периода синуса, 16 точек
    type lut_t is array (0 to 15) of integer range 0 to 127;
    constant QUARTER : lut_t := (
        0, 12, 24, 36, 48, 58, 68, 77,
        85, 92, 98, 103, 107, 110, 112, 113);

    signal idx : integer range 0 to 15;
    signal quad : std_logic_vector(1 downto 0);
    signal val  : integer range 0 to 127;
begin
    process (clk, rst)
    begin
        if rst = '1' then
            phase <= (others => '0');
        elsif rising_edge(clk) then
            phase <= phase + unsigned(phase_inc);       -- накопление фазы
        end if;
    end process;

    -- старшие биты фазы: номер четверти и индекс в таблице
    quad <= std_logic_vector(phase(PHASE_W-1 downto PHASE_W-2));
    idx  <= to_integer(phase(PHASE_W-3 downto PHASE_W-6));
    val  <= QUARTER(idx);

    -- симметрия синуса: одна четверть таблицы даёт весь период
    process (quad, val, idx)
    begin
        case quad is
            when "00"   => wave <= std_logic_vector(to_unsigned(128 + val, 8));
            when "01"   => wave <= std_logic_vector(to_unsigned(128 + QUARTER(15-idx), 8));
            when "10"   => wave <= std_logic_vector(to_unsigned(127 - val, 8));
            when others => wave <= std_logic_vector(to_unsigned(127 - QUARTER(15-idx), 8));
        end case;
    end process;
end architecture rtl;

Пояснения

  • Частота на выходе равна тактовой частоте, умноженной на приращение и делённой на два в степени разрядности фазы — отсюда чрезвычайно мелкий шаг перестройки.
  • Хранение только четверти периода экономит три четверти памяти: остальное восстанавливается зеркальным отражением и инверсией.
  • Разрядность фазы отвечает за шаг частоты, а разрядность отсчёта — за чистоту спектра; это независимые параметры.

Что дальше

Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.