Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement special register access mode to write to AVR protected registers #784

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

G33KatWork
Copy link

The newer AVR Attiny chips contain a mechanism Microchip calls CCP: Configuration Change Protection.

There are a few special registers that configure system-level peripherals like the clock- or the flash-controller which should not be modified by accident. To prevent that, a special unlock byte has to be written to the CCP unlock register and then the protected register has to be written within the next four executed instructions. See chapter 8.5.7 on page 56 on the linked datasheet for more details.

I tried doing this using the regular register access mechanisms, but the rust compiler always reordered and optimized code so that this didn't work out. Even if it would have, it wouldn't be exactly reliable.

This patch adds a new Target type for AVRs and if it's selected the new file generic_avr_ccp.rs is emitted when generating code. This file contains a bunch of traits to define an unlock register, protected registers and blanket implementations that implement protected writes in case a register implements the aforementioned marker traits.

This has to be used in conjunction with atdf2svd to get an SVD. After generating the pac, you still need to manually define the list of protected registers like this:

pub use crate::generic::ProtectedWritable;

#[cfg(feature = "attiny817")]
pub mod attiny817 {
    use crate::generic::{UnlockRegister, Protected};

    // Mark the CPU.CCP register with the UnlockRegister trait so that it can be used to unlock the below defined registers
    impl UnlockRegister for crate::attiny817::cpu::ccp::CCP_SPEC { const PTR: *mut u8 = 0x34 as *mut u8; }

    // Configuration change protected registers in NVMCTRL
    impl Protected for crate::attiny817::nvmctrl::ctrla::CTRLA_SPEC { const MAGIC: u8 = 0x9D; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::nvmctrl::ctrlb::CTRLB_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }

    // Configuration change protected registers in CLKCTRL
    impl Protected for crate::attiny817::clkctrl::mclkctrlb::MCLKCTRLB_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::mclklock::MCLKLOCK_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::xosc32kctrla::XOSC32KCTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::mclkctrla::MCLKCTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::osc20mctrla::OSC20MCTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::osc20mcaliba::OSC20MCALIBA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::osc20mcalibb::OSC20MCALIBB_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::clkctrl::osc32kctrla::OSC32KCTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }

    // Configuration change protected registers in RSTCTRL
    impl Protected for crate::attiny817::rstctrl::swrr::SWRR_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }

    // Configuration change protected registers in CPUINT
    impl Protected for crate::attiny817::cpuint::ctrla::CTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }

    // Configuration change protected registers in BOD
    impl Protected for crate::attiny817::bod::ctrla::CTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }

    // Configuration change protected registers in WDT
    impl Protected for crate::attiny817::wdt::ctrla::CTRLA_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
    impl Protected for crate::attiny817::wdt::status::STATUS_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }

    // Configuration change protected registers in TCD0
    impl Protected for crate::attiny817::tcd0::faultctrl::FAULTCTRL_SPEC { const MAGIC: u8 = 0xD8; type CcpReg = crate::attiny817::cpu::ccp::CCP_SPEC; }
}

I have not found a way yet to figure out the information that a register is protected by using the ATDF/SVD.

There will be a follow-up PR on avr-device which uses svd2rust to generate PACs for all kinds of AVR devices. I added support for the attiny817 which depends on this new CCP register access code.

As soon as I submitted that PR, I'll cross-reference it.

I am open to suggestions on how to make the code nicer. Especially the parts written in assembler. It was very finicky to make this work and I am still not 100% satisfied with how it looks. I tried getting rid of the ldi instruction, but couldn't make it work.

…ethods for AVR registers which are protected by the configuration change protection (CCP)
@burrbull
Copy link
Member

I have not found a way yet to figure out the information that a register is protected by using the ATDF/SVD.

If ATDF contains this information, svd2rust could use atdf2svd as library dependency and take it directly.

@G33KatWork
Copy link
Author

I just checked again. There is a key link missing.

The CCP register is defined and the two magic values that are required to be written into it to either unlock an IOREG or perform a flash memory write.

However, if we look at the register description for an IOREG-protected register like CLKCTRL.MCLKCTRLB, I don't see mentioned that it is actually IOREG protected.

So I think I was wrong. I have this stuff laying around for a while now and must've forgotten that this link is missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants