📄 161_spi.asm
字号:
/* ==============================================================================
161_spi.asm
Serial Peripheral Interface compatible port based boot loader for the
ADSP21161 processor
Copyright (c) 2002 Analog Devices Inc. All rights reserved.
version 0.1 6/21/01 - Zero initializations and external memory sections
have not been tested. Check for newest version of the SPI kernel at
ftp.analog.com/pub/dsp/211xx/dev_tools/loader.
version 0.2 8/1/01 - All sections of kernel have been tested.
version 1.0 10/17/01 - R14 = 0xA001F81; was improperly initialized to
R14 = 0xA801F81; in the previous version of the kernel.
version 1.1 1/14/02 - changed some comments and fixed wait state init in
SDRAM init section
version 2.0 05.Sept.02 MSW
- Added initialization of all external memory types
-Added code to patch SPI IVT entry in final init
- Added final-init comments
- Added comments regarding tag info for each section
version 2.1 17.Oct.02 MSW
- Added comments regarding spi overflow errors possible during zero
initialization sections
- Added code to trap on spi overflow
version 2.2 20.DEC.2002 - M.Walsh
- Changed #define SDRAM 1 to #define SDRAM 0
SPI Feedback version 1.0 24.FEB.2003 - B.Mitchell
- Included feedback scheme described in EE-177
==============================================================================*/
#include <def21161.h>
#define SDRAM 1
.SECTION/PM seg_ldr;
NOP;NOP;NOP;NOP; // Reserved interrupt
reset: nop; // - Instruction specified here is never executed. The core implicitly executes
// an IDLE instruction here until 256 instructions are booted. Once the kernel
// is booted, execution resumes at 0x40040 (the SPI Rx ISR). This address
// (0x40005) and the next (0x40006) are also used as a temporary buffer to DMA-in
// words from the SPI port
nop; // scratch memory2
//turn off SPI dma while kernel is executing
ustat1=dm(SPICTL);
bit clr ustat1 RDMAEN; //clear DMA bit in SPICTL
dm(SPICTL)=ustat1;
call user_init; // call subroutine at end of kernel in which users can insert
// initalization code, such as SDRAM, waitstates, etc.
R5=2; // used to decrement tag by two during loading.
L0=0; L1=0; L4=0; // Zero out L-registers so they can be used without wrap
L8=0; L12=0;L15=0; // ie. no circular buffering
M5=0;M6=1; // Setup M-registers to use for various memory transfers
M13=0; M14=1; M15=3; // use to load count in read_dma_info
I15 = 0x40006; // scratch DMA destination address for 32 bit words;
R14 = 0xA001F83; // sets up and enables SPI port with DMA, this is also the default setting
R11=DM(SYSCON); // Read current SYSCON setting
R12=PASS R11; // Hold Initial SYSCON setting
I12 = SYSCON; // Address of SYSCON
// ========================== READ_BOOT_INFO =======================================
// Places TAG in R8, Word Count in R2, and Destination Address in R3
// -----------------------------------------------------------------------------------
read_boot_info:
call read_dma_info; // place two 48-bit words into 0x40004 and 0x40005
// equivalent to placing three 32-bit words into 0x40006,7,& 8
R2=dm(0x40006); //r2 passes section length
R3=dm(0x40007); //r3 passes destination address
R8=dm(0x40008); //r8 passes data-type tag
// -----------------------------------------------------------------------------------
// ========================== SPI Overflow Detect ====================================
// Monitor the SPI Status register to ensure that no overflow has ocurred. The RBSY
// (receiver busy) bit in SPI is set to 1 when the SPI port receive buffer is full and
// another word is clocked into the shift register. This may happen if the core is
// performing a zero-initialization section, and the SPI-master device continues to
// send data. RBSY is a sticky bit, so it will stay set until the spi port is
// disabled.
//
// If an overflow is detected the kernel will trap in an infinite loop so that user
// intervention can determine the problem. If this problem occurs, the SPI-master
// device must be configured to pause while the DSP performs the zero initializations.
// This can be done in software by looking at the .LDR section headers, or in hardware
// by configuring the kernel to set a DSP flag pin while it is performing zero-
// initializations. More information can be found in an EE note on the ADI website
// titled "21161 SPI Booting". http://www.analog.com/dsp
// -----------------------------------------------------------------------------------
USTAT1=DM(SPISTAT);
bit tst USTAT1 RBSY;
if tf JUMP (pc,0);
jump start_loader;
// -----------------------------------------------------------------------------------
// ============================= FINAL_INIT =========================================
// This code is the very last piece run in the kernel. It uses a DMA to initialize the
// 256 instructions that reside in the interrupt vector table.
// -----------------------------------------------------------------------------------
final_init: R9=0xb16b0000; /* Load instruction PM(0,I8)=PX; */
R11=BSET R11 BY 9; /* Set IMDW to 1 for inst write */
DM(SYSCON)=R11; /* Setup for instruction write */
I4=0x40004; /* Point to 0x040004 for self modifing code*/
PX=pm(0x40004); /* Load instruction to be patched into 0x40040*/
I8=0x40040; /* Point to DMA8 vector to patch */
R9=pass R9, R11=R12; /* Clear AZ, hold initial SYSCON */
DO reset UNTIL EQ; /* set top of loop value to 0x40004 (see comments below)*/
PCSTK=0x40004; /* Set top of PC-stack, so RTI after final DMA goes to 0x40004;*/
//setup DMA parameters
DM(IMSRX)=M6; /* Set to increment internal ptr */
R1=0x40000; DM(IISRX)=R1; /* Setup DMA to load over ldr */
R2=0x180; DM(CSRX)=R2; /* Load internal count */
bit clr LIRPTL SPIRI; // clear pending SPIRX interrupt latch */
bit set IMASK LPISUMI; // enable LPISUMI interrupt
bit set LIRPTL SPIRMSK; // enable SPI receive interrupt
bit set mode1 IRPTEN; // enable global interrupts
FLUSH CACHE; /* Flush cache for kernel overwrite */
DM(SPICTL)=R14; /* Start DMA transfer */
JUMP 0x40004 (DB); /* Jump to start */
IDLE; /* After IDLE, patch then start */
IMASK=0; // clear IMASK on way to 0x40004
// When this final DMA completes, IDLE in the delayed branch completes, IMASK is cleared,
// and then the DSP executes the instruction at 0x40004. The loader automatically places the following
// instruction at that address:
//
// R0=R0-R0, DM(I4,M5)=R9, PM(I12,M13)=R11; //opcode: x39732D802000
//
// Executing this instruction performs the three things:
// 1) It sets the AN flag ("until EQ" now evaluates as true)
// 2) It restores the power-up value of SYSCON (held in R11).
// 3) It overwrites itself with the instruction PM(0,I8)=PX; (held in R9)
//
// Because the termination condition (EQ) is achieved within the last three instructions
// of the loop, the DSP jumps to the top-of-loop address one last time. We manually changed this
// top-of-loop address (PCSTK) to x40004, so, to conclude the kernel, the DSP executes the instruction
// at 0x40004 again. The new instruction there resets the DMA10 interrupt vector (x40050) to it's
// user-intended final value. At this point, the kernel is finished, and execution continues at
// 0x40005 as if nothing happened!
//
// ========================== END FINAL_INIT ==============================
// ==========================================================================
// ========================== SPI Recieve DMA ISR ===========================
// This code must resolve to 0x40 in the interrupt vector table. If any code
// is added or removed prior to this without padding with "NOP's", then this
// kernel will not function.
nop;nop;nop;nop;// added nop's to resolve RTI to correct address
nop;nop;
x40040: nop; RTI; // return to 0x40006 and begin executing kernel
// ========================== END SPI Recieve DMA ISR ======================
// ============================= START LOAD ========================================
// The following instructions analyze the TAG for each data section and initialize
// accordingly. READ_BOOT_INFO places the TAG in R8, Word Count in R2, and Destination
// Address in R3.
//
// Notes:
// - Several tags are initialized with common code to save space.
// - These instructions must execute sequentially because the tag value
// is decremented after each false test.
// -----------------------------------------------------------------------------------
start_loader:
R0=PASS R8;
IF EQ jump final_init; // jump if tag = 0
//--------------- 1 0x0001 ZERO DM16 ---------------
//--------------- 2 0x0002 ZERO DM32 ---------------
test_dm_zero: R0=R0-R5; // R5=2
IF GT JUMP (PC, test_dm40_zero);
dm_zero:
bit set mode2 0x8000; //Set FLAG0 as an Output
bit clr flags 0x1; //Clear FLAG0 to pause SPI Host
R0=R0-R0, I0=R3; //clears r0 and sets I0 to R3 which is the address information
LCNTR=R2, DO dm_zero.end UNTIL LCE;
dm_zero.end: DM(I0,M6)=R0; // writes zeros to dm memory; M6=1
bit set flags 0x1; //Set FLAG0 to continue boot data
JUMP read_boot_info;
// ---------------------------------------------------
//--------------- 3 0x0003 ZERO DM40 ---------------
test_dm40_zero: R0=R0-1; // checks for tag zero dm40
IF NE JUMP (PC, test_dm_init);
dm40_zero:
bit set mode2 0x8000; //Set FLAG0 as an Output
bit clr flags 0x1; //Clear FLAG0 to pause SPI Host
PX1=0;
PX2=0; // clears px
R0=R0-R0, I0=R3;
LCNTR=R2, DO dm40_zero.end UNTIL LCE;
dm40_zero.end: DM(I0,M6)=PX; // writes px out to correct memory locations
bit set flags 0x1; //Set FLAG0 to continue boot data
JUMP read_boot_info;
// -------------------------------------------------------
//--------------- 4 0x0004 INIT DM16 ---------------
//--------------- 5 0x0005 INIT DM32 ---------------
test_dm_init: R0=R0-r5; // checks for tag init dm16
IF GT JUMP (PC, test_dm40_init);
dm_init: B0=R3;
LCNTR=R2, DO dm16_init.end UNTIL LCE;
CALL read_SPI_word;
DM(I0,M6)=R8;
NOP;
dm16_init.end: NOP; //a call can't be in the last 3 instructions
JUMP read_boot_info;
// -------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -