Обновить
0

Пользователь

Отправить сообщение

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

В FPGA с определенного уровня отсутствует гибкость по выбору предложений по работе, и вилка зарплат существенно ниже, в сравнении теми же программистами в web.

Как не прискорбно, но такие мысли в последнее время посещают и меня…
Не уверен, буду ли я заниматься цифровой обработкой на ПЛИС в будущем, но если я останусь в рядах разработчиков и радиоинженеров, то вскоре вы увидите и другие интересные проекты с открытым исходным кодом!

Вот эта вот фраза несколько пугает. Почему
если я останусь в рядах разработчиков и радиоинженеров
?
Иногда без процессорного ядра задача крайне сложно решается на ПЛИС.
Но процессорное ядро Nios имеет большое количество ограничений, одно из которых то, что оно требует денег. Но на проектах «поиграться» вполне годиться.
Но так это же проблема как раз неправильно заведенного сброса!
Тем более, что если этот сигнал асинхронный, то кто мешал отдельно этот сигнал пропустить через триггер, для удаления иголок и потом уже применить как сброс?
А можно пример, где иголки приводят к проблемам?
Разумеется мы не берем в расчет «кривые» проекты, где, например, выход комбинаторной логики без применения специальных методов подавления иголок заводится на асинхронные входы.
А вот это неверное утверждение.

Секундочку!

Иголки — это следствие того, что сигналы какого-нибудь счетчика, например, переключаются не мгновенно, а с некоторой разбежкой (единицы наносекунд, а порой и сотни пикосекунд если брать современные FPGA). И из-за этого у нас и образуются иголки.

Но когда мы говорим о FPGA мы подразумеваем концепцию синхронного проектирования. И данные с этого счетчика (или сигнал с компаратора данных счетчика) будут защелкнуты фронтом синхрочастоты. А раз так, то у нас есть временной интервал Tsetup + Thold в течении которого данные должны быть стабильны.

Так вот иголки, которые возникают — они возникают за пределами этого временного окна. Фактически на границах сигнала. Если эти иголки попали в этот временной интервал — значит схема работает на слишком большой частоте.

Я вот этого не могу понять, откуда вообще могут появиться иголки?
Не понимаю как можно сделать правильно:) Комбинаторная схема во время переключения входных сигналов имеет полное право генерировать «иголки», которые вызовут сброс.
Все тоже самое верно и для комбинаторных выходов. Комбинаторные сигналы безопасно идут только внутри домена синхронных схем, как только вы их выдали наружу или в домен с другим клоком, вы потенциально породили проблемы. Если не сейчас, так завтра.

А откуда там взяться иголкам? Вы сбрасываете асинхронно регистр состояний, а выходные сигналы зависят только от текущего состояния.
Абсолютно те-же иголки формируются при синхронном переключении состояний автомата.
Не понимаю я, как асинхронный сброс может вызвать сбой?
Конечно же при условии применения reset_bridge, который синхронно снимает сигнал сброса с домена схемы. Я поэтому и говорил, что сброс должен быть заведен правильно ^_^

Ну если уж так хочется — формируйте на триггере сигнал data_valid — строб сопровождающий правильные данные. И сбрасывайте этот сигнал вместе с автоматом. Тогда при отсутствии этого сигнала нам вообще не важно, что в этот момент происходит на шине данных например.

Комбинаторные сигналы безопасно идут только внутри домена синхронных схем

Пересечение клоковых доменов — это вообще отдельная тема для дискуссий )
Нет, если мы говорим не про формальное разделение на блоки, а про функциональное. Третий always блок для формирования регистрового, а не комбинационного выхода. И это не философия.

Редкий случай когда нужны регистровые выходы — это размещение выходных сигналов в fsat output регистры, для более полного управления зажержками этих сигналов путем задания временных ограничений. Но для этого совсем необязательно применять регистровые выходы FSM.

До тех пор пока выход автомата не зашел на какой-либо асинхронный сброс или подобное…

Если асинхронный сброс сделан правильно, то никаких проблем в схеме наблюдаться не должно. Если есть проблемы- значит сброс выполнен неверно!

Я бы не называл данный автомат в 7 состояний большим, но интересно было бы посмотреть вашу реализацию. Пока мне не очевидно как предлагаемое Вами разбиение сделает работу более наглядной.

