Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Не работает модуль на verilog
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Языки проектирования на ПЛИС (FPGA)
Sprite
Доброго всем времени суток!
Прошу меня сильно не пинать, Verilog начал изучать совсем недавно. Модуль не хочет работать ни в симуляторе, ни в железе( Код программы, RTL и скрин симуляции прилагаются, могу скинуть исходный код. Проект собирается в Quartus II 9.1, ПЛИС cyclone III.
Код:
Код
module MEM_CONTROLLER
(
    input   clk, cs,
    input   [15:0] data_in,
    output  [15:0] data_out,
    output  reg [3:0] addr,
    output  wr
);
    reg [15:0] data_out_reg;
    reg [2:0] state;
    reg b_write;    //Если 1 - то записываем данные
    reg wr_reg;        

    parameter S0 = 0, S1 = 1, S2 = 2, S3 = 3, S4 = 4, S5 = 5, S6 = 6;

    always @ (posedge clk)
    begin
        case (state)
            
            //---------------------------
            // Ждем спад 1 cs
            //---------------------------
            S0:
                if (cs==0)
                    state <= S1;
                else
                    state <= S0;
            //---------------------------
            // Фронт 1 cs, заносим адрес в регистр addr
            // Если старший бит = 1 - то записываем данные в RAM,
            // иначе - читаем
            //---------------------------
            S1:
                if (cs)
                    begin
                        case (data_in[2:0])
                            3'b001  : addr <= 4'b0001;
                            3'b010  : addr <= 4'b0010;
                            3'b011  : addr <= 4'b0100;
                            3'b100  : addr <= 4'b1000;
                            default : addr <= 4'b0000;
                        endcase
                        
                        b_write <= data_in[15]? 1'b1:1'b0;
                        state <= S2;
                    end
                else
                    state <= S1;
            //---------------------------
            // Ждем спад 2 cs            
            //---------------------------
            S2:
                if (cs==0)
                    state <= S3;
                else
                    state <= S2;
            //---------------------------
            // Ждем фронт 2 cs
            //---------------------------
            S3:
                if (cs)
                    if (b_write)
                        begin
                            data_out_reg[15:0] <= data_in[15:0];
                            state <= S4;
                        end
                    else
                        state <= S0;    //Читаем данные (выводим их на data_out)
                else
                    state <= S3;
            //---------------------------
            // Ждем установки addr
            // Устанавливаем wr = 1
            //---------------------------        
            S4:
            begin
                wr_reg <= 1;
                state <= S5;
            end
            //---------------------------
            // Сбрасываем wr
            //---------------------------        
            S5:
            begin    
                wr_reg <= 0;
                state <= S0;
            end
            //---------------------------
        endcase
    end

    assign data_out[15:0] = data_out_reg[15:0];
    assign wr = wr_reg;

endmodule

RTL:

Waveform:


В симулиции по логике там где стоит маркер должен происходить переход в состояние S2, но ничего не происходит(
Flip-fl0p
Цитата(Sprite @ May 10 2018, 07:33) *
Прошу меня сильно не пинать, Verilog начал изучать совсем недавно. Модуль не хочет работать ни в симуляторе, ни в железе

1. Я бы посоветовал проводить симуляцию не тем симулятором, который поставляется вместе с Quartus, а в таких симуляторах, как например, Modelsim или AtiveHDL. У всех есть бесплатные версии с ограничениями.
2. Начните описывать автоматы 3 классическими always блоками :
Первый блок синхронный - переключение состояний автомата.
Второй блок комбинационный - вычисление следующего состояния автомата.
Третий блок комбинационный - выходня логика автомата.
И лишь тогда, когда Вы научитесь правильно описывать автоматы таким стилем, только тогда переходите к другим стилям описания автомата. Сейчас Вы выбрали самый сложный стиль описания, где допустить ошибку очень и очень просто. А найти её, да ещё при использовании альтеровского симулятора, очень сложно.
3. Ну и присвойте нормальные имена состояниям автомата вместо:
Код
parameter S1 = 0, S1 = 1, S2 = 2, S3 = 3, S4 = 4, S5 = 5, S6 = 6;

Правильно присвоенные имена - очень помогают ориентироваться по коду ,особенно людям с ним незнакомым, а также избежать ошибок.
AVR
Цитата(Sprite @ May 10 2018, 07:33) *
Модуль не хочет работать ни в симуляторе, ни в железе

Не вижу ничего криминального, сам так пишу. Но вот какой смысл писать эти S0 S1,2...? Просто числа, либо идентификаторы. А тут можно просто числами ограничиться было.
Первое что заметил, в Verilog все имена зависят от высоты букв, поэтому разумеется что CS и cs это разные переменные для Verilog и в S2 мы так никогда и не попадем, хотя я не вижу код тестбенча, чтобы утверждать точно.
Далее, симулятор показывает белиберду в state.S0..N, почему нельзя просто вывести state ввиде числа и там было бы всё ясно и однозначно.
В общем, нужен опыт и всё станет хорошо.
Sprite
Цитата(Flip-fl0p @ May 10 2018, 12:01) *
2. Начните описывать автоматы 3 классическими always блоками :
Первый блок синхронный - переключение состояний автомата.
Второй блок комбинационный - вычисление следующего состояния автомата.
Третий блок комбинационный - выходня логика автомата.
И лишь тогда, когда Вы научитесь правильно описывать автоматы таким стилем, только тогда переходите к другим стилям описания автомата. Сейчас Вы выбрали самый сложный стиль описания, где допустить ошибку очень и очень просто. А найти её, да ещё при использовании альтеровского симулятора, очень сложно.

А можно чуть подробнее? Для реализации state-машины я использовал стандартный шаблон автомата Мура:
Код
module four_state_moore_state_machine
(
    input    clk, in, reset,
    output reg [1:0] out
);

    // Declare state register
    reg        [1:0]state;

    // Declare states
    parameter S0 = 0, S1 = 1, S2 = 2, S3 = 3;

    // Output depends only on the state
    always @ (state) begin
        case (state)
            S0:
                out = 2'b01;
            S1:
                out = 2'b10;
            S2:
                out = 2'b11;
            S3:
                out = 2'b00;
            default:
                out = 2'b00;
        endcase
    end

    // Determine the next state
    always @ (posedge clk or posedge reset) begin
        if (reset)
            state <= S0;
        else
            case (state)
                S0:
                    state <= S1;
                S1:
                    if (in)
                        state <= S2;
                    else
                        state <= S1;
                S2:
                    if (in)
                        state <= S3;
                    else
                        state <= S1;
                S3:
                    if (in)
                        state <= S2;
                    else
                        state <= S3;
            endcase
    end

endmodule

Но здесь всего 2 блока: синхронный (Determine the next state) и комбинационный - вычисление следующего состояния автомата (Output depends only on the state). О каком третьем блоке Вы говорите? Насчет имен состояний полностью согласен - напишу что-нибудь более вразумительное.
andrew_b
Цитата(Flip-fl0p @ May 10 2018, 08:01) *
Сейчас Вы выбрали самый сложный стиль описания, где допустить ошибку очень и очень просто.
Я придерживаюсь прямо противоположного взгляда. Ничего простого и удобного в размазывании КА по нескольким процессам не вижу.
Flip-fl0p
Цитата(andrew_b @ May 10 2018, 09:36) *
Я придерживаюсь прямо противоположного взгляда. Ничего простого и удобного в размазывании КА по нескольким процессам не вижу.

У Вас опыта гораздо больше biggrin.gif.
Согласен, что несколько неудобно, т.к описание такого КА получается достаточно громоздким. Но при разделении автомата на 3 процесса - меньше шансов наделать ошибок, особенно это важно для начинающих. Считаю, что сначала надо научиться делать "классически" и "неудобно" - и когда будет полное понимание того, что происходит при описании автомата и в какую схему в итоге это выливается(да и вообще важно понимать что схемотехнически представляет собой цифровой автомат), тогда можно писать удобным для себя стилем, опять-же с полным пониманием зачем и почему.
Sprite
По совету Flip-fl0p переделал state-машину на 2 always-блока:
Код
module MEM_CONTROLLER
(
    input    clk, cs,
    input     [15:0] data_in,
    output     reg[15:0] data_out,
    output     reg[3:0] addr,
    output     reg wr
);
    reg [15:0] data_out_reg;
    reg [3:0] addr_reg;
    reg b_write;    //Если 1 - то записываем данные
    reg wr_reg;        
    
    initial
    begin
        data_out_reg = 15'b0;
        addr_reg = 4'b0;
    end
    
    
    reg    [2:0] state;
    parameter S0 = 0, S1 = 1, S2 = 2, S3 = 3, S4 = 4;


    // Output depends only on the state
    always @ (state, data_out_reg, addr_reg, data_in)
    begin
        case (state)
            S0,S1,S2,S3:
            begin
                data_out[15:0] = data_out_reg[15:0];
                addr[3:0] = addr_reg[3:0];
                wr = 1'b0;
            end
            S4:
            begin
                data_out[15:0] = data_in[15:0];
                addr[3:0] = addr_reg[3:0];
                wr = 1'b1;
            end    
            
            default:
            begin
                data_out[15:0] = 15'b0;
                addr[3:0] = 4'b0;
                wr = 1'b0;
            end
        endcase
    end


    always @ (posedge clk)
    begin
        case (state)
            
            //---------------------------
            // Ждем спад 1 cs
            //---------------------------
            S0:
                if (cs==0)
                    state <= S1;
                else
                    state <= S0;
            //---------------------------
            // Ждем фронт 1 cs, заносим адрес в регистр addr
            // Если старший бит в адресе = 1 - то записываем данные,
            // иначе - читаем
            //---------------------------
            S1:
                if (cs)
                    begin
                        case (data_in[2:0])
                            3'b001  : addr_reg <= 4'b0001;
                            3'b010  : addr_reg <= 4'b0010;
                            3'b011  : addr_reg <= 4'b0100;
                            3'b100  : addr_reg <= 4'b1000;
                            default : addr_reg <= 4'b0000;
                        endcase
                        
                        b_write <= data_in[15]? 1'b1:1'b0;
                        state <= S2;
                    end
                else
                    state <= S1;
            //---------------------------
            // Ждем спада 2 cs            
            //---------------------------
            S2:
                if (cs==0)
                    state <= S3;
                else
                    state <= S2;
            //---------------------------
            // Ждем фронт 2 cs
            //---------------------------
            S3:
                if (cs)
                    if (b_write)
                        state <= S4;
                    else
                        state <= S0;    //Читаем данные (выводим их на data_out)
                else
                    state <= S3;
            //---------------------------
            // Устанавливаем wr = 1
            //---------------------------        
            S4:
                state <= S0;
            //---------------------------
        endcase
    end

endmodule


В результате получилась более компактная и понятная RTL-схема:


И симуляция частично поправилась:

Только теперь значение data_out меняется только на момент состояния S4, при этом не сохраняя измененное значение ( Как это можно поправить?
one_eight_seven
Цитата
Только теперь значение data_out меняется только на момент состояния S4, при этом не сохраняя измененное значение ( Как это можно поправить?

Начать действовать последовательно и осмысленно.
Во-первых, если вы отлаживаете FSM, то нужно и смотреть FSM (регистр state), и входные сигналы, которые влияют на переход из одного состояния в другое.
Соответвствие выходов состоянию отладите позже.
Но если вы считаете, что автомат проходит по состояниям верно, то отлажвайте выходы. В вашем коде, в состояниях, кроме S4 data_out равно значению регистра, который инициализируется, но присваиваний которому не производится, т.е. он неизменный на протяжении работы системы, потому и data_out не изменяется. А в состоянии S4 сигналу data_out присваивается другое значение. То есть, всё работает так, как написано, и если вы хотите другого поведения, то его надо определить и описать в коде.
Flip-fl0p
Цитата
Только теперь значение data_out меняется только на момент состояния S4, при этом не сохраняя измененное значение ( Как это можно поправить?

Потому-что data_out у вас мультиплексор, который в состоянии S4: выдает то, что на входе (data_in) :

Код
                data_out[15:0] = data_in[15:0];
                addr[3:0] = addr_reg[3:0];
                wr = 1'b1;


а в остальных состояниях выдает нули.

Код
            default:
            begin
                data_out[15:0] = 15'b0;
                addr[3:0] = 4'b0;
                wr = 1'b0;
            end


Делать надо немного по-другому. В состоянии S4 выдается сигнал разрешения записи в регистр:
Код
            S4:
            begin
                    write_ena = '1';
            end


И где-то описать эти регистры(Verilog только читаю, а не пишу на нем. Мог допустить ошибки. Передал суть):

Код
    always @ (posedge clk)
    begin
        if (write_ena)
            begin
                data_out[15:0] = 15'b0;
                addr[3:0] = 4'b0;
                wr = 1'b0;
            end
        
    end


Вот тут посмотрите : http://www.asic-world.com/tidbits/verilog_fsm.html
P.S. А почему такая старая версия Quartus ? Последняя версия, поддерживающая Cyclone III - Quartus 13.1.
bogaev_roman
to Sprite Посмотрел я на времянку и мозг отказал минут на 5 - как машина может находиться одновременно в нескольких состояниях? Там вообще мусор должен быть на моделировании - начальное состояние не определено - либо сброс добавьте, либо начальное условие при инициализации. Или это картинка не моделирования, а сигнал тап?
Sprite
Ошибся я немного. Переписал state-машину:
Код
    // Output depends only on the state
    always @ (state, data_out, addr_reg, data_in)
    begin
        case (state)
            S0,S1,S2,S3:
            begin
                data_out[15:0] = [b]data_out[15:0];[/b]
                addr[3:0] = addr_reg[3:0];
                wr = 1'b0;
            end
            S4:
            begin
                data_out[15:0] = data_in[15:0];
                addr[3:0] = addr_reg[3:0];
                wr = 1'b1;
            end    
            
            default:
            begin
                data_out[15:0] = 15'b0;
                addr[3:0] = 4'b0;
                wr = 1'b0;
            end
        endcase
    end

Вместо data_out_reg[15:0] нужно было делать присвоение data_out[15:0] < data_out[15:0]. Естественно все заработало) Только вылезла целая куча варнингов, типа:
Код
Warning (10240): Verilog HDL Always Construct warning at MEM_CONTROLLER.v(27): inferring latch(es) for variable "data_out", which holds its previous value in one or more paths through the always construct

Как я понял квартус ругается на наличие latch, но именно так я и планировал.

RTL-схема:


и правильный waveform:



Конечно, некрасиво с варнингами, буду думать как устранить.
Flip-fl0p
Код
Как я понял квартус ругается на наличие latch, но именно так я и планировал.

А Вы читали, что я Вам писал ?
Latch - в 99% случаев это ошибка. Вы пытаетесь в комбинационном блоке сохранить значения. Ну Qurtus и сделал Вам защелку, как Вы и просили его.
Sprite
Цитата(Flip-fl0p @ May 10 2018, 15:13) *
Код
Как я понял квартус ругается на наличие latch, но именно так я и планировал.

А Вы читали, что я Вам писал ?
Latch - в 99% случаев это ошибка. Вы пытаетесь в комбинационном блоке сохранить значения. Ну Qurtus и сделал Вам защелку, как Вы и просили его.


Поправил) Вынес защелку в отдельный блок:
Цитата
module MEM_CONTROLLER
(
input clk, cs,
input [15:0] data_in,
output [15:0] data_out,
output [3:0] addr,
output wr
);
reg [15:0] data_out_reg;
reg [3:0] addr_reg;
reg b_write; //Если 1 - то записываем данные
reg wr_reg;

initial
begin
data_out_reg = 15'b0;
addr_reg = 4'b0;
end


reg [2:0] state;
parameter S0 = 0, S1 = 1, S2 = 2, S3 = 3, S4 = 4, S5 = 5;

always @ (posedge clk)
begin
case (state)

//---------------------------
// Ждем спад 1 cs
//---------------------------
S0:
if (cs==0)
state <= S1;
else
state <= S0;
//---------------------------
// Ждем фронт 1 cs, заносим адрес в регистр addr
// Если старший бит в адресе = 1 - то записываем данные,
// иначе - читаем
//---------------------------
S1:
if (cs)
begin
case (data_in[2:0])
3'b001 : addr_reg <= 4'b0001;
3'b010 : addr_reg <= 4'b0010;
3'b011 : addr_reg <= 4'b0100;
3'b100 : addr_reg <= 4'b1000;
default : addr_reg <= 4'b0000;
endcase

b_write <= data_in[15]? 1'b1:1'b0;
state <= S2;
end
else
state <= S1;
//---------------------------
// Ждем спад 2 cs
//---------------------------
S2:
if (cs==0)
state <= S3;
else
state <= S2;
//---------------------------
// Ждем фронт 2 cs, устанавливаем wr_reg
//---------------------------
S3:
if (cs)
if (b_write)
state <= S4;
else
state <= S0; //Читаем данные (выводим их на data_out)
else
state <= S3;
//---------------------------
// Устанавливаем wr_reg
//---------------------------
S4:
begin
wr_reg <= 1'b1;
state <= S5;
end
//---------------------------
// Сбрасываем wr_reg
//---------------------------
S5:
begin
wr_reg <= 1'b0;
state <= S0;
end
//---------------------------
endcase
end

//---------------------------
// Блок-защелка данных
//---------------------------
always @ (posedge clk)
begin
if (state == S4)
data_out_reg[15:0] = data_in[15:0];
end


//---------------------------
assign data_out = data_out_reg;
assign addr = addr_reg;
assign wr = wr_reg;
//---------------------------
endmodule


RTL-схема:



И нерабочий Waveform:



В итоге пришел к самому первому варианту( Один большой плюс: варнинги исчезли))
Marat Zuev
Цитата(Sprite @ May 10 2018, 09:38) *
Код
//---------------------------
// Блок-защелка данных
//---------------------------
always @ (posedge clk)
begin
  if (state == S4)
    data_out_reg[15:0] = data_in[15:0];
end
Это - не защёлка, а 15 параллельных триггеров:
Код
always @ (posedge clk)
    data_out_reg = state == S4 ? data_in : data_out_reg;

А, вообще, давайте Ваш проект (тем более, что он небольшой) сюда (с тестбенчем, конечно): будет время - можно повозиться.
Sprite
Цитата(Marat Zuev @ May 10 2018, 15:56) *
Это - не защёлка, а 15 параллельных триггеров:
Код
always @ (posedge clk)
    data_out_reg = state == S4 ? data_in : data_out_reg;

А, вообще, давайте Ваш проект (тем более, что он небольшой) сюда (с тестбенчем, конечно): будет время - можно повозиться.


Проект Во вложении. entity - test3.qpf Собирал в Quartus II 9.1, использовал для теста встроенный симулятор (файл test3.vwf)
Marat Zuev
Цитата(Sprite @ May 10 2018, 10:19) *
Проект Во вложении. entity - test3.qpf Собирал в Quartus II 9.1, использовал для теста встроенный симулятор (файл test3.vwf)
Ну, функциональное моделирование у Вас проходит безошибочно?
Если так, то ломающееся временное свидетельствует об асинхронщине (а она есть у Вас в модуле spi_slave), либо..
Не хотите для начала именно с асинхронщиной побороться, чтобы на примере этого проекта навсегда забыть о ней?

На всякий случай рекомендую и на метастабильность обратить внимание...
Jackov
Цитата(bogaev_roman @ May 10 2018, 10:49) *
to Sprite Посмотрел я на времянку и мозг отказал минут на 5 - как машина может находиться одновременно в нескольких состояниях? Там вообще мусор должен быть на моделировании - начальное состояние не определено - либо сброс добавьте, либо начальное условие при инициализации. Или это картинка не моделирования, а сигнал тап?

Квартусовский симулятор сам инициализирует все триггеры нулями, ибо разработчики ПЛИС гарантируют, что по концу конфигурирования все триггеры в ПЛИС будут сброшены. Я всё ищу такую опцию для ModelSim-а, а то запарился каждый раз сброс прописывать.
Marat Zuev
Цитата(Jackov @ May 10 2018, 17:01) *
Квартусовский симулятор сам инициализирует все триггеры нулями, ибо разработчики ПЛИС гарантируют, что по концу конфигурирования все триггеры в ПЛИС будут сброшены.
Привязка в таких вопросах к одному производителю и его ПО не есть достойное решение.
Цитата(Jackov @ May 10 2018, 17:01) *
Я всё ищу такую опцию для ModelSim-а, а то запарился каждый раз сброс прописывать.
Может, signal_force вас спасёт? Но, по-хорошему, все триггеры, включая FSM в проекте рекомендуется заводить со сбросами
(вот какой лучше: синхронизированный асинхронный или синхронный - каждый выбирает по себе )))
Jackov
Цитата(Marat Zuev @ May 10 2018, 21:57) *
Может, signal_force вас спасёт?

