公開日:2023/12/09
title.png

28th Season


#28-5 非同期SRAMを使う

 最近使い始めたCmod A7ボード(FPGA)には、基板上に512KバイトSRAMチップがユーザリソースとして搭載されています。今回は、このSRAMをオリジナル16ビットCPUコアMoscoviumからアクセスできるように機能を追加したいと思います。

SRAMチップ概要

 以前使っていたPrimerボードのSDRAM(Synchronous Dynamic RAM)と、Cmod A7ボードのSRAM(Static RAM)を対比してみると、以下の通りとなります。

 SDRAMの場合、何が大変って、外部チップとの間で、クロックに同期して制御する必要があった事です。FPGA内部であれば、Vivadoなど配置配線ツールがクロックツリーを生成してタイミングを合わせてくれますが、外部チップとの間は対象外なので、スキューが合いません。
 クロックのスキュー合わせ等が煩雑で使い難いのです。

 一方、非同期インターフェイスのSRAMは、こちらが出力したストローブ信号のタイミングで制御できるので、制御が楽で使い易いのが嬉しいです。具体的な制御方法を調べてみましょう。

SRAMの仕様

 Cmod A7サポートページによると、搭載されたSRAMは型名IS61WV5128BLLだと判りました。

  IS61WV5128BLLデータシートを検索(別ウインドウが開きます)

 データシートによると、8ビット幅で512Kバイト、かつ、アクセスタイム8nsという高速品ですねぇ〜。アクセス仕様は以下の通りです。

a285_sram_tmg.png
※IS61WV5128BLLデータシートから引用

 上のタイミング図を言葉で簡単にまとめると、以下となります。

  SRAMのアクセス仕様:

 インターフェイスとしては、いたって単純で使い易いです。
 アドレスやデータと、ストローブ信号(CEN,OEN,WEN)との間に、セットアップ時間やホールド時間などのタイミング規定があるので、データシートで確認して設計しなくてはなりません。しかし、このSRAM自体は、アクセスタイム8nsと非常に高速なメモリなので、そんなにシビアに考えなくても、タイミング規定は守ることができるはずです。
 とは言え、少し不安な所もあるので、実装後のタイミングシミュレーションで確認しておくこととします。

Moscoviumからアクセス

 前節で調べた仕様に合わせてアクセスするため、Moscovium @24MHzに適応した読み出し/書き込みタイミングを具体的に考察して行きましょう。

読み出しアクセス

 Moscovium @24MHzに合わせた、SRAMの読み出しサイクルは以下図の通りとします。

a285_sram_rd.png

 上図で、最初のバスサイクルはバイトサイズデータの読み出しサイクル、2個目のバスサイクルはロングワードサイズデータの読み出しサイクルです。読み出しは、Nサイクル/Nバイトで決着しました。
 fCPU=24MHzに於ける1サイクルは、約41.7nsです。SRAMのアクセスタイム8nsには、ずいぶんと余裕が有り、問題なしです。ひっくり返すと、折角のアクセスタイム8ns高速SRAMを活かしきれていない、とも言えます。

 アクセスタイム8nsをフル活用するため、SRAMだけPLLなどでクロックアップしてアクセスするという選択肢も、無くは無いです。しかし、折角、非同期I/Fで扱い易いSRAMに対して、わざわざPLLを追加してクロックアップ、非同期クロック間のデータ授受やタイミング設計(クロックツリー構築、クロックスキュー調整)を経てまでアクセスする必要は無かろうという判断です。

書き込みアクセス

 ちょっと気に食わなかったのは、書き込みサイクルです。以下図に書き込みタイミングを示します。

a285_sram_wr.png

 こちらの図も、最初のバスサイクルはバイトサイズデータの書き込みサイクル、2個目のバスサイクルはロングワードサイズデータの書き込みサイクルです。書き込みは、(N+1)サイクル/Nバイトで決着しました。

 鍵を握ったのは、書き込み許可信号WENの制御仕様でした。

 上の仕様を満たすWENを出力するため、以下の様な内部信号(wrt,wrt_r)を、書き込みサイクルの1サイクル手前から生成しなくては、なりませんでした。

a285_sram_wrn.png

 このため上図の通り、準備サイクル(1サイクル)が追加となりました。こちらのタイミング図では、状態コード(state=)BP,LP状態が相当しています。一度にNバイトを書き込む場合、準備1サイクル+書き込みNサイクルと、読み出しに対して1サイクル遅くなってしまいましたが、致し方なしです。
 所詮、8ビット単位でしかアクセスできず、バンド幅の狭い(アクセスの遅い)メモリなので、許容する事とします。

 更に、1/2サイクルの短パルスを作る都合上、逆相クロックでタイミングを取る必要が生じてしまい(wrt_r)、美しくありません。グリッチなどを生じさせずに、正しいストローブ信号(WEN)を出力できるものでしょうか??一応、机上の期待値としては問題なしと判定したのですが、後ほどタイミングシミュレーションで確認してみましょう。

 更に更に、もう一つ懸案が……。
 こちらのタイミング図で、タイミング違反となる可能性があるのが、WEN↓とアドレスの変化タイミングが同時になってしまっているところです。対象となるタイミング規定は、アドレスセットアップ時間(tSA)です。
 WENアサート(WEN=0)期間中、アドレスは変化してはならないのですが、論理的には同時変化なので、もし、少しでもアドレスの変化時間がWEN↓時間より遅くなってしまうと、タイミング違反となります。
 論理的に対策する場合、書き込みサイクル数を伸ばして同時変化を抑止するしかないのですが、これまた書き込みサイクル数が増加して鈍足アクセスとなってしまう痛い所です。

 ここは、論理的動作でタイミング規定を満たすより、コンパイル(論理合成・配置配線)時にタイミング制約をかけて、アドレスの変化タイミングがWENの変化タイミングよりも早くなる様に実装するのが正しい対処です。

