Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

177 changes: 177 additions & 0 deletions aal/src/fuse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/
//
// Copyright 2026 Oxide Computer Company

//! Fuse data types for Tofino ASICs.
//!
//! Chip revision info is exposed in oximeter metrics via the `asic_lot` field
//! (e.g., "FL1234-B1").
//!
//! TODO: Add a dedicated `asic_chip_rev` field to oximeter timeseries when
//! reconfigurator supports oximeter schema migration. Currently there is no
//! window during online updates where all switches stop producing data, so the
//! old schema cannot be safely updated.

use schemars::JsonSchema;
use serde::Serialize;

/// Chip revision derived from device_id and rev_num fuse fields.
#[derive(Clone, Debug, JsonSchema, Serialize)]
pub struct ChipRevision {
/// Computed revision string (e.g., "A0", "B0", "B1").
pub rev: String,
/// Raw device ID from fuse.
pub device_id: u16,
/// Raw revision number from fuse.
pub rev_num: u8,
}

impl ChipRevision {
/// Compute chip revision from device_id and rev_num.
pub fn from_fuse(device_id: u16, rev_num: u8) -> Self {
let rev = match device_id {
0x0100 => "A0".to_string(),
0x0110 => match rev_num {
0 => "B0".to_string(),
2 => "B1".to_string(),
_ => format!("{:04x}", device_id),
},
_ => format!("{:04x}", device_id),
};
Self { rev, device_id, rev_num }
}
}

/// Part identification from fuse data.
#[derive(Clone, Debug, JsonSchema, Serialize)]
pub struct PartInfo {
/// Part number (13 bits).
pub part_num: u16,
/// Package ID (2 bits).
pub pkg_id: u8,
/// Fuse version (2 bits).
pub version: u8,
}

/// Features disabled via fuse programming.
#[derive(Clone, Debug, JsonSchema, Serialize)]
pub struct DisabledFeatures {
/// Disabled pipes (4-bit bitmap).
pub pipes: u8,
/// Disabled ports (40-bit bitmap).
pub ports: u64,
/// Disabled speeds (64-bit bitmap).
pub speeds: u64,
/// Disabled MAUs per pipe (21 bits each).
pub mau: [u32; 4],
/// Disabled traffic manager memory (32-bit bitmap).
pub tm_mem: u32,
/// Buffer sync disabled.
pub bsync: bool,
/// Packet generator disabled.
pub pgen: bool,
/// Resubmit disabled.
pub resub: bool,
}

/// Frequency settings from fuse data.
#[derive(Clone, Debug, JsonSchema, Serialize)]
pub struct FrequencySettings {
/// Frequency disabled.
pub disabled: bool,
/// Backplane port speed frequency (2 bits).
pub bps: u8,
/// Packet processing speed frequency (2 bits).
pub pps: u8,
/// Extended backplane frequency (4 bits).
pub bps_ext: u8,
/// Extended packet speed frequency (4 bits).
pub pps_ext: u8,
/// PCIe disabled (2 bits).
pub pcie_dis: u8,
/// CPU speed disabled (2 bits).
pub cpu_speed_dis: u8,
}

/// Manufacturing and repair data from fuse.
#[derive(Clone, Debug, JsonSchema, Serialize)]
pub struct ManufacturingData {
/// Voltage scaling value (12 bits).
pub voltage_scaling: u16,
/// PMRO and skew value (12 bits).
pub pmro_and_skew: u16,
/// Die rotation.
pub die_rotation: bool,
/// Silent spin (2 bits).
pub silent_spin: u8,
/// Wafer core repair applied.
pub wf_core_repair: bool,
/// Core repair applied.
pub core_repair: bool,
/// Tile repair applied.
pub tile_repair: bool,
/// Soft pipe disable (4 bits).
pub soft_pipe_dis: u8,
}