Примерно так и делаю. Но всё равно ведь ручками приходится. Хотелось бы чтобы он сам по умолчанке делал.
Marat Zuev
Цитата(Jackov @ May 10 2018, 19:40) *
Примерно так и делаю. Но всё равно ведь ручками приходится. Хотелось бы чтобы он сам по умолчанке делал.
Вот вы снова привязываетесь к инструменту. Комильфо проект (включая testbench) - инструментонезависимый.
Пишите так, чтобы и реализация и моделирование проходили бы одинаково и в Modelsime и в Active-HDL-е и где бы то ни было....
Sprite
Цитата(Marat Zuev @ May 10 2018, 19:21) *
Ну, функциональное моделирование у Вас проходит безошибочно?
Если так, то ломающееся временное свидетельствует об асинхронщине (а она есть у Вас в модуле spi_slave), либо..
Не хотите для начала именно с асинхронщиной побороться, чтобы на примере этого проекта навсегда забыть о ней?

На всякий случай рекомендую и на метастабильность обратить внимание...


Спасибо за дельный совет! Действительно, данные (регистр data_in[15:0]) поступавшие в модуль были несинхронизированы, поправил. По совету iosifk поставил ветку default в state-машину. Но ошибка в модуле по прежнему имеется, в железе data_out после 10-15 раза выдает неверные данные, разбираюсь дальше.
Marat Zuev
Цитата(Sprite @ May 11 2018, 04:32) *
Но ошибка в модуле по прежнему имеется, в железе data_out после 10-15 раза выдает неверные данные, разбираюсь дальше.
Пришлите "синхронизированный" модуль посмотреть.
Ошибка на временном моделировании так и осталась?
Кстати, а Вы умеете SignalTap-ом пользоваться?
Flip-fl0p
Зачем вообще проводить временную симуляцию ?
Это потраченное впустую уйма времени !
Проводите в Modelsim функциональное моделирование. Когда HDL описание работает как задумано, то описываете временные ограничения. И практически наверняка проект заработает сразу.
Sprite
Цитата(Marat Zuev @ May 11 2018, 11:40) *
Пришлите "синхронизированный" модуль посмотреть.
Ошибка на временном моделировании так и осталась?
Кстати, а Вы умеете SignalTap-ом пользоваться?
Моделировал в стандартном квартусовском симуляторе - ошибок нет, но при работе с железом ошибка присутствует: для этого специально поставил 2 компаратора на вход data_in и 2 компаратора на выход data_out, а сигналы с компараторов вывел на светодиоды платы. На входе данные всегда правильные, а выход после N передач выдает либо фиксированные данные, либо 0. SignalTap-ом пользоваться не умею.

