Browse code

Add implementation

Robert Cranston authored on 29/10/2023 05:38:08
Showing 1 changed files
1 1
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()