公開日:2021/11/06
title.png

18th Season


#18-7 Moscovium色々拡張

 オリジナル16ビットCPUコアMoscoviumマイコンを色々と拡張しておこうと思います。
 まず、汎用I/Oポート数が不足して来たので、従来の8本から16本へ倍増🐾させようと思います。
 次に、UARTです。Arduinoの「シリアルモニタ」的用途で必要な1本分しか無いので、他の周辺モジュールと通信するためのUARTが残っていません。こちらも1ユニット追加しておこうと思います。
 更に、プログラムメモリの機能を拡張します。

汎用I/Oポートの拡張

 以下の様な記事でソフトウェアSPIを使った際、だいぶI/Oポートを使いました。

  「#18-2 SPI通信する

  「#18-5 MoscoviumとSPI-TFT

 MoscoviumマイコンでのSPI通信はソフトウェアSPIを使うという方針にしたので、早晩汎用I/Oポート数が足りなくなるでしょう。
 そこで、PORT8I8Oユニットをもう1個インスタンスして、I/Oポート数を倍増しておこうと思います。
 問題はFPGAボードの端子数で、こちらも空きが無くなってきています。現在のFPGAボード端子割り当てはこちらです。
 FPGAボードのピンソケットに40本の端子が割り当てられています。この他、端面スルーホールに+20本の端子が割り当てられているので、端面スルーホールにピンヘッダなどをはんだ付けできれば+20本を利用可能です。が、そんな腕は筆者にはありません……。だって、2.54mmの1/2で1.27mmピッチですよ……、ムリ(1^^;)。

 仕方ないので、現状のピンソケット20本の範囲内で割り当てを変更して対処します。以下図の様に、入力専用だったport_inp[7:0]を入出力ポートに変更して、ここに、追加した8本の汎用I/Oポートを接続します。

a187_portex.png

 緑色にした部分が今回の追加・変更箇所です。
 2個目のPORT8I8O#1ユニットの入力端子を選択するsel1[7:0]信号は必要ないので、出力開放で放置します。
 この構成で、従来の8本I/O版との上位互換性は保持しています。

 変更に合わせて、端子名をport_iop[15:0]に変更します。テストベンチ(test.v)やI/O制約ファイル(ADCファイル)を変更する必要があることに留意してください。

 PORT8I8O#1ユニット(2個目)のI/Oレジスタ一覧はこちらと同じです。ただし、アドレスのみ0xf0d0〜0xf0dfに変更となります。また、PORT8I8O#1ユニットのportselレジスタは意味をなさないので、リザーブとします。

UARTの拡張

 元々搭載していたUART8N1ユニットは1個だけで、シリアルモニタ的用途に使用するのと、プログラムコードのダウンロード(ブートローダ)用に使う必要があるので、他のUART機器との通信には使えませんでした。
 とは言っても、色々な外部センサモジュールの場合、通信仕様はI2CであったりSPIであったりしてUARTを採用している機器が少ないこともあり、あまり用途がありませんでした。今までを振り返ってみると、ESP32をATコマンドファームウェアで制御した時、Arduino UNOとの接続にUARTを使いました。

 この時必要だったのが、ハードウェアフロウ制御(RTS,CTS)です。 「#4-10 ArduinoとESP32の非同期シリアル通信」に解説があります。

 一言で言うと、「バッファがオーバフロウしない様にハンドシェイクする通信線」です。
 ESP32からインターネットへIP接続して、その応答をUARTで受けるのだけど、HTMLなんて軽く数10Kバイトになるし、そんなものを115,200baud(あるいはそれ以上)で送り付けられたら、非力なUNO @16MHzでは簡単にバッファオーバフロウしてしまいます。
 Moscovium @24MHzも例外ではありません。よって、UART8N1ユニットをハードウェアフロウ制御(RTS,CTS)に対応できるように改造します。

 まとめると、

 今回はこの二点を増設します。1)に関しては、前節のPORT8I8Oと同様、同じUART8N1ユニットを2個インスタンスするだけですので、以降の節では2)の増設について述べます。

UART8N1ユニットの改造

 まず、CTSを追加してみましょう。
 CTSは入力信号です。CTS端子を新設してその信号をモニタし、CTS信号がアサートされていれば通常通りデータを送信します。CTS信号がネゲートされていたら、新たなデータ送信を保留してCTS信号がアサートされるまで待ちます。この処理を送信制御ステートマシンへ追加します。
 CTS信号は非同期なので、こちらと同様にして同期化してから使います。

