#!/usr/bin/python3
# -*- coding: utf-8 -*-
# armcpu identifies known ARM64 CPU models.
# Copyright 2023,2024,2025,2026 by Todd Allen.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
"""\
armcpu identifies known ARM64 CPU models.
"""
__author__     = "Todd Allen"
__copyright__  = "Copyright 2023, 2024, 2025, 2026, Todd Allen"
__license__    = "GPLv2+"
__version__    = "20260415"
__maintainer__ = "Todd Allen"
__email__      = "armcpu@etallen.com"
__status__     = "Production"

import argparse
import sys
import os

################################################################################

#
# From "ARM Architecture Reference Manual ARMv8":
#
# There are 5 bit fields in the ARM MIDR_EL1 system register:
#    impl         (24-31) : implementer code
#    architecture (16-19) : not used in this tool
#    part         ( 4-15) : implementer-defined
#    variant      (20-23) : implementer-defined
#    revision     ( 0- 3) : implementer-defined
#
# In the linux kernel, MIDR_EL1 is read_cpuid_id() or cpuinfo->reg_midr.
# /proc/cpuinfo dumps those values for userland.
#
# Comments indicate information sources.  Common shorthand:
#    CTRM*  = Arm {name} Core Technical Reference Manual,
#                 MIDR_EL1, Main ID Register, El1 | CPUID Base Register
#                 Bit field descriptions
#                 (or similar)
#    LX*    = Linux kernel (git): arch/arm64/include/asm/cputype.h
#    gcc*   = gcc (git): gcc/config/aarch64/aarch64-cores.def
#    llvm*  = llvm (git): llvm/lib/TargetParser/Host.cpp
#    Apple* = apple-oss-distributions/xnu (git): osfmk/arm/cpuid.h
#    Julia* = julia (git): src/processor_arm.cpp 
#                   (removed in d5c76d6dcbe17d7ee01720a5ce5b3f4ab65b32e7)
#    lscpu* = util-linux (git): sys-utils/lscpu-arm.c
# NOTE: One might think that one could just use lscpu's "Model name".  But it
#       appears to be built incorrectly on some distros (e.g. L4T), reporting
#       only "ARMv8 Processor rev X (v8l)".  So, it's unreliable.  :(
#

# Ampere decided to foray into Latin-1 for its identifying character:
#    LATIN CAPITAL LETTER A WITH GRAVE
Amp = chr(0xC0)

variants = {
   ( 'H', 0xd02, 0x2 ) : "HiSilicon TaiShan v120", # https://github.com/ThomasKaiser/sbc-bench
   ( 'P', 0x000, 0x0 ) : "APM X-Gene (Potenza)",   # APM Mustang system
   ( 'P', 0x000, 0x3 ) : "Ampere eMAG",            # MiTAC Raptor system
   ( 'S', 0x001, 0x4 ) : "Samsung Exynos M2",      # Julia*
}

