Hardware

AM335x NAND Boot Troubleshooting: SPL Rejected by BootROM

AM335x NAND boot troubleshooting: why BootROM rejects SPL (ECC/OOB layout, tiimage header, SYSBOOT, pad-byte, watchdog). Verification steps and fixes.

1 answer 1 view

AM335x NAND Boot Issue: Custom Bare-Metal SPL (No U-Boot) Not Loaded by BootROM, Causes Reset Loop and Beeps

I’m working on a custom AM3352 board using a fully custom bare-metal SPL (no U-Boot or Linux) for NAND boot mode.

Successfully achieved:

  • NAND initialization (GPMC + ELM + BCH8) via FlashTool
  • Programmed SPL image into NAND (multiple boot copies)
  • Verified pages + OOB + ECC with custom readback code (all OK)

Problem after power-on reset:

  • Board does not jump to SPL
  • Enters apparent reset loop
  • Emits beeps (likely watchdog resets)
  • No UART output (expected at this stage)

Key SPL details:

  • Bare-metal, custom ARM startup
  • Entry point: 0x402F0400 (OCMC) per BootROM requirements
  • NAND ECC: BCH8 using GPMC + ELM
  • OOB ECC layout: 14 bytes per 512B sector (13 ECC + 1 pad), offsets 2, 16, 30, 44
  • Page size: 2048 bytes, OOB: 64 bytes
  • BBM bytes: 0xFF
  • Image wrapped with tiimage (TI tool)
  • Tested without GEL (true cold boot)

Questions:

  • Are there known cases where AM335x BootROM rejects a valid-looking NAND SPL, resulting in reset loops, beeps, or resets?
  • What are common BootROM pitfalls causing this (e.g., ECC polarity, pad byte value, SYSBOOT settings, timing, first page content, copy count)?
  • Is there a minimal known-good NAND SPL layout (non-U-Boot) confirmed to boot on AM335x?
  • Any TI guidance, documentation, or references to similar issues?

Yes — the AM335x BootROM will quietly reject a NAND SPL that looks “valid” to your tools if any ROM‑level expectation is off: header/wrapper, OOB/ECC layout (including the pad byte), SYSBOOT strapping or the ROM’s copy limits. Start by verifying the 0x402F0400 load header, the 2‑byte BBM and BCH8 OOB offsets (2, 16, 30, 44), and the SPL size; if those pass, try a known‑good TI/U‑Boot SPL and a pad‑byte A/B test (0x00 vs 0xFF).


Contents


Symptoms & quick checks

What you describe — no UART output, repeated resets/beeps — is consistent with the ROM failing to validate/load the SPL and letting the watchdog reset the system. A few quick, high‑impact checks you can do right now:

  • Verify SYSBOOT is set to NAND boot (pin straps or EEPROM). If SYSBOOT points elsewhere the ROM won’t attempt the NAND SPL you programmed.
  • Read back the first NAND page (main + OOB) exactly as the ROM sees it. Confirm the 8‑byte image header in the page main area (load address & image size) and the OOB layout (BBM + ECC bytes at the expected offsets).
  • Confirm your SPL binary (wrapped) is small enough for the ROM to copy into OCMC: keep the SPL ≤ 0x20000 (128 KB) and ensure the header size field matches the actual SPL size (the ROM enforces copy limits) — see the AM335x U‑Boot notes and reverse engineering notes for these constraints.
  • Try a known‑good SPL (TI or U‑Boot SPL image) burned into NAND. If that boots, the problem is the image format or SPL content; if it also fails, suspect hardware/strap/timing.

For reference on BootROM expectations and examples, see the TI E2E ECC mapping discussion and the U‑Boot user guide linked below: TI E2E ECC mapping thread and AM335x U‑Boot User’s Guide.


How the AM335x BootROM expects NAND SPL to look

High‑level requirements the ROM enforces (summarized):

  • Image header/wrapper: The ROM expects a TI‑style image header at the very start of the NAND image (first bytes of the first page). The common header is 8 bytes: 4 bytes load address and 4 bytes image size; the ROM uses these when copying into OCMC (0x402F0400) and when deciding whether the image fits. See the U‑Boot notes on the image header and the reverse‑engineered BootROM behavior for details (U‑Boot guide, reverse engineering repo).
  • Load address: The ROM copies the SPL into OCMC and expects to jump to 0x402F0400. If the header load address is wrong the ROM won’t find your reset vector.
  • SPL size limit: The ROM’s copy window to OCMC is limited (practical guidance says keep SPL ≤ 0x20000 and fields in header must match actual size). Oversized images are rejected.
  • NAND ECC/OOB layout: With 2KB pages + 64B OOB and BCH8 you must place a 2‑byte BBM followed by four 14‑byte ECC groups (per 512‑B sector) at offsets the ROM expects. If the ECC layout or placement is wrong the ROM fails its checks and bails out (this is explicitly discussed by TI staff in the E2E thread).

Concrete layout expectations are covered in the sources below; the reverse‑engineering notes are useful for what the ROM actually checks at boot (am335xbootrom repo).


