Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: "Схемотехнические трюки для ПЛИСоводов"
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Работаем с ПЛИС, области применения, выбор
Страницы: 1, 2, 3, 4, 5
TRILLER
Цитата(ISK @ Oct 2 2012, 13:32) *
Была у меня похожая задача, на 100 МГц на циклоне 3 схема работала.

ISK, не могли бы подробнее рассказать что к чему? Уже несколько часов потратил, никак не разберусь - толи устал, толи способностей не хватает(
Переменной point_cnt вы что задаёте? Также меня интересует начальное состояние cmp_res... В зависимости от него в строке cmp_res_reg(15) независимо от add_sub_res будет либо X"8000" , либо X"FFFF"... Ничего не понимаю, объясните пожалуйста подробнее.
TRILLER
Вобщем, с кодом ISKа разобраться так и не смог, сделал свой вариант.
Если входной массив разбить по 4 слова, и использовать не сравнение значений, а разность(т.е. логику ускоренного переноса), то получается довольно неплохо.
CODE
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

library UNISIM;
use UNISIM.VComponents.all;

entity Sort_Four is
generic(
wigth : natural := 16);
Port (
clk : in std_logic;
in_word : in std_logic_vector((4*wigth - 1) downto 0);
----
out_index : out std_logic_vector(1 downto 0);
out_data : out std_logic_vector((wigth - 1) downto 0));
end Sort_Four;

architecture Behavioral of Sort_Four is

-----------------------------------------------------------
-----------------------------------------------------------
attribute syn_hier : string;
attribute syn_hier of Behavioral : architecture is "hard";

attribute syn_keep : boolean;
-----------------------------------------------------------
-----------------------------------------------------------
type word_type is array (0 to 3) of std_logic_vector((wigth - 1) downto 0);
signal word, word_1d : word_type;

signal sub_0f : std_logic_vector(1 downto 0);
signal word_mux_st, word_mux_ml : std_logic_vector((wigth - 1) downto 0);
signal sub_1f : std_logic;
signal sub_general : std_logic_vector(2 downto 0);

-- signal data_1d, data : std_logic_vector((order - 1) downto 0);

-----------------------------------------------------------
-----------------------------------------------------------

begin


-----------------------------------------------------------
-----------------------------------------------------------
process (clk)
begin
if (clk = '1' and clk'event) then
for i in 0 to 3 loop
word_1d(i) <= in_word(((i + 1)*wigth - 1) downto i*wigth);
word(i) <= word_1d(i);
end loop;
end if;
end process;

-----------------------------------------------------------
-----------------------------------------------------------
process (word)
variable sub_w32_temp, sub_w10_temp : std_logic_vector((wigth - 1) downto 0);
begin
sub_w32_temp := (word(3) + (not word(2)) + 1);
sub_w10_temp := (word(1) + (not word(0)) + 1);
sub_0f <= (sub_w32_temp(wigth - 1)&sub_w10_temp(wigth - 1));
end process;

word_mux_st <= word(3) when sub_0f(1) = '1' else word(2);
word_mux_ml <= (not word(1)) when sub_0f(0) = '1' else (not word(0));

-----------------------------------------------------------
-----------------------------------------------------------
process (word_mux_st, word_mux_ml)
variable word_mux_temp : std_logic_vector((wigth - 1) downto 0);
begin
word_mux_temp := (word_mux_st + word_mux_ml + 1);
sub_1f <= word_mux_temp(wigth - 1);
end process;


-----------------------------------------------------------
-----------------------------------------------------------
-- process (clk)
-- begin
-- if (clk = '1' and clk'event) then
-- if sub_1f = '1' then
-- if sub_0f(1) = '1' then
-- data_1d <= word(3);
-- else
-- data_1d <= word(2);
-- end if;
-- else
-- if sub_0f(0) = '1' then
-- data_1d <= word(1);
-- else
-- data_1d <= word(0);
-- end if;
-- end if;
-- data <= data_1d;
-- end if;
-- end process;

-- out_data <= data;
-----------------------------------------------------------
-----------------------------------------------------------
sub_general <= (sub_1f&sub_0f);
process (sub_general, word)
begin
case sub_general is
when "000" => out_data <= word(0);
out_index <= "00";
when "001" => out_data <= word(0);
out_index <= "00";
when "010" => out_data <= word(1);
out_index <= "01";
when "011" => out_data <= word(1);
out_index <= "01";
when "100" => out_data <= word(2);
out_index <= "10";
when "101" => out_data <= word(2);
out_index <= "10";
when "110" => out_data <= word(3);
out_index <= "11";
when "111" => out_data <= word(3);
out_index <= "11";
when others => null;
end case;
end process;


end Behavioral;

При входном массиве 16x16 получается 8 слоёв логики(не считая carry-chain), и для V6-1 схема разводится на 185МГц...
Жалко только, что мне надо 300 - буду ставить конвеер(
Corner
Быстрый счетчик реализуется на нескольких сумматорах с зарегистренным переносом. Сложение по факту выполняется за несколько тактов.
Я использовал 4 сумматора +1, +2, +3 и +4 от общего регистра, который обновляется раз в 4 такта от сумматора +4, а результаты сложений перебираются каждый такт мультиплексором. В результате получаешь счетчик работающий не в реальном времени, но собирающийся на частоте +30... 40 % к однопроходному.
riza
всем привет! учусь на втором курсе.задали сделать семестровую работу по теме: "Моделирование САУ Адаптивного робота:Проектирование в виде системы на кристалле или интегральная схема - на основе метода ПЛИС, используя VHDL..но я не знаю как(VHDL не проходили даже)...помогите пожалуйста!
iosifk
Цитата(riza @ May 11 2014, 17:53) *
всем привет! учусь на втором курсе.задали сделать семестровую работу по теме: "Моделирование САУ Адаптивного робота:Проектирование в виде системы на кристалле или интегральная схема - на основе метода ПЛИС, используя VHDL..но я не знаю как(VHDL не проходили даже)...помогите пожалуйста!

1. Написать алгоритм работы робота
2. По нему сделать блок-схему алгоритма
3. По ней сделать блок-схему вычислителя.
4. Проверить ее в маткадах-иатлабах
5. Вычислитель реализовать в ПЛИС...

Вот собственно и все... Первые 4 пункта можно делать, ничего не зная о ПЛИС... Или мало зная о ПЛИС.
Потом задать преподу вопрос: почему заставляют делать то, чему не учили...

Мы готовы ответить на конкретный вопрос, но не делать за Вас всю работу. Нам это не интересно, это Ваша проблема, а не наша...

Да и тему надо перенести в другой раздел.... Либо для "начинающих", либо "предлагаю работу"....
riza
спасибо большое!
olgapet
2 вопроса:
а) чем содержательно должен отличаться подразумеваемый раздел от 4-ого раздела по ПЛИС "Системы на ПЛИС - System on Programmable Chip" (он открывался для обсуждения корок)? большей мелкозернистостью обсуждаемых модулей?
б) по какому принципу будет организовываться структура раздела? надеюсь каталогизация подразумевается?
axalay
может кому будет нужно
`timescale 1ns / 1ps
//
module divider #
(
parameter WIDTH_DIVIDENT = 27,
parameter WIDTH_DIVIDER = 9
)
(
// global
input Clock,
// input
input DivIn,
input [WIDTH_DIVIDENT-1 : 0] Divident,
input [WIDTH_DIVIDER-1 : 0] Divider,
// output
output wire DivOut,
output wire [WIDTH_DIVIDENT-1 : 0] Result,
output wire [WIDTH_DIVIDER-1 : 0] Fractional,
// debug
output wire Test
);


//
wire [0 : 0] Comp [WIDTH_DIVIDENT-1 : 0];
wire [WIDTH_DIVIDER-1 : 0] CarryTemp [WIDTH_DIVIDENT-1 : 0];



// sift registers
reg [0 : 0] DivTemp [WIDTH_DIVIDENT-1 : 0];
reg [WIDTH_DIVIDENT-1 : 0] ResultTemp [WIDTH_DIVIDENT-1 : 0];
reg [WIDTH_DIVIDENT+WIDTH_DIVIDER-1 : 0] DividentTemp [WIDTH_DIVIDENT-1 : 0];
reg [WIDTH_DIVIDER-1 : 0] DividerTemp [WIDTH_DIVIDENT-1 : 0];

// first
always @(posedge Clock) begin
DivTemp[0][0 : 0] = DivIn;
ResultTemp[0][WIDTH_DIVIDENT-1 : 0] = (Comp[0]) ? 8'd1 : 8'd0;
DividentTemp[0][WIDTH_DIVIDENT+WIDTH_DIVIDER-2 : 0] = {{(WIDTH_DIVIDER-1){1'b0}}, Divident[WIDTH_DIVIDENT-1 : 0]};
DividerTemp[0][WIDTH_DIVIDER-1 : 0] = Divider[WIDTH_DIVIDER-1 : 0];
end //always

// next all
genvar i;
generate
for (i=1; i<WIDTH_DIVIDENT; i=i+1)
begin: shift
always @(posedge Clock) begin
DivTemp[i][0 : 0] = DivTemp[i-1][0 : 0];
ResultTemp[i][WIDTH_DIVIDENT-1 : 0] = {(ResultTemp[i-1][WIDTH_DIVIDENT-2 : 0]), ((Comp[i]) ? 1'b1 : 1'b0)};
DividentTemp[i][WIDTH_DIVIDENT+WIDTH_DIVIDER-2 : 0] = {CarryTemp[i-1][WIDTH_DIVIDER-2 : 0], DividentTemp[i-1][WIDTH_DIVIDENT-2 : 0], 1'b0};
DividerTemp[i][WIDTH_DIVIDER-1 : 0] = DividerTemp[i-1][WIDTH_DIVIDER-1 : 0];
end //always
end
endgenerate


// calculate
genvar j;
generate
for (j=0; j<WIDTH_DIVIDENT; j=j+1)
begin: calc
assign Comp[j][0 : 0] = (DividentTemp[j][WIDTH_DIVIDENT+WIDTH_DIVIDER-2 :WIDTH_DIVIDENT-1] >= DividerTemp[j][WIDTH_DIVIDER-1 : 0]) ? 1'b1 : 1'b0;
assign CarryTemp[j][WIDTH_DIVIDER-1 : 0] = (Comp[j]) ? (DividentTemp[j][WIDTH_DIVIDENT+WIDTH_DIVIDER-2 :WIDTH_DIVIDENT-1] - DividerTemp[j][WIDTH_DIVIDER-1 : 0]) : DividentTemp[j][WIDTH_DIVIDENT+WIDTH_DIVIDER-2 :WIDTH_DIVIDENT-1];
end
endgenerate


// fractional
reg [WIDTH_DIVIDER-1 : 0] FractionalReg;
always @(posedge Clock)
FractionalReg[WIDTH_DIVIDER-1 : 0] = CarryTemp[WIDTH_DIVIDENT-1];


// result valid
reg DivReg;
always @(posedge Clock)
DivReg = DivTemp[WIDTH_DIVIDENT-1][0 : 0];


// output
assign Result[WIDTH_DIVIDENT-1 : 0] = ResultTemp[WIDTH_DIVIDENT-1];
assign Fractional[WIDTH_DIVIDER-1 : 0] = FractionalReg[WIDTH_DIVIDER-1 : 0];
assign DivOut = DivReg;


// debug
assign Test = 1'b0;


endmodule //divider
serjj
Цитата
может кому будет нужно
...

Оформите в виде исходного файла + простенького тб в архиве
блоки
Код
// next all
...

// calculate
...

имхо лучше сделать как обычные циклы под always@(posedge Clock) и always@(*) соответственно
ну и если вруг не лениво, то небольшую справку по результатам имплементации на любой понравившейся ПЛИС, чтобы человек, если вдруг заинтересуется, имел представление во что это ему может вылиться.
axalay
вот другая реализация. Это простые делители - первый итерационный, а этот нет

// Fast Array Divider 8 bit to 4 bit
module fast_array_divider (a_in, d_in, q_out, r_out);

input [7:0] a_in;
input [3:0] d_in;
output [7:0] q_out; wire [7:0] q_out;
output [3:0] r_out; wire [3:0] r_out;

wire [12:1] a;
wire [4:1] d;
wire [8:1] q;
wire [8:5] r;


wire c_0_3, c_0_2, c_0_1, c_0_0;
wire s_0_3, s_0_2, s_0_1, s_0_0;
wire q1_inv;
restoring_array_divider array_0_0 ( a[2], d[1], c_0_1, q1_inv, c_0_0, s_0_0);
restoring_array_divider array_0_1 ( a[3], d[2], c_0_2, q1_inv, c_0_1, s_0_1);
restoring_array_divider array_0_2 ( a[4], d[3], c_0_3, q1_inv, c_0_2, s_0_2);
restoring_array_divider array_0_3 ( a[5], d[4], 1'b0, q1_inv, c_0_3, s_0_3);
assign q[1] = ( a[1] | !c_0_0 );
assign q1_inv=!q[1];

wire c_1_3, c_1_2, c_1_1, c_1_0;
wire s_1_3, s_1_2, s_1_1, s_1_0;
wire q2_inv;
restoring_array_divider array_1_0 ( s_0_1, d[1], c_1_1, q2_inv, c_1_0, s_1_0);
restoring_array_divider array_1_1 ( s_0_2, d[2], c_1_2, q2_inv, c_1_1, s_1_1);
restoring_array_divider array_1_2 ( s_0_3, d[3], c_1_3, q2_inv, c_1_2, s_1_2);
restoring_array_divider array_1_3 ( a[6], d[4], 1'b0, q2_inv, c_1_3, s_1_3);
assign q[2] = ( s_0_0 | !c_1_0 );
assign q2_inv=!q[2];

wire c_2_3, c_2_2, c_2_1, c_2_0;
wire s_2_3, s_2_2, s_2_1, s_2_0;
wire q3_inv;
restoring_array_divider array_2_0 ( s_1_1, d[1], c_2_1, q3_inv, c_2_0, s_2_0);
restoring_array_divider array_2_1 ( s_1_2, d[2], c_2_2, q3_inv, c_2_1, s_2_1);
restoring_array_divider array_2_2 ( s_1_3, d[3], c_2_3, q3_inv, c_2_2, s_2_2);
restoring_array_divider array_2_3 ( a[7], d[4], 1'b0, q3_inv, c_2_3, s_2_3);
assign q[3] = ( s_1_0 | !c_2_0 );
assign q3_inv=!q[3];

wire c_3_3, c_3_2, c_3_1, c_3_0;
wire s_3_3, s_3_2, s_3_1, s_3_0;
wire q4_inv;
restoring_array_divider array_3_0 ( s_2_1, d[1], c_3_1, q4_inv, c_3_0, s_3_0);
restoring_array_divider array_3_1 ( s_2_2, d[2], c_3_2, q4_inv, c_3_1, s_3_1);
restoring_array_divider array_3_2 ( s_2_3, d[3], c_3_3, q4_inv, c_3_2, s_3_2);
restoring_array_divider array_3_3 ( a[8], d[4], 1'b0, q4_inv, c_3_3, s_3_3);
assign q[4] = ( s_2_0 | !c_3_0 );
assign q4_inv=!q[4];

wire c_4_3, c_4_2, c_4_1, c_4_0;
wire s_4_3, s_4_2, s_4_1, s_4_0;
wire q5_inv;
restoring_array_divider array_4_0 ( s_3_1, d[1], c_4_1, q5_inv, c_4_0, s_4_0);
restoring_array_divider array_4_1 ( s_3_2, d[2], c_4_2, q5_inv, c_4_1, s_4_1);
restoring_array_divider array_4_2 ( s_3_3, d[3], c_4_3, q5_inv, c_4_2, s_4_2);
restoring_array_divider array_4_3 ( a[9], d[4], 1'b0, q5_inv, c_4_3, s_4_3);
assign q[5] = ( s_3_0 | !c_4_0 );
assign q5_inv=!q[5];

wire c_5_3, c_5_2, c_5_1, c_5_0;
wire s_5_3, s_5_2, s_5_1, s_5_0;
wire q6_inv;
restoring_array_divider array_5_0 ( s_4_1, d[1], c_5_1, q6_inv, c_5_0, s_5_0);
restoring_array_divider array_5_1 ( s_4_2, d[2], c_5_2, q6_inv, c_5_1, s_5_1);
restoring_array_divider array_5_2 ( s_4_3, d[3], c_5_3, q6_inv, c_5_2, s_5_2);
restoring_array_divider array_5_3 ( a[10], d[4], 1'b0, q6_inv, c_5_3, s_5_3);
assign q[6] = ( s_4_0 | !c_5_0 );
assign q6_inv=!q[6];

wire c_6_3, c_6_2, c_6_1, c_6_0;
wire s_6_3, s_6_2, s_6_1, s_6_0;
wire q7_inv;
restoring_array_divider array_6_0 ( s_5_1, d[1], c_6_1, q7_inv, c_6_0, s_6_0);
restoring_array_divider array_6_1 ( s_5_2, d[2], c_6_2, q7_inv, c_6_1, s_6_1);
restoring_array_divider array_6_2 ( s_5_3, d[3], c_6_3, q7_inv, c_6_2, s_6_2);
restoring_array_divider array_6_3 ( a[11], d[4], 1'b0, q7_inv, c_6_3, s_6_3);
assign q[7] = ( s_5_0 | !c_6_0 );
assign q7_inv=!q[7];

wire c_7_3, c_7_2, c_7_1, c_7_0;
wire s_7_3, s_7_2, s_7_1, s_7_0;
wire q8_inv;
restoring_array_divider array_7_0 ( s_6_1, d[1], c_7_1, q8_inv, c_7_0, s_7_0);
restoring_array_divider array_7_1 ( s_6_2, d[2], c_7_2, q8_inv, c_7_1, s_7_1);
restoring_array_divider array_7_2 ( s_6_3, d[3], c_7_3, q8_inv, c_7_2, s_7_2);
restoring_array_divider array_7_3 ( a[12], d[4], 1'b0, q8_inv, c_7_3, s_7_3);
assign q[8] = ( s_6_0 | !c_7_0 );
assign q8_inv=!q[8];


assign r[8:5]={s_7_3, s_7_2, s_7_1, s_7_0};

assign a[12:1]={a_in[0], a_in[1], a_in[2], a_in[3], a_in[4], a_in[5], a_in[6], a_in[7], 4'b0000};
assign d[4:1]={d_in[0], d_in[1], d_in[2], d_in[3]};
assign q_out[7:0]={q[1] ,q[2], q[3], q[4], q[5], q[6], q[7], q[8]};
assign r_out[3:0]={r[5], r[6], r[7], r[8]};

endmodule //fast_array_divider
_Anatoliy
Ну что,продолжим?
Параметризуемый полифазный фильтр КИХ. Количество фаз - 2.
Реализация - вторая прямая форма,порядок чётный,ИХ симметричная.
Maverick
Решил поделиться алгоритмом:
Например, вам надо понять, является ли введенное число степенью двойки или нет. Для этого вы можете просто делить это число на 2, пока не получите двойку на последнем шаге. Этот алгоритм будет работать, он выдаст результат, но на больших числах будет тратить много лишнего времени. А можете написать простое выражение в одну строку: x & (x-1) == 0. Тут используется немного битовой магии, но результат будет тем же, но за гораздо меньшее время.

PS Надеюсь будет кому-то полезным...
andrew_b
Цитата(Maverick @ Jan 29 2016, 15:13) *
А можете написать простое выражение в одну строку: x & (x-1) == 0. Тут используется немного битовой магии, но результат будет тем же, но за гораздо меньшее время.
Вы не читали "Трюки для программистов"?
Timmy
Цитата(Maverick @ Jan 29 2016, 14:13) *
Решил поделиться алгоритмом:
Например, вам надо понять, является ли введенное число степенью двойки или нет. Для этого вы можете просто делить это число на 2, пока не получите двойку на последнем шаге. Этот алгоритм будет работать, он выдаст результат, но на больших числах будет тратить много лишнего времени. А можете написать простое выражение в одну строку: x & (x-1) == 0. Тут используется немного битовой магии, но результат будет тем же, но за гораздо меньшее время.

Здесь надо не делить на 2, а подсчитать количество единичных битов, и сравнить его с единицей. Поскольку конкретное значение количества единичных битов вычислять не требуется, стандартная архитектура решения этой задачи упрощается. Например, для 15 бит на Ксайлинксах потребуется три пары LUT5 на нижнем слое логики, и один LUT6 на следующем, как раз один слайс. Нижний слой выдаёт по трём группам из пяти бит значения "нет единиц", "одна единица", "больше одной единицы", а верхний LUT6 выносит окончательный вердикт.
А через вычитание потребуются 4 слайса только для вычитания и потом ещё 2 на логику.
ViKo
Если число делится на 2, у него только одна единица среди битов. 4-входовая LUT выдает 1 для чисел 0001, 0010, 0100, 1000. Следующая такая же LUT принимает 4 выхода с предыдущего слоя, и т.д. Красота.
Timmy
Цитата(ViKo @ Mar 2 2016, 15:22) *
Если число делится на 2, у него только одна единица среди битов. 4-входовая LUT выдает 1 для чисел 0001, 0010, 0100, 1000. Следующая такая же LUT принимает 4 выхода с предыдущего слоя, и т.д. Красота.

Так просто не выйдет, если, например, взять числа 1000 0000 и 1000 0110, на выходе нижнего слоя различий не будет, получим ложное срабатывание. Поэтому требуется два бита с каждой группы нижнего слоя. И формулировка задачи не "число делится на два", а "число является степенью двойки":).
ViKo
Цитата(Timmy @ Mar 2 2016, 15:39) *
Так просто не выйдет, если, например, взять числа 1000 0000 и 1000 0110, на выходе нижнего слоя различий не будет, получим ложное срабатывание. Поэтому требуется два бита с каждой группы нижнего слоя. И формулировка задачи не "число делится на два", а "число является степенью двойки":).

Да, степенью двойки, конечно. Я неправильно выразился.
Да, не получается. crying.gif
Flip-fl0p
Дайте подсказку как лучше организовать отсчёт нужного количества тактов.
Пример: есть автомат который должен находиться в одном из состояний определённое количество тактов, например: Из состояния S3 автомат переходит в состояние S4, там находится 2 такта, а на третий такт переходит в следующее состояние S5. Как это правильнее реализовать. На счетчике, или на сдвиговом регистре ?
Maverick
Цитата(Flip-fl0p @ Sep 29 2016, 10:39) *
Дайте подсказку как лучше организовать отсчёт нужного количества тактов.
Пример: есть автомат который должен находиться в одном из состояний определённое количество тактов, например: Из состояния S3 автомат переходит в состояние S4, там находится 2 такта, а на третий такт переходит в следующее состояние S5. Как это правильнее реализовать. На счетчике, или на сдвиговом регистре ?

можно на каждом состоянии загружать декрементирующий счетчик начальным значением и считать до нуля

reg_cnt <= reg_cnt - std_logic_vector(to_unsigned(1, reg_cnt 'length)));

В комбинационной логике переходов состояний автомата отслеживать значение нуля счетчика и переходить на новое состояние

upd
процесс счетчика (отдельный) примерно такой:

Код
Process (all)
begin
if rst = '1' then
reg_cnt <= (OTHERS => '0');  
elsif clk='1' and clk'event then
if reg_cnt =  std_logic_vector(to_unsigned(0, cnt 'length))) then
   reg_cnt <= load_cnt;  
  else
   reg_cnt <= reg_cnt -  std_logic_vector(to_unsigned(1, cnt 'length)));
end if;
end if;
end process;


в состояниях автомата присваиваете load_cnt необходимое значение и счетчик начинает отсчет... Как-то так
Мур
http://www.iconfs.net/en/w.infocom2016/met...osuvannyam-vhdl

Методика проектування цифрових фільтрів з застосуванням VHDL
Мур
Понравилась идея борьбы с дребезгом на дискретных входах на ресурсе http://embedders.org/blog/gdi/debouncing.html

Попробовал для усвоения и показалось интересным. Оказалось задержка не 3 тика, а 4. Даю вместе с бенчем.
Flip-fl0p
Цитата(Мур @ Sep 15 2017, 11:06) *
Понравилась идея борьбы с дребезгом на дискретных входах на ресурсе http://embedders.org/blog/gdi/debouncing.html

Попробовал для усвоения и показалось интересным. Оказалось задержка не 3 тика, а 4. Даю вместе с бенчем.

Какая практическая польза от применения такого антидребезга в FPGA ?
Мур
Цитата(Flip-fl0p @ Sep 15 2017, 11:26) *
Какая практическая польза от применения такого антидребезга в FPGA ?

Прямая...
При сборе информации с дискретных входов я обычно делал "в лоб". Это усложняло архитектуру(хранение счетчиков(предистория) по числу входных разрядов). Тут на порядок проще и экономит ресурс.

Странный вопрос
iosifk
Цитата(Мур @ Sep 15 2017, 11:58) *
Прямая...
При сборе информации с дискретных входов я обычно делал "в лоб". Это усложняло архитектуру(хранение счетчиков(предистория) по числу входных разрядов). Тут на порядок проще и экономит ресурс.

Странный вопрос

На самом деле немного не так...
Если речь идет о большом количестве дискретных входов, то многоканальный таймер и немного памяти будут предпочтительней.
А вот если речь идет о нескольких входных сигналах, которые идут на последователностную логику и в том числе на FSM, то тогда действительно лучше иметь отдельные блоки подавления дребезга...
Мур
Цитата(iosifk @ Sep 15 2017, 12:03) *
На самом деле немного не так...
Если речь идет о большом количестве дискретных входов, то многоканальный таймер и немного памяти будут предпочтительней.
А вот если речь идет о нескольких входных сигналах, которые идут на последователностную логику и в том числе на FSM, то тогда действительно лучше иметь отдельные блоки подавления дребезга...

Так это как раз мой случай. Я ведь принимаю непрерывно про SPI больше сотни входов. Это порождает громоздкие постороения на счетчиках для дальнейшего учета в память.(это уже доступно внешнему процессору) Тут решается красивее.

Тут только фильтр. Простой и понятный. Для расширение разрядности счетчиков нужно добавлять дополнительные регистры. 2**N
nice_vladi
Цитата(Maverick @ Sep 29 2016, 07:58) *
можно на каждом состоянии загружать декрементирующий счетчик начальным значением и считать до нуля

reg_cnt <= reg_cnt - std_logic_vector(to_unsigned(1, reg_cnt 'length)));

В комбинационной логике переходов состояний автомата отслеживать значение нуля счетчика и переходить на новое состояние

upd
процесс счетчика (отдельный) примерно такой:

Код
....


в состояниях автомата присваиваете load_cnt необходимое значение и счетчик начинает отсчет... Как-то так


Добрый день! Интересно, почему часто применяется отсчет счетчиком до нуля и загрузка в него каких-то начальных значений. Почему просто не обнулять счетчик и не считать до требуемой величины? Например:

Код
reg [7:0] cnt;

always@(posedge iclk) begin
  if (rst)               cnt <= '0;
  else if (~&cnt)    cnt <= cnt + 1'b1;
end

always_comb begin
  strb0 = cnt == 10;
  strb1 = cnt == 12;
  ...
end


Стробы strb0,1... можно использовать для переход МКС. Rst = strb0||strb1... ну или в состояниях МКС описать.

На мой взгляд, гораздо логичнее выглядит. Отсчет от Н до нуля - это вопрос предпочтений? Или есть какое-то обоснование, почему так лучше?
ViKo
Потому, что сравнивать с нулем гораздо проще и быстрее, чем сравнивать с неким числом.
Flip-fl0p
Цитата(ViKo @ Nov 13 2017, 10:20) *
Потому, что сравнивать с нулем гораздо проще и быстрее, чем сравнивать с неким числом.

А какая разница ? Компаратору без разницы, что сравнивать, схема у него одинаковая, что для сравнения нулей или других чисел.

Другой вопрос, если нам надо например одним счетчиком отсчитывать разные интервалы. Например:
Счетчик досчитал до 10 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 15 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 18 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 24 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 32 - что-то делаем, и сбрасываем счетчик.
В этом случае у нас будет несколько схем сравнения с 10, 15, 18, 24, 32 - что собственно совсем не хорошо.
В случае если счетчик считает до 0 у нас будет только 1 схема сравнения с нулем.
one_eight_seven
Сравнение с нулём - это простое ИЛИ всех битов счётчика.

Сравнение с каким-то числом - это сначала XOR с заданным значением, после чего уже ИЛИ всех битов результата.
Flip-fl0p
Цитата(one_eight_seven @ Nov 13 2017, 11:09) *
Сравнение с нулём - это простое ИЛИ всех битов счётчика.

Сравнение с каким-то числом - это сначала XOR с заданным значением, после чего уже ИЛИ всех битов результата.

Но в ПЛИС то это реализуется на LUT (Look-Up Table) и на них без разницы как сравнивать, нули или конкретное значение.
Просто сказанное ниже, я имел ввиду применительно к ПЛИС:
Цитата
А какая разница ? Компаратору без разницы, что сравнивать, схема у него одинаковая, что для сравнения нулей или других чисел.
nice_vladi
Цитата(Flip-fl0p @ Nov 13 2017, 07:34) *
А какая разница ? Компаратору без разницы, что сравнивать, схема у него одинаковая, что для сравнения нулей или других чисел.

Другой вопрос, если нам надо например одним счетчиком отсчитывать разные интервалы. Например:
Счетчик досчитал до 10 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 15 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 18 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 24 - что-то делаем, и сбрасываем счетчик.
Счетчик досчитал до 32 - что-то делаем, и сбрасываем счетчик.
В этом случае у нас будет несколько схем сравнения с 10, 15, 18, 24, 32 - что собственно совсем не хорошо.
В случае если счетчик считает до 0 у нас будет только 1 схема сравнения с нулем.


А установка регистра счетчика в какую-то начальную величину не будет занимать логику? Т.е., получается, при счете до нуля мы тратим логику на инициализацию счетчика, а при счете до константы тратим логику на сравнение значения счетчика с константой, я правильно понимаю?
ViKo
Если сравнивать с нулем и только с нулем, а при загрузке задавать произвольное значение счетчика, будет проще и быстрее, чем сравнивать с произвольным значением и сбрасывать счетчик.
one_eight_seven
Цитата
Но в ПЛИС то это реализуется на LUT (Look-Up Table) и на них без разницы как сравнивать, нули или конкретное значение.
Просто сказанное ниже, я имел ввиду применительно к ПЛИС:

Даже если LUT, то в случае ИЛИ - одна ячейка обрабатывает столько бит входных данных, сколько у неё входов, в случае же сравнения с загруженным числом, на каждый бит нужен регистр. Да и интерконнект сильнее нагружается. Хотя, тут настаивать не буду - с ПЛИСами работаю крайне мало.
nice_vladi
Цитата(ViKo @ Nov 13 2017, 08:31) *
Если сравнивать с нулем и только с нулем, а при загрузке задавать произвольное значение счетчика, будет проще и быстрее, чем сравнивать с произвольным значением и сбрасывать счетчик.


Почему так? Как было сказано выше, компаратору нет разницы, какие данные у него на входе. Нули-не нули, он все равно будет их сравнивать, так же, как и полноценные данные.

Ну и с точки зрения наглядности - гораздо понятнее событие, которое происходит через Н тактов (запись cnt == N), чем событие, которое происходит, когда счетчик в 0( cnt == 0, а инициализация где-то в другой части модуля, например). Хотя тут уже вопрос привычки, я думаю.
ViKo
Цитата(nice_vladi @ Nov 13 2017, 12:02) *
Почему так? Как было сказано выше, компаратору нет разницы, какие данные у него на входе. Нули-не нули, он все равно будет их сравнивать, так же, как и полноценные данные.

Как было сказано выше :-), сравнить только с нулем - это объединить биты по ИЛИ.
Кроме того, я вообще не сравниваю биты счетчика, а использую бит переноса. Которым загружаю в счетчик число, хранящееся в рядом находящемся регистре.
alxkon
Цитата(ViKo @ Nov 13 2017, 13:07) *
Как было сказано выше :-), сравнить только с нулем - это объединить биты по ИЛИ.
Кроме того, я вообще не сравниваю биты счетчика, а использую бит переноса. Которым загружаю в счетчик число, хранящееся в рядом находящемся регистре.

А можете проилюстрировать в коде?
ViKo
Вот. Последние варианты (4 и 5, одинаковые). Остальные показывают процесс разработки. :-)

CODE

`define LENTH 28
`define VAR5

`ifdef VAR1
`define TIM1
`elsif VAR2
`define TIM1
`elsif VAR3
`define TIM1
`else
`define TIM2
`endif

module Timer_m
`ifdef TIM1
(
(* chip_pin = "91",
altera_attribute = "-name global_signal on; -name io_standard lvds" *)
input bit clk, // high-speed clock
input bit [`LENTH-1:0] rld, // reload data
output bit pls // timer overload pulse
);
`endif

`ifdef VAR1
bit [`LENTH:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH]) cnt <= {1'b0, rld};
else cnt <= cnt - 1;
end
assign pls = cnt[`LENTH];