parts = {
   ( 'A', 0xd00 ) : "Aarch64 Foundation",   # LX*
   ( 'A', 0xd01 ) : "ARM Cortex-A32",       # CTRM*
   ( 'A', 0xd02 ) : "ARM Cortex-A34",       # CTRM*
   ( 'A', 0xd03 ) : "ARM Cortex-A53",       # CTRM*
   ( 'A', 0xd04 ) : "ARM Cortex-A35",       # CTRM*
   ( 'A', 0xd05 ) : "ARM Cortex-A55",       # CTRM*
   ( 'A', 0xd06 ) : "ARM Cortex-A65",       # CTRM*
   ( 'A', 0xd07 ) : "ARM Cortex-A57",       # CTRM*, Jetson TX1/TX2 systems
   ( 'A', 0xd08 ) : "ARM Cortex-A72",       # CTRM*
   ( 'A', 0xd09 ) : "ARM Cortex-A73",       # CTRM*
   ( 'A', 0xd0a ) : "ARM Cortex-A75",       # CTRM*
   ( 'A', 0xd0b ) : "ARM Cortex-A76",       # CTRM*
   ( 'A', 0xd0c ) : "ARM Neoverse N1",      # CTRM*
   ( 'A', 0xd0d ) : "ARM Cortex-A77",       # CTRM*
   ( 'A', 0xd0e ) : "ARM Cortex-A76AE",     # CTRM*
   ( 'A', 0xd0f ) : "ARMv8 \"fast model\"", # LX*
   ( 'A', 0xd13 ) : "ARM Cortex-R52",       # CTRM*
   ( 'A', 0xd14 ) : "ARM Cortex-R82AE",     # CTRM*
   ( 'A', 0xd15 ) : "ARM Cortex-R82",       # CTRM*
   ( 'A', 0xd16 ) : "ARM Cortex-R52+",      # CTRM*
   ( 'A', 0xd20 ) : "ARM Cortex-M23",       # CTRM*
   ( 'A', 0xd21 ) : "ARM Cortex-M33",       # CTRM*
   ( 'A', 0xd22 ) : "ARM Cortex-M55",       # CTRM*
   ( 'A', 0xd23 ) : "ARM Cortex-M85",       # CTRM*
   ( 'A', 0xd40 ) : "ARM Neoverse V1",      # CTRM*
   ( 'A', 0xd41 ) : "ARM Cortex-A78",       # CTRM*
   ( 'A', 0xd42 ) : "ARM Cortex-A78AE",     # CTRM*
   ( 'A', 0xd43 ) : "ARM Cortex-A65AE",     # CTRM*
   ( 'A', 0xd44 ) : "ARM Cortex-X1",        # CTRM*
   ( 'A', 0xd46 ) : "ARM Cortex-A510",      # CTRM*
   ( 'A', 0xd47 ) : "ARM Cortex-A710",      # CTRM*
   ( 'A', 0xd48 ) : "ARM Cortex-X2",        # CTRM*
   ( 'A', 0xd49 ) : "ARM Neoverse N2",      # CTRM*
   ( 'A', 0xd4a ) : "ARM Neoverse E1",      # CTRM*
   ( 'A', 0xd4b ) : "ARM Cortex-A78C",      # CTRM*, Jetson Orin system
   ( 'A', 0xd4c ) : "ARM Cortex-X1C",       # CTRM*
   ( 'A', 0xd4d ) : "ARM Cortex-A715",      # CTRM*
   ( 'A', 0xd4e ) : "ARM Cortex-X3",        # CTRM*
   ( 'A', 0xd4f ) : "ARM Neoverse V2",      # CTRM*
   ( 'A', 0xd80 ) : "ARM Cortex-A520",      # CTRM*
   ( 'A', 0xd81 ) : "ARM Cortex-A720",      # CTRM*
   ( 'A', 0xd82 ) : "ARM Cortex-X4",        # CTRM*
   ( 'A', 0xd83 ) : "ARM Neoverse V3AE",    # CTRM*
   ( 'A', 0xd84 ) : "ARM Neoverse V3",      # CTRM*
   ( 'A', 0xd85 ) : "ARM Cortex-X925",      # CTRM*
   ( 'A', 0xd87 ) : "ARM Cortex-A725",      # CTRM*
   ( 'A', 0xd88 ) : "ARM Cortex-A520AE",    # CTRM*
   ( 'A', 0xd89 ) : "ARM Cortex-A720AE",    # CTRM*
   ( 'A', 0xd8a ) : "ARM C1-Nano",          # CTRM* (online only)
   ( 'A', 0xd8b ) : "ARM C1-Pro",           # CTRM*
   ( 'A', 0xd8c ) : "ARM C1-Ultra",         # CTRM*
   ( 'A', 0xd8e ) : "ARM Neoverse N3",      # CTRM*
   ( 'A', 0xd8f ) : "ARM Cortex-A320",      # CTRM*
   ( 'A', 0xd90 ) : "ARM C1-Premium",       # CTRM*
   ( 'B', 0x516 ) : "Broadcom Vulcan",      # LX*
   ( 'C', 0x0a1 ) : "Cavium Thunder X1 (pass 1.0)",     # LX*
   ( 'C', 0x0a2 ) : "Cavium Thunder X1 81xx",           # LX*
   ( 'C', 0x0a3 ) : "Cavium Thunder X1 83xx",           # LX*
   ( 'C', 0x0af ) : "Cavium Thunder X2",                # Gigabyte R181-T90 system, LX*
   ( 'C', 0x0b0 ) : "Cavium/Marvell Octeon TX2",        # gcc*
   ( 'C', 0x0b1 ) : "Cavium/Marvell Octeon TX2 98xx",   # LX*
   ( 'C', 0x0b2 ) : "Cavium/Marvell Octeon TX2 96xx",   # LX*
   ( 'C', 0x0b3 ) : "Cavium/Marvell Octeon TX2 95xx",   # LX*
   ( 'C', 0x0b4 ) : "Cavium/Marvell Octeon TX2 95xxN",  # LX*
   ( 'C', 0x0b5 ) : "Cavium/Marvell Octeon TX2 95xxMM", # LX*
   ( 'C', 0x0b6 ) : "Cavium/Marvell Octeon TX2 95xxO",  # LX*
   ( 'C', 0x0b8 ) : "Cavium/Marvell Thunder X3 T110",   # gcc*
   ( 'F', 0x001 ) : "Fujitsu A64FX",            # LX*
   ( 'F', 0x003 ) : "Fujitsu Monaka",           # gcc*
   ( 'H', 0xd01 ) : "HiSilicon TaiShan v110",   # LX*
   ( 'H', 0xd02 ) : "HiSilicon TaiShan v200",   # https://lkml.org/lkml/2021/11/26/188 (v120 in variants)
   ( 'H', 0xd06 ) : "HiSilicon HIP12",          # gcc*, https://lore.kernel.org/r/20250425033845.57671-2-yangyicong@huawei.com
   ( 'H', 0xd40 ) : "HiSilicon Cortex A-76",    # https://www.reddit.com/user/tkaiser-1/
   ( 'H', 0xd41 ) : "HiSilicon Cortex A-77",    # https://www.reddit.com/user/tkaiser-1/
   ( 'H', 0xd42 ) : "HiSilicon TaiShan v120",   # https://github.com/ThomasKaiser/sbc-bench
   ( 'N', 0x000 ) : "NVIDIA Denver 1.0 rev 0",  # Julia*
   ( 'N', 0x003 ) : "NVIDIA Denver 2.0",        # Jetson TX2 system, LX*
   ( 'N', 0x004 ) : "NVIDIA Carmel",            # Jetson Xavier system, LX*
   ( 'N', 0x010 ) : "NVIDIA Olympus",           # gcc*, llvm*
   ( 'Q', 0x001 ) : "Qualcomm Oryon X1",                # LX*
   ( 'Q', 0x800 ) : "Qualcomm Falkor V1/Kryo 2xx Gold", # LX*
   ( 'Q', 0x801 ) : "Qualcomm Kryo 2xx Silver",         # LX*
   ( 'Q', 0x802 ) : "Qualcomm Kryo 3xx Gold",           # LX*
   ( 'Q', 0x803 ) : "Qualcomm Kryo 3xx Silver",         # LX*
   ( 'Q', 0x804 ) : "Qualcomm Kryo 4xx Gold",           # LX*
   ( 'Q', 0x805 ) : "Qualcomm Kryo 4xx Silver",         # LX*
   ( 'Q', 0xc00 ) : "Qualcomm Falkor V2",               # LX*
   ( 'Q', 0xc01 ) : "Qualcomm Saphira",                 # gcc*
   ( 'S', 0x001 ) : "Samsung Exynos M1",        # gcc*, Julia* (M2 in variants)
   ( 'S', 0x002 ) : "Samsung Exynos M3",        # Julia*
   ( 'S', 0x003 ) : "Samsung Exynos M4",        # Julia*
   ( 'S', 0x004 ) : "Samsung Exynos M5",        # Julia*
   ( 'a', 0x000 ) : "Apple A6: Bali: Swift",                          # Apple*
   ( 'a', 0x001 ) : "Apple A7: Alcatraz: Cyclone",                    # Apple*
   ( 'a', 0x002 ) : "Apple A8: Fiji: Typhoon",                        # Apple*
   ( 'a', 0x003 ) : "Apple A8X: Capri: Typhoon",                      # Apple*
   ( 'a', 0x004 ) : "Apple A9: Maui: Twister",                        # Apple*
   ( 'a', 0x005 ) : "Apple A9X: Elba/Malta: Twister",                 # Apple*
   ( 'a', 0x006 ) : "Apple A10: Cayman: Hurricane",                   # Apple*
   ( 'a', 0x007 ) : "Apple A10X: Myst: Hurricane",                    # Apple*
   ( 'a', 0x008 ) : "Apple A11: Skye: Monsoon (P-core)",              # Apple*
   ( 'a', 0x009 ) : "Apple A11: Skye: Mistral (E-core)",              # Apple*
   ( 'a', 0x00b ) : "Apple A12: Cyprus: Vortex (P-core)",             # Apple*
   ( 'a', 0x00c ) : "Apple A12: Cyprus: Tempest (E-core)",            # Apple*
   ( 'a', 0x00f ) : "Apple M9: Tempest (E-core)",                     # Apple*
   ( 'a', 0x010 ) : "Apple A12X: Aruba: Vortex (P-core)",             # Apple*
   ( 'a', 0x011 ) : "Apple A12X: Aruba: Tempest (E-core)",            # Apple*
   ( 'a', 0x012 ) : "Apple A13: Cebu: Lightning (P-core)",            # Apple*
   ( 'a', 0x013 ) : "Apple A13: Cebu: Thunder (E-core)",              # Apple*
   ( 'a', 0x020 ) : "Apple A14: Sicily: Icestorm (E-core)",           # Apple*
   ( 'a', 0x021 ) : "Apple A14: Sicily: Firestorm (P-core)",          # Apple*
   ( 'a', 0x022 ) : "Apple M1: Tonga: Icestorm (E-core)",             # Apple*
   ( 'a', 0x023 ) : "Apple M1: Tonga: Firestorm (P-core)",            # Apple*
   ( 'a', 0x024 ) : "Apple M1 Pro: Jade Chop: Icestorm (E-core)",     # Apple*
   ( 'a', 0x025 ) : "Apple M1 Pro: Jade Chop: Firestorm (P-core)",    # Apple*
   ( 'a', 0x026 ) : "Apple M10: Thunder",                             # Apple*
   ( 'a', 0x028 ) : "Apple M1 Max: Jade 1C/2C: Icestorm (E-core)",    # Apple*
   ( 'a', 0x029 ) : "Apple M1 Max: Jade 1C/2C: Firestorm (P-core)",   # Apple*
   ( 'a', 0x030 ) : "Apple A15: Ellis: Blizzard (E-core)",            # Apple*
   ( 'a', 0x031 ) : "Apple A15: Ellis: Avalanche (P-core)",           # Apple*
   ( 'a', 0x032 ) : "Apple M2: Staten: Blizzard (E-core)",            # Apple*
   ( 'a', 0x033 ) : "Apple M2: Staten: Avalanche (P-core)",           # Apple*
   ( 'a', 0x034 ) : "Apple M2 Pro: Rhodes Chop: Blizzard (E-core)",   # Apple*
   ( 'a', 0x035 ) : "Apple M2 Pro: Rhodes Chop: Avalanche (P-core)",  # Apple*
   ( 'a', 0x036 ) : "Apple A16: Crete: Sawtooth (E-core)",            # lscpu* (llvm disagrees)
   ( 'a', 0x037 ) : "Apple A16: Crete: Everest (P-core)",             # lscpu* (llvm disagrees)
   ( 'a', 0x038 ) : "Apple M2 Max: Rhodes 1C/2C: Blizzard (E-core)",  # Apple*
   ( 'a', 0x039 ) : "Apple M2 Max: Rhodes 1C/2C: Avalanche (P-core)", # Apple*
   ( 'a', 0x040 ) : "Apple A17: Tupai: Sawtooth (E-core)",            # Apple* (Tupai purportedly is A18) (llvm thinks this is Crete)
   ( 'a', 0x041 ) : "Apple A17: Tupai: Everest (P-core)",             # Apple* (Tupai purportedly is A18) (llvm thinks this is Crete)
   ( 'a', 0x042 ) : "Apple M3: Ibiza E-core",                         # Apple*
   ( 'a', 0x043 ) : "Apple M3: Ibiza P-core",                         # Apple*
   ( 'a', 0x044 ) : "Apple M3 Pro: Lobos E-core",                     # Apple*
   ( 'a', 0x045 ) : "Apple M3 Pro: Lobos P-core",                     # Apple*
   ( 'a', 0x046 ) : "Apple M11: Sawtooth (E-core)",                   # Apple*
   ( 'a', 0x048 ) : "Apple M3 Max: Palma E-core",                     # Apple*
   ( 'a', 0x049 ) : "Apple M3 Max: Palma P-core",                     # Apple*
   ( 'a', 0x050 ) : "Apple A17 Pro: Coll E-core",                     # Apple*
   ( 'a', 0x051 ) : "Apple A17 Pro: Coll P-core",                     # Apple*
   ( 'a', 0x052 ) : "Apple M4: Donan E-core",                         # Apple*
   ( 'a', 0x053 ) : "Apple M4: Donan P-core",                         # Apple*
   ( 'a', 0x054 ) : "Apple M4 Pro: Brava-S E-core",                   # Apple*
   ( 'a', 0x055 ) : "Apple M4 Pro: Brava-S P-core",                   # Apple*
   ( 'a', 0x058 ) : "Apple M4 Max: Brava-C E-core",                   # Apple*
   ( 'a', 0x059 ) : "Apple M4 Max: Brava-C P-core",                   # Apple*
   ( 'a', 0x062 ) : "Apple M5: Hidra E-core",                         # gcc*
   ( 'a', 0x063 ) : "Apple M5: Hidra P-core",                         # gcc*
   ( 'a', 0x064 ) : "Apple M5 Pro: Sotra-S E-core",                   # gcc*
   ( 'a', 0x065 ) : "Apple M5 Pro: Sotra-S P-core",                   # gcc*
   ( 'a', 0x068 ) : "Apple M5 Max: Sotra-C E-core",                   # gcc*
   ( 'a', 0x069 ) : "Apple M5 Max: Sotra-C P-core",                   # gcc*
   ( 'c', 0x132 ) : "ARM China Star-MC1",   # llvm*
   ( 'c', 0xd24 ) : "ARM China Cortex-M52", # CTRM*
   ( 'c', 0xd25 ) : "ARM China Star-MC3",   # llvm*
   ( 'h', 0x000 ) : "Huaxintong Phecda", # gcc*, Julia*
   ( 'm', 0xd49 ) : "Microsoft Azure Cobalt 100", # LX*, gcc*
   ( 'p', 0x303 ) : "Phytium FTC310",    # lscpu*
   ( 'p', 0x660 ) : "Phytium FTC660",    # lscpu*
   ( 'p', 0x661 ) : "Phytium FTC661",    # lscpu*
   ( 'p', 0x662 ) : "Phytium FTC662",    # lscpu*
   ( 'p', 0x663 ) : "Phytium FTC663",    # lscpu*
   ( 'p', 0x664 ) : "Phytium FTC664",    # lscpu*
   ( 'p', 0x862 ) : "Phytium FTC862",    # lscpu*
   ( Amp, 0xac3 ) : "Ampere Ampere 1",   # LX*
   ( Amp, 0xac4 ) : "Ampere Ampere 1a",  # gcc*
   ( Amp, 0xac5 ) : "Ampere Ampere 1b",  # gcc*
   ( Amp, 0xac7 ) : "Ampere Ampere 1c",  # gcc*, llvm*
}

