Подчиненное устройство SPI не работает, когда я следую спецификации, а когда нет?

Я написал подчиненное устройство SPI в Verilog. Есть некоторые реализации, но я все еще изучаю Verilog и цифровую логику в целом, поэтому я решил попробовать написать это самостоятельно.

Моя реализация работает. Но мне пришлось внести изменения, чтобы заставить его работать, и внесение изменений (я думаю) ставит мою реализацию в противоречие со спецификацией Motorola SPI. Мне интересно: правильно ли я считаю, что это странно, или я просто не понимаю, как что-то работает?

Сигналы SPI поступают по четырем проводам, которые называются SCK, SS, MOSI и MISO. В этом нет ничего удивительного. FPGA работает на частоте 12 МГц, а шина SPI работает на частоте 1 МГц. Стратегия состоит в том, чтобы сэмплировать шину SPI для каждой позиции тактового сигнала FPGA и отслеживать текущее и последнее значение для SCK и SS, чтобы я мог обнаруживать фронты. Я также пропускаю каждый сигнал через буферную регистрацию. Таким образом, логика всегда работает на два тактовых сигнала FPGA позади фактических событий: на тактовом сигнале 1 я фиксирую сигнал в буфере; на часах 2 я копирую его в регистр, который использую для логики, а на часах 3 я действую в соответствии с ним.

Я использую режим SPI 0. В соответствии со спецификацией SPI в режиме 0 ведомое устройство должно выбрать строку MOSI на позиции SCK, а затем передать на negedge SCK.

Я написал это именно так:

reg [511:0] data; // I exchange 512-bit messages with the master.

reg [2:0] SCK_buffer = 0;
always @(posedge clock) begin // clock is my FPGA clock
    SCK_buffer <= {SCK_buffer[1:0], SCK};
end 
wire SCK_posedge = SCK_buffer[2:1] == 2'b01;
wire SCK_negedge = SCK_buffer[2:1] == 2'b10;

reg [2:0] SS_buffer = 0;
always @(posedge clock) begin
    SS_buffer <= {SS_buffer[1:0], SS};
end
wire SS_posedge = SS_buffer[2:1] == 2'b01;
wire SS_negedge = SS_buffer[2:1] == 2'b10;
wire SS_active = ~SS_buffer[1];

reg [1:0] MOSI_buffer = 0;
always @(posedge clock) begin
    MOSI_buffer = {MOSI_buffer[0], MOSI};
end
wire MOSI_in = MOSI_buffer[1];     

assign MISO = data[511];

always @(posedge clock) begin 
    if (SS_active) begin
        if (SCK_posedge) begin
            // Clock goes high: capture one bit from MOSI.
            MOSI_capture <= MOSI_in;
        end
        else if (SCK_negedge) begin
            // Shift the captured MOSI bit into the LSB of data and output 
            // the MSB from data.  Note: MISO is a wire that outputs data[511].
            data <= {data[510:0], MOSI_capture};
        end
    end
end

Код должен работать так:

  1. Когда SS становится активным (низким), данные [511] уже выводятся на MISO через провод. (Здесь нет трех состояний, потому что я единственный, кто едет в автобусе.)
  2. При выдаче SCK ведомое устройство выбирает MOSI, а ведущее устройство получает данные [511] по MISO.
  3. При отсутствии SCK ведомое устройство сдвигает бит, выбранный из MOSI, в данные, заставляя новый бит перемещаться в позицию вывода MISO.

Когда я запускал эту версию, биты падали повсюду. Буквально половина 512-битных сообщений, которые я отправлял от ведущего к ведомому, оказалась поврежденной.

Я посмотрел на реализацию ведомого устройства SPI с открытым исходным кодом и заметил, что он не использует необходимость SCK. Поэтому я изменил свой код на этот:

always @(posedge clock) begin 
    if (SS_active) begin
        if (SCK_posedge) begin
            // Skip that "capture" business and just shift MOSI right into the
            // data reg.
            data <= {data[510:0], MOSI_in};
        end
    end
end

После внесения этого изменения все заработало отлично. Полностью пуленепробиваемый.

Но я думаю, а?

Я понимаю, что это нормально. MISO получает свое новое значение сразу после постановки SCK, и оно все еще присутствует в следующей постановке, когда мастер сэмплирует ее. Но что плохого в том, чтобы изменить MISO на negedge? Несмотря на то, что я запускаю два цикла FPGA позади шины SPI из-за буферизации, у меня все еще есть 12 тактов FPGA на каждый тик SCK, что означает, что у меня есть шесть циклов FPGA между SCK negedge и следующей позицией. Я должен вовремя разобраться в MISO, верно?

На практике все ли просто (в режиме SPI 0) обновляют MISO сразу после установки SCK и не беспокоятся о ней?

Спасибо!


person Willis Blackburn    schedule 13.08.2013    source источник


Ответы (1)


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

В любом случае, первое, что вам нужно сделать, это получить техническое описание главного устройства и выяснить, что оно должно делать. В идеале вы также должны получить осциллограф на SCLK, MOSI и SS и выяснить, каковы фактические основные временные параметры. Узнайте, когда мастер изменяет MOSI, и какова настройка MOSI и удержание края SCLK (положительное или отрицательное).

Ваш код на самом деле не делает выборку MOSI по переднему фронту SCK, а вторая версия не делает выборку по заднему фронту. Он сэмплирует где-то у края. Проблема в том, что у вас есть отдельные синхронизаторы на SCK и MOSI (и SS). Как правило, это рецепт сбоя, но в вашем случае он может сработать, а может и не сработать, в зависимости от конкретных сроков. Подумайте об этом таким образом. Если вы правы, и «MOSI получает свое новое значение сразу после появления SCK», то MOSI выполняется долгая настройка и короткое удержание, и ваша выборка не удастся, потому что к тому времени, когда вы увидите высокое значение на SCK (до 84 нс после того, как это произошло на самом деле), MOSI изменился и является недействительным.

Правильный способ сделать это - сэмплировать MOSI (и, возможно, SS) на F / F, синхронизируемый SCK, и использовать небольшую цифровую PLL для привязки к SCK, чтобы вы знали, когда читать выходные данные сэмплера. Если вы понимаете время, имеете огромную настройку и держитесь за все, что сэмплируете, то вместо этого вы можете сэмплировать SCK, как вы сейчас делаете. В этом случае вам не нужны синхронизаторы на MOSI и SS; создайте разрешающий сигнал для их выборки, когда они станут стабильными.

person EML    schedule 13.08.2013
comment
К сожалению, я написал, что MOSI получает свое новое значение сразу после позиции SCK ... когда я должен был написать MISO gets ... - person Willis Blackburn; 14.08.2013
comment
Вы написали, что общая идея состоит в том, что образцы чтения на другом фронте тактового сигнала на том, который сгенерировал данные, ... Я полностью согласен, и вот как (я думаю) моя первая реализация работала: Я сгенерировать данные о negedge SCK (точнее, сразу после обнаружения negedge), а мастер сэмплирует их на posedge. Но это не сработало. Это сработало только тогда, когда я изменил код для генерации данных сразу после обнаружения позы. - person Willis Blackburn; 14.08.2013
comment
Не могли бы вы объяснить, что вы имеете в виду под «Проблема в том, что у вас есть отдельные синхронизаторы на SCK и MOSI (и SS)»? Как бы объединить их в одно? - person Willis Blackburn; 15.08.2013
comment
Интересно, что когда я увеличил тактовую частоту FPGA с 12 МГц до 120 МГц, мое ведомое устройство SPI снова начало терять биты. Я собираюсь сегодня вечером поставить его на прицел и подробно проанализировать. (Прежде чем я это сделаю, мне нужно более короткое сообщение - 512 бит - это много, чтобы смотреть на маленький экран.) Моя первоначальная мысль заключалась в том, что с более медленными часами FPGA и буферизацией ввода мой ведомый был слишком медленным, чтобы реагировать на SCK negedge, поэтому изменение MISO на (предыдущей) posedge заставило его работать, в то время как с более быстрыми часами FPGA изменение posedge фактически нарушает попытку мастера сэмплировать предыдущий бит. - person Willis Blackburn; 15.08.2013