Показания SPI смещены, несовместимы (ведущее устройство nRF, подчиненное устройство FPGA)

Я постараюсь максимально упростить свой проект, чтобы сделать это понятным. Я подключаю плату nRF52 PCA10040 к FPGA iCE5LP (Lattice).

У меня проблема с байтами, поступающими на MCU (линия MISO), вот порядок действий:

  1. Мастер отправляет команду через SPI
  2. Ведомый выполняет команду и подготавливает данные к передаче
  3. После достаточно долгого ожидания обработки команды и подготовки данных мастер отправляет фиктивные байты для получения регистра tx от FPGA.
  4. Данные выглядят сдвинутыми и противоречивыми.

Данные правильно загружены, в соответствии с симуляцией. При каждой второй передаче я получаю ожидаемый байт, но сдвинутый на один бит. См. следующие скриншоты;

Это симуляция: МоделированиеЭто вывод логического анализатора: Лос-АнджелесЧто я ожидаю: (это процедура инициализации eMMC)

  • 0xC0FF8080 или 0x40FF8080 в зависимости от результата инициализации

Что я получаю случайно:

0xE07FC040 (0xC0FF8080 >> 1) или 0x007F8040 (0x00FF0080 >> 1).

static uint8_t       m_rx_buf[4];        /**< RX buffer. */
static uint8_t       m_rx_buf_ext[6];    /**< RX buffer. */
static uint8_t       m_tx_buf[] = {0x00, 0xaa, 0xbb, 0xcc};           /**< TX buffer. */


/**
 * @brief SPI user event handler.
 * @param event
 */
void spi_event_handler(nrf_drv_spi_evt_t const * p_event) {
    spi_xfer_done = true;
    NRF_LOG_PRINTF("Received: %x %x %x %x\n", m_rx_buf[0], m_rx_buf[1], m_rx_buf[2], m_rx_buf[3]);
}



/**
 * Function to send a command to a specific module.
 * @param module  : Module to communicate with
 * @param command : Command to send
 */
ret_code_t sendCommand(uint8_t module, uint8_t command, uint8_t mmc_command, uint32_t mmc_arg) {
    ret_code_t err_code;
    uint8_t firstByte = module + command;
    if (command > 31 || module > 7) {
        return APP_ERROR_INVALID_CMD;
    }

    uint8_t msg_a[] = {firstByte, mmc_command, mmc_arg >> 24, mmc_arg >> 16,  mmc_arg >> 8, mmc_arg & 0xFF};
    uint8_t msg_length = sizeof(msg_a);

    memset(m_rx_buf_ext, 0, msg_length);

    spi_xfer_done = false;

    err_code = nrf_drv_spi_transfer(&spi, msg_a, msg_length, m_rx_buf_ext, msg_length);
    while (!spi_xfer_done) {
        __WFE();
    }
    if (err_code != NRF_SUCCESS) {
        NRF_LOG_PRINTF("Error during transfer : %d\n", err_code);
    }
    return err_code;
}


ret_code_t initMMC() {
    ret_code_t err_code;
    bool mmc_initialized = false;

    nrf_delay_us(200);  /**< Wait for more than 74 clock cycles before issuing a command. */
    err_code = sendCommand(MODULE_CMD_SEND, CMD_SEND_CMD_TO_MMC, CMD0, 0);

    while (!mmc_initialized) {
        nrf_delay_us(300);
        err_code = sendCommand(MODULE_CMD_SEND, CMD_SEND_CMD_TO_MMC, CMD1, ARG_BUS_INIT);

        nrf_delay_us(600);

        memset(m_rx_buf, 0, 4);
        spi_xfer_done = false;

        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, m_tx_buf, 4, m_rx_buf, 4));

        while (!spi_xfer_done)
        {
            __WFE();
        }

        if (m_rx_buf[0] == 0xC0 || m_rx_buf[0] == 0x80 || m_rx_buf[0] == 0xE0) {
            NRF_LOG_PRINTF("eMMC Initialized.\n");
            mmc_initialized = true;
        }
        nrf_delay_ms(1000);
    }
    return err_code;
}

