Skip to content

Conversation

@madeleyneVaca
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings November 24, 2025 18:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds power optimization features to reduce VDDCore voltage through slower system PLL speeds. It introduces a new fsl_power module (ported from NXP's fsl_power.c) for voltage/frequency management and removes lingering hard coding so all fields of a ClockConfig are used when clocks are initialized

Key changes:

  • New fsl_power module with LDO voltage management and body bias control
  • main clock mult, div, and src values pulled from self.config

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 8 comments.

File Description
src/fsl_power.rs New power management module with voltage scaling, LVD control, and body bias functions
src/clocks.rs Added voltage management integration
src/lib.rs Exported the new fsl_power module

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 14 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings December 3, 2025 00:18
@madeleyneVaca madeleyneVaca changed the title Experiment to lower VDDCore by using slower syspll speeds Clocks: Remove remaining hard coding, add partial NXP fsl_power library support Dec 3, 2025
@madeleyneVaca madeleyneVaca changed the title Clocks: Remove remaining hard coding, add partial NXP fsl_power library support [RFC] Clocks: Remove remaining hard coding, add partial NXP fsl_power library support Dec 3, 2025
@madeleyneVaca madeleyneVaca marked this pull request as ready for review December 3, 2025 00:19
@madeleyneVaca madeleyneVaca requested a review from a team as a code owner December 3, 2025 00:19
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@RobertZ2011 RobertZ2011 self-requested a review December 19, 2025 17:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Remove some hardcoded clock reg settings and use the config blob fields instead. Experimental slow-clocks feature to be removed after more thorough validation

Add default impl for ClockConfig
Remove slow-clocks feature as end user defines the clock config blob which is now utilized fully. Use the requested main clock frequency to set the LDO voltage level for optimal power consumption at that speed.

Restructure LDO voltage test to better cover voltage calculations.
… comment

 Was accidentally put in wrong order and does not match C implementation of fsl_power
Copilot AI review requested due to automatic review settings December 23, 2025 17:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

asasine
asasine previously approved these changes Dec 23, 2025
Copy link

@asasine asasine left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine to me. No obvious UB in the fsl_power port. I don't know a ton about the HAL and Clocks API.

let pmc = unsafe { crate::pac::Pmc::steal() };

// Enter FBB mode if not already in FBB
if get_body_bias_mode(&pmc) != BodyBiasMode::Fbb {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the default behavior of the boot ROM? Are we already in fbb mode by default?

self.mult = MainPllClkMult::Mult33;
}
_ => {
warn!(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is reasonable to panic here as the value input should be constrained by the enum in the configuration struct.

Copy link
Contributor

@kurtjd kurtjd Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or better yet, make set_clock_rate take a MainPllClkMult instead of a u8 (unless there's a good reason not to that I'm missing).


// Configure power domains for sleep: MAINCLK_SHUTOFF=1, FBB_PD=0 (keep FBB powered)
let pdsleepcfg0 = (sysctl0.pdruncfg0().read().bits()
| (1 << 20)) // MAINCLK_SHUTOFF_MASK
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't SYSCTL0_PDSLEEPCFG0_MAINCLK_SHUTOFF be bit position 0?

#define SYSCTL0_PDSLEEPCFG0_MAINCLK_SHUTOFF_MASK (0x1U)
#define SYSCTL0_PDSLEEPCFG0_MAINCLK_SHUTOFF_SHIFT (0U)

And SYSCTL0_PDSLEEPCFG0_FBB be bit position 12?

#define SYSCTL0_PDSLEEPCFG0_FBB_PD_MASK          (0x1000U)
#define SYSCTL0_PDSLEEPCFG0_FBB_PD_SHIFT         (12U)

We should update PAC to add these field definition so they are not as error prone.

// Configure PMC: enable auto-wakeup, disable LVD during transition
pmc.ctrl().write(|w| unsafe {
w.bits(
(pmc_ctrl | (1 << 15)) // AUTOWKEN_MASK
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be bit 28?

#define PMC_CTRL_AUTOWKEN_MASK                   (0x10000000U)
#define PMC_CTRL_AUTOWKEN_SHIFT                  (28U)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or pmc.ctrl() .modify(|_, w| w.autowken().set_bit().lvdcorere().clear_bit().lvdcoreie().clear_bit());

});

// Enable PMC interrupt for auto-wake (IRQ 49, bit 49-32=17 in STARTEN1)
sysctl0.starten1_set().write(|w| unsafe { w.bits(1 << 17) });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be bit 26?

Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or sysctl0.starten1_set().write(|w| w.pmic().set_bit());

.write(|w| unsafe { w.bits(sysctl0.pdruncfg3().read().bits()) });

// PDWAKECFG: Keep FBB state after wake (FBBKEEPST_MASK = bit 17)
sysctl0.pdwakecfg().write(|w| unsafe { w.bits(1 << 17) });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is bit position 1:

#define SYSCTL0_PDWAKECFG_FBBKEEPST_MASK         (0x2U)
#define SYSCTL0_PDWAKECFG_FBBKEEPST_SHIFT        (1U)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or sysctl0.pdwakecfg().write(|w| w.fbbkeepst().fbbkeepst_1());

sysctl0.starten1_set().write(|w| unsafe { w.bits(1 << 17) });

// Execute WFI - will wake automatically via PMC timer
cortex_m::asm::wfi();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a critical section, interrupt should be disabled globally. Can we even wake up from WFI if global interrupt is disabled? And we did not seem to have enabled the PMIC IRQ here?

Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at arm v8 spec, it seems a WFI should still wake even with global interrupts disabled:
image

];

/// Sentinel for invalid/unsupported voltage level
pub const POWER_INVALID_VOLT_LEVEL: u32 = 0xFFFFFFFF;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? Can we just return Results instead where this is used?

let pmc_ctrl = pmc.ctrl().read().bits();

// Enable deep sleep mode (set SLEEPDEEP bit in SCR)
const SCB_SCR_SLEEPDEEP: u32 = 1 << 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing this, we could have do this instead:

       {
            let mut cortex = cortex_m::Peripherals::take().unwrap();
            cortex.SCB.set_sleepdeep();
        }

}

/// Returns the current body bias mode.
pub fn get_body_bias_mode(pmc: &crate::pac::Pmc) -> BodyBiasMode {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the SDK, this is actually reading PDSLEEPCFG0 or PDRUNCFG0, and not PMC runctrl.

static inline body_bias_mode_t POWER_GetBodyBiasMode(pmic_mode_reg_t reg)
{
    uint32_t mode   = (uint32_t)reg;
    uint32_t bbMode = (SYSCTL0_TUPLE_REG(mode) & (SYSCTL0_PDSLEEPCFG0_RBB_PD_MASK | SYSCTL0_PDSLEEPCFG0_FBB_PD_MASK)) >>
                      SYSCTL0_PDSLEEPCFG0_RBB_PD_SHIFT;

    return (body_bias_mode_t)bbMode;
}

enter_fbb(&pmc);
}

let idx = temp_range as usize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is already an enum, matching to the num or implement into() is simpler.

);

// Find the first frequency threshold that freq is greater than
let position = freq_levels.iter().position(|&threshold| freq > threshold);
Copy link
Contributor

@jerrysxie jerrysxie Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is relatively straightforward. However, the code is very unreadable in its current form. It is traversing through frequency levels and picking a voltage level based on desired frequency.

We can simplify the logic and avoid POWER_INVALID_VOLT_LEVEL (let it return an option) .

Define POWER_LDO_VOLT_LEVEL as 2 instances, one for full and one for low:

const LOW_POWER_LDO_VOLT_LEVEL: [LDOVoltLevel; 3] = [
    LDOVoltLevel::Ldo0p9v,
    LDOVoltLevel::Ldo0p8v,
    LDOVoltLevel::Ldo0p7v,
];

const FULL_POWER_LDO_VOLT_LEVEL: [LDOVoltLevel; 5] = [
    LDOVoltLevel::Ldo1p13v,
    LDOVoltLevel::Ldo1p0v,
    LDOVoltLevel::Ldo0p9v,
    LDOVoltLevel::Ldo0p8v,
    LDOVoltLevel::Ldo0p7v,
];

The logic of function can be something like this
    let freq_iter = freq_levels.iter();
    let volt_level_iter = POWER_LDO_VOLT_LEVEL.iter();

    if let Some(max_freq) = freq_iter.next() {
        if freq > *max_freq {
            return None;
        }
    }

    for (freq_threshold, volt_level) in freq_iter.zip(volt_level_iter) {
        if freq > *freq_threshold {
            return Some(*volt_level as u32);
        }
    }

    return POWER_LDO_VOLT_LEVEL.last();

};

let cm33_volt = calc_volt_level(cm33_levels, cm33_freq);
let dsp_volt = calc_volt_level(dsp_levels, dsp_freq);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just ignore the DSP freq and voltage as it is not currently being used?

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

Labels

BREAKING CHANGE PR causes a breaking change

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

6 participants