Git Product home page Git Product logo

svdtools's Introduction

crates.io

svdtools

svdtools is a set of tools for modifying vendor-supplied, often buggy SVD files. It can be imported as a library for use in other applications, or run directly via the included svdtools CLI utility.

A common use case is patching vendor-supplied SVD files, then applying svd2rust to the resulting patched SVD.

This project is developed and maintained by the Tools team.

Getting Started with Python version

Python 3.6 or newer is required to install and use svdtools. To install:

$ pip3 install --upgrade --user svdtools

Once installation has completed, the svd utility can be called from the command line.

An example is given in make example, which calls svd patch example/incomplete-stm32l4x2.yaml and generates a patched SVD file example/stm32l4x2.svd.patched.

See Device and Peripheral YAML Format for more information on creating patches.

Getting Started with Rust version

This crate is guaranteed to compile on stable Rust 1.58.0 and up. To install:

$ cargo install svdtools

Once installation has completed, the svdtools utility can be called from the command line. Command line interface is same as CLI for Python version.

Develop

To each their own, but the intended workflow is as follows:

  1. Setup a virtual environment via make setup; this also installs the svd CLI
  2. Activate the virtual environment by running source venv/bin/activate (or use direnv)
  3. Iterate, running make check and make fix as necessary

Device and Peripheral YAML Format

The patch specifications are in YAML, and have the following general format:

# Path to the SVD file we're targeting. Relative to this file.
# This must be included only in the device YAML file.
_svd: "../svd/STM32F0x0.svd"

# Include other YAML files. Path relative to this file.
_include:
    - "../peripherals/gpio_v2.yaml"

# Alter top-level information and peripherals for this device
_modify:
    version: 1.1
    description: bla bla
    addressUnitBits: 8
    width: 32
    cpu:
        revision: r1p2
        mpuPresent: true
    # Peripherals can either live directly at this level (but other top-level
    # fields will name match first)
    C_ADC:
        name: ADC_Common
    # Or they can be inside a _peripherals block, to avoid name conflicts.
    _peripherals:
        FSMC:
            description: Flexible static memory controller

            # Multiple address blocks are supported via the addressBlocks list
            # use either addressBlock or addressBlocks, but not both
            addressBlocks:
                -   offset: 0x0
                    size: 0x400
                    usage: "ADC base registers"
                -   offset: 0x1000
                    size: 0x400
                    usage: "ADC extra registers"



# Add whole new peripherals to this device.
# Incredibly this feature is required.
_add:
    ADC_Common:
        description: ADC Common registers
        groupName: ADC
        baseAddress: 0x40012300
        addressBlock:
            offset: 0x0
            size: 0x400
            usage: "All ADC registers"
        # Multiple address blocks are supported via the addressBlocks list
        addressBlocks:
            -   offset: 0x0
                size: 0x400
                usage: "ADC base registers"
            -   offset: 0x1000
                size: 0x400
                usage: "ADC extra registers"
        registers:
            CSR:
                description: ADC Common status register
                addressOffset: 0x0
                access: read-only
                resetValue: 0x00000000
                fields:
                    OVR3:
                        description: Overrun flag of ADC3
                        bitOffset: 21
                        bitWidth: 1
        interrupts:
            ADC1_2:
                description: ADC global interrupt
                value: 18

# A whole new peripheral can also be created as derivedFrom another peripheral.
_add:
    USART3:
        derivedFrom: USART1
        baseAddress: "0x40004800"
        interrupts:
            USART3:
                description: USART3 global interrupt
                value: 39

# A new peripheral can have all its registers copied from another, in case
# it cannot quite be derivedFrom (e.g. some fields need different enumerated
# values) but it's otherwise almost exactly the same.
# The registers are copied but not name or address or interrupts, which are
# preserved if the target already exists.
_copy:
    ADC3:
        from: ADC2

# The new peripheral can also be copied from another svd file for a different
# device. This is useful when a peripheral is missing in a device but the exact
# same peripheral already exist in another device.
# When copying from another file, all fields including interrupts are copied.
_copy:
    TIM1:
        from: ../svd/stm32f302.svd:TIM1

# Replace peripheral registers by a 'deriveFrom'.
# This is used when e.g. UART4 and UART5 are both independently defined,
# but you'd like to make UART5 be defined as derivedFrom UART4 instead.
_derive:
    # The KEY peripheral looses all its elements but 'interrupt', 'name',
    # and 'baseAddress', and it is derivedFrom the VALUE peripheral.
    # Peripherals that were 'deriveFrom="KEY"' are now 'deriveFrom="VALUE"'.
    UART5: UART4

# Reorder the hierarchy of peripherals with 'deriveFrom'.
# This is used when e.g. I2C1 is marked as derivedFrom I2C3,
# but you'd like to swap that so that I2C3 becomes derivedFrom I2C1.
_rebase:
    # The KEY peripheral steals everything but 'interrupt', 'name',
    # and 'baseAddress' elements from the VALUE peripheral.
    # Peripherals that were 'deriveFrom="VALUE"' are now 'deriveFrom="KEY"'.
    # The VALUE peripheral is marked as derivedFrom the updated KEY.
    I2C1: I2C3