出力遅延制約

 Vivadoの「Flow Navigatorサブウインドウ ⇒ IMPLEMENTATION ⇒ Open Implemented Design ⇒ Edit Timing Constraints」を実行して、出力遅延制約を追加します。「Timing Constraintsウインドウ ⇒ Outputs ⇒ Set Output Delay」で、以下の通り、新しいタイミング制約を追加します。

  出力遅延制約:

 こちらはSTAで確認するのが基本ですが、信号間の相対的な変化タイミングなので、念のため、後ほどタイミングシミュレーションでも確認しておくことにします。

端子割り当て

 Vivado環境を立ち上げた時にダウンロードした制約ファイル(mcoc_constr.xdc)の中に、SRAMとの接続設定がコメントされていました。Moscoviumマイコンに搭載したSRAMC512Kユニットの端子名を「sram_*」で確定したので、以下の様に端子名を差し替え、コメントを外せば終了です。

  ## Cellular RAM
  set_property -dict {PACKAGE_PIN M18 IOSTANDARD LVCMOS33} [get_ports {sram_adr[0]}]
  set_property -dict {PACKAGE_PIN M19 IOSTANDARD LVCMOS33} [get_ports {sram_adr[1]}]
  set_property -dict {PACKAGE_PIN K17 IOSTANDARD LVCMOS33} [get_ports {sram_adr[2]}]
  set_property -dict {PACKAGE_PIN N17 IOSTANDARD LVCMOS33} [get_ports {sram_adr[3]}]
  set_property -dict {PACKAGE_PIN P17 IOSTANDARD LVCMOS33} [get_ports {sram_adr[4]}]
  set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVCMOS33} [get_ports {sram_adr[5]}]
  set_property -dict {PACKAGE_PIN R18 IOSTANDARD LVCMOS33} [get_ports {sram_adr[6]}]
  set_property -dict {PACKAGE_PIN W19 IOSTANDARD LVCMOS33} [get_ports {sram_adr[7]}]
  set_property -dict {PACKAGE_PIN U19 IOSTANDARD LVCMOS33} [get_ports {sram_adr[8]}]
  set_property -dict {PACKAGE_PIN V19 IOSTANDARD LVCMOS33} [get_ports {sram_adr[9]}]
  set_property -dict {PACKAGE_PIN W18 IOSTANDARD LVCMOS33} [get_ports {sram_adr[10]}]
  set_property -dict {PACKAGE_PIN T17 IOSTANDARD LVCMOS33} [get_ports {sram_adr[11]}]
  set_property -dict {PACKAGE_PIN T18 IOSTANDARD LVCMOS33} [get_ports {sram_adr[12]}]
  set_property -dict {PACKAGE_PIN U17 IOSTANDARD LVCMOS33} [get_ports {sram_adr[13]}]
  set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports {sram_adr[14]}]
  set_property -dict {PACKAGE_PIN V16 IOSTANDARD LVCMOS33} [get_ports {sram_adr[15]}]
  set_property -dict {PACKAGE_PIN W16 IOSTANDARD LVCMOS33} [get_ports {sram_adr[16]}]
  set_property -dict {PACKAGE_PIN W17 IOSTANDARD LVCMOS33} [get_ports {sram_adr[17]}]
  set_property -dict {PACKAGE_PIN V15 IOSTANDARD LVCMOS33} [get_ports {sram_adr[18]}]
  set_property -dict {PACKAGE_PIN W15 IOSTANDARD LVCMOS33} [get_ports {sram_dq[0]}]
  set_property -dict {PACKAGE_PIN W13 IOSTANDARD LVCMOS33} [get_ports {sram_dq[1]}]
  set_property -dict {PACKAGE_PIN W14 IOSTANDARD LVCMOS33} [get_ports {sram_dq[2]}]
  set_property -dict {PACKAGE_PIN U15 IOSTANDARD LVCMOS33} [get_ports {sram_dq[3]}]
  set_property -dict {PACKAGE_PIN U16 IOSTANDARD LVCMOS33} [get_ports {sram_dq[4]}]
  set_property -dict {PACKAGE_PIN V13 IOSTANDARD LVCMOS33} [get_ports {sram_dq[5]}]
  set_property -dict {PACKAGE_PIN V14 IOSTANDARD LVCMOS33} [get_ports {sram_dq[6]}]
  set_property -dict {PACKAGE_PIN U14 IOSTANDARD LVCMOS33} [get_ports {sram_dq[7]}]
  set_property -dict {PACKAGE_PIN P19 IOSTANDARD LVCMOS33} [get_ports sram_oen]
  set_property -dict {PACKAGE_PIN R19 IOSTANDARD LVCMOS33} [get_ports sram_wen]
  set_property -dict {PACKAGE_PIN N19 IOSTANDARD LVCMOS33} [get_ports sram_cen]


 以下ページのピン配置表を更新しておきましたのでご確認ください。

  Moscoviumマイコンのピン配置表

 アドレス19本、データ8本、ストローブ3本と、結構な端子数(合計30本)を喰うのですが、Cmod A7ボードに搭載されたFPGA、Xilinx XC7A35Tチップは236ピンのチップスケールBGAパッケージなので、端子数は潤沢です。Cmod A7ボードの48ピンDIP互換ピンヘッダに引き出された46本のI/Oポートを1本も消費することなく、外部SRAMチップと接続することができます。


 一旦、ここで切って✂、次回完結編といたします。次回は、回路設計、タイミングシミュレーション、実機(FPGA)検証と、進める予定です。


btn_arduino.png

btn_prev.png btn_next.png

home