/* Shared by the application and the separate Flash bootloader. */ /* Аппаратный слой: общая RAM между reset, watchdog и SPI EEPROM. * Один исходник компилируется и в приложение, и в отдельный Flash-загрузчик. * В отличие от store.c этот файл требует настоящих регистров F28335. */ #include "DSP2833x_Device.h" #include "f28335_boot_platform.h" #include "f28335_can_boot.h" /* Must be NOINIT in BOTH linkers, outside ROM scratch and application payload. */ #pragma DATA_SECTION(boot_handoff,".boot_shared") /* 8 слов NOINIT по адресу 0x50, одинаково зарезервированных обоими linker-файлами. * [0..1] magic; [2] запрошенная session; [3] запущенный слот; [4..6] служебные * поля; [7] XOR-контроль. Это кратковременная передача контекста через watchdog * reset, а не замена энергонезависимого журнала EEPROM. */ static volatile unsigned int boot_handoff[8]; /* Проверяем magic и XOR прежде, чем трактовать RAM как запрос входа в boot. * После включения питания содержимое NOINIT может быть произвольным. */ static unsigned int handoff_valid(void) { unsigned int i,sum=0; for(i=0;i<8;++i) sum^=boot_handoff[i]; return boot_handoff[0]==0xB283 && boot_handoff[1]==0x4D7C && sum==0xA95B; } /* Сначала снимаем признак действительности, затем заполняем поля и контроль. * Первый magic записываем последним; volatile сохраняет обращения к общей RAM. * request=0 означает обычный запуск, ненулевая session — явный ENTER_BOOT. */ static void handoff_set(unsigned int request,unsigned int running) { unsigned int i,sum=0; boot_handoff[0]=0; boot_handoff[1]=0x4D7C; boot_handoff[2]=request; boot_handoff[3]=running; boot_handoff[4]=0xF335; boot_handoff[5]=0x0CCA; boot_handoff[6]=1; sum=0xB283; for(i=1;i<7;++i) sum^=boot_handoff[i]; boot_handoff[7]=sum^0xA95B; boot_handoff[0]=0xB283; /* publish last */ } /* Приложение узнаёт свой слот из handoff, а не предполагает, что оно active: * во время пробного запуска выполняется pending, active ещё указывает на старый образ. */ unsigned int F28335Boot_RunningSlot(void) { return handoff_valid()?boot_handoff[3]:F28335_AB_NONE; } unsigned int F28335Boot_RequestedSession(void) { return handoff_valid() && boot_handoff[2]<256?boot_handoff[2]:0; } /* Одновременно публикуем выбранный слот и потребляем запрос ENTER_BOOT: * он не должен повторяться при следующем обычном reset. */ void F28335Boot_SetRunning(unsigned int slot) { handoff_set(0,slot); } /* Timer1 должен давать 1 мс на InterruptCount и в приложении, и в загрузчике. * Тайм-ауты используют разность unsigned-счётчиков для переноса через wrap. */ uint32_t F28335Boot_Now(void) { return CpuTimer1.InterruptCount; } /* Вызвать сразу после InitSysCtrl, который штатно отключает watchdog. * Сбрасываем разрешение кормления из ISR до полной инициализации CAN-сервиса. * Для приложения, запущенного нашим загрузчиком, включаем watchdog с делителем. */ void F28335Boot_StartupWatchdog(void) { F28335CanBoot_EarlyInit(); if(F28335Boot_RunningSlot()<2) { EALLOW; SysCtrlRegs.WDKEY=0x55; SysCtrlRegs.WDKEY=0xAA; SysCtrlRegs.WDCR=0x002F; EDIS; } } /* Платформенный Quiesce останавливает управление выходами перед reset. * Session сохраняется до включения watchdog; ненулевую session загрузчик * использует для отложенного ACK ENTER_BOOT. В бесконечном цикле watchdog * намеренно не обслуживается — выход из функции должен произойти через reset. */ void F28335Boot_Reset(unsigned int session) { F28335Boot_Quiesce(); DINT; handoff_set(session,F28335_AB_NONE); EALLOW; SysCtrlRegs.WDCR=0x0028; EDIS; for(;;) { } } /* Main context only. Legacy Timer2 EEPROM driver must be idle/stopped. Save * its SPI configuration: it changes the character width during transactions. */ typedef struct { unsigned int ccr,ctl,brr,tx,rx,ct,pri; } boot_spi_saved; static void spi_cs(unsigned int high) { if(high) GpioDataRegs.GPASET.bit.GPIO19=1; else GpioDataRegs.GPACLEAR.bit.GPIO19=1; } /* Доступ разрешён только из main при остановленном Timer2 старого SPI-драйвера. * Это проверка занятости в однопоточном приложении, не универсальный mutex. * Сохраняем режим SPI, поскольку штатный драйвер меняет разрядность передачи. * CS сначала поднимаем, затем переключаем SPI на 8-битные транзакции. */ static bool spi_take(boot_spi_saved *s) { volatile unsigned int discard; if(!CpuTimer2Regs.TCR.bit.TSS) return false; s->ccr=SpiaRegs.SPICCR.all; s->ctl=SpiaRegs.SPICTL.all; s->brr=SpiaRegs.SPIBRR; s->tx=SpiaRegs.SPIFFTX.all; s->rx=SpiaRegs.SPIFFRX.all; s->ct=SpiaRegs.SPIFFCT.all; s->pri=SpiaRegs.SPIPRI.all; EALLOW; GpioCtrlRegs.GPAMUX2.bit.GPIO16=1; GpioCtrlRegs.GPAMUX2.bit.GPIO17=1; GpioCtrlRegs.GPAMUX2.bit.GPIO18=1; GpioCtrlRegs.GPAMUX2.bit.GPIO19=0; GpioDataRegs.GPASET.bit.GPIO19=1; GpioCtrlRegs.GPADIR.bit.GPIO19=1; GpioCtrlRegs.GPAQSEL2.bit.GPIO17=3; EDIS; SpiaRegs.SPICCR.all=7; SpiaRegs.SPICTL.all=0x0E; /* master, phase=1, IRQ off */ SpiaRegs.SPIBRR=CLKMULT*6+1; SpiaRegs.SPIFFTX.all=0x8000; SpiaRegs.SPIFFRX.all=0; SpiaRegs.SPIFFCT.all=0; SpiaRegs.SPIPRI.all=0x10; SpiaRegs.SPICCR.all=0x87; /* Снимаем оставшийся RX/INT_FLAG от прежней операции, чтобы первый обмен * не принял старое содержимое RXBUF за ответ EEPROM. */ discard=SpiaRegs.SPIRXBUF; (void)discard; return true; } /* Выход и при успехе, и при ошибке: CS не должен остаться активным. * Настройки возвращаются при удержании SPI в reset; исходный SPISWRESET * восстанавливается последним, после остальных регистров. */ static void spi_release(const boot_spi_saved *s) { volatile unsigned int discard; spi_cs(1); discard=SpiaRegs.SPIRXBUF; (void)discard; SpiaRegs.SPICCR.all=s->ccr&0x7FU; SpiaRegs.SPICTL.all=s->ctl; SpiaRegs.SPIBRR=s->brr; SpiaRegs.SPIFFTX.all=s->tx; SpiaRegs.SPIFFRX.all=s->rx; SpiaRegs.SPIFFCT.all=s->ct; SpiaRegs.SPIPRI.all=s->pri; SpiaRegs.SPICCR.all=s->ccr; } /* У F28335 8-битный символ для передачи выравнивается в старшие биты TXBUF. * Полученный октет читается из младших битов RXBUF. Счётчик budget ограничивает * ожидание даже при неработающем Timer1; это число итераций, а не миллисекунды. */ static bool spi_octet(unsigned int tx,unsigned int *rx) { uint32_t budget=100000UL; SpiaRegs.SPITXBUF=(tx&255U)<<8; while(!SpiaRegs.SPISTS.bit.INT_FLAG) if(--budget==0) return false; *rx=SpiaRegs.SPIRXBUF&255U; return true; } /* Команда EEPROM и 24-битный байтовый адрес, старший октет адреса первым. * CS удерживается вызывающей функцией на всю команду и полезные данные. */ static bool spi_prefix(unsigned int command,uint32_t address) { unsigned int discard; return spi_octet(command,&discard) && spi_octet((unsigned int)(address>>16),&discard) && spi_octet((unsigned int)(address>>8),&discard) && spi_octet((unsigned int)address,&discard); } /* RDSR (05h): WIP в бите 0 означает незавершённую внутреннюю запись EEPROM. * Ожидание ограничено и 50 мс, и числом опросов на случай остановки tick. * После каждого чтения статуса CS снимается, в том числе при ошибке SPI. */ static bool spi_ready(void) { unsigned int status,discard; uint32_t started=F28335Boot_Now(),budget=20000UL; do { spi_cs(0); if(!spi_octet(5,&discard) || !spi_octet(255,&status)) { spi_cs(1); return false; } spi_cs(1); if(!(status&1U)) return true; } while(--budget && (uint32_t)(F28335Boot_Now()-started)<50UL); return false; } /* Читать разрешено всю физическую EEPROM, включая параметры. Проверка * через вычитание защищает от переполнения адреса. spi_release обязателен * после успешного захвата SPI независимо от результата последующего обмена. */ bool F28335Boot_Read(void *u,uint32_t address,pcan_boot_byte_t *p,unsigned int n) { boot_spi_saved saved; unsigned int i,value; bool ok; (void)u; if(address>0x40000UL || n>0x40000UL-address || !spi_take(&saved)) return false; ok=spi_ready(); spi_cs(0); if(ok) ok=spi_prefix(3,address); for(i=0;ok && i0x3FE00UL || n>0x3FE00UL-address || !spi_take(&saved)) return false; ok=spi_ready(); while(ok && n) { /* Нельзя пересекать страницу одной командой WRITE: EEPROM может завернуть * адрес внутри страницы. Для каждого фрагмента заново выдаём WREN (06h), * WRITE (02h) и ждём снятия WIP. Сравнение readback выполняет верхний слой. */ chunk=256U-(unsigned int)(address&255U); if(chunk>n) chunk=n; spi_cs(0); ok=spi_octet(6,&discard); spi_cs(1); /* WREN */ spi_cs(0); if(ok) ok=spi_prefix(2,address); for(i=0;ok && i