# An STM32 peripheral, matches an SVD <peripheral> tag.
# Does not match any tag with derivedFrom attribute set.
"GPIO*":
    # We can include other YAML files inside this peripheral
    _include:
        - "path/to/file.yaml"

    # Alter fields on existing registers inside this peripheral
    _modify:
        # Rename this badly named register. Takes effect before anything else.
        # Don't use wildcard matches if you are changing the name!
        # We could have specified name or description or other tags to update.
        GPIOB_OSPEEDR:
          name: OSPEEDR
        # Equivalently the register could go in a '_registers' block
        _registers:
            GPIOB_OSPEEDR:
                name: OSPEEDR
        # Change the value of an interrupt in this peripheral
        _interrupts:
            EXTI0:
                value: 101


    # Add new registers and interrupts to this peripheral.
    # Entries are registers by default, which can also go inside a '_registers'
    # block, or interrupts go in an '_interrupts' block.
    _add:
        EXAMPLER:
            description: An example register
            addressOffset: 0x04
            access: read-write
            fields:
                EXR1:
                    description: Example field
                    bitOffset: 16
                    bitWidth: 4
        _registers:
            EXAMPLR2:
                description: Another example register
        _interrupts:
            EXAMPLEI:
                description: An example interrupt
                value: 100

    # Anywhere you can '_add' something, you can also '_delete' it.
    # Wildcards are supported. The value here can be a YAML list of registers
    # to delete (supported for backwards compatibility), or a YAML mapping
    # of lists of registers or interrupts.
    _delete:
        GPIO*_EXTRAR:
        _registers:
            - GPIO*_EXAMPLER
        _interrupts:
            - USART1

    # If registers have unnecessary common prefix/postfix,
    # you can clean it in all registers in peripheral by:
    _strip:
        - "PREFIX_*_"
    _strip_end:
        - "_POSTFIX_"

    # You can collect several same registers into one register array
    # that will be represented with svd2rust as array or elements
    # with one type
    # Minimal version:
    _array:
        ARRAY*: {}

    # You can also use the modifiers shown below:
    _array:
        ARRAY*:
            name: NEW_NAME%s
            _modify:
                FIELD: [MINIMUM, MAXIMUM]
                FIELD:
                  description: NEWDESC
        OTHER_ARRAY*: {}

    # If you have registers that make up a group and can be repeated,
    # you can collect them into cluster like this:
    _cluster:
        CLUSTER%s:
            FIRST_REG: {}
            SECOND_REG: {}

    # A register on this peripheral, matches an SVD <register> tag
    MODER:
        # As in the peripheral scope, rename or redescribe a field.
        # Don't use wildcard matches if you are changing the name!
        _modify:
            FIELD:
              description: NEWDESC

              # Change the writeConstraint of a field to enumerateValues
              _write_constraint: "enum"

              # Remove any writeConstraint from this field
              _write_constraint: "none"

              # Change the writeConstraint of a field to a range of values
              _write_constraint: [MINIMUM, MAXIMUM]

        # Add new fields to this register
        _add:
            NEWFIELD:
              description: DESCRIPTION
              bitOffset: 12
              bitWidth: 4
              access: read-write

        # Often fields that should be one contiguous integer are specified
        # as a number of individual bits instead. This merges any matching
        # registers into a single field with the combined bitwidth and lowest
        # bit offset, and the shared description and access.
        _merge:
            - "FIELD*"

        # You can also merge fields with different base name like this:
        _merge:
            FIELD: [FIELD1, FIELD_?]
        # Or like this:
        _merge:
            FIELD:
                - FIELD1
                - FIELD_?
        # Or even like this:
        _merge:
            NEW_FIELD: "FIELD*"

        # A field in this register, matches an SVD <field> tag
        FIELD:
            # You can optionally specify name for `enumeratedValues`
            _name: NAME
            # By giving the field a dictionary we construct an enumerateValues
            VARIANT: [VALUE, DESCRIPTION]
            VARIANT: [VALUE, DESCRIPTION]
            # Use `-1` for "default" variant which will be consider
            # for all other values that are not listed explicitly
            # usually datasheet marks them `0b0xxx`, `0b1x`, etc.
            VARIANT: [-1, DESCRIPTION]

        FIELD:
            # If a field already has enumerateValues, drop them and
            # replace them with entirely new ones.
            _replace_enum:
                VARIANT: [VALUE, DESCRIPTION]
                VARIANT: [VALUE, DESCRIPTION]

        # Another field. A list of two numbers gives a range writeConstraint.
        FIELD: [MINIMUM, MAXIMUM]

        # Another field with separate enumerated values for read and write
        FIELD:
            _read:
                VARIANT: [VALUE, DESCRIPTION]
                VARIANT: [VALUE, DESCRIPTION]
            _write:
                VARIANT: [VALUE, DESCRIPTION]
                VARIANT: [VALUE, DESCRIPTION]
        # Sometimes fields are to big so we need to split them into smaller fields
        EXTI:
          IMR:
            # This would split MR into MRi where i = 0 ... bitlength
            _split: [MR]
            # This would split CHxFM into CHiFM where i = 0 ... bitlength
            # and use the current bit for the description in each field
            _split:
              CHxFM:
                name: CH%sFM
                description: Processor 2 transmit channel %s free interrupt mask

            # If fields have unnecessary common prefix/postfix,
            # you can clean it in all registers in peripheral by:
            _strip:
                - "PREFIX_*_"
            _strip_end:
                - "_POSTFIX_"

# You can list glob-like rules separated by commas to cover more periperals or registers at time.
# If rule is optional (peripheral may be missing in some devices) add `?~` in the header.
# Don't abuse it. First test not optional rule.
"?~TIM[18],TIM20":
  CR2:
    # Fields also support collecting in arrays
    _array:
      OIS?:
        description: Output Idle state (OC%s output)
      # Optional rules are supported here too
      "?~OIS?N":
        description: Output Idle state (OC%sN output)

Name Matching

Peripheral, register, and field names can be specified:

  • Directly (eg. the full name of the peripheral/register/field)
  • Using ? and * for single- and multi- character wildcards
  • Using [ABC] to give a list of possible matching characters
  • Using commas to separate a list of possible matches

You must quote the name if using any special characters in YAML.

The enumerated values On and Off are treated as a boolean in YAML and Python will throw the error: AttributeError: 'bool' object has no attribute 'startswith', which does not give you much information about where the error is. To avoid it, surround the values with quotes like any other special character.

Style Guide

  • Enumerated values should be named in the past tense (enabled, masked, etc.)
  • Descriptions should start with capital letters and should not end with a period

License

svdtools is licensed under either of

at your option.

Contribute

Pull requests are very welcome!

Please apply black and isort before committing. This can be accomplished by:

  • running make fix
  • running black svdtools/ and isort -y --recursive svdtools/
  • installing an editor/IDE plugin

This avoids bikeshedding over formatting issues :)

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Code of Conduct

Contribution to this crate is organized under the terms of the Rust Code of Conduct, the maintainer of this crate, the Tools team, promises to intervene to uphold that code of conduct.

svdtools's People

Contributors

burrbull avatar adamgreig avatar marcoieni avatar bors[bot] avatar nickray avatar erdnaxe avatar jessebraham avatar kossnikita avatar mabezdev avatar arjanmels avatar simpkins avatar emilgardis avatar jannic avatar rahix avatar maximeborges avatar sonic0 avatar 9names avatar duskmoon314 avatar mciantyre avatar newam avatar couchand avatar ejpcmac avatar jorgeig-space avatar forsakenharmony avatar richardeoin avatar rhamalainen avatar takluyver avatar octycs avatar

Stargazers

ChenKexiong avatar Paolo Barbolini avatar Julian Ortel avatar Matt Schultz avatar  avatar Alessandro Pezzato avatar TANIGUCHI,Kazushige avatar Thiago Holanda avatar David Benjamin avatar Lucas Brendel avatar Fahmim Rezuan avatar Jonathan Beri avatar Decidedly Gray avatar Yusef Karim avatar brokenbyte  avatar Roman Pavelka avatar Seonghyun Park avatar Philip avatar Jon Velando avatar Adriel Souza avatar George Kontridze avatar taotieren avatar Jerry JIN avatar  avatar Niklas Hauser avatar David Dias avatar kevin nel avatar  avatar Michael Berger avatar Rock Boynton avatar Michael avatar Luis Eduardo Gutiérrez avatar Krishna Kumar avatar  avatar Michael Richters avatar  avatar Anton Patrushev avatar Gaute Hope avatar Scott Shawcroft avatar Ahmed Khalaf avatar Jevin Sweval avatar  avatar  avatar Rafael Ristovski avatar W.C. Pannell avatar  avatar Viktor Vilhelm Sonesten avatar Felix Richter avatar Henrik Böving avatar Fabian avatar  avatar Tom Burdick avatar Jared Boone avatar  avatar  avatar Jean-Pierre De Jesus DIAZ avatar Andrey Gulitsky avatar

Watchers

Diego Barrios Romero avatar  avatar Niklas Hauser avatar Jevin Sweval avatar Jacob Rosenthal avatar James Cloos avatar ryan avatar Markus Reiter avatar  avatar Daniel Egger avatar  avatar  avatar  avatar