implementers = {
   'A' : "ARM",                  # CTRM*
   'B' : "Broadcom",
   'C' : "Cavium",
   'D' : "DEC",
   'F' : "Fujitsu",
   'H' : "HiSilicon",            # LX*
   'I' : "Infineon",
   'M' : "Motorola / Freescale",
   'N' : "NVIDIA",
   'P' : "APM",
   'Q' : "Qualcomm",
   'S' : "Samsung",
   'V' : "Marvell",
   'a' : "Apple",
   'c' : "ARM China",            # CTRM*
   'h' : "Huaxintong",           # gcc*, Julia*
   'i' : "Intel",
   'm' : "Microsoft",            # LX*
   'p' : "Phytium",              # lscpu*
   Amp : "Ampere",
}

################################################################################

class Cpu:
   def __init__(self):
      self.impl     = None
      self.variant  = None
      self.part     = None
      self.revision = None

   def __repr__(self):
      result = ""
      if self.impl != None:
         result += ", impl='" + str(self.impl) + "'" 
         result += "(" + hex(ord(self.impl)) + ")"
      if self.part != None:
         result += ", part=" + hex(self.part)
      if self.variant != None:
         result += ", variant=" + hex(self.variant)
      if self.revision != None:
         result += ", revision=" + str(self.revision)
      return "{" + result[2:] + "}"

   def empty(self):
      return     self.impl     == None \
             and self.variant  == None \
             and self.part     == None \
             and self.revision == None

   def model(self):
      try:
         return variants[self.impl, self.part, self.variant]
      except KeyError:
         None
      try:
         if self.impl == 'A' or self.impl == 'c':
            # Implementations from ARM have a common use for (Variant,Revision),
            # rVpR, so append that here.
            return (parts[self.impl, self.part] \
                    + " r" + str(self.variant) + "p" + str(self.revision))
         else:
            return parts[self.impl, self.part]
      except KeyError:
         None
      try:
         return implementers[self.impl] + " unknown model: " \
                + "{part=" + hex(self.part) \
                + ", variant=" + hex(self.variant) \
                + ", revision=" + str(self.revision) + "}"
      except KeyError:
         None
      return "unknown: " + str(self)

################################################################################

parser = argparse.ArgumentParser(
            add_help = False,
            formatter_class = argparse.RawDescriptionHelpFormatter,
            description=("Determines ARM64 CPU model name from MIDR_EL1 values"
                         " read from /proc/cpuinfo or the command line."))

parser.add_argument("-1", "--one-cpu",
                    action="store_true", dest="oneCpu", default=False,
                    help="display information only for the first CPU")
parser.add_argument("-h", "-H", "--help", action='help',
                    default=argparse.SUPPRESS,
                    help="show this help message and exit")
def implementer(value):
   try:
      return chr(int(value, base=0))
   except ValueError:
      if len(value) == 1:
         return value[0]
      else:
         raise argparse.ArgumentTypeError("invalid int or character value:"
                                          + " '" + value + "'")
parser.add_argument("-i", "--implementer",
                    type=implementer, dest="impl",
                    help=("decode command-line argument with implementor IMPL,"
                          " either an integer or a single character whose ASCII"
                          " value is used"))
def int_any_base(value):
   try:
      return int(value, 0)
   except ValueError:
      raise argparse.ArgumentTypeError("invalid int value: '" + value + "'")
parser.add_argument("-v", "--variant",
                    action="store", type=int_any_base, dest="variant",
                    metavar="VAR",
                    help="decode command-line argument with variant VAR")
parser.add_argument("-p", "--part",
                    action="store", type=int_any_base, dest="part",
                    help="decode command-line argument with part PART")
parser.add_argument("-r", "--revision",
                    action="store", type=int_any_base, dest="revision",
                    metavar="REV",
                    help="decode command-line argument with revision REV")
parser.add_argument("-V", "--version",
                    action="version", version=("%(prog)s " + __version__),
                    help="show version and exit")

args = parser.parse_args()

################################################################################

cpus = {}

if args.impl != None \
   or args.variant != None \
   or args.part != None \
   or args.revision != None:
   current = cpus[0] = Cpu()
   current.impl     = args.impl     or 0
   current.variant  = args.variant  or 0
   current.part     = args.part     or 0
   current.revision = args.revision or 0
   args.oneCpu = True
else:   
   with open("/proc/cpuinfo") as file:
      current = Cpu() # dummy in case of garbage lines before "processor"
      for line in file.readlines():
          line = line.strip()
          if line:
             fields = line.split(":")
             name = fields[0].strip()
             if name == "processor":
                 if args.oneCpu and len(cpus) >= 1:
                    break
                 number = int(fields[1].strip())
                 cpus[number] = Cpu()
                 current = cpus[number] # reference to array element
             elif name == "CPU implementer":
                 current.impl = chr(int(fields[1].strip(), base=0))
             elif name == "CPU variant":
                 current.variant = int(fields[1].strip(), base=0)
             elif name == "CPU part":
                 current.part = int(fields[1].strip(), base=0)
             elif name == "CPU revision":
                 current.revision = int(fields[1].strip(), base=0)

first = cpus[next(iter(cpus))]
if first.empty():
   progname = os.path.basename(__file__)
   print(progname + ": No ARM CPU information available.", file=sys.stderr)
   print(progname + ": /proc/cpuinfo contained no implementer, part, variant,"
         + " or revision.", file=sys.stderr)
   print(progname + ": This suggests that the system isn't ARM-based.",
         file=sys.stderr)
   sys.exit(1)

if args.oneCpu:
   print("processor: " + first.model())
else:   
   for number in sorted(cpus.keys()):
      print("processor " + str(number) + ": " + cpus[number].model())
