MicroPython的午睡(19) ラズパイPico、「普通の」SRAM接続に手こずる

Joseph Halfmoon

前回前々回とシリアル接続のメモリをRaspberry Pi PicoのMicroPythonで読み書きしてみました。今回は「普通の」バスのSRAMを読み書きしてみようと思います。今時、「普通の」バスを扱えるMCUなど滅多に見ないし、ラズパイPicoにも外バスはありません。例のPIO使えばデキそうなんだけれど。

※「MicroPython的午睡」投稿順 Indexはこちら

アドレスバス、データバス、昔の「マイコン」、例えばZ80とか8086とかには普通に出てましたパラレルバス。ROM/RAM搭載する「マイクロコントローラ」でも外部バスが利用できるものが結構ありました。しかし、昨今のマイコンではパラレルのアドレスバスとかデータバスとかサッパリ見なくなりました(手元のマイコンでは1機種出せるものがあります。)前回までに使ったシリアル接続の「今時」のマイコン用メモリを買ったついでに、古典的なパラレルのアドレス、データ端子が出ている「(昔)普通の」SRAMも買ってしまいました(秋月電子通商殿には在庫ありです。)

Seiko EPSON製SRM2B256SLMX

256Kビット、8ビット幅のメモリなので32Kバイト品です。アドレス15本、データ8本を含み28ピンのSOPパッケージ品です。1.27mmピッチの端子の表面実装品なので老眼の目をこすりながらはんだ付けしてブレークアウト化しました。前回使用のマイクロチップ23LC512が8ピンDIPで64Kバイトだったですから、見た目の大きさの割には小容量です。多分、すでに製造されていないのではないかと想像します。

この「古色蒼然とした」感じのメモリをラズパイPicoに接続して読み書きを試みました。例のPIO(Programable IO)あればこそ、のトライです。PIOのSM(ステートマシン)をプログラムすればできないことは無い筈との思い込み。実際、「制限ありで」読み、書きできたのですが、大変カッコ悪かったです。未だちゃんと理解できていないラズパイPicoのPIOなので、正しいやり方を調査中であります。ということで今回は失敗含みの「トライアル」実装編であります。

読み書きテストに使用したMicroPythonコードの全文は末尾にあります。

さて、ラズパイPicoの端子を総動員すれば、アドレス15本、データ8本、そして必要な制御信号3本の合計26本の信号を取り出すことは可能です。しかし、そんなことをすると他に何もできないPicoになってしまいます。このようなバス接続のSRAMにR/Wできる機能は、後で他のシステムを「乗っ取るために」使おうと心の中で考えています。使用端子数を減らすためにとりあえず

アドレスバスの上の方は0固定

で本数をバッサリ削りました。アドレス線は6本のみ。下の64バイトにしかアクセスできませぬ。なんとかRAMとしての動作は確認できるでしょう。

RPiPico_2B256schematicごちゃごちゃした実際のブレッドボード配線写真を下に掲げます。「バス」を観察するためにDigilent AnalogDiscovery2の「ロジアナ」を接続しているのでさらに混雑しています。途中、上の回路図にも配線間違いなど多く、切れました。ブレッドボードでパラレルバスの実配線大変だな。今時使うもんじゃありません。

2B256DUT

末尾に掲げたプログラムの問題点は2つ。

  1. WRITE時に6ビットのアドレスと下2ビットのデータは出力できるが上6ビットのデータ線がピクリともしない。一応アドレス6ビット+データ8ビット=14ビットの出力させるつもりで書いているのだけれど。
  2. WRITE時には出力の「データバス」を、READのために入力に切り替えたいのだけれど、うまい切り替えの方法が分からない。PIOの外で無理やり切り替えた。

1は少しチェックしてみたのだけれど配線間違えではなさそう。PIOの設定の問題?かもしれないので後でまたチェックすることとし、今回のメモリR/Wでは、データ線は下2本としてしまいました。

一応、末尾のプログラムを動作させると、4か所に書き、4か所から読み出してそれらしい結果を返すことは返します。