svdtools's Issues

Detect duplicate YAML keys

Currently duplicate keys are silently ignored, which is a common cause of errors.

In principle any conforming YAML parser should emit a warning in such a situation, but PyYAML does not. A workaround might be possible.

_derive on a register fails when "displayName" is not defined (RP2040)

derive_register() contains the following code:

        rcopy = copy.deepcopy(source)
        rcopy.find("name").text = rname
        rcopy.find("displayName").text = rname

The RP2040 SVD, however, does not define any displayName, so the last line causes AttributeError: 'NoneType' object has no attribute 'text'.

The following modification fixes the problem:

        if rcopy.find("displayName") != None:
            rcopy.find("displayName").text = rname

Derive registers regex

Regex in derive patch doesnt work
Derive from registers is not documented by the way.

Works correctly:

FLASH:
  _derive:
    UNLOCK2: UNLOCK
    UNLOCK3: UNLOCK

Error:

FLASH:
  _derive:
    UNLOCK?: UNLOCK
    Caused by:
        0: Processing device `AT32A403Axx_v2`
        1: According to `FLASH`
        2: Processing peripheral `FLASH`
        3: Deriving register `UNLOCK?` from `String("UNLOCK")`
        4: `Build error: `address_offset` must be initialized
        5: `address_offset` must be initialized

Ignored:

FLASH:
  UNLOCK?: 
    _derivedFrom: UNLOCK

Support REGEX

Hello! The current state of the tool is very limited. Can you add regex to it?

Trying to patch registers with missing fields causes a backtrace

Problem

Consider the following register layout in a pre-generated SVD file:

<register>
          <name>TACCR0</name>
          <description>Timer A Capture/Compare 0</description>
          <addressOffset>68</addressOffset>
          <size>16</size>
          <resetMask>65535</resetMask>
</register>

along with the following YAML file (_svd points to the right place):

_svd: ../../msp430g2211.svd

"TIMER_A2":
  TACCR0:
    _add:
      TACCR0:
        description: "Timer A capture/compare register 0"
        bitOffset: 0
        bitWidth: 16

svd will die with the following backtrace:

$ svd patch overrides/devices/msp430g2211.yaml
Traceback (most recent call last):
  File "C:/msys64/mingw64/lib/python3.8/runpy.py", line 193, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:/msys64/mingw64/lib/python3.8/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:/msys64/mingw64/bin/svd.exe/__main__.py", line 7, in <module>
  File "C:/msys64/mingw64/lib/python3.8/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "C:/msys64/mingw64/lib/python3.8/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/svdtools/cli.py", line 16, in patch
    svdtools.patch.main(yaml_file)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/svdtools/patch.py", line 1025, in main
    process_device(svd, root)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/svdtools/patch.py", line 1005, in process_device
    d.process_peripheral(periphspec, device[periphspec], update_fields)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/svdtools/patch.py", line 434, in process_peripheral
    p.process_register(rspec, register, update_fields)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/svdtools/patch.py", line 756, in process_register
    r.add_field(fname, fadd)
  File "C:/msys64/mingw64/lib/python3.8/site-packages/svdtools/patch.py", line 834, in add_field
    for ftag in parent.iter("field"):
AttributeError: 'NoneType' object has no attribute 'iter'

Cause

According to IRC:

(5:12:22 AM) agg: hah, yes, I see the issue
(5:12:43 AM) agg: the backtrace you're hitting is when it goes through existing fields on the register to check you're not adding a duplicate name
(5:12:51 AM) agg: but there's no <fields> member in this register at all

Workaround

Workaround for now is to delete the register completely and recreate it:

TIMER_A2:
  _delete:
    - TACCR0
  _add:
    TACCR0:
      description: Timer A Capture/Compare 0
      addressOffset: 68
      size: 16
      resetMask: 65535
      fields:
        TACCR0:
          description: Timer A capture/compare register 0
          bitOffset: 0
          bitWidth: 16

svd2rust is unable to generate pac for nxp svd file

Hi,

I'm attempting to generate a pac for an nxp rt633s device (svd: https://github.com/nxp-mcuxpresso/mcux-soc-svd/blob/main/MIMXRT633S/MIMXRT633S.xml) however, svd2rust ends with an error:

[INFO  svd2rust] Parsing device from SVD file
[INFO  svd2rust] Rendering device
[ERROR svd2rust] Error rendering device

    Caused by:
        0: Rendering error at peripheral
           Name: I2S0
           Description: LPC-Next0 I2S interface
           Group: I2S
        1: Could not expand register or cluster block
        2: Error expanding cluster
           Name: SECCHANNEL[%s]
           Description: no description available
        3: Cluster SECCHANNEL[%s] has size 24928 bits that is more then array increment 256 bits

Looking at that particular cluster, we have 96 bits (3x 32-bit registers):

        <cluster>
          <dim>3</dim>
          <dimIncrement>0x20</dimIncrement>
          <name>SECCHANNEL[%s]</name>
          <description>no description available</description>
          <addressOffset>0</addressOffset>
          <register>
            <name>PCFG1</name>
            <description>Configuration register 1 for channel pair</description>
            <addressOffset>0xC20</addressOffset>
            <size>32</size>
            <access>read-write</access>
            <resetValue>0</resetValue>
            <resetMask>0x401</resetMask>
            <fields>
              <field>
                <name>PAIRENABLE</name>
                <description>Enable for this channel pair..</description>
                <bitOffset>0</bitOffset>
                <bitWidth>1</bitWidth>
                <access>read-write</access>
              </field>
              <field>
                <name>ONECHANNEL</name>
                <description>Single channel mode.</description>
                <bitOffset>10</bitOffset>
                <bitWidth>1</bitWidth>
                <access>read-write</access>
              </field>
            </fields>
          </register>
          <register>
            <name>PCFG2</name>
            <description>Configuration register 2 for channel pair</description>
            <addressOffset>0xC24</addressOffset>
            <size>32</size>
            <access>read-write</access>
            <resetValue>0</resetValue>
            <resetMask>0x1FF0000</resetMask>
            <fields>
              <field>
                <name>POSITION</name>
                <description>Data Position.</description>
                <bitOffset>16</bitOffset>
                <bitWidth>9</bitWidth>
                <access>read-write</access>
              </field>
            </fields>
          </register>
          <register>
            <name>PSTAT</name>
            <description>Status register for channel pair</description>
            <addressOffset>0xC28</addressOffset>
            <size>32</size>
            <access>read-write</access>
            <resetValue>0</resetValue>
            <resetMask>0xF</resetMask>
            <fields>
              <field>
                <name>BUSY</name>
                <description>Busy status for this channel pair.</description>
                <bitOffset>0</bitOffset>
                <bitWidth>1</bitWidth>
                <access>read-write</access>
              </field>
              <field>
                <name>SLVFRMERR</name>
                <description>Save Frame Error flag.</description>
                <bitOffset>1</bitOffset>
                <bitWidth>1</bitWidth>
                <access>read-write</access>
              </field>
              <field>
                <name>LR</name>
                <description>Left/Right indication.</description>
                <bitOffset>2</bitOffset>
                <bitWidth>1</bitWidth>
                <access>read-write</access>
              </field>
              <field>
                <name>DATAPAUSED</name>
                <description>Data Paused status flag.</description>
                <bitOffset>3</bitOffset>
                <bitWidth>1</bitWidth>
                <access>read-only</access>
              </field>
            </fields>
          </register>
        </cluster>

What might be causing the error?

Conflict when adding enumeratedValues to fields in registers in a cluster

The Atmel SAML10 chips have a somewhat odd structure for their UART/SPI/I2C peripherals. There are a couple of single SERCOM peripherals, which can be configured into one of those modes based on a MODE register, whose offset is the same for all modes.

The Atmel-provided SVD files model this like so:

<peripheral>
  <name>SERCOM0</name>
  <registers>
    <cluster><name>I2CM</name>
      <register>
        <name>CTRLA</name>
        <fields>
          <field><name>SDAHOLD</name></field>
          ....
        </fields>
      </register>
      ...
    </cluster>
    <cluster><name>SPI</name>
      <register>
        <name>CTRLA</name>
        <fields>
          <field><name>CPHA</name></field>
          ....
        </fields>
      </register>
    </cluster>
  </registers>
</peripheral>

That is, there's a cluster for each independent configuration of the peripheral, with the same register name defined, and different fields defined. (SDAHOLD does not exist in SPI mode, and CPHA does not exist in I2C mode.) Other fields, such as MODE, are in both.

I don't see a way to select just one of these clusters to modify. If I match on CTRLA then I can't match on the right fields, because the selector code in patch.py returns all four of the CTRLA register tags for all four clusters. If I try to introduce a _cluster: entry in the YAML then the code insists on grouping all four of the CTRLA registers together into a block, and failing to validate the offsets or the bitmasks.

Is there a way to use the cluster name purely for selection, instead of for cluster creation? If not, can there be?

0.3.6 doesn't patch a clustered register as 0.2.8 did

My svd has a peripheral PWM with a cluster SM and register SMOCTRL

In 0.2.8 I could patch fields in SMOCTRL with...

PWM?:
  SMOCTRL:
    _clear:
      - PWMXFS
      - PWMBFS
      - PWMAFS

With 0.3.6 it can't find the SWOCTRL register and its unclear to me how to select it (in the cluster SM)

Actual yaml patch
https://github.com/imxrt-rs/imxrt-ral/blob/master/devices/imxrt1176_cm7.yaml#L105

Actual SVD
https://github.com/imxrt-rs/imxrt-ral/blob/master/svd/imxrt1176_cm7.svd

Remove empty tag

How to remove empty fields tag?

<peripheral>
      <name>FLASH</name>
      <description>Flash memory controler</description>
      <groupName>FLASH</groupName>
      <registers>
        <register>
          <name>PSR</name>
          <displayName>PSR</displayName>
          <description>Performance selection register</description>
          <addressOffset>0x0</addressOffset>
          <size>0x20</size>
          <resetValue>0x00000030</resetValue>
          <fields>
          </fields>
         </register>
       </registers>
</peripheral>

Register arrays for registers with fields longer than 32 bit fail

I have a peripheral with a number of registers, each 64 bits long. Within each register is one 40 bit long field.
Trying to merge these registers into an array fails with the following error message:

Traceback (most recent call last):
  File ".venv/bin/svd", line 8, in <module>
    sys.exit(svdtools_cli())
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/cli.py", line 16, in patch
    svdtools.patch.main(yaml_file)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/patch.py", line 1332, in main
    process_device(svd, root)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/patch.py", line 1309, in process_device
    d.process_peripheral(periphspec, device[periphspec], update_fields)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/patch.py", line 665, in process_peripheral
    p.collect_in_array(rspec, rmod)
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/patch.py", line 878, in collect_in_array
    bitmasks = [Register(r[0]).get_bitmask() for r in registers]
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/patch.py", line 878, in <listcomp>
    bitmasks = [Register(r[0]).get_bitmask() for r in registers]
  File "/mnt/btrfs/svdtools/.venv/lib/python3.8/site-packages/svdtools/patch.py", line 1259, in get_bitmask
    mask |= (0xFFFFFFFF >> (32 - fwidth)) << foffset
ValueError: negative shift count
make: *** [Makefile:12: example] Error 1

I have created a minimum failing example that fits neatly into the example from the repo:

_add:
  EXAMPLE:
    registers:
      REG0:
        addressOffset: "0x0000000"
        size: 0x40
        fields:
          FIELD:
            bitOffset: 0
            bitWidth: 33
      REG1:
        addressOffset: "0x0000008"
        size: 0x40
        fields:
          FIELD:
            bitOffset: 0
            bitWidth: 33

EXAMPLE:
  _array:
    REG*: {}

I have played around a bit, and the patch seems to generate fine as long as the field is shorter than 32 bits, even if I offset it into the top half of the register.

(Found on v0.1.13)

Suggestion: peripheral library.

ARM (and others, I assume) provide a number of ready-to-use IP blocks which chip manufacturers
can use. Examples include the SP804 dual timer, SP805 watchdog, PL390 GIC, PL080 DMAC, PL011
UART, and so on.

It could be useful to provide a collection of these 'standard parts' in a library for use in patching
and creating svds.

cannot _derive from a register that is affected by a _cluster modification

I'm trying to make a patch to clean up the peripherals in the ESP32 USB0 peripheral. It has registers for several endpoints that need to be clustered, and I also want to make some of the registers derive from other registers. svdtools currently doesn't appear to allow me to both apply clustering and make derivedFrom modifications at the same time.

If I add a _derive section, these changes appear to take be applied before the _cluster section. When the _derive changes are made it checks that the derivedFrom register names are valid. However, these register names are later changed by the _cluster operation, which makes svdtools generate an invalid SVD files since the derivedFrom fields refer to the old names. I can't use the new names in the _derive section, since svdtools rejects them as invalid since they don't exist yet when the check is made.

In theory I suppose svdtools should track all derivedFrom attributes throughout the SVD file, and update them correctly whenever it renames/clusters/derives a register that is the destination of an existing derivedFrom attribute. This seems possibly more complicated than it is worth, however. Alternatively, it would be nice if there were an easy way to force svdtools to skip the register name check in _derive, so I could specify the desired derivedFrom name without svdtools complaining because it doesn't exist yet.

Here is a with more details about what I would like to accomplish
https://github.com/esp-rs/esp-pacs/pull/213/files/e36f054438d1a2a44e81f24eea4efae80ea861e0#r1550642332

makedeps fails to generate valid `.d` files on cygwin.

There is a failing test:

image

Left:  test.d: \\?\D:\Users\Hydra\Documents\dev\projects\stm32-rs\svdtools\res\makedeps\sub-tests\inc1.yaml \\?\D:\Users\Hydra\Documents\dev\projects\stm32-rs\svdtools\res\makedeps\sub-tests\inc2.yaml
Right: test.d: D:\Users\Hydra\Documents\dev\projects\stm32-rs\svdtools\res\makedeps\sub-tests/inc1.yaml D:\Users\Hydra\Documents\dev\projects\stm32-rs\svdtools\res\makedeps\sub-tests/inc2.yaml

The use of std::fs::canonicalize is adding \\?\ in addition to removing \.\ path components.

These paths are not valid paths when svdtools is used as part of stm32-rs when running make patch in cygwin.
The resulting error from the command that uses the generated dependencies make svd2rust is as follows:

$ make svd2rust
.deps/stm32c011.d:1: *** target pattern contains no '%'.  Stop.

If you inspect the generated file you see this:

$ more .deps/stm32c011.d
stm32c011.d: \\?\D:\Users\Hydra\Documents\dev\projects\stm32-rs\stm32-rs\devices\common_patches\c0.yaml \\?\D:\Users\Hydra\Documents\dev\projects\stm32-rs\stm32-rs\devices\common_patches\nvic\2_prio_bits.yaml

As a workaround I hacked up abspath as follows:

/// Gets the absolute path of relpath from the point of view of frompath.
fn abspath(frompath: &Path, relpath: &Path) -> Result<PathBuf, std::io::Error> {
    //std::fs::canonicalize(frompath.parent().unwrap().join(relpath))
    let path = frompath.parent().unwrap().join(relpath);

    let path_str = format!("{}", path.display()).replace("\\", "/");

    Result::<_,std::io::Error>::Ok(PathBuf::from(path_str))
}

Branch with that is here: https://github.com/hydra/svdtools/tree/working-windows-makedeps

and now the test fails as follows:

Left:  test.d: D:/Users/Hydra/Documents/dev/projects/stm32-rs/svdtools/res/makedeps/sub-tests/inc1.yaml D:/Users/Hydra/Documents/dev/projects/stm32-rs/svdtools/res/makedeps/sub-tests/inc2.yaml
Right: test.d: D:\Users\Hydra\Documents\dev\projects\stm32-rs\svdtools\res\makedeps\sub-tests/inc1.yaml D:\Users\Hydra\Documents\dev\projects\stm32-rs\svdtools\res\makedeps\sub-tests/inc2.yaml

However, the resulting .d files are now valid, here's the same one from the stm32-rs project from before:

$ more .deps/stm32c011.d
stm32c011.d: devices/./common_patches/c0.yaml devices/./common_patches/nvic/2_prio_bits.yaml

I'm no expert on the String, PathBuf or FS APIs so not sure what a proper cross-platform fix is, but this unblocked me today.

How to delete the `dim` register?

I have registers that need to be deleted as follows:

        <register>
          <dim>2</dim>
          <dimIncrement>0x4</dimIncrement>
          <name>gpio_set[%s]</name>
          <description>Set pin output value to high</description>
          <addressOffset>0xAEC</addressOffset>
        </register>

However, I attempted to use _delete without success:

  _delete:
    gpio_set[%s]:
      dim: 35

or

  _delete:
    gpio_set:
      dim: 35

At the same time, I also tried using modify to make changes, but it didn't work either:

  _modify:
    gpio_set[%s]:
      dim: 35

or

  _modify:
    gpio_set:
      dim: 35

It seems like I cannot select this register. How can I do it?

Panic in field array dim calculation

EXINT:
  INTEN:
    _array:
      INTEN?,INTEN10:
        description: "Interrupt enable or disable on line %s"

Token boundaries are calculated without comma split.

let (li, ri) = spec_ind(fspec);

li = 5, ri = 8
thread 'main' panicked at 'byte index 18446744073709551614 is out of bounds of `INTEN0`', src\patch\register.rs:383:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

.map(|f| f.name[li..f.name.len() - ri].to_string())

patch testing

I am trying to solve #9 and I noticed that the patch command has no tests.
If you want to help me you can provide here some test cases 😀

You should provide: test description, input svd and yaml.

You don't have to provide also the patched svd (the output), since I can obtain it by launching the python script.

Example

Test description

Modify register field.

svd

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<device schemaVersion="1.1"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:noNamespaceSchemaLocation="CMSIS-SVD_Schema_1_1.xsd">
  <name>STM32L4x2</name>
  <version>1.6</version>
  <description>STM32L4x2</description>
  <!-- details about the cpu embedded in the device -->
  <cpu>
    <name>CM4</name>
    <revision>r1p0</revision>
    <endian>little</endian>
    <mpuPresent>false</mpuPresent>
    <fpuPresent>false</fpuPresent>
    <nvicPrioBits>3</nvicPrioBits>
    <vendorSystickConfig>false</vendorSystickConfig>
  </cpu>
  <!--Bus Interface Properties-->
  <!--Cortex-M3 is byte addressable-->
  <addressUnitBits>8</addressUnitBits>
  <!--the maximum data bit width accessible within a single transfer-->
  <width>32</width>
  <!--Register Default Properties-->
  <size>0x20</size>
  <resetValue>0x0</resetValue>
  <resetMask>0xFFFFFFFF</resetMask>
  <peripherals>
    <peripheral>
      <name>DAC1</name>
      <description>Digital-to-analog converter</description>
      <groupName>DAC</groupName>
      <baseAddress>0x40007400</baseAddress>
      <addressBlock>
        <offset>0x0</offset>
        <size>0x400</size>
        <usage>registers</usage>
      </addressBlock>
      <registers>
        <register>
          <name>CR</name>
          <displayName>CR</displayName>
          <description>control register</description>
          <addressOffset>0x0</addressOffset>
          <size>0x20</size>
          <access>read-write</access>
          <resetValue>0x00000000</resetValue>
          <fields>
            <field>
              <name>EN1</name>
              <description>DAC channel1 enable</description>
              <bitOffset>0</bitOffset>
              <bitWidth>1</bitWidth>
            </field>
          </fields>
        </register>
      </registers>
    </peripheral>
  </peripherals>
</device>

yaml

_svd: stm32l4x2.svd

# SVD incorrectly labels APB1ENR1 bit 18 as USART1EN instead of USART3EN.
# SVD incorrectly labels APB1ENR1 bit 26 as USBF instead of USBFSEN.
DAC1:
  CR:
    _modify:
      EN1:
        name: EN2
        description: EN2 description

Documentation

Move and improve the documentation from stm32-rs.

How to bootstrap development environment?

I pulled down the repository and tried to run make. After setting up python venv, I'm getting:

make: venv/bin/pip: Command not found

Am I missing some critical first step here? Forgive me, I've never used python venv before.

`dimArrayIndex` not supported & it's use leads to abortion of patching process

I'm using dimArrayIndex to simplify access to array elements (register arrays). The element has been introduced with SVD version 1.3.2 and is not supported by svdtools.

On the one hand, it doesn't seem to be possible to define enumeration values for registers and patch them into an SVD file.
And on the other hand, the patch process aborts (quits) as soon as dimArrayIndex is used in the SVD file which is supposed to be patched. It must be removed to successfully patch the file and inserted again afterwards.

Is there any plan to support this?

Allow adding derivedFrom attribute to enumeratedValues

As far as I can tell, there is no way to use the svd patch tool to add a derivedFrom attribute to an enumeratedValues tag. This should be possible. I would like to do this so that svd2rust uses the same enum type for the equivalent field on several different peripherals. I would expect to be able to do something like

TIMER2:
  CHCTL2:
    _derive:
      CH0P: TIMER0.CHCTL2.CH0P

Or perhaps:

TIMER2:
  CHCTL2:
    _modify:
      CH0P:
        derivedFrom: TIMER0.CHCTL2.CH0P

But neither of these seem to work at the moment.

svdtools: patching yaml files/multipart yaml conversion?

So, I know this is mostly used to patch vendor-shipped svd files, but as prior issues have stated,
I've been using this to write svd's for cortex-a socs in yaml for use in other projects (ghidra, pycortexmdebug,
etc).

Is it possible, for example, do do the patching type stuff without a 'real' 'seed' svd file?
Like, I'd want to define all the svd blocks for stuff that is unique to this particular soc (clock controllers,
system controllers, various glue peripherals) in the primary yaml file, and draw from various 'library'
yaml files that define 'standard' peripherals (arm primecell stuff, designware stuff, etc).

I realize I can just concatenate main.yaml, gpioX.yaml, and so on, but that gets awkward as time goes on
and the file grows.

For example, one could have a file tree like this:

.
├── hi3516dv300.yaml
├── hi3521av100.yaml
└── peripherals
    ├── designware
    │   └── dw-mmc.yaml
    └── primecell
        ├── pl011.yaml
        ├── pl061.yaml
        └── sp804.yaml

Where hi3516dv300.yaml defines that there is a pl061 gpio at every 0x120d_{0..b}000 address block but
hi3521av100 has them at 0x12{15..22}_0000 and so on?

modifiedWriteValues ignored in patches

#106 added support for modifiedWriteValues but this ignored in patches

  SCR:
    _modify:
      IOSB0:
        access: write-only
        modifiedWriteValues: oneToClear
            <field>
              <name>IOSB0</name>
              <description>Set bit 0</description>
              <bitOffset>0</bitOffset>
              <bitWidth>1</bitWidth>
              <access>write-only</access> << it works
            </field>

_strip_end behavior incorrect

The current behavior of _strip_end will trim the input string to the length of the substring targeted for removal.

For example:
a patch to _strip_end "_REG" from all register names, with the register name IO_MUX_GPIO36_REG will result in IO_M

The conditional to determine whether a name should be stripped on the front or back is also not behaving as intended. If the function is called to strip the end, and doesn't strip the end, it will then try to strip the front. For example, with the previous patch _strip_end "_REG", "_REG_IO_MUX" would become "_IO_MUX".

`bit_range` must be initialized when renaming bitfields

I want to rename some register blocks, and their respective bitfields on a given peripheral. Here is my yaml:

SYSTIMER:
  _modify:
      VALUE_LO:
        name: UNIT0_VALUE_LO
      VALUE_HI:
        name: UNIT0_VALUE_HI
      UPDATE:
        name: UNIT0_OP
        fields:
          TIMER_VALUE_VALID:
            name: TIMER_UNIT0_VALUE_VALID 
          TIMER_UPDATE:
            name: TIMER_UNIT0_UPDATE

The registers are correctly renamed, but when I try and rename the bit fields I get the following error:

Error: Processing device `ESP32-S2`

Caused by:
    0: According to `SYSTIMER`
    1: Processing peripheral `SYSTIMER`
    2: Modifying registers matched to `UPDATE`
    3: `Build error: `bit_range` must be initialized

Have I got the syntax wrong, or is this a bug?

svdpatch collect array requires base address as string

When running svdpatch against register with modified base address written as number I get the following error:

Traceback (most recent call last):
  File "scripts/svdpatch.py", line 883, in <module>
    main()
  File "scripts/svdpatch.py", line 876, in main
    process_device(svd, root)
  File "scripts/svdpatch.py", line 855, in process_device
    update_fields)
  File "scripts/svdpatch.py", line 400, in process_peripheral
    p.collect_in_array(rspec, rmod)
  File "scripts/svdpatch.py", line 511, in collect_in_array
    int(rtag.findtext('addressOffset'), 0)])
TypeError: int() can't convert non-string with explicit base
Makefile:38: recipe for target 'svd/stm32f427.svd.patched' failed
make: *** [svd/stm32f427.svd.patched] Error 1

Input used to generate this error:

OTG_HS_GLOBAL:
  _modify:
    DIEPTXF3:
      addressOffset: 0x10C
  
  _array:
    DIEPTXF[12345]: {}

If I put the address in quotes like this: addressOffset: 0x10C everything works fine.

conversion error: unsure if I'm just missing something

Ello. I'm using the rust svdtools program to build an svd for a chip without one.
I started by converting the rp2040.svd to rp2040.yaml as a reference, and began
writing the yaml for my chip, to convert to svd later for use with gdb and such.
Some of the data is dummied out (for instance, its not a traditional mcu, but is
a cortex-a7 proc). Attempting to convert with what I currently have (below) results
in the following error:
[2022-04-11T13:49:24Z ERROR svdtools::cli] peripherals[0]: invalid type: map, expected a sequence at line 20 column 9
Line 20 column 9 translates to the : before BOOT_ADDR; this seems correct,
as the rp2040.yaml file has the same kind of setup:

