| ... | ... |
@@ -5,6 +5,25 @@ Parse [Raspberry Pi revision codes][]. |
| 5 | 5 |
[`raspi-revcode`]: https://git.rcrnstn.net/rcrnstn/raspi-revcode |
| 6 | 6 |
[Raspberry Pi revision codes]: https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-revision-codes |
| 7 | 7 |
|
| 8 |
+## Usage |
|
| 9 |
+ |
|
| 10 |
+`raspi-revcode --help`: |
|
| 11 |
+ |
|
| 12 |
+``` |
|
| 13 |
+raspi-revcode 1.0 |
|
| 14 |
+ |
|
| 15 |
+Parse Raspberry Pi revision codes. |
|
| 16 |
+ |
|
| 17 |
+Usage: |
|
| 18 |
+ raspi-revcode [<code>] |
|
| 19 |
+ raspi-revcode -h|--help |
|
| 20 |
+ |
|
| 21 |
+Arguments: |
|
| 22 |
+ <code> |
|
| 23 |
+ A hexadecimal revision code to parse. If not given, '/proc/cpuinfo' will be |
|
| 24 |
+ used to get the revision code of the current device. |
|
| 25 |
+``` |
|
| 26 |
+ |
|
| 8 | 27 |
## Install |
| 9 | 28 |
|
| 10 | 29 |
Make sure [Python is installed][Python download], [`pip`][] is available and |
| 11 | 30 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,192 @@ |
| 1 |
+#!/usr/bin/env python3 |
|
| 2 |
+ |
|
| 3 |
+ |
|
| 4 |
+## Help |
|
| 5 |
+ |
|
| 6 |
+""" |
|
| 7 |
+raspi-revcode 1.0 |
|
| 8 |
+ |
|
| 9 |
+Parse Raspberry Pi revision codes. |
|
| 10 |
+ |
|
| 11 |
+Usage: |
|
| 12 |
+ raspi-revcode [<code>] |
|
| 13 |
+ raspi-revcode -h|--help |
|
| 14 |
+ |
|
| 15 |
+Arguments: |
|
| 16 |
+ <code> |
|
| 17 |
+ A hexadecimal revision code to parse. If not given, '/proc/cpuinfo' will be |
|
| 18 |
+ used to get the revision code of the current device. |
|
| 19 |
+""" |
|
| 20 |
+ |
|
| 21 |
+ |
|
| 22 |
+## Imports |
|
| 23 |
+ |
|
| 24 |
+import sys |
|
| 25 |
+import re |
|
| 26 |
+import docopt |
|
| 27 |
+ |
|
| 28 |
+ |
|
| 29 |
+## Constants |
|
| 30 |
+ |
|
| 31 |
+# https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-revision-codes |
|
| 32 |
+CPUINFO_PATH = '/proc/cpuinfo' |
|
| 33 |
+CPUINFO_RE = r'^Revision\s*:\s*(.*)$' |
|
| 34 |
+ |
|
| 35 |
+# https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#old-style-revision-codes |
|
| 36 |
+# https://forums.raspberrypi.com//viewtopic.php?p=176865#p177014 |
|
| 37 |
+OLD_STYLE_WARRANTY_BIT = 24 |
|
| 38 |
+OLD_STYLE_WARRANTY_FIELD = 'W' |
|
| 39 |
+OLD_STYLE = {
|
|
| 40 |
+ 'names': ['Type', 'Revision', 'Memory size', 'Manufacturer'], |
|
| 41 |
+ 0x0002: ['B', '1.0', '256MB', 'Egoman' ], |
|
| 42 |
+ 0x0003: ['B', '1.0', '256MB', 'Egoman' ], |
|
| 43 |
+ 0x0004: ['B', '2.0', '256MB', 'Sony UK' ], |
|
| 44 |
+ 0x0005: ['B', '2.0', '256MB', 'Qisda' ], |
|
| 45 |
+ 0x0006: ['B', '2.0', '256MB', 'Egoman' ], |
|
| 46 |
+ 0x0007: ['A', '2.0', '256MB', 'Egoman' ], |
|
| 47 |
+ 0x0008: ['A', '2.0', '256MB', 'Sony UK' ], |
|
| 48 |
+ 0x0009: ['A', '2.0', '256MB', 'Qisda' ], |
|
| 49 |
+ 0x000d: ['B', '2.0', '512MB', 'Egoman' ], |
|
| 50 |
+ 0x000e: ['B', '2.0', '512MB', 'Sony UK' ], |
|
| 51 |
+ 0x000f: ['B', '2.0', '512MB', 'Egoman' ], |
|
| 52 |
+ 0x0010: ['B+', '1.2', '512MB', 'Sony UK' ], |
|
| 53 |
+ 0x0011: ['CM1', '1.0', '512MB', 'Sony UK' ], |
|
| 54 |
+ 0x0012: ['A+', '1.1', '256MB', 'Sony UK' ], |
|
| 55 |
+ 0x0013: ['B+', '1.2', '512MB', 'Embest' ], |
|
| 56 |
+ 0x0014: ['CM1', '1.0', '512MB', 'Embest' ], |
|
| 57 |
+ 0x0015: ['A+', '1.1', '256MB/512MB', 'Embest' ], |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+# https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#new-style-revision-codes |
|
| 61 |
+NEW_STYLE_BIT = 23 |
|
| 62 |
+NEW_STYLE_BITS = 'NOQuuuWuFMMMCCCCPPPPTTTTTTTTRRRR' |
|
| 63 |
+NEW_STYLE = {
|
|
| 64 |
+ 'N': ['Overvoltage', {
|
|
| 65 |
+ 0x0: 'Overvoltage allowed', |
|
| 66 |
+ 0x1: 'Overvoltage disallowed', |
|
| 67 |
+ }], |
|
| 68 |
+ 'O': ['OTP Program', {
|
|
| 69 |
+ 0x0: 'OTP programming allowed', |
|
| 70 |
+ 0x1: 'OTP programming disallowed', |
|
| 71 |
+ }], |
|
| 72 |
+ 'Q': ['OTP Read', {
|
|
| 73 |
+ 0x0: 'OTP reading allowed', |
|
| 74 |
+ 0x1: 'OTP reading disallowed', |
|
| 75 |
+ }], |
|
| 76 |
+ # 'uuu': ['Unused', {
|
|
| 77 |
+ # u: 'Unused' for u in range(2**len('uuu'))
|
|
| 78 |
+ # }], |
|
| 79 |
+ 'W': ['Warranty bit', {
|
|
| 80 |
+ 0x0: 'Warranty is intact', |
|
| 81 |
+ 0x1: 'Warranty has been voided by overclocking', |
|
| 82 |
+ }], |
|
| 83 |
+ # 'u': ['Unused', {
|
|
| 84 |
+ # u: 'Unused' for u in range(2**len('u'))
|
|
| 85 |
+ # }], |
|
| 86 |
+ 'F': ['New flag', {
|
|
| 87 |
+ 0x1: 'New-style revision', |
|
| 88 |
+ 0x0: 'Old-style revision', |
|
| 89 |
+ }], |
|
| 90 |
+ 'MMM': ['Memory size', {
|
|
| 91 |
+ 0x0: '256MB', |
|
| 92 |
+ 0x1: '512MB', |
|
| 93 |
+ 0x2: '1GB', |
|
| 94 |
+ 0x3: '2GB', |
|
| 95 |
+ 0x4: '4GB', |
|
| 96 |
+ 0x5: '8GB', |
|
| 97 |
+ }], |
|
| 98 |
+ 'CCCC': ['Manufacturer', {
|
|
| 99 |
+ 0x0: 'Sony UK', |
|
| 100 |
+ 0x1: 'Egoman', |
|
| 101 |
+ 0x2: 'Embest', |
|
| 102 |
+ 0x3: 'Sony Japan', |
|
| 103 |
+ 0x4: 'Embest', |
|
| 104 |
+ 0x5: 'Stadium', |
|
| 105 |
+ }], |
|
| 106 |
+ 'PPPP': ['Processor', {
|
|
| 107 |
+ 0x0: 'BCM2835', |
|
| 108 |
+ 0x1: 'BCM2836', |
|
| 109 |
+ 0x2: 'BCM2837', |
|
| 110 |
+ 0x3: 'BCM2711', |
|
| 111 |
+ 0x4: 'BCM2712', |
|
| 112 |
+ }], |
|
| 113 |
+ 'TTTTTTTT': ['Type', {
|
|
| 114 |
+ 0x0: 'A', |
|
| 115 |
+ 0x1: 'B', |
|
| 116 |
+ 0x2: 'A+', |
|
| 117 |
+ 0x3: 'B+', |
|
| 118 |
+ 0x4: '2B', |
|
| 119 |
+ 0x5: 'Alpha (early prototype)', |
|
| 120 |
+ 0x6: 'CM1', |
|
| 121 |
+ 0x8: '3B', |
|
| 122 |
+ 0x9: 'Zero', |
|
| 123 |
+ 0xa: 'CM3', |
|
| 124 |
+ 0xc: 'Zero W', |
|
| 125 |
+ 0xd: '3B+', |
|
| 126 |
+ 0xe: '3A+', |
|
| 127 |
+ 0xf: 'Internal use only', |
|
| 128 |
+ 0x10: 'CM3+', |
|
| 129 |
+ 0x11: '4B', |
|
| 130 |
+ 0x12: 'Zero 2 W', |
|
| 131 |
+ 0x13: '400', |
|
| 132 |
+ 0x14: 'CM4', |
|
| 133 |
+ 0x15: 'CM4S', |
|
| 134 |
+ 0x16: 'Internal use only', |
|
| 135 |
+ 0x17: '5', |
|
| 136 |
+ }], |
|
| 137 |
+ 'RRRR': ['Revision', {
|
|
| 138 |
+ rev: f'1.{rev}' for rev in range(2**len('RRRR'))
|
|
| 139 |
+ }], |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+## Error |
|
| 143 |
+ |
|
| 144 |
+def error(msg): |
|
| 145 |
+ print(msg, file=sys.stderr) |
|
| 146 |
+ exit(1) |
|
| 147 |
+ |
|
| 148 |
+## Main |
|
| 149 |
+ |
|
| 150 |
+def main(): |
|
| 151 |
+ args = docopt.docopt(__doc__) |
|
| 152 |
+ code = args.get('<code>')
|
|
| 153 |
+ try: |
|
| 154 |
+ if not code: |
|
| 155 |
+ with open(CPUINFO_PATH) as file: |
|
| 156 |
+ match = re.search(CPUINFO_RE, file.read(), re.MULTILINE) |
|
| 157 |
+ if not match: |
|
| 158 |
+ error(f"Could not find revision code in '{CPUINFO_PATH}'.")
|
|
| 159 |
+ code = match.group(1) |
|
| 160 |
+ code = int(code, 16) |
|
| 161 |
+ except IOError: |
|
| 162 |
+ error(f"Could not open '{CPUINFO_PATH}'.")
|
|
| 163 |
+ except ValueError: |
|
| 164 |
+ error(f"Code '{code}' is not hexadecimal number.")
|
|
| 165 |
+ if (code >> NEW_STYLE_BIT) & 0x1: |
|
| 166 |
+ width = max(1 + len(name) for _, (name, _) in NEW_STYLE.items()) |
|
| 167 |
+ for field, _ in re.findall(r'((.)\2*)', NEW_STYLE_BITS[::-1]): |
|
| 168 |
+ length = len(field) |
|
| 169 |
+ value = code & ((1<<length)-1) |
|
| 170 |
+ code = code >> length |
|
| 171 |
+ info = NEW_STYLE.get(field) |
|
| 172 |
+ if info: |
|
| 173 |
+ name = info[0] |
|
| 174 |
+ desc = info[1].get(value, '<UNKNOWN>') |
|
| 175 |
+ print(f"{name:{width}}: {desc}")
|
|
| 176 |
+ else: |
|
| 177 |
+ width = max(1 + len(name) for name in OLD_STYLE['names']) |
|
| 178 |
+ warranty = code & (1<<OLD_STYLE_WARRANTY_BIT) |
|
| 179 |
+ code = code & ~(1<<OLD_STYLE_WARRANTY_BIT) |
|
| 180 |
+ width = max(width, 1 + len(NEW_STYLE[OLD_STYLE_WARRANTY_FIELD][0])) |
|
| 181 |
+ info = OLD_STYLE.get(code) |
|
| 182 |
+ if info: |
|
| 183 |
+ for name, desc in zip(OLD_STYLE['names'], info): |
|
| 184 |
+ print(f"{name:{width}}: {desc}")
|
|
| 185 |
+ info = NEW_STYLE[OLD_STYLE_WARRANTY_FIELD] |
|
| 186 |
+ name = info[0] |
|
| 187 |
+ desc = info[1].get(bool(warranty)) |
|
| 188 |
+ print(f"{name:{width}}: {desc}")
|
|
| 189 |
+ |
|
| 190 |
+ |
|
| 191 |
+if __name__ == '__main__': |
|
| 192 |
+ main() |