rustembeddedraspberry-pi-picoperipherals

Can't modify peripheral registers on the rp2040


I'm scratching my head because I can't write and read some/most peripheral registers. I'm using Rust here and the setup code from the rp2040-hal.

The following code tries to write 0x42 to the DMA's CH0_READ_ADDR register (0x50000000) and reads it back:

let ptr = (0x50000000 + 0x000) as *mut u16; // crtl
rprintln!("ptr {:p}", ptr);
unsafe {
    rprintln!("reg_before {:b}", ptr.read_volatile());
    ptr.write(0x42);
    rprintln!("reg_after {:b}", ptr.read_volatile());
}

This outputs:

ptr 0x50000000
reg_before 0
reg_after 0

This happens with several registers I tested. However, with the Watchdog's scratch register WATCHDOG_SCRATCH0 (0x4005800c) (as suggested in the example on datasheet p.19), everything works as expected.

I thought that maybe that peripheral is not clocked or so, but I didn't find anything useful in the datasheet. Perhaps somebody has an idea on what's going wrong here.

Minimal working example:

#![no_std]
#![no_main]

extern crate panic_rtt_target;

use cortex_m_rt::entry;
use rtt_target::{rprintln, rtt_init_print};
use rp2040_hal as hal;
use hal::pac;
use embedded_time::fixed_point::FixedPoint;
use rp2040_hal::clocks::Clock;

#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;

const XTAL_FREQ_HZ: u32 = 12_000_000u32;

#[entry]
fn entry() -> ! {
    let mut pac = pac::Peripherals::take().unwrap();
    let core = pac::CorePeripherals::take().unwrap();

    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

    let clocks = hal::clocks::init_clocks_and_plls(
        XTAL_FREQ_HZ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    rtt_init_print!();
    rprintln!("Hello registers!");

    //let ptr = (0x4005800c) as *mut u16; // scratchreg
    let ptr = (0x50000000 + 0x000) as *mut u16; // dma ch0 read addr
    rprintln!("ptr {:p}", ptr);
    unsafe {
        rprintln!("reg_before {:b}", ptr.read_volatile());
        ptr.write(0x42);
        rprintln!("reg_after {:b}", ptr.read_volatile());
    }

    loop {
    }
}

Solution

  • Ok, found out: It is about the resets of the chip.

    The datasheet states in 2.14.1:

    Every peripheral reset by the reset controller is held in reset at power-up. It is up to software to deassert the reset of peripherals it intends to use.

    So in this case the DMA bit of the RESET register has to be cleared:

    pac.RESETS.reset.modify(|_, w| w.dma().clear_bit());
    

    Afterwards the registers can be written to and read from as expected.