Am I missing something or is this a legit bug?

---
vendor: Raspberry Pi
name: RP2040
version: "0.1"
description: ""
licenseText: "\n    Copyright (c) 2020 Raspberry Pi (Trading) Ltd. \\n\n    \\n\n    SPDX-License-Identifier: BSD-3-Clause\n  "
cpu:
  name: CM0PLUS
  revision: r0p1
  endian: little
  mpuPresent: true
  fpuPresent: false
  vtorPresent: true
  nvicPrioBits: 2
  vendorSystickConfig: false
  deviceNumInterrupts: 26
addressUnitBits: 8
width: 32
peripherals:
  - name: XIP_CTRL
---
vendor: Hisilicon
name: Hi3521A
version:  "0.0"
description: ""
licenseText: ""
cpu:
  name: other
  revision: r0p1
  endian: little
  mpuPresent: true
  fpuPresent: true
  vtorPresent: true
  nvicPrioBits: 0
  vendorSystickConfig: false
  deviceNumInterrupts: 64
addressUnitBits: 8
width: 32
peripherals:
  - name: BOOT_ADDR
    version: "1"
    description: ""
    baseAddress: 0x0
    size: 32
    addressBlock:
      offset: 0
      size: 0x4000000
      usage: buffer
  - name: BOOT_ROM
    version: "1"
    description: ""
    baseAddress: 0x04000000
    size: 32
    addressBlock:
      offset: 0
      size: 0x1000
      usage: buffer
  - name: SRAM
    version: "1"
    description: ""
    baseAddress: 0x04010000
    size: 32
    addressBlock:
      offset: 0
      size: 0x4000
      usage: buffer
  - name: FMC_CTRL
    version: "1"
    description: ""
    baseAddress: 0x10000000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: OHCI
    version: "1"
    description: ""
    baseAddress: 0x10030000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: EHCI
    version: "1"
    description: ""
    baseAddress: 0x10040000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: DMAC
    version: "1"
    description: ""
    baseAddress: 0x10060000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: CIPHER
    version: "1"
    description: ""
    baseAddress: 0x10070000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: SCD
    version: "1"
    description: ""
    baseAddress: 0x10080000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: GSF
    version: "1"
    description: ""
    baseAddress: 0x100a0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: SATA
    version: "1"
    description: ""
    baseAddress: 0x11010000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: TIMER01
    version: "1"
    description: ""
    baseAddress: 0x12000000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: TIMER23
    version: "1"
    description: ""
    baseAddress: 0x12010000
    size: 32
  - name: TIMER45
    version: "1"
    description: ""
    baseAddress: 0x12020000
    size: 32
  - name: TIMER67
    version: "1"
    description: ""
    baseAddress: 0x12030000
    size: 32
  - name: CRG
    version: "1"
    description: ""
    baseAddress: 0x12040000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
    registers:
      - register:
        name: CRG_PLL0
        description: APLL configuration register 0
        addressOffset: 0x0000
        resetValue: 0x12000000
        fields:
          - name: postdiv2
            description: Level-2 output divider of the APLL
            bitRange: "[30:28]"
            access: read-write
          - name: postdiv1
            description: Level-1 output divider of the APLL
            bitRange: "[26:24]"
            access: read-write
          - name: frac
            description: Decimal divider of the APLL
            bitRange: "[23:0]"
            access: read-write
        name: CRG_PLL1
        description: APLL configuration register 0
        addressOffset: 0x0004
        resetValue: 0x02003064
        fields:
          - name: bypass
            description: APLL clock frequency-division bypass control
            bitRange: "[26:26]"
            access: read-write
          - name: dacpd
            description: APLL test signal control
            bitRange: "[25:25]"
            access: read-write
          - name: dsmpd
            description: APLL decimal frequency-division control
            bitRange: "[24:24]"
            access: read-write
          - name: pd
            description: PLL power-down control
            bitRange: "[23:23]"
            access: read-write
          - name: foutvcopd
            description: Power-down control for the APLL VCO output
            bitRange: "[22:22]"
            access: read-write
          - name: postdivpd
            description: Power-down control for the APLL POSTDIV output
            bitRange: "[21:21]"
            access: read-write
          - name: fout4phasepd
            description: Power-down control for the APLL FOUT output
            bitRange: "[20:20]"
            access: read-write
          - name: refdiv
            description: Divider of the APLL reference clock
            bitRange: "[17:12]"
            access: read-write
          - name: fbdiv
            description: Integral multiplier of the APLL
            bitRange: "[11:0]"
            access: read-write
  - name: SYSCTL
    version: "1"
    description: ""
    baseAddress: 0x12050000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: WDG
    version: "1"
    description: ""
    baseAddress: 0x12070000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: UART0
    version: "1"
    description: ""
    baseAddress: 0x12080000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: UART1
    version: "1"
    description: ""
    baseAddress: 0x12090000
    size: 32
  - name: UART2
    version: "1"
    description: ""
    baseAddress: 0x120a0000
    size: 32
  - name: RTC
    version: "1"
    description: ""
    baseAddress: 0x120b0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: I2C
    version: "1"
    description: ""
    baseAddress: 0x120c0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: SSP
    version: "1"
    description: ""
    baseAddress: 0x120d0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: PMC
    version: "1"
    description: ""
    baseAddress: 0x120e0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: PINMUX
    version: "1"
    description: ""
    baseAddress: 0x120f0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: PINCTRL
    version: "1"
    description: ""
    baseAddress: 0x120f0800
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: DDRT0
    version: "1"
    description: ""
    baseAddress: 0x12100000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: DDRC
    version: "1"
    description: ""
    baseAddress: 0x12110000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: PERICTL
    version: "1"
    description: ""
    baseAddress: 0x12120000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: IR
    version: "1"
    description: ""
    baseAddress: 0x12140000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: GPIO00
    version: "1"
    description: ""
    baseAddress: 0x12150000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: GPIO01
    version: "1"
    description: ""
    baseAddress: 0x12160000
    size: 32
  - name: GPIO02
    version: "1"
    description: ""
    baseAddress: 0x12170000
    size: 32
  - name: GPIO03
    version: "1"
    description: ""
    baseAddress: 0x12180000
    size: 32
  - name: GPIO04
    version: "1"
    description: ""
    baseAddress: 0x12190000
    size: 32
  - name: GPIO05
    version: "1"
    description: ""
    baseAddress: 0x121a0000
    size: 32
  - name: GPIO06
    version: "1"
    description: ""
    baseAddress: 0x121b0000
    size: 32
  - name: GPIO07
    version: "1"
    description: ""
    baseAddress: 0x121c0000
    size: 32
  - name: GPIO08
    version: "1"
    description: ""
    baseAddress: 0x121d0000
    size: 32
  - name: GPIO09
    version: "1"
    description: ""
    baseAddress: 0x121e0000
    size: 32
  - name: GPIO10
    version: "1"
    description: ""
    baseAddress: 0x121f0000
    size: 32
  - name: GPIO11
    version: "1"
    description: ""
    baseAddress: 0x12200000
  - name: GPIO12
    version: "1"
    description: ""
    baseAddress: 0x12210000
  - name: GPIO13
    version: "1"
    description: ""
    baseAddress: 0x12220000
  - name: HDMI
    version: "1"
    description: ""
    baseAddress: 0x13010000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: VDP
    version: "1"
    description: ""
    baseAddress: 0x13020000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: AVC0
    version: "1"
    description: ""
    baseAddress: 0x13040000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: TDE
    version: "1"
    description: ""
    baseAddress: 0x13050000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: IVE
    version: "1"
    description: ""
    baseAddress: 0x13060000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: JPGD
    version: "1"
    description: ""
    baseAddress: 0x13070000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: VGS
    version: "1"
    description: ""
    baseAddress: 0x13080000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: VICAP
    version: "1"
    description: ""
    baseAddress: 0x130c0000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: AVC1
    version: "1"
    description: ""
    baseAddress: 0x13100000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: VPSS
    version: "1"
    description: ""
    baseAddress: 0x13110000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: VOIE
    version: "1"
    description: ""
    baseAddress: 0x13120000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: JPGE
    version: "1"
    description: ""
    baseAddress: 0x13130000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: AIAO
    version: "1"
    description: ""
    baseAddress: 0x13140000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: MDU
    version: "1"
    description: ""
    baseAddress: 0x13150000
    size: 32
    addressBlock:
      offset: 0
      size: 0x10000
      usage: registers
  - name: SFC_MEM
    version: "1"
    description: ""
    baseAddress: 0x14000000
    size: 32
    addressBlock:
      offset: 0
      size: 0x1000000
      usage: buffer
  - name: DRAM
    version: "1"
    description: ""
    baseAddress: 0x80000000
    size: 32
    addressBlock:
      offset: 0
      size: 0x80000000
      usage: buffer

