В процессоре 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. |