81634fc5 |
#!/usr/bin/env python3
## Help
"""
raspi-revcode 1.0
Parse Raspberry Pi revision codes.
Usage:
raspi-revcode [<code>]
raspi-revcode -h|--help
Arguments:
<code>
A hexadecimal revision code to parse. If not given, '/proc/cpuinfo' will be
used to get the revision code of the current device.
"""
## Imports
import sys
import re
import docopt
## Constants
# https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-revision-codes
CPUINFO_PATH = '/proc/cpuinfo'
CPUINFO_RE = r'^Revision\s*:\s*(.*)$'
# https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#old-style-revision-codes
# https://forums.raspberrypi.com//viewtopic.php?p=176865#p177014
OLD_STYLE_WARRANTY_BIT = 24
OLD_STYLE_WARRANTY_FIELD = 'W'
OLD_STYLE = {
'names': ['Type', 'Revision', 'Memory size', 'Manufacturer'],
0x0002: ['B', '1.0', '256MB', 'Egoman' ],
0x0003: ['B', '1.0', '256MB', 'Egoman' ],
0x0004: ['B', '2.0', '256MB', 'Sony UK' ],
0x0005: ['B', '2.0', '256MB', 'Qisda' ],
0x0006: ['B', '2.0', '256MB', 'Egoman' ],
0x0007: ['A', '2.0', '256MB', 'Egoman' ],
0x0008: ['A', '2.0', '256MB', 'Sony UK' ],
0x0009: ['A', '2.0', '256MB', 'Qisda' ],
0x000d: ['B', '2.0', '512MB', 'Egoman' ],
0x000e: ['B', '2.0', '512MB', 'Sony UK' ],
0x000f: ['B', '2.0', '512MB', 'Egoman' ],
0x0010: ['B+', '1.2', '512MB', 'Sony UK' ],
0x0011: ['CM1', '1.0', '512MB', 'Sony UK' ],
0x0012: ['A+', '1.1', '256MB', 'Sony UK' ],
0x0013: ['B+', '1.2', '512MB', 'Embest' ],
0x0014: ['CM1', '1.0', '512MB', 'Embest' ],
0x0015: ['A+', '1.1', '256MB/512MB', 'Embest' ],
}
# https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#new-style-revision-codes
NEW_STYLE_BIT = 23
NEW_STYLE_BITS = 'NOQuuuWuFMMMCCCCPPPPTTTTTTTTRRRR'
NEW_STYLE = {
'N': ['Overvoltage', {
0x0: 'Overvoltage allowed',
0x1: 'Overvoltage disallowed',
}],
'O': ['OTP Program', {
0x0: 'OTP programming allowed',
0x1: 'OTP programming disallowed',
}],
'Q': ['OTP Read', {
0x0: 'OTP reading allowed',
0x1: 'OTP reading disallowed',
}],
# 'uuu': ['Unused', {
# u: 'Unused' for u in range(2**len('uuu'))
# }],
'W': ['Warranty bit', {
0x0: 'Warranty is intact',
0x1: 'Warranty has been voided by overclocking',
}],
# 'u': ['Unused', {
# u: 'Unused' for u in range(2**len('u'))
# }],
'F': ['New flag', {
0x1: 'New-style revision',
0x0: 'Old-style revision',
}],
'MMM': ['Memory size', {
0x0: '256MB',
0x1: '512MB',
0x2: '1GB',
0x3: '2GB',
0x4: '4GB',
0x5: '8GB',
}],
'CCCC': ['Manufacturer', {
0x0: 'Sony UK',
0x1: 'Egoman',
0x2: 'Embest',
0x3: 'Sony Japan',
0x4: 'Embest',
0x5: 'Stadium',
}],
'PPPP': ['Processor', {
0x0: 'BCM2835',
0x1: 'BCM2836',
0x2: 'BCM2837',
0x3: 'BCM2711',
0x4: 'BCM2712',
}],
'TTTTTTTT': ['Type', {
0x0: 'A',
0x1: 'B',
0x2: 'A+',
0x3: 'B+',
0x4: '2B',
0x5: 'Alpha (early prototype)',
0x6: 'CM1',
0x8: '3B',
0x9: 'Zero',
0xa: 'CM3',
0xc: 'Zero W',
0xd: '3B+',
0xe: '3A+',
0xf: 'Internal use only',
0x10: 'CM3+',
0x11: '4B',
0x12: 'Zero 2 W',
0x13: '400',
0x14: 'CM4',
0x15: 'CM4S',
0x16: 'Internal use only',
0x17: '5',
}],
'RRRR': ['Revision', {
rev: f'1.{rev}' for rev in range(2**len('RRRR'))
}],
}
## Error
def error(msg):
print(msg, file=sys.stderr)
exit(1)
## Main
def main():
args = docopt.docopt(__doc__)
code = args.get('<code>')
try:
if not code:
with open(CPUINFO_PATH) as file:
match = re.search(CPUINFO_RE, file.read(), re.MULTILINE)
if not match:
error(f"Could not find revision code in '{CPUINFO_PATH}'.")
code = match.group(1)
code = int(code, 16)
except IOError:
error(f"Could not open '{CPUINFO_PATH}'.")
except ValueError:
error(f"Code '{code}' is not hexadecimal number.")
if (code >> NEW_STYLE_BIT) & 0x1:
width = max(1 + len(name) for _, (name, _) in NEW_STYLE.items())
for field, _ in re.findall(r'((.)\2*)', NEW_STYLE_BITS[::-1]):
length = len(field)
value = code & ((1<<length)-1)
code = code >> length
info = NEW_STYLE.get(field)
if info:
name = info[0]
desc = info[1].get(value, '<UNKNOWN>')
print(f"{name:{width}}: {desc}")
else:
width = max(1 + len(name) for name in OLD_STYLE['names'])
warranty = code & (1<<OLD_STYLE_WARRANTY_BIT)
code = code & ~(1<<OLD_STYLE_WARRANTY_BIT)
width = max(width, 1 + len(NEW_STYLE[OLD_STYLE_WARRANTY_FIELD][0]))
info = OLD_STYLE.get(code)
if info:
for name, desc in zip(OLD_STYLE['names'], info):
print(f"{name:{width}}: {desc}")
info = NEW_STYLE[OLD_STYLE_WARRANTY_FIELD]
name = info[0]
desc = info[1].get(bool(warranty))
print(f"{name:{width}}: {desc}")
if __name__ == '__main__':
main()
|