Working within clusters

TL;DR: most svdtools features don't work within the <cluster> tag. Should they?

Background

Somewhat similar to #27, our platform SVDs have registers organized into clusters like so:

<peripheral>
  <name>SUBSYSTEM</name>
  <cluster>
    <name>PERIPHERAL_1</name>
    <description />
    <register>REG_1</register>
    <register>REG_2.</register>
  </cluster>
  <cluster>
    <name>PERIPHERAL_2name>
    <description />
    <register>...</register>
    <register>...</register>
  </cluster>
</peripheral>

Problem

I have no idea whether it's a good idea to arrange the device like so but in case one does, we encounter issues with svd patch which seems to be unable to make most types of changes inside clusters.

Take for instance _array. If you were to write the following patch.yaml against the above architecture:

"SUBSYSTEM":
  _array:
    REG_*: {}

you would get the following error:

File "src/lxml/etree.pyx", line 943, in lxml.etree._Element.remove
ValueError: Element is not a child of this node.

while if you didn't have the cluster layer, the operation would pass successfully. I suspect the svd patch code is looking for the matching "REG_*" symbols as leaf of the <peripheral>SUBSYSTEM</peripheral>-layer, while they cannot be found due to the symbols actually being wrapped one layer deeper inside a <cluster>-tag. However, we cannot currently represent the cluster-layer in the YAML syntax. #27 seems to work around this through clever re-use of _cluster tag.

