Skip to content

LPC82x: Wrong offsets in GPIO_PORT peripheral #137

@hannobraun

Description

@hannobraun

I'm currently working on a Rust project using an LPC82x microcontroller by NXP. I've generated register mappings with svd2rust from this file: LPC82x.svd

Please note that this file won't work as-is. I've had to make some minor modifications to get the resulting Rust code to compile. I plan to release all of this soon, but haven't so far. Sorry it's not available right now, but I hope the full context won't be relevant for this issue.

The results have been mostly good so far, but the register offsets for the GPIO_PORT peripheral are wrong. Right at the start of GPIO_PORT's address space are 28 byte-sized registers, one for each I/O pin. Here's how this is specified in the SVD file:

<register>                                              
    <dim>29</dim>                                           
    <dimIncrement>0x1</dimIncrement>                                            
    <dimIndex>0-28</dimIndex>   
    <name>B%s</name>                                            
    <description>Byte pin registers port 0; pins PIO0_0 to PIO0_28</description>

    <addressOffset>0x0000</addressOffset>
    <size>1</size>
    <access>read-write</access>                                         
    <resetValue>0</resetValue>                                          
    <resetMask>0x00000000</resetMask>   
    
    <fields>                                            
        <field>                                     
            <name>PBYTE</name>                                  
            <description>Read: state of the pin PIO0_n, regardless of direction, masking, or alternate function, except that pins configured as analog I/O always read as 0. One register for each port pin: n = pin 0 to 28. Write: loads the pin's output bit.</description>                                  
            <bitRange>[0:0]</bitRange>                                  
        </field>                                        
    </fields>                                           
</register>

Here's the code that svd2rust generates from this:

#[doc = "0x00 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b0: B,
_reserved0: [u8; 1usize],
#[doc = "0x01 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b1: B,
_reserved1: [u8; 1usize],
#[doc = "0x02 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b2: B,
_reserved2: [u8; 1usize],
#[doc = "0x03 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b3: B,
_reserved3: [u8; 1usize],
#[doc = "0x04 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b4: B,
_reserved4: [u8; 1usize],
#[doc = "0x05 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b5: B,
_reserved5: [u8; 1usize],
#[doc = "0x06 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b6: B,
_reserved6: [u8; 1usize],
#[doc = "0x07 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b7: B,
_reserved7: [u8; 1usize],
#[doc = "0x08 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b8: B,
_reserved8: [u8; 1usize],
#[doc = "0x09 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b9: B,
_reserved9: [u8; 1usize],
#[doc = "0x0a - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b10: B,
_reserved10: [u8; 1usize],
#[doc = "0x0b - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b11: B,
_reserved11: [u8; 1usize],
#[doc = "0x0c - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b12: B,
_reserved12: [u8; 1usize],
#[doc = "0x0d - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b13: B,
_reserved13: [u8; 1usize],
#[doc = "0x0e - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b14: B,
_reserved14: [u8; 1usize],
#[doc = "0x0f - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b15: B,
_reserved15: [u8; 1usize],
#[doc = "0x10 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b16: B,
_reserved16: [u8; 1usize],
#[doc = "0x11 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b17: B,
_reserved17: [u8; 1usize],
#[doc = "0x12 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b18: B,
_reserved18: [u8; 1usize],
#[doc = "0x13 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b19: B,
_reserved19: [u8; 1usize],
#[doc = "0x14 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b20: B,
_reserved20: [u8; 1usize],
#[doc = "0x15 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b21: B,
_reserved21: [u8; 1usize],
#[doc = "0x16 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b22: B,
_reserved22: [u8; 1usize],
#[doc = "0x17 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b23: B,
_reserved23: [u8; 1usize],
#[doc = "0x18 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b24: B,
_reserved24: [u8; 1usize],
#[doc = "0x19 - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b25: B,
_reserved25: [u8; 1usize],
#[doc = "0x1a - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b26: B,
_reserved26: [u8; 1usize],
#[doc = "0x1b - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b27: B,
_reserved27: [u8; 1usize],
#[doc = "0x1c - Byte pin registers port 0; pins PIO0_0 to PIO0_28"]
pub b28: B,
_reserved28: [u8; 4068usize],
#[doc = "0x1000 - Word pin registers port 0"]
pub w0: W,

