Skip to content

Odd (and incorrect) code generation from SVD #966

Open

Description

@MsFormula

I was in the process of updating a project that I am working on to new SVD's and when I generated the PAC I got some strange code. This first block is and excerpt from the relevant part of the SVD. The full thing can be found here:

<field>
    <name>CTS_ASSERT</name>
    <description>CTS assert level</description>
    <bitOffset>7</bitOffset>
    <bitWidth>1</bitWidth>
    <enumeratedValues>
        <name>ASSERT_LEVEL</name>
        <enumeratedValue>
            <name>HIGH</name>
            <description>Assert high</description>
            <value>0</value>
        </enumeratedValue>
        <enumeratedValue>
            <name>LOW</name>
            <description>Assert low</description>
            <value>1</value>
        </enumeratedValue>
    </enumeratedValues>
</field>
<field>
    <name>RTS_ASSERT</name>
    <description>RTS assert level</description>
    <bitOffset>6</bitOffset>
    <bitWidth>1</bitWidth>
    <enumeratedValues derivedFrom="UART1.CNTL.CTS_ASSERT.ASSERT_LEVEL"></enumeratedValues>
</field>

Generated code is as follows:

#[doc = "RTS assert level"]
pub use AssertLevel;
#[doc = "Field `RTS_ASSERT` reader - RTS assert level"]
pub use CtsAssertR as RtsAssertR;
#[doc = "Field `RTS_ASSERT` writer - RTS assert level"]
pub use CtsAssertW as RtsAssertW;
#[doc = "CTS assert level\n\nValue on reset: 0"]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AssertLevel {
    #[doc = "0: Assert high"]
    High = 0,
    #[doc = "1: Assert low"]
    Low = 1,
}
impl From<AssertLevel> for bool {
    #[inline(always)]
    fn from(variant: AssertLevel) -> Self {
        variant as u8 != 0
    }
}
#[doc = "Field `CTS_ASSERT` reader - CTS assert level"]
pub type CtsAssertR = crate::BitReader<AssertLevel>;
impl CtsAssertR {
    #[doc = "Get enumerated values variant"]
    #[inline(always)]
    pub const fn variant(&self) -> AssertLevel {
        match self.bits {
            false => AssertLevel::High,
            true => AssertLevel::Low,
        }
    }
    #[doc = "Assert high"]
    #[inline(always)]
    pub fn is_high(&self) -> bool {
        *self == AssertLevel::High
    }
    #[doc = "Assert low"]
    #[inline(always)]
    pub fn is_low(&self) -> bool {
        *self == AssertLevel::Low
    }
}
#[doc = "Field `CTS_ASSERT` writer - CTS assert level"]
pub type CtsAssertW<'a, REG> = crate::BitWriter<'a, REG, AssertLevel>;
impl<'a, REG> CtsAssertW<'a, REG>
where
    REG: crate::Writable + crate::RegisterSpec,
{
    #[doc = "Assert high"]
    #[inline(always)]
    pub fn high(self) -> &'a mut crate::W<REG> {
        self.variant(AssertLevel::High)
    }
    #[doc = "Assert low"]
    #[inline(always)]
    pub fn low(self) -> &'a mut crate::W<REG> {
        self.variant(AssertLevel::Low)
    }
}

The error here is that for some reason a pub use AssertLevel; was generated just before pub enum AssertLevel {...}. I am using svd2rust version 0.37.1 and rust version 1.92.0. I am running Ubuntu 24.04.

As to what the actual issue here is I'm not sure. However it is unlikely to be the SVD because that specific file has been the same for years.

Let me know if anymore information is needed.

Activity

  1. burrbull commented on Aug 23, 2026

    @burrbull
    Member

    You are right. It looks like a bug and should be easy to fix.

    But I understand why it was not found earlier. Usually we use relative derive path instead of absolute in such cases.

  2. burrbull commented on Aug 23, 2026

    @burrbull
    Member

    Oh. No. It looks like there is other issue.

    Due to this sort:

    fields.sort_by_key(|f| f.bit_offset());

    RTS_ASSERT is being processed before CTS_ASSERT
    This leads to this hashset
    let derives;

    is empty when pub use AssertLevel; is generated.

  3. burrbull commented on Aug 23, 2026

    @burrbull
    Member

    Fast fix from SVD side is to derive CTS_ASSERT from RTS_ASSERT instead of RTS_ASSERT from CTS_ASSERT.

    The fix from svd2rust side is to move filling of derives hashset out of main cycle.

  4. burrbull commented on Aug 23, 2026

    @burrbull
    Member

    I was wrong. Only deleting sort by offset could fix your issue from svd2rust side. But I afraid it can lead to more bugs.

  5. MsFormula commented on Aug 26, 2026

    @MsFormula
    Author

    Sorry I've been away without internet for the past few days. Using the branch and using a the reverse flag works perfectly! Thank you so much!

  6. MsFormula commented on Sep 1, 2026

    @MsFormula
    Author

    @burrbull
    Ok I'm getting the same issue now but in another part of the code:

    #[doc = "Resistor select for 15"]
    pub use BpPull;
    ...
    #[doc = "Resistor select for 0"]
    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    #[repr(u8)]
    pub enum BpPull {
        #[doc = "0: No pull"]
        None = 0,
        #[doc = "1: Pull up"]
        Up = 1,
        #[doc = "2: Pull down"]
        Down = 2,
    }
    

    SVD:

    <register>
        <name>GPIO_PUP_PDN_CNTRL_REG0</name>
        <description>GPIO Pull-up / Pull-down Register 0</description>
        <addressOffset>0xe4</addressOffset>
        <access>read-write</access>
        <fields>
            <field>
                <name>GPIO_PUP_PDN_CNTRL0</name>
                <description>Resistor select for 0</description>
                <bitOffset>0</bitOffset>
                <bitWidth>2</bitWidth>
                <enumeratedValues>
                    <name>BP_PULL</name>
                    <usage>read-write</usage>
                    <headerEnumName>BP_PULL</headerEnumName>
                    <enumeratedValue>
                        <name>NONE</name>
                        <description>No pull</description>
                        <value>0</value>
                    </enumeratedValue>
                    <enumeratedValue>
                        <name>UP</name>
                        <description>Pull up</description>
                        <value>1</value>
                    </enumeratedValue>
                    <enumeratedValue>
                        <name>DOWN</name>
                        <description>Pull down</description>
                        <value>2</value>
                    </enumeratedValue>
                </enumeratedValues>
            </field>
            {% for i in range(1, 16) %}
            <field>
                <name>GPIO_PUP_PDN_CNTRL{{ i }}</name>
                <description>Resistor select for {{ i }}</description>
                <bitOffset>{{ 2 * i }}</bitOffset>
                <bitWidth>2</bitWidth>
                <enumeratedValues derivedFrom="GPIO.GPIO_PUP_PDN_CNTRL_REG0.GPIO_PUP_PDN_CNTRL0.BP_PULL"/>
            </field>
            {% endfor %}
        </fields>
    </register>
    
  7. MsFormula commented on Sep 6, 2026

    @MsFormula
    Author

    I've continued testing and in 0.36.0 I get the same error. Not sure about anything lower.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions