use std::time::Duration;
use thiserror::Error;
use top_earlgrey::top_earlgrey;
use crate::chip::boolean::MultiBitBool4;
use crate::dif::clkmgr::{ClkmgrExtclkCtrl, ClkmgrReg};
use crate::io::jtag::Jtag;
use crate::test_utils::poll;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClockSpeed {
Low,
High,
}
pub struct ExternalClock;
impl ExternalClock {
const CLKMGR_BASE_ADDR: u32 = top_earlgrey::CLKMGR_AON_BASE_ADDR as u32;
const EXTCLK_CTRL_REGWEN_ADDR: u32 =
Self::CLKMGR_BASE_ADDR + ClkmgrReg::ExtclkCtrlRegwen as u32;
const EXTCLK_CTRL_ADDR: u32 = Self::CLKMGR_BASE_ADDR + ClkmgrReg::ExtclkCtrl as u32;
const EXTCLK_STATUS_ADDR: u32 = Self::CLKMGR_BASE_ADDR + ClkmgrReg::ExtclkStatus as u32;
const POLL_TIMEOUT: Duration = Duration::from_millis(500);
const POLL_DELAY: Duration = Duration::from_millis(5);
pub fn enable(jtag: &mut dyn Jtag, speed: ClockSpeed) -> Result<(), ExternalClockError> {
if !Self::write_enabled(jtag)? {
return Err(ExternalClockError::WriteDisabled);
}
let hi_speed = match speed {
ClockSpeed::Low => MultiBitBool4::False,
ClockSpeed::High => MultiBitBool4::True,
};
let fields = [
(ClkmgrExtclkCtrl::SEL, MultiBitBool4::True),
(ClkmgrExtclkCtrl::HI_SPEED_SEL, hi_speed),
];
let extclk_ctrl = fields.into_iter().fold(0, |acc, (field, value)| {
acc | field.emplace(u8::from(value) as u32)
});
jtag.write_memory32(Self::EXTCLK_CTRL_ADDR, &[extclk_ctrl])
.map_err(|source| ExternalClockError::Jtag { source })?;
Self::wait_for_ack(jtag)?;
Ok(())
}
pub fn disable(jtag: &mut dyn Jtag) -> Result<(), ExternalClockError> {
if !Self::write_enabled(jtag)? {
return Err(ExternalClockError::WriteDisabled);
}
let extclk_ctrl = ClkmgrExtclkCtrl::SEL.emplace(u8::from(MultiBitBool4::False) as u32);
jtag.write_memory32(Self::EXTCLK_CTRL_ADDR, &[extclk_ctrl])
.map_err(|source| ExternalClockError::Jtag { source })?;
Self::wait_for_ack(jtag)?;
Ok(())
}
fn write_enabled(jtag: &mut dyn Jtag) -> Result<bool, ExternalClockError> {
let mut buf = [0];
jtag.read_memory32(Self::EXTCLK_CTRL_REGWEN_ADDR, &mut buf)
.map_err(|source| ExternalClockError::Jtag { source })?;
Ok(buf[0] & 1 == 1)
}
fn wait_for_ack(jtag: &mut dyn Jtag) -> Result<(), ExternalClockError> {
poll::poll_until(Self::POLL_TIMEOUT, Self::POLL_DELAY, || {
let mut status_buf = [0];
jtag.read_memory32(Self::EXTCLK_STATUS_ADDR, &mut status_buf)
.map_err(|source| ExternalClockError::Jtag { source })?;
Ok(status_buf[0] == u8::from(MultiBitBool4::True) as u32)
})
.map_err(|source| ExternalClockError::Nack { source })
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ExternalClockError {
#[error("failed to communicate over JTAG")]
Jtag { source: anyhow::Error },
#[error("did not read ACK for external clock change")]
Nack { source: anyhow::Error },
#[error("writing to external clock registers is disabled")]
WriteDisabled,
}