Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: помогите разобраться с кодом
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Работаем с ПЛИС, области применения, выбор
mcaffee
Всем доброго времени суток!
Среди поисков алгоритмов dds нашел вот такой код.
Из этого кода все, что я понял, так это то, что 40 рязрядный сумматор разбит на десять 4-х разрядных сумматоров. Не могли бы Вы описать более подробную картину, что здесь происходит?)

CODE
long_code_here =
library ieee;
USE IEEE.std_logic_1164.all;
USE IEEE.std_logic_arith.all;
entity Accumulator is
port (
clk : in std_logic;
rst : in std_logic;
wr : in std_logic;
D : in std_logic_vector (39 downto 0);
S : out std_logic_vector (39 downto 0)
);
end Accumulator;
architecture rtl of Accumulator is
component Adder_4bit is
port (
clk : in std_logic;
rst : in std_logic;
A : in std_logic_vector (3 downto 0);
B : in std_logic_vector (3 downto 0);
cin : in std_logic;
S : out std_logic_vector (3 downto 0);
cout : out std_logic
);
end component;
component my_reg is
generic (
size : integer range 1 to 255:=1;
W : integer range 1 to 32:=4
);
port (
clk : in std_logic;
rst : in std_logic;
wr : in std_logic;
D : in std_logic_vector (W-1 downto 0);
Q : out std_logic_vector (W-1 downto 0)
);
end component;
type result_type is array(9 downto 0) of std_logic_vector(3 downto 0);
signal res0, res1, res, input : result_type:=(others=>(others=>'0'));
signal carry_chain0, carry_chain1, carry_chain : std_logic_vector (9 downto 0):=(others=>'0');
begin
inputs : for i in 1 to 10 generate
reg_chain : my_reg
generic map (i, 4)
port map (clk, rst, D(4*i-1 downto 4*(i-1)), input(i-1));
end generate;

first_adder : Adder_4bit
port map (clk, rst, input(0), res(0), '0', res(0), carry_chain(0));
adders : for i in 1 to 9 generate
carry : Adder_4bit
port map (clk, rst, input(i), res(i), '1', res1(i), carry_chain1(i));
no_carry : Adder_4bit
port map (clk, rst, input(i), res(i), '0', res0(i), carry_chain0(i));
end generate;
sum_mux : for i in 1 to 9 generate
res(i)<=
res0(i) when carry_chain(i-1)='0' else
res1(i);
carry_chain(i)<=
carry_chain0(i) when carry_chain(i-1)='0' else
carry_chain1(i);
end generate;
output_assign : for i in 1 to 10 generate
S(4*i-1 downto 4*(i-1))<=res(i-1);
end generate;
end rtl;


Далее идет описание компонента Adder_4bit
CODE
long_code_here =
library ieee;
USE IEEE.std_logic_1164.all;
USE IEEE.std_logic_unsigned.all;
USE IEEE.std_logic_arith.all;

entity Adder_4bit is
port (
clk : in std_logic;
rst : in std_logic;
A : in std_logic_vector (3 downto 0);
B : in std_logic_vector (3 downto 0);
cin : in std_logic;
S : out std_logic_vector (3 downto 0);
cout : out std_logic
);
end Adder_4bit;

