Skip to main content

luwen_api/
arc_msg.rs

1// SPDX-FileCopyrightText: © 2023 Tenstorrent Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use thiserror::Error;
5
6use crate::{
7    chip::{AxiError, HlComms},
8    error::PlatformError,
9};
10
11#[derive(Debug, Clone, Copy)]
12pub enum PowerState {
13    Busy,
14    ShortIdle,
15    LongIdle,
16}
17
18#[derive(Debug)]
19pub enum ArcState {
20    A0,
21    A1,
22    A3,
23    A5,
24}
25
26#[derive(Debug)]
27pub enum FwType {
28    ArcL2,
29    FwBundle,
30    FwBundleSPI,
31}
32
33#[derive(Debug)]
34pub enum TypedArcMsg {
35    Nop,
36    Test { arg: u32 },
37    ArcGoToSleep,
38
39    SetPowerState(PowerState),
40
41    FwVersion(FwType),
42    GetSmbusTelemetryAddr,
43
44    SetArcState { state: ArcState },
45
46    ResetSafeClks { arg: u32 },
47    ToggleTensixReset { arg: u32 },
48    DeassertRiscVReset,
49    GetAiclk,
50    TriggerReset,
51    GetHarvesting,
52    TriggerSpiCopyLtoR,
53    GetSpiDumpAddr,
54    SpiRead { addr: u32 },
55    SpiWrite,
56}
57
58impl From<TypedArcMsg> for ArcMsg {
59    fn from(val: TypedArcMsg) -> Self {
60        ArcMsg::Typed(val)
61    }
62}
63
64impl TypedArcMsg {
65    pub fn msg_code(&self) -> u16 {
66        match self {
67            TypedArcMsg::Nop => 0x11,
68            TypedArcMsg::ArcGoToSleep => 0x55,
69            TypedArcMsg::Test { .. } => 0x90,
70            TypedArcMsg::GetSmbusTelemetryAddr => 0x2C,
71            TypedArcMsg::TriggerSpiCopyLtoR => 0x50,
72            TypedArcMsg::SetPowerState(state) => match state {
73                PowerState::Busy => 0x52,
74                PowerState::ShortIdle => 0x53,
75                PowerState::LongIdle => 0x54,
76            },
77            TypedArcMsg::TriggerReset => 0x56,
78            TypedArcMsg::GetHarvesting => 0x57,
79            TypedArcMsg::DeassertRiscVReset => 0xba,
80            TypedArcMsg::ResetSafeClks { .. } => 0xbb,
81            TypedArcMsg::ToggleTensixReset { .. } => 0xaf,
82            TypedArcMsg::GetAiclk => 0x34,
83            TypedArcMsg::SetArcState { state } => match state {
84                ArcState::A0 => 0xA0,
85                ArcState::A1 => 0xA1,
86                ArcState::A3 => 0xA3,
87                ArcState::A5 => 0xA5,
88            },
89            TypedArcMsg::FwVersion(_) => 0xb9,
90            TypedArcMsg::GetSpiDumpAddr => 0x29,
91            TypedArcMsg::SpiRead { .. } => 0x2A,
92            TypedArcMsg::SpiWrite => 0x2B,
93        }
94    }
95}
96
97#[derive(Debug)]
98pub enum ArcMsg {
99    Typed(TypedArcMsg),
100    Raw { msg: u16, arg0: u16, arg1: u16 },
101    Buf([u32; 8]),
102}
103
104impl ArcMsg {
105    pub fn msg_code(&self) -> u16 {
106        let code = match self {
107            ArcMsg::Raw { msg, .. } => *msg,
108            ArcMsg::Typed(msg) => msg.msg_code(),
109            ArcMsg::Buf([msg, ..]) => u16::from(msg.to_le_bytes()[0]),
110        };
111
112        0xaa00 | code
113    }
114
115    pub fn args(&self) -> (u16, u16) {
116        match self {
117            ArcMsg::Raw { arg0, arg1, .. } => (*arg0, *arg1),
118            ArcMsg::Typed(msg) => match &msg {
119                TypedArcMsg::Test { arg }
120                | TypedArcMsg::ResetSafeClks { arg }
121                | TypedArcMsg::ToggleTensixReset { arg }
122                | TypedArcMsg::SpiRead { addr: arg } => {
123                    ((arg & 0xFFFF) as u16, ((arg >> 16) & 0xFFFF) as u16)
124                }
125                TypedArcMsg::SpiWrite => (0xFFFF, 0xFFFF),
126                TypedArcMsg::Nop
127                | TypedArcMsg::ArcGoToSleep
128                | TypedArcMsg::GetSmbusTelemetryAddr
129                | TypedArcMsg::SetPowerState(_)
130                | TypedArcMsg::DeassertRiscVReset
131                | TypedArcMsg::GetAiclk
132                | TypedArcMsg::TriggerReset
133                | TypedArcMsg::GetHarvesting
134                | TypedArcMsg::GetSpiDumpAddr
135                | TypedArcMsg::TriggerSpiCopyLtoR
136                | TypedArcMsg::SetArcState { .. } => (0, 0),
137                TypedArcMsg::FwVersion(ty) => match ty {
138                    FwType::ArcL2 => (0, 0),
139                    FwType::FwBundle => (1, 0),
140                    FwType::FwBundleSPI => (2, 0),
141                },
142            },
143            ArcMsg::Buf(_) => unimplemented!(),
144        }
145    }
146
147    pub fn from_values(msg: u32, arg0: u16, arg1: u16) -> Self {
148        let arg = ((arg1 as u32) << 16) | arg0 as u32;
149        let msg = 0xFF & msg;
150        let msg = match msg {
151            0x11 => TypedArcMsg::Nop,
152            0x34 => TypedArcMsg::GetAiclk,
153            0x56 => TypedArcMsg::TriggerReset,
154            0xbb => TypedArcMsg::ResetSafeClks { arg },
155            0xaf => TypedArcMsg::ToggleTensixReset { arg },
156            0xba => TypedArcMsg::DeassertRiscVReset,
157            0x50 => TypedArcMsg::TriggerSpiCopyLtoR,
158            0x52 => TypedArcMsg::SetPowerState(PowerState::Busy),
159            0x53 => TypedArcMsg::SetPowerState(PowerState::ShortIdle),
160            0x54 => TypedArcMsg::SetPowerState(PowerState::LongIdle),
161            0x57 => TypedArcMsg::GetHarvesting,
162            0x90 => TypedArcMsg::Test { arg },
163            0xA0 => TypedArcMsg::SetArcState {
164                state: ArcState::A0,
165            },
166            0xA1 => TypedArcMsg::SetArcState {
167                state: ArcState::A1,
168            },
169            0xA3 => TypedArcMsg::SetArcState {
170                state: ArcState::A3,
171            },
172            0xA5 => TypedArcMsg::SetArcState {
173                state: ArcState::A5,
174            },
175            0xB9 => TypedArcMsg::FwVersion(match arg {
176                0 => FwType::ArcL2,
177                1 => FwType::FwBundle,
178                2 => FwType::FwBundleSPI,
179                _ => panic!("Unknown FW type {arg}"),
180            }),
181            value => {
182                unimplemented!("Unknown ARC message {:#x}", value)
183            }
184        };
185
186        ArcMsg::Typed(msg)
187    }
188}
189
190#[derive(Error, Debug)]
191pub enum ArcMsgProtocolError {
192    #[error("Message {0} not recognized")]
193    MsgNotRecognized(u16),
194    #[error("Timed out while waiting {0:?} for ARC to respond")]
195    Timeout(std::time::Duration),
196    #[error("ARC is asleep")]
197    ArcAsleep,
198    #[error("Failed to trigger FW interrupt")]
199    FwIntFailed,
200    #[error("Mailbox {0} is invalid")]
201    InvalidMailbox(usize),
202    #[error("Unknown error code {0}")]
203    UnknownErrorCode(u8),
204}
205
206impl ArcMsgProtocolError {
207    #[inline(always)]
208    pub fn into_error(self) -> ArcMsgError {
209        ArcMsgError::ProtocolError {
210            source: self,
211            backtrace: crate::error::BtWrapper(std::backtrace::Backtrace::capture()),
212        }
213    }
214}
215
216#[derive(Error, Debug)]
217pub enum ArcMsgError {
218    #[error("{source}\n{backtrace}")]
219    ProtocolError {
220        source: ArcMsgProtocolError,
221        backtrace: crate::error::BtWrapper,
222    },
223
224    #[error(transparent)]
225    AxiError(#[from] AxiError),
226}
227
228#[derive(Debug)]
229pub enum ArcMsgOk {
230    Ok { rc: u32, arg: u32 },
231    OkBuf([u32; 8]),
232    OkNoWait,
233}
234
235/// Returns True if new interrupt triggered, or False if the
236/// FW is currently busy. The message IRQ handler should only take a couple
237/// dozen cycles, so if this returns False it probably means something went
238/// wrong.
239fn trigger_fw_int<T: HlComms>(comms: &T, addrs: &ArcMsgAddr) -> Result<bool, PlatformError> {
240    let misc = comms.axi_read32(addrs.arc_misc_cntl)?;
241
242    if misc & (1 << 16) != 0 {
243        return Ok(false);
244    }
245
246    let misc_bit16_set = misc | (1 << 16);
247    comms.axi_write32(addrs.arc_misc_cntl, misc_bit16_set)?;
248
249    Ok(true)
250}
251
252#[derive(Clone, Debug)]
253pub struct ArcMsgAddr {
254    pub scratch_base: u64,
255    pub arc_misc_cntl: u64,
256}
257
258pub fn arc_msg<T: HlComms>(
259    comms: &T,
260    msg: &ArcMsg,
261    wait_for_done: bool,
262    timeout: std::time::Duration,
263    msg_reg: u64,
264    return_reg: u64,
265    addrs: &ArcMsgAddr,
266) -> Result<ArcMsgOk, PlatformError> {
267    const MSG_ERROR_REPLY: u32 = 0xffffffff;
268
269    let (arg0, arg1) = msg.args();
270
271    let code = msg.msg_code();
272
273    let current_code = comms.axi_read32(addrs.scratch_base + (msg_reg * 4))?;
274    if (current_code & 0xFFFF) as u16 == TypedArcMsg::ArcGoToSleep.msg_code() {
275        Err(ArcMsgProtocolError::ArcAsleep.into_error())?;
276    }
277
278    comms.axi_write32(
279        addrs.scratch_base + (return_reg * 4),
280        arg0 as u32 | ((arg1 as u32) << 16),
281    )?;
282
283    comms.axi_write32(addrs.scratch_base + (msg_reg * 4), code as u32)?;
284
285    if !trigger_fw_int(comms, addrs)? {
286        Err(ArcMsgProtocolError::FwIntFailed.into_error())?;
287    }
288
289    if wait_for_done {
290        let start = std::time::Instant::now();
291        loop {
292            let status = comms.axi_read32(addrs.scratch_base + (msg_reg * 4))?;
293            if (status & 0xFFFF) as u16 == code & 0xFF {
294                let exit_code = (status >> 16) & 0xFFFF;
295                let arg = comms.axi_read32(addrs.scratch_base + (return_reg * 4))?;
296
297                return Ok(ArcMsgOk::Ok { rc: exit_code, arg });
298            } else if status == MSG_ERROR_REPLY {
299                Err(ArcMsgProtocolError::MsgNotRecognized(code).into_error())?;
300            }
301
302            std::thread::sleep(std::time::Duration::from_millis(1));
303            if start.elapsed() > timeout {
304                Err(ArcMsgProtocolError::Timeout(timeout).into_error())?;
305            }
306        }
307    }
308
309    Ok(ArcMsgOk::OkNoWait)
310}