※CTS制御を有効にしてCTS端子がネゲートされたままになっていると、いつまでたってもデータが送信されないので、ご注意ください。


 次はRTSの追加です。
 RTSはこちらからの出力信号です。こちら側が、データを受信できる準備が整っていればアサート、データを受信できない場合にはネゲートとする信号です。
 「準備が整」うとは、平たく言うと受信バッファに空きがあるか否か?ということです。

 現在のUART8N1ユニットでは受信FIFOバッファの状態として、完全にエンプティの状態EMPTYフラグと、完全にフルの状態FULLフラグの2状態しか知り得ない回路構成でした。
 RTSをネゲートしても、相手がそれを認識して送信を停止してくれるまでのタイムラグがあるので、FULLフラグを確認してからRTSをネゲートしても間に合わないケースがあると考えられます。
 TD IDEのFIFOマクロでは、「ほとんどエンプティ(almost empty、AEMPTY)」と、「ほとんどフル(almost full、AFULL)」のフラグを出力する様にコンフィギュレーションできるので、これを使います(下図)。

a187_uartmcr.png

 TD IDEのIP Generatorで上の様にしてFIFOマクロを作成してください。今まで使っていたUART8N1用FIFOマクロと同じ名前ですので、上書きしてください。ついでに、9KビットRAMマクロをフルに活用するため、FIFO段数を512段 → 1,024段に設定してあります。

 上図のFIFOマクロを使って、RTS信号を以下の通り制御します。

 上図FIFOマクロの設定画面で、AEMPTYフラグとAFULLフラグを出力する閾値(Threshold)を変化させると、RTSの挙動を調整することが可能です。

新しいI/Oレジスタ一覧

 以下表に新規追加したレジスタやフラグを含むI/Oレジスタ一覧を示し、レジスタの説明を続けます。

Addr Name R/W Bit:15〜0 Memo
0xf0c0 uartctl RW RA
VL
RF
UL
BR
DF
TF
UL
FR
ST
RX
E
TX
E
uart control
0xf0c2 uartbaud RW BAUD[15:0] baud rate control
0xf0c4 uarttdat W TDAT[7:0] transfer data
0xf0c6 uartrdat R RDAT[7:0] receive data
0xf0c8 uartflow RW CT
SS
RT
SS
CT
SP
RT
SP
CT
SE
RT
SE
uart flow control
0xf0ca reserved
0xf0cc reserved
0xf0ce uartbres R BRES[15:0] bit rate estimator

 BUSC出力のセレクト信号はbcs_uar1_nです。上表の範囲でアサートされるので、UART8N1ユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットはバイトアクセス禁止です。必ずワードアクセスしてください。

 改造によって追加となったのは、uartflowレジスタ、uartbresレジスタ、uartctlレジスタのBRDFフラグです。
 通信速度の自動検出機能(uartbresレジスタ等)については、次回解説予定です。

プログラムメモリの拡張

 プログラムメモリとは、Moscovium CPUがプログラムコードをフェッチするためのメモリを指します。
 現在はROMとなっています。ただし、FPGAボード上では、RAMマクロを使っており、ブートリセット(bootr_n)時にシリアルモニタからプログラムコードをダウンロードして書き込みすることが出来ます。
 とは言え、ブートリセットした時しか書き込みできないので、通常使用時はただのROMにしか見えません。例えば、何らかの通信(SPIやI2C等)によって、新しいプログラムをダウンロード・実行したり等、動的にプログラムを変更することはできませんでした。

 そこで、プログラムフェッチ可能なRAMを設けます。命令RAM(I-RAM)と呼ぶことにします(下図)。

a156_memap.png

 I-RAMに求められる機能は、1)オペランドアクセスで読み/書きできること、2)フェッチアクセスで読み出しできること、の二点です。この機能を、TD IDEのデュアルポートRAMマクロを使って実現します。一方のポートからフェッチアクセス、他方のポートからオペランドアクセスを実行することで実現可能です。

デュアルコアでも使う?

 デュアルコア版でも使えるようにしておきましょう。
 ただ、RAMマクロのデュアルポート機能は、フェッチとオペランドの同時アクセスを実現するために使ってしまったため、2個のCPUからの同時アクセスに使用することが出来なくなっています。
 このため、I-RAMの実体メモリを2個インスタンスしておいて、メモリバスのbmst信号を使ってCPU毎に異なるI-RAMにアクセスさせる方法で回避します。この方式に於ける仕様制限は以下の通りとなります。

  仕様制限:

 ダウンロードした同じプログラムを実行したい場合には、CPU間相互通信FIFOユニットICFF16なり、共有可能なRAMなりを使って共有することは可能なので、問題になるような仕様制限ではないはずです。

ただのRAMだよね?

 その心は、「普通のRAMとしても使えるよね?」という質問ですが、YesでもありNoでもあります。正確には、RAMマクロの作り方によって変わります。

 プログラムメモリとしてだけではなく、普通にRAMとして使いたいのならバイトアクセスは必須となります。
 一方、プログラムメモリとしてだけ使うことを前提とするなら、バイトアクセスは必要なく、ワードアクセスさえできればOKです。
 ここ、大きな違いなので掘り下げておきます。

 TD IDEのRAMマクロを使って、16ビット×2,048ワード(=4Kバイト)のRAMマクロを生成すると以下の様になります。

I-RAM容量 バイト
アクセス
9Kビット
RAMマクロ
32Kビット
RAMマクロ
16ビット×2,048ワード
(=4Kバイト)
不可 4個 1個
可能 4個 2個

 注目は、32KビットRAMを使った場合のマクロ使用数です。バイトアクセスを有効化すると、RAMマクロ数が2個必要になってしまいます。
 おそらく、各バイト(8ビット)毎に32KビットRAMマクロを1個割り当てる必要があるのでしょう。
 9KビットRAMマクロを使う場合は、元々複数RAMマクロを組み合わせないと4KバイトRAMを構成できなかったため、バイトアクセスの有無でマクロ数に変化が無かったのだと思います。

妥協と結論

 I-RAMラッパはバイトアクセス可能な様に作っておくので、9KビットRAMでも、32KビットRAMでも、バイトアクセス可でも不可でも、どちらでもお好きな方を選んで使ってくださいm(1__)m。最大仕様のマイコンをカスタマイズして作ろうとしない限り、どうとでもなるはずです。

a187_irammcr.png

※バイトアクセスの可/不可でRAMマクロを混同しないよう、バイトアクセス可能なRAMマクロ名は、「ram4kib_mat("b"を付ける)」で作成してください。

 バイトアクセスの有効化/無効化は「Byte Write Enable」の項目で選択します。9KビットRAMマクロと32KビットRAMマクロは「Implement Option」の項目で選択できます。
 上図の設定では、バイトアクセス(※)できませんのでご注意ください。命令RAMとして使う場合には、上の設定で全く問題ありません。プログラムコードを転送する(書き込む)時に、「ワードサイズで書き込むこと」という仕様制限が付くだけです。

※正確には、「バイト書き込み」が出来ないだけで、「バイト読み出し」は可能なため、プログラムフェッチ用メモリとして使う分には何ら問題ありません。書き込む時はワード書き込んでください。

デザインの更新

 以下リンクより、諸々拡張したMoscoviumマイコンMCOC115EX4808のデザインをダウンロードできます。FPGAボード(Sipeed Lichee Tang Primer)への書き込み手順はこちらを参照してください。

btn_moscovium.png

 iverilogを使ったシミュレーションで、一通り動作確認してあります。「サポート無しの無保証」に変わりありません。

 端子名の変更(port_iop[15:0])や追加(UART8N1#1の通信端子)がありましたので、I/O制約ADCファイルが一通り変更になっています。最新のADCファイル(MCOC_full.adc)によるピン配置はこちらとなります。
 また、プログラムフェッチ用ROMのサイズも狭くなってきたので、今回のエディションでは8Kバイトに拡張してあります。


 以下に、コンパイル(論理合成・配置配線)結果をまとめておきます。

 今回の拡張に関するコンパイルオプションを以下に説明しておきます。

 最上位モジュール(mcoc115ex4808.v等)の先頭付近で選択できます。


 喉奥に引っ掛かっていた骨を一気に取り除いてみました。現時点で具体的アプリケーションが無いので、FPGAボード(実機)での動作確認は、追々必要になった時に実施しようと思います。


btn_arduino.png

btn_prev.png btn_next.png

home