I've been using these parts for well over a decade and they're good, if perhaps not popular with amateur makers. I built product around the original Stellaris parts. Right now I'm porting Zephyr to the TM4C129X, so for the first time in forever I need an external debugger.
This is a close relative of other parts in the Stellaris Lineage, although it has a notable difference in that the flash memory controller has a different erase page size. You can program it with the wrong page size, but it goes very slowly. One future enhancement is that it's got a fast programming mode. Maybe later.
I've already modified a local copy of the FW for the Snowflake part on my desk. I have a working probe.
I'm working up a MR. I'm having a little trouble getting it rebased on to the current main, but I'll resolve that soon.
Here's the summary.
Table Driven device information
The existing case switching code should move to a table driven solution to make it easier to add other related parts. That also simplified the probe code and probably makes it shorter by eliminating complex case statements with embedded function calls.
/* Stellaris Fury/DustDevil */
#define LMI_DID1_LM3S3748 0x1049U
#define LMI_DID1_LM3S5732 0x1096U
#define LMI_DID1_LM3S8962 0x10a6U
/* Tiva-C Blizzard (TM4C123) */
#define LMI_DID1_TM4C123GH6PM 0x10a1U
#define LMI_DID1_TM4C1230C3PM 0x1022U
/* Tiva-C Snowflake (TM4C129x) */
#define LMI_DID1_TM4C1294NCPDT 0x101fU
#define LMI_DID1_TM4C129XNCZAD 0x1032U
#define LMI_DID1_TM4C1294KCPDT 0x1034U
typedef struct {
uint16_t did1;
uint32_t ram_size;
uint32_t flash_size;
uint32_t block_size;
const char *driver;
uint32_t target_options;
uint32_t dp_quirks;
} lmi_device_s;
/* To add a new part: verify the DID1 upper 16 bits from the device datasheet and add a row. */
static const lmi_device_s lmi_devices[] = {
/* Stellaris Fury/DustDevil — 1 KiB erase blocks */
{LMI_DID1_LM3S3748, 0x10000U, 0x020000U, 0x400U, "Stellaris", 0, 0},
{LMI_DID1_LM3S5732, 0x10000U, 0x020000U, 0x400U, "Stellaris", 0, 0},
{LMI_DID1_LM3S8962, 0x10000U, 0x040000U, 0x400U, "Stellaris", 0, 0},
/* Tiva-C Blizzard (TM4C123) — 1 KiB erase blocks */
{LMI_DID1_TM4C123GH6PM, 0x10000U, 0x080000U, 0x400U, "Tiva-C", TOPT_INHIBIT_NRST, ADIV5_DP_QUIRK_DUPED_AP},
{LMI_DID1_TM4C1230C3PM, 0x06000U, 0x010000U, 0x400U, "Tiva-C", TOPT_INHIBIT_NRST, ADIV5_DP_QUIRK_DUPED_AP},
/* Tiva-C Snowflake (TM4C129x) — 16 KiB erase blocks */
{LMI_DID1_TM4C1294KCPDT, 0x40000U, 0x080000U, 0x4000U, "Tiva-C", TOPT_INHIBIT_NRST, ADIV5_DP_QUIRK_DUPED_AP},
{LMI_DID1_TM4C1294NCPDT, 0x40000U, 0x100000U, 0x4000U, "Tiva-C", TOPT_INHIBIT_NRST, ADIV5_DP_QUIRK_DUPED_AP},
{LMI_DID1_TM4C129XNCZAD, 0x40000U, 0x100000U, 0x4000U, "Tiva-C", TOPT_INHIBIT_NRST, ADIV5_DP_QUIRK_DUPED_AP},
};
This works unchanged with the existing lmi_flash_erase(), which erases in blocks.
Order of operations discrepancy in the flash stub.
Per the data sheet, the device requires Data, Address, Control. The existing stub writes Address, Data, Control. Existing devices obviously work, that might just be luck.
Also, the existing stub is names stm32f1_flash_write_stub(), which seems wrong.
Correct per data sheet:
void __attribute__((naked))
lmi_flash_write_stub(const uint32_t *const dest, const uint32_t *const src, const uint32_t size)
{
for (uint32_t i = 0; i < (size / 4U); ++i) {
/* §8.2.3.10 (TM4C129X datasheet): write FMD before FMA, then trigger via FMC */
/* Also checked against 6965 and 9B92 Sandstorm/Fury */
LMI_FLASH_FMD = src[i];
LMI_FLASH_FMA = (uintptr_t)(dest + i);
LMI_FLASH_FMC = LMI_FLASH_FMC_WRKEY | LMI_FLASH_FMC_WRITE;
while (LMI_FLASH_FMC & LMI_FLASH_FMC_WRITE)
continue;
}
stub_exit(0);
}