0x03= 03
0x01= 01
0x02= 02
0x00= 00

上の問題2に書きましたが、PIOを使う上で一番の分からなかったのは、双方向のデータバスの制御です。現時点では無理やり感が強い以下のやり方しています。

  • 書き込み用(データバスは出力)と読み込み用(データバスは入力)の2個のSMのコードを用意して使い分けることでデータバスの方向の機能的な切り替えを行ってみた
  • 書き込みした後、端子モードが出力モードのままになってしまう。するとREAD時にメモリから出力とバッティングしてしまって読み出せない。そこで読み込み前にSMの外側で端子を入力モードに強制切り替えして対応。

SMの切り替えといい、端子のモード切替といい、とても見苦しい上です。バスという雰囲気じゃありません。もっと良い方法がないものかと探索中です。

とりあえず、現時点の動作の波形は以下に。2.7Vでアクセス100nsのRAMです。テストプログラムでは、それをとても遅い100kHzのクロックで触っているので、CS/WE/OEの波形のタイミングなどは「テキトー」です。もしかするとここにもまずいところあるかも。

書き込みのときの波形

Raspberry Pi Pico, ext. memory write

読み出しのときの波形

Raspberry Pi Pico, ext. memory readうーむ、スッキリしないことこの上ないけれど、本日は時間切れだな。PIOについては後日また調査。

MicroPython的午睡(18) ラズパイPico、SPIでシリアルSRAM接続 へ戻る

MicroPython的午睡(20) ラズパイPico、PIO操作は最大8ビット? へ進む

とりあえずのR/Wテストプログラム(トライアル編、いろいろ問題アリ)
from rp2 import PIO, asm_pio
from machine import Pin, Timer
import time

weB = Pin(18, Pin.OUT)
oeB = Pin(19, Pin.OUT)
csB = Pin(20, Pin.OUT)
weB.value(1)
oeB.value(1)
csB.value(1)

def writeMEM(adr, dat):
    temp = ((dat & 0xFF) << 6) | (adr & 0x3F)
    sm0.put(temp)
    
def readMEM(adr):
    sm1.put((adr & 0x3F))
    temp = sm1.get() & 0xFF
    return temp

@asm_pio(sideset_init=(rp2.PIO.OUT_HIGH,rp2.PIO.OUT_HIGH,rp2.PIO.OUT_HIGH), out_init=(rp2.PIO.OUT_LOW,)*14, out_shiftdir=PIO.SHIFT_RIGHT)
def writeBUS():
    pull()
    out(pins, 14).side(0x7)
    nop().side(0x2)         [1]
    nop().side(0x7)

@asm_pio(sideset_init=(rp2.PIO.OUT_HIGH,rp2.PIO.OUT_HIGH,rp2.PIO.OUT_HIGH), out_init=(rp2.PIO.OUT_LOW,)*6, out_shiftdir=PIO.SHIFT_RIGHT)
def readBUS():
    pull()
    out(pins, 6).side(0x7)
    nop().side(0x1)         [1]
    in_(pins, 2)
    nop()	                [1]
    nop().side(0x7)         [1]
    push()


sm0 = rp2.StateMachine(0, writeBUS, freq=100000, sideset_base=Pin(18), out_base=Pin(0))
sm1 = rp2.StateMachine(1, readBUS,  freq=100000, sideset_base=Pin(18), out_base=Pin(0), in_base=Pin(6))

sm0.active(1)
writeMEM(0x03, 0x03)
writeMEM(0x02, 0x02)
writeMEM(0x01, 0x01)
writeMEM(0x00, 0x00)
time.sleep_ms(1)
sm0.active(0)

for pidx in range(6, 14):
    Pin(pidx, Pin.IN)

sm1.active(1)
d3 = readMEM(0x03)
d1 = readMEM(0x01)
d2 = readMEM(0x02)
d0 = readMEM(0x00)
print("0x03={0:02x}".format(d3))
print("0x01={0:02x}".format(d1))
print("0x02={0:02x}".format(d2))
print("0x00={0:02x}".format(d0))
sm1.active(0)