На скорую руку сделал проект, как я его вижу. К сожалению на таком простом примере преимущества моего подхода показать тяжело. Но в более сложных контроллерах. Например контроллерах SDRAM все преимущества такого подхода налицо.
Вот так вот выглядит структура контроллера:
<img src="ARCH.PNG" alt=«image»/>
Контроллер задержек — это фактически обычный счетчик с синхронной загрузкой:
<img src="CNT.PNG" alt=«image»/>
Код выглядит так (писал на VHDL на скорую руку):
library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    
entity CMD_CTRL is
    generic
    (
        READ_SETUP   : natural  := 5;
        READ_PULSE   : natural  := 3;
        READ_HOLD    : natural  := 1;
        WRITE_SETUP  : natural  := 5;
        WRITE_PULSE  : natural  := 3;
        WRITE_HOLD   : natural  := 1
    );
	port
    (
        clk           : in  std_logic;
        reset         : in  std_logic;
        w_strb        : in  std_logic; 
        r_strb        : in  std_logic;
        
        s_waddress    : in  std_logic_vector(7 downto 0);
        s_raddress    : in  std_logic_vector(7 downto 0);
        s_data_to     : in  std_logic_vector(7 downto 0);
        s_data_from   : out std_logic_vector(7 downto 0);
        done          : out std_logic;
        
        --------------------- Интерфейс с контроллером задержек ----------------------
        delay_done    : in  std_logic;
        delay_start   : out std_logic;
        delay_data    : out natural range 0 to 7;
        
         --------------------- Интерфейс с памятью ----------------------
        m_data_to     : out std_logic_vector(7 downto 0);
        m_data_from   : in  std_logic_vector(7 downto 0);
        m_address     : out std_logic_vector(7 downto 0);
        cs_n          : out std_logic;
        oe_n          : out std_logic;
        we            : out std_logic
    );
end entity;

architecture rtl of CMD_CTRL is
    type state_type is 
    (
        IDLE,                                              -- Состояние записи данных в 0 буфер 
        PREPARE_READ,                                     -- Состояние записи данных в 1 буфер 
        READ,
        END_READ,
        PREPARE_WRITE,
        WRITE,
        END_WRITE
    );
    signal pres_state, next_state : state_type := IDLE;  
    attribute syn_encoding : string;                        -- атрибуты синтеза
    attribute syn_encoding of state_type : type is "safe";  -- создать машину состойний, выходящую из ошибочных состояний
    
    signal addr_latch : std_logic_vector(7 downto 0) := (others => '0');
    signal data_latch : std_logic_vector(7 downto 0) := (others => '0');
    signal read_ena   : std_logic;
    signal dellay_cnt : unsigned(5 downto 0) := (others => '0');
    signal dellay     : std_logic_vector(2 downto 0);
begin

    --======================================================
    -- Смена состояний автомата на вычесленное состояние
    --======================================================
    next_state_proc : process(clk, reset)
    begin  
        if (reset = '1') then
            pres_state <= IDLE;
        elsif (rising_edge(clk)) then
            pres_state <= next_state;            
        end if;
    end process;
    
    --======================================================
    -- Процес вычесления следующего состояния автомата
    --======================================================
    next_state_logic : process(all)
    begin
        case pres_state is
            when IDLE            =>
                                    next_state <= IDLE;
                                    if (w_strb = '1') then 
                                        next_state <= PREPARE_WRITE;
                                    end if;
                                    
                                    if (r_strb = '1') then
                                        next_state <= PREPARE_READ;
                                    end if;
            --====================================================================================================
            when PREPARE_READ    =>
                                    next_state <= PREPARE_READ;
                                    if (delay_done = '1') then 
                                        next_state <= READ;
                                    end if;
            --====================================================================================================
            when READ            =>
                                    next_state <= READ;
                                    if (delay_done = '1') then 
                                        next_state <= END_READ;
                                    end if;
            --====================================================================================================
            when END_READ        =>
                                    next_state <= END_READ;
                                    if (delay_done = '1') then 
                                        next_state <= IDLE;
                                    end if;
            --====================================================================================================
            when PREPARE_WRITE   =>
                                    next_state <= PREPARE_WRITE;
                                    if (delay_done = '1') then 
                                        next_state <= WRITE;
                                    end if;
            --====================================================================================================
            when WRITE           =>
                                    next_state <= WRITE;
                                    if (delay_done = '1') then 
                                        next_state <= END_WRITE;
                                    end if;
            --====================================================================================================
            when END_WRITE       =>
                                    next_state <= END_WRITE;
                                    if (delay_done = '1') then 
                                        next_state <= IDLE;
                                    end if;
            
            when others         => null;
        end case;
    end process;

    --======================================================
    -- Процесс выходных данных
    --======================================================
    output_data_logic : process(all)
    begin
        cs_n        <= '1';
        oe_n        <= '1';
        we          <= '0';  
        m_address   <= (others => '-');
        done        <= '0';
        read_ena    <= '0';
        delay_start <= '0';
        dellay      <= (others => '-');
        case pres_state is
            when IDLE            =>
                                    cs_n <= '1';
                                    oe_n <= '1';
                                    we   <= '0'; 
                                    
                                    
                                    if (w_strb = '1') then 
                                        delay_start <= '1';
                                        dellay <= std_logic_vector(to_unsigned(WRITE_SETUP,3));
                                    end if;
                                    
                                    if (r_strb = '1') then
                                        delay_start <= '1';
                                        dellay <= std_logic_vector(to_unsigned(READ_SETUP,3));
                                    end if;
            --====================================================================================================
            when PREPARE_READ    =>
                                    cs_n <= '0';
                                    oe_n <= '1';
                                    we   <= '0'; 
                                    m_address <= addr_latch;
                                    
                                    if (delay_done = '1') then
                                        delay_start <= '1';
                                    end if;
                                    
                                    dellay <= std_logic_vector(to_unsigned(READ_PULSE,3));
                                    
            --====================================================================================================
            when READ            =>
                                    cs_n <= '0';
                                    oe_n <= '0';
                                    we   <= '0'; 
                                    
                                    if (delay_done = '1') then
                                        read_ena <= '1';
                                        delay_start <= '1';
                                    end if;
                                    m_address <= addr_latch;
                                    dellay <= std_logic_vector(to_unsigned(READ_HOLD,3));
            --====================================================================================================
            when END_READ        =>
                                    cs_n <= '0';
                                    oe_n <= '1';
                                    we   <= '0'; 
                                    if (delay_done = '1') then
                                        done <= '1';
                                        delay_start <= '1';
                                    end if;
                                    m_address <= addr_latch;
                                    
            --====================================================================================================
            when PREPARE_WRITE   =>
                                    cs_n <= '0';
                                    oe_n <= '1';
                                    we   <= '0'; 
                                    m_address   <= addr_latch;
                                    if (delay_done = '1') then
                                        delay_start <= '1';
                                    end if;
                                    dellay <= std_logic_vector(to_unsigned(WRITE_PULSE,3));
            --====================================================================================================
            when WRITE           =>
                                    cs_n <= '0';
                                    oe_n <= '1';
                                    we   <= '1'; 
                                    m_address <= addr_latch;
                                    if (delay_done = '1') then
                                        delay_start <= '1';
                                    end if;
                                    
                                    dellay <= std_logic_vector(to_unsigned(WRITE_HOLD,3));
            --====================================================================================================
            when END_WRITE       =>
                                    cs_n <= '0';
                                    oe_n <= '1';
                                    we   <= '0'; 
                                    if (delay_done = '1') then
                                        done <= '1';
                                        delay_start <= '1';
                                    end if;
                                    m_address <= addr_latch;
                                    
            when others         => null;
        end case;
    end process;
    
    
    m_data_to <= data_latch;
    delay_data <= to_integer(unsigned(dellay));
    
    --======================================================
    -- Защелкивание данных и адреса в регистры
    --======================================================
    addr_and_data_latch : process(clk, reset)
    begin  
        if (rising_edge(clk)) then
            if (w_strb = '1') then
                addr_latch <= s_waddress;
                data_latch <= s_data_to;
            end if;
            
            if (r_strb = '1') then
                addr_latch <= s_raddress;
            end if;
        end if;
    end process;
    --======================================================
    -- Чтение данных из памяти
    --======================================================
    read_data_from_memory : process(clk, reset)
    begin  
        if (rising_edge(clk)) then
            if (read_ena = '1') then
                s_data_from <= m_data_from;
            end if;
        end if;
    end process;
    
end architecture;




По занимаемым ресурсам — получается в 2 раза меньше.
По скорости почти на 100 MГц выше.
Как по мне, так описание одни always блоком или в одном process( в терминологии VHDL) порождает на выходе FSM лишние и, самое главное, никому не нужные триггеры.
Если брать реализацию классического автомата то это 3 always блока.
Первый always блок — последовательная логика.
В нем FSM приобретает начальное значение по сбросу и в этом процессе в регистр состояния записывается новое состояние автомата.

Второй always блок — комбинационная логика переходов.
Здесь вычисляется следующее состояние автомата.

Третий always блок- выходная логика автомата.
В нем в зависимости от текущего состояния автомата формируются выходные сигналы. Если сигналы зависят только от текущего состояние — это это автомата Мура. Если же сигналы зависят как от текущего состояния автомата, так и от входных воздействий — то это автомат Мили.

При этом если последние 2 always блока объединить в один, то не изменится ровным счетом ничего.
Писать автомат в 2 always блоках или в 3 — вопрос чисто философский. Я предпочитаю 3 блока. Для меня так автомат нагляднее.

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

Я бы данную большую FSM разбил на 2 отдельных файла.
Первый файл — FSM, выдающая команды памяти.
Второй файл — контроллер задержек, который анализирует команды памяти, и отсчитывает необходимые задержки и выдает первой FSM данные о статусе задержек. Первая FSM на основании этих задержек и логики переходов меняет своё состояние.
В этом случае, видимый код сокращается в 2 раза. И самое главное становится более наглядной работа автомата.
Я вообще считаю, что большие автоматы, надо дробить на более маленькие и простые. И порой после такого деления работа всей системы становится простой и понятной. И немаловажным фактором является то, что отлаживать такие автоматы проще чем один большой и сложный

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность