Skip to content

Peripheral Access Conventions

bunnie edited this page Sep 26, 2020 · 12 revisions

Rationale/Requirements

  • Make it clear what's going on
  • Make it easy to inspect constants for correctness
  • Base off of "Volatile" crate syntax
  • Rely on Rust optimizer to turn otherwise complicated ops into the desired shift-and-masks
  • Split into "base/offset", so the crate can be used for e.g. virtual memory mapped peripherals

Worked Exmaples

A modestly simple register example

Simple Register Example

soc.svd gives us bit fields, but not memory regions or interrupts:

        <peripheral>
            <name>AUDIO</name>
            <baseAddress>0xF0016000</baseAddress>
            <groupName>AUDIO</groupName>
            <registers>
                <register>
                    <name>RX_CTL</name>
                    <description><![CDATA[Rx data path control]]></description>
                    <addressOffset>0x000c</addressOffset>
                    <resetValue>0x00</resetValue>
                    <size>32</size>
                    <access>read-write</access>
                    <fields>
                        <field>
                            <name>enable</name>
                            <msb>0</msb>
                            <bitRange>[0:0]</bitRange>
                            <lsb>0</lsb>
                            <description><![CDATA[Enable the receiving data]]></description>
                        </field>
                        <field>
                            <name>reset</name>
                            <msb>1</msb>
                            <bitRange>[1:1]</bitRange>
                            <lsb>1</lsb>
                            <description><![CDATA[Writing `1` resets the FIFO. Reset happens regardless of enable state.]]></description>
                        </field>
                    </fields>
                </register>
            </registers>
         </peripheral>

csr.csv gives us only names of whole registers and bases, but also gives us memory regions and interrupts:

csr_base,audio,0xf0016000,,
csr_register,audio_rx_ctl,0xf001600c,1,rw
constant,audio_interrupt,6,,
memory_region,audio,0xe0000000,4,io

proposed Rust-syntax accessors to be generated from a combination of the above two files:

    // this goes in betrusted_pac.rs
    const HW_AUDIO_BASE: u32 = 0xf001_6000;
    const HW_AUDIO_MEM: u32 = 0xe000_0000;
    const HW_AUDIO_LEN: u32 = 0x1000; // minimum 1x 4k-page
    const HW_AUDIO_RX_CTL: u32 = 0xc;
    const HW_AUDIO_RX_CTL_ENABLE: [u32; 2] = [0b01; 0];
    const HW_AUDIO_RX_CTL_RESET: [u32; 2]  = [0b10; 1];
    
    // set up the base pointers (note "unsafe" blocks omitted for clarity)
    let audio_base = (HW_AUDIO_BASE as *mut u32) as *mut Volatile<u32>;
    let audio_mem = (HW_AUDIO_MEM as *mut u32) as *mut Volatile<u32>;

    // read RX_CTL
    let rx_ctl_value = (*audio_base.add(HW_AUDIO_RX_CTL)).r();
    let enable: bool = (*audio_base.add(HW_AUDIO_RX_CTL)).rm(HW_AUDIO_RX_CTL_ENABLE) != 0; // read based on mask
    let reset: bool = (*audio_base.add(HW_AUDIO_RX_CTL)).rm(HW_AUDIO_RX_CTL_RESET) != 0;

    (*audio_base.add(HW_AUDIO_RX_CTL)).ow(HW_AUDIO_RX_CTL_RESET); // sets the RESET bit, and also implicitly clears ENABLE
    (*audio_base.add(HW_AUDIO_RX_CTL)).ow(0, HW_AUDIO_RX_CTL_RESET_MASK); // dynamic dispatch is possible in Rust, yes?

    // these two proposals manipulates two bits
    (*audio_base.add(HW_AUDIO_RX_CTL))
       .rw(0, HW_AUDIO_RX_CTL_RESET)
       .rw(1, HW_AUDIO_RX_CTL_ENABLE);

    // actually same as above. The "False" can be dropped with no harm.
    (*audio_base.add(HW_AUDIO_RX_CTL))
       .rw(1, HW_AUDIO_RX_CTL_ENABLE);

    // write and read to the audio memory window
    (*audio_mem).write( audio_sample );
    let mic_data: u16 = (*audio_mem).read() as u16;

    // map to virtual memory
    let audio_base_vmem = xous::syscall::map_memory(
        xous::MemoryAddress::new(HW_AUDIO_BASE),
        None,
        HW_AUDIO_LEN,
        xous::MemoryFlags::R | xous::MemoryFlags::W,
    )
    .expect("couldn't map audio port");

     // example of using vmem to access using the same constants/crate
     let abase = (audio_base_vmem.as_ptr() as *mut u32) as *mut Voltaile<u32>;
     (*abase.add(HW_AUDIO_RX_CTL)).rw(1, HW_AUDIO_RX_CTL_ENABLE);
 
     // not addressed: interrupts??

A really simple register example

Simple Register Example
        <peripheral>
            <name>UART</name>
            <baseAddress>0xF0004000</baseAddress>
            <groupName>UART</groupName>
            <registers>
                <register>
                    <name>RXTX</name>
                    <addressOffset>0x0000</addressOffset>
                    <resetValue>0x00</resetValue>
                    <size>32</size>
                    <access>read-write</access>
                    <fields>
                        <field>
                            <name>rxtx</name>
                            <msb>7</msb>
                            <bitRange>[7:0]</bitRange>
                            <lsb>0</lsb>
                        </field>
                    </fields>
                </register>
             </registers>
         </peripheral>
csr_base,uart,0xf0004000,,
csr_register,uart_rxtx,0xf0004000,1,rw
    const HW_UART_BASE: u32 = 0xf004_0000;
    const HW_UART_RXTX: u32 = 0x0;
    const HW_UART_RXTX_RXTX: [u32; 2] = [0b1111_1111, 0];
    
    // example of the simplest type of register accessor
    let uart_base = (HW_UART_BASE as *mut u32) as *mut Volatile<u32>;

    let rx_char: u8 = (*uart_base.add(HW_UART_RXTX)).r() as u8;

    // these are identical in this case because the register is simple
    (*uart_base.add(HW_UART_RXTX)).ow(tx_char, HW_UART_RXTX_RXTX);
    (*uart_base.add(HW_UART_RXTX)).ow(tx_char); 

    (*uart_base.add(HW_UART_RXTX))
       .rw(tx_char, HW_UART_RXTX_RXTX); // this will read the register and throw away contents before replacing it

A difficult, compound register example

Simple Register Example
        <peripheral>
            <name>AUDIO</name>
            <baseAddress>0xF0016000</baseAddress>
            <groupName>AUDIO</groupName>
            <registers>
                <register>
                    <name>RX_STAT</name>
                    <description><![CDATA[Rx data path status]]></description>
                    <addressOffset>0x0010</addressOffset>
                    <resetValue>0x80000000</resetValue>
                    <size>32</size>
                    <access>read-only</access>
                    <fields>
                        <field>
                            <name>overflow</name>
                            <msb>0</msb>
                            <bitRange>[0:0]</bitRange>
                            <lsb>0</lsb>
                            <description><![CDATA[Rx overflow]]></description>
                        </field>
                        <field>
                            <name>underflow</name>
                            <msb>1</msb>
                            <bitRange>[1:1]</bitRange>
                            <lsb>1</lsb>
                            <description><![CDATA[Rx underflow]]></description>
                        </field>
                        <field>
                            <name>dataready</name>
                            <msb>2</msb>
                            <bitRange>[2:2]</bitRange>
                            <lsb>2</lsb>
                            <description><![CDATA[256 words of data loaded and ready to read]]></description>
                        </field>
                        <field>
                            <name>empty</name>
                            <msb>3</msb>
                            <bitRange>[3:3]</bitRange>
                            <lsb>3</lsb>
                            <description><![CDATA[No data available in FIFO to read]]></description>
                        </field>
                        <field>
                            <name>wrcount</name>
                            <msb>12</msb>
                            <bitRange>[12:4]</bitRange>
                            <lsb>4</lsb>
                            <description><![CDATA[Write count]]></description>
                        </field>
                        <field>
                            <name>rdcount</name>
                            <msb>21</msb>
                            <bitRange>[21:13]</bitRange>
                            <lsb>13</lsb>
                            <description><![CDATA[Read count]]></description>
                        </field>
                        <field>
                            <name>fifo_depth</name>
                            <msb>30</msb>
                            <bitRange>[30:22]</bitRange>
                            <lsb>22</lsb>
                            <description><![CDATA[FIFO depth as synthesized]]></description>
                        </field>
                        <field>
                            <name>concatenate_channels</name>
                            <msb>31</msb>
                            <bitRange>[31:31]</bitRange>
                            <lsb>31</lsb>
                            <description><![CDATA[Receive and send both channels atomically]]></description>
                        </field>
                    </fields>
                </register>
            </registers>
         </peripheral>
csr_base,audio,0xf0016000,,
csr_register,audio_rx_stat,0xf0016010,1,ro
constant,audio_interrupt,6,,
memory_region,audio,0xe0000000,4,io
    const HW_AUDIO_BASE: u32 = 0xf001_6000;
    const HW_AUDIO_MEM: u32 = 0xe000_0000;
    const HW_AUDIO_RX_STAT: u32 = 0x10;
    const HW_AUDIO_RX_STAT_EMPTY: [u32; 2] = [0b1000, 3];
    const HW_AUDIO_RX_STAT_RDCOUNT: [u32; 2]  = [0b11_1111_1110_0000_0000_0000, 13];
    
    // set up the base pointers
    let audio_base = (HW_AUDIO_BASE as *mut u32) as *mut Volatile<u32>;
    let audio_mem = (HW_AUDIO_MEM as *mut u32) as *mut Volatile<u32>;

    // read RX_STAT
    let rx_stat = (*audio_base.add(HW_AUDIO_RX_STAT)).r(); // full 32 bit value
    let empty: bool = (*audio_base.add(HW_AUDIO_RX_STAT)).rm(HW_AUDIO_RX_STAT_EMPTY) != 0;
    // this will shift rdcount automatically
    let rdcount: usize = (*audio_base.add(HW_AUDIO_RX_STAT)).rm(HW_AUDIO_RX_STAT_RDCOUNT);

    // this overwrites the *entire* register with just WRCOUNT, and everything else as 0
    (*audio_base.add(HW_AUDIO_RX_STAT)).ow(value, HW_AUDIO_RX_STAT_RDCOUNT);

    // this will read the existing value and just update these two fields
    (*audio_base.add(HW_AUDIO_RX_STAT))
       .rw(value1, HW_AUDIO_RX_STAT_EMPTY)
       .rw(value2, HW_AUDIO_RX_STAT_RDCOUNT);

    // write and read to the audio memory window
    (*audio_mem).write( audio_sample );
    let mic_data: u16 = (*audio_mem).read() as u16;
 
     // not addressed: interrupts??
Clone this wiki locally