公開日:2023/11/18
title.png

28th Season


#28-2 大容量内蔵RAMを使う

 開発中のオリジナル16ビットCPUコアMoscoviumに於いては、新しく入手したFPGAボードであるCmod A7ボードへの移植を進めています。

 Cmod A7ボードには、Xilinx社のArtix-7ファミリXC7A35TというFPGAチップが搭載されています。
 このチップには、36KビットRAMマクロが搭載されていて、最大50個使用可能です。このRAMマクロを32ビット×1KワードのRAMとして使用すると、RAMマクロ1個当たり4KバイトのRAMとして使うことができます。それが最大50個搭載されているので、合計4Kバイト×50個=200Kバイトと言う大容量RAMとなります。

※手持ちのMCUボードでは、Arduino DUEが96KバイトRAM、ESP32が320KバイトRAMです。

 今回のテーマは、この大容量RAMをMoscoviumマイコンで使用できる様に拡張する事です。

RAMマクロの用途

 Cmod A7ボードに搭載されたFPGAチップである、Xilinx Artix-7ファミリXC7A35Tには、36KビットRAMマクロが50個集積されていて、約200Kバイトのメインメモリとして使用可能です。
 しかし、RAMマクロは色々と用途があって、メインメモリ以外にも、命令ROM命令RAM、大容量のFIFOマクロなどにも使用します。本ページの執筆時点で移植が済んでいませんが、日本語フォントFONTJPユニットや、unicode⇔S-JIS変換UNISJIユニットなどのROMとしても、RAMマクロを消費します。

 メインメモリ(RAM)として使える分のRAMマクロ数は、上の様な用途の分を差し引いて残った容量が最大値となります。200Kバイト全容量をマッピングできる事は無く、恐らく128Kバイト辺りが現実的な最大容量でしょう。

メモリマップ

 Moscoviumコアは設計当初、16ビットアドレス空間として設計しました(下図)。

a156_memap.png

 16ビットでアドレッシング可能な64Kバイト空間に命令ROM,命令RAM,メインメモリ(RAM),I/O領域が設定されているので、メインメモリ(RAM)としては最大40Kバイトの領域を確保していました。
 お解りの様に、全体で64Kバイトの空間しか無いので、この空間に128Kバイトと言う大容量RAMをマッピングすることは不可能、な訳です。


 その後MoscoviumにSDRAM(8Mバイト)を接続した時、アドレス空間を24ビットに拡張したので、128Kバイトの大容量RAMは、そちらの広い空間にマッピングします(下図)。

a282_memmap.png

 拡張RAM領域(ERAM;Extended RAM)としては、最大512Kバイトの領域(0x08_0000〜0x0f_ffff)を割り当てておきます。
 Cmod A7ボード搭載のFPGAチップXC7A35Tとしては最大200Kバイトしか使えないし、ROM等に使う分を差し引くと、ERAMサイズはもっと小さくなります。しかし、Xilinx社製Artix-7ファミリのXC7A50TやXC7A100Tなど上位のFPGAチップを使えば、もっと多くのRAMマクロが使用可能なので、その様なケースを鑑みて、512Kバイト領域を割り当てておく事とします。


 尚、上のメモリマップ図では、Cmod A7ボード上に実装された外部SRAMのアクセス領域として、SRAM領域(0x10_0000〜0x17_ffff)を予約してあります(現時点ではMoscoviumマイコンへ未実装)。外部SRAMチップは8ビット幅512Kバイトなので、ピッタリ収まります。

こちらの記事で外部SRAMチップへのアクセスを追加しました。

2つのRAM領域

 前節でメモリマップに示した通り、メインメモリ(RAM)領域が2か所(RAM領域とERAM領域)に分散してしまいました。

 プログラム次第で色々な使い方があるだろうと思いますが、一般的には複数の領域に分散されているより、1つの領域へフラットにマッピングされていた方が使い易いはずです。
 と言って、32ビットのNihoniumならいざ知らず、16ビットのMoscoviumでは、Trレジスタを上位16ビットアドレスとする拡張アドレスアクセス命令(stwx命令ldwx命令等)を使わないとERAM領域にアクセスできないので、少し使い難いのです。
 更に、Moscoviumコアでは、スタックポインタspが16ビット幅なので、24ビット拡張アドレス空間のERAM領域にスタックを設定することができません。このため、16ビット空間のRAMは残さなくてはなりません。

 そこで、次節の通り目標仕様とします。

目標仕様

 以下に拡張RAM(ERAM)領域の目標仕様を示します。

  目標仕様:

 回路としては、ERAM用として特別な回路を設計する必要は無く、従来のRAM領域用ラッパを改造して対応します。

RAMマクロのサイズは2冪数

 前節に示した目標仕様に基づき、ERAMユニットの設計を進めて行きました。
 RAMマクロをインスタンスする時に指定する属性の中で、総ビット数を設定するMEMORY_SIZE属性と、アドレス入力のビット幅を指定するADDR_WIDTH属性が、あるのですが、以下の様に指定しました。

  `define MCOC_ERAM_ABIT $clog2 (`MCOC_ERAM*1024/4)
  xpm_memory_spram #(
    .ADDR_WIDTH_A(`MCOC_ERAM_ABIT),  // DECIMAL
    .MEMORY_SIZE(`MCOC_ERAM*1024*8),  // DECIMAL
    :
    : (中略)
    :
  ) ramhm (
    .dbiterra(dbiterra_open)
    :
    : (後略)
    :


※Verilog-HDLの$clog2()システムタスクは、データ量(バイト数)を引数として、そのデータをアドレスするのに必要なビット数を返す関数で、整数値が返ります。$clog2 (x)=$ceil ($ln (x)/$ln (2))って事ですね。便利です。

 上の状態で、ERAMサイズ(MCOC_ERAM設定値)を変えながらコンパイルして、実際に使用したRAMマクロ数を確認してみると、どうも、MEMORY_SIZE属性(総ビット数の指定)より、ADDR_WIDTH属性(アドレスビット幅の指定)の方が優先されている様です。
 2冪数以外の例えば48Kバイトに設定した場合でも、64Kバイト分のRAMマクロを消費してしまう事に気が付きました(48Kバイトに対して1段上の2冪数が64Kバイト故)。

 Vivado(Artix-7チップ)の仕様なのかな?

 36KビットRAMマクロ(4Kバイト)が最小単位なので、48Kバイトと言う中途半端(非2冪数)なサイズでも、48Kバイト÷4Kバイト=12個でピッタリ作り込めるはず……。仕様と言い切るにはあまりにお粗末で、バグに近いんじゃないのかな?

※仕様にするなら、MEMORY_SIZE属性とADDR_WIDTH属性の両方を指定させる必要が無い。

 ともあれ、Vivado(手元のバージョンはv2023.1.1)では、2冪数以外のRAM容量を指定しても、結局1段上の2冪数分のRAMマクロが消費されてしまう事が判明しました。よって、MCOC_ERAMコンパイルオプションでERAMサイズを設定する場合には、2冪数サイズとする仕様としました。

  例)`define MCOC_ERAM 64  // 64~512[KB], power of 2

 Cmod A7ボードに搭載されたXC7A35Tでは、128Kバイトの1段上、256Kバイトは、RAMマクロ数(最大200Kバイト使用可能)を超過してしまうので、指定しても、コンパイルするとエラー終了してしまいます。従って、XC7A35Tでの最大ERAMサイズは128Kバイトとなります。
 同じXilinx Artix-7ファミリ👪の上位チップでは、エラー終了しない範囲で最大512Kバイトまで指定可能です。

IDREGユニット

 デフォルトで搭載されているIDREGユニットには、搭載されたRAMサイズを読み出すことのできるidrgramsレジスタがあります。

 メインメモリ(RAM)のサイズをバイト単位の数値で読み出せるレジスタなのですが、16ビット幅なので、最大0xffff=63.99[Kバイト]までしか表現できません。しかも、今まで以下例の様にして、RAM領域の最終アドレスにスタックポインタを設定していたため、idrgramsおよびidrgramtレジスタの仕様を変更してしまうと、下のコーディングを適応していたプログラムが正常動作しなくなってしまいます。

  // set sp to the bottom of ram area
  ldwi r7,idrgramt  // idrg: ram top addr.
  ldw r7,[r7]
  ldwi r6,idrgrams  // idrg: ram size
  ldw r6,[r6]
  add r7,r6
  movtc sp,r7


 そこで、恐らく今まで使い道の無かったであろうと思われる、idrgromtレジスタにERAMサイズを示すフィールドを設けることにしました。こちらの仕様を更新しておきました。
 idrgromtレジスタは、命令ROMの先頭アドレスが読み出せるレジスタなのですが、Moscoviumはリセット解除後、0x0000番地から最初の命令をフェッチして動作開始する仕様なので、常にidrgromt=0x0000で固定であり、使用される事は無かったばす、と言う前提です。

※万一、idrgromtレジスタ値を使用するプログラムを組んでいた場合は「ゴメンナサイ」ですが、idrgramtレジスタやidrgramsレジスタよりは、影響範囲は狭いと信じています。

 変更した仕様では、idrgromtレジスタの下位4ビットから読み出した値をRAME[3:0]と置くと、以下の様にしてERAMサイズを知ることができます。

 上に示した仕様の通り、ERAM非搭載版では、idrgromtレジスタの読み出し値に変更有りません。

デザイン

 Cmod A7ボード用Moscoviumマイコンの設計データ(Vivadoプロジェクト)は、Githubで提供します。以下リンクよりアクセスしてください。

btn_moscovium.png

 ダウンロード方法などについては、トップのREADME.mdをご覧ください。

 デザイン(回路)をCmod A7ボードに書き込む手順については、こちらをご確認ください。

型名ルール

 型名ルールには、メモリサイズが含まれています。

  例)MCOC115CA0408

 今回設計した拡張RAM(ERAM)ユニットは、最大512Kバイトの領域を持つので、2桁では足りなくなってしまいました。128Kバイト以上のERAMを持つ場合、素直に3桁とする事にします。

  例)NHOC113CA04128


 併せて、Cmod A7ボードへの移植にかこつけて、こっそり対応していた1Kバイト以下の分散RAM版の型名も追加しておきます。
 「RAMなんて、ほとんど使わないのだよなぁ〜。でも、他の用途で大量にRAMマクロは使いたい」等と言った場合には、RAMマクロを消費せず、最低限必要なRAMを分散RAMで構成可能なオプションです。
 分散RAMのサイズをxバイト(ただし、xは2冪数)とすると、x=2N[バイト]なので、2桁のRAMサイズ[Kバイト]に代えて、文字「L」+Nを型名とします。

  例)TSOC117CA04L7

 1,024バイト版だけは1,024バイト=1Kバイトなので、型名は「〜L10」ではなく、「〜01」と、なります。

※最小の32バイトRAMになった場合でもブートプログラムが動作できる様に、シリアルモニタからの受信データバッファサイズ(max_lbuf)を小さく設定しておきました(mcoc_boot.asm)。アセンブラ(asm_mcvm.olv)が出力するDAT2ファイルを転送する限り、問題ないはずです。


 コンパイルオプションの優先順位は、MCOC_ERAM>MCOC_RAM_LE1K>MCOC_RAM_*K(*の降順)です。複数`defineされた場合に適応されます。

コンパイル

 Cmod A7ボード用に最適化した標準マイコン(CA版)のERAM 128Kバイト版をNihoniumコアで起こしました。

  型名:NHOC113CA04128

 Vivadoでのコンパイル(論理合成・配置配線)結果を以下にまとめます。

  コンパイル結果:

 これだけアンバランスにメインメモリ(RAM)の容量を大きく(128Kバイト)しても、まだ、RAMマクロは17個(≒68Kバイト分)残っています。Cmod A7ボードに搭載されたXC7A35Tチップでも、十分な性能が有ります。Xilinx Artix-7ファミリには、まだ上位機種のXC7A50TやXC7A100T等が有りますし、更に上位ファミリの、Kintex-7ファミリやVirtex-7ファミリ展開もあります。
 ただ、上位機種は高性能な分、割高となりますので、費用を抑えたい場合には下位ファミリのSpartan-7ファミリから選択するのが吉です。

記憶行進の実験

 メモリ(RAM)の動作確認なんて、何をすれば良い物か?
 バイト/ワード/ロングワードサイズでアクセスできるか?とか、書き込んだデータが読み出せるか?とか、シミュレーションで粗方確認はしました。
 後は、製造不良の検査に使用するマーチングテストを、ERAM領域全面に対して実行しておいたら良さそうです。

 ただ、128Kバイトなんて言う大容量RAMのマーチングテストなんて、シミュレーションするには時間がかかりすぎるので、Cmod A7ボードに書き込み、実物でテストします。実物(FPGA)なら、ミリ秒オーダでテストが終わるので、現実的です。
 前節で準備したNHOC113CA04128を使いますので、こちらに従ってコンパイルして、Cmod A7ボードに書き込んでおいてください。


 テスト用に以下のプログラムを使いますので、こちらに従ってアセンブルし、Cmod A7ボードに転送してください。

  プログラム:test_march_nh.asm

 Nihoniumで動作する、ロングワードサイズでRAMのマーチングテストを実行するプログラムです。シリアルモニタから以下のコマンドを受け付けるようになっています。

  シリアルモニタコマンド仕様:

 以下、実験中の様子です(写真は使い回し)。

a274_expr.jpg


 実験を進めましょう。

  実験サンプル:
  【march,080000,0a0000】
  OK: long marching (0x00080000~0x000a0000, 0x0000a556[us])

 128Kバイト分のERAM領域(全面)に対して、ロングワードサイズでマーチングテストを実行しました。結果はパスです。

 128Kバイト全面のマーチングテストに要した時間は、0x0000_a556μs≒42.3msでした。出荷検査としては、マーチングテストだけでは全く不十分で、他にもいろいろなテストを実施しなくては、なりません。量産して何万個も出荷する場合、テスト時間が出荷数で掛け算となるのでバカにできません。
 テスト時間が伸びると、その分原価が上昇するので安く売ることができなくなり、同等の機能を有する他社チップとの競争力が低下します。如何にして出荷検査を最適化して、テスト時間を短くするのか?は、重要なファクタなのですが、幸い、本HPは趣味の世界で、かつFPGAを使った設計ですので、ここでストップします。


 大容量RAMのマッピング仕様を策定し、実機(Cmod A7ボード)で動作の確認ができました。過去、本HPで取り上げてきたテーマでは、大容量のRAMを必要とするアプリケーションが少ないので、活用する機会があるかどうかは、不明です。


btn_arduino.png

btn_prev.png btn_next.png

home