FPGAボード(Sipeed Lichee Tang Primer)には、SDRAMが搭載されていることが1つのセールスポイントになっていました。
前回はSDRAMを制御する上での必要事項を調査しただけで終わってしまいました。今回は、Moscoviumマイコンに組み込めるよう、詳細設計を進めて行きたいと思います。
CPU (Moscoviumコア)に手を入れるのは久しぶりだなぁ〜。
Moscoviumは16ビットのオリジナルCPUコアですから、データバスが16ビットであることに加えて、アドレスバスも16ビットしか持っていません。16ビットアドレスでアクセス可能なメモリ空間は、216=64Kバイトまでです。
16ビット(64Kバイト)空間しか持たないMoscoviumでは、8MバイトSDRAMの一部分にしかアクセスできません。メモリ空間の拡張が必要となります。
「アドレスバスを16ビット(64Kバイト空間) → 32ビット(4Gバイト空間)に拡張する」
これが課題。
なるべくスマートに、簡易に解決したいです。
現在のメモリアクセス命令は?と言うと:
これらの命令では、16ビット汎用レジスタを使ってアドレス指定しているため、16ビットアドレスまでしか指定することが出来ません。
Rd1:Rd2の様に、汎用レジスタを2個ペア👭で使って、32ビット拡張アドレスを表現する方法がありますが、こちらの命令コードフィールドが示すように、Moscoviumの命令コードには、あまり空きがありません。CPUの内部バス構造に関しても、32ビット分のアドレス情報を同時に転送するバスが足りず、拡張が必要となってしまいます。
そこで、32ビット拡張アドレス指定に汎用レジスタを使う案は却下して、追加となる上位16ビットアドレス値を保持する、専用の制御レジスタを割り当てて対処しようと考えました。上位16ビットアドレスを指定する制御レジスタは、一時レジスタtrを流用することとします。
以下図が、32ビット拡張アドレス対応後のCPUブロック図です。
レジスタファイルRGFブロックからメモリバスインターフェイスMEMブロックへ専用線で出力されている「tr」が、一時レジスタtrからの出力データです。この値が32ビット拡張アドレスの上位16ビットとなります。
専用線でMEMブロックへデータを送るので、レジスタを選択したり、内部バスに載せたりする論理追加は必要なく、最小限のリソース増加で実現できます。
従来の命令群(ldb命令、stb命令、ldw命令、stw命令)を使った時に、常にTrレジスタを上位16ビットとする32ビット拡張アドレスを出力しても良いのかもしれませんが、旧命令群の互換性を最大限確保するため、32ビット拡張アドレスでメモリアクセスする専用の命令を追加することにしました。
旧メモリアクセス命令に「x」が付いただけの命令を4個追加します。アセンブラで記述する場合は、「Tr:」を省略可能です。ペアになるレジスタはTrだと決まっているので、記述する必要が無いのです。
これらの命令では、一時レジスタtrと、汎用レジスタRnをペアで使って32ビット拡張アドレスを指定します。Trレジスタが上位16ビット、Rnレジスタが下位16ビットアドレスとなります。
例)32ビット拡張アドレス空間へのアクセス
// 0x80_0000〜0xff_ffffまで、0xaaaaを書き込む
ldwi r6,0x0080
ldwi r7,0x0000
ldwi r4,0x00ff
ldwi r5,0xffff
ldwi r0,0xaaaa
ldbiu r1,0
loop:
movtc tr,r6
stwx [r7],r0 // 32bit空間[tr:r7]アクセス命令
addi r7,2
adc r6,r1
cmp r7,r5
cmb r6,r4
blo loop
Moscoviumコアは、新規追加した4命令実行時以外では、32ビット拡張アドレスバスの上位側16ビットを常に0x0000出力することとします。Trレジスタ値には関係なしで、常に0x0000です。
Tr:Rnのレジスタペアで32ビット拡張アドレス指定できるのは、追加した4命令だけです。プログラムカウンタpc、割り込みベクタiv、スタックポインタspなどのアドレス指定する制御レジスタは16ビットのままなので、今まで通り16ビットアドレス空間にしかアクセスできません。
これにより、新規追加した4命令を使わない限り、従来のプログラムは全く同じ動作となります。従来機能に影響を与えることなく、新機能(32ビット空間アクセス)が実現できました。
意外と簡単でしたね。命令4個を追加しただけでアドレス空間が32ビット(4Gバイト)に広がりました。Good。
次にバスステートコントローラBUSCを拡張して行きます。
Moscoviumコアは、アドレスバスを32ビット(4Gバイト空間)に拡張しましたが、BUSCとしては24ビット(16Mバイト空間)とします。今回使うSDRAMが8Mバイトなので、24ビットで必要十分なためです。アドレスのビット数が増えると、その分復号論理が大きくなって遅くなるので必要最小限でとどめるのが賢いやり方です。
アドレスマップを以下図のように拡張します。
ご覧の通り、拡張した24ビット(16Mバイト)空間の最下位アドレス範囲に従来の16ビット(64Kバイト)空間がマッピングされています。
※拡張するのはメモリバスだけなので、フェッチバス側は従来通り16ビット(64Kバイト)空間です。
Moscoviumは、追加した4命令以外の命令では、上位16ビットアドレスに0x0000を出力しているので、上のアドレスマップ拡張後も、従来プログラムは0x00_0000〜0x00_ffffまでの64Kバイト空間にアクセスすることとなり、従来プログラムがそのまま動作します。
Tr=0x0080〜0x00ffに設定して、追加した4命令を使うことでSDRAM(8Mバイト)へアクセスすることが可能となります。
追加した4命令は、Tr=0としてアクセスした場合、0x00_0000〜0x00_ffffまでの64Kバイト空間にアクセスするので、旧メモリアクセス命令(ldb命令、stb命令、ldw命令、stw命令)と全く同じ動作となります。
BUSCとしては、以下の様な論理拡張が必要となります。
SDRAMアクセスは、後で設計するSDRAMコントローラに丸投げするので、BUSCとしての修正量はごくわずかです。
なお、Moscoviumが出力する32ビット拡張アドレスの最上位8ビットは、今回のBUSCではDo not careですが、将来の更なる拡張を見通して、最上位8ビット=0x00で使用することを推奨します。
SDRAMコントローラSDRAMC8Mユニットのブロック図を以下図に示して、各ブロックの解説を続けます。
以下表にSDRAMC8MユニットのI/Oレジスタ一覧を示し、その後ろに解説を続けます。
Addr | Name | R/W | Bit:15〜0 | Memo | |||||||||||||||
0xf0f0 | sdrmctl | RW |
TS TE |
− | − | − | − | − | − | − |
SD RM |
SR DY |
− | − | − | − | − | − | sdram control |
BUSC出力のセレクト信号はbcs_sdrc_nです。上表の範囲でアサートされるので、SDRAMC8Mユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。
ここで、オリジナル16ビットMoscoviumマイコンからSDRAMへアクセスする方法を検討しておきます。
以上より、SDRAMコントローラが実現すべきプロトコルは、以下の通りとなります。
リードサイクル(CL=3、4バースト、プリチャージ) | |||||||||||
CMD | ACT | NOP | RDC | NOP | PRE | NOP | |||||
ADR | ROW | X | CLM | X | ALL | X | |||||
DAT | X | X | X | X | X | DAT1 | (DAT2) | ||||
AC SPEC | tRCD | tRCD | |||||||||
tRAS | tRAS | tRAS | tRAS | ||||||||
CL3 | CL3 | CL3 | |||||||||
tRP | tRP | ||||||||||
ライトサイクル(シングル、プリチャージ) | |||||||||||
CMD | ACT | NOP | WRC | NOP | PRE | NOP | |||||
ADR | ROW | X | CLM | X | ALL | X | |||||
DAT | X | X | DAT1 | X | X | X | |||||
AC SPEC | tRCD | tRCD | |||||||||
tRAS | tRAS | tRAS | tRAS | ||||||||
tWR | tWR | ||||||||||
tRP | tRP | ||||||||||
オートリフレッシュサイクル | |||||||||||
CMD | CBR | NOP | NOP | NOP | NOP | NOP | |||||
ADR | X | X | X | X | X | X | |||||
DAT | X | X | X | X | X | X | |||||
AC SPEC | tRC | tRC | tRC | tRC | tRC | tRC | |||||
パワーオンシーケンス | |||||||||||
CMD | PRE | NOP | MRS | NOP | CBR | NOP | NOP | NOP | NOP | NOP | ※ |
ADR | ALL | X | MOD | X | X | X | X | X | X | X | |
DAT | X | X | X | X | X | X | X | X | X | X | |
AC SPEC | tRP | tRP | |||||||||
tMRS | tMRS | ||||||||||
tRC | tRC | tRC | tRC | tRC | tRC |
※パワーオンシーケンス実行後、無条件でオートリフレッシュサイクルへ遷移します。パワーオンシーケンスには、少なくとも2回のオートリフレッシュサイクルが必要なためです。
セルの各列は、96MHzクロックで1サイクル(約10.4ns)を表しています。ADR,DAT欄の「X」は、Do not care/HIGH-Zです。コマンドCMDについてはこちらを参照してください。
凝った使い方をしないのであれば、SDRAMへのアクセスは、上表に示した4つのシーケンスを実現すれば良く、回路の実装自体はそれほど難しくありません。
Moscoviumのメモリバス@24MHzに対して、SDRAM @96MHzの回路となります。AD変換器の時の様に非同期設計していては、信号やり取りに時間がかかってアクセス時間が伸びますので、24MHz⇔96MHz間は同期設計します。
24MHz⇔96MHz間のデータ転送方法については、クロックツリーの所(次回)で解説するので、ここではスキップします。
24MHz⇔96MHz間同期設計としたところ、アクセス時間は以下の通りとなりました。
計算、合わないのですけどね……。
6サイクル@96MHzなら、24MHz換算では1.5サイクルなので、2サイクル@24MHzにできるんじゃないの?ってことですよね?ただ、それをするには、一部データが24MHzの半相転送(1.5サイクルの0.5部分ね)になったり、タイミングが厳しくなってしまうのです。それを回避しようとすると、3サイクル@24MHzになってしまうのよ〜。
と、言う事で、24MHzメモリバスとのインターフェイスオーバヘッドによって、リードに関しては3サイクル@24MHzとなりました。ライトは2サイクルで実行できます。
あんまり速くはないですね……。でも、所詮SDRAMなんてメインメモリだから。メインメモリは遅いもの、と相場は決まってますから〜。そのうちキャッシュでも挟めば良いことじゃない?
一気に、拡張や新規設計が必要なユニットの設計を済ませてしまいました。しかしここから先、まだ大きな落とし穴が開いていようとは……。実物(FPGAボード)で実験するにはまだ、ひと穴飛び越さないとなりません。続きは次回。