Repository navigation
Add datasheet metadata extraction, UNICOMM/VREF/TIMB blocks, and four register fixes - #40
Draft
CordlessCoder wants to merge 137 commits into
Draft
CordlessCoder wants to merge 137 commits into
CordlessCoder wants to merge 137 commits into
Conversation
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
Signed-off-by: CordlessCoder <roman@vm.net.ua>
CordlessCoder
force-pushed
the
low-power-metadata
branch
from
July 30, 2026 07:57
5d41980 to
9e11862
Compare
CordlessCoder
force-pushed
the
low-power-metadata
branch
from
July 30, 2026 08:39
9e11862 to
d63bb5f
Compare
i509VCB
reviewed
Aug 1, 2026
SLAU847 contradicts itself: its Reset Causes table lists 10 as reserved and 13 as the software-triggered BOOTRST, while the RSTCAUSE field description for this SYSCTL variant says 10, and the SVD was generated from the latter. Every other family, and SLAU846 throughout, agree on 13. BOOTWWDT0 was already corrected against the same table.
SLAU893 puts all three at bit 0, as do every other family's register YAML and SVD; only the C-series SVD says bit 2. SETUSEEXLF at bit 2 means LFCLK_IN can never be selected, since the write lands on a reserved bit.
SLAU893 documents two C-series SYSCTLs by name, SYSCTL_C1103_C1104 and SYSCTL_C1105_C1106, and sysctl_c110x describes the first. Sharing it left MSPM0C1105/C1106 unable to reach its crystal or its 1-wait-state flash band. Transcribed from SLAU893 2.7 and checked field-by-field against it.
The tools/ scripts read datasheets, errata sheets and reference manuals, and until now there was no way to obtain them.
Every instance shares tim_v1, so nothing in the register YAMLs or the SVDs says which one has a repeat counter or a 32-bit counter. The datasheet's TIMx configuration table does, and is the only source which is both per-device and complete; the module docs give the counterexamples which rule out the SVDs, the TRM and sysconfig's SYS_FLAVOR.
Generated by tools/timers.py. Nothing reads it yet.
How long a device takes to reach RUN bounds which sleep mode is usable before a given deadline. Note the figures are typical rather than guaranteed: the datasheet's value cell spans its MIN, TYP and MAX columns. STOP2 is also not slower to leave than STOP1 everywhere, since it stops SYSOSC rather than throttling it.
Generated by tools/wakeup.py. Nothing reads it yet.
Contributor
|
Well done! but I agree with @i509VCB , maybe group the changes into specific topics (for example sysctl, adc etc) and push dedicated PRs |
Contributor
Author
My metapac branch is what backs my HAL branch, which I've extensively tested. |
TEMP_SENSE0 is one ADC code and means nothing without the trim temperature, the sensor's slope, the sample window and the reference the factory measured against. None of the four has a machine-readable source. The reference varies per device across three values, and three datasheets contradict themselves about it. The overrides file records which half hardware agreed with.
Also reports a family with no constants, and a positive temperature coefficient, which is what a misread MIN/TYP/MAX column looks like.
The calibration sample window is the one worth spelling out: too short but still past settling reads plausibly and drifts, where a wrong reference is off by hundreds of degrees. Also names the g350x/g351x pair, which is the clearest evidence that the reference is a device fact and not a document defect.
The earlier sweep left stride out because the sources looked split. They are not. The SVDs, the shared hw_dma.h and driverlib all describe the IP superset and carry the stride encodings on every device, so none of the three is a per-device statement; the header constant and the datasheet's feature table are, and they agree on all ten families which state both. The TRMs' DMA_A/DMA_B comparison settles nothing either way. It grants DMA_A stride, against both per-device sources, and denies it a repeated mode which its own transfer-mode table types full-feature.
DMA_SYS_MMR_EM reads like a switch for the whole of DMACTL.EM and is not one. The field selects four modes and the older DMA implements three: every datasheet carrying the feature table ticks table and fill on its full channels whether or not the constant is defined. Only gather follows it. Recording the constant as an extended-mode bool would have denied fill and table to the eleven families which have them.
CTL0.PWRDN resets to automatic power down, and the TRM pays for the wake out of the programmed sample window rather than in hardware: SCOMPx has to cover Twakeup on top of the sampling the signal needs. A window sized for the signal alone samples an input that has not finished waking, and nothing reports it. Nothing machine-readable carries the figure. sysconfig only warns that it exists and points at the datasheet. The maximum and the typical are separate keys because the datasheets fill different columns: fifteen families state a ceiling, three state a typical and no ceiling at all. One field would let a typical be read as a bound on the three that publish none. The table is unruled, so the column is decided by comparing the figure's position against the MIN/TYP/MAX headers.
The array is based at 0x1020 with a stride of 44, so index 1 - the generic event group - resolves to 0x104c instead of 0x1050. hw_dma.h places DMA_GEN_EVENT.IIDX at 0x1050, and the eleven SVDs which describe the two groups as separate clusters agree. The ten which state one two-element array give a dimIncrement of 44, which is the block's own size rather than the distance between instances.
SYSCTL's SVD body carries every other peripheral's clock config as SYSCTL_MGMT_<other>_CLKCFG, twelve registers on a G350x, each with the field. Testing for it anywhere in the body reported SYSCTL as having a bit it does not have, on 32 of 43 chips. No SYSCTL in the SDK defines a CLKCFG at all. The only BLOCKASYNC-family constant there is SYSOSCCFG.BLOCKASYNCALL, which masks every peripheral at once and is a different thing.
SAMPCLK has no published ceiling, so what bounds a conversion is the window. A SCOMPx shorter than tSample converts an input that has not finished settling, and nothing reports it. It adds to Twakeup rather than replacing it. Every figure sits in the MIN column on all eighteen families. tSample and tSample_step are one quantity: only mspm0h321x states a Vstep, and it is the only 4.5-5.5V part, so the older documents left the step implicit at the device's own full scale. That family states the row twice and the larger is recorded, since 188ns is not a bound at its own rail. Figures round up, so 62.5ns stores as 63. tSample_PGA is keyed by gain rather than by channel, and at x32 it is an order of magnitude above the bare-pin figure. Neither of its unpublished axes is derivable: the G datasheets print six gains and the L datasheets two, and the curves cross; and every row is conditioned GBW = 0x1, which nothing gives a counterpart for. Seven datasheets print the table for the four families that have an OPA, so the map is attached only where an instance exists.
An internal signal needs far longer than a package pin: 10us for the internal reference against a 156ns pin on mspm0l211x. A window sized for the pin samples a signal that has not settled, and nothing reports it. Keyed by signal in the YAML because that is how the datasheets state it, and attached per channel because the signal reaches a different channel number on different families and two channels on some devices. The temperature sensor and the OPA outputs are deliberately absent, so their None means look elsewhere rather than unpublished. The sensor states two figures which differ and are independently absent, so no scalar can carry it; the OPA window is keyed by PGA gain. The tSample_VREF row states the channel it was measured on, and generation now fails if that disagrees with the channel mapping table. One page of the datasheet against another, and the only independent check available on a channel map.
Recorded as 64KB against a 128KB part. SLASEW9E's device comparison table, its family-members bullet and the SDK's mspm0g1507.cmd all say 128; G1506 is the 64KB part.
parts.yaml states flash and SRAM by hand because sysconfig describes neither, and nothing checked either. The datasheets' device comparison table states both per orderable part, so the transcription is verifiable after the fact. Checks rather than writes: the rest of a part number's entry has no machine-readable source. Entries are summed by kind, since the Gx51x parts spend one datasheet figure over RAM and RAM_BANK.
DAC0 has been in the metadata on ten chips with an address, pins and an interrupt, and no register block behind any of it. Extracted from the G151x SVD. The G150x and G350x SVDs give the block an FSUB_1 at 404h which neither hw_dac12.h nor SLAU846 Table 20-7 has, and fold the two interrupt groups into one array where the newer ones split CPU_INT and GEN_EVENT. Both groups have the same layout and the same IIDX values, so they share one fieldset.
The other direction of register_blocks_exist. A peripheral with no perimap entry is described in the metadata and cannot be addressed, and nothing said so. Sixteen types are in that state and are listed; a new one now fails generation rather than joining them.
The package filter cannot tell a package this part is not offered in from one sysconfig has no entry for, so four part numbers across g150x and g5187 have been losing a package silently. Both datasheets sell it: MSPM0G150xSDGS28R and MSPM0G5187SDGS20R. Reported rather than fatal - the source is incomplete, not contradictory, and parts.yaml should keep saying what TI sells.
62 instances across fifteen families have been in the metadata with no register block, which is why no legacy SPI driver could be written against the PAC. Extracted from the G151x SVD, which names the three interrupt groups as hw_spi.h does. Twelve SVDs call them INT_EVENT0-2, the C110x and MSPS003FX pair describe only the first, and the three L SVDs drop INTCTL; all of that is SVD incompleteness. Those parts' own device headers declare SPI0 as a full SPI_Regs, driverlib gates none of it per device, and SLAU847's SPI register table lists INTCTL and all three groups. Shaped to match unicommspi_v1, which describes the same SPI function.
The note said "legacy versus B" and left two register blocks to write. The calendar registers are byte-identical in hw_rtc.h and hw_lfss.h, at the same absolute address, so there are not two RTCs - there is an RTC-only peripheral and an LFSS that wraps the same group with tamper I/O, a watchdog and scratch-pad memory. Two things a writer would otherwise get wrong. The legacy GEN_EVENT group is spaced 4 bytes, alone in the portfolio; hw_rtc.h and SLAU846 SS37.4 both say so and the SVD normalises it. And RTC_B is not a block: sysconfig emits it and LFSS as two instances at one address on five families, while four others have an LFSS and no RTC_B node at all.
GpAmp and Event are on the list for a different reason from the other thirteen. Neither has registers of its own: the GPAMP is configured through SYSCTL.PMUOPAMP, which every SYSCTL block already generates, and the event fabric is the per-peripheral FSUB/FPUB registers every block already carries. TI ships no base address, no register struct and no driverlib module for either. Both are reachable today, so reading the list as fifteen pending blocks overstates the work by two.
Two event subscriber ports, which are the whole programmable surface: the wake behaviour itself is automatic and SLAU847 SS3.3.1.3 calls it transparent to software. They let a generic event channel wake the device rather than an NVIC interrupt, and had no accessor. CHANID is four bits, per SLAU847 Table 3-26 and the range in the field's own description. The 21 SVDs give it 2, 3, 4 or 9 and hw_wuc.h masks it 0xFF; adc_v1 and comp_dacout already carry the same field at four.
Three families called them WUC_FSUB0/1 and eight EVENT_SUB_PORT0/1, so a consumer matching on the name got a different answer per family. WUC_FSUB0 is the TRM's wording rather than the datasheets', and is chosen against the datasheet on purpose: a group member becomes a variant of the generated Group0 enum, and this name maps one-to-one onto the register a driver writes. Existence still comes from the datasheet, which is why c110x, l110x and msps003fx have neither.
Group membership came from the device header's IRQn entries, with the YAML supplying only the IIDX. IRQn names peripherals, and the wake-up controller's two event subscriber ports are not peripherals - no header on any device lists them, though every datasheet's interrupt table gives them an IIDX. So both entries were dropped on all eleven families which declare them, and nothing could tell a consumer which group member had woken it after arming WUC.FSUB. That is the whole point of the block added in the previous commit.
DMACTL.EM value 1h was absent, so the generated enum said _RESERVED_1 for a mode the device implements and Dma::gather_mode already documents. Reaching it meant Em::from_bits(1). The block was extracted from an SVD of the older generation: those twelve omit the value and the nine newer ones enumerate it, while hw_dma.h has defined DMAEM_GATHERMODE as 1 throughout. Second time that extraction choice has cost this block something, after SRCWDTH/DSTWDTH. Every other enum here was diffed against the union of all 21 SVDs by value; this was the only gap.
The useful half is the DSSM mailbox at 1100h-110Ch: two 32-bit buffers with flow control carrying messages between a debug probe and the target over SWD, which SLAU847 SS35.2.4 offers for talking to application software when no other interface is present. PWRUPIFG and PWRDWNIFG report a probe attaching and disconnecting, which is the only way software can know it is being debugged. Extracted from the G518x SVD rather than the widest. The C110x one adds a PINCM array holding SWCLK/SWDIO and a CPU_CONNECT_0 at 480h; hw_debugss.h has neither and SLAU893 Table 22-11 - the C-series TRM, for that same family - lists the fourteen registers here and calls every other offset reserved. TX and RX are named from the probe's side, so TXD is read-only to the CPU and reading it is the only way to clear TXCTL.TRANSMIT. TRANSMIT and RECEIVE keep their EMPTY/FULL enums for that reason, where the equivalent bits in spi_v1 were dropped.
"IOMUX register index" left a consumer to guess between three things: the register address, the zero-based accessor index, and TI's own one-based PINCM number. A host tool reading it picked the first and landed one register high on every pin. Also record that it cannot be computed from the pin number. That holds on 14 of the 43 part numbers and on the other 29 the difference spans -27 to +28.
…ata lands `wakeup_pins` is None on 30 of the 43 part numbers, so a reader who meets it without a count has no way to tell a vendor gap from a negative answer. The obvious fallback is the IO structure, which SLAU846 Table 8-1 gets wrong on the two families whose open-drain pins have no wakeup logic. Both pin maps are top-level in the JSON and per-pin in the PAC, which has already sent a consumer looking for a field that is not there.
Four conflicts, all against changes this branch already carried: cpuss_v1 and gpio_v1 are mspm0-rs#46 coming back. The branch has the same widths under the cleanup pass, which merges the five interrupt fieldsets and drops the trivial enums, so the branch side was kept. Flattening both revisions to register offset and field bit-ranges shows no difference: 15 registers in cpuss_v1, 62 in gpio_v1. ./d takes the Darwin job count, which supersedes the plain nproc here. GENERATE_PERIPHERALS takes both entries.
An entry keeps permitting the absence after the block arrives, so it would swallow a later regression that dropped it again.
This is mspm0-rs#42 coming back. Its commits were cut from this branch, which has since carried each of them further, so every conflict takes the branch side. The merged tree is identical to the branch before the merge.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This started as low-power metadata and went well past it. Three themes, and a suggested review order
at the end.
This PR was created with LLM assistance, but I have reviewed all of the code as it was being written, and it's proven to work in my HAL branch.
Metadata the datasheets have and the machine-readable sources don't
TI's SysConfig and SDK headers say nothing about power modes, wake-up cost, timer capability or
errata. Eight extractors under
tools/read the datasheet and errata PDFs into per-family YAML underdata/. Re-run them after a source bump rather than editing the YAML by hand. Each reports a problemper family it cannot answer, so a bump that breaks an extraction is not silent.
operating_modes.pydata/operating_modes/timers.pydata/timers/wakeup.pydata/wakeup/errata.pydata/errata/vref.pydata/vref/Tstartup, and whether the reference drives theVREF+pinadc_channels.pydata/adc_channels/uart.pydata/uart/comp.pydata/comp/The ADC channel mapping is the one of these the SDK does state machine-readably, and the datasheets
still won: the SDK keys it per SDK family, and a family overstates its parts. It routes the OPA
outputs on MSPM0G110x, which has no OPA; the per-part tables are right everywhere the two disagree.
The UART features repeat the lesson per instance: sysconfig's
SYS_LIN_ENis1on main UARTsacross most older families, against their own datasheets, SVDs and driverlib.
Alongside them, curated per-family values in
data/parts.yamlthat have no machine-readable origineither: the clock ceilings,
sysosc_base_hz, theflash_wait_hzladder,adc_clock_hz,trng_clock_hz,standby1_timersandflash_sector_bytes, each with the datasheet it came fromnamed in a comment.
ChipandPeripheralgrow the fields to carry all of this, plus interrupts linked to theirperipherals, memory regions, NVIC priority bits and the clock tree. The doc comments in
mspm0-data-typessay what each field means and when it isNone.verify.rswent from 10 checksto 24 to keep them honest. Six of the old ten only asserted power domains and are gone, since the
corrections are now applied unconditionally. It now checks that wake times are ordered by depth, that
a timer's counter count is addressable by its register block, and that every peripheral type is one
the generator knows.
PowerModecarries the STOP and STANDBY sub-mode. STOP0/1/2 and STANDBY0/1 each disable a supersetof the one before, so they order cleanly. RUN and SLEEP stay whole, because their sub-modes are
clock-source policies rather than depths: RUN2 runs the CPU with SYSOSC off where the deeper SLEEP0
has it on.
New register blocks
unicomm_v1and its four mode maps (unicommi2cc,unicommi2ct,unicommspi,unicommuart)vref_v1tim_btimersysctl_c1105_c1106. This one exists because SLAU893 documents two C-series SYSCTLs by name, andthe C1105/C1106 one is a strict superset: crystal drivers,
MCLKCFG.FLASHWAITand the HSCLK mux.sysctl_g518x. The G518x had been borrowing the G350x block, which misses its entire 0x3000flash-protection region and describes a
PMUOPAMPand aCANCLKSRCthe part does not have.TI's per-family header and SVD agree it is the G351x layout minus the SRAM bank-1 registers,
plus the USB FLL — including
EXCLKSRCposition 6, which the G350x/G351x SVDs leave reserved.comp_v1andcomp_dacout, for the 12 families with a comparator. One layout;dacoutaddsonly
CTL1.DACOUTEN(8-bit reference DAC to a pin) on the four families whose headers stateCOMP_SYS_DACOUT_EN. The G350x-era SVDs put the interrupt registers at the wrong offsets anddrop
GEN_EVENTentirely; the header, both TRMs and the newer SVDs agree on 0x1020/0x1050.IIDX.STATis widened to three bits: every TI source masks it0x3while listing value4h.crc_v1,crc_16andcrc_p, covering all 18 families.16dropsPOLYSIZE(those devicesare CRC-16 only, per the headers'
CRC_SYS_CRC32_ENABLE = 0);padds theprogrammable-polynomial register (TI's CRCP). The data registers are plain words.
Four fixes that change which bits generated code touches
cpuss_v1gave the interrupt group'sMIS,ISETandICLRone bit each.hw_cpuss.hmasks allfive registers
0xFF.gpio_v1based both generic event blocks at bit 0.GEN_EVENT1covers DIO16 to DIO31 at bits 16to 31.
vref_v1had a singleREADYbit. There are three, and they are not evenly spaced:READY0isbit 0,
READY1bit 2,READY2bit 3.CRC0was reported in PD0 on the L112x and L211x families. It is PD1.Plus three C-series SYSCTL field offsets and the software
BOOTRSTencoding onsysctl_l110x_l130x_l134x. Two more came out of the operating-mode extractor: MSPM0H3216's table hasno SHUTDOWN column, so a trailing-column heuristic was eating its STANDBY1, and a footnote marker
rendered without parentheses cost the MSPM0C1106 GPIO row its deepest column.
The rest of the register changes are shape-neutral cleanup along the lines the README already lays
out. Each cleanup was checked against the generated Rust, and the offsets, masks and shifts are
unchanged.
data/registers/README.mdis new. It records two things that previously lived only in commitmessages: the evidence for each place a block deliberately departs from the SVD, and a table of what
the cleanup checks still report and why each was left alone.
chiptool fmtstrips comments, so thatevidence could not live in the YAML.
Generator
build-metapacformats withprettypleasein-process instead of spawningrustfmtper file. Thatwas 33.6s of its 34s runtime, and is now about 2.6s.
./d checkbuilds the chips in parallel over ashared jobserver.
Additional cleanup: the source values are gathered into one
Sourcesstruct that narrows to afamily, the
data/readers share one loader, and the peripheral-type prefix matching is a tableinstead of an
ifchain.Two deliberate changes. The power-domain corrections are now stated once per series rather than as
per-family name lists, which had never been extended to the newer families; that is the
CRC0fixabove. And a perimap version now means the register block exists, which
verify.rschecks. RTCpreviously claimed
legacyandbon 21 chips with neither block curated.Suggested review order
mspm0-data-types/src/lib.rsandmspm0-metapac-gen/res/metadata.rs, as final state rather thanas the 25 commits that touch them. This is the public API, and the doc comments are the
authoritative statement of what
Nonemeans.mspm0-data-gen/src/verify.rs. It decides how much of the rest needs auditing by hand.data/parts.yamlanddata/operating_mode_overrides.yaml: datasheet values that could not beextracted automatically.
for each is in
data/registers/README.md.than the field lists, since they decide which family gets which block.
its source table and the trap it works around.
./d,Cargo.toml, the READMEs,transforms/.The extractors, the cleanups and the script changes are independent of everything else, and could be
split into a separate PR.