NAND OOB / BCH8 mapping and pad‑byte ambiguity

Typical OOB mapping for 2K/64 OOB with BCH8 (4 × 512‑byte sectors):

  • OOB[0…1] — Bad‑block marker (BBM); good block = 0xFF 0xFF
  • OOB[2…15] — ECC for sector 0 (14 bytes)
  • OOB[16…29]— ECC for sector 1 (14 bytes)
  • OOB[30…43]— ECC for sector 2 (14 bytes)
  • OOB[44…57]— ECC for sector 3 (14 bytes)
  • OOB[58…63]— spare (typically 0xFF)

Offsets (2, 16, 30, 44) are the 0‑based starts of the 14‑byte ECC groups — exactly what the TI E2E post and community work have recorded. See the TI thread for the TI engineers’ statement that “the BootROM on the AM335x expects this layout when it reads the first page of the boot sector” (TI E2E ECC mapping thread).

Pad‑byte ambiguity

  • There is a real, documented conflict in public resources: some TI guidance (forum excerpt) says the 14th ECC byte is used as a 0x00 pad for alignment; other references (U‑Boot guide and reverse‑engineering notes) and community experience report 0xFF as the pad/spare default. That single byte can flip the ROM’s validation result.
  • What to do: try both. Program two boot copies (or two boards): one where the ECC 14‑byte groups end with 0x00, another ending with 0xFF. If one boots and the other doesn’t, you’ve reproduced the ROM expectation. If neither boots, continue down the checklist.

Also confirm ECC polarity/ordering — some tools or ELM implementations produce bytes in a different bit order or with inverted polarity. The reverse‑engineering notes explicitly call out “wrong ECC polarity” as a boot‑failure cause (am335xbootrom).


Common BootROM pitfalls causing reset loops & beeps

Here are the faults that most often look like the symptoms you reported, plus how to check them:

  • Incorrect image header or wrapper
  • Symptom: ROM reads first page but refuses to copy/jump.
  • Check: read the first 8 bytes of the image; decode as <load_addr:uint32><size:uint32> little‑endian and confirm load_addr == 0x402F0400 and size equals your SPL. Use the TI image wrapper or a verified mkimage/tiimage step to generate a proper header. See the U‑Boot notes and reverse‑engineering findings for the ROM’s header checks (U‑Boot guide, am335xbootrom).
  • SPL too large for OCMC copy window
  • Symptom: ROM aborts copy or copies truncated data → immediate crash/reset.
  • Fix: shrink SPL or move large data to later stages (keep SPL ≤ ~128 KB).
  • Bad‑block marker (BBM) not 0xFFFF at start of page
  • Symptom: ROM skips block or treats page as bad.
  • Fix: ensure erase + program sets OOB[0…1] = 0xFF 0xFF for the block you’re using.
  • ECC layout mismatch (offsets, pad byte, polarity)
  • Symptom: ROM ECC check fails and it gives up.
  • Fix: place ECC at offsets 2/16/30/44 and test pad byte variations as noted above.
  • SYSBOOT mis‑strapping / EEPROM contents
  • Symptom: ROM attempts a different boot device or sequence.
  • Check: verify SYSBOOT pin states and any EEPROM that supplies SYSBOOT bits; compare to the intended NAND boot configuration.
  • Watchdog / clock/timer mismatch
  • Symptom: rapid watchdog resets, beeps; no chance for SPL to run initialization.
  • Check: confirm the clock source used by your board (internal 32 kHz can be unreliable for watchdog timing). The TI forum thread on early watchdog resets discusses clock and timer issues that cause premature watchdog expiry in SPL (TI E2E watchdog thread).
  • Hardware timing / GPMC signals wrong (CE, ALE, RE, RDY)
  • Symptom: silent failure to read pages or corrupted reads.
  • Check: scope the GPMC lines during boot to ensure timings match the NAND datasheet and GPMC config.
  • ROM’s copy destination not matching your entry point / vector table not at 0x402F0400
  • Symptom: ROM copies image but jumps to wrong address or your reset vector is misaligned.
  • Fix: place the ARM vectors at the load address you declare in the header.

Short test to isolate: program a stock TI/U‑Boot SPL (known to boot on AM335x) to NAND. If that boots, your board/NAND/ECC wiring and OOB format are almost certainly correct and the issue is in the header/wrapping or your SPL code. The community reverse‑engineer and docs recommend this as a fast way to eliminate hardware vs image problems (am335xbootrom, U‑Boot guide).


Known‑good minimal SPL NAND layout (non‑U‑Boot)

Use this as a bench reference for a single 2KB page + 64B OOB first‑page layout that the ROM expects (conceptual):

Main area (first page)

  • bytes 0…3 : image load address (little‑endian) = 0x402F0400
  • bytes 4…7 : image size (little‑endian) = size of SPL in bytes
  • bytes 8…N-1 : SPL payload (vectors + code), padded as needed

OOB area (64 bytes)

  • OOB[0…1] = 0xFF 0xFF (BBM — good block marker)
  • OOB[2…15] = ECC( sector0 ) (14 bytes)
  • OOB[16…29]= ECC( sector1 ) (14 bytes)
  • OOB[30…43]= ECC( sector2 ) (14 bytes)
  • OOB[44…57]= ECC( sector3 ) (14 bytes)
  • OOB[58…63]= spare bytes (prefer 0xFF)

A few practical notes:

  • The header must be at the start of the image that you program into NAND (first bytes of the first page). If you generate the SPL file separately, prepend the 8‑byte header before programming.
  • When computing ECC, ensure the ELM/GPMC method you use produces the same exact 14‑byte grouping and byte ordering the ROM expects. Differences in bit order or byte order will fail the ROM check.
  • Because of the documented conflict about the 14th ECC byte pad value, try creating one boot copy with the last byte of each 14‑byte ECC group set to 0x00, and another copy with it set to 0xFF. If only one variant boots, you’ve found the ROM’s pad expectation.

If you want a quick script example to create a headered SPL (Python), here’s a minimal approach:

python
# pack_header.py
import struct
spl = open("spl.bin","rb").read()
loadaddr = 0x402F0400
size = len(spl)
with open("spl-with-header.bin","wb") as f:
 f.write(struct.pack("<II", loadaddr, size))
 f.write(spl)

Wrap or sign that spl-with-header.bin with your TI image tool if required by your platform, then program to NAND.


Verification checklist & tools (bench flow)

Step‑by‑step when you’re at the bench — run these in order and stop when you get a positive result:

  1. Sanity test: program a stock TI/U‑Boot SPL image in NAND and try a cold boot. If that works, proceed to focus on your SPL image format and code; if not, suspect hardware/strap/timing.
  2. Read first page main+OOB with your FlashTool and verify:
  • header load address = 0x402F0400
  • header size matches SPL file size
  • OOB[0…1] == 0xFF 0xFF
  • ECC groups present at offsets 2,16,30,44
  1. SPL size: confirm SPL ≤ 0x20000. If greater, shrink or relocate large data sections.
  2. ECC parity/order: recompute ECC for each 512‑byte sector using the same algorithm (BCH8) and compare byte‑for‑byte to the OOB. If they differ, check ordering/polarity and the pad byte at the end of each 14‑byte block.
  3. Pad test: program two boot copies (or areas) with pad byte = 0x00 and pad byte = 0xFF. Boot and observe whether ROM accepts one variant.
  4. SYSBOOT and EEPROM: verify strap pins and EEPROM contents that set the boot configuration. Make sure the ROM is actually configured to try NAND boot first.
  5. Watchdog/clock: confirm oscillator / timer source used for watchdog is sane. If SPL depends on a clock that doesn’t tick as you assumed, the watchdog will expire (see TI watchdog thread for examples and guidance: watchdog thread).
  6. JTAG inspection: after a cold reset, break with JTAG and inspect 0x402F0400 — if the ROM copied the image you should see your SPL contents in OCMC. If the memory is empty, ROM didn’t copy (header/wrapper/OOB failure); if the memory is present but CPU resets right after jump, your code crashed.
  7. Signal timing: scope CE/ALE/RE/WP/RDY during ROM reads to confirm proper bus timing. Bad timing can look exactly like data corruption.
  8. If still stuck: capture and post the raw first page hex (main + OOB) and a short description of how you generated ECC; the community and TI E2E responders often spot a single mismatched byte quickly.

Useful tools: FlashTool (you already use), JTAG (to inspect OCMC and registers), NAND readback utility (dump main+OOB), logic analyzer / scope for GPMC timing, and test images (stock SPL/U‑Boot).


Recovery & fallback options

If NAND boot stays stubborn, use these fallbacks to gain debug visibility:

  • Boot from UART or USB boot to load a minimal program into RAM and run it. That gives you console output to debug clocks, SYSBOOT and watchdog handling.
  • Use SD/eMMC if your board supports it; program a small image there and let the ROM boot from it.
  • JTAG: single‑step through ROM behavior or run your SPL in RAM to check vector/stack setup and early peripherals.
  • As a last resort, create a small two‑stage approach: a tiny ROM‑friendly SPL (under the size limit) whose only job is to initialize clocks and disable/reset watchdog, then load your main code from NAND. That isolates watchdog/timing issues from NAND validation.

Sources


Conclusion

AM335x NAND boot failures that present as reset loops and beeps are usually the ROM doing exactly what it’s supposed to do: validate and reject an image that doesn’t meet its exact expectations. Focus first on the 0x402F0400 header + SPL size, the 2‑byte BBM and the BCH8 OOB placement (offsets 2, 16, 30, 44), and the pad‑byte ambiguity (test both 0x00 and 0xFF). If a known‑good TI/U‑Boot SPL boots on your board, you’ve narrowed the problem to image format or ECC ordering — if it doesn’t, concentrate on SYSBOOT, GPMC timing, and watchdog/clock sources. Start with the simple A/B pad test and a stock SPL; those two quick experiments usually reveal whether this is a ROM/format issue or a hardware/timing problem.

Authors
Verified by moderation
Moderation
AM335x NAND Boot Troubleshooting: SPL Rejected by BootROM