use crate::cpu::info::Vendor;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Microarch {
Willamette,
Northwood,
Prescott,
Nehalem,
Westmere,
SandyBridge,
IvyBridge,
Haswell,
Broadwell,
Skylake,
KabyLake,
CometLake,
CannonLake,
IceLake,
TigerLake,
AlderLake,
RaptorLake,
MeteorLake,
SapphireRapids,
GraniteRapids,
K8,
K10,
Bobcat,
Bulldozer,
Piledriver,
Steamroller,
Excavator,
Jaguar,
Zen,
ZenPlus,
Zen2,
Hygon,
Zen3,
Zen3Plus,
Zen4,
Zen5,
AppleM1,
AppleM2,
AppleM3,
AppleM4,
}
impl Microarch {
#[must_use]
pub fn process_nm(&self) -> Option<u32> {
match self {
Microarch::Willamette => Some(180),
Microarch::Northwood | Microarch::K8 => Some(130),
Microarch::Prescott => Some(90),
Microarch::Nehalem => Some(45),
Microarch::K10 => Some(65),
Microarch::Bobcat => Some(40),
Microarch::Westmere | Microarch::SandyBridge | Microarch::Bulldozer | Microarch::Piledriver => Some(32),
Microarch::IvyBridge | Microarch::Haswell => Some(22),
Microarch::Steamroller | Microarch::Excavator | Microarch::Jaguar => Some(28),
Microarch::Broadwell
| Microarch::Skylake
| Microarch::KabyLake
| Microarch::CometLake
| Microarch::Zen
| Microarch::Hygon => Some(14),
Microarch::CannonLake
| Microarch::IceLake
| Microarch::TigerLake
| Microarch::AlderLake
| Microarch::RaptorLake
| Microarch::SapphireRapids => Some(10),
Microarch::ZenPlus => Some(12),
Microarch::Zen2 | Microarch::Zen3 => Some(7),
Microarch::Zen3Plus => Some(6),
Microarch::Zen4 | Microarch::AppleM1 | Microarch::AppleM2 => Some(5), Microarch::MeteorLake | Microarch::Zen5 => Some(4),
Microarch::GraniteRapids | Microarch::AppleM3 | Microarch::AppleM4 => Some(3), }
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Microarch::Willamette => "Willamette",
Microarch::Northwood => "Northwood",
Microarch::Prescott => "Prescott",
Microarch::Nehalem => "Nehalem",
Microarch::Westmere => "Westmere",
Microarch::SandyBridge => "Sandy Bridge",
Microarch::IvyBridge => "Ivy Bridge",
Microarch::Haswell => "Haswell",
Microarch::Broadwell => "Broadwell",
Microarch::Skylake => "Skylake",
Microarch::KabyLake => "Kaby Lake",
Microarch::CometLake => "Comet Lake",
Microarch::CannonLake => "Cannon Lake",
Microarch::IceLake => "Ice Lake",
Microarch::TigerLake => "Tiger Lake",
Microarch::AlderLake => "Alder Lake",
Microarch::RaptorLake => "Raptor Lake",
Microarch::MeteorLake => "Meteor Lake",
Microarch::SapphireRapids => "Sapphire Rapids",
Microarch::GraniteRapids => "Granite Rapids",
Microarch::K8 => "K8",
Microarch::K10 => "K10",
Microarch::Bobcat => "Bobcat",
Microarch::Bulldozer => "Bulldozer",
Microarch::Piledriver => "Piledriver",
Microarch::Steamroller => "Steamroller",
Microarch::Excavator => "Excavator",
Microarch::Jaguar => "Jaguar",
Microarch::Zen => "Zen",
Microarch::ZenPlus => "Zen+",
Microarch::Zen2 => "Zen 2",
Microarch::Hygon => "Hygon",
Microarch::Zen3 => "Zen 3",
Microarch::Zen3Plus => "Zen 3+",
Microarch::Zen4 => "Zen 4",
Microarch::Zen5 => "Zen 5",
Microarch::AppleM1 => "Apple M1",
Microarch::AppleM2 => "Apple M2",
Microarch::AppleM3 => "Apple M3",
Microarch::AppleM4 => "Apple M4",
}
}
}
impl fmt::Display for Microarch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[must_use]
pub fn detect_uarch(vendor: &Vendor, family: u8, model: u8) -> Option<Microarch> {
match vendor {
Vendor::Intel => detect_intel_uarch(family, model),
Vendor::AMD => detect_amd_uarch(family, model),
_ => None,
}
}
fn detect_intel_uarch(family: u8, model: u8) -> Option<Microarch> {
match family {
15 => match model {
0 | 1 => Some(Microarch::Willamette),
2 => Some(Microarch::Northwood),
3 | 4 | 6 => Some(Microarch::Prescott),
_ => None,
},
6 => match model {
0x1A | 0x1E | 0x1F | 0x2E => Some(Microarch::Nehalem),
0x25 | 0x27 | 0x2C | 0x2F => Some(Microarch::Westmere),
0x2A | 0x2D => Some(Microarch::SandyBridge),
0x3A | 0x3E => Some(Microarch::IvyBridge),
0x3C | 0x3F | 0x45 | 0x46 => Some(Microarch::Haswell),
0x3D | 0x47 | 0x4F | 0x56 => Some(Microarch::Broadwell),
0x4E | 0x5E | 0x55 => Some(Microarch::Skylake),
0x8E | 0x9E => Some(Microarch::KabyLake),
0xA5 | 0xA6 => Some(Microarch::CometLake),
0x66 => Some(Microarch::CannonLake),
0x7D | 0x7E | 0x6A | 0x6C => Some(Microarch::IceLake),
0x8C | 0x8D => Some(Microarch::TigerLake),
0x97 | 0x9A | 0xBE => Some(Microarch::AlderLake),
0xB7 | 0xBF | 0xBA => Some(Microarch::RaptorLake),
0xAA | 0xAC => Some(Microarch::MeteorLake),
0xCF | 0x8F => Some(Microarch::SapphireRapids),
0xAD | 0xAE => Some(Microarch::GraniteRapids),
_ => None,
},
_ => None,
}
}
fn detect_amd_uarch(family: u8, model: u8) -> Option<Microarch> {
match family {
15 => Some(Microarch::K8),
16 | 18 => Some(Microarch::K10),
20 => Some(Microarch::Bobcat),
21 => match model {
0x02 | 0x10..=0x1F => Some(Microarch::Piledriver),
0x30..=0x3F => Some(Microarch::Steamroller),
0x60..=0x7F => Some(Microarch::Excavator),
_ => Some(Microarch::Bulldozer),
},
22 => Some(Microarch::Jaguar),
23 => match model {
0x08 | 0x18 => Some(Microarch::ZenPlus), 0x20 | 0x31 | 0x47 | 0x60 | 0x68 | 0x71 | 0x90 => Some(Microarch::Zen2), _ => Some(Microarch::Zen), },
24 => Some(Microarch::Hygon),
25 => match model {
0x40 | 0x44 => Some(Microarch::Zen3Plus), 0x61 | 0x74 | 0x78 => Some(Microarch::Zen4), _ => Some(Microarch::Zen3), },
26 => Some(Microarch::Zen5),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_intel_nehalem() {
assert_eq!(detect_uarch(&Vendor::Intel, 6, 0x1A), Some(Microarch::Nehalem));
}
#[test]
fn test_intel_raptor_lake() {
assert_eq!(detect_uarch(&Vendor::Intel, 6, 0xB7), Some(Microarch::RaptorLake));
}
#[test]
fn test_intel_sapphire_rapids() {
assert_eq!(detect_uarch(&Vendor::Intel, 6, 0xCF), Some(Microarch::SapphireRapids));
}
#[test]
fn test_amd_zen3() {
assert_eq!(detect_uarch(&Vendor::AMD, 25, 0x21), Some(Microarch::Zen3));
}
#[test]
fn test_amd_zen5() {
assert_eq!(detect_uarch(&Vendor::AMD, 26, 0), Some(Microarch::Zen5));
}
#[test]
fn test_amd_hygon() {
assert_eq!(detect_uarch(&Vendor::AMD, 24, 0), Some(Microarch::Hygon));
}
#[test]
fn test_microarch_display() {
assert_eq!(Microarch::Zen3.to_string(), "Zen 3");
assert_eq!(Microarch::SandyBridge.to_string(), "Sandy Bridge");
assert_eq!(Microarch::ZenPlus.to_string(), "Zen+");
}
#[test]
fn test_process_nm() {
assert_eq!(Microarch::Zen3.process_nm(), Some(7));
assert_eq!(Microarch::Zen5.process_nm(), Some(4));
assert_eq!(Microarch::Haswell.process_nm(), Some(22));
assert_eq!(Microarch::RaptorLake.process_nm(), Some(10));
}
#[test]
fn test_unknown_vendor_returns_none() {
assert_eq!(detect_uarch(&Vendor::ARM, 6, 0x97), None);
}
}