architecture rtl of Adder_4bit is
signal sum : std_logic_vector (4 downto 0);
signal op1, op2 : std_logic_vector (3 downto 0);
begin
process (rst,clk)
begin
if (rst='1') then
sum<=(others=>'0');
elsif (clk'event and clk='1') then
sum <= ('0' & A)+('0' & B )+cin;
end if;
end process;
S <= sum(3 downto 0);
cout <= sum(4);
end rtl;


И компонента my_reg
CODE
long_code_here =
library ieee;
USE IEEE.std_logic_1164.all;
USE IEEE.std_logic_unsigned.all;

entity my_reg is
generic (
size : integer range 1 to 255:=1;
W : integer range 1 to 32:=4
);
port (
-- Системный интерфейс
clk : in std_logic;
rst : in std_logic;
wr : in std_logic;
D : in std_logic_vector (W-1 downto 0);
Q : out std_logic_vector (W-1 downto 0)
);
end my_reg;

architecture rtl of my_reg is
type reg_chain_type is array (size-1 downto 0) of std_logic_vector (W-1 downto 0);
signal reg_chain : reg_chain_type;
signal ena_chain : std_logic_vector (size-1 downto 0);
begin
process (clk, rst)
begin
if (rst='1') then
ena_chain<=(others=>'0');
elsif (clk'event and clk='1') then
ena_chain(0)<='1';
if (size>1) then
for i in 1 to size-1 loop
ena_chain(i)<=ena_chain(i-1);
end loop;
end if;
end if;
end process;

process (rst, clk)
begin
if (rst='1') then
reg_chain<=(others=>(others=>'0'));
elsif (clk'event and clk='1') then
if (ena_chain(0)='1') then
reg_chain(0)<=D;
end if;
if (size>1) then
for i in 1 to size-1 loop
if (ena_chain(i)='1') then
reg_chain(i)<=reg_chain(i-1);
end if;
end loop;
end if;
end if;
end process;
Q<=reg_chain(size-1);
end rtl;


Здесь, в таких тонкостях, еще хуже. … не понятно, как это функционирует все вместе…
Подскажите, поделитесь своими мыслями!)
Хочу разобраться для начала с этим, поскольку дальше еще используется перекодировка с таблицей синусов.
Заранее большое спасибо всем откликнувшимся!)

Krys
Предлагаю для начала ознакомиться с готовыми реализациями: xilinx dds compiler, начиная со стр. 11 там дана теория вопроса. Может, тогда станет понятнее, откуда взялся такой код.
Artemius_tv
Сумматор разбит на 10 маленьких для организации конвеера, чтобы повысить скорость работы. К алгоритму это все отношения не имеет. Поначалу можно заменить простым аккумулятором, а если не будет хватать скорости, уже думать об оптимизации.

А если надо разобраться именно с этим кодом, лучше его просимулировать.
mcaffee
В продолжении обсуждения вопроса про dds. С выхода накопительного сумматора берем 10 старших разрядов и с помощью таблицы синусов вычисляем значения амплитуды. Есть вот такой код:
CODE


library ieee;
USE IEEE.std_logic_1164.all;
USE IEEE.std_logic_unsigned.all;

entity Sin_func is
port(
clk : in std_logic;
rst : in std_logic;
Din : in std_logic_vector(9 downto 0); -- входные данные. это 10 страших разрядов с выхода накопительного сумматора.
Sout : out std_logic_vector (11 downto 0) -- выходные данные. Значения идут на цап. (амплитуда синуса)
);
end Sin_func;

architecture rtl of Sin_func is
type sin_type is array(31 downto 0) of std_logic_vector(11 downto 0);
signal Reg : sin_type;
signal In_reg : std_logic_vector (9 downto 0);
begin
table0: process(clk, rst)
begin
if (rst='1') then
Reg(0) <= (others=>'0');
elsif(clk'event and clk='1') then
case Din(4 downto 0) is
when "00000" => Reg(0) <= "011111111111";
when "00001" => Reg(0) <= "100000001011";
when "00010" => Reg(0) <= "100000011000";
when "00011" => Reg(0) <= "100000100100";
when "00100" => Reg(0) <= "100000110001";
when "00101" => Reg(0) <= "100000111101";
when "00110" => Reg(0) <= "100001001010";
when "00111" => Reg(0) <= "100001010110";
when "01000" => Reg(0) <= "100001100011";
when "01001" => Reg(0) <= "100001110000";
when "01010" => Reg(0) <= "100001111100";
when "01011" => Reg(0) <= "100010001001";
when "01100" => Reg(0) <= "100010010101";
when "01101" => Reg(0) <= "100010100010";
when "01110" => Reg(0) <= "100010101110";
when "01111" => Reg(0) <= "100010111011";
when "10000" => Reg(0) <= "100011000111";
when "10001" => Reg(0) <= "100011010100";
when "10010" => Reg(0) <= "100011100000";
when "10011" => Reg(0) <= "100011101101";
when "10100" => Reg(0) <= "100011111001";
when "10101" => Reg(0) <= "100100000110";
when "10110" => Reg(0) <= "100100010010";
when "10111" => Reg(0) <= "100100011111";
when "11000" => Reg(0) <= "100100101011";
when "11001" => Reg(0) <= "100100111000";
when "11010" => Reg(0) <= "100101000100";
when "11011" => Reg(0) <= "100101010000";
when "11100" => Reg(0) <= "100101011101";
when "11101" => Reg(0) <= "100101101001";
when "11110" => Reg(0) <= "100101110110";
when "11111" => Reg(0) <= "100110000010";
when others => Reg(0) <= "000000000000";
end case;
end if;
end process;
table1: process(clk, rst)
begin
if (rst='1') then
Reg(1) <= (others=>'0');
elsif(clk'event and clk='1') then
case Din(4 downto 0) is
when "00000" => Reg(1) <= "100110001110";
when "00001" => Reg(1) <= "100110011011";
when "00010" => Reg(1) <= "100110100111";
и так далее... всего 32 таких таблицы.

process(clk, rst)
begin
if (rst='1') then
In_reg <= (others=>'0');
elsif (clk'event and clk='1')then
In_reg <= Din;
end if;
end process;
Sout <= Reg (conv_integer(In_reg(9 downto 5)));
end architecture rtl;


Как видно, здесь значения аргумента принимают значения от 0 до 31. каждому значению фазы ставится в соответствие значение амплитуды синуса. Din это 10 старших разрядов с выхода сумматора. Непонятно почему берется только 5 младших разрядов от Din и им ставится в соответсвие значение амплитуды. и непонятно, что значит предпоследняя строчка, где задействованы 5 старших разрядов от входных данных..
Sout <= Reg (conv_integer(In_reg(9 downto 5)));
Подскажите, пожалуйста, поделитесь мыслями.
Заранее большое спасибо!!
Krys
Вобщем, здесь тоже разбито на 2 стадии. Вся таблица содержит 1024 ячейки, она логически разбита на 32 подтаблицы по 32 ячейки. Значение из подтаблицы выбирается на первом такте. А сама подтаблица выбирается на 2м такте. Разбивка на 5 битов адресации очень удобна: дело в том, что в Xilinx LUTы могут иметь 2 выхода и 5 входов, если входы одинаковые (что и есть в нашем случае). Таким образом хорошо экономятся ресурсы.
Ну вкратце так.
Если что неясно описал - спрашивайте, какое конкретно место непонятно в объяснениях.
mcaffee
Цитата(Krys @ Sep 21 2015, 15:00) *
Вобщем, здесь тоже разбито на 2 стадии. Вся таблица содержит 1024 ячейки, она логически разбита на 32 подтаблицы по 32 ячейки. Значение из подтаблицы выбирается на первом такте. А сама подтаблица выбирается на 2м такте. Разбивка на 5 битов адресации очень удобна: дело в том, что в Xilinx LUTы могут иметь 2 выхода и 5 входов, если входы одинаковые (что и есть в нашем случае). Таким образом хорошо экономятся ресурсы.
Ну вкратце так.
Если что неясно описал - спрашивайте, какое конкретно место непонятно в объяснениях.

Да))) теперь стало понятно! Большое спасибо!!
Но есть еще вопрос..
А если нужно взять не 10, а 12 старших разрядов, то тогда у нас уже будет 4096 ячеек во всей таблице и 64 подтаблицы с 64 ячейками, так?)
Но тут скорее всего не хватит ресурсов под такую таблицу( задумался над укорачиванием таблицы. Сделал расчет для четверти периода. Тогда получится 16 подтаблиц с 64 ячейками. Так?) получается во всей таблице записано 1024 значения, от нуля до макс. Но как сделать так, чтобы и на оставшиеся 3/4 периода была адресация и высчитывались значения, и чтобы в итоге получился целый синус?
Большое спасибо!)
Artemius_tv
Самый старший (12-й) бит будет отвечать за знак, а 11-й за инвертирование фазы. Получается 11-й бит будет использоваться до регистров Reg, поэтому, наверное, лучше 32 таблицы на 32 значения. Таблицу нужно будет пересчитать (наверное, просто сдвинуть вниз и отбросить старший бит).
Будет что-то вроде:

