Part Number: TMS320F28069
Other Parts Discussed in Thread: CONTROLSUITE
Tool/software: TI C/C++ Compiler
Hi Guys,
I'm having some trouble with a design using a TMS320F28069. Using a new PCB assembly the processor can be flashed and the code runs, but only once. A subsequent re-flash will cause the bootloader in the ROM to crash at with the ESTOP0 instruction. At first I thought it was a problem with the PCB assembly so I tried it on a brand new one, same problem. This design hasn't changed for a while and previous manufacturing runs have all worked as designed. I've also tried on another PCBA with the same processor and everything appears to work.
Once the boot ROM has crashed, I can simply halt the processor and set the program counter (PC) to 0x3F7FF6 (the flash entry point) and everything runs perfectly fine. This means the boot ROM has found an error.
Stepping through the boot ROM on the chip using my debugger, this is what I found.
- PC starts from 0x3FF75C: SP = 4, OBJMODE = 1, AMODE = 0, M0M1MAP = 1, PAGE0 = 0, DP = 0, OVM = 0, SPM = 0. These set up the core, seems fine.
- Call function at 0x3FF456 (from address 0x3FF766):
- Load WD key, 0x55 followed by 0xAA to WDKEY (0x7025), this resets the WDT.
- Load 0x0100 to address 0x0986, which is a reserved register associated with BOR or power control. No idea what this does.
- Set PLLSTS[DIVSEL] = 3. This sets a OSCCLK/1.
- Set PCLKCR0[ADCENCLK] = 1. This enables the ADC clock.
- Call function at address 0x3D7C80 (from address 0x3FF469) using the LCR instruction.
- Function at 0x3D7C80 (setup ADC trim?):
- Set INTOSC1TRIM = 0x5601
- Set INTOSC2TRIM = 0x5C00
- Wait 512 clock cycles (repeat NOP)
- Set ADCREFTRIM = 0x1B34
- Set ADCOFFTRIM[0] = 0x0007, ADCOFFTRIM[11] = 0x000D, ADCOFFTRIM[14] = 0x0202
- Push P (= 0) to SP (=0x000C). SP = 0x000C before push, SP = 0x000E after push.
- Move ACC (=0x00000100) to SP. SP = 0x0010 after push.
- Move 0x3FF4C0 to P, using two MOV operations.
- Push RPC (= 0x3FF46A). SP = 0x0010 before push, SP = 0x0012 after push.
- Move SP (= 0x0012) to ACC. SP = 0x0012 before MOV, SP = 0x0010 after MOV. ACC = 0x3FF46A
- If calling function was 0x3FF4C0 execute the following. (not sure what's at that address, our return pointer is at RPC = 0x3FF46A)
- // start an ADC conversion and wait for it to complete
- ADCSOCFRC1[SOC0] = 1. This starts an ADC conversion
- Wait 21 clock cycles (repeat NOP)
- Set SOCPRICTL[SOCPRIORITY] = 1. Sets SOC0 to high priority.
- Set SOCPRICTL[SOCPRIORITY] = 0. Sets SOC to round-robin mode
- Move the contents of SP (= 0x0010) to ACC. SP = 0x0010 before move, SP = 0x000E after move. ACC = 0x00000100
- Pop P = 0x00000000, after pop. SP = 0x000E before pop, SP = 0x000C after pop
- Return from function to RPC = 0x3FF46A
- At address 0x3FF46A, disable ADC clock by setting PCLKCR0[ADCENCLK] = 0 using an AND instruction.
- Zero address 0x0D04. (the address space 0x0D00 to 0x0E00 is the PIE Vector RAM)
- Zero address 0x0D02.
- Read the CSM passwords from 0x3F7FF8 - 0x3F7FFF to AL. (they're all equal to 0xFFFF, not sure why this is done)
- Store DEVICECNF[TRSTn] to AL. (this is done by storing the result of a logical AND with DEVICECNF[ and 0x0800 to AL)
- // the state of TRSTn is checked from address 0x3FF481 - 0x3FF487
- If TRST == 1 and execute the following, else jump to address 0x3FF494
- // keep in mind that 0x0D00 - 0x0E00 is the PIE vector space, probably just used as scratch memory
- // at this point SP = 0x000A, so SP[-3] =0x000A - 3 = 0x0007
- // 0x0D00 is EMU_KEY, 0x0D01 is EMU_BMODE
- If address 0x0D00 != 0x55AA then store 0x2 to SP[-3] (this is evaluates to true), else store the contents of 0xD01 to SP[-3]
- Jump to address 0x3FF4A7
- (Else) at address 0x3FF494 (TRSTn == 0, check the boot pins)
- If TDO ==1, then store 0x2 to SP[-3]
- If GPIO34 == 1, then set the LSB of SP[-3]. The first two bits of SP[-3] now equals the values of TDO and GPIO34, respectively.
- Store SP[-3] (the state of the two boot pins) to 0x0D01
- Store 0x55AA to 0x0D00 (EMU_KEY)
- Store the contents of SP[-3] (the boot pin values) to 0x0D01 (EMU_BMODE)
- Continue to address 0x3FF4A7
- // at this point the first two bits of SP[-3] (0x000A - 3 = 0x0007) should contain the boot mode, determined by the nTRST or the boot mode pins (or by whatever sets 0x0D00 to 0x55AA)
- // we're now at address 0x3FF4A7 - 0x3FF4AA to check SP[-3]
- If SP[-3] == 0x2 jump to address 0x3FF4F8 (SP[-3] == 0x2 so we make the jump), else (SP[-3] == 0x3, 0x1, or 0x0) call function at 0x3FF748 (disable WDT) and return to address 0x3FF4B1
- Continuing at address 0x3FF4F8,
- Call function at address 0x3FF750 (reset WDT)
- Loop ESTOP0 forever... (at address 0x3FF4FA)
- Continuing at address 0x3FF4F8,
- Continuing at address 0x3FF4B1
- If SP[-3] == 0x3 jump to 0x3D7CC0 (Get_mode function), else jump to address 0x3FF4B8
- // the 'Get_mode' function probably returns to address 0x3FF4B7 and stores return value to AL and SP[-3]
- // continuing at address 0x3FF4B8 there's a big 'switch' statement that looks approximately like this
- swtich( Get_mode() ):
- // the 'Get_mode' function just checks OTP_KEY value at 0x3D7BFB and OTP_BMODE at 0x3D7BFE
- // if the keys are unprogrammed or invalid it just boots to flash, for me both are unprogrammed with values of 0xFFFF
- 0xB, SP[-2] = 0x3F7FF7 (flash boot, this is the default return value of 'Get_mode')
- 0x6, SP[-2] = 0x3D7800 (something in reserved memory)
- 0xA, SP[-2] = 0
- 0x1, SP[-2] = 0x3FF698 (SCI_BOOT in boot ROM)
- 0x4, SP[-2] = 0x3FF3E1 (SPI_BOOT in boot ROM)
- 0x5, SP[-2] = 0x3FF5DE (I2C_BOOT in boot ROM)
- 0x0, SP[-2] = 0x3FF629 (this is not mentioned as a valid boot mode in SPRU18H but it exists...)
- 0x7, SP[-2] = 0x3FF4FC (CAN_BOOT in boot ROM)
- Store SP[-2] to ACC
- Subtract 4 from SP, resulting in (0x000A - 4 = 0x0006)
- Return from function via LRETR (RPC = 0x3FF768)
- // code at address 0x3FF768 - 0x3FF781 resets the processor core and jumps to the address that was in SP[-2]. The boot ROM exits at address 0x3FF781
- Function at address 0x3FF750 (reset WDT):
- Write 0x28 to WDCR. This enables the watchdog (WDDIS = 0) and writes 0b101 to the WDCHK register (as should be done on all writes to WDCR)
- Write 0x55 to WDKEY
- Write 0xAA to WDKEY. These two writes reset the watch dog
- return from function (to RPC)
- At address 0x3FF748 (disable WDT):
- Disable WDT by writing 0x68 to WDCR
- return from function (to RPC)
- Disable WDT by writing 0x68 to WDCR
Basically the boot ROM is doing what is described in section 2.2.1 (Bootloader Functional Operation) in SPRUH18H. I have a JTAG connected so TRSTn is high therefore the boot ROM checks EMU_KEY (at 0x0D00) and EMU_BMODE (at 0x0D01). On my test board both values are incorrect, EMU_KEY is invalid (not 0x55AA) as is EMU_BMODE (0x3D). I'm stumped, what writes these to values and when?