DSLite to SVD converter for MSP430
DSLite is a Texas Instruments-internal
file format for describing their microcontroller address layout and
peripherals. For the purposes of using svd2rust
with TI devices, msp430_svd
can generate System View Description
files instead. SVD may be an ARM format, but the format is nominally
CPU-neutral; TI even previously used SVD for some of their microcontrollers.
This repository supplies a copy of TI's C headers and DSLite files for the msp430 family. All commands are run from the root directory of this repository.
You will need svd2rust
commit 783fbd0
or later to generate PACs for SVD files from msp430_svd
.
This command will create an SVD file (out.svd
) for the MSP430G2553 MCU:
$ cargo run -- msp430g2553 > out.svd
You can reformat the result using xmllint
:
$ xmllint -format out.svd > msp430g2553.svd
If you are interested in just getting started quickly, you can stop here and
generate a PAC using svd2rust
:
$ svd2rust -g -i msp430g2553.svd --target msp430
However, you will probably want to patch your shiny new SVD file later.
Although the the output from msp430_svd
in the first step is usable as-is,
it is likely some register fields in the TI-provided DSLite files will be wrong
or missing. To fix these registers, we can leverage the work of the stm32-rs
team to patch the output of msp430_svd
using svdtools
.
svdtools
is written in Python. The stable version can be installed from
PyPI:
$ pip3 install svdtools
Patches are kept under overrides/devices
. There is one patch file per device,
named after the device (e.g. msp430g2211.yaml
). The overrides/peripherals
directory is meant to facilitate code reuse.
If a patch file for your MCU doesn't exist under overrides/devices
, see the
next section for a quick start on finding missing
register fields and creating your own patch.
Create a patch by invoking svd
like the following for your device (continuing
to use msp430g2553 as an example):
$ svd patch overrides/devices/msp430g2553.yaml
The output of the above command will be written to msp430g2211.svd.patched
(hardcoded). You can format the patched portions by running xmllint
:
$ mv msp430g2553.svd.patched msp430g2553.svd.patched.bak
xmllint -format msp430g2553.svd.patched.bak > msp430g2553.svd.patched
At this point, you may wish to compare the original formatted output to the patched output to double-check the results:
$ diff -u {{DEVICE}}.svd {{DEVICE}}.svd.patched
svdtools
patch files are written in a YAML format described here.
Look at the overrides/devices
directory for existing examples on how to write
patches, along with the linked documentation.
Versions of svdtools before commit 79a57db
do not properly handle
registers that completely omit their fields. A workaround is to _delete
such
registers and recreate them with the missing fields.
When possible, I use the MSP430x{1,2,3,4,5,6}xx Family User Guide name for missing fields and enums in registers. My theory is that TI deliberately omits the DSLite-equivalent of SVD fields when the entire register contains a single field. In the User Guide, these missing fields tend to be named after the register itself.
The toolchain files (msp430-gcc-support-files/include
) seem to omit the field
names as redundant. As of this writing (4-23-2020), removing the redundancy is
not possible to do safely via svd2rust
- the bits()
method is always unsafe.
Sometimes, the enum or register name to choose isn't obvious from the User
Guide. In that case, I do a grep of device-specific toolchain files- located
under msp430-gcc-support-files/include
- for hints. Using TAIV
for
msp430g2211 as an example:
$ grep TAIV msp430-gcc-support-files/include/{msp430g2211.h,msp430g2211.ld,msp430g2221_symbols.ld}
msp430-gcc-support-files/include/msp430g2211.h:sfr_w(TAIV); /* Timer A Interrupt Vector Word */
msp430-gcc-support-files/include/msp430g2211.h:#define TA0IV TAIV /* Timer A Interrupt Vector Word */
msp430-gcc-support-files/include/msp430g2211.h:#define TA0IV_ TAIV_ /* Timer A Interrupt Vector Word */
msp430-gcc-support-files/include/msp430g2211.h:#define TAIV_NONE (0x0000) /* No Interrupt pending */
msp430-gcc-support-files/include/msp430g2211.h:#define TAIV_TACCR1 (0x0002) /* TACCR1_CCIFG */
msp430-gcc-support-files/include/msp430g2211.h:#define TAIV_2 (0x0004) /* Reserved */
msp430-gcc-support-files/include/msp430g2211.h:#define TAIV_6 (0x0006) /* Reserved */
msp430-gcc-support-files/include/msp430g2211.h:#define TAIV_8 (0x0008) /* Reserved */
msp430-gcc-support-files/include/msp430g2211.h:#define TAIV_TAIFG (0x000A) /* TAIFG */
msp430-gcc-support-files/include/msp430g2221_symbols.ld:PROVIDE(TAIV = 0x012E);
As of this writing (4-23-2020), I'm not sure of a good way to detect missing
or downright incorrect registers before a PAC
from svd2rust
is generated. Missing registers will of course become apparent
when you try to access a register on a peripheral you know exists, but the
compiler errors unable to find said register name.
It is also possible for registers to be shared between multiple peripherals, such as certain interrupt enable bits. This is okay as long as the DSLite file specifies the registers as belonging to at least one (and ideally one) of the peripherals.
Generating docs via rustdoc for your PAC and using the search facility is an easy way to distinguishing missing registers from registers shared by multiple peripherals. I'm not sure of an obvious way to detect an incorrectly-specified register short of reading all DSLite files and comparing them to the User Guides (impractical!), or checking the documentation of your generated PAC for registers that look wrong.
The same problem as above applies to registers which contain some fields but not all. I'm aware that msp430 DSLite files completely omit the following fields:
WDTPW
field of theWDTCTL
register of the Watchdog Timer+ peripheral.
The DSLite files from TI sometimes completely omit fields for a register.
msp430_svd
will print out registers with completely missing register fields
and enums while generating an SVD file (msp430g2211 in this example):
skipping aliased vector TIMER0_A1_VECTOR
skipping aliased vector TIMER0_A0_VECTOR
warning: no fields in register CALDCO_1MHZ
warning: no fields in register CALBC1_1MHZ
warning: no fields in register TAIV
warning: no fields in register TAR
warning: no fields in register TACCR0
warning: no fields in register TACCR1
To fix these registers, read the Family User's Guide for your specific MCU to find the missing fields and enums- see Naming Convention section for details.
Currently reset values for all registers are set to 0, because I don't have a good way to get them yet. See rust-embedded/msp430#4