Код
.....

signal Din_1 : std_logic_vector (9 downto 0);

.....

Din_1 <= Din(9 downto 0) when (Din(10) = '0') else "1111111111" - Din(9 downto 0);

table0: process(clk, rst)
begin
    if (rst='1') then
        Reg(0) <= (others=>'0');
    elsif(clk'event and clk='1') then
        case Din_1(4 downto 0) is
            when "00000" => Reg(0) <= "000000000000";
            when "00001" => Reg(0) <= "000000001010";
.....

process(clk, rst)
begin
    if (rst='1') then
        In_reg <= (others=>'0');
    elsif (clk'event and clk='1')then
        In_reg(11 downto 10) <= Din;
        In_reg(9 downto 0) <= Din_1;
    end if;
end process;
    Sout <= "111111111111" + Reg (conv_integer(In_reg(9 downto 5))) when (In_reg(11) = '0'); else "111111111111" - Reg (conv_integer(In_reg(9 downto 5)));

Надеюсь, нигде не ошибся.
mcaffee
Цитата(Artemius_tv @ Sep 21 2015, 17:16) *
Самый старший (12-й) бит будет отвечать за знак, а 11-й за инвертирование фазы. Получается 11-й бит будет использоваться до регистров Reg, поэтому, наверное, лучше 32 таблицы на 32 значения. Таблицу нужно будет пересчитать (наверное, просто сдвинуть вниз и отбросить старший бит).
Будет что-то вроде:

Код
.....

signal Din_1 : std_logic_vector (9 downto 0);

.....

Din_1 <= Din(9 downto 0) when (Din(10) = '0') else "1111111111" - Din(9 downto 0);

table0: process(clk, rst)
begin
    if (rst='1') then
        Reg(0) <= (others=>'0');
    elsif(clk'event and clk='1') then
        case Din_1(4 downto 0) is
            when "00000" => Reg(0) <= "000000000000";
            when "00001" => Reg(0) <= "000000001010";
.....

process(clk, rst)
begin
    if (rst='1') then
        In_reg <= (others=>'0');
    elsif (clk'event and clk='1')then
        In_reg(11 downto 10) <= Din;
        In_reg(9 downto 0) <= Din_1;
    end if;
end process;
    Sout <= "111111111111" + Reg (conv_integer(In_reg(9 downto 5))) when (In_reg(11) = '0'); else "111111111111" - Reg (conv_integer(In_reg(9 downto 5)));

Надеюсь, нигде не ошибся.


Большое Вам спасибо!!
Подскажите, а вот тут все правильно?
In_reg(11 downto 10) <= Din;
In_reg(9 downto 0) <= Din_1;

Din же (11 downto 0). Наверное и In_reg тоже должен быть (11 downto 0). Или именно 11 downto 10?? И зачем мы в In_reg 2 раза перезаписываем тогда?

А если будет так, что в этой записи Sout <= "111111111111" + Reg (conv_integer(In_reg(9 downto 5))), у Reg (conv_integer(In_reg(9 downto 5)) будут одни нули, то тогда на все разряды цапа будут поданы "1". получается, что это -1, но как это воспримет цап? в десятичной системе амплитуда синуса принимает значения от -1 до 511 и дальше вниз до 0, до -511 и снова до 0. Как такие числа воспримет цап? Подскажите, запутался совсем...
Krys
А какой ЦАП? Надо на него доку читануть, из неё станет понятно.

Цитата(mcaffee @ Sep 21 2015, 18:54) *
А если нужно взять не 10, а 12 старших разрядов, то тогда у нас уже будет 4096 ячеек во всей таблице и 64 подтаблицы с 64 ячейками, так?)
Чото теперь уже я не понял: у Вас весь синус 12 битов аргумент имеет? Тогда таблица на 10 битов аргумента так и остаётся. Это четверть периода. А старшие 2 бита, как написали, указывают, как эту четверть развернуть, чтобы получить результат в нужном квадранте.
mcaffee
Цитата(Krys @ Sep 22 2015, 09:06) *
А какой ЦАП? Надо на него доку читануть, из неё станет понятно.

Чото теперь уже я не понял: у Вас весь синус 12 битов аргумент имеет? Тогда таблица на 10 битов аргумента так и остаётся. Это четверть периода. А старшие 2 бита, как написали, указывают, как эту четверть развернуть, чтобы получить результат в нужном квадранте.

ЦАП планируется применять вот такой: 1273 па4т ( он вообще 14 разрядный)
Да, согласен, в таблице у аргумента будет 10 разрядов и амплитуда там принимает значения от 0 до 1023. Всего 1024 значения и 32 на 32. Но вот как правильно описать как эти четверти развернуть, чтобы все правильно получилось?)
Artemius_tv
Цитата(mcaffee @ Sep 22 2015, 00:37) *
Подскажите, а вот тут все правильно?

Нет, конечно же я наошибался.

Надо
Код
In_reg(11 downto 10) <= Din(11 downto 10);
In_reg(9 downto 0) <= Din_1;

На следующий такт передаем старшие 2 бита в неизменном виде, а младшие 10 бит уже перевернутые или нет в соответствии с Din(10). Хотя Reg(4 downto 0) и Reg(10) все равно не используются.

И с
Код
Sout <= "111111111111" + Reg (conv_integer(In_reg(9 downto 5))) when (In_reg(11) = '0'); else "111111111111" - Reg (conv_integer(In_reg(9 downto 5)));

тоже ошибся, надо "011111111111" или "100000000000"

Кстати, насколько я помню, количество используемых разрядов фазы должно быть больше разрядности ЦАП. В AD9834 ЦАП 10-битный и используют 12 бит фазы, AD9854 ЦАП 12-бит и 14 бит фазы. Т.е. для 14-разрядного ЦАП'а 12 бит фазы это мало.
Krys
А ПЛИС какая, что ресурсов на таблицу не хватит? Бывает ещё на блочной памяти делают таблицу, а не на LUT.
Golikov A.
Цитата
Кстати, насколько я помню, количество используемых разрядов фазы должно быть больше разрядности ЦАП.


Это для лучшей характеристики по сигнал/шум ДДС, там дополнительные разряды добавляются и четность фазы ломается и другие хитрости делаются, это уже следующий этап для ТС. Это все до таблицы должно быть, битность таблицы должна быть как у ЦАПа со всеми расширениями, потому что иначе это хранение лишней информации.
Krys
Тем более, что это всё в теории описано в том документе от Xilinx, ссылку на который я давал в начале
Krys
Цитата(mcaffee @ Sep 22 2015, 04:37) *
А если будет так, что в этой записи Sout <= "111111111111" + Reg (conv_integer(In_reg(9 downto 5))), у Reg (conv_integer(In_reg(9 downto 5)) будут одни нули, то тогда на все разряды цапа будут поданы "1". получается, что это -1, но как это воспримет цап? в десятичной системе амплитуда синуса принимает значения от -1 до 511 и дальше вниз до 0, до -511 и снова до 0. Как такие числа воспримет цап? Подскажите, запутался совсем...


Цитата(mcaffee @ Sep 22 2015, 12:38) *
ЦАП планируется применять вот такой: 1273 па4т ( он вообще 14 разрядный)


Доку то на цап читали? Там поди есть ответы )))


Цитата(mcaffee @ Sep 22 2015, 12:38) *
Но вот как правильно описать как эти четверти развернуть, чтобы все правильно получилось?)
Вы нарисуйте период синуса на бумажке. Увидите там 4 повторяющиеся части )))
Но правильнее увидеть сначала 2 крупные повторяющиеся части (полупериоды), а в каждой из частей ещё 2 мелкие повторяющиеся части (четверти).
Закономерности:
1. Полупериоды просто имеют противоположный знак. За полупериоды отвечает старший бит аргумента (поскольку это самые крупные повторяющиеся части).
2. Четвертинки имеют обратную зависимость от аргумента: т.е. если аргумент таблицы в первой четвертинке меняется от 0 до 1023, то во второй четвертинке он должен меняться от 1023 до 0. За четвертинки отвечает следующий от старшего бит аргумента.
Krys
ТС, Вы куда исчезаете так надолго? Потом приходится людей опять в тему созывать через приват...
Corner
Для ускорения аккумулятора больше 4 счетчиков избыточно-частота перестает расти. Для реализации синуса можно использовать кусочно-линейную аппроксимацию. Хватает двух таблиц на 256 точек - основная и поправочная. Точность выходить в районе 16 бит. Что лучше таблицы в 4096 точек - дает только 12 бит.
Стандартный DDS это аккумулятор+сумматор фазы+табличка.
Krys
Не очень понятно, про какие 4 счётчика идёт речь?
Krys
И как пользоваться поправочной табличкой? Может, дадите ссылку, что читануть по этому поводу?
И вот Вы пишете:
"Стандартный DDS это аккумулятор+сумматор фазы+табличка."
непонятно: чем аккумулятор отличается от сумматора фазы?
Golikov A.
ну как так?
аккумулятор - аккумулирует
а сумматор - суммирует...