Код
module spi_slave
(
    input CLK,
    input CS,
    input MOSI,
    input SCK,
    input [15:0] DATA_IN,
    
    output reg MISO,
    output reg [15:0] DATA_OUT
);

    reg [15:0]     data_receive_reg;
    reg [15:0]     data_send_reg;
    reg [3:0]     bit_counter;
    reg             miso_reg;

    //============================
    // По спаду CS заносим данные в
    // data_send_reg
    //============================
    always @(negedge CS)
    begin
        data_send_reg <= DATA_IN;
   end
    
    //============================
    // Если CS=1 - обнуляем bitcounter
    // По фронту SCK заносим данные
    // в регистр data_receive_reg
    //============================
    always @(negedge SCK, posedge CS)
        if (CS)
            bit_counter <= 4'b0000;
        else
        begin
            data_receive_reg[15-bit_counter] <= MOSI;
            bit_counter <= bit_counter + 4'b0001;    
        end
    //============================        
    // Если CS=1 - обнуляем MISO
    // По фронту SCK выводим данные
    //============================
    always @(posedge SCK, posedge CS)
        if (CS)
            miso_reg <= 1'b0;
        else
            miso_reg <= data_send_reg[15-bit_counter];
    
    //============================
    // Синхронизация данных
    //============================
    always @(posedge CLK)
    begin
        DATA_OUT = data_receive_reg;
        MISO = miso_reg;
    end
    //============================