/// Organized fuse data from the Tofino ASIC.
#[derive(Clone, Debug, JsonSchema, Serialize)]
pub struct FuseData {
/// Chip revision information.
pub chip_rev: ChipRevision,
/// Part identification.
pub part: PartInfo,
/// Disabled features.
pub disabled: DisabledFeatures,
/// Frequency settings.
pub frequency: FrequencySettings,
/// Manufacturing and repair data.
pub manufacturing: ManufacturingData,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn chip_revision_a0() {
let rev = ChipRevision::from_fuse(0x0100, 0);
assert_eq!(rev.rev, "A0");
assert_eq!(rev.device_id, 0x0100);
assert_eq!(rev.rev_num, 0);
}

#[test]
fn chip_revision_b0() {
let rev = ChipRevision::from_fuse(0x0110, 0);
assert_eq!(rev.rev, "B0");
assert_eq!(rev.device_id, 0x0110);
assert_eq!(rev.rev_num, 0);
}

#[test]
fn chip_revision_b1() {
let rev = ChipRevision::from_fuse(0x0110, 2);
assert_eq!(rev.rev, "B1");
assert_eq!(rev.device_id, 0x0110);
assert_eq!(rev.rev_num, 2);
}

#[test]
fn chip_revision_unknown_rev_num() {
let rev = ChipRevision::from_fuse(0x0110, 5);
assert_eq!(rev.rev, "0110");
assert_eq!(rev.device_id, 0x0110);
assert_eq!(rev.rev_num, 5);
}

#[test]
fn chip_revision_unknown_device_id() {
let rev = ChipRevision::from_fuse(0x0200, 0);
assert_eq!(rev.rev, "0200");
assert_eq!(rev.device_id, 0x0200);
assert_eq!(rev.rev_num, 0);
}
}
5 changes: 5 additions & 0 deletions aal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use thiserror::Error;

use common::ports::{PortFec, PortMedia, PortPrbsMode, PortSpeed, TxEq};

mod fuse;
pub use fuse::*;

mod match_action;
pub use match_action::*;

Expand Down Expand Up @@ -43,8 +46,10 @@ pub trait SidecarIdentifiers {
fn asic_backend(&self) -> &str;
fn fab(&self) -> Option<char>;
fn lot(&self) -> Option<char>;
fn lotnum(&self) -> Option<[char; 4]>;
fn wafer(&self) -> Option<u8>;
fn wafer_loc(&self) -> Option<(i16, i16)>;
fn fuse_info(&self) -> Option<FuseData>;
}

/// Error type conveying additional information about ASIC errors
Expand Down
5 changes: 4 additions & 1 deletion asic/src/chaos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,10 @@ impl AsicOps for Handle {

fn get_sidecar_identifiers(&self) -> AsicResult<impl SidecarIdentifiers> {
unfurl!(self, get_sidecar_identifiers);
Ok(Identifiers::default())
Ok(Identifiers {
fuse: Some(crate::faux_fuse_data()),
..Default::default()
})
}
}

Expand Down
56 changes: 56 additions & 0 deletions asic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,44 @@ use uuid::Uuid;
#[cfg(not(feature = "tofino_asic"))]
mod faux_fsm;

/// Returns faux fuse data for non-hardware backends.
#[cfg(not(feature = "tofino_asic"))]
pub(crate) fn faux_fuse_data() -> aal::FuseData {
aal::FuseData {
chip_rev: aal::ChipRevision::from_fuse(0x0110, 2), // B1
part: aal::PartInfo { part_num: 0x1234, pkg_id: 1, version: 2 },
disabled: aal::DisabledFeatures {
pipes: 0,
ports: 0,
speeds: 0,
mau: [0; 4],
tm_mem: 0,
bsync: false,
pgen: false,
resub: false,
},
frequency: aal::FrequencySettings {
disabled: false,
bps: 3,
pps: 3,
bps_ext: 0,
pps_ext: 0,
pcie_dis: 0,
cpu_speed_dis: 0,
},
manufacturing: aal::ManufacturingData {
voltage_scaling: 0,
pmro_and_skew: 0,
die_rotation: false,
silent_spin: 0,
wf_core_repair: false,
core_repair: false,
tile_repair: false,
soft_pipe_dis: 0,
},
}
}

/// Identifiers are used to uniquely identify an ASIC.
///
/// This includes identifiers the sidecar idfor the fab, lot, wafer, and
Expand All @@ -36,11 +74,19 @@ pub struct Identifiers {
fab: Option<char>,
/// Lot identifier.
lot: Option<char>,
/// Lot number (4-character identifier within the lot).
///
/// The 4-character size comes from the Tofino ASIC fuse layout, which
/// stores lotnum as four separate character fields (lotnum0-3) in
/// `tofino::fuse::ChipId`.
lotnum: Option<[char; 4]>,
/// Wafer number within the lot.
wafer: Option<u8>,
/// The wafer location as (x, y) coordinates on the wafer, represented as
/// an array due to the lack of tuple support in OpenAPI.
wafer_loc: Option<(i16, i16)>,
/// Organized fuse data from the ASIC.
fuse: Option<aal::FuseData>,
}

