ADSP-BF538F: драйвер встроенной FLASH-памяти S29AL008D Печать
Добавил(а) microsin   

В процессоре ADSP-BF538F применяется встроенная FLASH-память на основе микросхемы S29AL008D (8 мегабит/1 мегабайт, организация 512K x 16 бита с возможностью чтения как 1M x 8 бит). Обычно эту память применяют для загрузки процессора (для хранения образа LDR-файла [3] кода программы). Чтобы использовать эту память для самопрограммирования (например, для хранения энергонезависимых констант или реализации загрузчика) необходимо реализовать алгоритм программирования этой памяти, описанный в даташите на микросхему [1]. К счастью, в составе библиотек VisualDSP есть код драйвера для этой микросхемы adi_S29AL004D_8D.c. Этот драйвер предназначен для программирования микросхем FLASH-памяти S29AL004D и S29AL008D.

Код драйвера adi_S29AL004D_8D.c можно найти в каталоге %ProgramFiles% \ Analog Devices \ VisualDSP 5.0 \ Blackfin \ lib \ src \ drivers \ flash \ S29AL004D_8D, заголовочный файл adi_S29AL004D_8D.h находится в каталоге %ProgramFiles% \ Analog Devices \ VisualDSP 5.0 \ Blackfin \ include \ drivers \ flash. Пример использования можно найти в проекте %ProgramFiles \ Analog Devices \ VisualDSP 5.0 \ Blackfin \ Examples \ ADSP-BF538F EZ-KIT Lite \ Flash Programmer \ InternalFlash (файл main.c).

Однако в самом драйвере adi_S29AL004D_8D нашел ошибку. Оригинальный код компилируется, но не работает. Неправильно срабатывает определение количества секторов Flash (gNumSectors) в функции GetCodes, потому что для этого вычисления используется функция GetFlashStartAddress, которая в свою очередь использует GetSectorStartEnd с параметром gNumSectors. Получается попытка поймать себя за хвост - из-за того, что изначально gNumSectors равно 0, то GetSectorStartEnd вычисляет адреса неправильно, следовательно GetFlashStartAddress возвращает неверный адрес, и в результате тип микросхемы определяется неправильно и gNumSectors получает ошибочное значение 11 вместо 19.

Исправление ошибки:

unsigned long GetFlashStartAddress( unsigned long ulAddr)
{
   /*   unsigned long ulFlashStartAddr;  // начальный адрес flash
   unsigned long ulSectStartAddr;   // начальный адрес сектора
   unsigned long ulSectEndAddr;     // конечный адрес сектора
   unsigned long ulMask;            // маска смещения
 
   // Вычисление начального адреса flash от абсолютных адресов.
   GetSectorStartEnd( &ulSectStartAddr, &ulSectEndAddr, (gNumSectors-1));
   ulMask            = ~(ulSectEndAddr);
   ulFlashStartAddr  =  ulAddr & ulMask;
   return(ulFlashStartAddr);   */
 
   return 0x20000000;   // Сюда подставляется адрес FLASH, который он занимает
                        // в адресном пространстве ADSP-BF538F.
}

[Принцип работы драйвера adi_S29AL004D_8D]

Драйвер реализует чтение и запись микросхем S29AL004D и S29AL008D на низком уровне, порциями по 2 байта (через одномерный буфер ADI_DEV_1D_BUFFER). Запись сделана блокирующей с ожиданием готовности микросхемы, т. е. для записи каждых 2 байт прерывания запрещаются, выполняется запись, и процессор в функции PollToggleBit выполняет пустые циклы опроса с ожиданием, пока операция записи не завершится.

Запись осуществляется нулевыми битами, т. е. если бит ячейки памяти установлен в лог. 0, то записать в единицу его уже нельзя. Таким образом, данные успешно запишутся произвольными данными только в том случае, если исходное состояние всех байт ячеек данных в 0xFF. Поэтому, если нужно поменять хотя бы один байт таким образом, чтобы у него некоторые биты перешли из состояния лог. 0 в лог. 1, то это будет возможно только если предварительно стереть весть сектор FLASH-памяти целиком.

Драйвер adi_S29AL004D_8D в терминологии ADI это стандартный драйвер, и управляется он обычным образом [2], как это принято в модели библиотек Системных Служб (System Services Libraries, SSL) компании Analog Devices.

adi_dev_Open открывает драйвер и выполняет его необходимую инициализацию.

adi_dev_Control конфигурирует драйвер - устанавливает способ обмена данными, обеспечивает управление драйвером и получение от него служебной информации (информация о микросхеме, количество секторов памяти FLASH, начальный и конечный адрес нужного сектора, стирание всей микросхемы или конкретного сектора, получение номера сектора по абсолютному адресу).

adi_dev_Read выполняет чтение данных из микросхемы FLASH.

adi_dev_Write выполняет запись данных в микросхему FLASH.

adi_dev_Close закрывает драйвер и освобождает выделенные под него ресурсы.

[Пример Flash Programmer \ InternalFlash]

Пример проекта Flash Programmer \ InternalFlash для процессора ADSP-BF538 показывает, как использовать драйвер adi_S29AL004D_8D для программирования микросхем FLASH-памяти S29AL004D и S29AL008D. Показана инициализация драйвера и реализация функций для манипулирования содержимым памяти микросхем (см. таблицу ниже).

Функция
Назначение
GetNumSectors Получает информацию о количестве секторов микросхемы.
GetSectorMap Заполняет массив информации о секторах, в котором хранятся начальный и конечный адрес каждого сектора микросхемы. ИМХО довольно бесполезная функция.
GetFlashInfo Получает у драйвера информацию о микросхеме - коды производителя и устройства, и их текстовое описание.
SetupForFlash Бесполезная функция, она просто вызывает GetNumSectors.
FillData Заполняет память FLASH одинаковыми данными с заданным шагом. Также бесполезная функция.
ReadData Читает память FLASH с заданным шагом.
WriteData Записывает память FLASH с заданным шагом.

Пример получился перегружен ненужным кодом и имеет ошибки. Что не так:

1. Реализован ненужный код, который проверяет записанные в память данные. Ветки с проверкой и без проверки данных дублируются, что приводит к разрастанию кода. 

2. В функции WriteData есть ошибка - в цикле записи данных вызывается adi_dev_Control вместо adi_dev_Write, в результате чего ветка, которая должна работать без проверки памяти при записи на самом деле ничего в память не пишет.

3. Код функций FillData и WriteData неоправданно запутан.

4. Используется динамическое выделение памяти под составление карты памяти секторов микросхемы, что ИМХО не нужно, потому что память все равно обычно записывается в пределах одного сектора, и достаточно вызвать один раз функцию определения начального и конечного адреса каждого сектора в процессе чтения и записи.

5. Функция чтения данных ReadData фактически бесполезна, поскольку микросхема все равно включена в адресное пространство, и её данные можно читать напрямую.

6. Для чтения / записи данных выделяется динамический буфер размером в 0x600 байт, что ИМХО также бесполезно, поскольку не дает никакого прироста производительности при записи, а чтение можно осуществлять напрямую обращением к адресному пространству.

[Ссылки]

1. S29AL008D 8 Mbit (1M x 8-Bit / 512 K x 16-Bit), 3 V Boot Sector Flash site:cypress.com.
2. VDK: драйверы устройств и системные службы процессоров Blackfin.
3. Как происходит загрузка ADSP-BF533 Blackfin.