BEKEN: как читать и записывать память FLASH Печать
Добавил(а) microsin   

Физическая память FLASH микроконтроллеров BK72xx устроена довольно непривычным образом. Каждый блок памяти в 32 байта снабжен двухбайтовой контрольной суммой CRC16, вычисленной по полиному 0x8005 с начальным значением 0xFFFF.

[                        блок 32 байта                       ][CRC16]
[                        блок 32 байта                       ][CRC16]
[                        блок 32 байта                       ][CRC16]
...
[                        блок 32 байта                       ][CRC16]

Код из репозитория RT-Thread для микроконтроллера BK7231N:

/*
 * Контрольная сумма CRC16 для 32-байтных блоков микроконтроллеров BEKEN.
 * Старший байт (MSB) идет первым, полином 8005, начальное значение FFFF.
*/
unsigned short CRC16_CCITT_FALSE(unsigned char *puchMsg, unsigned int usDataLen)
{  
   unsigned short wCRCin = 0xFFFF;
   unsigned short wCPoly = 0x8005;
   unsigned char wChar = 0;
   int i;
 
   while (usDataLen--)
   {
      wChar = *(puchMsg++);
      wCRCin ^= (wChar << 8);
      for(i = 0;i < 8;i++)
      {
         if(wCRCin & 0x8000)
            wCRCin = (wCRCin << 1) ^ wCPoly;
         else
            wCRCin = wCRCin << 1;
      }
   }
   return (wCRCin); 
}

Чтение FLASH. Память программ и данных находится в памяти FLASH, и она доступна для ядра процессора в логическом виде, без контрольных сумм CRC16. Аппаратура доступа к памяти на лету преобразует логические адреса в физические:

[                        блок 32 байта                       ]
[                        блок 32 байта                       ]
[                        блок 32 байта                       ]
...
[                        блок 32 байта                       ]

В коде программы нельзя напрямую читать и записывать адресное пространство flash, иначе (случайным образом) может происходить непредсказуемое поведение, обычно заканчивающееся зависанием с перезагрузкой по сторожевому таймеру. Для чтения памяти необходимо использовать специальный драйвер в модуле flash.c, который находится в каталоге ./beken378/driver/flash репозитория [1]. Ниже показан пример чтения физического содержимого памяти, вместе с контрольными суммами CRC16. Для вызова функций из модуля flash.c используется промежуточная модель драйвера на основе функций ddev_open, ddev_read, ddev_close.

uint8_t xbuf[EF_ERASE_MIN_SIZE];
 
// Вывод физического содержимого памяти FLASH MCU BK7231N:
static int xdump (int argc, char **argv)
{
   uint32_t address, len;
   UINT32 status;
   DD_HANDLE flash_handle;
 
   switch (argc)
   {
   case 2:
      sscanf(argv[1], "%X", &address);
      len = 64;
      break;
   case 3:
      sscanf(argv[1], "%X", &address);
      sscanf(argv[2], "%X", &len);
      break;
   default:
      bk_printf("Usage: %s addrhex [lenhex]\n", argv[0]);
      return 1;
   }
 
   flash_handle = ddev_open(FLASH_DEV_NAME, &status, 0);
   ddev_read(flash_handle, (char *)xbuf, len, address);
   dump_hex(address, (void*)xbuf, len);
   ddev_close(flash_handle);
 
   return 0;
}
 
MSH_CMD_EXPORT(xdump, dump physical FLASH memory);

Пример вывода физического содержимого первых 64 байт FLASH, где находится код загрузчика:

msh />xdump 0 40
00000000: AA 00 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5  ................
00000010: 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5  ................
00000020: 16 CE B8 05 00 00 4C 05 00 00 C8 05 00 00 D8 05  ......L.........
00000030: 00 00 E8 05 00 00 5C 05 00 00 6C 05 00 00 EF BE  ......\...l.....

Для практического использования данных FLASH в программе, чтобы читать и записывать данные не по физическим, а по логическим адресам, удобнее другой интерфейс, реализованный в модуле fal_flash_beken7231_crc_port.c (находится в каталоге samples/ota/port/). Пример чтения логического содержимого памяти FLASH:

// Вывод логического содержимого памяти FLASH MCU BK7231N:
static int dump (int argc, char **argv)
{
   uint32_t logaddress, len, portion;
   uint8_t buf[32];
 
   switch (argc)
   {
   case 2:
      sscanf(argv[1], "%X", &logaddress);
      len = 64;
      break;
   case 3:
      sscanf(argv[1], "%X", &logaddress);
      sscanf(argv[2], "%X", &len);
      break;
   default:
      bk_printf("Usage: %s addrhex [lenhex]\n", argv[0]);
      return 1;
   }
 
   fal_flash_dev_t ffd = (fal_flash_dev_t)&beken_onchip_flash_crc;
   ffd->ops.read(FLASH_NVSETTINGS_ADDR, (uint8_t*)&nvdata, sizeof(nvdata));
   while(len)
   {
      portion = len < 32 ? len : 32;
      ffd->ops.read(logaddress, buf, portion);
      dump_hex(logaddress, buf, portion);
      logaddress += 32;
      len -= portion;
   }
   return 0;
}
 
MSH_CMD_EXPORT(dump, dump logical FLASH memory);

Пример вывода логического содержимого первых 64 байт FLASH, где находится код загрузчика:

msh />dump 0 40
00000000: AA 00 00 EA 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5  ................
00000010: 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5 14 F0 9F E5  ................
00000020: B8 05 00 00 4C 05 00 00 C8 05 00 00 D8 05 00 00  ....L...........
00000030: E8 05 00 00 5C 05 00 00 6C 05 00 00 EF BE AD DE  ....\...l.......

Обратите внимание, что в этом примере вывода по адресу 00000020 отсутствут байты CRC16 от предыдущего блока 32 байт.

Запись FLASH. Запись в память FLASH реализуется аналогично, либо по физическим адресам через интерфейс flash.c, либо по логическим адресам через интерфейс fal_flash_beken7231_crc_port.c. Ниже приведен код записи физическим адресам. Он дан только для примера, это ошибочный код, потому что здесь блоки по 32 байта перед записью не снабжаются контрольной суммой CRC16.

// Запись физического содержимого FLASH:
void Save (void)
{
   UINT32 status;
   DD_HANDLE flash_hdl;
   GLOBAL_INT_DECLARATION();
   uint32_t address, len;
 
   nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t));
   flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0);
   if (DD_HANDLE_UNVALID == flash_hdl)
   {
      os_printf("%s open failed\r\n", __FUNCTION__);
      return;
   }
 
   // Снятие защиты на запись:
   bk_flash_enable_security(FLASH_PROTECT_NONE);
   
   // Перед записью записываемый сектор необходимо стереть:
   address = FLASH_NVSETTINGS_ADDR;     // физический адрес FLASH
   len = EF_ERASE_MIN_SIZE;
   GLOBAL_INT_DISABLE();                // запрет прерываний
   ef_port_erase(address, len);
   
   nvdata.seed = rt_tick_get();
   nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t));
   // Запись по данных физическому адресу FLASH_NVSETTINGS_ADDR:
   ddev_write(flash_hdl, (char*)&nvdata, sizeof(nvdata), FLASH_NVSETTINGS_ADDR);
   GLOBAL_INT_RESTORE();                // восстановление прерываний
   
   // Возобновление защиты на запись:
   bk_flash_enable_security(FLASH_UNPROTECT_LAST_BLOCK);
   ddev_close(flash_hdl);
}

Для работы с памятью по физическим адресам на чтение и запись удобно использовать интерфейс драйвера fal_flash_beken7231_crc_port.c. Он сам заботится о добавлении контрольных сумм CRC16 в записываемые данные и об остановке и возобновлении прерываний. Ниже показан практический пример записи в FLASH по логическим адресам с помощью интерфейса erase и write драйвера. Имейте в виду, что перед использованием интерфейса этого драйвера необходимо вызывать функцию fal_init (обычно при старте приложения). Как видно, код получился намного проще:

// Запись логического содержимого FLASH:
void Save (void)
{
   fal_flash_dev_t ffd = (fal_flash_dev_t)&beken_onchip_flash_crc;
   bk_flash_enable_security(FLASH_PROTECT_NONE);
   ffd->ops.erase(FLASH_NVSETTINGS_ADDR, sizeof(nvdata));
   nvdata.seed = rt_tick_get();
   nvdata.CRC32 = ef_calc_crc32(0, &nvdata, sizeof(TNVsettings)-sizeof(uint32_t));
   ffd->ops.write(FLASH_NVSETTINGS_ADDR, (uint8_t*)&nvdata, sizeof(nvdata));
   bk_flash_enable_security(FLASH_UNPROTECT_LAST_BLOCK);
}

[Ссылки]

1. bekencorp / bdk_rtt site:github.com.