Follow up

Did I get the problem statement right? How do you feel about the issue? Do you think it's a user problem i.e. too weird SVD layout, or would it be appropriate for svd patch to support changes inside clusters.

If you think this is in the "should fix" category, I could investigate if I could come up with a PR for it. In that case I'd need at least an opinion on if the cluster-layer could be represented in the YAML patch syntax similar to the peripheral, register and field layers, like so:

"PERIPHERAL":
  CLUSTER:
    REGISTER:
      _modify:
        FIELD:
          description: "a description"

Testing

The stm32-rs crates are depended on by many. Perhaps we should store their outputs with git lfs or similar, and run regression tests against current last release (or HEAD?) of stm32-rs?

Cannot copy from a peripheral that is also deleted in the same patch

If I try to first _copy (or _rebase) a peripheral and then delete the original it does not work. This is the simplest reproduction of this issue:

_svd: svd/stm32g070.svd

_copy:
  TIM3:
    from: TIM2

_delete:
  - TIM2

It crashes with the error

svdtools.patch.SvdPatchError: peripheral None not found

How is the order of operations defined? It seems like _delete is done before _copy. If I remove the _delete, the _copy works fine. Is there a workaround?

Having an enumerated value `Off` throws an error

I am writing a peripheral for STM32WLE. One of the bits is best described as "Off", so I wrote:

    RRS:
      Off: [0, "SRAM2 powered off in Standby mode (SRAM2 content lost)"]
      OnLPR: [1, "SRAM2 powered by the low-power regulator in Standby mode (SRAM2 content kept)"]

When running make, I get the following output:

❯ make
svd makedeps devices/stm32wle5.yaml .deps/stm32wle5.d
svd patch devices/stm32wle5.yaml
Traceback (most recent call last):
  File "/Users/xxxx/.pyenv/versions/stm32-rs/bin/svd", line 8, in <module>
    sys.exit(svdtools_cli())
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/click/core.py", line 1137, in __call__
    return self.main(*args, **kwargs)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/click/core.py", line 1062, in main
    rv = self.invoke(ctx)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/click/core.py", line 1668, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/click/core.py", line 763, in invoke
    return __callback(*args, **kwargs)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/cli.py", line 16, in patch
    svdtools.patch.main(yaml_file)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 1407, in main
    process_device(svd, root)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 1384, in process_device
    d.process_peripheral(periphspec, device[periphspec], update_fields)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 661, in process_peripheral
    p.process_register(rspec, register, update_fields)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 1032, in process_register
    r.process_field(pname, fspec, field)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 1234, in process_field
    self.process_field_enum(pname, fspec, field)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 1260, in process_field_enum
    enum = make_enumerated_values(name, field, usage=usage)
  File "/Users/xxxx/.pyenv/versions/3.8.9/envs/stm32-rs/lib/python3.8/site-packages/svdtools/patch.py", line 148, in make_enumerated_values
    if vname.startswith("_"):
AttributeError: 'bool' object has no attribute 'startswith'
make: *** [svd/stm32wle5.svd.patched] Error 1

I am using Python 3.8.9, compiled on x86_64 (I have an M1 so I tried both x86_64 and aarch64).

If instead, I change the bit to:

    RRS:
      PowerOff: [0, "SRAM2 powered off in Standby mode (SRAM2 content lost)"]
      OnLPR: [1, "SRAM2 powered by the low-power regulator in Standby mode (SRAM2 content kept)"]

Everything works normally.

I have tried in other peripherals and bits and the same issue occurs. The same is true if the value is On, e.g.:

    RRS:
      PowerOff: [0, "SRAM2 powered off in Standby mode (SRAM2 content lost)"]
      On: [1, "SRAM2 powered by the low-power regulator in Standby mode (SRAM2 content kept)"]

RIIR

There have been votes to rewrite this in Rust, I think most everyone would be open to this, but it would need an external initiative.

Making a cluster array of register arrays

Context

Let’s say we have some peripheral that exposes an interface to feed it with some 128-bit inputs (let’s say 3 for the example), and we have a 32-bit bus. It then provides registers like: INPUT0A, INPUT0B, INPUT0C, INPUT0D, INPUT1A, ..., INPUT3D. INPUT0A is the first 32-bit word in input 0, INPUT3B the second 32-bit word in input 3, and so on.

As you can imagine, working with such registers in a generic way in the code is not very handy, so I’d like to group them in some kind of two-dimentional array, like INPUT[n][w], where n is the input number and w the 32-bit word index inside this 128-bit input.

Trials with svdtools

The first step I’ve done is to create arrays for each input, like:

PERIPHERAL:
    _array:
        INPUT0?: {}
        INPUT1?: {}
        INPUT2?: {}

This is better since I can now do in my Rust code:

peripheral.input0[w].write(|w| ...);
// ...
peripheral.input1[w].write(|w| ...);

There is still however a hard-coded index in the names, so I need to use macros to get generic code, which is not optimal.

Trials with the SVD file

While looking to the SVD documentation, I came to see that we can put registers in clusters, and we can do arrays of clusters. I can then write something like:

<cluster>
  <dim>3</dim>
  <dimIncrement>0x10</dimIncrement>
  <dimIndex>0-2</dimIndex>

  <name>INPUT%s</name>
  <description>Input registers</description>
  <addressOffset>0x100</addressOffset>

  <register>
    <dim>4</dim>
    <dimIncrement>0x04</dimIncrement>
    <dimIndex>0-3</dimIndex>

    <name>WORD%s</name>
    <description>Word</description>
    <addressOffset>0x0</addressOffset>
    <size>32</size>
    <access>read-write</access>
    <resetValue>0x0</resetValue>
    <resetMask>0xFFFFFFFF</resetMask>

    <fields>
      <field>
        <name>WORD</name>
        <description>Word</description>
        <bitOffset>0</bitOffset>
        <bitWidth>32</bitWidth>
        <access>read-write</access>
      </field>
    </fields>
  </register>
</cluster>

This allows me to write Rust code like:

peripheral.input[n].word[w].write(|w| ...);

which is exactly what I want.

How to do this with svdtools?

Is it possible to achieve this with svdtools? I see two possible paths:

  1. Is there a way to create clusters from register arrays, and then an array from clusters? I’ve tried to do so but I’m getting some errors.
  2. Is there a way to _add clusters and arrays? (Actually the original SVD I’m using has the registers for only 1 input, so I’d be fine to just define them from scratch in a _add block)

Create new arrays

The _array KEY is useful for aggregating existing registers but I'd like to request functionality to create new arrays without first _adding all the individual registers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.