Here's the definition of B:

pub struct B {
    register: VolatileCell<u8>,
}

As you can see, svd2rust inserts one additional byte of reserved space per register. As B is already one byte wide, this adds one byte of additional offset per register. There's also an additional byte in the offset between b28 and w0 (second-to-last and last fields in the code block above).

I don't know SVD well enough to rule out a mistake there, but it seems obvious that something is wrong with svd2rust here. As you can see, the offsets it inserts in the documentation are the correct ones, they don't account for the additional byte of reserved space.

I think this problem is specific to registers that are 1 byte wide. The next block of registers looks pretty much the same, except that the registers are 4 bytes wide:

<register>                                              
    <dim>29</dim>                                           
    <dimIncrement>0x4</dimIncrement>                                            
    <dimIndex>0-28</dimIndex>                                           
    <name>W%s</name>                                            
    <description>Word pin registers port 0</description>                                            
    <addressOffset>0x1000</addressOffset>                                           
    <access>read-write</access>                                         
    <resetValue>0</resetValue>                                          
    <resetMask>0x00000000</resetMask>                                           
    <fields>                                            
        <field>                                     
            <name>PWORD</name>                                  
            <description>Read 0: pin PIOm_n is LOW.  Write 0: clear output bit. Read 0xFFFF FFFF: pin PIOm_n is HIGH.  Write any value 0x0000 0001 to 0xFFFF FFFF: set output bit. Only 0 or 0xFFFF FFFF can be read. Writing any value other than 0 will set the output bit. One register for each port pin: n = pin 0 to 28.</description>                                    
            <bitRange>[31:0]</bitRange>                                 
        </field>                                        
    </fields>                                           
</register>

The code that svd2rust generates for those looks correct:

#[doc = "0x1000 - Word pin registers port 0"]
pub w0: W,
#[doc = "0x1004 - Word pin registers port 0"]
pub w1: W,
#[doc = "0x1008 - Word pin registers port 0"]
pub w2: W,
#[doc = "0x100c - Word pin registers port 0"]
pub w3: W,
#[doc = "0x1010 - Word pin registers port 0"]
pub w4: W,
#[doc = "0x1014 - Word pin registers port 0"]
pub w5: W,
#[doc = "0x1018 - Word pin registers port 0"]
pub w6: W,
#[doc = "0x101c - Word pin registers port 0"]
pub w7: W,
#[doc = "0x1020 - Word pin registers port 0"]
pub w8: W,
#[doc = "0x1024 - Word pin registers port 0"]
pub w9: W,
#[doc = "0x1028 - Word pin registers port 0"]
pub w10: W,
#[doc = "0x102c - Word pin registers port 0"]
pub w11: W,
#[doc = "0x1030 - Word pin registers port 0"]
pub w12: W,
#[doc = "0x1034 - Word pin registers port 0"]
pub w13: W,
#[doc = "0x1038 - Word pin registers port 0"]
pub w14: W,
#[doc = "0x103c - Word pin registers port 0"]
pub w15: W,
#[doc = "0x1040 - Word pin registers port 0"]
pub w16: W,
#[doc = "0x1044 - Word pin registers port 0"]
pub w17: W,
#[doc = "0x1048 - Word pin registers port 0"]
pub w18: W,
#[doc = "0x104c - Word pin registers port 0"]
pub w19: W,
#[doc = "0x1050 - Word pin registers port 0"]
pub w20: W,
#[doc = "0x1054 - Word pin registers port 0"]
pub w21: W,
#[doc = "0x1058 - Word pin registers port 0"]
pub w22: W,
#[doc = "0x105c - Word pin registers port 0"]
pub w23: W,
#[doc = "0x1060 - Word pin registers port 0"]
pub w24: W,
#[doc = "0x1064 - Word pin registers port 0"]
pub w25: W,
#[doc = "0x1068 - Word pin registers port 0"]
pub w26: W,
#[doc = "0x106c - Word pin registers port 0"]
pub w27: W,
#[doc = "0x1070 - Word pin registers port 0"]
pub w28: W,

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