Raspberry Pi /proc/cpuinfo の Hardwareは嘘

# はじめに

Raspberry Pi ユーザなら ボードのモデルやプロセッサを調べるとき、
`cat /proc/cpuinfo` を使うと思いますが、ここに表される`Hardware`は`嘘`です。というお話。

```terminal:例
$ cat /proc/cpuinfo
（略）
Hardware    : BCM2835 ⭐️
Revision    : a02082
Serial      : 00000000765fc593

$
```

<br />　

# どれも`BCM2835`と表示される


[公式ドキュメント](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#raspberry-pi-revision-codes)に次の注意書きがあります。
![scr3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/4fd0acf2-ebe1-2952-0ef0-4d84041bdf41.png)

:::note warn
カーネル4.9より、BCM2836、BCM2837、もしくは、BCM2711 プロセッサを搭載しているすべての Raspberry Pi が BCM2835 と報告します。<br/>（よって、）この文字列をプロセッサの検出に使用しないでください。 _以下の情報を使用して_ Revision コードをデコードするか、または、
`cat /sys/firmware/devicetree/base/model`。
:::

<br />　

## `/sys/firmware/devicetree/base/model`

モデルの判別は可能ですが、プロセッサの情報は出てきません。

```terminal
$ cat /sys/firmware/devicetree/base/model
Raspberry Pi 4 Model B Rev 1.4$ 
```
（_ファイル内に改行文字が無い_）


<br />　


# Raspberry Pi のプロセッサたち

[公式ドキュメント](https://www.raspberrypi.com/documentation/computers/processors.html)によると、以下の**プロセッサ**が様々なRaspberry Piに搭載されています。

<dl>
  <dt>・BCM2835</dt>
  <dd>・Raspberry Pi 1 Models A, A+, B, B+<br />・Raspberry Pi Zero<br />・Raspberry Pi Zero W<br />・Raspberry Pi Compute Module 1</dd>
  <dt>・BCM2836</dt>
  <dd>・Raspberry Pi 2 Model B</dd>
  <dt>・BCM2837</dt>
  <dd>・Raspberry Pi 3 Model B<br />・Raspberry Pi 2 Model Bの後期モデル<br />・Raspberry Pi Compute Module 3</dd>
  <dt>・BCM2837B0</dt>
  <dd>・Raspberry Pi 3 Models A+, B+,<br />・Raspberry Pi Compute Module 3+</dd>
  <dt>・BCM2711</dt>
  <dd>・Raspberry Pi 4 Model B,<br />・Raspberry Pi 400<br />・Raspberry Pi Compute Module 4</dd>
</dl>

<br />　

# Revisionコードのデコード方法

[公式ドキュメント](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#new-style-revision-codes)で以下のように説明されています。
![IMG_2165.JPEG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283293/128cda20-8dca-f0b3-1ee3-5d6cc659f728.jpeg)


具体的なデコード方法を、次の2つのRevisionコードを例に説明します。

```terminal
$ cat /proc/cpuinfo # 2つの例

Revision : a02082 （例1）

Revision : d03114 （例2）
```

`Revision`の16進数を2進数にして、右詰めで32ビットを並べる。不足する左側のビットは0とする。（読み易くするために、4ビットづつに区切る）

```terminal
　　　NOQu uuWu FMMM CCCC PPPP TTTT TTTT RRRR
例1  0000 0000 1010 0000 0010 0000 1000 0010
例2  0000 0000 1101 0000 0011 0001 0001 0100
```

|Part|例1|例2|Represents|
|:-:|-:|-:|-|
|N|0|0|Overvoltage|
|O|0|0|OTP Program|
|Q|0|0|OTP Read|
|W|0|0|Warranty bit|
|F|1|1|New Flag<br />　1: new-style revision|
|MMM|2|5|Memory Size<br />　2: 1GB<br />　5: 8GB|
|CCCC|0|0|Manufacturer<br />　0: Sony UK|
|PPPP|2|3|<b>Processor</b><br />　2: BCM2837<br />　3: BCM2711|
|TTTTTTTT|8|17|Type<br />　8: 3B<br />　17: 4B|
|RRRR|2|4|Revision<br />　2: 1.2<br />　4: 1.4|


つまり、

|Revision|Type|Revision|Processor|Memory Size|Manufacturer|
|:-:|:-:|:-:|:-:|:-:|:-:|
|a02082|3B|1.2|BCM2837|1GB|Sony UK|
|d03114|4B|1.4|BCM2711|8GB|Sony UK|

<br />　

# RevisionデコードPythonコード

[公式で紹介されているコード](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#getting-the-revision-code-in-your-program)を参考に、`/proc/cpuinfo`の`Revision`デコードをコマンド化した。

引数にRevisionコードを指定する。引数を省略すると`cat /proc/cpuinfo`から取得する。

```terminal
pi@raspberrypi:~ $ ./rev.py a02082
Revision: a02082
	Model: 3B
	Revision: 1.2
	Memory Size: 1GB
	Processor: BCM2837
	Manufacturer: Sony UK

pi@raspberrypi:~ $ ./rev.py 
Revision: d03114
	Model: 4B
	Revision: 1.4
	Memory Size: 8GB
	Processor: BCM2711
	Manufacturer: Sony UK

pi@raspberrypi:~ $
```

以下のファイルに実行権限を付与することで、コマンドとして機能する。

```python:rev.py
#!/usr/bin/python

import sys

if len(sys.argv) == 1:
	import subprocess
	cmd = "cat /proc/cpuinfo | awk '/Revision/ {print $3}'"
	revcode = subprocess.check_output(cmd, shell=True)
	revcode = revcode.decode('utf-8')[:-1]
else:
	revcode = sys.argv[1]

code = int(revcode, 16)
new = (code >> 23) & 0x1
mem = (code >> 20) & 0x7
man = (code >> 16) & 0xf
proc = (code >> 12) & 0xf
model = (code >> 4) & 0xff
rev = code & 0xf

if new:
	mem = ["256MB", "512MB", "1GB", "2GB", "4GB", "8GB", "16GB", ][mem]
	man = ["Sony UK", "Egoman", "Embest", "Sony Japan", "Embest", "Stadium", ][man]
	proc = ["BCM2835", "BCM2836", "BCM2837", "BCM2711", "BCM2712", ][proc]
	model = ["A", "B", "A+", "B+", "2B", "Alpha (early prototype)", "CM1", "0x07", "3B", "Zero", "CM3", "0x0b", "Zero W", "3B+", "3A+", "Internal use only", "CM3+", "4B", "Zero 2 W", "400", "CM4", "CM4S", "Internal use only", "5", "CM5", "500", "CM5 Lite", ][model]
	print(f"Revision: {revcode}\n\tModel: {model}\n\tRevision: 1.{rev}\n\tMemory Size: {mem}\n\tProcessor: {proc}\n\tManufacturer: {man}\n")
elif 0x2 <= code <= 0x9 or 0xd <= code <= 0x15:
	mem = "256MB" if 0x2 <= code <= 0x9 or 0x12 == code else "256/512MB" if 0x15 == code else "512MB"  
	model = "B" if 0x2 <= code <= 0x6 or 0xd <= code <= 0xf else "A" if 0x7 <= code <= 0x9 else "B+" if 0x10 == code or 0x13 == code else "A+" if 0x12 == code or 0x15 == code else "CM1"
	rev = "1.0" if 0x2 <= code <= 0x3 or 0x11 == code or 0x14 == code else "1.1" if 0x12 == code or 0x14 == code else "1.2" if 0x10 == code or 0x13 == code else "2.0"
	man = "Egoman" if 0x2 <= code <= 0x3 or 0x6 <= code <= 0x7 or 0xd == code or 0xf == code else "Sony UK" if 0x10 <= code <= 0x12 or 0x4 == code or 0x8 == code or 0xe == code else "Qisda" if 0x5 == code or 0x9 == code else "Embest"
	print(f"Revision: {revcode}\n\tModel: {model}\n\tRevision: {rev}\n\tMemory Size: {mem}\n\tManufacturer: {man}\n")
else:
	print(f"Revision: {revcode}\n\tUnknown revision\n")
```



# おわりに

今回の記事を書くきっかけは、QiitaのQ&Aにて、冒頭の話の嘘を信じたことで、Raspberry Pi 4のプロセサを`BCM2835`だと説明された方がおられたことです。


たしかに『なぜ`Hardware`に`BCM2835`と表示されるのか？』・・・今回はこれを調べた結果をまとめました。参考になれば幸いです。

#### 以上