endmodule
Flip-fl0p
Цитата(Sprite @ May 11 2018, 09:32) *

Жуть !
1. Фильтруйте входные внешние асинхронные сигналы от пологих фронтов, и возможного дребезга около фронтов.
2. Работайте только про одному фронту синхросигнала. Хотите выделить фронт сигнала - применяйте схемы детектора фронтов(гуглится легко). Так как сделано у Вас - в FPGA не делается.
3.
Код
    always @(negedge SCK, posedge CS)
        if (CS)
            bit_counter <= 4'b0000;
        else
        begin
            data_receive_reg[15-bit_counter] <= MOSI;
            bit_counter <= bit_counter + 4'b0001;    
        end

Так конечно можно. Но правильнее и красивее в сдвиговый регистр данные записывать. Записали и сдвинули. Приняли все биты - отдали данные в модуль, обрабатывающий слова.
Marat Zuev
Цитата(Sprite @ May 11 2018, 07:32) *
Моделировал в стандартном квартусовском симуляторе - ошибок нет, но при работе с железом ошибка присутствует: для этого специально поставил 2 компаратора на вход data_in и 2 компаратора на выход data_out, а сигналы с компараторов вывел на светодиоды платы. На входе данные всегда правильные, а выход после N передач выдает либо фиксированные данные, либо 0.
Значит, модель Ваша не совпадает с действительностью.
И уж если взялись, то возьмите осциллограф. Раз интерфейс отлаживаете - смотрите его на экране осциллоскопа, а не на мигании светодиодов.
Цитата(Sprite @ May 11 2018, 07:32) *
SignalTap-ом пользоваться не умею.
А вот это освойте: и сейчас пригодится, и в будущем. Начинать, например, можно отсюда.

