Skip to main content

luwen_api/chip/
blackhole.rs

1// SPDX-FileCopyrightText: © 2024 Tenstorrent Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use bytemuck::bytes_of;
5use num_traits::cast::FromPrimitive;
6
7use std::sync::Arc;
8
9use crate::{
10    arc_msg::ArcMsgOk,
11    chip::{
12        communication::{chip_comms::ChipComms, chip_interface::ChipInterface},
13        hl_comms::HlCommsInterface,
14    },
15    error::{BtWrapper, PlatformError},
16    ArcMsg, ChipImpl,
17};
18
19use super::{
20    eth_addr::EthAddr,
21    hl_comms::HlComms,
22    init::status::{ComponentStatusInfo, InitOptions, WaitStatus},
23    remote::EthAddresses,
24    ArcMsgOptions, AxiData, ChipInitResult, CommsStatus, InitStatus, NeighbouringChip,
25};
26
27pub mod boot_fs;
28pub mod message;
29pub mod spirom_tables;
30
31#[macro_use]
32pub mod telemetry_tags;
33use crate::chip::blackhole::telemetry_tags::TelemetryTags;
34use prost::Message;
35use serde_json::Value;
36use std::collections::HashMap;
37
38// pub use telemetry_tags::telemetry_tags_to_u32;
39
40fn u32_from_slice(data: &[u8], index: u16) -> u32 {
41    let mut output = 0;
42    let index = index * 4;
43    let data_chunk = &data[index as usize..(index + 4) as usize];
44    for i in data_chunk.iter().rev().copied() {
45        output <<= 8;
46        output |= i as u32;
47    }
48    output
49}
50
51#[derive(Clone)]
52pub struct Blackhole {
53    pub chip_if: Arc<dyn ChipInterface + Send + Sync>,
54    pub arc_if: Arc<dyn ChipComms + Send + Sync>,
55
56    pub message_queue: once_cell::sync::OnceCell<message::MessageQueue<8>>,
57
58    pub eth_locations: [EthCore; 14],
59    pub eth_addrs: EthAddresses,
60
61    spi_buffer_addr: AxiData,
62    telemetry_struct_addr: AxiData,
63    scratch_ram_base: AxiData,
64}
65
66impl HlComms for Blackhole {
67    fn comms_obj(&self) -> (&dyn ChipComms, &dyn ChipInterface) {
68        (self.arc_if.as_ref(), self.chip_if.as_ref())
69    }
70}
71
72impl HlComms for &Blackhole {
73    fn comms_obj(&self) -> (&dyn ChipComms, &dyn ChipInterface) {
74        (self.arc_if.as_ref(), self.chip_if.as_ref())
75    }
76}
77
78#[derive(Clone, Copy, Debug)]
79pub struct EthCore {
80    pub x: u8,
81    pub y: u8,
82    pub enabled: bool,
83}
84
85impl Default for EthCore {
86    fn default() -> Self {
87        Self {
88            x: 0,
89            y: 0,
90            enabled: true,
91        }
92    }
93}
94
95struct SpiBuffer {
96    addr: u32,
97    size: u32,
98}
99
100#[derive(Debug)]
101#[repr(u8)]
102pub enum ArcFwInitStatus {
103    NotStarted = 0,
104    Started = 1,
105    Done = 2,
106    Error = 3,
107    Unknown(u8),
108}
109
110impl From<u8> for ArcFwInitStatus {
111    fn from(value: u8) -> Self {
112        match value {
113            0 => ArcFwInitStatus::NotStarted,
114            1 => ArcFwInitStatus::Started,
115            2 => ArcFwInitStatus::Done,
116            3 => ArcFwInitStatus::Error,
117            other => ArcFwInitStatus::Unknown(other),
118        }
119    }
120}
121
122impl Blackhole {
123    pub(crate) fn init<
124        CC: ChipComms + Send + Sync + 'static,
125        CI: ChipInterface + Send + Sync + 'static,
126    >(
127        arc_if: CC,
128        chip_if: CI,
129    ) -> Result<Self, PlatformError> {
130        // let mut version = [0; 4];
131        // arc_if.axi_read(&chip_if, 0x0, &mut version);
132        // let version = u32::from_le_bytes(version);
133        let _version = 0x0;
134
135        let output = Blackhole {
136            chip_if: Arc::new(chip_if),
137
138            message_queue: once_cell::sync::OnceCell::new(),
139
140            eth_addrs: EthAddresses::default(),
141
142            spi_buffer_addr: arc_if.axi_translate("arc_ss.reset_unit.SCRATCH_RAM[10]")?,
143            telemetry_struct_addr: arc_if.axi_translate("arc_ss.reset_unit.SCRATCH_RAM[13]")?,
144            scratch_ram_base: arc_if.axi_translate("arc_ss.reset_unit.SCRATCH_RAM[0]")?,
145
146            arc_if: Arc::new(arc_if),
147
148            eth_locations: [
149                EthCore {
150                    x: 1,
151                    y: 1,
152                    ..Default::default()
153                },
154                EthCore {
155                    x: 16,
156                    y: 1,
157                    ..Default::default()
158                },
159                EthCore {
160                    x: 2,
161                    y: 1,
162                    ..Default::default()
163                },
164                EthCore {
165                    x: 15,
166                    y: 1,
167                    ..Default::default()
168                },
169                EthCore {
170                    x: 3,
171                    y: 1,
172                    ..Default::default()
173                },
174                EthCore {
175                    x: 14,
176                    y: 1,
177                    ..Default::default()
178                },
179                EthCore {
180                    x: 4,
181                    y: 1,
182                    ..Default::default()
183                },
184                EthCore {
185                    x: 13,
186                    y: 1,
187                    ..Default::default()
188                },
189                EthCore {
190                    x: 5,
191                    y: 1,
192                    ..Default::default()
193                },
194                EthCore {
195                    x: 12,
196                    y: 1,
197                    ..Default::default()
198                },
199                EthCore {
200                    x: 6,
201                    y: 1,
202                    ..Default::default()
203                },
204                EthCore {
205                    x: 11,
206                    y: 1,
207                    ..Default::default()
208                },
209                EthCore {
210                    x: 7,
211                    y: 1,
212                    ..Default::default()
213                },
214                EthCore {
215                    x: 10,
216                    y: 1,
217                    ..Default::default()
218                },
219            ],
220        };
221
222        Ok(output)
223    }
224
225    pub fn init_eth_addrs(&mut self) -> Result<(), PlatformError> {
226        if self.eth_addrs.masked_version == 0 {
227            let telemetry = self.get_telemetry()?;
228
229            self.eth_addrs = EthAddresses::new(telemetry.eth_fw_version);
230        }
231
232        Ok(())
233    }
234
235    pub fn get_if<T: ChipInterface>(&self) -> Option<&T> {
236        self.chip_if.as_any().downcast_ref::<T>()
237    }
238
239    pub fn arc_fw_init_status(&self) -> Option<ArcFwInitStatus> {
240        self.axi_read32(self.scratch_ram_base.addr + (4 * 2))
241            .ok()
242            .map(|boot_status_0| ArcFwInitStatus::from(((boot_status_0 >> 1) & 0x3) as u8))
243    }
244
245    pub fn check_arc_msg_safe(&self) -> bool {
246        // Note that hw_ready can be false while we can safely send an arc_msg
247        // This confuses me a bit because this means you can send arc messages that will potentially poke an uninitialized hw
248        if let Ok(boot_status_0) = self.axi_read32(self.scratch_ram_base.addr + (4 * 2)) {
249            (boot_status_0 & 0x1) == 1
250        } else {
251            false
252        }
253    }
254
255    /// Sends a blackhole ARC message.
256    ///
257    /// # Note
258    ///
259    /// Data is an (up to) 8-word buffer. Code overwrites the LSB of word 0.
260    fn bh_arc_msg(
261        &self,
262        code: u8,
263        data: [u32; 8],
264        timeout: Option<std::time::Duration>,
265    ) -> Result<(u8, u16, [u32; 8]), PlatformError> {
266        if !self.check_arc_msg_safe() {
267            return Err(PlatformError::ArcNotReady(
268                crate::error::ArcReadyError::BootIncomplete,
269                BtWrapper::capture(),
270            ));
271        }
272
273        // Prepare request from data buffer
274        let mut request = [0; 8];
275        for (src, dst) in data.iter().copied().zip(request.iter_mut()) {
276            *dst = src;
277        }
278        // Override message ID
279        //
280        // This is the LSB of the first word.
281        request[0] &= !0xff;
282        request[0] |= u32::from(code);
283
284        let timeout = timeout.unwrap_or(std::time::Duration::from_millis(500));
285
286        let queue = self.message_queue.get_or_try_init::<_, PlatformError>(|| {
287            let message_queue_info_address = self
288                .arc_if
289                .axi_sread32(&self.chip_if, "arc_ss.reset_unit.SCRATCH_RAM[11]")?
290                as u64;
291            let queue_base = self
292                .arc_if
293                .axi_read32(&self.chip_if, message_queue_info_address)?;
294            let queue_sizing = self
295                .arc_if
296                .axi_read32(&self.chip_if, message_queue_info_address + 4)?;
297            let queue_size = queue_sizing & 0xFF;
298            let queue_count = (queue_sizing >> 8) & 0xFF;
299
300            Ok(message::MessageQueue {
301                header_size: 8,
302                entry_size: 8,
303                queue_base: queue_base as u64,
304                queue_size,
305                queue_count,
306                fw_int: self
307                    .arc_if
308                    .axi_translate("arc_ss.reset_unit.ARC_MISC_CNTL.irq0_trig")?,
309            })
310        })?;
311
312        let response = queue.send_message(self, 2, request, timeout)?;
313        let status = (response[0] & 0xFF) as u8;
314        let rc = (response[0] >> 16) as u16;
315
316        if status < 240 {
317            let data = [
318                response[0],
319                response[1],
320                response[2],
321                response[3],
322                response[4],
323                response[5],
324                response[6],
325                response[7],
326            ];
327            Ok((status, rc, data))
328        } else if status == 0xFF {
329            Err(PlatformError::ArcMsgError(
330                crate::ArcMsgError::ProtocolError {
331                    source: crate::ArcMsgProtocolError::MsgNotRecognized(code as u16),
332                    backtrace: BtWrapper::capture(),
333                },
334            ))
335        } else {
336            Err(PlatformError::ArcMsgError(
337                crate::ArcMsgError::ProtocolError {
338                    source: crate::ArcMsgProtocolError::UnknownErrorCode(status),
339                    backtrace: BtWrapper::capture(),
340                },
341            ))
342        }
343    }
344
345    fn get_spi_buffer(&self) -> Result<SpiBuffer, Box<dyn std::error::Error>> {
346        let buffer_addr = self.axi_read32(self.spi_buffer_addr.addr)?;
347
348        Ok(SpiBuffer {
349            addr: (buffer_addr & 0xFFFFFF) + 0x10000000, /* magic offset to translate to the correct buffer address */
350            size: 1 << ((buffer_addr >> 24) & 0xFF),
351        })
352    }
353
354    pub fn spi_write(&self, mut addr: u32, value: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
355        let buffer = self.get_spi_buffer()?;
356
357        /*
358         * Since SPI unlock/lock commands are not supported in older firmwares,
359         * we will ignore errors from them and return a default status
360         */
361        let (status, _, _) = self
362            .bh_arc_msg(0xC2, [0, 0, 0, 0, 0, 0, 0, 0], None)
363            .unwrap_or_default();
364
365        if status != 0 {
366            return Err("Failed to unlock spi".into());
367        }
368
369        for chunk in value.chunks(buffer.size as usize) {
370            self.axi_write(buffer.addr as u64, chunk)?;
371            let (status, _, _) = self.bh_arc_msg(
372                0x1A,
373                [0, addr, chunk.len() as u32, buffer.addr, 0, 0, 0, 0],
374                None,
375            )?;
376
377            std::thread::sleep(std::time::Duration::from_millis(100));
378
379            if status != 0 {
380                return Err("Failed to write to SPI".into());
381            }
382
383            addr += chunk.len() as u32;
384        }
385
386        /* Similar to above, ignore errors from lock command */
387        let (status, _, _) = self
388            .bh_arc_msg(0xC3, [0, 0, 0, 0, 0, 0, 0, 0], None)
389            .unwrap_or_default();
390
391        if status != 0 {
392            return Err("Failed to lock spi".into());
393        }
394
395        Ok(())
396    }
397
398    pub fn spi_read(
399        &self,
400        mut addr: u32,
401        value: &mut [u8],
402    ) -> Result<(), Box<dyn std::error::Error>> {
403        let buffer = self.get_spi_buffer()?;
404
405        for chunk in value.chunks_mut(buffer.size as usize) {
406            let (status, _, _) = self.bh_arc_msg(
407                0x19,
408                [0, addr, chunk.len() as u32, buffer.addr, 0, 0, 0, 0],
409                None,
410            )?;
411
412            if status != 0 {
413                return Err("Failed to read from SPI".into());
414            }
415
416            self.axi_read(buffer.addr as u64, chunk)?;
417
418            addr += chunk.len() as u32;
419        }
420
421        Ok(())
422    }
423
424    pub fn get_local_chip_coord(&self) -> Result<EthAddr, PlatformError> {
425        Ok(EthAddr {
426            rack_x: 0,
427            rack_y: 0,
428            shelf_x: 0,
429            shelf_y: 0,
430        })
431    }
432
433    pub fn get_boot_fs_tables_spi_read(
434        &self,
435        tag_name: &str,
436    ) -> Result<Option<(u32, boot_fs::TtBootFsFd)>, Box<dyn std::error::Error>> {
437        let spi_reader = |addr: u32, size: usize| {
438            let mut buf = vec![0; size];
439            self.spi_read(addr, &mut buf).unwrap();
440            buf
441        };
442        Ok(boot_fs::read_tag(spi_reader, tag_name))
443    }
444
445    pub fn decode_boot_fs_table(
446        &self,
447        tag_name: &str,
448    ) -> Result<HashMap<String, Value>, Box<dyn std::error::Error>> {
449        // Return the decoded boot fs table as a HashMap
450        // Get the spi address and image size of the tag and read the proto bin
451        // Decode the proto bin and convert it to a HashMap
452        let tag_info = self
453            .get_boot_fs_tables_spi_read(tag_name)?
454            .ok_or_else(|| format!("Tag '{tag_name}' not found in boot FS tables"))?;
455        let spi_addr = tag_info.1.spi_addr;
456        let image_size = tag_info.1.flags.image_size();
457
458        // declare as vec to allow non-const size
459        let mut proto_bin = vec![0u8; image_size as usize];
460        self.spi_read(spi_addr, &mut proto_bin)?;
461        let final_decode_map: HashMap<String, Value>;
462        // remove padding
463        proto_bin = spirom_tables::remove_padding_proto_bin(&proto_bin)?.to_vec();
464
465        if tag_name == "cmfwcfg" || tag_name == "origcfg" {
466            final_decode_map =
467                spirom_tables::to_hash_map(spirom_tables::fw_table::FwTable::decode(&*proto_bin)?);
468        } else if tag_name == "boardcfg" {
469            final_decode_map = spirom_tables::to_hash_map(
470                spirom_tables::read_only::ReadOnly::decode(&*proto_bin)?,
471            );
472        } else if tag_name == "flshinfo" {
473            final_decode_map = spirom_tables::to_hash_map(
474                spirom_tables::flash_info::FlashInfoTable::decode(&*proto_bin)?,
475            );
476        } else {
477            return Err(format!("Unsupported tag name: {tag_name}").into());
478        };
479        Ok(final_decode_map)
480    }
481
482    pub fn encode_and_write_boot_fs_table(
483        &self,
484        hashmap: HashMap<String, Value>,
485        tag_name: &str,
486    ) -> Result<(), Box<dyn std::error::Error>> {
487        // Convert the HashMap to a proto message and encode it to a proto bin
488        let mut proto_bin = if tag_name == "cmfwcfg" {
489            spirom_tables::from_hash_map::<spirom_tables::fw_table::FwTable>(hashmap)
490                .encode_to_vec()
491        } else if tag_name == "boardcfg" {
492            spirom_tables::from_hash_map::<spirom_tables::read_only::ReadOnly>(hashmap)
493                .encode_to_vec()
494        } else if tag_name == "flshinfo" {
495            spirom_tables::from_hash_map::<spirom_tables::flash_info::FlashInfoTable>(hashmap)
496                .encode_to_vec()
497        } else {
498            return Err(format!("Unsupported tag name: {tag_name}").into());
499        };
500        // Pad the proto bin to be a multiple of 4 bytes to fit into the spirom requirements
501        let padding = 4 - (proto_bin.len() % 4);
502        for i in 0..padding {
503            proto_bin.push(i as u8);
504        }
505
506        // Write the proto bin to the spirom and update the checksums
507        let tag_info = self
508            .get_boot_fs_tables_spi_read(tag_name)?
509            .ok_or_else(|| format!("Tag '{tag_name}' not found in boot FS tables"))?;
510
511        let mut fd_in_spi = tag_info.1;
512        fd_in_spi.flags.set_image_size(proto_bin.len() as u32);
513
514        let data_chk = spirom_tables::calculate_checksum(&proto_bin);
515        fd_in_spi.data_crc = data_chk;
516        fd_in_spi.fd_crc = 0;
517
518        // do length -4 because of a bug in checksum calculation in bootrom
519        let fd_chk = {
520            let fd_bytes = bytes_of(&fd_in_spi);
521            spirom_tables::calculate_checksum(&fd_bytes[..fd_bytes.len() - 4])
522        };
523        fd_in_spi.fd_crc = fd_chk;
524
525        self.spi_write(tag_info.0, bytes_of(&fd_in_spi))?;
526        self.spi_write(fd_in_spi.spi_addr, &proto_bin)?;
527
528        Ok(())
529    }
530}
531
532fn default_status() -> InitStatus {
533    InitStatus {
534        comms_status: super::CommsStatus::CanCommunicate,
535        arc_status: ComponentStatusInfo {
536            name: "ARC".to_string(),
537            wait_status: Box::new([WaitStatus::Waiting(None)]),
538
539            start_time: std::time::Instant::now(),
540            timeout: std::time::Duration::from_secs(5),
541        },
542        dram_status: ComponentStatusInfo::init_waiting(
543            "DRAM".to_string(),
544            std::time::Duration::from_secs(300),
545            8,
546        ),
547        eth_status: ComponentStatusInfo::init_waiting(
548            "ETH".to_string(),
549            std::time::Duration::from_secs(15 * 60),
550            14,
551        ),
552        cpu_status: ComponentStatusInfo::init_waiting(
553            "CPU".to_string(),
554            std::time::Duration::from_secs(60),
555            4,
556        ),
557
558        init_options: InitOptions { noc_safe: false },
559
560        unknown_state: false,
561    }
562}
563
564impl ChipImpl for Blackhole {
565    // For now just assume the chip is fully working
566    // will implement proper status checking later
567    fn update_init_state(
568        &mut self,
569        status: &mut InitStatus,
570    ) -> Result<ChipInitResult, PlatformError> {
571        if status.unknown_state {
572            let init_options = std::mem::take(&mut status.init_options);
573            *status = default_status();
574            status.init_options = init_options;
575        }
576
577        {
578            let comms = &mut status.comms_status;
579            *comms = match self.axi_sread32("arc_ss.reset_unit.SCRATCH_0") {
580                Ok(_) => CommsStatus::CanCommunicate,
581                Err(err) => CommsStatus::CommunicationError(err.to_string()),
582            }
583        }
584
585        {
586            let status = &mut status.arc_status;
587            for s in status.wait_status.iter_mut() {
588                match s {
589                    WaitStatus::Waiting(status_string) => {
590                        let msg_safe = self.check_arc_msg_safe();
591                        let fw_status = self.arc_fw_init_status();
592
593                        if let Some(fw_status) = fw_status {
594                            match fw_status {
595                                ArcFwInitStatus::NotStarted => {
596                                    *status_string = Some("BH FW boot not started".to_string());
597                                }
598                                ArcFwInitStatus::Started => {
599                                    *status_string = Some("BH FW boot not complete".to_string());
600                                }
601                                ArcFwInitStatus::Done => {
602                                    if !msg_safe {
603                                        *status_string = Some(
604                                            "BH FW arc msg queue init not complete".to_string(),
605                                        );
606                                    } else {
607                                        *s = WaitStatus::JustFinished;
608                                    }
609                                }
610                                ArcFwInitStatus::Error => {
611                                    *status_string = Some("BH FW Boot error".to_string());
612                                    *s = WaitStatus::Error(
613                                        super::init::status::ArcInitError::WaitingForInit(
614                                            crate::error::ArcReadyError::BootError,
615                                        ),
616                                    );
617                                }
618                                ArcFwInitStatus::Unknown(status) => {
619                                    *status_string = Some(format!("BH FW Boot status unknown {status} (will wait to see if it becomes known)"));
620                                }
621                            }
622
623                            // If we are still waiting after changing status and (potentially) reassigning to a hard error
624                            if let WaitStatus::Waiting(_) = s {
625                                // and we timed out, then raise a boot incomplete error
626                                if status.start_time.elapsed() > status.timeout {
627                                    *s = WaitStatus::Error(
628                                        super::init::status::ArcInitError::WaitingForInit(
629                                            crate::error::ArcReadyError::BootIncomplete,
630                                        ),
631                                    );
632                                }
633                            }
634                        } else {
635                            *status_string =
636                                Some("Failed to access fw to read init status".to_string());
637                            *s = WaitStatus::Error(
638                                super::init::status::ArcInitError::WaitingForInit(
639                                    crate::error::ArcReadyError::NoAccess,
640                                ),
641                            );
642                        }
643                    }
644                    WaitStatus::JustFinished => {
645                        *s = WaitStatus::Done;
646                    }
647                    _ => {}
648                }
649            }
650        }
651
652        {
653            let status = &mut status.dram_status;
654            for s in status.wait_status.iter_mut() {
655                *s = WaitStatus::Done;
656            }
657        }
658
659        {
660            let status = &mut status.eth_status;
661            for s in status.wait_status.iter_mut() {
662                *s = WaitStatus::Done;
663            }
664        }
665
666        {
667            let status = &mut status.cpu_status;
668            for s in status.wait_status.iter_mut() {
669                *s = WaitStatus::Done;
670            }
671        }
672
673        Ok(ChipInitResult::NoError)
674    }
675
676    fn get_arch(&self) -> luwen_def::Arch {
677        luwen_def::Arch::Blackhole
678    }
679
680    fn arc_msg(&self, msg: ArcMsgOptions) -> Result<ArcMsgOk, PlatformError> {
681        let code = msg.msg.msg_code();
682        let data = if let ArcMsg::Buf(msg) = msg.msg {
683            msg
684        } else {
685            let args = msg.msg.args();
686            [0, args.0 as u32 | ((args.1 as u32) << 16), 0, 0, 0, 0, 0, 0]
687        };
688
689        let (_status, rc, response) = self.bh_arc_msg(code as u8, data, Some(msg.timeout))?;
690        Ok(match msg.msg {
691            ArcMsg::Buf(_) => ArcMsgOk::OkBuf(response),
692            _ => ArcMsgOk::Ok {
693                rc: rc as u32,
694                arg: response[1],
695            },
696        })
697    }
698
699    fn get_neighbouring_chips(&self) -> Result<Vec<NeighbouringChip>, crate::error::PlatformError> {
700        Ok(vec![])
701    }
702
703    fn as_any(&self) -> &dyn std::any::Any {
704        self
705    }
706
707    fn get_telemetry(&self) -> Result<super::Telemetry, PlatformError> {
708        // Get chip telemetry and device data
709        // Read telemetry data block address from scratch ram
710        // Then read and parse telemetry data
711
712        // Get address of telem struct from scratch ram
713        let mut scratch_reg_13_value = [0u8; 4];
714        self.axi_read_field(&self.telemetry_struct_addr, &mut scratch_reg_13_value)?;
715        let telem_struct_addr = u32::from_le_bytes(scratch_reg_13_value);
716        if telem_struct_addr == 0 {
717            return Err(PlatformError::ArcNotReady(
718                crate::error::ArcReadyError::BootIncomplete,
719                BtWrapper::capture(),
720            ));
721        }
722        // Check if the address is within CSM memory. Otherwise, it must be invalid
723        if !(0x10000000..=0x1007FFFF).contains(&telem_struct_addr) {
724            return Err(PlatformError::Generic(
725                format!("Invalid Telemetry struct address: 0x{telem_struct_addr:08x}"),
726                BtWrapper::capture(),
727            ));
728        }
729
730        // Read the data block from the address in sctrach 13
731        // Parse out the version and entry count before reading the data block
732        let _version = self.axi_read32(telem_struct_addr as u64)?;
733        let entry_count = self.axi_read32(telem_struct_addr as u64 + 4)?;
734
735        // TODO: Implement version check and data block parsing based on version
736        // For now, assume version 1 and parse data block as is
737        // let version = u32::from_le_bytes(version);
738
739        // Get telemetry tags data block and telemetry data data block
740        let mut telemetry_tags_data_block: Vec<u8> = vec![0u8; (entry_count + 1) as usize * 4];
741        let mut telem_data_block: Vec<u8> = vec![0u8; (entry_count + 1) as usize * 4];
742
743        self.axi_read(
744            (telem_struct_addr + 8) as u64,
745            &mut telemetry_tags_data_block,
746        )?;
747        self.axi_read(
748            (telem_struct_addr + 8 + entry_count * 4) as u64,
749            &mut telem_data_block,
750        )?;
751
752        // Parse telemetry data
753        let mut telemetry_data = super::Telemetry::default();
754        for i in 0..entry_count as u16 {
755            let entry = u32_from_slice(&telemetry_tags_data_block, i);
756            let tag = entry & 0xFFFF;
757            let offset = (entry >> 16) & 0xFFFF;
758            let data = u32_from_slice(&telem_data_block, offset as u16);
759            // print!("Tag: {} Data: {:#02x}\n", tag, data);
760            if let Some(tag) = TelemetryTags::from_u32(tag) {
761                match tag {
762                    TelemetryTags::BoardIdHigh => telemetry_data.board_id_high = data,
763                    TelemetryTags::BoardIdLow => telemetry_data.board_id_low = data,
764                    TelemetryTags::AsicId => telemetry_data.asic_id = data,
765                    TelemetryTags::HarvestingState => telemetry_data.harvesting_state = data,
766                    TelemetryTags::UpdateTelemSpeed => telemetry_data.update_telem_speed = data,
767                    TelemetryTags::Vcore => telemetry_data.vcore = data,
768                    TelemetryTags::Tdp => telemetry_data.tdp = data,
769                    TelemetryTags::Tdc => telemetry_data.tdc = data,
770                    TelemetryTags::VddLimits => telemetry_data.vdd_limits = data,
771                    TelemetryTags::ThmLimits => telemetry_data.thm_limits = data,
772                    TelemetryTags::AsicTemperature => telemetry_data.asic_temperature = data,
773                    TelemetryTags::VregTemperature => telemetry_data.vreg_temperature = data,
774                    TelemetryTags::BoardTemperature => telemetry_data.board_temperature = data,
775                    TelemetryTags::AiClk => telemetry_data.aiclk = data,
776                    TelemetryTags::AxiClk => telemetry_data.axiclk = data,
777                    TelemetryTags::ArcClk => telemetry_data.arcclk = data,
778                    TelemetryTags::L2CPUClk0 => telemetry_data.l2cpuclk0 = data,
779                    TelemetryTags::L2CPUClk1 => telemetry_data.l2cpuclk1 = data,
780                    TelemetryTags::L2CPUClk2 => telemetry_data.l2cpuclk2 = data,
781                    TelemetryTags::L2CPUClk3 => telemetry_data.l2cpuclk3 = data,
782                    TelemetryTags::EthLiveStatus => telemetry_data.eth_status0 = data,
783                    TelemetryTags::DdrStatus => telemetry_data.ddr_status = data,
784                    TelemetryTags::DdrSpeed => telemetry_data.ddr_speed = Some(data),
785                    TelemetryTags::EthFwVersion => telemetry_data.eth_fw_version = data,
786                    TelemetryTags::DdrFwVersion => telemetry_data.ddr_fw_version = data,
787                    TelemetryTags::BmAppFwVersion => telemetry_data.m3_app_fw_version = data,
788                    TelemetryTags::BmBlFwVersion => telemetry_data.m3_bl_fw_version = data,
789                    TelemetryTags::FlashBundleVersion => telemetry_data.fw_bundle_version = data,
790                    // TelemetryTags::CM_FW_VERSION => telemetry_data.cm_fw_version = data,
791                    TelemetryTags::L2cpuFwVersion => telemetry_data.l2cpu_fw_version = data,
792                    TelemetryTags::FanSpeed => telemetry_data.fan_speed = data,
793                    TelemetryTags::TimerHeartbeat => {
794                        telemetry_data.timer_heartbeat = data;
795                    }
796                    TelemetryTags::TelemEnumCount => telemetry_data.entry_count = data,
797                    TelemetryTags::EnabledTensixCol => telemetry_data.tensix_enabled_col = data,
798                    TelemetryTags::EnabledEth => telemetry_data.enabled_eth = data,
799                    TelemetryTags::EnabledGddr => telemetry_data.enabled_gddr = data,
800                    TelemetryTags::EnabledL2Cpu => telemetry_data.enabled_l2cpu = data,
801                    TelemetryTags::PcieUsage => telemetry_data.enabled_pcie = data,
802                    TelemetryTags::NocTranslation => {
803                        telemetry_data.noc_translation_enabled = data != 0
804                    }
805                    TelemetryTags::FanRpm => telemetry_data.fan_rpm = data,
806                    TelemetryTags::Gddr01Temp => telemetry_data.gddr01_temp = data,
807                    TelemetryTags::Gddr23Temp => telemetry_data.gddr23_temp = data,
808                    TelemetryTags::Gddr45Temp => telemetry_data.gddr45_temp = data,
809                    TelemetryTags::Gddr67Temp => telemetry_data.gddr67_temp = data,
810                    TelemetryTags::Gddr01CorrErrs => telemetry_data.gddr01_corr_errs = data,
811                    TelemetryTags::Gddr23CorrErrs => telemetry_data.gddr23_corr_errs = data,
812                    TelemetryTags::Gddr45CorrErrs => telemetry_data.gddr45_corr_errs = data,
813                    TelemetryTags::Gddr67CorrErrs => telemetry_data.gddr67_corr_errs = data,
814                    TelemetryTags::GddrUncorrErrs => telemetry_data.gddr_uncorr_errs = data,
815                    TelemetryTags::MaxGddrTemp => telemetry_data.max_gddr_temp = data,
816                    TelemetryTags::AsicLocation => telemetry_data.asic_location = data,
817                    TelemetryTags::BoardPowerLimit => telemetry_data.board_power_limit = data,
818                    TelemetryTags::InputPower => telemetry_data.input_power = data,
819                    TelemetryTags::TdcLimitMax => telemetry_data.tdc_limit_max = data,
820                    TelemetryTags::ThmLimitThrottle => telemetry_data.thm_limit_throttle = data,
821                    TelemetryTags::ThermTripCount => telemetry_data.therm_trip_count = data,
822                    TelemetryTags::AsicIdHigh => telemetry_data.asic_id_high = data,
823                    TelemetryTags::AsicIdLow => telemetry_data.asic_id_low = data,
824                    TelemetryTags::AiclkLimitMax => telemetry_data.aiclk_limit_max = data,
825                    TelemetryTags::TdpLimitMax => telemetry_data.tdp_limit_max = data,
826                    _ => (),
827                }
828            }
829        }
830        telemetry_data.board_id =
831            ((telemetry_data.board_id_high as u64) << 32) | telemetry_data.board_id_low as u64;
832        telemetry_data.arch = self.get_arch();
833        Ok(telemetry_data)
834    }
835
836    fn get_device_info(&self) -> Result<Option<crate::DeviceInfo>, PlatformError> {
837        Ok(self.chip_if.get_device_info()?)
838    }
839}