EEPROM
Ошибка чтения по AHB после записи, стирания и чтения по APB
Описание
Если выполняется несколько чтений подряд с одного и того же адреса по шине AHB, то второе и последующие чтения выполняются без реальной выборки данных из EEPROM, а на выходе блока EEPROM хранится последнее прочитанное значение.
При операции записи или стирания на выходе блока EEPROM формируется 0. Часть контроллера EEPROM, отвечающая за взаимодействия с AHB, не отрабатывает такую ситуацию и считает, что на выходе EEPROM по-прежнему валидные данные. Затем при повторном чтении после записи или стирания будет считан 0.
Аналогичная ситуация возникает, если между чтениями по AHB было чтение из EEPROM через регистры контроллера по интерфейсу APB. В этом случае на выходе EEPROM будут считанные данные.
Таким образом, если программа выполняется из EEPROM, а затем вызывается функция из RAM, то в конвейер загружается следующая инструкция из EEPROM, которая не выполняется и отбрасывается. При возврате из функции RAM инструкция из EEPROM считана не будет, так как была считана ранее при входе в функцию и отброшена. Если в функции проводилась операция чтения или стирания EEPROM, то на выходе EEPROM будет 0, который будет считан при выходе из функции, что вызовет исключение. Если внутри функции выполнить чтение EEPROM через регистры по интерфейсу APB, то считанное значение останется на выходе блока и будет считано ядром при выходе из функции, что также может вызвать исключение.
Обход
Для того чтобы значение на выходе EEPROM обновилось и стало валидным нужно выполнить чтение по AHB по любому адресу, отличному от последнего прочитанного. Рекомендуется выполнить две операции чтения по AHB по двум разным адресам, что гарантирует валидное значение на выходе EEPROM независимо от последнего прочитанного адреса.
Пример для функции в RAM, которая вызывается из EEPROM и содержит операции записи, стирания или чтения через регистры контроллера EEPROM:
#define RAM_ATTR __attribute__((section(".ram_text")))
RAM_ATTR void eeprom_write_word(uint32_t data, uint32_t address)
{
/* Стирание */
EEPROM_REGS->EECON = EEPROM_EECON_BWE_M;
EEPROM_REGS->EEA = address;
EEPROM_REGS->EEDAT = 0;
EEPROM_REGS->EECON |= EEPROM_EECON_WRBEH(EEPROM_WRBEH_SINGLE) |
EEPROM_EECON_OP(EEPROM_EECON_OP_ER) |
EEPROM_EECON_EX_M;
while ((EEPROM_REGS->EESTA & EEPROM_EESTA_BSY_M) != 0)
;
/* Запись */
EEPROM_REGS->EECON = EEPROM_EECON_BWE_M;
EEPROM_REGS->EEA = address;
EEPROM_REGS->EEDAT = data;
EEPROM_REGS->EECON |= EEPROM_EECON_WRBEH(EEPROM_WRBEH_SINGLE) |
EEPROM_EECON_OP(EEPROM_EECON_OP_PR) |
EEPROM_EECON_EX_M;
while ((EEPROM_REGS->EESTA & EEPROM_EESTA_BSY_M) != 0)
;
// Два чтения для гарантированного считывания инструкции из EEPROM
// при выходе из функции.
asm volatile(
"lw x0,0(%0)\n\t"
"lw x0,4(%0)\n\t"
:
: "r"(EEPROM_BASE_ADDRESS));
}