И скрипт Вы, конечно, "синхронизировали" сильно. Сделайте так, как сказал Flip-fl0p.
Sprite
Цитата(Flip-fl0p @ May 11 2018, 13:44) *
Жуть !
1. Фильтруйте входные внешние асинхронные сигналы от пологих фронтов, и возможного дребезга около фронтов.
2. Работайте только про одному фронту синхросигнала. Хотите выделить фронт сигнала - применяйте схемы детектора фронтов(гуглится легко). Так как сделано у Вас - в FPGA не делается.
3. Но правильнее и красивее в сдвиговый регистр данные записывать. Записали и сдвинули. Приняли все биты - отдали данные в модуль, обрабатывающий слова.


Ошибку исправил, засинхронизировал сигнал CS, переписал модуль SPI как советовал Flip-fl0p. В железе все работает, но при компиляции куча варнингов, типа:
Код
Warning: Presettable and clearable registers converted to equivalent circuits with latches. Registers power-up to an undefined state, and DEVCLRn places the registers in an undefined state.
    Warning (13310): Register "SPIslave:inst16|data_in_reg[15]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[15]~_emulated" and latch "SPIslave:inst16|data_in_reg[15]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[14]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[14]~_emulated" and latch "SPIslave:inst16|data_in_reg[14]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[13]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[13]~_emulated" and latch "SPIslave:inst16|data_in_reg[13]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[12]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[12]~_emulated" and latch "SPIslave:inst16|data_in_reg[12]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[11]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[11]~_emulated" and latch "SPIslave:inst16|data_in_reg[11]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[10]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[10]~_emulated" and latch "SPIslave:inst16|data_in_reg[10]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[9]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[9]~_emulated" and latch "SPIslave:inst16|data_in_reg[9]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[8]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[8]~_emulated" and latch "SPIslave:inst16|data_in_reg[8]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[7]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[7]~_emulated" and latch "SPIslave:inst16|data_in_reg[7]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[6]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[6]~_emulated" and latch "SPIslave:inst16|data_in_reg[6]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[5]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[5]~_emulated" and latch "SPIslave:inst16|data_in_reg[5]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[4]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[4]~_emulated" and latch "SPIslave:inst16|data_in_reg[4]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[3]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[3]~_emulated" and latch "SPIslave:inst16|data_in_reg[3]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[2]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[2]~_emulated" and latch "SPIslave:inst16|data_in_reg[2]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[1]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[1]~_emulated" and latch "SPIslave:inst16|data_in_reg[1]~latch"
    Warning (13310): Register "SPIslave:inst16|data_in_reg[0]" is converted into an equivalent circuit using register "SPIslave:inst16|data_in_reg[0]~_emulated" and latch "SPIslave:inst16|data_in_reg[0]~latch"
Warning: Timing Analysis is analyzing one or more combinational loops as latches
    Warning: Node "inst16|data_in_reg[15]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[14]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[13]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[12]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[11]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[10]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[9]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[8]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[7]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[6]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[5]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[4]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[3]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[2]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[1]~latch|combout" is a latch
    Warning: Node "inst16|data_in_reg[0]~latch|combout" is a latch


Новый код ниже, не судите строго - только учусь)
Код
module SPIslave(
    input clk,
    input CS,
    input MOSI,
    input SCK,
    input [15:0] data_in,
    output reg MISO,
    output reg [15:0] data_out,
    output reg data_ready
    );

reg [15:0] data_receive;
reg [15:0] data_in_reg;
reg [4:0]  bit_counter;
reg fall_CS_reg;
wire fall_CS;

    //----------------------------------------
    // По спаду SCK принимаем данные
    //----------------------------------------
    always @ (negedge SCK, posedge CS)
        if (CS)
            bit_counter <= 5'b00000;
        else
        begin
            data_receive <= { data_receive[14:0], MOSI };
            bit_counter <= bit_counter + 5'b00001;
        end
    //----------------------------------------
    // Выделяем спад CS
    //----------------------------------------
    always @(posedge clk)
        fall_CS_reg <= CS;
    assign fall_CS = fall_CS_reg & ~CS;


    //----------------------------------------
    // По фронту CS заносим данные в data_in_reg
    // По фронту SCK сдвигаем данные
    //----------------------------------------
    always @ (posedge SCK, posedge fall_CS)
    begin
        if (fall_CS)
            data_in_reg <= data_in;
        else
        begin
            MISO <= data_in_reg[15];
            data_in_reg <= (data_in_reg << 1);
        end
    end    

    //----------------------------------------
    // По переполнению счетчика выводим данные и сигнал готовности
    //----------------------------------------
    always @ (posedge clk)
    begin
        if (bit_counter[4])
            begin    
                data_out <= data_receive;
                data_ready <= 1'b1;
            end
        else
            begin
                data_out <= data_out;
                data_ready <= 1'b0;
            end
    end
