Bus PirateでSPI接続したフラッシュメモリを読み出す

@kitokay さんが取りあげられていて、
きっとドハマりする人が多くなると思うので、使い方やドハマりポイントとか書いていきます。

# 注意

お仕事でフラッシュメモリを使う場合は、Bus Pirateではなく、品質の保証されたROMライタで正確に読み書きすることを推奨します・・・。

# はじめに

検証環境：
- Ubuntu 17.10
- Bus Pirate v3.6
- 読み出し対象：Macronix MX25L3206E

```
Bus Pirate v3a
Firmware v5.10 (r559)  Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
CFG1:0xFFDF CFG2:0xFF7F
```

今回のターゲットは、Macronix MX25L3206E の SOP8 200mil品 です。32Mbit(=4MB)の容量があり、3,000円くらいの安価な無線ルータのファームウェアの保存や、機器の設定の保持等によく使われています。


# 目次

1. フラッシュメモリのデータシート入手
1. 正確な配線と接触の確保
2. Bus Pirateの準備
2. JEDEC IDの読み出し
3. ファームウェアの読み出し
4. トラブルシューティング

## フラッシュメモリのデータシート入手

データシート無くして中身は読めません。通信方式や絶対定格などが記載されているので入手しましょう。
幸いにも今回は公開されていました。
http://www.macronix.com/Lists/Datasheet/Attachments/6665/MX25L3206E,%203V,%2032Mb,%20v1.5.pdf

## 正確な配線と接触の確保

読み出し対象のフラッシュメモリは、以下のような秋月電子等で販売されている変換基板にはんだづけしてピンヘッダを立てると良いです。

