This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler/TMS320F28069: Boot ROM Issues

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.

  1. 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.
  2. Call function at 0x3FF456 (from address 0x3FF766):
    1. Load WD key, 0x55 followed by 0xAA to WDKEY (0x7025), this resets the WDT.
    2. Load 0x0100 to address 0x0986, which is a reserved register associated with BOR or power control. No idea what this does.
    3. Set PLLSTS[DIVSEL] = 3. This sets a OSCCLK/1.
    4. Set PCLKCR0[ADCENCLK] = 1. This enables the ADC clock.
    5. Call function at address 0x3D7C80 (from address 0x3FF469) using the LCR instruction.
  3. Function at 0x3D7C80 (setup ADC trim?):
    1. Set INTOSC1TRIM = 0x5601
    2. Set INTOSC2TRIM = 0x5C00
    3. Wait 512 clock cycles (repeat NOP)
    4. Set ADCREFTRIM = 0x1B34
    5. Set ADCOFFTRIM[0] = 0x0007, ADCOFFTRIM[11] = 0x000D, ADCOFFTRIM[14] = 0x0202
    6. Push P (= 0) to SP (=0x000C). SP = 0x000C before push, SP = 0x000E after push.
    7. Move ACC (=0x00000100) to SP. SP = 0x0010 after push.
    8. Move 0x3FF4C0 to P, using two MOV operations.
    9. Push RPC (= 0x3FF46A). SP = 0x0010 before push, SP = 0x0012 after push.
    10. Move SP (= 0x0012) to ACC. SP = 0x0012 before MOV, SP = 0x0010 after MOV. ACC = 0x3FF46A
    11. If calling function was 0x3FF4C0 execute the following. (not sure what's at that address, our return pointer is at RPC = 0x3FF46A)
      1. // start an ADC conversion and wait for it to complete
      2. ADCSOCFRC1[SOC0] = 1. This starts an ADC conversion
      3. Wait 21 clock cycles (repeat NOP)
      4. Set SOCPRICTL[SOCPRIORITY] = 1. Sets SOC0 to high priority.
      5. Set SOCPRICTL[SOCPRIORITY] = 0. Sets SOC to round-robin mode

    12. Move the contents of SP (= 0x0010) to ACC. SP = 0x0010 before move, SP = 0x000E after move. ACC = 0x00000100
    13. Pop P = 0x00000000, after pop. SP = 0x000E before pop, SP = 0x000C after pop
    14. Return from function to RPC = 0x3FF46A

  4. At address 0x3FF46A, disable ADC clock by setting PCLKCR0[ADCENCLK] = 0 using an AND instruction.
  5. Zero address 0x0D04. (the address space 0x0D00 to 0x0E00 is the PIE Vector RAM)
  6. Zero address 0x0D02.
  7. Read the CSM passwords from 0x3F7FF8 - 0x3F7FFF to AL. (they're all equal to 0xFFFF, not sure why this is done)
  8. Store DEVICECNF[TRSTn] to AL. (this is done by storing the result of a logical AND with DEVICECNF[ and 0x0800 to AL)
  9. // the state of TRSTn is checked from address 0x3FF481 - 0x3FF487
  10. If TRST == 1 and execute the following, else jump to address 0x3FF494
    1. // keep in mind that 0x0D00 - 0x0E00 is the PIE vector space, probably just used as scratch memory
    2. // at this point SP = 0x000A, so SP[-3] =0x000A - 3 = 0x0007
    3. // 0x0D00 is EMU_KEY, 0x0D01 is EMU_BMODE
    4. If address 0x0D00 != 0x55AA then store 0x2 to SP[-3] (this is evaluates to true), else store the contents of 0xD01 to SP[-3]
    5. Jump to address 0x3FF4A7

  11. (Else) at address 0x3FF494 (TRSTn == 0, check the boot pins)
    1. If TDO ==1, then store 0x2 to SP[-3]
    2. 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.
    3. Store SP[-3] (the state of the two boot pins) to 0x0D01
    4. Store 0x55AA to 0x0D00 (EMU_KEY)
    5. Store the contents of SP[-3] (the boot pin values) to 0x0D01 (EMU_BMODE)
    6. Continue to address 0x3FF4A7

  12. // 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)
  13. // we're now at address 0x3FF4A7 - 0x3FF4AA to check SP[-3]
  14. 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
    1. Continuing at address 0x3FF4F8,
      1. Call function at address 0x3FF750 (reset WDT)
      2. Loop ESTOP0 forever... (at address 0x3FF4FA)
  15. Continuing at address 0x3FF4B1
  16. If SP[-3] == 0x3 jump to 0x3D7CC0 (Get_mode function), else jump to address 0x3FF4B8
    1. // the 'Get_mode' function probably returns to address 0x3FF4B7 and stores return value to AL and SP[-3]
  17. // continuing at address 0x3FF4B8 there's a big 'switch' statement that looks approximately like this
  18. swtich( Get_mode() ):
    1. // the 'Get_mode' function just checks OTP_KEY value at 0x3D7BFB and OTP_BMODE at 0x3D7BFE
    2. // if the keys are unprogrammed or invalid it just boots to flash, for me both are unprogrammed with values of 0xFFFF
    3. 0xB, SP[-2] = 0x3F7FF7 (flash boot, this is the default return value of 'Get_mode')
    4. 0x6, SP[-2] = 0x3D7800 (something in reserved memory)
    5. 0xA, SP[-2] = 0
    6. 0x1, SP[-2] = 0x3FF698 (SCI_BOOT in boot ROM)
    7. 0x4, SP[-2] = 0x3FF3E1 (SPI_BOOT in boot ROM)
    8. 0x5, SP[-2] = 0x3FF5DE (I2C_BOOT in boot ROM)
    9. 0x0, SP[-2] = 0x3FF629 (this is not mentioned as a valid boot mode in SPRU18H but it exists...)
    10. 0x7, SP[-2] = 0x3FF4FC (CAN_BOOT in boot ROM)
  19. Store SP[-2] to ACC
  20. Subtract 4 from SP, resulting in (0x000A - 4 = 0x0006)
  21. Return from function via LRETR (RPC = 0x3FF768)
  22. // 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
  23. Function at address 0x3FF750 (reset WDT):
    1. 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)
    2. Write 0x55 to WDKEY
    3. Write 0xAA to WDKEY. These two writes reset the watch dog
    4. return from function (to RPC)
  24. At address 0x3FF748 (disable WDT):
    1. Disable WDT by writing 0x68 to WDCR
    2. return from function (to RPC)

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?

  • Looks like if I simply program address 0x0D00 = 0x55AA and address 0x0D01 = 0x000B (boot to flash, as per Table 2-7 in SPRU18H) when TRSTn is checked, then everything works as expected. Is there something that should be programming these values when I boot over JTAG?

  • Oh, one more thing I forgot to mention. With the JTAG disconnected (TRSTn == 0) the board boots and runs from flash as designed.

    So technically this is not a bug in the boot ROM but in whatever is supposed to write the EMU_KEY and EMU_BMODE registers when debugging with a JTAG. I was able to verify correct boot behavior with a JTAG connected by setting ST0[Z] = 0 right before executing address 0x3FF487 in the boot ROM. This tricks the boot ROM into thinking there is no JTAG connected as the TRSTn == 1 check fails.

  • Thanks for updating.

  • Since this hack doesn't actually fix my problem I've investigated a little further but still have yet to find my root-cause. Here's what I found...

    On a similar board, using the same TMS320F28069 processor, if I single-step through the boot ROM I get the same results, ESTOP0 at address 0x3FF4FA. But if I reset the CPU and then click the Restart button, the code boots to c_int00() just fine. The funny thing is, 0xD00 = 0xAC87 and 0xD01 = 0x003D, even before c_int00() has a change to execute. I assume the boot ROM didn't re-write these values (why would it) and was able to successfully boot with them as is... but how? I don't really understand the logic.

    Single stepping through the boot ROM leads to an ESTOP0 at 0x3FF4FA...

    Restarting (and then running) the code results in a good boot to c_int00...

    ... in both cases the values at 0xD00 = 0xAC87 and 0xD01 = 0x003D. So how does this work? Is there another branch of execution in the boot ROM that I missed?

  • "Restarting (and then running) the code results in a good boot to c_int00..."

    when you did this was the debugger connected ? in CCS is the target connected ?

  • Yes, in both cases (single stepping assembly and just running from the reset vector) the debug probe was physically connected as well as logically connected in debug mode.

    In other words, we have two cases that should in theory be starting from the same state (processor registers, etc.). But in the first case I stepped through the assembly one line at a time. In the second case I simply hit 'run'. In the former case the code halts at ESTOP0, in the latter, the boot ROM executes as expected. But in both cases the data at 0xD00 = 0xAC87 and 0xD01 = 0x003D. This doesn't make sense to me.

    When I'm single stepping, I have to manually set address 0x0D00 = 0x55AA and address 0x0D01 = 0x000B, for the boot ROM to operate correctly. So then the question remains, how does the boot ROM end up jumping to my C-code and booting correctly in the second scenario?

    While my workaround is adequate, I haven't found the the root-cause of this issue; why does the boot ROM seem to operate in two different ways with the same starting conditions? And why just in this one design / project?

  • in the second case it seems to have picked the flash boot.

    in the first case when it halts at ESTOP0 do you see which routine it is. There are multiple reasons why it would go to estop0, so knowing the specific case will help.

    which emulator are you using.

  • I don't have the C source code for the boot ROM but looking at the assembly, it seems that during a failed boot my code always ends up at the ESTOP0 in the function at 0x3FF748.

    All of my testing so far has been with the XDS100 v2 emulator.

  • the source code and .out should be available in controlsuite. please load the symbols and see which function the control is in.