Задача
Сформировать отсчёты синусоиды с задаваемой частотой.
Тип схемы: Синхронная схема: накопитель фазы и таблица отсчётов.
Модуль: dds. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования.
Теорию по языку смотрите в статье VHDL — язык описания аппаратуры,
по самим микросхемам — на странице ПЛИС.
Алгоритм
- Каждый такт прибавлять к накопителю фазы код приращения.
- Старшие разряды фазы использовать как адрес таблицы.
- Два самых старших разряда определяют четверть периода.
- По симметрии синуса развернуть четверть таблицы в полный период.
Код на 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.