opentitanlib/test_utils/
extclk.rs1use std::time::Duration;
8
9use thiserror::Error;
10
11use top_earlgrey::top_earlgrey;
12
13use crate::chip::boolean::MultiBitBool4;
14use crate::dif::clkmgr::{ClkmgrExtclkCtrl, ClkmgrReg};
15use crate::io::jtag::Jtag;
16use crate::test_utils::poll;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum ClockSpeed {
21 Low,
23 High,
25}
26
27pub struct ExternalClock;
29
30impl ExternalClock {
31 const CLKMGR_BASE_ADDR: u32 = top_earlgrey::CLKMGR_AON_BASE_ADDR as u32;
33
34 const EXTCLK_CTRL_REGWEN_ADDR: u32 =
36 Self::CLKMGR_BASE_ADDR + ClkmgrReg::ExtclkCtrlRegwen as u32;
37 const EXTCLK_CTRL_ADDR: u32 = Self::CLKMGR_BASE_ADDR + ClkmgrReg::ExtclkCtrl as u32;
38 const EXTCLK_STATUS_ADDR: u32 = Self::CLKMGR_BASE_ADDR + ClkmgrReg::ExtclkStatus as u32;
39
40 const POLL_TIMEOUT: Duration = Duration::from_millis(500);
42 const POLL_DELAY: Duration = Duration::from_millis(5);
43
44 pub fn enable(jtag: &mut dyn Jtag, speed: ClockSpeed) -> Result<(), ExternalClockError> {
46 if !Self::write_enabled(jtag)? {
47 return Err(ExternalClockError::WriteDisabled);
48 }
49
50 let hi_speed = match speed {
52 ClockSpeed::Low => MultiBitBool4::False,
53 ClockSpeed::High => MultiBitBool4::True,
54 };
55 let fields = [
56 (ClkmgrExtclkCtrl::SEL, MultiBitBool4::True),
57 (ClkmgrExtclkCtrl::HI_SPEED_SEL, hi_speed),
58 ];
59
60 let extclk_ctrl = fields.into_iter().fold(0, |acc, (field, value)| {
62 acc | field.emplace(u8::from(value) as u32)
63 });
64
65 jtag.write_memory32(Self::EXTCLK_CTRL_ADDR, &[extclk_ctrl])
67 .map_err(|source| ExternalClockError::Jtag { source })?;
68
69 Self::wait_for_ack(jtag)?;
70
71 Ok(())
72 }
73
74 pub fn disable(jtag: &mut dyn Jtag) -> Result<(), ExternalClockError> {
76 if !Self::write_enabled(jtag)? {
77 return Err(ExternalClockError::WriteDisabled);
78 }
79
80 let extclk_ctrl = ClkmgrExtclkCtrl::SEL.emplace(u8::from(MultiBitBool4::False) as u32);
82 jtag.write_memory32(Self::EXTCLK_CTRL_ADDR, &[extclk_ctrl])
83 .map_err(|source| ExternalClockError::Jtag { source })?;
84
85 Self::wait_for_ack(jtag)?;
86
87 Ok(())
88 }
89
90 fn write_enabled(jtag: &mut dyn Jtag) -> Result<bool, ExternalClockError> {
93 let mut buf = [0];
94 jtag.read_memory32(Self::EXTCLK_CTRL_REGWEN_ADDR, &mut buf)
95 .map_err(|source| ExternalClockError::Jtag { source })?;
96
97 Ok(buf[0] & 1 == 1)
98 }
99
100 fn wait_for_ack(jtag: &mut dyn Jtag) -> Result<(), ExternalClockError> {
102 poll::poll_until(Self::POLL_TIMEOUT, Self::POLL_DELAY, || {
103 let mut status_buf = [0];
105 jtag.read_memory32(Self::EXTCLK_STATUS_ADDR, &mut status_buf)
106 .map_err(|source| ExternalClockError::Jtag { source })?;
107
108 Ok(status_buf[0] == u8::from(MultiBitBool4::True) as u32)
109 })
110 .map_err(|source| ExternalClockError::Nack { source })
111 }
112}
113
114#[derive(Debug, Error)]
116#[non_exhaustive]
117pub enum ExternalClockError {
118 #[error("failed to communicate over JTAG")]
119 Jtag { source: anyhow::Error },
120
121 #[error("did not read ACK for external clock change")]
122 Nack { source: anyhow::Error },
123
124 #[error("writing to external clock registers is disabled")]
125 WriteDisabled,
126}