`elsif VAR2
bit [`LENTH-1:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH-1]) cnt <= rld;
else cnt <= cnt - 1;
end
assign pls = cnt[`LENTH-1];

`elsif VAR3
bit [`LENTH-1:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH-1]) cnt <= rld;
else cnt <= cnt[`LENTH-2:0] - 1;
end
assign pls = cnt[`LENTH-1];
`endif

`ifdef TIM2
(
(* chip_pin = "91",
altera_attribute = "-name global_signal on; -name io_standard lvds" *)
input bit clk, // high-speed clock
input bit [`LENTH-1:0] rld, // reload data
input bit ldp, // load pulse
output bit pls // timer overload pulse
);
`endif

`ifdef VAR4
bit [`LENTH:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH]) cnt <= {ldp, rld};
else cnt <= cnt[`LENTH-1:0] - 1;
end
assign pls = cnt[`LENTH];
`endif

`ifdef VAR5
bit [`LENTH-1:0] cnt;
always_ff @(posedge clk) begin
if (pls) {pls, cnt} <= {ldp, rld};
else {pls, cnt} <= cnt - 1;
end
`endif

endmodule : Timer_m
Timmy
Если речь о счётчике, то выгодней вообще не сравнивать все биты, а прекращать счёт при смене знака, то есть при переходе 0 -> -1 или -1 -> 0 в зависимости от направления счёта.
Flip-fl0p
Может кому пригодится. Обычный счетчик с синхронным сбросом и сигналом разрешения работы.
Основной "фишкой" счетчика является возможность выбора направление счета, и уменьшение занимаемого места если коэффициент пересчета это 2^n.

CODE
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY KAA_COUNTER_ENA_SCLR IS
GENERIC
(
DIRECTION : STRING := "UP"; -- UP / DOWN Направление счета
MAX_VALUE : INTEGER := 127 -- До скольки считает счетчик(от нуля до MAX_VALUE)
);
PORT
(
CLK : IN STD_LOGIC;
ENA : IN STD_LOGIC;
SCLR : IN STD_LOGIC;
DATA_OUT : OUT INTEGER RANGE 0 TO MAX_VALUE
);
END ENTITY;

ARCHITECTURE RTL OF KAA_COUNTER_ENA_SCLR IS
--=====================================================================
-- Функция определения разрядности шины - логарифм по основанию 2
--=====================================================================
FUNCTION LOG2 (MAX_VALUE : INTEGER) RETURN INTEGER IS -- Функция вычесления логарифма по основанию 2 для определения разрядности
VARIABLE CNT : INTEGER := 0; -- Счетчик для подсчета количества делений на 2
VARIABLE TEMP : INTEGER := MAX_VALUE + 1; -- Делимое - это максимальное количество СОСТОЯНИЙ счетчика
BEGIN
WHILE (TEMP > 1) LOOP -- Пока делимое больше 1 совершаем цикл деления на 2
CNT := CNT + 1 ; -- Считаем количество циклов деления
TEMP := TEMP/2; -- Делим число на 2 каждый цикл
END LOOP;
RETURN CNT; -- Значение счетчика и есть логарифм
END LOG2;

SIGNAL COUNTER_INTEGER : INTEGER RANGE 0 TO MAX_VALUE := 0; -- Счетчик обнуляемый условием максимального счета
SIGNAL COUNTER_UNSIGNED : UNSIGNED(LOG2(MAX_VALUE)-1 DOWNTO 0) := (others => '0'); -- Счетчик с автомватическим обнулением при переполнении
BEGIN

--================================================================================
==========================
-- Если максимальное значение счетчика переведенное в двоичное число не состоит из одних только единиц
-- Значит у нас счетчик обнуляется значением максимального счета т.е до наступления переполнения разрядов
--================================================================================
==========================
COUNTER_INTEGER_GEN : IF ( TO_UNSIGNED(MAX_VALUE,LOG2(MAX_VALUE) ) /= ( LOG2(MAX_VALUE)-1 DOWNTO 0 => '1' ) ) GENERATE
CNT_PROC : PROCESS
(
CLK
)
BEGIN
IF (RISING_EDGE(CLK)) THEN
--==========================================================================
-- Генерируем суммирующий счетчик обнуляющийся максимальным значением счета
--==========================================================================
IF (DIRECTION = "UP") THEN -- Если направление счета "вверх" генерируется суммирующий счетчик
IF (ENA = '1') THEN -- При активном сигнале разрешения раблоты
IF (COUNTER_INTEGER = MAX_VALUE) THEN -- При наступлении максимального значения счетчика
COUNTER_INTEGER <= 0; -- Обнулим его
ELSE
COUNTER_INTEGER <= COUNTER_INTEGER + 1; -- Иначе будем его инкрементировать
END IF;
END IF;
IF (SCLR = '1') THEN -- При активном синхронном сбросе
COUNTER_INTEGER <= 0; -- Сбросим счетчик в ноль
END IF;
END IF;
--==========================================================================
-- Генерируем вычитающий счетчик обнуляющийся максимальным значением счета
--==========================================================================
IF (DIRECTION = "DOWN") THEN -- Если направление счета "вниз" то генерируется вычитающий счетчик
IF (ENA = '1') THEN -- При активном сигнале разрешения раблоты
IF (COUNTER_INTEGER = 0) THEN -- При наступлении максимального значения счетчика
COUNTER_INTEGER <= MAX_VALUE; -- Обнулим его
ELSE
COUNTER_INTEGER <= COUNTER_INTEGER - 1; -- Иначе будем его декрементировать
END IF;
END IF;
IF (SCLR = '1') THEN -- При активном синхронном сбросе
COUNTER_INTEGER <= MAX_VALUE; -- Сбросим счетчик в максимальное значение
END IF;
END IF;
END IF;
END PROCESS;
DATA_OUT <= COUNTER_INTEGER; -- Выдадим значение счетчика на выходные порты модуля
END GENERATE;

--================================================================================
==========================
-- Если максимальное значение счетчика это (2**N - 1)
-- Значит у нас полный счетчик, который обнуляется переполнением разрядов
--================================================================================
==========================
COUNTER_UNSIGNED_GEN : IF ( TO_UNSIGNED(MAX_VALUE,LOG2(MAX_VALUE) ) = ( LOG2(MAX_VALUE)-1 DOWNTO 0 => '1' ) ) GENERATE
CNT_PROC : PROCESS
(
CLK
)
BEGIN
IF (RISING_EDGE(CLK)) THEN
--==========================================================================
-- Генерируем суммирующий счетчик обнуляющийся переполнением разрядов
--==========================================================================
IF (DIRECTION = "UP") THEN -- Если направление счета "вверх" генерируется суммирующий счетчик
IF (ENA = '1') THEN -- При активном сигнале разрешения раблоты
COUNTER_UNSIGNED <= COUNTER_UNSIGNED + "1"; -- Инкрементируем его каждый такт
END IF;
IF (SCLR = '1') THEN -- При активном синхронном сбросе
COUNTER_UNSIGNED <= (OTHERS => '0'); -- Сбросим счетчик в ноль
END IF;
END IF;
--==========================================================================
-- Генерируем вычитающий счетчик обнуляющийся переполнением разрядов
--==========================================================================
IF (DIRECTION = "DOWN") THEN -- Если направление счета "вниз" то генерируется вычитающий счетчик
IF (ENA = '1') THEN -- При активном сигнале разрешения раблоты
COUNTER_UNSIGNED <= COUNTER_UNSIGNED - "1"; -- Декрементируем его каждый такт
END IF;
IF (SCLR = '1') THEN -- При активном синхронном сбросе
COUNTER_UNSIGNED <= TO_UNSIGNED(MAX_VALUE,LOG2(MAX_VALUE) ); -- Сбросим счетчик в единицы
END IF;
END IF;
END IF;
END PROCESS;
DATA_OUT <= TO_INTEGER(COUNTER_UNSIGNED); -- Выдадим значение счетчика на выходные порты модуля
END GENERATE;
END ARCHITECTURE;
andrew_b
Нет проверки на то, что что в DIRECTION будет нечто, отличное от "UP" и "DOWN". Например, "Up" или "Down". Не всем нравится верхний регистр. Лучше заменить строку на boolean.
От счётчика с integer никакого профита. То же самое можно сделать двоичным счётчиком. Тем более, что вы не сможете сделать integer-счётчик более 32 бит.
Есть старый программистский трюк: если число x является степенью 2, то
Код
x and (x - 1) = 0
Flip-fl0p
Цитата(andrew_b @ Dec 30 2017, 07:54) *
Нет проверки на то, что что в DIRECTION будет нечто, отличное от "UP" и "DOWN". Например, "Up" или "Down". Не всем нравится верхний регистр. Лучше заменить строку на boolean.
От счётчика с integer никакого профита. То же самое можно сделать двоичным счётчиком. Тем более, что вы не сможете сделать integer-счётчик более 32 бит.
Есть старый программистский трюк: если число x является степенью 2, то
Код
x and (x - 1) = 0

Спасибо за критику ! Я обязательно подумаю над этим.
Вот со счётчиками типа integer я не совсем согласен. Какой тип использовать в качестве выхода: SLV, integer, unsigned - зависит лишь от предпочтений разработчика. Никаких преимуществ SLV над integer нет, как и наоборот. Это лишь вопрос удобства. В конце концов, всегда значение можно преобразовать в тот тип, который нужен smile3046.gif
andrew_b
Цитата(Flip-fl0p @ Dec 30 2017, 13:12) *
Никаких преимуществ SLV над integer нет, как и наоборот. Это лишь вопрос удобства. В конце концов, всегда значение можно преобразовать в тот тип, который нужен
Ну попробуйте сделать на integer счётчик разрядностью более 32 бит.
Flip-fl0p
Цитата(andrew_b @ Dec 30 2017, 13:15) *
Ну попробуйте сделать на integer счётчик разрядностью более 32 бит.

Вы снова правы ! biggrin.gif
Вот только мне ещё ни разу не приходилось работать с такими счетчиками...
Можете сказать навскидку где вообще такой счетчик мог бы пригодится ?
blackfin
Цитата(Flip-fl0p @ Dec 30 2017, 13:31) *
Можете сказать навскидку где вообще такой счетчик мог бы пригодится ?

Для адресации внешней SDRAM памяти объемом 128 ГБайт. biggrin.gif
Александр77
Цитата(andrew_b @ Dec 30 2017, 07:54) *
Нет проверки на то, что что в DIRECTION будет нечто, отличное от "UP" и "DOWN". Например, "Up" или "Down". Не всем нравится верхний регистр. Лучше заменить строку на boolean.

В VHDL нет разницы между UP, Up, uP или up. Про Down можно сказать тоже самое.
Flip-fl0p
Цитата(Александр77 @ Dec 30 2017, 14:17) *
В VHDL нет разницы между UP, Up, uP или up. Про Down можно сказать тоже самое.

Для типа string разница есть !
andrew_b
Цитата(Александр77 @ Dec 30 2017, 14:17) *
В VHDL нет разницы между UP, Up, uP или up. Про Down можно сказать тоже самое.
Тащемта там строка, а не перечислимый тип.

Цитата(Flip-fl0p @ Dec 30 2017, 13:31) *
Можете сказать навскидку где вообще такой счетчик мог бы пригодится ?
Дело в принципе. Если вы пишете модуль, претендующий на универсальность, пишите его на самом деле универсальным, с минимальным количеством ограничений.
Flip-fl0p
Цитата(andrew_b @ Dec 30 2017, 15:39) *
Тащемта там строка, а не перечислимый тип.

Дело в принципе. Если вы пишете модуль, претендующий на универсальность, пишите его на самом деле универсальным, с минимальным количеством ограничений.

Понял. Учтем !
А вообще в VHDL сделать модуль полностью универсальным очень сложно.
Например я как-то хотел сделать модуль полностью настраиваемым. Т.е в области generic настраивать все возможные комбинации входных и выходных портов. Например если нужен сигнал синхронного сброса, сигнал переноса, разрешения работы, и пр то их можно включить просто настройкой параметров модуля. Но столкнулся с проблемой отсутствием возможности генерировать по условию входные\выходные порты. Вернее не так, возможность есть, но она очень кривая и модуль выдает кучу предупреждений. Хотя синтезируется нормально.
Иногда даже жалко что у VHDL нет предпроцессора.
alxkon
Цитата(ViKo @ Nov 13 2017, 14:27) *
Вот. Последние варианты (4 и 5, одинаковые). Остальные показывают процесс разработки. :-)

CODE

`define LENTH 28
`define VAR5

`ifdef VAR1
`define TIM1
`elsif VAR2
`define TIM1
`elsif VAR3
`define TIM1
`else
`define TIM2
`endif