endmodule


Кстати, RTL-схема получилась довольно интересная:


andrew_b
У вас тут большие проблемы с CDC (clock domain crossing). Точнее, его просто нет.
Вы формируете fall_CS_reg по clk, а используете для SCK. Так делать нельзя.

Что там с латчами, сказать не берусь. Вроде криминала не вижу, но я Верилог воспринимаю плохо.
AnatolySh
Цитата(Sprite @ May 12 2018, 16:40) *
переписал модуль SPI как советовал Flip-fl0p.
Flip-fl0p советовал работать только от одного клока: у Вас это, как понимаю, clk.
И в списке чувствительности, помимо него, имеет право быть (для проекта комильфо) только, если, конечно, он Вам нужен, ещё асинхронный глобальный сброс.
А у Вас в этих списках зоопарк.
Кстати, боюсь предложить Вам ещё включить (если я правильно помню называние) Design Assistant )))
И пока перестаньте нас радовать Вашим RTL: давайте сначала код вылизывать.
Лучше выкладывайте экран функционального моделирования для этого модуля.
iosifk
Цитата(Sprite @ May 12 2018, 16:40) *
Ошибку исправил, засинхронизировал сигнал CS, переписал модуль SPI как советовал Flip-fl0p. В железе все работает, но при компиляции куча варнингов, типа:

Кстати, RTL-схема получилась довольно интересная:

Давайте с конца начнем...
Какой может быть интерес к "RTL-схема", если она потом оптимизируется и от нее мало что останется?
Ваш стиль кодирования "все в одном" приведет к мало-отлаженному проекту. Который будет трудно сопровождать...
На самом деле, все это гораздо проще ложится в набор автоматов:
нижний, который умеет только работать с битом,
средний, который умеет только передать-принять кадр, управляя при этом нижним,
верхний, который работает с сигналами типа CS и запускает средний на выполнение.

При этом, нижний может еще и фильтровать клоки и данные, если они приходят из SPI, а верхний может синхронизировать и фильтровать сигналы типа CS...
И когда к этому придете, то станет понятно, что "есть только один клок в проекте и сигнал разрешения - пророк его"... А попутно сделаете привязку асинхронных сигналов..
Потом сделайте еще параметры, в которых бы задавалось число клоков на один бит SPI отдельно для симуляции и отдельно для железа...Скажем, если для железа надо 100 клоков на бит, то дляя симуляции и 4-х хватит... Тогда симуляция будет делаться быстро..
Sprite
Добрый день всем и спасибо за конструктивную критику!

Вернул свой первоначальный вариант, немного доработав его (добавил сигнал data_ready)
Код
module spi_slave
(
    input CLK,
    input CS,
    input MOSI,
    input SCK,
    input [15:0] DATA_IN,
    
    output MISO,
    output reg [15:0] DATA_OUT,
    output reg data_ready
);

    reg [15:0]         data_receive_reg;
    reg [15:0]         data_send_reg;
    reg [4:0]          bit_counter;
    reg               miso_reg;

    //============================
    // По спаду CS заносим данные в
    // data_send_reg
    //============================
    always @(negedge CS)
    begin
        data_send_reg <= DATA_IN;
   end
    
    //============================
    // Если CS=1 - обнуляем bitcounter
    // По фронту SCK заносим данные
    // в регистр data_receive_reg
    //============================
    always @(negedge SCK, posedge CS)
        if (CS)
            bit_counter <= 4'b00000;
        else    
        begin
            data_receive_reg <= { data_receive_reg[14:0], MOSI };
            bit_counter <= bit_counter + 5'b00001;
        end
            
    //============================        
    // Если CS=1 - обнуляем MISO
    // По фронту SCK выводим данные
    //============================
    always @(posedge SCK, posedge CS, posedge bit_counter[4])
        if (CS)
            miso_reg <= 1'b0;
        else if (bit_counter[4])
            miso_reg <= 1'b0;
        else
            miso_reg <= data_send_reg[15-bit_counter];
    assign MISO = miso_reg;        
            
    //============================
    // Синхронизация данных
    //============================
    always @(posedge CLK)
    begin
        DATA_OUT = data_receive_reg;
        data_ready = (bit_counter[4])? 1'b1 : 1'b0;
    end
    //============================
endmodule


Жду конструктивной критики по коду!

Теперь немного вопросов:

Цитата(AnatolySh @ May 12 2018, 21:02) *
Flip-fl0p советовал работать только от одного клока: у Вас это, как понимаю, clk.
И в списке чувствительности, помимо него, имеет право быть (для проекта комильфо) только, если, конечно, он Вам нужен, ещё асинхронный глобальный сброс.
А у Вас в этих списках зоопарк.
Кстати, боюсь предложить Вам ещё включить (если я правильно помню называние) Design Assistant )))
И пока перестаньте нас радовать Вашим RTL: давайте сначала код вылизывать.
Лучше выкладывайте экран функционального моделирования для этого модуля.


1. По фронту clk работать можно, но как быть если частота clk = 36 МГц, а SPI CLK = 21МГц? Останется ли время на выделение фронтов? и обработку state-машин, как советовали Flip-fl0p и iosifk?
2. Для чего вводить сигнал асинхронного глобального сброса? Не совсем понимаю. Для установки триггеров в начальные значения? Каких именно?
3. Не совсем понял фразу "Какой может быть интерес к "RTL-схема", если она потом оптимизируется и от нее мало что останется?". Т.е. при добавлении в проект нового кода RTL-схема уже созданных блоков может быть изменена? Если это так, то как можно это предотвратить? И вообще как можно тогда быть уверенным, что любой идеальный код будет работать так, как это планировалось?
4. По совету AnatolySh поменял настройки вместо временного на функциональное моделирование.

Функциональная симуляция:



И RTL:




Проект с этим кодом прошил и поставил вчера на тест: с МК отправляю запись данных по адресу 2, затем считываю данные по этому адресу и сверяю с отправленным значением. Далее повторяю процедуру, но по этому же адресу пишу считываю и сверяю уже другие данные. Если данные отправленные и принятые не совпадают - то выставляю флаг ошибки. Пришел сегодня посмотреть результаты - отправлено принято всего 5 300 000 посылок, кол-во ошибок - 0. Результат вполне устраивает.
Но есть узкое место: Т.к. адрес и данные поступают двумя разными SPI-передачами по 16 бит, то в случае помехи по сигналу CS может произойти рассинхронизация в приеме дынных, проще говоря ПЛИС не поймет что пришло данные или адрес. Можно конечно ввести дополнительный сигнал синхронизации, но я думаю поступить по другому - держать CS в нуле на все 32 бита, пропуская последовательно адрес и данные.
nice_vladi
Цитата(Sprite @ May 13 2018, 07:48) *
Добрый день всем и спасибо за конструктивную критику!

Вернул свой первоначальный вариант, немного доработав его (добавил сигнал data_ready)
...


Я бы постарался отвязаться от работы на внешней тактовой частоте и завел бы внутри ПЛИС свою, с хорошим перетактированием, допустим, 100 МГц. Работать внутри ПЛИС на внешней частоте, асинхронной внутренним частотам ПЛИС - головная боль. Тем более, когда частоты невысокие и не составляет труда с помощью перетактирования затащить данные под внутреннюю частоту ПЛИС.

Тогда можно было бы очень просто продетектировать фронты тактовой частоты SPI и по ним (при условии опущенного CS) укладывать данные с шины данных в сдвиговый регистр. А окончание одной посылки отсчитывать флагом заполнения 3битного счетчика (он как раз 8 тактов насчитает).

Что-то вроде:

CODE


if (~CS) cnt <= cnt + 1'b1;
else cnt <= '0;

if (front & ~CS) shft_reg <= shft_reg << 1 | dat ;


if (&cnt) rdy_dat <= shft_reg;


И писанины меньше, и нагляднее. Ну и да: Questa/ModelSim - маст хев. Без них - никуда. Встроенный симулятор Квартуса - боль.
Flip-fl0p
Цитата
1. По фронту clk работать можно, но как быть если частота clk = 36 МГц, а SPI CLK = 21МГц? Останется ли время на выделение фронтов? и обработку state-машин, как советовали Flip-fl0p и iosifk?

У вас Cyclone III. Примените один блок PLL...

Цитата
2. Для чего вводить сигнал асинхронного глобального сброса? Не совсем понимаю. Для установки триггеров в начальные значения? Каких именно?

Для Cyclone III можно и без асинхронного глабального сброса. На данном чипе регистры можно принудительно инициализировать нужными значениями. Сделайте правильно без асинхронного сброса сначала...

Цитата
3. Не совсем понял фразу "Какой может быть интерес к "RTL-схема", если она потом оптимизируется и от нее мало что останется?". Т.е. при добавлении в проект нового кода RTL-схема уже созданных блоков может быть изменена? Если это так, то как можно это предотвратить? И вообще как можно тогда быть уверенным, что любой идеальный код будет работать так, как это планировалось?

RTL нужен только для того, чтобы посмотреть иерархию блоков, посмотреть какие блоки куда подключены. Ну и оценить приблизительно соответствует ли схема Вашему ожиданию. Но потом данная схема ложится на логические ячейки FPGA и от того RTL представления практически ничего не остается, т.к САПР всё это добро оптимизирует. Поэтому с точки зрения схемотехники - смотреть на RTL смысла нет.

Цитата
4. По совету AnatolySh поменял настройки вместо временного на функциональное моделирование.

Изучайте Modelsim. Это стандарт де-факто.

А вообще Вам уже говорили.
Сначала делайте автомат который принимает биты.
Он занимается только приемом битов.
Потом этот автомат переедает данные другому автомату, который уже обрабатывает слова. И.т.д. Сейчас Вы вернулись к неправильному варианту.

Вот есть сигнал SCK.
Вы его для начала пропускаете через цепочку синхронизаторов для подавления метастабильного состояния.
Затем пропускаете через простейший анти дребезг, чтобы удалить возможный дребезг сигнала.
Потом выделяете детектором фронта - нужный вам фронт.
И все это работает на системном клоке, который Вы формируете на PLL.
В итоге все синхронные always блоки будут содержать только @(posedge CLK)
iosifk
Цитата(Sprite @ May 13 2018, 10:48) *
Проект с этим кодом прошил и поставил вчера на тест: с МК отправляю запись данных по адресу 2, затем считываю данные по этому адресу и сверяю с отправленным значением. Далее повторяю процедуру, но по этому же адресу пишу считываю и сверяю уже другие данные. Если данные отправленные и принятые не совпадают - то выставляю флаг ошибки. Пришел сегодня посмотреть результаты - отправлено принято всего 5 300 000 посылок, кол-во ошибок - 0. Результат вполне устраивает.

Про все дела уже написали, но вот есть еще и небольшенькая такая тонкость...
Если дело поставлено именно так: " отправляю запись данных по адресу 2, затем считываю данные по этому адресу и сверяю с отправленным значением", причем без паузы и без выдачи на шину данных другого кода, то вполне возможно, что читаете Вы не содержимое ячеек памяти, а паразитный емкостной заряд на шине... А он конечно всегда будет таким, какой Вы и выставляли на этой шине... sm.gif
Так что надо "записать по правильному адресу", потом выдать что-то по любому "пустому месту", лишь бы на шинах поменялись данные, а только потом читать...
А если делать по хорошему, то в блок, который открывает шинники ПЛИС по 3-му состоянию, надо сделать задержку на время разряда емкости на линиях. Так хоть будет немного дольше, но ПЛИС будет греться значительно меньше...


Sprite
Добрый день всем!

Еще одна редакция модуля с учетом исправлений и замечаний:

Код
module SPI_slave2
(
    input clk,
    input cs,
    input mosi,
    input sck,
    input [15:0] data_in,
    
    output reg miso = 1'b0,
    output reg [15:0] adr_out = 32'd0,
    output reg [15:0] data_out = 32'd0,
    output reg adr_ready = 1'b0,
    output reg data_ready = 1'b0,
    output wr,
    output [15:0] bitcnt
);

    reg[1:0] sck_reg;
    reg[1:0] mosi_reg;
    reg[5:0] bit_counter;
    reg[15:0] data_send_reg;

    wire sck_risingEdge;
    wire sck_fallingEdge;
    wire mosi_data;

    //---------------------------------
    // Выделяем фронт и спад сигнала sck
    //---------------------------------
    always @(posedge clk)
    begin
        if(cs)
            sck_reg <= 2'b00;
        else
            sck_reg <= {sck_reg[0], sck};
    end
    assign sck_risingEdge = (sck_reg == 2'b01);
    assign sck_fallingEdge = (sck_reg == 2'b10);

    //---------------------------------
    // Выделяем сигнал mosi
    //---------------------------------
    always @(posedge clk)
    begin
        if(cs)
            mosi_reg <= 2'b00;
        else
            mosi_reg <= {mosi_reg[0], mosi};
    end
    assign mosi_data = mosi_reg[1];

    //---------------------------------
    // Если cs==1 - очищаем приемный регистр
    // и обнуляем счетчик.
    // Иначе по спаду sck принимаем данные
    // и инкрементируем счетчик.
    //---------------------------------
    always @(posedge clk)
    begin
        if(cs)
        begin
            adr_out <= 16'd0;
            data_out <= 16'd0;
            bit_counter <= 6'b000000;
        end
        else if (sck_fallingEdge)
        begin
            if (bit_counter[4])
                data_out <= {data_out[14:0], mosi_data};
            else
                adr_out <= {adr_out[14:0], mosi_data};
            bit_counter <= bit_counter + 6'b000001;
        end
    end

    //---------------------------------
    // Выделяем сигналы готовности адреса и данных
    //---------------------------------
    always @(posedge clk)
    begin
        adr_ready <= (~cs) && (bit_counter[4]);
        data_ready <= (~cs) && (bit_counter[5]);
    end
    //---------------------------------
    // Если cs==1 - заносим в регистр данные для посылки
    // Иначе сдвигаем данные и отправляем побитно
    //---------------------------------
    always @(posedge clk)
    begin
        if(cs)
        begin
            miso <= 1'b0;
            data_send_reg <= data_in;
        end    
        else if (sck_risingEdge)
        begin
            if (bit_counter[4])
            begin
                miso <= data_send_reg[15];
                data_send_reg <= {data_send_reg[14:0], 1'b0};
            end
        end
    end
    
    assign wr = adr_out[15];
    assign bitcnt = bit_counter;
    
endmodule

По совету nice_vladi и Flip-fl0p поставил pll, умножил частоту на 3 - получил 108Мгц. Провел функциональную симуляцию - все ОК. Частота SPI - 5 МГц, пока выше не поднимал. Итоговый Waveform:



В результате появились "слаки"(



Насколько это криминально - не знаю.

Файл констрейна:
Код
derive_clock_uncertainty
create_clock -name clk -period 36.1MHz [get_ports {clk}]
create_clock -name sck -period 5MHz [get_ports {SCK}]
create_generated_clock -name CLK_108MHz -source [get_ports {clk}] -multiply_by 3 -duty_cycle 50 [get_nets {inst19|altpll_component|auto_generated|wire_pll1_clk[0]}]
Flip-fl0p
Вы как-то очень избирательно читаете rolleyes.gif
PLL поставили - уже хорошо. Работаете только по одному CLK - отлично !
А вот про цепочку синхронизаторов для подавления метастабильного состояния забыли.
Да и простейший фильтр дребезга не мешало бы поставить. Сигналы Вашего SPI относительно FPGA имеют очень пологие фронты. Это надо фильтровать. А то задолбаетесь искать сбои в работе, когда они будут. А они будут будьте уверены rolleyes.gif
И да. Я бы не считал линию SCK полноценным клоком. Соответственно из констрейнов его убрать нафиг.
Flip-fl0p
Смотрите как я делаю:
1. У меня отдельный модуль антидребезга, где внутри есть цепочка синхронизации для подавления метастабильного состояния. Я параметрами задаю длину цепочки синхронизации и время фильтрации дребезга.
2. У меня отдельный модуль детектора фронтов, где я параметрами задаю тип детектируемого фронта.
Самое главное, что это у меня отдельные модули, которые отлажены и гарантированно работают. И если что-то у меня не работает, то я знаю, что 100% проблема не в них. Тем более, эти модули присутствуют в большинстве проектов. Зачем по 100 раз описывать одно и то же. Да ещё и тестировать их потом ?




PS. Не претендую на правильность. Но практика показала, что так быстрее и удобнее.
Сначала на бумаге набросал всю схему и все алгоритмы, потом перенес на HDL - более 50% проекта собрал из готовых библиотечных блоков собственной разработки
Sprite
Цитата(Flip-fl0p @ May 15 2018, 11:43) *
Вы как-то очень избирательно читаете rolleyes.gif
PLL поставили - уже хорошо. Работаете только по одному CLK - отлично !
А вот про цепочку синхронизаторов для подавления метастабильного состояния забыли.
Да и простейший фильтр дребезга не мешало бы поставить. Сигналы Вашего SPI относительно FPGA имеют очень пологие фронты. Это надо фильтровать. А то задолбаетесь искать сбои в работе, когда они будут. А они будут будьте уверены rolleyes.gif
И да. Я бы не считал линию SCK полноценным клоком. Соответственно из констрейнов его убрать нафиг.


Синхронизаторы - это хорошо) Подумаю над этим. Если у Вас есть готовый пример фильтра антидребезга - может поделитесь? Насчет SCK - согласен, только я его потому и описывал в констрейне, т.к. надоели всякие варнинги типа "found dedicated clock...".
Flip-fl0p
Цитата(Sprite @ May 15 2018, 10:52) *
Синхронизаторы - это хорошо) Подумаю над этим. Если у Вас есть готовый пример фильтра антидребезга - может поделитесь? Насчет SCK - согласен, только я его потому и описывал в констрейне, т.к. надоели всякие варнинги типа "found dedicated clock...".

Над синхронизаторами не думать надо, а ставить их !
Вот пример фильтра.
http://allaboutfpga.com/wp-content/uploads...gic_circuit.jpg
Можно описать в виде FSM, но мне в виде схемы он понятней. Легко сделать параметризируемым
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.