[秋月電子通商 ＳＯＰ８（１．２７ｍｍ）ＤＩＰ変換基板　金フラッシュ　（９枚入）](http://akizukidenshi.com/catalog/g/gP-05154/)

テストクリップで直接ピンにアタッチしてもいいですが、振動で外れやすいのでこのような用途にはおすすめしません。

[秋月電子通商 マイクロテストクリップＦＰ－７Ｓ（０．２～０．５ｍｍピッチＩＣ用　世界最小・超極細）](http://akizukidenshi.com/catalog/g/gP-06501/)

Bus PirateとMX25L3206Eとの結線は以下のように行いました。GNDとの電圧差の絶対定格が4.6Vまでなので、MX25L3206EのVCCピンはBus Pirateの+5Vピンに接続してはいけません。

![pin_connection.png](https://qiita-image-store.s3.amazonaws.com/0/230940/064c712d-7108-811a-c3e2-fa0f54b8277a.png)

加えて、Bus PirateのVPUピンを3V3ピンに接続します。3V3ピンには、Bus PirateのVPUピン、MX25L3206EのHOLD#、WP#を接続するため、ユニバーサル基板にピンヘッダを意図的にジャンプさせた治具を使って接続しています。（普段は机上に置いた際の短絡を防ぐため、はんだづけ部分に絶縁テープを貼付しています）

![3v3_0.jpeg](https://qiita-image-store.s3.amazonaws.com/0/230940/4b9ce3c5-40dd-98a9-1baf-bad3264ac80a.jpeg)

![3v3_1.jpeg](https://qiita-image-store.s3.amazonaws.com/0/230940/b71496e2-dde9-36df-7ca0-101526e69707.jpeg)

このような配線をするため、Bus Pirate付属のリボンケーブルは使用せず、ふつーのブレッドボードジャンパワイヤを使用しています。
[ブレッドボード・ジャンパー延長ワイヤ（メス－メス）　１５ｃｍ青　（１０本入）](http://akizukidenshi.com/catalog/g/gP-03476/)

HOLD#を接続しなければいけない理由はデータシートのに記載されているのですが、このピンをHighレベルに維持しないと、Bus PirateからMX25L3206Eへの書き込みコマンドを受け付けてくれないからです。（改めて確認すると、読み出しするだけならHOLD#は接続しなくてよかった気もする。）


## Bus Pirateの準備

ここからやっとBus pirateを使ったお話です。

```
$ cu -l /dev/ttyUSB0 -s 115200

HiZ> m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. KEYB
9. LCD
x. exit(without change)

(1)>5  << SPI を選ぶ

Set speed:
 1.  30 KHz
 2. 125 KHz
 3. 250 KHz
 4.   1 MHz
 5.  50 KHz
 6. 1.3 MHz
 7.   2 MHz
 8. 2.6 MHz
 9. 3.2 MHz
10.   4 MHz
11. 5.3 MHz
12.   8 MHz

(1)>4  << 1MHzを選ぶ
Clock polarity:
 1. Idle low *default
 2. Idle high

(1)>     << Enterを押してデフォルトでOK
Output clock edge:
 1. Idle to active
 2. Active to idle *default

(2)>     << Enterを押してデフォルトでOK
Input sample phase:
 1. Middle *default
 2. End

(1)>     <<  Enterを押してデフォルトでOK
CS:
 1. CS
 2. /CS *default

(2)>     <<  Enterを押してデフォルトでOK
Select output type:
 1. Open drain (H=Hi-Z, L=GND)
 2. Normal (H=3.3V, L=GND)

(1)>2     << 2 を選ぶ
Ready
SPI> W << 接続したフラッシュメモリへ電源を供給する
POWER SUPPLIES ON
```

以上でフラッシュメモリと通信する準備ができました。


## JEDECIDの読み出し

正常に通信できることを確認するため、まずは確実に値が既知なものを読み出します。このようなフラッシュメモリには製造者固有のJEDEC IDを返答する機能があるため、これを使います。

データシート P.22 より

![jedecid.png](https://qiita-image-store.s3.amazonaws.com/0/230940/fd9a5801-1335-794a-f761-229f7133e3d0.png)

JEDEC IDを読んでみたログを以下に記載します。

```
SPI>{
/CS ENABLED
SPI>0x9f r:3
WRITE: 0x9F READ: 0x00
READ: 0xC2 0x20 0x16　　　　<< 読めた！
SPI>]
/CS DISABLED
```

詳しい読み出しシーケンスは、データシートの P.22 RDIDコマンドをご覧ください。


## ファームウェアの読み出し

あとはREADコマンドを利用して読み出すだけです。読み出しはBus PirateからMX25L3206EへREAD（0x03） 、続けて読み出し位置のアドレスを3バイト送信し、あとはクロックを送信するだけで勝手にフラッシュメモリ全体をシーケンシャルに読んでくれます。以下はとりあえず128バイト読んでみるの図

```
SPI>{ 0x03 0 0 0 r:128 ]
/CS ENABLED
WRITE: 0x03 READ: 0x00
WRITE: 0x00 READ: 0x00
WRITE: 0x00 READ: 0x00
WRITE: 0x00 READ: 0x00
READ: 0x12 0x34 0x56 ...
/CS DISABLED
```

Bus PirateにはREAD用のバッファが43KBくらいまでしか無いので、私は32KBずつ読んでいます。送信するコマンドはこんな感じ。

```
{ 0x03 0 0 0
r:32768
r:32768
r:32768
...
(128回繰り返し)
...
]
```

いつもはtmuxでログを取りながら、
tmuxでキー入力を自動で行って、

```sh
#!/bin/bash

for i in `seq 1 128`; do
    tmux send-keys -t hogehoge-pane-name "r:32768
"
    sleep 20
done
```

ログからこんな感じでバイナリに直しています

```
grep "READ" 20180113-191615-0-4.0.log | sed -e "s/^READ://g" | tr -d '\r' | tr -d '\n' | xxd -r --pr > unbaba.bin
```

※自動化してないのはそんなに何回も行う作業じゃないからなので許して・・・
※Bus Pirate、設定項目多くてめんどくさいよね。いつかflashromコマンドみたいに、Bus Pirateの設定、読み出し、バイナリに固める、を全て一撃で済むようにしたいなあ
※きとけーさん、うまく自動化してないかしら？

## トラブルシューティング

いくつかドハマりした失敗例を挙げていきます

### JEDEC ID が正しくない（再現性あり）

30回JEDEC IDを読んで毎回同じ値が返ってくる場合は・・・

- SPIのモード設定時、これを間違えてない？

```
Select output type:
 1. Open drain (H=Hi-Z, L=GND)
 2. Normal (H=3.3V, L=GND)

(1)>2     << 2 を選ぶ
```

### JEDEC ID が正しくない（再現性なし）

今回のMX25L3206Eの場合はこのケースに当てはまらない。あなたが全く別のフラッシュメモリに対して読み出しをお試し中の場合だと信じて書く。

- Output type を  1. Open drain (H=Hi-Z, L=GND) にする
- 内蔵プルアップ抵抗を使う

こんな感じ

```
Select output type:
 1. Open drain (H=Hi-Z, L=GND)
 2. Normal (H=3.3V, L=GND)

(1)>      << Enterでデフォルトを選択する
Ready
SPI> P
Pull-up resistors ON
```

### JEDEC ID を読んでも0x00しか返ってこない

原因

- MX25L3206Eに電源が入っていない
    - vコマンドで確認して、3.3Vピンがきちんと3.3Vなことを確認する
- MX25L3206Eとの接点が悪い
    - テスタでピンヘッダとピンの間の導通を確認する

### JEDEC ID を読んでも0xFFしか返ってこない

- Bus Pirateの内蔵プルアップ抵抗が On になっていない？
    - pコマンドで内蔵プルアップ抵抗を Off にする

### CSが High にならない

現在のBus Pirateの状態を確認するには vコマンドを用いる。Bus PirateのCSを disabledにした状態でCSピンが H にならない状態だと、内蔵プルアップ抵抗を用いると解消する。

こんなケース。

```
SPI>]
/CS DISABLED
SPI>v
Pinstates:
1.(BR)  2.(RD)  3.(OR)  4.(YW)  5.(GN)  6.(BL)  7.(PU)  8.(GR)  9.(WT)  0.(Blk)
GND     3.3V    5.0V    ADC     VPU     AUX     CLK     MOSI    CS      MISO
P       P       P       I       I       I       O       O       O       I
GND     3.30V   4.96V   0.00V   0.00V   L       L       L       L<<BAD! L
```

通常はこうなる。

```
SPI>]
/CS DISABLED
SPI>v
Pinstates:
1.(BR)  2.(RD)  3.(OR)  4.(YW)  5.(GN)  6.(BL)  7.(PU)  8.(GR)  9.(WT)  0.(Blk)
GND     3.3V    5.0V    ADC     VPU     AUX     CLK     MOSI    CS      MISO
P       P       P       I       I       I       O       O       O       I
GND     3.30V   4.96V   0.00V   0.00V   L       L       L       H<<GOOD!L
```

誤り、誤字などがあれば @yutateshima までお願いします。