impl Default for Identifiers {
Expand All @@ -50,8 +96,10 @@ impl Default for Identifiers {
asic_backend: "chaos".to_string(),
fab: None,
lot: None,
lotnum: None,
wafer: None,
wafer_loc: None,
fuse: None,
}
}
}
Expand All @@ -73,13 +121,21 @@ impl aal::SidecarIdentifiers for Identifiers {
self.lot
}

fn lotnum(&self) -> Option<[char; 4]> {
self.lotnum
}

fn wafer(&self) -> Option<u8> {
self.wafer
}

fn wafer_loc(&self) -> Option<(i16, i16)> {
self.wafer_loc
}

fn fuse_info(&self) -> Option<aal::FuseData> {
self.fuse.clone()
}
}

/// A collections of counters reflecting the number of times each
Expand Down
2 changes: 2 additions & 0 deletions asic/src/softnpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,10 @@ impl AsicOps for Handle {
asic_backend: "softnpu".to_string(),
fab: None,
lot: None,
lotnum: None,
wafer: None,
wafer_loc: None,
fuse: Some(crate::faux_fuse_data()),
})
}

Expand Down
53 changes: 53 additions & 0 deletions asic/src/tofino_asic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,18 +224,71 @@ impl AsicOps for Handle {
let sidecar_id = uuid::Uuid::from_u128(fuse.chip_id as u128);
let chip_id: ChipId = fuse.chip_id.into();

let fuse_info = aal::FuseData {
chip_rev: aal::ChipRevision::from_fuse(
fuse.device_id as u16,
fuse.rev_num as u8,
),
part: aal::PartInfo {
part_num: fuse.part_num as u16,
pkg_id: fuse.pkg_id as u8,
version: fuse.version as u8,
},
disabled: aal::DisabledFeatures {
pipes: fuse.pipe_dis as u8,
ports: fuse.port_dis,
speeds: fuse.speed_dis,
mau: [
fuse.pipe0_mau_dis as u32,
fuse.pipe1_mau_dis as u32,
fuse.pipe2_mau_dis as u32,
fuse.pipe3_mau_dis as u32,
],
tm_mem: fuse.tm_mem_dis as u32,
bsync: fuse.bsync_dis != 0,
pgen: fuse.pgen_dis != 0,
resub: fuse.resub_dis != 0,
},
frequency: aal::FrequencySettings {
disabled: fuse.freq_dis != 0,
bps: fuse.freq_bps as u8,
pps: fuse.freq_pps as u8,
bps_ext: fuse.freq_bps_2 as u8,
pps_ext: fuse.freq_pps_2 as u8,
pcie_dis: fuse.pcie_dis as u8,
cpu_speed_dis: fuse.cpu_speed_dis as u8,
},
manufacturing: aal::ManufacturingData {
voltage_scaling: fuse.voltage_scaling as u16,
pmro_and_skew: fuse.pmro_and_skew as u16,
die_rotation: fuse.die_rotation != 0,
silent_spin: fuse.silent_spin as u8,
wf_core_repair: fuse.wf_core_repair != 0,
core_repair: fuse.core_repair != 0,
tile_repair: fuse.tile_repair != 0,
soft_pipe_dis: fuse.soft_pipe_dis as u8,
},
};

Ok(Identifiers {
id: sidecar_id,
asic_backend: "tofino_asic".to_string(),
fab: Some(chip_id.fab),
lot: Some(chip_id.lot),
lotnum: Some([
chip_id.lotnum0,
chip_id.lotnum1,
chip_id.lotnum2,
chip_id.lotnum3,
]),
wafer: Some(chip_id.wafer),
wafer_loc: Some(wafer_loc_from_coords(
chip_id.xsign,
chip_id.x,
chip_id.ysign,
chip_id.y,
)),
fuse: Some(fuse_info),
})
}

Expand Down
Loading