/*******************************************************************************/ /* */ /* Boot ROM for the Blackfin products and silicon revisions */ /* */ /* ADSP-BF534-0.3 */ /* ADSP-BF536-0.3 */ /* ADSP-BF537-0.3 */ /* */ /* for documentation see manual */ /* */ /* ADSP-BF537 Blackfin® Hardware Reference - Section Booting */ /* */ /* (c) Analog Devices Inc. (2005-2006) */ /* */ /*******************************************************************************/ #include #define SYSMMR_BASE 0xFFC00000 #define COREMMR_BASE 0xFFE00000 #define DMA_Destination 0xFF807FF0 // do not the following ones in code. Offset is hardcoded. For dokumentation only. #define TargetAddress 0xFF807FF0 #define BlockSize 0xFF807FF4 #define Flags 0xFF807FF8 #define ZeroAndTrash 0xFF807FFC #define L1_Code 0xFFA00000 //L1 Code SRAM #define L1_Scratch_END 0XFFB00FFC //L1 Scratch SRAM (end) #define ASYNC_Bank0 0x20000000 //Async Bank 0 #define ASYNC_Bank1 0x20100000 //Async Bank 1 #define ASYNC_Bank2 0x20200000 //Async Bank 2 #define ASYNC_Bank3 0x20300000 //Async Bank 3 #define PayLoadSize 256 .SECTION twidata; .BYTE Boot_PayLoad[PayLoadSize]; // this 256-byte buffer is used by TWI slave and master boot modes .SECTION program; /*******************************************************************************/ /*******************************************************************************/ /* */ /* ROM entrypoint. Resides at address 0xEF000000 */ /* */ /*******************************************************************************/ /*******************************************************************************/ .GLOBAL _BOOTROM; _BOOTROM: JUMP_TABLE: #undef _BOOTROM_RESET .GLOBAL _BOOTROM_RESET; _BOOTROM_RESET: // 0xEF00.0000 JUMP BOOT_MENU; _BOOTROM_RESET.END: #undef _BOOTROM_FINAL_INIT; .GLOBAL _BOOTROM_FINAL_INIT; _BOOTROM_FINAL_INIT: // 0xEF00.0002 JUMP SWRESET; _BOOTROM_FINAL_INIT.END: // RESERVED // 0xEF00.0004 RTS; #undef _BOOTROM_DO_MEMORY_DMA; .GLOBAL _BOOTROM_DO_MEMORY_DMA; _BOOTROM_DO_MEMORY_DMA: // 0xEF00.0006 JUMP FDMA; _BOOTROM_DO_MEMORY_DMA.END: #undef _BOOTROM_BOOT_DXE_FLASH; .GLOBAL _BOOTROM_BOOT_DXE_FLASH; _BOOTROM_BOOT_DXE_FLASH: // 0xEF00.0008 JUMP Boot_DXE_Flash; _BOOTROM_BOOT_DXE_FLASH.END: #undef _BOOTROM_BOOT_DXE_SPI; .GLOBAL _BOOTROM_BOOT_DXE_SPI; _BOOTROM_BOOT_DXE_SPI: // 0xEF00.000A JUMP Boot_DXE_SPI; _BOOTROM_BOOT_DXE_SPI.END: #undef _BOOTROM_BOOT_DXE_TWI; .GLOBAL _BOOTROM_BOOT_DXE_TWI; _BOOTROM_BOOT_DXE_TWI: // 0xEF00.000C JUMP Boot_DXE_TWI; _BOOTROM_BOOT_DXE_TWI.END: // RESERVED // 0xEF00.000E RTS; #undef _BOOTROM_GET_DXE_ADDRESS_FLASH; .GLOBAL _BOOTROM_GET_DXE_ADDRESS_FLASH; _BOOTROM_GET_DXE_ADDRESS_FLASH: JUMP Get_DXE_Address_Flash; // 0xEF00.0010 _BOOTROM_GET_DXE_ADDRESS_FLASH.END: #undef _BOOTROM_GET_DXE_ADDRESS_SPI; .GLOBAL _BOOTROM_GET_DXE_ADDRESS_SPI; _BOOTROM_GET_DXE_ADDRESS_SPI: // 0xEF00.0012 JUMP Get_DXE_Address_SPI; _BOOTROM_GET_DXE_ADDRESS_SPI.END: #undef _BOOTROM_GET_DXE_ADDRESS_TWI; .GLOBAL _BOOTROM_GET_DXE_ADDRESS_TWI; _BOOTROM_GET_DXE_ADDRESS_TWI: // 0xEF00.0014 JUMP Get_DXE_Address_TWI; _BOOTROM_GET_DXE_ADDRESS_TWI.END: // RESERVED // 0xEF00.0014 // RTS; // RESERVED // 0xEF00.0016 RTS; JUMP_TABLE.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from host through 16-bit parallel FIFO .*/ /* */ /*******************************************************************************/ /*******************************************************************************/ FIFO_BOOT: /*******************************************************************************/ /* */ /* This uses exactly the same code as normal flash boot, only that it is */ /* headed by the setup code that initializes the HMDMA1 module, in order */ /* that every word transfered by the MDMA must be requested by a rising */ /* edge on the DMAR1 input pin. FIFO expected on /MS3 strobe. */ /* */ /*******************************************************************************/ R1.L = 0x0009; // enable all four banks and CLKOUT W[P1+LO(EBIU_AMGCTL)] = R1; // CLKOUT code be used to dummy request // DMA. Then this functions as normal // flash boot from AMS3 space P0.H = HI(ASYNC_Bank3); // P0.L is already 0 R1 = PF1 (Z); // function enable for DMAR1 W[P1 + LO(PORTF_FER)] = R1; R1 = PFDE (Z); // enable DMARx pin rather than UART0 W[P1 + LO(PORT_MUX)] = R1; W[P1+LO(HMDMA1_BCINIT)] = R3; // R3 = 1, Block Size = 1 // HMDMA1_ECINIT resets to zero R1 = 0x3; W[P1+LO(HMDMA1_CONTROL)] = R1; // set HMDMAEN for rising edges /*******************************************************************************/ /* */ /* Regardless of the HMDMA settings, the source channel of the memory DMA */ /* prefetches four 32-bit words as soon as enabled. Only the transmit channel */ /* is stalled and triggered by the HMDMA module. In 16-bit DMA mode, these */ /* four early reads result in eight 16-bit reads. */ /* */ /* The kernel ensures that at least eight valid data words are ready in the */ /* external FIFO by counting eight rising edges on the DMAR1 request input */ /* and by disabling the ECOUNT afterward again. Afterward, the prefetch will */ /* find valid data. The MDMA can be started safely. */ /* */ /* This method requires the host to send N more request strobes after it has */ /* sent the complete boot stream to the FIFO. This is because the transmit */ /* channel of the DMA still has to drain the FIFO. Setting N to 16 here. */ /* */ /*******************************************************************************/ R0 = 15; INITIAL_REQUESTS: R7 = W[P1+LO(HMDMA1_ECOUNT)] (Z); // wait for 16 DMA requests CC = R7 < R0; IF CC JUMP INITIAL_REQUESTS; R0 = 0x0; W[P1+LO(HMDMA1_CONTROL)] = R0; // disable and reenable to clear W[P1+LO(HMDMA1_CONTROL)] = R1; // edge count R1 = 0x20; // always 16-bit DMA assumption to CALL GET_FLASH_TYPE.FIFO; // avoids initial read access as in // Flash mode JUMP FGRAB_HEADER; FIFO_BOOT.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from 8-bit or 16-bit parallel flash thread . */ /* */ /*******************************************************************************/ /*******************************************************************************/ FLASH_BOOT: /*******************************************************************************/ /* */ /* On top of BF533-type of 16-bit flash boot, this kernel supports also */ /* a new mode that performs true 16-bit DMA operations when reading from */ /* the 16-bit flash device. This does however require that there are no */ /* blocks with odd byte count in the boot stream. Similarly the destination */ /* must always be even. */ /* */ /* First test whether 8-bit or 16-bit device connected to EBIU, /MS0 . */ /* 8-bit boot stream starts with an 0x40 byte (8-bit DMA) */ /* 16-bit boot streams start with an 0x60 byte (8-bit DMA) */ /* 16-bit boot streams start with an 0x20 byte (16-bit DMA) */ /* */ /* Subroutine GET_FLASH_TYPE tests upper nibble of first byte and */ /* arranges DMA settings accordingly. This causes an extra EBIU access. */ /* */ /* P0 is now a parameter to support Boot_DXE_Flash routine */ /* R0 functions as Next Boot Block Pointer . */ /* */ /*******************************************************************************/ P0.H = HI(ASYNC_Bank0); // P0.L is already 0 FDXE_ENTRY: // Boot_DXE_Flash enters here CALL GET_FLASH_TYPE; /*******************************************************************************/ /* */ /* Parse Boot Stream Block by Block . */ /* */ /* First DMA 10-bytes header in and evaluate flags */ /* */ /* Bit 0: ZEROFILL */ /* Bit 1: RESVECT (always 1) */ /* Bit 2: reserved */ /* Bit 3: INIT */ /* Bit 4: IGNORE */ /* Bits 8- 5: PFLAG */ /* Bits 10- 9: PPORT */ /* Bits 14-11: reserved */ /* Bits 15: FINAL */ /* */ /* Fetches the next 10-byte Block Header, and updated working registers */ /* accordingly. L1 memory is used for temporary storage. */ /* */ /*******************************************************************************/ FGRAB_HEADER: W[P1+LO(MDMA_D1_X_MODIFY)] = R6; // set destination modify again to 1 or 2 R1 = P3; // intermediate L1 storage address R2 = 0xA; // 10-bytes header CALL FDMA; // perform Memory DMA R1 = [P3]; // Target Address R2 = [P3+0x4]; // Byte Count R5 = W[P3+0x8](Z); // Flags CALL SET_HWAIT; /*******************************************************************************/ /* */ /* Test ZEROFILL Flag . */ /* */ /* If set simply patch the MDMA Source Pointer and zero Source Modifier */ /* */ /*******************************************************************************/ FCHECK_ZEROFILL_BIT: CC = BITTST(R5,0); IF !CC JUMP FCHECK_IGNORE_BIT; // Zero Fill? CALL ZEROFILL_MDMA; JUMP FCHECK_LAST_SECTION; /*******************************************************************************/ /* */ /* Test IGNORE Flag . */ /* */ /* If set don't perform further DMA. Only update Next Boot Block Pointer in */ /* R0 by the block's byte count, which is multiplied by 2 in 8-bit boot mode. */ /* Finally test also for INIT and FINAL flag which can coexist now. */ /* */ /* However, in FIFO mode payload data needs to be trashed actively. */ /* */ /*******************************************************************************/ FCHECK_IGNORE_BIT: CC = BITTST(R5,4); IF !CC JUMP FDO_DMA; // Ignore ? R7 = W[P1+LO(HMDMA1_CONTROL)] (Z); // Test HMDMAEN Bit CC = BITTST(R7,0); IF CC JUMP FIFO_IGNORE_BIT; FLASH_IGNORE_BIT: // Skip IGNORE data in master mode R2 <<= R4; R0 = R0 + R2; JUMP FCHECK_INIT_BIT; FIFO_IGNORE_BIT: // Consume and trash IGNORE data in // master mode R7 = 0x0; W[P1+LO(MDMA_D1_X_MODIFY)] = R7; R1 = M2; // Set the SPI DMA Start Address to // 0xFF807FFC (4th to last location of // L1 Data Bank A) /*******************************************************************************/ /* */ /* Finally perform the Memory DMA . */ /* */ /*******************************************************************************/ FDO_DMA: CALL FDMA; /*******************************************************************************/ /* */ /* Test INIT Flag . */ /* */ /* If set issue a CALL instruction to the blocks target address after having */ /* it booted in. */ /* */ /* An INIT Block can be also a FINAL block at the same time now. */ /* */ /*******************************************************************************/ FCHECK_INIT_BIT: CC = BITTST(R5,3); IF !CC JUMP FCHECK_LAST_SECTION; // Init ? P2 = [P3]; CALL (P2); /*******************************************************************************/ /* */ /* Test FINAL Flag . */ /* */ /* Either fetch next block header or exit and jump to reset vector */ /* */ /*******************************************************************************/ FCHECK_LAST_SECTION: CC = BITTST(R5,15); // Last Section? IF !CC JUMP FGRAB_HEADER; // If not, Jump to grab header JUMP BOOT_END; FLASH_BOOT_END: /*******************************************************************************/ /* */ /* FDMA/MEMDMA Subroutine for Flash Boot Mode (subroutine to all boot modes) */ /* */ /* Sets up a MDMA sequence and waits until MDMA is done. */ /* This functions is used to fetch block headers, to boot payload data, as */ /* well as to perform zero-initialization. It supports 32-bit byte counts */ /* including 0. */ /* */ /* R0 = Start Source Address */ /* returns updated as R0 = R0 + (R2 << M1) */ /* */ /* R1 = Start Destination Address */ /* returns updated as R1 = R1 + (R2 << M1) */ /* */ /* R2 = Byte Count */ /* is internally divided by 2 in 16-bit DMA mode */ /* */ /* M1 = 16-bit DMA Indicator */ /* 0: 8-bit DMA */ /* 1: 16-bit DMA */ /* 2: 32-bit DMA */ /* */ /* M3 = HWAIT GPIO Number */ /* */ /* P1 = System MMR base */ /* always 0xFFC0.0000 */ /* */ /* P4 = Pointer to either MDMA_S0_CONFIG or MDMA_S1_CONFIG. Determines */ /* whether MDMA0 or MDMA1 is used. The FDMA entry overwrites P4 */ /* to MDMA_S1_CONFIG always. */ /* */ /* P5 = Pointer to GPIO Toggle register are dummy memory address */ /* */ /* */ /* Note that both MDMA X Modify register must be set by the calling routine. */ /* */ /*******************************************************************************/ FDMA: // entry for legacy calls [--SP] = (R7:2,P5:4); P4 = P1; // whether MDMA0 or MDMA1 P4.L = LO(MDMA_S1_CONFIG); JUMP MEMDMA.ENTRY; // bypass push operation MEMDMA: // entry for ZEROFILL calls [--SP] = (R7:2,P5:4); MEMDMA.ENTRY: R6.H = 0x6000; // Only enable MemDMA to wake up R6.L = 0x0; // the core from idle state [P1 + LO(SIC_IWR)] = R6; R4.L = DI_EN | WDSIZE_8 | WNR | DMAEN; // Destination DMA config // 0x0083 = DMA Enable, Memory Write // 8-Bit Transfers, 1-D DMA, // Flow = Stop, Interrupt on // Completion R6 = DMAEN; // Source DMA config /*******************************************************************************/ /* */ /* If M1=0 then perform 8-bit DMA */ /* If M1=1 then perform 16-bit DMA */ /* If M1=2 then perform 32-bit DMA */ /* */ /*******************************************************************************/ R5 = M1; // M1 = 0 normally, but 1 for true 16-bit flash R2 >>= R5; // half transfer count R5 <<= 2; R4 = R4 | R5; // enable 16-bit DMA operation for destination channel R7 = R5 | R6; // enable 16-bit DMA operation for source channel /*******************************************************************************/ /* */ /* To support byte counts greater than 16 bit, multiple DMA sequences might */ /* required. The first pass performs the 16-bit reminder, afterward all DMAs */ /* have a transfer count of 65536. */ /* */ /* Transfer counts of zero bypass the sequence as no action is required. */ /* */ /*******************************************************************************/ R6 <<= 16; // R6 = 0x0001.0000 R5 = R2.L (Z); // R5.L = R2.L CC = R5 == 0; // R5.H = 0 IF R2.L != 0 IF CC R5 = R6; // R5.H = 1 IF R2.L == 0 // R6 used below again. MEMDMA.NEXT: CC = R2 == 0; IF CC JUMP MEMDMA.EXIT; // If the COUNT = 0, skip the DMA SSYNC; /*******************************************************************************/ /* */ /* Set Source and Destination settings as calculated above. */ /* Destination DMA issues interrupt when done. 1D-DMA, Flow = STOP */ /* */ /*******************************************************************************/ [P4+MDMA_S1_START_ADDR-MDMA_S1_CONFIG] = R0; // Set Source Address [P4+MDMA_D1_START_ADDR-MDMA_S1_CONFIG] = R1; // Set Destination Address W[P4+MDMA_S1_X_COUNT-MDMA_S1_CONFIG] = R5; // Set Source Count W[P4+MDMA_D1_X_COUNT-MDMA_S1_CONFIG] = R5; // Set Destination Count W[P4+MDMA_S1_CONFIG-MDMA_S1_CONFIG] = R7; // Enable Source DMA W[P4+MDMA_D1_CONFIG-MDMA_S1_CONFIG] = R4; // Enable Destination DMA /*******************************************************************************/ /* */ /* Wait for MDMA to complete. IRQ of Destination channels wakes core up. */ /* Clear interrupt request afterward. */ /* */ /* HWAIT signal indicates that kernel is ready to consume data. */ /* M3 holds the HWAIT GPIO number. P5 holds GPIO toggle register address. */ /* */ /*******************************************************************************/ R3 = M3; W[P5] = R3; // Toggle HWAIT Flag (enable) IDLE; // Wait for DMA to Complete W[P5] = R3; // Toggle HWAIT Flag (disable) R3 = 1; // W1C to clear IRQ request W[P4+MDMA_D1_IRQ_STATUS-MDMA_S1_CONFIG] = R3; /*******************************************************************************/ /* */ /* If the total transfer count was greater than 64k the DMA will be started */ /* again. Next start addresses and count values need to be calculated for the */ /* next run. Since both, source and destination DMA may have modify values of */ /* 0, 1 or 2, it is simpler to read the CURR_ADDR registers than calculating */ /* the new address by math. */ /* */ /* If the count was greater than 64k then the first run processed the */ /* reminder, if there was any. All further DMA sequences perform full 64k */ /* transfer count. */ /* */ /* Funny instruction order due to anomaly 05000198, never saw it fail however */ /* */ /*******************************************************************************/ R2 = R2 - R5; // Decrement Transfer Count R0 = [P4+MDMA_S1_CURR_ADDR-MDMA_S1_CONFIG]; // update address pointer R5 = R6; // count = 64k next time R1 = [P4+MDMA_D1_CURR_ADDR-MDMA_S1_CONFIG]; // update address pointer JUMP MEMDMA.NEXT; // next DMA sequence MEMDMA.EXIT: (R7:2, P5:4) = [SP++]; RTS; MEMDMA.END: FDMA.END: /*******************************************************************************/ /* */ /* ZEROFILL_MDMA Subroutine for all Boot Modes . */ /* */ /* When the ZEROFILL flag is set by any boot mode this function is called. */ /* It simply patches the Source channel of the MDMA and calls MEMDMA then. */ /* */ /* This operation uses MDMA0 always. Note that in FIFO mode the MDMA1 is */ /* gated by the DMAR1 and cannot be used for this purpose, therefore. */ /* */ /* The ZERO variable is no longer an 8-bit constant in ROM. Rather it is a */ /* 16-bit value in RAM. This way, the ZEROFILL functionality can be abused */ /* to full memory with other byte values than zero, when an INIT code patches */ /* the ZERO RAM location. M0 is expected to hold the value for zerofill DMA. */ /* */ /* Having ZERO located within L1 memory, reduces speed of zero-filling of L1 */ /* memories, but slightly improves zero-filling to external memory, which may */ /* be much bigger in size. */ /* */ /*******************************************************************************/ ZEROFILL_MDMA: [--SP] = (R7:0,P5:4); [--SP] = M3; [--SP] = RETS; R0 = 0x0; M3 = R0; // temporary disable HWAIT toggle W[P1+LO(MDMA_S0_X_MODIFY)] = R0; // Source Modify = 0 for Zero Fill R0 = W[P1+LO(MDMA_D1_X_MODIFY)] (Z); // copy modify value from MDMA1 W[P1+LO(MDMA_D0_X_MODIFY)] = R0; P4 = M2; // Pointer to ZERO Location R0 = M2; // Source Base Address = ZERO R7 = M0; // source value used for zerofill [P4] = R7; // defaults to zero if not // patched by init codes P4 = P1; P4.L = LO(MDMA_S0_CONFIG); // MDMA routine uses MDMA0 CALL MEMDMA; RETS = [SP++]; M3 = [SP++]; (R7:0,P5:4) = [SP++]; RTS; ZEROFILL_MDMA.END: /*******************************************************************************/ /* */ /* GET_FLASH_TYPE Subroutine for Flash Boot Mode . */ /* */ /* Called prior to booting this routine reads the very first byte of a boot */ /* stream to determine operation mode. This has become a subfunction because */ /* the code is common to the Get_DXE_Address_Flash routine and therefore */ /* called also from there. */ /* */ /* The target address field of the first IGNORE blocks reads to the kernel: */ /* */ /* Address 8-bit flash 16-bit flash */ /* 0x20000000 xx 40 00 60 */ /* 0x20000002 xx 00 FF 80 */ /* 0x20000004 xx 80 cc cc */ /* 0x20000006 xx FF cc cc */ /* */ /* */ /* Code for boot modes BF533 BF537/4 */ /* */ /* 0x10: 32-bit boot no no */ /* 0x20: 16-bit boot no yes */ /* 0x30: reserved no no */ /* 0x40: 8-bit boot yes yes */ /* 0x50: 16-bit DMA on 32-bit flash no no */ /* 0x60: 8-bit DMA on 16-bit flash yes yes */ /* 0x70: 8-bit DMA on 32-bit flash no no */ /* */ /* Note: restrictions apply to 16- and 32-bit modes, as target addresses and */ /* blocks sizes must be divisibly by 2 or 4. */ /* */ /* Parameters: */ /* */ /* P0 = Boot Stream Flash Address Pointer */ /* R1 = 1 expected */ /* */ /* Return Values: */ /* */ /* Code 0x40 0x60 0x20 */ /* R4 1 0 0 Shift Size of Ignore Blocks */ /* R5 2 1 2 Source MDMA Modify */ /* R6 1 1 2 Desitination Modify */ /* M1 0 0 1 MDMA Word Size Indicator */ /* */ /*******************************************************************************/ GET_FLASH_TYPE: R1 = B[P0](Z); // Read the 1st byte from flash memory GET_FLASH_TYPE.FIFO: // FIFO mode always assumes R1 = 0x20 R4 = 0; CC = BITTST(R1,5); IF !CC R4 = R3; R6 = 2; CC = BITTST(R1,6); IF CC R6 = R3; R5 = R4 + R6; R1 = R6 >> 1; M1 = R1; // M1 is used in FDMA/MEMDMA again W[P1+LO(MDMA_S1_X_MODIFY)] = R5; R0 = P0; // Set R0 to the start boot stream // Async Bank 0 (0x20000000) by default RTS; GET_FLASH_TYPE.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from SPI memory (SPI Master Boot) thread . */ /* */ /*******************************************************************************/ /*******************************************************************************/ SPI_MASTER_BOOT: /*******************************************************************************/ /* */ /* By default the SPI memory is expected at chip select SSEL1 (PF10). When */ /* calling this function at run-time, this can be any other pin of Port F. */ /* Also the baudrate is programmable then. */ /* */ /*******************************************************************************/ I1 = PF10(Z); // Set SSEL to PF10 by default R5 = 0x0085(Z); // SPI Baudrate value R3 = 0x0; // SPI start address /*******************************************************************************/ /* */ /* Entry for run-time calls . */ /* */ /*******************************************************************************/ BOOT_DXE_SPI_ENTRY: /*******************************************************************************/ /* */ /* Globally define the SPI_CTL setting using R4 and determine which device */ /* is connected. */ /* */ /*******************************************************************************/ R4 = SPE | MSTR | (TIMOD & 2); CALL SPI_DEVICE_DETECTION; /*******************************************************************************/ /* */ /* Parse Boot Stream Block by Block . */ /* */ /* First DMA 10-bytes header in and evaluate flags */ /* */ /* Bit 0: ZEROFILL */ /* Bit 1: RESVECT (always 1) */ /* Bit 2: reserved */ /* Bit 3: INIT */ /* Bit 4: IGNORE */ /* Bits 8- 5: PFLAG */ /* Bits 10- 9. PPORT */ /* Bits 14-11: reserved */ /* Bits 15: FINAL */ /* */ /*******************************************************************************/ GRAB_HEADER: CALL SPI_ADDRESS; // Address passed in R3. Initial value // is zero. CALL DMA_HEADER; // Fetch header to intermediate memory // R1 = Target Address // R2 = Byte Count // R5 = Flags CALL SET_HWAIT; // Calculate HWAIT out of PFLAG and // PPORT settings CALL DISABLE_SPI; // Disable SPI Interface R3 += 0xA; // Increment external address by header /*******************************************************************************/ /* */ /* Test ZEROFILL Flag . */ /* */ /* If set patch Source Address and start Flash Boot Memory DMA */ /* */ /*******************************************************************************/ CHECK_ZERO_FILL_BIT: CC = BITTST(R5,0); IF !CC JUMP CHECK_IGNORE_BIT; // Zero Fill? CALL ZEROFILL_MDMA; JUMP CHECK_LAST_SECTION; // Check if this is the last block /*******************************************************************************/ /* */ /* Test IGNORE Flag . */ /* */ /* If set incremenent Source Address Pointer R3 and don't perform any DMA. */ /* Enable INIT flag and FINAL flag to be active at the same time. */ /* */ /*******************************************************************************/ CHECK_IGNORE_BIT: CC = BITTST(R5,4); // Ignore ? IF CC JUMP DONT_DMA; /*******************************************************************************/ /* */ /* DMA payload data in . */ /* */ /*******************************************************************************/ DO_DMA: CALL SPI_ADDRESS; // Before setting up SPI DMA, we need // to address the SPI memory CALL SPI_DMA; CALL DISABLE_SPI; // Disable SPI Interface DONT_DMA: R3 = R3 + R2; // increment external address by count // value /*******************************************************************************/ /* */ /* Test INIT Flag . */ /* */ /* If set DMA data in and issue a CALL instruction to the target address. */ /* INIT flag and FINAL flag may now coexist in the same block */ /* */ /*******************************************************************************/ CHECK_INIT_BIT: CC = BITTST(R5,3); IF !CC JUMP CHECK_LAST_SECTION; // Init ? P2 = [P3]; // Block's Destination Address CALL (P2); /*******************************************************************************/ /* */ /* Test FINAL Flag . */ /* */ /* Either fetch next block header or exit and JUMP to reset vector */ /* */ /*******************************************************************************/ CHECK_LAST_SECTION: CC = BITTST(R5,15); // Last Section? IF !CC JUMP GRAB_HEADER; // Otherwise parse next block JUMP BOOT_END; SPI_MASTER_BOOT.END: /*******************************************************************************/ /* */ /* SPI_DEVICE_DETECTION Subroutine for SPI Master Boot Mode . */ /* */ /* The following type of SPI devices are supported: */ /* SPI Flash/EEPROM with 8-bit addressing scheme (1 address byte) */ /* SPI Flash/EEPROM with 16-bit addressing scheme (2 address bytes) */ /* SPI Flash/EEPROM with 24-bit addressing scheme (3 address bytes) */ /* Atmel DataFlash */ /* */ /*******************************************************************************/ SPI_DEVICE_DETECTION: [--SP] = RETS; // Save return address onto stack /*******************************************************************************/ /* */ /* Set Bit Rate . */ /* */ /*******************************************************************************/ W[P1+LO(SPI_BAUD)] = R5; // set baud rate register /*******************************************************************************/ /* */ /* Enable output driver for SPI chip (slave) select. . */ /* */ /* PF10 (SSEL1) is the default SPI slave select, but for use with the */ /* Boot_DXE_SPI Function this is a variable value. Can be any non-SPI GPIO */ /* of Port F. */ /* */ /*******************************************************************************/ R0 = I1; W[P1+LO(PORTFIO_DIR)] = R0; /*******************************************************************************/ /* */ /* Start with device detection and return result in R6 . */ /* */ /* R6 = 0x3 for 8-bit SPI Memory */ /* R6 = 0x2 for 16-bit SPI Memory */ /* R6 = 0x1 for 24-bit SPI Memory */ /* R6 = 0x0 for ATMEL DataFlash Memory */ /* */ /*******************************************************************************/ PART_SELECT: R1 = 0xFF; // Test value for receive byte R6 = 0x3; // set R6 = 0x3 for 8-bit part R0 = SPE | MSTR | (TIMOD & 1); // 0x5001; W[P1+LO(SPI_CTL)] = R0; // enable SPI, non-DMA TX Mode, 8 bits R0 = I1; // default is PF10 W[P1+LO(PORTFIO_CLEAR)] = R0; // assert CS low R0 = 0x3; CALL SINGLE_BYTE_TRANSFER; // send out control word R0 = 0x0; CALL SINGLE_BYTE_TRANSFER; // send out address byte (address = 0) /*******************************************************************************/ /* */ /* If the byte received in the third transfer is not equal 0xFF then it is */ /* an 8-bit addressable SPI memory. 16- and 24-bit devices don't send data */ /* yet and the MISO line is still in high impedance state. That is why a */ /* pull-up resistor is required on the MISO signal. */ /* */ /*******************************************************************************/ EIGHT_BIT_CHECK: R0 = 0x0; // send out dummy byte for 8-bit devices CALL SINGLE_BYTE_TRANSFER; // or second address byte otherwise CC = R0 == R1; // does the received byte equal 0xFF ? IF !CC JUMP SPI_DEVICE_DETECTION.EXIT; // otherwise it is an 8-bit part /*******************************************************************************/ /* */ /* If the byte received in the fourth transfer is not equal 0xFF then it is */ /* a 16-bit addressable SPI memory. 24-bit devices don't send data yet and */ /* the MISO line is still in high impedance state. */ /* */ /*******************************************************************************/ SIXTEEN_BIT_CHECK: R0 = 0x0; // send out dummy byte for 16-bit devices CALL SINGLE_BYTE_TRANSFER; // or third byte otherwise CC = R0 == R1; // does the received byte equal 0xFF ? R6 += -1; // set R6 = 0x2 for 16-bit parts IF !CC JUMP SPI_DEVICE_DETECTION.EXIT; // if not 0xFF it is a 16-bit part /*******************************************************************************/ /* */ /* If the byte received in the fifth transfer is not equal 0xFF then it is */ /* a 24-bit addressable SPI memory. Atmel DataFlash devices still tri-state */ /* the MISO pin. */ /* */ /*******************************************************************************/ TWENTYFOUR_BIT_CHECK: R0 = 0x0; // send out dummy byte CALL SINGLE_BYTE_TRANSFER; CC = R0 == R1; // does the received byte equal 0xFF ? R6 += -1; // set R6 = 0x1 for 24-bit parts IF !CC JUMP SPI_DEVICE_DETECTION.EXIT; // if not 0xFF it is a 24-bit part ATMEL_PART: R6 += -1; // if all of the tests above fail, we are CALL READ_STATUS_REGISTER; // going to assume we have an ATMEL // DataFlash Memory connected SPI_DEVICE_DETECTION.EXIT: CALL DISABLE_SPI; // disable SPI module again RETS = [SP++]; // Restore return address from stack RTS; SPI_DEVICE_DETECTION.END: /*******************************************************************************/ /* */ /* DMA_HEADER Subroutine for SPI Master and Slave Boot Mode */ /* */ /* It fetches the next 10-byte header in by DMA. L1 memory is used for */ /* intermediate storage, but then result is copied to registers: */ /* */ /* R1 = Target Address (4 bytes) */ /* R2 = Byte Count (4 bytes) */ /* R5 = Flags (2 bytes) */ /* */ /*******************************************************************************/ DMA_HEADER: [--SP] = RETS; R1 = 1; W[P4+DMA7_X_MODIFY-DMA7_CONFIG] = R1; // SPI DMA Modify = 1 R1 = P3; // DMA Destination = 0xFF807FF0 R2 = 0xA; // DMA Count = 10 CALL SPI_DMA; // instruction order because of // anomaly #05000209 R5.L = 0x1001; // Deposit 1 bit at bit position = 16 R1 = [P3]; // Target Address R2 = [P3+0x4]; // Byte Count R0 = W[P3+0x8](Z); // Flags R5 = DEPOSIT(R0, R5); // After this instruction R5.L = R0.L // and bit0 of R5.H will be retrieved RETS = [SP++]; RTS; DMA_HEADER.END: /*******************************************************************************/ /* */ /* SPI_DMA Subroutine for SPI Master, SPI Slave and UART Slave Boot Mode */ /* */ /* It starts a SPI DMA and wait until it completes */ /* It supports 32-bit byte counts including from 0 */ /* */ /* R1 = Destination Start Address */ /* Updated by the routine (R1 = R1 + R2) */ /* */ /* R2 = Byte Count (0..2^32-1) */ /* */ /* */ /* R4 = SPI CTL register value */ /* SPI master: R4=SPE | MSTR | (TIMOD & 2) */ /* SPI slave: R4=SPE | CPHA | (TIMOD & 2) */ /* UART: R4=0 */ /* */ /* P4 = DMA Pointer */ /* SPI master: P4=DMA7_CONFIG */ /* SPI slave: P4=DMA7_CONFIG */ /* UART: P4=DMA8_CONFIG */ /* */ /* P5 = HWAIT Toggle Register */ /* M3 = HWAIT GPIO Number */ /* */ /* */ /*******************************************************************************/ SPI_DMA: [--SP] = (R7:2); SPI_DMA.NEXT: CC = R2 == 0x0; IF CC JUMP SPI_DMA.EXIT; // guard against zero byte count SSYNC; R3.L = 0x0C00; // Only enable UART/SPI to wake up [P1 + LO(SIC_IWR)] = R3; // the core from idle state [P4+DMA7_START_ADDR-DMA7_CONFIG] = R1; // Set Destination Base Address W[P4+DMA7_X_COUNT-DMA7_CONFIG] = R2; // Set Destination Count R3.L = DI_EN | WDSIZE_8 | WNR | DMAEN; // 0x0083: SPI DMAConfig = // DMA Enable, 8-bit Memory Write, // 1-D DMA, Flow = Stop, W[P4+DMA7_CONFIG-DMA7_CONFIG] = R3; // Interrupt on Completion /*******************************************************************************/ /* */ /* Wait for DMA to complete. DMA_DONE IRQ wakes core up from idle state. */ /* Clear interrupt request afterward. */ /* */ /* HWAIT signal indicates that kernel is ready to consume data. */ /* M3 holds the HWAIT GPIO number. P5 holds GPIO toggle register address. */ /* */ /*******************************************************************************/ R3 = M3; W[P5] = R3; // Toggle HWAIT Flag (enable) W[P1 + LO(SPI_CTL)] = R4; // R4 defined globally IDLE; // Wait for DMA to Complete W[P5] = R3; // Toggle HWAIT Flag (disable) R3 = 0x1; W[P4+DMA7_IRQ_STATUS-DMA7_CONFIG] = R3; // Write 1 to clear DMA interrupt /*******************************************************************************/ /* */ /* If the total transfer count was greater than 64k the DMA will be started */ /* again. Next start address and count value needs to be calculated for the */ /* next run. Since DMA may have modify values of either 0 or 1 it is simpler */ /* to read the CURR_ADDR register than calculating the new address by math. */ /* */ /* If the count was greater than 64k then the first run processed the */ /* reminder, if there was any. All further DMA sequences perform full 64k */ /* transfer count. R2 holds the count value. */ /* */ /*******************************************************************************/ R6 = R3 << 16; // R6 = 0x0001.0000 R5 = R2.L (Z); // copy lower count bits CC = R5 == 0; // R5.H = 0 IF R2.L != 0 IF CC R5 = R6; // R5.H = 1 IF R2.L == 0 R2 = R2 - R5; // substract count already processed R1 = [P4+DMA7_CURR_ADDR-DMA7_CONFIG]; // return next DMA address CC = BITTST(R4,12); // Disable in SPI Master mode IF !CC JUMP SPI_DMA.NEXT; // except in UART and SPI Slave R3 = 0; W[P4+DMA7_CONFIG-DMA7_CONFIG] = R3; // clear DMA enable JUMP SPI_DMA.NEXT; SPI_DMA.EXIT: (R7:2) = [SP++]; RTS; SPI_DMA.END: /*******************************************************************************/ /* */ /* SPI_ADDRESS Subroutine for SPI Master Boot Mode */ /* */ /* Sends a new Address Command to SPI memory device, Address passed in R3. */ /* */ /* Recall: */ /* R6 = 0x3 for 8-bit SPI Memory */ /* R6 = 0x2 for 16-bit SPI Memory */ /* R6 = 0x1 for 24-bit SPI Memory */ /* R6 = 0x0 for ATMEL DataFlash Memory */ /* */ /*******************************************************************************/ SPI_ADDRESS: [--SP] = RETS; // Save return address onto the stack I0 = R3; // save logical address in I0 R0 = SPE | MSTR | (TIMOD & 1); // 0x5001; W[P1+LO(SPI_CTL)] = R0; // enable SPI, non-DMA TX mode, 8 bits R0 = I1; // default is PF10 W[P1+LO(PORTFIO_CLEAR)] = R0; // assert slave select low CC = R6 < 1; IF !CC JUMP ADDRESS_8_16_24; ATMEL: CALL ADDRESS_DECODE; // convert logical address into a page number // and byte number R0 = 0xE8; // control word for ATMEL SPI Memory CALL SINGLE_BYTE_TRANSFER; // send out control word JUMP ADDRESS_24; ADDRESS_8_16_24: // normal SPI devices R0 = 0x3; // control word for 8-/16-/24-bit SPI Memory CALL SINGLE_BYTE_TRANSFER; // send out control word CC = R6 < 2; IF !CC JUMP ADDRESS_16; ADDRESS_24: R0 = R3 >> 16; // send out upper address byte CALL SINGLE_BYTE_TRANSFER; ADDRESS_16: CC = R6 < 3; IF !CC JUMP ADDRESS_8; R0 = R3 >> 8; // send out middle address byte CALL SINGLE_BYTE_TRANSFER; ADDRESS_8: R0 = R3; // send out lower address byte CALL SINGLE_BYTE_TRANSFER; CC = R6 < 1; IF !CC JUMP SPI_ADDRESS.EXIT; P2 = 0x4; // send out 4 don't care bytes LSETUP(ATMEL_Begin, ATMEL_End) LC0=P2; // for ATMEL SPI Memory ATMEL_Begin: R0 = R3; CALL SINGLE_BYTE_TRANSFER; ATMEL_End: NOP; R3 = I0; // restore logical address from I0 SPI_ADDRESS.EXIT: R0 = SPE | MSTR | (TIMOD & 0); // 0x5000; W[P1+LO(SPI_CTL)] = R0; // set TIMOD = 00 for SPI port RETS = [SP++]; // Restore return address from the stack RTS; SPI_ADDRESS.END: /*******************************************************************************/ /* */ /* SINGLE_BYTE_TRANSFER Subroutine for SPI Master Boot Mode */ /* */ /* Initiates a single transfer by transmitting the value stored in R0. */ /* The received value is returned in R0 again. */ /* */ /*******************************************************************************/ SINGLE_BYTE_TRANSFER: W[P1+LO(SPI_TDBR)] = R0; // send out byte and start transfer SSYNC; WAIT_FOR_RXS: R0 = W[P1+LO(SPI_STAT)] (Z); // test bit 5 (RXS) of SPI_STAT register to CC = BITTST(R0,5); // see if the RX Data Buffer is full, IF !CC JUMP WAIT_FOR_RXS; // if 0 repeat test R0 = W[P1+LO(SPI_RDBR)] (Z); // read buffer and return in R0 RTS; SINGLE_BYTE_TRANSFER.END: /*******************************************************************************/ /* */ /* DISABLE_SPI Subroutine for SPI Master Boot Mode */ /* */ /* It disables the SPI port and holds it disabled for 500ns */ /* */ /*******************************************************************************/ DISABLE_SPI: R0 = 0x0000; W[P1+LO(SPI_CTL)] = R0; // disable SPI R0 = I1; // default is PF10 W[P1+LO(PORTFIO_SET)] = R0; // de-assert CS P2 = 0x01A4; // Delay of 500ns at SCLK=600MHz LSETUP(DELAY_LOOP, DELAY_LOOP) LC0=P2; DELAY_LOOP: NOP; RTS; DISABLE_SPI.END: /*******************************************************************************/ /* */ /* ADDRESS_DECODE Subroutine for SPI Master Boot Mode */ /* */ /* It converts a logical address to a page number and a byte number for the */ /* ATMEL SPI DataFlash. */ /* AT45DB041 has 11 page bits and 9 byte bits (264 bytes/page), R5.H = 0x0 */ /* AT45DB081 has 12 page bits and 9 byte bits (264 bytes/page), R5.H = 0x0 */ /* AT45DB161 has 12 page bits and 10 byte bits (528 bytes/page), R5.H = 0x1 */ /* AT45DB642 has 12 page bits and 11 byte bits (1056 bytes/page), R5.H = 0x2 */ /* */ /*******************************************************************************/ ADDRESS_DECODE: ATMEL_161: R7 = 0x210; // 528 bytes per page for AT45DB161 CC = BITTST(R5, 16); // bit 16 set ==> 528 bytes/page IF CC JUMP ADDRESS_DECODE.COMPARE; CC = BITTST(R5, 17); // bit 17 set ==> 1056 bytes/page IF CC JUMP ATMEL_642; ATMEL_041_081: R7 = 0x108; // 264 bytes per page for AT45DB041 JUMP ADDRESS_DECODE.COMPARE; // and AT45DB081 ATMEL_642: R7 = 0x420; // 1056 bytes per page ADDRESS_DECODE.COMPARE: R0 = 0x0; // R0 holds the page number CC = R3 <= R7; // Is the logical address less than IF CC JUMP LESS_THAN_PAGE; // bytes/page? ADD_PAGE_SUBTRACT: R0 += 1; // If not, add 1 to page number and R3 = R3 - R7; // subtract bytes/page from logical JUMP ADDRESS_DECODE.COMPARE; // address. Then, compare again LESS_THAN_PAGE: CC = R3 == R7; // If so, check if we have exactly IF !CC JUMP FINISHED_DECODING; // one page. R3 = 0x0; // If exact page, byte number = 0 R0 += 1; // and increment page number FINISHED_DECODING: R0 = R0 << 11; // left shift page number by 11 for CC = BITTST(R5,17); // the 1056 bytes/page devices IF CC JUMP ADDRESS_DECODE.EXIT; R0 = R0 >> 1; // down shift to 10 bits (# page bits) CC = BITTST(R5,16); // for the 528 bytes/page devices IF CC JUMP ADDRESS_DECODE.EXIT; R0 = R0 >> 1; // down shift to 9 bits (# page bits) ADDRESS_DECODE.EXIT: R3 = R0 | R3; // OR page number and byte number R7 = 0x0; // restore R7 to 0x0 for SPI Slave use RTS; ADDRESS_DECODE.END: /********************************************************************************/ /* */ /* READ_STATUS_REGISTER Subroutine for SPI Master Boot Mode */ /* */ /* It reads the status register of an Atmel DataFlash device to determine */ /* which device is connected. */ /* */ /* AT45DB041 has bits 5:2 set to 0111 (set R5.H = 0x0) */ /* AT45DB081 has bits 5:2 set to 1001 (set R5.H = 0x0) */ /* AT45DB161 has bits 5:2 set to 1011 (set R5.H = 0x1) */ /* AT45DB642 has bits 5:2 set to 1111 (set R5.H = 0x2) */ /* AT45DB1282 has bits 5:2 set to 0100 (set R5.H = 0x2) */ /* */ /********************************************************************************/ READ_STATUS_REGISTER: [--SP] = RETS; // Save return address onto stack CALL DISABLE_SPI; // disable SPI for new transfer R0 = SPE | MSTR | (TIMOD & 1); // 0x5001; W[P1+LO(SPI_CTL)] = R0; // enable SPI, non-DMA TX Mode, 8 bits R0 = I1; // variable for Boot_DXE_SPI Function W[P1+LO(PORTFIO_CLEAR)] = R0; // (default is PF10) assert CS low R0 = 0xD7; // send out control word to CALL SINGLE_BYTE_TRANSFER; // read status register CALL SINGLE_BYTE_TRANSFER; // result in R0 R5.H = 0x2; // For 1056 bytes/page group R0 = R0 >> 2; // Right align Density status info R1 = 0xF; // AT45DB642 R0 = R0 & R1; // lower nibble mask P2 = 0x1; // 1 devices in the 1056 bytes/page group CALL PAGE_SIZE; R1 = 0x4; // For the new upcoming device AT45DB1282D CALL PAGE_SIZE; // it has same paging scheme as the DB642 R5.H = 0x1; R1 = 0xB; P2 = 0x2; // 2 devices in the 528 bytes/page group CALL PAGE_SIZE; R5.H = 0x0; R1 = 0x3; P2 = 0x4; // 4 devices in the 264 bytes/page group CALL PAGE_SIZE; READ_STATUS_REGISTER.EXIT: RETS = [SP++]; RTS; PAGE_SIZE: LSETUP(Page_S, Page_End) LC0 = P2; Page_S: CC = R0 == R1; IF CC JUMP READ_STATUS_REGISTER.EXIT; Page_End: R1 += 0x2; RTS; PAGE_SIZE.END: READ_STATUS_REGISTER.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* BOOT_MENU. Main routine called after hardware and software reset, except */ /* in bypass mode (BMODE=000). This is placed in the middle of the file to */ /* to save code space due to the many conditional far jumps. */ /* */ /*******************************************************************************/ /*******************************************************************************/ BOOT_MENU: /*******************************************************************************/ /* */ /* Initialize Global Registers and set Supervisor Stack Pointer to L1 Scratch */ /* Memory */ /* */ /*******************************************************************************/ CALL Global_Setup; /*******************************************************************************/ /* */ /* Test Bit 4 of SYSCR Register whether need to reboot on software reset. */ /* Otherwise bypass boot sequence and JUMP to reset vector */ /* */ /*******************************************************************************/ R0 = W[P1+LO(SYSCR)] (Z); CC = BITTST(R0,4); // save R0 for boot menu IF CC JUMP SWRESET; HARD_RESET: R1.H = HI(L1_Code); // define L1 reset vector on hard reset only R1.L = LO(L1_Code); // can be patched by either init codes or [P0+LO(EVT1)] = R1; // user programs /*******************************************************************************/ /* */ /* JUMP to respective boot threads as programmed by the three BMODE pins. */ /* */ /* BMODE[2:0] = 000 Bypass (no-boot) mode, bootkernel is not even invoked */ /* 001 Boot from 8-bit or 16-bit parallel flash */ /* 010 reserved */ /* 011 SPI master boot from 8/16/24-bit memory or DataFlash */ /* 100 SPI slave boot from host */ /* 101 TWI master boot from memory */ /* 110 TWI slave boot from host */ /* 111 UART slave boot from host */ /* */ /* The following functions are enabled in the individual boot modes: */ /* (PORT_MUX and PORTF_FER are zero by default) */ /* */ /* PF14 - SPI SS */ /* PF13 - SPI SCK */ /* PF12 - SPI MISO */ /* PF11 - SPI MOSI */ /* PF1 - UART0 RX and DMAR1 */ /* PF0 - UART0 TX */ /* */ /*******************************************************************************/ R2 = 0x0007; // mask the three BMODE bits R0 = R0 & R2; CC = R0 == 0x1; // BMODE = 001, Boot from Flash memory IF CC JUMP FLASH_BOOT; CC = R0 == 0x2; // BMODE = 010, Boot from FIFO device IF CC JUMP FIFO_BOOT; R1 = 5; // BMODE = 101, Boot from TWI memory CC = R0 == R1; IF CC JUMP TWI_MASTER_BOOT; R1 = 6; // BMODE = 110, Boot from TWO host CC = R0 == R1; IF CC JUMP TWI_SLAVE_BOOT; CC = R0 == R2; // BMODE = 111, Boot from UART host IF CC JUMP UART_BOOT; R1 = PF14 | PF13 | PF12 | PF11 (Z); // Function enable for SPI signals W[P1 + LO(PORTF_FER)] = R1; CC = R0 == 3; // BMODE = 011, Boot from SPI memory IF CC JUMP SPI_MASTER_BOOT; JUMP SPI_SLAVE_BOOT; // BMODE = 100, Boot from SPI host BOOT_MENU.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from UART host (UART Slave Boot) thread on UART 0 . */ /* */ /*******************************************************************************/ /*******************************************************************************/ UART_BOOT: R1 = PF1 | PF0; // function enable on UART signals W[P1 + LO(PORTF_FER)] = R1; /*******************************************************************************/ /* */ /* Perform bitrate detection. By assuming an autobaud detection character */ /* of 0x40 ('@') Timer 1 captures the period between the falling edge which */ /* is leading the start bit and the falling edge after bit 7. This period */ /* encloses 8 bit times. Eight data bits but no parity bit assumed. */ /* */ /* First of all, enable UART clock. */ /* */ /*******************************************************************************/ R1 = UCEN; W[P1+LO(UART0_GCTL)] = R1; /*******************************************************************************/ /* */ /* Activate Loopback mode in order the receive channel is disconnected */ /* from RX pin during autobaud detection. LOOPBACK bit was called LOOP in */ /* earlier VisualDSP++ revisions. */ /* */ /*******************************************************************************/ #ifndef LOOPBACK #define LOOPBACK 0x0010 #endif R1 = LOOPBACK; W[P1+LO(UART0_MCR)] = R1; /*******************************************************************************/ /* */ /* Disable Timer 1 and clear all latches in case of unexpected history */ /* */ /*******************************************************************************/ CALL TIMER1_DISABLE; /*******************************************************************************/ /* */ /* Setup Timer1 to capture period between two falling edges on UART0 RXD pin. */ /* Enable IRQ_ENA, but don't enable the interrupt at system level (SIC). */ /* */ /*******************************************************************************/ R1 = TIN_SEL | IRQ_ENA | PERIOD_CNT | WDTH_CAP; W[P1 + LO(TIMER1_CONFIG)] = R1; /*******************************************************************************/ /* */ /* Start the timer and wait until the according interrupt latch bit TIMIL1 */ /* in the TIMER_STATUS register is set. Then, two falling edges on the RxD */ /* pin have been detected. */ /* */ /*******************************************************************************/ R1 = TIMEN1; W[P1 + LO(TIMER_ENABLE)] = R1; UART_WAIT_AUTOBAUD: R1 = W[P1 + LO(TIMER_STATUS)] (Z); CC = BITTST (R1,1); IF !CC JUMP UART_WAIT_AUTOBAUD; /*******************************************************************************/ /* */ /* Disable Timer 1 again and save captured period value into R0 */ /* */ /*******************************************************************************/ CALL TIMER1_DISABLE; R0 = [P1 + LO(TIMER1_PERIOD)]; /*******************************************************************************/ /* */ /* In order to support also half-duplex connections, we need to delay any */ /* transmission, so that the sent character does not overlap the autobaud */ /* pattern. */ /* */ /* Use Timer 1 to perform this delay. Note that the Period Register still */ /* contains the proper value and also the Width Register has any meaningful */ /* content. */ /* */ /*******************************************************************************/ R1 = OUT_DIS | IRQ_ENA | PERIOD_CNT | PWM_OUT; W[P1 + LO(TIMER1_CONFIG)] = R1; R1 = TIMEN1; W[P1 + LO(TIMER_ENABLE)] = R1; UART_WAIT_DELAY: R1 = W[P1 + LO(TIMER_STATUS)] (Z); CC = BITTST (R1,1); IF !CC JUMP UART_WAIT_DELAY; /*******************************************************************************/ /* */ /* Disable Timer 1 and loopback mode again */ /* */ /*******************************************************************************/ CALL TIMER1_DISABLE; R1 = 0; W[P1 + LO(UART0_MCR)] = R1; /*******************************************************************************/ /* */ /* Autobaud detection done. Result returned in R0. Since the value in R0 */ /* represents 8 bit times, apply formula DL = PERIOD / (16 x 8 bits) and */ /* write the result to the two 8-bit DL registers (DLH:DLL). */ /* */ /*******************************************************************************/ R0 >>= 7; // 16*8 = 128 = 2^7 R1 = DLAB; W[P1 + LO(UART0_LCR)] = R1; // DLAB enables access to DLL and DLH W[P1 + LO(UART0_DLL)] = R0; R1 = R0 >> 8; W[P1 + LO(UART0_DLH)] = R1; /*******************************************************************************/ /* */ /* Clear DLAB again and set UART frame to 8 data bits, no parity, 1 stop bit. */ /* */ /*******************************************************************************/ R1 = WLS(8); W[P1 + LO(UART0_LCR)] = R1; /*******************************************************************************/ /* */ /* Signal the completion of the autobaud detection to the host. */ /* Four bytes are transmitted back to the host: */ /* */ /* 0xBF (which stands for Blackfin) */ /* UART_DLL value */ /* UART_DLH value */ /* 0x00 to terminate string */ /* */ /*******************************************************************************/ R1 = 0xBF; W[P1 + LO(UART0_THR)] = R1; P2 = 0x3; // send four bytes LSETUP (UART_TX_BEGIN, UART_TX_END) LC0 = P2; UART_TX_BEGIN: R1 = W[P1 + LO(UART0_LSR)] (Z); CC = BITTST(R1,5); IF !CC JUMP UART_TX_BEGIN; W[P1 + LO(UART0_THR)] = R0; // Send UART_DLL and UART_DLH values UART_TX_END: R0 = R0 >> 8; // UART_DLH value /*******************************************************************************/ /* */ /* Jump into SPI boot routine as the rest of the boot sequence is almost */ /* identical. Bit 1 of R7 is used by those subroutines that distinguish */ /* between UART and SPI boot where required. */ /* */ /*******************************************************************************/ R4 = 0; // do not enable SPI_CTL in SPI_DMA routine P4.L = LO(DMA8_CONFIG); // Use DMA8 instead of DMA7 R1 = ERBFI (Z); // in UART mode, enable ERBFI bit W[P1 + LO(UART0_IER)] = R1; // zero in both SPI modes JUMP UART_ENTRY; /*******************************************************************************/ /* */ /* Simply disables Timer 1 and clears all irq flags */ /* */ /*******************************************************************************/ TIMER1_DISABLE: R1 = TIMDIS1; W[P1+LO(TIMER_DISABLE)] = R1; R1 = TRUN1 | TOVL_ERR1 | TIMIL1; W[P1 + LO(TIMER_STATUS)] = R1; RTS; TIMER1_DISABLE_END: UART_BOOT_END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from SPI host (SPI Slave Boot) thread . */ /* This is also the core for UART boot */ /* */ /*******************************************************************************/ /*******************************************************************************/ SPI_SLAVE_BOOT: R4 = SPE | CPHA | (TIMOD & 2); // value for SPI_CTL register /*******************************************************************************/ /* */ /* Entry point for UART boot */ /* */ /*******************************************************************************/ UART_ENTRY: /*******************************************************************************/ /* */ /* Parse Boot Stream Block by Block . */ /* */ /* First DMA 10-bytes header in and evaluate flags */ /* */ /* Bit 0: ZEROFILL */ /* Bit 1: RESVECT (always 1) */ /* Bit 2: reserved */ /* Bit 3: INIT */ /* Bit 4: IGNORE */ /* Bits 8- 5: PFLAG */ /* Bits 10- 9: PPORT */ /* Bits 14-11: reserved */ /* Bits 15: FINAL */ /* */ /*******************************************************************************/ SSB_GRAB_HEADER: CALL DMA_HEADER; // load next 10 bytes CALL SET_HWAIT; // Determine HWAIT pin /*******************************************************************************/ /* */ /* Test ZEROFILL Flag . */ /* */ /* If set use the Memory DMA of the Flash Boot Mode to zero number of bytes. */ /* Set Feedback PF Signal while MDMA is ongoing to signal host that device */ /* is busy for a while. */ /* */ /* Note that the ZEROFILL flag may coexist with the FINAL flag. */ /* */ /*******************************************************************************/ SSB_CHECK_ZERO_FILL_BIT: CC = BITTST(R5,0); IF !CC JUMP SSB_CHECK_IGNORE_BIT; // Zero Fill? CALL ZEROFILL_MDMA; JUMP SSB_CHECK_LAST_SECTION; /*******************************************************************************/ /* */ /* Test IGNORE Flag . */ /* */ /* If set set the DMA target modifier to zero and patch the DMA target */ /* address in order to trash received data */ /* */ /*******************************************************************************/ SSB_CHECK_IGNORE_BIT: CC = BITTST(R5,4); IF !CC JUMP SSB_DO_DMA; R0 = 0x0; W[P4+DMA7_X_MODIFY-DMA7_CONFIG] = R0; R1 = M2; // Set the SPI DMA Start Address to // 0xFF807FFC (4th to last location of // L1 Data Bank A) /*******************************************************************************/ /* */ /* Perform DMA and restore Modify value again . */ /* */ /*******************************************************************************/ SSB_DO_DMA: CALL SPI_DMA; R0 = 0x0001; W[P4+DMA7_X_MODIFY-DMA7_CONFIG] = R0; /*******************************************************************************/ /* */ /* Test INIT Flag . */ /* */ /* If set issue a CALL instruction to the target address. */ /* */ /* Note that the INIT flag may coexist with FINAL and IGNORE flags. */ /* */ /*******************************************************************************/ SSB_CHECK_INIT_BIT: CC = BITTST(R5,3); IF !CC JUMP SSB_CHECK_LAST_SECTION; P2 = [P3]; CALL (P2); /*******************************************************************************/ /* */ /* Test FINAL Flag . */ /* */ /* If set JUMP to the reset vector. Otherwise load next header and continue. */ /* */ /*******************************************************************************/ SSB_CHECK_LAST_SECTION: CC = BITTST(R5,15); // Last Section? IF CC JUMP BOOT_END; JUMP SSB_GRAB_HEADER; // JUMP to GRAB_HEADER if not done SPI_SLAVE_BOOT_END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from TWI memory (TWI Master Boot) thread . */ /* This is also the core for TWI host boot (TWI Slave Boot) */ /* */ /*******************************************************************************/ /*******************************************************************************/ TWI_MASTER_BOOT: /*******************************************************************************/ /* */ /* First initialize TWI controller and then start booting from I2C address */ /* 000. Note that there could be multiple memory devices out there. */ /* */ /*******************************************************************************/ R5 = 0x0811 (Z); // TWI_CLKDIV value to produce 400 KHz SCL in a // ~30% duty cycle R6 = 0xA0; // R6 holds the addressed memory device R4 = 0x0; // EEPROM address pointer. Start from zero TWI_BOOT_DXE_ENTRY: // if called at run time CALL TWI_INIT; /*******************************************************************************/ /* */ /* Parse Boot Stream Block by Block . */ /* */ /* First load 10-bytes header in and evaluate flags */ /* */ /* Bit 0: ZEROFILL */ /* Bit 1: RESVECT (always 1) */ /* Bit 2: reserved */ /* Bit 3: INIT */ /* Bit 4: IGNORE */ /* Bits 8- 5: PFLAG */ /* Bits 10- 9: PPORT */ /* Bits 14-11: reserved */ /* Bits 15: FINAL */ /* */ /* Fetches the next 10-byte Block Header, and updated working registers */ /* accordingly. L1 memory is used for temporary storage. */ /* */ /*******************************************************************************/ TWI_SLAVE_ENTRY: // routine also used by TWI slave mode TWI_GRAB_HEADER: R5 = 0xA; // initial number of bytes to transfer (read) P3 = R5; // P3 holds the TWI read loop count R5.H = 0x0A; // R5.H holds the count P2.H = Boot_PayLoad; // L1 memory for intermediate storage P2.L = Boot_PayLoad; CALL TWI_READ; R1 = [P2]; // destination address R2 = [P2+0x4]; // byte count R5 = W[P2+0x8](Z); // flags CALL SET_HWAIT; P0 = R1; // P0 = Destination Address R4 += 0xA; // Increment external address by header size /*******************************************************************************/ /* */ /* Test ZEROFILL Flag . */ /* */ /* If set Flash Boot's FDMA function is called to perform a Memory DMA that */ /* zeroes the destination. */ /* */ /*******************************************************************************/ TWI_CHECK_ZERO_FILL_BIT: CC = BITTST(R5,0); IF !CC JUMP TWI_CHECK_IGNORE_BIT; // Zero Fill? CALL ZEROFILL_MDMA; JUMP TWI_CHECK_LAST_SECTION; /*******************************************************************************/ /* */ /* Test IGNORE Flag . */ /* */ /* If set skip the payload and modify EEPROM pointer */ /* */ /*******************************************************************************/ TWI_CHECK_IGNORE_BIT: CC = BITTST(R5,4); IF !CC JUMP TWI_DO_DMA; /*******************************************************************************/ /* */ /* Different handling in master and slave mode: . */ /* */ /* Master mode simply increments address pointer, while slave mode must */ /* actively consume data bytes and trash them. */ /* */ /*******************************************************************************/ CC = BITTST(R7,2); IF CC JUMP TWI_SLAVE_IGNORE; TWI_MASTER_IGNORE: // just increment address pointer R4 = R4 + R2; // Update Source Base Address JUMP TWI_CHECK_INIT_BIT; // Check if this is the last block TWI_SLAVE_IGNORE: // trash ignore data actively R0 = 0x0; W[P1+LO(MDMA_D1_X_MODIFY)] = R0; // Set MDMA Modify to 0 R1 = M2; // Patch Destination Address // M2 points to 0xFF807FFC /*******************************************************************************/ /* */ /* Need to bring in more data thru IIC before calling DMA. */ /* */ /* Check Count (R2) to see if < 254, if > 254 need to call the */ /* TWI_READ routine and DMA to instruction L1 multiple times */ /* */ /* Need: */ /* P3 = LOOP COUNT */ /* P2 = BOOT_PAYLOAD */ /* R5.H = Data_Count */ /* */ /* R2 holds the count from TWI_HEADER */ /* */ /*******************************************************************************/ TWI_DO_DMA: CALL BringInApplication; R4 = R4 + R2; R0 = 0x1; // Reset MDMA Destination Modify to 1, W[P1+LO(MDMA_D1_X_MODIFY)] = R0; // It was set to zero for Ignore blocks /*******************************************************************************/ /* */ /* Test INIT Flag . */ /* */ /* If set issue a CALL instruction to the target address. */ /* */ /*******************************************************************************/ TWI_CHECK_INIT_BIT: CC = BITTST(R5,3); IF !CC JUMP TWI_CHECK_LAST_SECTION; CALL (P0); /*******************************************************************************/ /* */ /* Test FINAL Flag . */ /* */ /*******************************************************************************/ TWI_CHECK_LAST_SECTION: CC = BITTST(R5,15); // Last Section? IF !CC JUMP TWI_GRAB_HEADER; // If not, JUMP to grab header JUMP BOOT_END; TWI_MASTER_BOOT_END: /*******************************************************************************/ /* */ /* TWI_READ Subroutine for TWI slave and master boot modes */ /* */ /*******************************************************************************/ TWI_READ: [--SP] = RETS; [--SP] = (R7:1,P5:0); CC = BITTST(R7,2); IF CC JUMP TWI_READ.SLAVE; // master vs slave mode /*******************************************************************************/ /* */ /* Version for TWI Master Boot */ /* */ /*******************************************************************************/ TWI_READ.MASTER: /*******************************************************************************/ /* */ /* Send R4.L = 0xB1B0 address. First B0 and then B1 */ /* */ /*******************************************************************************/ R1 = R4 >> 8; W[P1 + LO(TWI_XMT_DATA8)] = R1; // Send out the EEPROM's MSB address W[P1 + LO(TWI_XMT_DATA8)] = R4; // Send out the EEPROM's LSB address // Send address to read from R1 = 0x00A9 (Z); // 2 address bytes, fast mode; restart; W[P1 + LO(TWI_MASTER_CTL)] = R1; // enable TWI_TX_FIFO: R1 = W[P1 + LO(TWI_FIFO_STAT)](Z); CC = R1; // stay here until fifo is empty, IF CC JUMP TWI_TX_FIFO; // i.e., R1 = 0 CALL M_COMP; R1 = 0xD ; // Base value: set MEN, MDIR (read), R5.L = 0x0608; // FAST the 5 LSBs contain the field // length to be deposited. // bits 12:8 contain the start bit // position from which to start the // bit field deposition R1 = DEPOSIT (R1, R5); // Now R1 contains the count value in W[P1 + LO(TWI_MASTER_CTL)] = R1; // the bit field 13:6 /*******************************************************************************/ /* */ /* Version for TWI Slave Boot and Part of Master Boot */ /* */ /*******************************************************************************/ TWI_READ.SLAVE: R6 = M3; W[P5] = R6; // Toggle HWAIT Flag (enable) /*******************************************************************************/ /* */ /* Loop to read data from IIC device P3 times */ /* */ /*******************************************************************************/ LSETUP (TWI_LOOP.START, TWI_LOOP.END) LC1 = P3; TWI_LOOP.START: R1 = W[P1 + LO(TWI_FIFO_STAT)](Z); CC = BITTST (R1, 2); // CC is set if there's a IF !CC JUMP TWI_LOOP.START; // byte in the RX FIFO R3 = W[P1 + LO(TWI_RCV_DATA8)] (Z); // read data TWI_LOOP.END: B[P2++] = R3; // store bytes into // Boot_PayLoad buffer CC = BITTST(R7,2); // Don't check Master Complete (MCOMP) IF CC JUMP TWI_READ.EXIT; // for Slave mode boot CALL M_COMP; // make sure transfer complete: // Master Complete bit will be set TWI_READ.EXIT: W[P5] = R6; // Toggle HWAIT Flag (disable) (R7:1,P5:0) = [SP++]; RETS = [SP++]; RTS; TWI_READ.END: /*******************************************************************************/ /* */ /* TWI_INIT Subroutine for TWI master boot mode */ /* */ /* The address passed in R6 needs to be shifted one place to the right */ /* e.g., 1010 001x becomes 0101 0001 (0x51) */ /* the TWI controller will actually send out 1010 001x */ /* where x is either a 0 for writes or 1 for reads */ /* which IIC Slave is accessed (R6) */ /* */ /*******************************************************************************/ TWI_INIT: W[P1 + LO(TWI_CLKDIV)] = R5; // TWI Serial Clock rate (R5) R1 = 0x008A (Z); // Prescale = 10 (0xA) for an SCLK = 100 MHz // (CLKIN = 50MHz) // Prescale = SCLK / 10 MHz W[P1 + LO(TWI_CONTROL)] = R1; // Set Prescale and TWI Enable R6 = R6 >> 1; // right shift the address W[P1 + LO(TWI_MASTER_ADDR)] = R6; R7 = 0; RTS; TWI_INIT.END: /*******************************************************************************/ /* */ /* M_COMP Subroutine for TWI master boot mode */ /* */ /* Doesn't return until master has completed transfer */ /* */ /*******************************************************************************/ M_COMP: R1 = W[P1 + LO(TWI_INT_STAT)](Z); CC = BITTST (R1, 4); IF !CC JUMP M_COMP; W[P1 + LO(TWI_INT_STAT)] = R1; RTS; M_COMP_END: /*******************************************************************************/ /* */ /* BringInApplication Subroutine for TWI master and slave boot modes */ /* */ /* DMAs code into L1 instruction or data memory */ /* */ /*******************************************************************************/ BringInApplication: CC = R2; IF !CC JUMP BringInApplication.EXIT; // if count is zero, exit [--SP] = RETS; [--SP] = (R7:1,P5:2); Check_Data_Count: R6 = 0xFE(Z); // max value for IIC Data_Count CC = R2 <= R6; // (254 bytes) IF CC JUMP Data_Count; BITSET(R7,15); // use bit 15 of R7 as a token to indicate // how many times to loop to get more data P3 = R6; // LOOP COUNT R5 = R6 << 16; // Data_Count R3 = R2; // Save current count R2 = R6; // set DMA COUNT TO 0xFE JUMP Call_TWI_READ; Data_Count: BITCLR(R7,15); // clear R7 P3 = R2; // LOOP COUNT R5 = R2 << 16; // Data_Count Call_TWI_READ: CALL TWI_READ; // R4 = source address, R5 = count R0.H = Boot_PayLoad; // temporary L1 storage R0.L = Boot_PayLoad; CALL FDMA; // call memory DMA R2 = R3 - R6; // decrement count by 0xFE CC = BITTST(R7,15); R1 = [P1+LO(MDMA_D1_CURR_ADDR)]; // save the current destination DMA address R4 = R4 + R6; // update EPROM's internal address IF CC JUMP Check_Data_Count; (R7:1,P5:2) = [SP++]; RETS = [SP++]; BringInApplication.EXIT: RTS; BringInApplication.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Boot from TWI host (TWI Slave Boot) thread . */ /* */ /*******************************************************************************/ /*******************************************************************************/ TWI_SLAVE_BOOT: R1 = 0x008A (Z); W[P1 + LO(TWI_CONTROL)] = R1; // Set Prescale and TWI Enable ; R1 = 0x5F; // program Slave Address W[P1 + LO(TWI_SLAVE_ADDR)] = R1; // 0xBF>>1 = 0x5F // program data to be transmitted by the R1 = 0xBF53 (Z); // slave: 0xBF53 W[P1 + LO(TWI_XMT_DATA16)] = R1; // this is optional and can be OPTIMIZED // out for space savings R1 = 0x5; // enable slave operation; 7-bit address; // Data in TX buffer for slave tx; W[P1 + LO(TWI_SLAVE_CTL)] = R1; // generate ACK; disable general call; R7 = 0x4; // Set Slave mode marker before jumping // to common subroutine JUMP TWI_SLAVE_ENTRY; TWI_SLAVE_BOOT.END: /*******************************************************************************/ /* */ /* SET_HWAIT Subroutine for all Boot Modes . */ /* */ /* Evaluates the PFLAG and PPORT bit fields in the FLAG word and enables */ /* the output driver of the respective GPIO pin. */ /* */ /* The polarity of the host wait signal HWAIT depends on whether there is */ /* a pull-up or pull-down resistor connected to the pin. */ /* */ /* This function is guarded against multiple calls. On initial call the */ /* inputs parameters are: */ /* */ /* R5 = FLAG word of block header. Thruth Table: */ /* */ /* FLAG[8:5] = PFLAG = GPIO number 0..15 */ /* */ /* FLAG[10:9] = PPORT = Port Number */ /* 00 = disable HWAIT */ /* 01 = HWAIT on Port F */ /* 10 = HWAIT on Port G */ /* 11 = HWAIT on Port H */ /* */ /* P5 = PORTFIO_TOGGLE address (xFFC0.070C) */ /* */ /* M3 = 0x8000.0000 */ /* */ /* The function returns the following values */ /* */ /* P5 = Address of toggle register for respective port */ /* */ /* PPORT = 00 : P5 = PORTFIO_TOGGLE (0xFFC0.070C) */ /* PPORT = 01 : P5 = PORTFIO_TOGGLE (0xFFC0.070C) */ /* PPORT = 10 : P5 = PORTGIO_TOGGLE (0xFFC0.150C) */ /* PPORT = 11 : P5 = PORTHIO_TOGGLE (0xFFC0.170C) */ /* */ /* M3 = 0 if PPORT = 0 */ /* = (1 << PFLAG) else */ /* */ /* Both registers are reserved for this purpose globally */ /* */ /*******************************************************************************/ SET_HWAIT: [--SP] = (R7:6); /*******************************************************************************/ /* */ /* Guard against multiple calls. */ /* */ /*******************************************************************************/ R7 = M3; // M3.H is set to 0x8000 initially CC = R7 < 0; // to executes routine only once IF !CC JUMP SET_HWAIT.EXIT; /*******************************************************************************/ /* */ /* Determine PPORT and set P5 to proper GPIO Toggle register. */ /* */ /*******************************************************************************/ CC = BITTST(R5,10); // test PPORT IF CC JUMP TEST_PORT_GH; TEST_PORT_F: CC = BITTST(R5,9); IF CC JUMP TEST_GPIO; M3 = 0 (Z); // when PPORT = 00 JUMP SET_HWAIT.EXIT; TEST_PORT_GH: P5.L = LO(PORTHIO_TOGGLE); CC = BITTST(R5,9); IF CC JUMP TEST_GPIO; // it is Port H P5.L = LO(PORTGIO_TOGGLE); // otherwise Port G TEST_GPIO: /*******************************************************************************/ /* */ /* Determine PFLAG and set M3 to (1<> 5; // mask PFLAG R7 = 0x000F; R6 = R7 & R6; R6.L = LSHIFT R3.L BY R6.L; // R6 holds the GPIO # /*******************************************************************************/ /* */ /* Pick current polarity by enabling HWAIT input driver shortly. */ /* Then disable input driver again. Note: no other GPIO has been in input */ /* mode yet. Otherwise read-modify-write operation was required. */ /* */ /*******************************************************************************/ W[P5+PORTFIO_INEN-PORTFIO_TOGGLE] = R6; M3 = R6; // M3 is the static copy R7 = 0; W[P5+PORTFIO_INEN-PORTFIO_TOGGLE] = R7; /*******************************************************************************/ /* */ /* Enable HWAIT output driver. This time read-modify-write is required as */ /* SPI modes may have enabled some GPIO output drivers already. */ /* */ /* Note that PORTx_FER registers do all reset to zero. Some may have been */ /* set by SPI, UART and FIFO boot modes already. Obviously, that those pins */ /* cannot be used for HWAIT functionality. If chosen by mistake, the */ /* enabled output driver would still not break functionality. */ /* */ /*******************************************************************************/ R7 = W[P5+PORTFIO_DIR-PORTFIO_TOGGLE] (Z); R7 = R7 | R6; W[P5+PORTFIO_DIR-PORTFIO_TOGGLE] = R7; /*******************************************************************************/ /* */ /* Toggle HWAIT first time to prevent host from sending further data. */ /* */ /*******************************************************************************/ W[P5] = R6; SET_HWAIT.EXIT: (R7:6) = [SP++]; RTS; SET_HWAIT.END: /*******************************************************************************/ /* */ /* BOOT_END routine for all boot modes. */ /* */ /* Once Boot Process has finished, some housecleaning is required. It is */ /* basically the restoring of reset values to most control registers touched */ /* during booting. */ /* */ /* The second piece is also executed on software resets that don't require */ /* rebooting. */ /* */ /* Finally, JUMP to reset vector stored in EVT1 to exit. Recall the */ /* processor is still running at reset priority. */ /* */ /*******************************************************************************/ BOOT_END: R1 = DRQ_MULTI; W[P1+LO(HMDMA1_CONTROL)] = R1; // restore reset value R1 = 0x00F2; // disable upper banks and SCLK again W[P1+LO(EBIU_AMGCTL)] = R1; R1 = 0x0; W[P1+LO(SPI_CTL)] = R1; MNOP; // W[P1+LO(UART0_GCTL)] = R1; don't disable UART to not looks Dl content W[P1+LO(UART0_IER)] = R1; W[P1+LO(TWI_CONTROL)] = R1; W[P1+LO(MDMA_S0_CONFIG)] = R1; W[P1+LO(MDMA_D0_CONFIG)] = R1; W[P1+LO(MDMA_S1_CONFIG)] = R1; W[P1+LO(MDMA_D1_CONFIG)] = R1; W[P4] = R1; // either DMA7_CONFIG or DMA8_CONFIG W[P1+LO(PORTF_FER)] = R1; W[P1+LO(PORT_MUX)] = R1; R1 = ~R1; [P1+LO(SIC_IWR)] = R1; // reset SIC_IWR to all FF's /*******************************************************************************/ /* */ /* Disable HWAIT and SPI SSEL output drivers. If there was any other output */ /* has been enabled by an init code it is not disabled here. */ /* */ /*******************************************************************************/ R1 = W[P5+PORTFIO_DIR-PORTFIO_TOGGLE] (Z); // disable HWAIT output driver R0 = M3; R0 = ~R0; R1 = R0 & R1; W[P5+PORTFIO_DIR-PORTFIO_TOGGLE] = R1; // PORTxIO_DIR R1 = W[P1+LO(PORTFIO_DIR)] (Z); // disable SPI SSEL output driver R0 = I1; R0 = ~R0; R1 = R0 & R1; W[P1+LO(PORTFIO_DIR)] = R1; /*******************************************************************************/ /* */ /* Jump to reset vector stored in EVT1 register. By default this is */ /* 0xFFA0.0000, but it can be patched by init codes as well as by user */ /* programs in case of software resets. */ /* */ /*******************************************************************************/ SWRESET: P0.L = LO(EVT1); P0.H = HI(EVT1); P1 = [P0]; // the Event Vector Table JUMP(P1); // JUMP to the RESET vector BOOT_END.END: /*******************************************************************************/ /* */ /* Global_Setup Routine */ /* */ /* Initializes most registers as required by various booting procedures. */ /* A special entry avois initialization of stack pointers for run-time calls. */ /* */ /*******************************************************************************/ Global_Setup: SP.H = HI(L1_Scratch_END); // Scratch memory used for stack purposes SP.L = LO(L1_Scratch_END); FP = SP; Global_Setup_Function_Entry: R3 = 0x0000(z); P0 = R3; // load P0.L P1 = R3; // load P1.L M0 = R3; // value used for zerofill purposes M1 = R3; // MDMA Word Width Indicator to 8-bit I1 = R3; // for clean restore in Boot_END P3.H = HI(DMA_Destination); // P3 Points to the DMA Header Destination. P3.L = LO(DMA_Destination); // 0xFF807FF0 P0.H = HI(COREMMR_BASE); // P0 Points to CORE MMR Space P1.H = HI(SYSMMR_BASE); // P1 Points to SYSTEM MMR Space P5 = P3; P5+= 0x0C; // 0xFF807FFC M2 = P5; // location for 32-bit zero and trash data P5 = P1; // load P5.H P5.L = LO(PORTFIO_TOGGLE); // Pointer to HWAIT toggle register M3 = R3; M3.H = 0x8000; // HWAIT GPIO number P4 = P1; // load P4.H P4.L = LO(DMA7_CONFIG); // overwritten to DMA8_CONFIG in UART mode /*******************************************************************************/ /* */ /* Global DMA Settings */ /* MDMA1 for parallel flash boot and zero fill */ /* DMA7 for SPI boot */ /* DMA8 for UART boot */ /* */ /*******************************************************************************/ R3 = 0x1; W[P1+LO(MDMA_D1_X_MODIFY)] = R3; // MDMA Destination Modify = 1 W[P1+LO(MDMA_S1_X_MODIFY)] = R3; RTS; Global_Setup.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* USER CALLABLE RUN-TIME FUNCTION */ /* */ /*******************************************************************************/ /*******************************************************************************/ /*******************************************************************************/ /*******************************************************************************/ /* */ /* BOOT DXE from FLASH Routine. User-callable through public label by . */ /* */ /* P0.H = HI(_BOOTROM_Boot_DXE_Flash); */ /* P0.L = LO(_BOOTROM_Boot_DXE_Flash); */ /* JUMP (P0); */ /* */ /* When called, this function assumes R7 points to a valid boot stream in */ /* external memory, and starts to process the boot stream. It never returns. */ /* Rather, it jumps to the reset vector once done. */ /* */ /* System configuration, such as Wait-state settings or SDRAM controller */ /* setup is up to the user to be performed prior to the JUMP. */ /* */ /*******************************************************************************/ /*******************************************************************************/ Boot_DXE_Flash: CALL Global_Setup; P0 = R7; // Start Address JUMP FDXE_ENTRY; Boot_DXE_Flash.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* BOOT DXE from SPI Memory Routine. User-callable through public label by */ /* */ /* P0.H = HI(_BOOTROM_Boot_DXE_SPI); */ /* P0.L = LO(_BOOTROM_Boot_DXE_SPI); */ /* JUMP (P0); */ /* */ /* INITIALIZE PORTF_FER PRIOR TO CALLING THIS FUNCTION!!! */ /* */ /* When called, this function assumes R7 points to a valid boot stream in */ /* SPI memory and R6 holds the SPI SSEL number. Then it starts to process */ /* the boot stream and never returns. Rather, it jumps to the reset vector */ /* once done. Applicable in the Master SPI boot scenario only. */ /* */ /* R5 holds SPI_BAUD value */ /* */ /*******************************************************************************/ /*******************************************************************************/ Boot_DXE_SPI: CALL Global_Setup; // P registers initialization R6.L = LSHIFT R3.L BY R6.L; I1 = R6; // I1 = /CS Value (SPI_SSEL) R3 = R7; // R3 = SPI Start Address. R7 = 0x0; // Set R7 = 0x0 for SPI Master Mode Boot JUMP BOOT_DXE_SPI_ENTRY; // for use in SPI_DMA routine Boot_DXE_SPI_END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* BOOT DXE from TWI Memory Routine. User-callable through public label by */ /* */ /* P0.H = HI(_BOOTROM_Boot_DXE_TWI); */ /* P0.L = LO(_BOOTROM_Boot_DXE_TWI); */ /* JUMP (P0); */ /* */ /* When called, this function assumes R7 points to a valid boot stream in */ /* TWI memory and R6 holds the TWI chip select. R5 holds the TWI CLKDIV */ /* value. Note that the user should pass the whole 7 bit address in R6 */ /* */ /* e.g., if A2=A1=A0 = 0, then R6 = b1010 000x (0xA0) */ /* A2=A1=0, A0 = 1; R6 = b1010 001x (0xA2) */ /* A2=0, A1=1, A0=0; R6 = b1010 010x (0xA4) */ /* etc... */ /* */ /* x is the R/W bit which could left as 0, it is a don't care */ /* since the TWI controller stuffs that bit when it sends out the */ /* address depending on the setting of the TWI Control register */ /* */ /* The function processes the boot stream and never returns. Rather, it */ /* jumps to the reset vector once done. Applicable in the Master TWI boot */ /* scenario only. */ /* */ /*******************************************************************************/ /*******************************************************************************/ Boot_DXE_TWI: CALL Global_Setup; R4 = R7; // R4 = TWI Start Address JUMP TWI_BOOT_DXE_ENTRY; Boot_DXE_TWI.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Get DXE Address from FLASH Routine. User-callable through public label by */ /* */ /* P0.H = HI(_BOOTROM_Get_DXE_Address_Flash); */ /* P0.L = LO(_BOOTROM_Get_DXE_Address_Flash); */ /* CALL (P0); */ /* */ /* When called, this function assumes R7 holds the sequential number of the */ /* DXE that should be booted in. R6 holds the start address where the search */ /* should begin (example 0x2000.0000 start of async bank 0). */ /* */ /* The function evaluates the Next-DXE-Pointers containing in the payload */ /* data of the initial IGNORE blocks. It is assumed that the entire boot */ /* image is structured as a chained list of individual boot streams. */ /* */ /* The DXE address requested will be returned in R7 in order to be passed to */ /* the _BOOTROM_Boot_DXE_Flash directly. */ /* */ /*******************************************************************************/ /*******************************************************************************/ Get_DXE_Address_Flash: [--SP] = RETS; CALL Global_Setup_Function_Entry; P0 = R6; // start address Get_DXE_Address_Flash.LOOP: CC = R7 == 0; IF CC JUMP Get_DXE_Address_Flash.EXIT; CALL GET_FLASH_TYPE; // 8-bit or 16-bit device W[P1+LO(MDMA_D1_X_MODIFY)] = R6; // set destination modify to 1 or 2 R1 = P3; // DMA destination address R2 = 14; // 10-bytes header + 4 byte count CALL FDMA; // perform Memory DMA (14 bytes) R1 = W[P3+10] (Z); // Get Next DXE Pointer R2 = W[P3+12] (Z); // as address is not divisable by 4 R1.H = R2.L >> 0; R1 <<= R4; // shift left for 8-bit mode R1 = R0 + R1; // Next DXE Pointer is relative P0 = R1; R7 += -1; // DXE number JUMP Get_DXE_Address_Flash.LOOP; Get_DXE_Address_Flash.EXIT: R7 = P0; // R7 holds the requested address RETS = [SP++]; RTS; Get_DXE_Address_Flash.END: /*******************************************************************************/ /*******************************************************************************/ /* */ /* Get DXE Address from SPI Routine. User-callable through public label by */ /* */ /* P0.H = HI(_BOOTROM_Get_DXE_Address_SPI); */ /* P0.L = LO(_BOOTROM_Get_DXE_Address_SPI); */ /* CALL (P0); */ /* */ /* This function sets up the SPI_BAUD register with the value passed in R5, */ /* activates the SPI memory slave connected to the PFx number passed in R6. */ /* Then, it assumes the DXE number of interest to be passed i