ПЛИС
ПЛИС\Удаленная прошивка ПЛИС\Драйвер Remote Update System
Драйвер Remote Update System для процессора NIOS
//******************************************
//******************************************
// Драйвер Remote Update System
// Devices: Arria GX, Stratix II, Stratix II GX, Stratix III, Cyclone III, Cyclone IV
// Configuration Mode: Active Serial (AS)
//******************************************
//******************************************
// Описание аргументов функций
//******************************************
// s2_ru_base_addr - Базовый адрес регистров управления
// s2_ru_num_par - Номер параметра
// s2_ru_val_par - Значение параметра
// s2_ru_wdt_tomeout - Значение тайм-аута сторожевого таймера = s2_ru_wdt_tomeout * 2^17
// В функции S2Ru_TryReconfigToApplication, если s2_ru_wdt_tomeout равен 0, то сторожевой таймер не используется.
// s2_ru_config_page - Биты адреса страницы памяти
// s2_ru_config_mode - Признак типа прошивки (0 - Factory, 1 - Application)
// ru_addr_reconfig - Адрес Application-прошивки, которую требуется загрузить:
// Для Arria GX, Stratix II, Stratix II GX : StartAddress[23..0] = {1'b0, PGM[6..0], 16'b0}
// Для Stratix III : StartAddress[23..0] = {PGM[23..0]}
// Для Cyclone III : StartAddress[23..0] = {PGM[21..0], 2'b0}
// где PGM[] - значение, загружаемое в регистр (передаваемое через переменную ru_addr_reconfig)
// eeprom_status_config_addr - адрес байта-признака прошивки в памяти EEPROM
// epcs_application_config_addr - Адрес Application-прошивки, которую требуется загрузить (идентичен описанию аргумента ru_addr_reconfig)
// ru_wdt_timeout - Значение тайм-аута сторожевого таймера (идентичен описанию аргумента s2_ru_wdt_tomeout)
//******************************************
// Parameters
//******************************************
//Глобальные параметры
#define RU_STRATIX_II 1
#define RU_STRATIX_II_GX 1
#define RU_ARRIA_GX 1
#define RU_STRATIX_III 2
#define RU_CYCLONE_III 3
#define RU_CYCLONE_IV 4
#include "main.h" //Можно отключить, если не используется драйвер EEPROM как в примере ниже (если не определено USE_RU_CONFIG_LIB1)
//Настройки драйвера
#ifndef RU_DEVICE_FAMILY //Если не задано семейство ПЛИС, по умолчанию используется Stratix II
#define RU_DEVICE_FAMILY RU_STRATIX_II
#endif
//#define USE_RU_CONFIG_LIB1 - Использовать библиотеку готовых функций (требуется наличие драйвера EEPROM)
//#define ENABLE_NSTATUS_CHECKING - признать ошибкой, если nSTATUS активирован внешним устройством как результат ошибки
//#define ENABLE_LOGIC_CHECKING - признать ошибкой, если текущая конфигурация была инициирована логикой
//#define ENABLE_NCONFIG_CHECKING - признать ошибкой, если текущая конфигурация была инициирована внешним сбросом (выводом nCONFIG)
//Настройки контроллера
#define NUM_PARAMETERS 8
#define ADDR_PARAMETERS 0
#define ADDR_PAR_SPAN (NUM_PARAMETERS-ADDR_PARAMETERS)
#define ADDR_FLAG_STATUS 8
#define MASK_RU_DATA 0x7FFFFFFF
#define MASK_RU_BUSY 0x80000000
#define FLAG_RU_READY 0
#define FLAG_RU_BUSY 1
#define SET_BIT_WDT_RESET 1
#define SET_BIT_RECONFIG 2
// Global Variables
alt_u32 s2_ru_base_addr;
// Регистрация драйвера и инициализация
inline void S2Ru_InitDriver(alt_u32 s2_ru_base_address)
{
s2_ru_base_addr = s2_ru_base_address;
return;
}
// Чтение регистра статуса
inline alt_u32 S2Ru_GetStatus(void)
{
alt_u32 temp_var1 = IORD(s2_ru_base_addr,ADDR_FLAG_STATUS);
return temp_var1;
}
// Ожидание готовности контроллера
inline void S2Ru_WaitForReady(void)
{
alt_u32 temp_var1 = 0xFFFFFFFF;
while (temp_var1 != 0)
{
temp_var1 = IORD(s2_ru_base_addr,ADDR_FLAG_STATUS);
temp_var1 = temp_var1 & MASK_RU_BUSY;
}
return;
}
// Чтение параметра
alt_u32 S2Ru_ReadParameter(alt_u32 s2_ru_num_par)
{
if (s2_ru_num_par > ADDR_PAR_SPAN) //проверка на допустимый диапазон номера параметра
s2_ru_num_par = ADDR_PAR_SPAN;
S2Ru_WaitForReady();
alt_u32 s2_ru_val_par = IORD(s2_ru_base_addr,s2_ru_num_par);
S2Ru_WaitForReady();
s2_ru_val_par = IORD(s2_ru_base_addr,ADDR_FLAG_STATUS);
s2_ru_val_par = s2_ru_val_par & MASK_RU_DATA;
return s2_ru_val_par;
}
// Запись параметра
void S2Ru_WriteParameter(alt_u32 s2_ru_num_par,alt_u32 s2_ru_val_par)
{
if (s2_ru_num_par > ADDR_PAR_SPAN) //проверка на допустимый диапазон номера параметра
s2_ru_num_par = ADDR_PAR_SPAN;
S2Ru_WaitForReady();
IOWR(s2_ru_base_addr,s2_ru_num_par,s2_ru_val_par);
return;
}
// Сброс сторожевого таймера
inline void S2Ru_ResetWDT(void)
{
//Перед сбросом WDT готовность можно не проверять
IOWR(s2_ru_base_addr,ADDR_FLAG_STATUS,SET_BIT_WDT_RESET);
return;
}
// Запуск реконфигурации
inline void S2Ru_Reconfig(void)
{
S2Ru_WaitForReady();
IOWR(s2_ru_base_addr,ADDR_FLAG_STATUS,SET_BIT_RECONFIG);
return;
}
// Определение причины реконфигурации
// Бит 0: crcerror_source: произошла ошибка CRC прикладной конфигурации
// Бит 1: nstatus_source: nSTATUS активирован внешним устройством как результат ошибки
// Бит 2: runconfig_source: конфигурация была инициирована массивом логики
// Бит 3: nconfig_source: конфигурация была инициирована внешним сбросом (выводом nCONFIG)
// Бит 4: wdtimer_source: тайм-аут пользовательского сторожевого таймера
#if RU_DEVICE_FAMILY == RU_CYCLONE_III
inline alt_u32 S2Ru_GetSourceOfReconfig(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(7);
temp_var1 = temp_var1 & 0x0000001F;
switch (temp_var1)
{
case 0:
temp_var1 = 2;
break;
case 1:
temp_var1 = 4;
break;
case 2:
temp_var1 = 1;
break;
case 3:
temp_var1 = 0;
break;
case 4:
temp_var1 = 3;
break;
default:
break;
}
return temp_var1;
}
#else
inline alt_u32 S2Ru_GetSourceOfReconfig(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(0);
temp_var1 = temp_var1 & 0x0000001F;
return temp_var1;
}
#endif
// Чтение значения тайм-аута сторожевого таймера
inline alt_u32 S2Ru_GetTimeoutWDT(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(2);
temp_var1 = temp_var1 & 0x00000FFF;
return temp_var1;
}
// Установка значения тайм-аута сторожевого таймера
// Значение тайм-аута сторожевого таймера имеет разрядность 12 бит,
// старшие разряды игнорируются
inline void S2Ru_SetTimeoutWDT(alt_u32 s2_ru_wdt_tomeout)
{
s2_ru_wdt_tomeout = s2_ru_wdt_tomeout & 0x00000FFF;
S2Ru_WriteParameter(2,s2_ru_wdt_tomeout);
return;
}
// Чтение значения флага разрешения работы сторожевого таймера
inline alt_u32 S2Ru_GetEnableFlagWDT(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(3);
temp_var1 = temp_var1 & 0x00000001;
return temp_var1;
}
// Разрешение работы сторожевого таймера
inline void S2Ru_EnableWDT(void)
{
S2Ru_WriteParameter(3,1);
return;
}
// Запрет работы сторожевого таймера
inline void S2Ru_DisableWDT(void)
{
S2Ru_WriteParameter(3,0);
return;
}
// Выбор страницы памяти
// Для AS Configuration Devices - разрядность 7, биты [6..0] формируют биты [22..16] 24-битного стартового адреса,
// другие 17 бит установлены в 0.
// Для Enhanced Configuration Devices - разрядность 3.
inline void S2Ru_SetConfigPage(alt_u32 s2_ru_config_page)
{
S2Ru_WriteParameter(4,s2_ru_config_page);
return;
}
// Получение статуса конфигурационной прошивки
// 0 - Factory (заводская, базовая прошивка)
// 1 - Application (прикладная, рабочая прошивка)
// 3 - Application с включенным сторожевым таймером (только для Cyclone III и Cyclone IV)
#if RU_DEVICE_FAMILY == RU_CYCLONE_III || RU_DEVICE_FAMILY == RU_CYCLONE_IV
inline alt_u32 S2Ru_GetConfigMode(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(0);
temp_var1 = temp_var1 & 0x00000003;
return temp_var1;
}
#else
inline alt_u32 S2Ru_GetConfigMode(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(5);
temp_var1 = temp_var1 & 0x00000001;
return temp_var1;
}
#endif
// Установка статуса конфигурационной прошивки
// 0 - Factory (заводская, базовая прошивка)
// 1 - Application (прикладная, рабочая прошивка)
// Рекомендуется его установить в "1" перед загрузкой страницы Application-прошивки
inline void S2Ru_SetConfigMode(alt_u8 s2_ru_config_mode)
{
S2Ru_WriteParameter(5,s2_ru_config_mode);
return;
}
// Принудительная досрочная проверка CONF_DONE (Cd_early) - Только для Cyclone III и Cyclone IV
#if RU_DEVICE_FAMILY == RU_CYCLONE_III || RU_DEVICE_FAMILY == RU_CYCLONE_IV
inline alt_u8 C2Ru_GetConfDoneStatus(void)
{
alt_u32 temp_var1 = S2Ru_ReadParameter(1);
temp_var1 = temp_var1 & 0x00000001;
return temp_var1;
}
#endif
//******************************************
// Библиотека функций удаленного конфигурирования
//******************************************
// Проверка текущей конфигурации. Если Factory и не было ошибок загрузки (т.е. это первая загрузка с момента включения),
// то инициировать загрузку конфигурации Application, иначе ничего не делать и выдать статус.
// Если s2_ru_wdt_tomeout равен 0, то сторожевой таймер не используется и отключен.
// Функция возвращает статус - причину невозможности загрузки Application-конфигурации:
// 0 - Реконфигурация не произошла, т.к. текущая конфигурация уже является Application
// 1 - Реконфигурация инициирована функцией, но по какой-то причине не произошла (аппаратные проблемы);
// 2 - Произошла ошибка CRC Application-конфигурации;
// 3 - nSTATUS активирован внешним устройством как результат ошибки (при предопределённом параметре ENABLE_NSTATUS_CHECKING);
// 4 - Текущая конфигурация была инициирована логикой (при предопределённом параметре ENABLE_LOGIC_CHECKING);
// 5 - Текущая конфигурация была инициирована внешним сбросом (выводом nCONFIG) (при предопределённом параметре ENABLE_NCONFIG_CHECKING)
// 6 - Текущая конфигурация была инициирована по тайм-ауту пользовательского сторожевого таймера
#if RU_DEVICE_FAMILY == RU_CYCLONE_III
//Для Cyclone III
alt_u32 S2Ru_TryReconfigToApplication(alt_u32 ru_addr_reconfig,alt_u32 s2_ru_wdt_tomeout)
{
alt_u32 ru_status;
alt_u32 ru_enable_reconfig = 0;
alt_u32 ru_temp_var2 = S2Ru_GetConfigMode();
if (ru_temp_var2 == 0) //Factory
{
ru_temp_var2 = S2Ru_GetSourceOfReconfig();
ru_status = ru_temp_var2 + 2; //т.к. коды статуса ровно на 2 больше возвращаемых функцией S2Ru_GetSourceOfReconfig значений
ru_enable_reconfig = 1;
if (ru_temp_var2 == 0)
ru_enable_reconfig = 0;
#ifdef ENABLE_NSTATUS_CHECKING
if (ru_temp_var2 == 1)
ru_enable_reconfig = 0;
#endif
#ifdef ENABLE_LOGIC_CHECKING
if (ru_temp_var2 == 2)
ru_enable_reconfig = 0;
#endif
#ifdef ENABLE_NCONFIG_CHECKING
if (ru_temp_var2 == 3)
ru_enable_reconfig = 0;
#endif
if (ru_temp_var2 == 4)
ru_enable_reconfig = 0;
if (ru_enable_reconfig == 1)
{
S2Ru_WriteParameter(1,0); //Не использовать принудительную досрочную проверку CONF_DONE
S2Ru_WriteParameter(6,0); //Не использовать внутренний тактовый генератор в качестве тактов машины состояний
S2Ru_SetConfigPage(ru_addr_reconfig); //Выбор страницы памяти c Application-конфигурацией
if (s2_ru_wdt_tomeout == 0)
{
S2Ru_DisableWDT();
}
else
{
S2Ru_SetTimeoutWDT(s2_ru_wdt_tomeout); //Установка тайм-аута WDT
S2Ru_ResetWDT();
S2Ru_EnableWDT();
}
ru_status = 1;
S2Ru_Reconfig();
}
}
else //Application
{
ru_status = 0;
}
return ru_status;
}
#elif RU_DEVICE_FAMILY == RU_CYCLONE_IV
//Для Cyclone IV
alt_u32 S2Ru_TryReconfigToApplication(alt_u32 ru_addr_reconfig,alt_u32 s2_ru_wdt_tomeout)
{
alt_u32 ru_status;
alt_u32 ru_enable_reconfig = 0;
alt_u32 ru_temp_var2 = S2Ru_GetConfigMode();
if (ru_temp_var2 == 0) //Factory
{
ru_temp_var2 = S2Ru_GetSourceOfReconfig();
ru_status = ru_temp_var2 + 2; //т.к. коды статуса ровно на 2 больше возвращаемых функцией S2Ru_GetSourceOfReconfig значений
ru_enable_reconfig = 1;
if (ru_temp_var2 == 0)
ru_enable_reconfig = 0;
#ifdef ENABLE_NSTATUS_CHECKING
if (ru_temp_var2 == 1)
ru_enable_reconfig = 0;
#endif
#ifdef ENABLE_LOGIC_CHECKING
if (ru_temp_var2 == 2)
ru_enable_reconfig = 0;
#endif
#ifdef ENABLE_NCONFIG_CHECKING
if (ru_temp_var2 == 3)
ru_enable_reconfig = 0;
#endif
if (ru_temp_var2 == 4)
ru_enable_reconfig = 0;
if (ru_enable_reconfig == 1)
{
S2Ru_WriteParameter(1,0); //Не использовать принудительную досрочную проверку CONF_DONE
S2Ru_WriteParameter(6,0); //Не использовать внутренний тактовый генератор в качестве тактов машины состояний
S2Ru_SetConfigPage(ru_addr_reconfig); //Выбор страницы памяти c Application-конфигурацией
if (s2_ru_wdt_tomeout == 0)
{
S2Ru_DisableWDT();
}
else
{
S2Ru_SetTimeoutWDT(s2_ru_wdt_tomeout); //Установка тайм-аута WDT
S2Ru_ResetWDT();
S2Ru_EnableWDT();
}
ru_status = 1;
S2Ru_Reconfig();
}
}
else //Application
{
ru_status = 0;
}
return ru_status;
}
#else
//Для остальных
alt_u32 S2Ru_TryReconfigToApplication(alt_u32 ru_addr_reconfig,alt_u32 s2_ru_wdt_tomeout)
{
alt_u32 ru_status;
alt_u32 ru_enable_reconfig = 0;
alt_u32 ru_temp_var2 = S2Ru_GetConfigMode();
if (ru_temp_var2 == 0) //Factory
{
ru_temp_var2 = S2Ru_GetSourceOfReconfig();
ru_status = ru_temp_var2 + 2; //т.к. коды статуса ровно на 2 больше возвращаемых функцией S2Ru_GetSourceOfReconfig значений
ru_enable_reconfig = 1;
if (ru_temp_var2 == 0)
ru_enable_reconfig = 0;
#ifdef ENABLE_NSTATUS_CHECKING
if (ru_temp_var2 == 1)
ru_enable_reconfig = 0;
#endif
#ifdef ENABLE_LOGIC_CHECKING
if (ru_temp_var2 == 2)
ru_enable_reconfig = 0;
#endif
#ifdef ENABLE_NCONFIG_CHECKING
if (ru_temp_var2 == 3)
ru_enable_reconfig = 0;
#endif
if (ru_temp_var2 == 4)
ru_enable_reconfig = 0;
if (ru_enable_reconfig == 1)
{
S2Ru_SetConfigMode(1); //Установка признака Application-конфигурации
S2Ru_SetConfigPage(ru_addr_reconfig); //Выбор страницы памяти c Application-конфигурацией
if (s2_ru_wdt_tomeout == 0)
{
S2Ru_DisableWDT();
}
else
{
S2Ru_SetTimeoutWDT(s2_ru_wdt_tomeout); //Установка тайм-аута WDT
S2Ru_ResetWDT();
S2Ru_EnableWDT();
}
ru_status = 1;
S2Ru_Reconfig();
}
}
else //Application
{
ru_status = 0;
}
return ru_status;
}
#endif
//*************************************************************************
// Пример использования в универсальной конфигурации (Factory/Application)
//*************************************************************************
// В EEPROM хранится байт-признак:
// 0xFF - Application-конфигурация отсутствует
// 0x00 - Application-конфигурация имеется, но ещё не запускалась ни разу (не проверена на работоспособность)
// 0x01 - Application-конфигурация имеется и уже успешно запускалась (работоспособна)
// 0x02 - Application-конфигурация имеется, но не работоспособна (была неуспешная попытка запуска) - код может быть замененн на 0xFF
//
// ********** Менеджер конфигураций (Factory/Application - универсальная конфигурация) **********
// Код вставляется после инициализации и перед основным циклом программы (до разрешения прерываний)
#ifdef USE_RU_CONFIG_LIB1
void Ru_CheckAndLoadApplication(eeprom_status_config_addr,epcs_application_config_addr,ru_wdt_timeout)
{
ReadEeprom(eeprom_status_config_addr,1); //Чтение из EEPROM статуса (байта-признака)
alt_u32 status_config = EepromReadBuffer(0);
if ((status_config == 0) || (status_config == 1)) //если имеется Application-прошивка, то попытаться её загрузить
{
alt_u32 reconfig_res = S2Ru_TryReconfigToApplication(epcs_application_config_addr,ru_wdt_timeout);
if ((reconfig_res == 0) && (status_config == 0)) //если текущая конфигурация - Application и это её первый запуск, то обновить байт-признак в EEPROM
{
EepromWriteToBuffer(0,0x01); //Запись в EEPROM признака "0x01"
WriteEeprom(eeprom_status_config_addr,1);
}
if (reconfig_res != 0) //Ошибка реконфигурации
{
EepromWriteToBuffer(0,0xFF); //Запись в EEPROM признака "0xFF"
WriteEeprom(eeprom_status_config_addr,1);
}
}
return; //нормальный выход из программы, если заглужена Application-конфигурация или её не удалось загрузить
}
#endif
// Перед началом записи в EPCS-конфигуратор новой Application-конфигурации, в EEPROM по адресу eeprom_status_config_addr
// записывается код 0xFF. Далее производится запись в EPCS-конфигуратор новой Application-конфигурации.
// После успешной записи в EEPROM по адресу eeprom_status_config_addr записывается код 0x00.
// После этого, если требуется загрузка новой конфигурации, выполняется команда сброса.
//************************************************************
Комментарии