потому что там w*t+fi
сумматор складывает фазу fi и набегующую от времени w*t. А она в свою очередь реализуется аккумулятором, складывающим по тактам...
Krys
иногда просто под аккумулятором понимается сумматор с регистром хранения предыдущих данных. Без суммирования не накопить.
А здесь похоже под аккумулятором понимается просто регистр. Зачем тогда путать людей и использовать один термин вместо другого...
Начальную фазу fi можно затолкать сразу в регистр аккумулятора как начальное значение. Тогда её не придётся постоянно прибавлять к текущей фазе wt
Golikov A.
аккумулятор - это хранение и сумма
сумматор - это сумма просто

фазу нельзя сразу затолкать, вы не сможете ее тогда на лету менять.... Если вы делаете фазовую подстройку, к примеру...
Krys
Ааа, тогда вопросов нет, спасибо.

Точнее остаются вопросы к ув. Corner, как пользоваться 2 табличками на 256 и что за 4 счётчика
Artemius_tv
Видимо эти 2 таблицы это таблица значений и таблица коэфициентов для линейной поправки.
Если берем 16 бит фазы phase[15:0], то старшие 8 бит будут использоваться для выбора значений из таблиц, а младшие 8 бит для линейной поправки.
Если таблица A - значения, а B - коэфициенты, то что-то типа:
A[phase[15:8]] + B[phase[15:8]]*phase[7:0]/256
Krys
Хорошо, допустим с таблицами понятно.
Хотя тоже не очень. Если Вы говорите про линейную поправку, подразумевая кусочно-линейную сплайн-интерполяцию, то почему бы её без таблицы не сделать примерно такой записью:
A[phase[15:8]] + (A[phase[15:8 + 1'b1]] - A[phase[15:8]]) * phase[7:0] / 256
?
Т.е. берутся 2 соседние точки таблицы, между ними проводится прямая, конкретная точка на прямой определяется младшими разрядами фазы.



Остаётся тогда вопрос: что за 4 счётчика?
Artemius_tv
Ну, я это вариант не предлагал, а только написал свое объяснение (возможно, неточное или даже неверное laughing.gif ).

Кстати, в варианте:
A[phase[15:8]] + (A[phase[15:8] + 1'b1] - A[phase[15:8]]) * phase[7:0] / 256
будет два обращения к памяти по адресу phase[15:8] и phase[15:8] + 1'b1, соответственно, нужно либо 2 такта, либо опять две таблицы.

4 счетчика, видимо (я опять только предполагаю), относится к самому началу темы, когда ТС спрашивал, зачем делить один счетчик на 10 маленьких.
Krys
Цитата(Artemius_tv @ Oct 12 2015, 15:25) *
соответственно, нужно либо 2 такта, либо опять две таблицы.
Либо двухпортовую память ))
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.