void init_clock() {
    NRF_CLOCK->LFCLKSRC            = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_LFCLKSTART    = 1;
    while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0); // Wait for clock to start
}


int main(void) {
    APP_ERROR_CHECK(NRF_LOG_INIT());
    init_clock();
    LEDS_CONFIGURE(LEDS_MASK);
    LEDS_OFF(LEDS_MASK);
    APP_TIMER_INIT(APP_TIMER_PRESCALER,APP_TIMER_OP_QUEUE_SIZE,NULL);
    APP_ERROR_CHECK(nrf_drv_gpiote_init());
    buttons_init();
    NRF_LOG_PRINTF(NRF_LOG_COLOR_RED"\nSTARTING.\r\n"NRF_LOG_COLOR_DEFAULT);

    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(SPI_INSTANCE);
    spi_config.ss_pin = SPI_CS_PIN;
    spi_config.frequency = NRF_SPI_FREQ_4M;
    spi_config.mode = NRF_DRV_SPI_MODE_0;
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));

    resetFPGA(RST_PIN);         //Pulls up fpga's reset pin for 5ms then down
    nrf_delay_ms(1000);

    APP_ERROR_CHECK(initMMC());
    while(1) {
        __WFE();
    }
}

У вас есть идея, что я делаю неправильно? Большое спасибо!

Вы абсолютно уверены, что часы синхронизированы?
Какие часы вы имеете в виду? Часы MCU и часы SPI, генерируемые MCU, идеально синхронизированы да
Как насчет SPI и FPGA?
Какие скорости вы используете в любом случае?
Тактовая частота FPGA составляет 12 МГц, SPI — 4 МГц. eMMC для инициализации тактируется до 400 кГц (но это не имеет значения). На самом деле они не синхронны, так как мне нужен один тактовый цикл FPGA для обнаружения фронта на spi-часах.
Вы пытались увеличить тактовую частоту FPGA (если это возможно) или замедлить скорость SPI? Какие результаты? Хотя разница должна быть достаточно большой, я не совсем уверен.
Нет, тактовая частота FPGA составляет максимум 12 МГц с внутренним генератором. Пробовал, замедлял до 2 и 3 МГц... ничего не изменилось. У меня установлено значение 1, и оно работает, почему? Я думал, что мне нужна как минимум половина частоты основных часов, поэтому ниже 6 МГц, почему он работает с 1, а не с 2 МГц?

Ответы (2)

Разницы между вашей скоростью захвата и скоростью передачи данных недостаточно. Увеличение скорости FPGA или уменьшение скорости SPI решает проблему выравнивания.

Ваши часы SPI и часы FPGA не синхронизированы. Это означает, что часы FPGA должны работать как минимум в два раза быстрее, чем часы SPI. Однако, поскольку SPI является внешним сигналом, он может быть не совсем чистым. Вероятно, есть джиттер на ваших часах и/или данных. Фактор 2 в качестве разницы просто сокращает его слишком близко.

У вас есть коэффициент 3. Если вы сомневаетесь в достоверности ваших сигналов, имейте в виду, что он близок к абсолютному минимуму. Увеличьте коэффициент. Как вы указали, фактор 12 работает. Ваш порог, вероятно, равен 8 (12/8 = 1,5 МГц).

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

График вашего логического анализатора не имеет необходимого разрешения. Но MISO и MOSI, похоже, не совпадают по фазе на половину цикла.

Обычно есть 4 режима, в которых работают шины SPI . В одном ожидается, что данные изменятся по заднему фронту, а выборка будет производиться по переднему фронту тактового сигнала. В другом все наоборот. Если ведущий и ведомый не были установлены одинаково, результат непредсказуем. Однако битовый сдвиг на 1 является разумным результатом такого несоответствия.

Спасибо за ваш ответ, я тоже изучил это на самом деле. Но и ведомый, и ведущий настроены на работу в режиме 0,0. Я думаю, что проблема «не в фазе» была симптомом ошибки, отмеченной в приведенном выше ответе. Этого больше не происходит