module Timer_m
`ifdef TIM1
(
(* chip_pin = "91",
altera_attribute = "-name global_signal on; -name io_standard lvds" *)
input bit clk, // high-speed clock
input bit [`LENTH-1:0] rld, // reload data
output bit pls // timer overload pulse
);
`endif

`ifdef VAR1
bit [`LENTH:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH]) cnt <= {1'b0, rld};
else cnt <= cnt - 1;
end
assign pls = cnt[`LENTH];

`elsif VAR2
bit [`LENTH-1:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH-1]) cnt <= rld;
else cnt <= cnt - 1;
end
assign pls = cnt[`LENTH-1];

`elsif VAR3
bit [`LENTH-1:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH-1]) cnt <= rld;
else cnt <= cnt[`LENTH-2:0] - 1;
end
assign pls = cnt[`LENTH-1];
`endif

`ifdef TIM2
(
(* chip_pin = "91",
altera_attribute = "-name global_signal on; -name io_standard lvds" *)
input bit clk, // high-speed clock
input bit [`LENTH-1:0] rld, // reload data
input bit ldp, // load pulse
output bit pls // timer overload pulse
);
`endif

`ifdef VAR4
bit [`LENTH:0] cnt;
always_ff @(posedge clk) begin
if (cnt[`LENTH]) cnt <= {ldp, rld};
else cnt <= cnt[`LENTH-1:0] - 1;
end
assign pls = cnt[`LENTH];
`endif

`ifdef VAR5
bit [`LENTH-1:0] cnt;
always_ff @(posedge clk) begin
if (pls) {pls, cnt} <= {ldp, rld};
else {pls, cnt} <= cnt - 1;
end
`endif

endmodule : Timer_m

Извините, забыл совсем ответить. Если я не ошибаюсь в Вашем варианте загружаемое значение lpd должно быть на 1 меньше от желаемого значения счета.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.