Merge pull request #294 from makermelissa/simplify_rev_codes
Added improved Pi Revision Code detection
This commit is contained in:
commit
7b3c72a581
5 changed files with 484 additions and 3 deletions
|
|
@ -201,10 +201,16 @@ class Board:
|
|||
# Check for Pi boards:
|
||||
pi_rev_code = self._pi_rev_code()
|
||||
if pi_rev_code:
|
||||
for model, codes in boards._PI_REV_CODES.items():
|
||||
if pi_rev_code in codes:
|
||||
return model
|
||||
from adafruit_platformdetect.revcodes import PiDecoder
|
||||
|
||||
try:
|
||||
decoder = PiDecoder(pi_rev_code)
|
||||
model = boards._PI_MODELS[decoder.type_raw]
|
||||
if isinstance(model, dict):
|
||||
model = model[decoder.revision]
|
||||
return model
|
||||
except ValueError:
|
||||
pass
|
||||
# We may be on a non-Raspbian OS, so try to lazily determine
|
||||
# the version based on `get_device_model`
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ RASPBERRY_PI_4B = "RASPBERRY_PI_4B"
|
|||
RASPBERRY_PI_AVNET_IIOT_GW = "RASPBERY_PI_AVNET_IIOT_GW"
|
||||
RASPBERRY_PI_400 = "RASPBERRY_PI_400"
|
||||
RASPBERRY_PI_CM4 = "RASPBERRY_PI_CM4"
|
||||
RASPBERRY_PI_CM4S = "RASPBERRY_PI_CM4S"
|
||||
|
||||
ODROID_C1 = "ODROID_C1"
|
||||
ODROID_C1_PLUS = "ODROID_C1_PLUS"
|
||||
|
|
@ -341,6 +342,7 @@ _RASPBERRY_PI_CM_IDS = (
|
|||
RASPBERRY_PI_CM3,
|
||||
RASPBERRY_PI_CM3_PLUS,
|
||||
RASPBERRY_PI_CM4,
|
||||
RASPBERRY_PI_CM4S,
|
||||
)
|
||||
|
||||
_ODROID_40_PIN_IDS = (
|
||||
|
|
@ -564,6 +566,31 @@ _PI_REV_CODES = {
|
|||
RASPBERRY_PI_ZERO_2_W: ("902120", "2902120"),
|
||||
}
|
||||
|
||||
_PI_MODELS = {
|
||||
0x00: RASPBERRY_PI_A,
|
||||
0x01: {
|
||||
1.0: RASPBERRY_PI_B_REV1,
|
||||
2.0: RASPBERRY_PI_B_REV2,
|
||||
},
|
||||
0x02: RASPBERRY_PI_A_PLUS,
|
||||
0x03: RASPBERRY_PI_B_PLUS,
|
||||
0x04: RASPBERRY_PI_2B,
|
||||
0x06: RASPBERRY_PI_CM1,
|
||||
0x08: RASPBERRY_PI_3B,
|
||||
0x09: RASPBERRY_PI_ZERO,
|
||||
0x0A: RASPBERRY_PI_CM3,
|
||||
0x0B: RASPBERRY_PI_AVNET_IIOT_GW,
|
||||
0x0C: RASPBERRY_PI_ZERO_W,
|
||||
0x0D: RASPBERRY_PI_3B_PLUS,
|
||||
0x0E: RASPBERRY_PI_3A_PLUS,
|
||||
0x10: RASPBERRY_PI_CM3_PLUS,
|
||||
0x11: RASPBERRY_PI_4B,
|
||||
0x12: RASPBERRY_PI_ZERO_2_W,
|
||||
0x13: RASPBERRY_PI_400,
|
||||
0x14: RASPBERRY_PI_CM4,
|
||||
0x15: RASPBERRY_PI_CM4S,
|
||||
}
|
||||
|
||||
# Onion omega boards
|
||||
_ONION_OMEGA_BOARD_IDS = (ONION_OMEGA, ONION_OMEGA2)
|
||||
|
||||
|
|
|
|||
296
adafruit_platformdetect/revcodes.py
Normal file
296
adafruit_platformdetect/revcodes.py
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
`adafruit_platformdetect.revcodes`
|
||||
================================================================================
|
||||
|
||||
Class to help with Raspberry Pi Rev Codes
|
||||
|
||||
* Author(s): Melissa LeBlanc-Williams
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
* Linux and Python 3.7 or Higher
|
||||
|
||||
Data values from https://github.com/raspberrypi/documentation/blob/develop/
|
||||
documentation/asciidoc/computers/raspberry-pi/revision-codes.adoc#new-style-revision-codes
|
||||
|
||||
"""
|
||||
|
||||
NEW_OVERVOLTAGE = (
|
||||
"Overvoltage allowed",
|
||||
"Overvoltage disallowed",
|
||||
)
|
||||
|
||||
NEW_OTP_PROGRAM = (
|
||||
"OTP programming is allowed",
|
||||
"OTP programming is disallowed",
|
||||
)
|
||||
|
||||
NEW_OTP_READ = (
|
||||
"OTP reading is allowed",
|
||||
"OTP reading is disallowed",
|
||||
)
|
||||
|
||||
NEW_WARRANTY_BIT = (
|
||||
"Warranty is intact",
|
||||
"Warranty has been voided by overclocking",
|
||||
)
|
||||
|
||||
NEW_REV_STYLE = (
|
||||
"Old-style revision",
|
||||
"New-style revision",
|
||||
)
|
||||
|
||||
NEW_MEMORY_SIZE = (
|
||||
"256MB",
|
||||
"512MB",
|
||||
"1GB",
|
||||
"2GB",
|
||||
"4GB",
|
||||
"8GB",
|
||||
)
|
||||
|
||||
NEW_MANUFACTURER = (
|
||||
"Sony UK",
|
||||
"Egoman",
|
||||
"Embest",
|
||||
"Sony Japan",
|
||||
"Embest",
|
||||
"Stadium",
|
||||
)
|
||||
|
||||
NEW_PROCESSOR = (
|
||||
"BCM2835",
|
||||
"BCM2836",
|
||||
"BCM2837",
|
||||
"BCM2711",
|
||||
)
|
||||
|
||||
PI_TYPE = {
|
||||
0x00: "A",
|
||||
0x01: "B",
|
||||
0x02: "A+",
|
||||
0x03: "B+",
|
||||
0x04: "2B",
|
||||
0x05: "Alpha (early prototype)",
|
||||
0x06: "CM1",
|
||||
0x08: "3B",
|
||||
0x09: "Zero",
|
||||
0x0A: "CM3",
|
||||
0x0B: "Custom",
|
||||
0x0C: "Zero W",
|
||||
0x0D: "3B+",
|
||||
0x0E: "3A+",
|
||||
0x0F: "Internal use only",
|
||||
0x10: "CM3+",
|
||||
0x11: "4B",
|
||||
0x12: "Zero 2 W",
|
||||
0x13: "400",
|
||||
0x14: "CM4",
|
||||
0x15: "CM4S",
|
||||
}
|
||||
|
||||
OLD_MANUFACTURER = (
|
||||
"Sony UK",
|
||||
"Egoman",
|
||||
"Embest",
|
||||
"Qisda",
|
||||
)
|
||||
|
||||
OLD_MEMORY_SIZE = ("256MB", "512MB", "256MB/512MB")
|
||||
|
||||
NEW_REV_STRUCTURE = {
|
||||
"overvoltage": (31, 1, NEW_OVERVOLTAGE),
|
||||
"otp_program": (30, 1, NEW_OTP_PROGRAM),
|
||||
"otp_read": (29, 1, NEW_OTP_READ),
|
||||
"warranty": (25, 1, NEW_WARRANTY_BIT),
|
||||
"rev_style": (23, 1, NEW_REV_STYLE),
|
||||
"memory_size": (20, 3, NEW_MEMORY_SIZE),
|
||||
"manufacturer": (16, 4, NEW_MANUFACTURER),
|
||||
"processor": (12, 4, NEW_PROCESSOR),
|
||||
"type": (4, 8, PI_TYPE),
|
||||
"revision": (0, 4, int),
|
||||
}
|
||||
|
||||
OLD_REV_STRUCTURE = {
|
||||
"type": (0, PI_TYPE),
|
||||
"revision": (1, float),
|
||||
"memory_size": (2, OLD_MEMORY_SIZE),
|
||||
"manufacturer": (3, OLD_MANUFACTURER),
|
||||
}
|
||||
|
||||
OLD_REV_EXTRA_PROPS = {
|
||||
"warranty": (24, 1, NEW_WARRANTY_BIT),
|
||||
}
|
||||
|
||||
OLD_REV_LUT = {
|
||||
0x02: (1, 1.0, 0, 1),
|
||||
0x03: (1, 1.0, 0, 1),
|
||||
0x04: (1, 2.0, 0, 0),
|
||||
0x05: (1, 2.0, 0, 3),
|
||||
0x06: (1, 2.0, 0, 1),
|
||||
0x07: (0, 2.0, 0, 1),
|
||||
0x08: (0, 2.0, 0, 0),
|
||||
0x09: (0, 2.0, 0, 3),
|
||||
0x0D: (1, 2.0, 1, 1),
|
||||
0x0E: (1, 2.0, 1, 0),
|
||||
0x0F: (1, 2.0, 1, 1),
|
||||
0x10: (3, 1.2, 1, 0),
|
||||
0x11: (6, 1.0, 1, 0),
|
||||
0x12: (2, 1.1, 0, 0),
|
||||
0x13: (3, 1.2, 1, 2),
|
||||
0x14: (6, 1.0, 1, 2),
|
||||
0x15: (2, 1.1, 2, 2),
|
||||
}
|
||||
|
||||
|
||||
class PiDecoder:
|
||||
"""Raspberry Pi Revision Code Decoder"""
|
||||
|
||||
def __init__(self, rev_code):
|
||||
try:
|
||||
self.rev_code = int(rev_code, 16) & 0xFFFFFFFF
|
||||
except ValueError:
|
||||
print("Invalid revision code. It should be a hexadecimal value.")
|
||||
|
||||
def is_valid_code(self):
|
||||
"""Quickly check the validity of a code"""
|
||||
if self.is_new_format():
|
||||
for code_format in NEW_REV_STRUCTURE.values():
|
||||
lower_bit, bit_size, values = code_format
|
||||
prop_value = (self.rev_code >> lower_bit) & ((1 << bit_size) - 1)
|
||||
if not self._valid_value(prop_value, values):
|
||||
return False
|
||||
else:
|
||||
if (self.rev_code & 0xFFFF) not in OLD_REV_LUT.keys():
|
||||
return False
|
||||
for code_format in OLD_REV_STRUCTURE.values():
|
||||
index, values = code_format
|
||||
code_format = OLD_REV_LUT[self.rev_code & 0xFFFF]
|
||||
if index >= len(code_format):
|
||||
return False
|
||||
if not self._valid_value(code_format[index], values):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_rev_prop_value(self, name, structure=None, raw=False):
|
||||
if structure is None:
|
||||
structure = NEW_REV_STRUCTURE
|
||||
if name not in structure.keys():
|
||||
raise ValueError(f"Unknown property {name}")
|
||||
lower_bit, bit_size, values = structure[name]
|
||||
prop_value = self._get_bits_value(lower_bit, bit_size)
|
||||
if not self._valid_value(prop_value, values):
|
||||
raise ValueError(f"Invalid value {prop_value} for property {name}")
|
||||
if raw:
|
||||
return prop_value
|
||||
return self._format_value(prop_value, values)
|
||||
|
||||
def _get_bits_value(self, lower_bit, bit_size):
|
||||
return (self.rev_code >> lower_bit) & ((1 << bit_size) - 1)
|
||||
|
||||
def _get_old_rev_prop_value(self, name, raw=False):
|
||||
if name not in OLD_REV_STRUCTURE.keys():
|
||||
raise ValueError(f"Unknown property {name}")
|
||||
index, values = OLD_REV_STRUCTURE[name]
|
||||
data = OLD_REV_LUT[self.rev_code & 0xFFFF]
|
||||
if index >= len(data):
|
||||
raise IndexError(f"Index {index} out of range for property {name}")
|
||||
if not self._valid_value(data[index], values):
|
||||
raise ValueError(f"Invalid value {data[index]} for property {name}")
|
||||
if raw:
|
||||
return data[index]
|
||||
return self._format_value(data[index], values)
|
||||
|
||||
@staticmethod
|
||||
def _format_value(value, valid_values):
|
||||
if valid_values is float or valid_values is int:
|
||||
return valid_values(value)
|
||||
return valid_values[value]
|
||||
|
||||
@staticmethod
|
||||
def _valid_value(value, valid_values):
|
||||
if valid_values is float or valid_values is int:
|
||||
return isinstance(value, valid_values)
|
||||
if isinstance(valid_values, (tuple, list)) and 0 <= value < len(valid_values):
|
||||
return True
|
||||
if isinstance(valid_values, dict) and value in valid_values.keys():
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_property(self, name, raw=False):
|
||||
if name not in NEW_REV_STRUCTURE:
|
||||
raise ValueError(f"Unknown property {name}")
|
||||
if self.is_new_format():
|
||||
return self._get_rev_prop_value(name, raw=raw)
|
||||
if name in OLD_REV_EXTRA_PROPS:
|
||||
return self._get_rev_prop_value(
|
||||
name, structure=OLD_REV_EXTRA_PROPS, raw=raw
|
||||
)
|
||||
return self._get_old_rev_prop_value(name, raw=raw)
|
||||
|
||||
def is_new_format(self):
|
||||
"""Check if the code is in the new format"""
|
||||
return self._get_rev_prop_value("rev_style", raw=True) == 1
|
||||
|
||||
@property
|
||||
def overvoltage(self):
|
||||
"""Overvoltage allowed/disallowed"""
|
||||
return self._get_property("overvoltage")
|
||||
|
||||
@property
|
||||
def warranty_bit(self):
|
||||
"""Warranty bit"""
|
||||
return self._get_property("warranty")
|
||||
|
||||
@property
|
||||
def otp_program(self):
|
||||
"""OTP programming allowed/disallowed"""
|
||||
return self._get_property("otp_program")
|
||||
|
||||
@property
|
||||
def otp_read(self):
|
||||
"""OTP reading allowed/disallowed"""
|
||||
return self._get_property("otp_read")
|
||||
|
||||
@property
|
||||
def rev_style(self):
|
||||
"""Revision Code style"""
|
||||
# Force new style for Rev Style
|
||||
return self._get_rev_prop_value("rev_style")
|
||||
|
||||
@property
|
||||
def memory_size(self):
|
||||
"""Memory size"""
|
||||
return self._get_property("memory_size")
|
||||
|
||||
@property
|
||||
def manufacturer(self):
|
||||
"""Manufacturer"""
|
||||
return self._get_property("manufacturer")
|
||||
|
||||
@property
|
||||
def processor(self):
|
||||
"""Processor"""
|
||||
return self._get_property("processor")
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
"""Specific Model"""
|
||||
return self._get_property("type")
|
||||
|
||||
@property
|
||||
def type_raw(self):
|
||||
"""Raw Value of Specific Model"""
|
||||
return self._get_property("type", raw=True)
|
||||
|
||||
@property
|
||||
def revision(self):
|
||||
"""Revision Number"""
|
||||
return self._get_property("revision")
|
||||
79
bin/rev_code_tester.py
Normal file
79
bin/rev_code_tester.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
`bin.rev_code_tester`
|
||||
================================================================================
|
||||
|
||||
Tests that all existing rev codes in the boards constant file
|
||||
match what the decoder finds.
|
||||
|
||||
* Author(s): Melissa LeBlanc-Williams
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
* Linux and Python 3.7 or Higher
|
||||
|
||||
"""
|
||||
|
||||
import adafruit_platformdetect
|
||||
import adafruit_platformdetect.constants.boards as ap_board
|
||||
from adafruit_platformdetect.revcodes import PiDecoder
|
||||
|
||||
detector = adafruit_platformdetect.Detector()
|
||||
|
||||
|
||||
def print_property(label, value):
|
||||
"Format and print a property"
|
||||
print(f"{label}: {value}")
|
||||
|
||||
|
||||
def print_info(pi_decoder):
|
||||
"Print the info for the board"
|
||||
if pi_decoder.is_new_format():
|
||||
print_property("Overvoltage", pi_decoder.overvoltage)
|
||||
print_property("OTP Program", pi_decoder.otp_program)
|
||||
print_property("OTP Read", pi_decoder.otp_read)
|
||||
print_property("Warranty bit", pi_decoder.warranty_bit)
|
||||
print_property("New flag", pi_decoder.rev_style)
|
||||
print_property("Memory size", pi_decoder.memory_size)
|
||||
print_property("Manufacturer", pi_decoder.manufacturer)
|
||||
print_property("Processor", pi_decoder.processor)
|
||||
print_property("Type", pi_decoder.type)
|
||||
print_property("Revision", pi_decoder.revision)
|
||||
else:
|
||||
print_property("Warranty bit", pi_decoder.warranty_bit)
|
||||
print_property("Model", pi_decoder.type)
|
||||
print_property("Revision", pi_decoder.revision)
|
||||
print_property("RAM", pi_decoder.memory_size)
|
||||
print_property("Manufacturer", pi_decoder.manufacturer)
|
||||
|
||||
|
||||
# Iterate through the _PI_REV_CODES dictionary to find the model
|
||||
# Run the code through the decoder to check that:
|
||||
# - It is a valid code
|
||||
# - It matches the model
|
||||
for model, codes in ap_board._PI_REV_CODES.items(): # pylint: disable=protected-access
|
||||
for pi_rev_code in codes:
|
||||
try:
|
||||
decoder = PiDecoder(pi_rev_code)
|
||||
except ValueError as e:
|
||||
print("Invalid revision code. It should be a hexadecimal value.")
|
||||
decoded_model = ap_board._PI_MODELS[ # pylint: disable=protected-access
|
||||
decoder.type_raw
|
||||
]
|
||||
if isinstance(decoded_model, dict):
|
||||
decoded_model = decoded_model[decoder.revision]
|
||||
if decoded_model == model:
|
||||
print(f"Decoded model matches expected model: {model}")
|
||||
else:
|
||||
print(f"Decoded model does not match expected model: {model}")
|
||||
print(f"Decoded model: {decoded_model}")
|
||||
print(f"Expected model: {model}")
|
||||
print_info(decoder)
|
||||
73
bin/rpi_info.py
Normal file
73
bin/rpi_info.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 Melissa LeBlanc-Williams for Adafruit Industries
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
`bin.rpi_info`
|
||||
================================================================================
|
||||
|
||||
Interactive mode will prompt for the revision code
|
||||
Otherwise it will be detected automatically
|
||||
|
||||
* Author(s): Melissa LeBlanc-Williams
|
||||
|
||||
Implementation Notes
|
||||
--------------------
|
||||
|
||||
**Software and Dependencies:**
|
||||
|
||||
* Linux and Python 3.7 or Higher
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
import adafruit_platformdetect
|
||||
from adafruit_platformdetect.revcodes import PiDecoder
|
||||
|
||||
pi_rev_code = None
|
||||
|
||||
detector = adafruit_platformdetect.Detector()
|
||||
pi_rev_code = detector.board._pi_rev_code() # pylint: disable=protected-access
|
||||
|
||||
if pi_rev_code is None:
|
||||
print("Raspberry Pi not detected. Using interactive mode")
|
||||
pi_rev_code = input("Enter a Raspberry Pi revision code (e.g. d03114 or 000f): ")
|
||||
|
||||
try:
|
||||
decoder = PiDecoder(pi_rev_code)
|
||||
except ValueError as e:
|
||||
print("Invalid revision code. It should be a hexadecimal value.")
|
||||
sys.exit(1)
|
||||
|
||||
if not decoder.is_valid_code():
|
||||
print(
|
||||
"Code is invalid. This rev code includes at least one "
|
||||
"value that is outside of the expected range."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_property(label, value):
|
||||
"Format and print a property"
|
||||
print(f"{label}: {value}")
|
||||
|
||||
|
||||
if decoder.is_new_format():
|
||||
print_property("Overvoltage", decoder.overvoltage)
|
||||
print_property("OTP Program", decoder.otp_program)
|
||||
print_property("OTP Read", decoder.otp_read)
|
||||
print_property("Warranty bit", decoder.warranty_bit)
|
||||
print_property("New flag", decoder.rev_style)
|
||||
print_property("Memory size", decoder.memory_size)
|
||||
print_property("Manufacturer", decoder.manufacturer)
|
||||
print_property("Processor", decoder.processor)
|
||||
print_property("Type", decoder.type)
|
||||
print_property("Revision", decoder.revision)
|
||||
else:
|
||||
print_property("Warranty bit", decoder.warranty_bit)
|
||||
print_property("Model", decoder.type)
|
||||
print_property("Revision", decoder.revision)
|
||||
print_property("RAM", decoder.memory_size)
|
||||
print_property("Manufacturer", decoder.manufacturer)
|
||||
Loading…
Reference in a new issue