今回は、FPGAボード(Sipeed Lichee Tang Primer)のMoscoviumマイコンに、日本語(漢字)フォントROMを搭載しようというテーマです。
昔(「#12-3 DUE+TFTで日本語表示」)、Arduino DUE+TFTシールドで考察したことがあるので、重複は避け、以下にまとめます。
まさに、Flashメモリが512KバイトもあるDUEだから実現できた応用だったのですが、これをMoscoviumマイコン(FPGAボード)で実行できるか?と言うと、できません。
メモリ(RAMマクロ)が全く足りないのです。FPGAボードの全RAMマクロを投入しても、128Kバイトにしかなりません。
※1トランジスタ/ビットのFlashメモリに比べて、FPGAボードのRAMはデュアルポートなので8トランジスタ/ビット程になるため、太刀打ちできません。
これをMoscoviumマイコン(FPGAボード)で実現するには、メモリ使用量の削減が必要です。以下に、その削減案を示します。
案1)及び2)を組み合わせて、結果、64Kバイト以下まで圧縮できたら、何とかFPGAボードのRAMマクロで賄うことが可能です。2)案でデータサイズは1/4、1)案でも1〜1/2程度まで圧縮可能なので、合わせ技で何とかなりそうな感触です。
これについても、「#12-3 DUE+TFTで日本語表示」で議論済みなので詳しくはそちらをご覧ください。
コード体系はシフトJIS(S-JIS)が便利なので、これを採用します。
S-JISから区点コードへの変換が必要となります。変換プログラムは、「#12-3 DUE+TFTで日本語表示」より、tft_kprint.inoの中で使ったtft_sjis2kuten()関数がそうなので、そちらを参照してください。
このプログラム(tft_kprint.ino)でもそうですが、区点コードは二次元(区と点)のマトリクスで扱い難い為、0番からのシリアル番号に変換して取り扱います(下式)。
区点シリアルコード=(<区>−1)×94+<点>−1
…… 1≦<区、点>≦94、0≦区点シリアルコード<8,836=942
半角フォントには対応しません。全角フォント8×8[ドット]に対して半角フォント4×8[ドット]では、あまりに視認性が悪すぎるためです。
ただし、半角のASCIIコードを指定された時に、漢字などと同じ8×8[ドット](全角)のフォントを表示可能な様に工夫しておきます。
具体的には、区点シリアルコードの0x0300〜0x03ff番にASCIIコード(0x00〜0xff)のフォントを入れておきます(下図)。
上図赤枠で囲んだ所が、区点シリアルコードで0x0300〜0x03ff番に該当する部分です。区点コードの9区〜11区辺りに相当します(上図一番上に見えている行が1区)。
元々、区点シリアルコードの0x0300番台は空き領域になっていて、有効な文字が存在しないため他の文字とは干渉しません。
ASCIIコード範囲のフォントデータを取り出したい時には、<ASCIIコード>+0x0300を区点シリアルコードとするフォントを取ってくれば良いことになります。
DUEのプログラム(tft_kprint.ino)では、プログラムメモリ上にフォントデータを配置しました。
Moscoviumの場合16ビットマイコン故、メモリ空間が64Kバイト分しかなく、現在のバスステートコントローラBUSC2040Dでは、RAM領域が最大40Kバイト、ROM領域が最大20Kバイトに制限されていて、メモリ空間にフォントROM 64Kバイトを配置するのは困難です。
そこでMoscoviumマイコンでは、I/Oユニットの1つとして「日本語フォントユニット」を作り、メインメモリとは別空間にフォントROMを持たせます。I/OレジスタにS-JISコードを書き込むと、別なレジスタからフォントデータを読み出せるような仕組みにしようと思います。
以下、日本語フォントFONTJPユニットの目標仕様をまとめます。
ブロック図は以下の様になります。
薄黄色の箱は、CPUからアクセス可能なI/Oレジスタです。レジスタの中に、その機能と関連する論理も含まれています。例えば、fnjpcodブロックにはS-JISコードから区点コードへの変換回路、fnjpdatブロックにはアフィン変換回路(後述)が含まれます。
以下表にI/Oレジスタ一覧を示し、その後に各レジスタの仕様を説明します。
Addr | Name | R/W | Bit:15〜0 | Memo | |||||||||||||||
0xf0b0 | fnjpctl | RW | − | − | − | − | − | − | − | − |
SJ S1 |
SJ S2 |
KT C |
IN V |
FL PH |
FL PV |
ROT [1:0] |
font rom control | |
0xf0b2 | fnjpcod | RW | FCOD[15:0] | S-JIS(W) or KU-TEN(R) code | |||||||||||||||
0xf0b4 | fnjpdbl | W | − | − | − | − | − | − | − | − | FNTD[7:0] | convert double | |||||||
R | DOBL[15:0] | ||||||||||||||||||
0xf0b6 | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | − | reserved |
0xf0b8 | fnjpdata | R | FDAT7[7:0] | FDAT6[7:0] | font rom data a | ||||||||||||||
0xf0ba | fnjpdatb | R | FDAT5[7:0] | FDAT4[7:0] | font rom data b | ||||||||||||||
0xf0bc | fnjpdatc | R | FDAT3[7:0] | FDAT2[7:0] | font rom data c | ||||||||||||||
0xf0be | fnjpdatd | R | FDAT1[7:0] | FDAT0[7:0] | font rom data d |
BUSC出力のセレクト信号はbcs_fnjp_nです。上表の範囲でアサートされるので、FONTJPユニット内では、アドレスbadr[3:0]を復号してレジスタを特定しています。このI/Oユニットでは、fnjpdata,fnjpdatb,fnjpdatc,fnjpdatdに対してはバイト/ワードアクセスが可能です。それ以外のレジスタは、必ずワードアクセスしてください。
前節レジスタ仕様に示した様に、90°毎の回転変換と、左右・上下反転(アフィン変換)をハードウェアで実行できるようにしておこうと思います。
例えば、時計回り90°回転は、以下表の様になります。
レジスタ | original font | → | rotate 90° | |||||||||||||||
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |||
fnjpdata | [15:8] | 63 | 62 | 61 | 60 | 59 | 58 | 57 | 56 | → | 7 | 15 | 23 | 31 | 39 | 47 | 55 | 63 |
[7:0] | 55 | 54 | 53 | 52 | 51 | 50 | 49 | 48 | 6 | 14 | 22 | 30 | 38 | 46 | 54 | 62 | ||
fnjpdatb | [15:8] | 47 | 46 | 45 | 44 | 43 | 42 | 41 | 40 | 5 | 13 | 21 | 29 | 37 | 45 | 53 | 61 | |
[7:0] | 39 | 38 | 37 | 36 | 35 | 34 | 33 | 32 | 4 | 12 | 20 | 28 | 36 | 44 | 52 | 60 | ||
fnjpdatc | [15:8] | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 3 | 11 | 19 | 27 | 35 | 43 | 51 | 59 | |
[7:0] | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 2 | 10 | 18 | 26 | 34 | 42 | 50 | 58 | ||
fnjpdatd | [15:8] | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 1 | 9 | 17 | 25 | 33 | 41 | 49 | 57 | |
[7:0] | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 0 | 8 | 16 | 24 | 32 | 40 | 48 | 56 |
表は8×8[ドット]のマトリクスとなっていて、レジスタ欄に、フォントデータを読み出し可能なfnjpdata〜fnjpdatdレジスタと、その上位・下位バイトが縦に並んでいます。
マトリクス内に記載した数字は、読み出したフォントデータのビット番号63〜0です。青く着色したセルから、ビット値"1"が読めると思ってください。上表の例では、平仮名「あ」のフォントデータを読み出している状態です。
左半分(original font)に示したフォントデータを時計回りに90°回転すると、右半分(rotate 90°)となるべきです。つまり、対応するビット番号(63〜0)が対応するレジスタから読める様、単にビット順番を組み替えれば良いことが解ります。
他の左右・上下反転も同じです。
すると、アフィン変換回路は以下図となります。
ビットの組み替え回路(○に×の⨂回路)は論理セルを消費しませんが、セレクタMUXは、64ビット2 to 1セレクタとなるので、そこそこ大きい回路にはなります。しかし、この変換をソフトウェアで実現しようとしたら、大変です。一体、何百サイクル掛かるんだ?ってことです。ハードウェアで持たせておいた方が絶対良いはず!
なお、複数のアフィン変換を組み合わせた場合の変換順番は、上図の通りとなっております。
時計回り90°回転回路(図中ROT90)を1つ作っておくと、それを3個カスケードに接続するだけで、0°、90°、180°、270°の回転に対応することが出来ます。
TD IDEのROMマクロは、RAMマクロをリードオンリで使っています。
RAMマクロには、9KビットRAMマクロ(最大64個使用可能)と、32KビットRAMマクロ(最大16個使用可能)の2種類があります。
Moscoviumマイコンに於いては、32KビットRAMマクロはCPUのメインメモリ(ROMおよびRAM)用として、最大でROM 20Kバイト+RAM 40Kバイトを使う場合があります。最大構成の場合に必要な32KビットRAMマクロ数は15個です。
残り1個では4Kバイト分にしかならず、フォントROMに使うにはサイズ不足です。
従って、フォントROM用としては9KビットRAMマクロを使うことになります。
9KビットRAMマクロも、UART8N1の受信FIFOバッファや、LOGA8CHの取り込みFIFOなどで使うため、全て使い切ってしまう様では、他のI/Oユニットとの同時使用が出来なくなります。
TD IDEのIP Generatorを起動して、以下図の様にROMマクロを作成しておいてください。
この場合の9KビットRAMマクロ数は、上図にある通り57個(推定値)です。最大64個まで、7個ほど余裕がありますので、UART8N1等いくつかのI/Oユニットとは共存できると思います。
第二水準までの文字で、ざっと7,000文字以上の漢字フォントを起こすのは大変……。気が遠くなりかけた時、福音がありました!フリーの8×8[ドット]フォントを提供してくれているHPを見つけたのです(↓)。
Little Limit(別ウインドウが開きます)
ありがたや。上HPから、「美咲フォント」を使わせていただこうと思います。
以下、「美咲フォント」のMIFファイルを生成する手順です。
謝辞: しっかり第二水準までカバーした極小フォントを提供してくださっている「Little Limit」の作者(門真 なむ)様に感謝いたします。
前節で完成させたFONTJPユニットを含む、MCOC115JP0408のデザインデータを以下リンクよりダウンロード可能です。FPGAボードへの書き込み手順はこちらを参照してください。
以下に、コンパイル(論理合成・配置配線)結果をまとめておきます。
FONTJPユニット単体でコンパイルしてみたところ、約1,000LUTと、かなり大きいことに驚きました。
FONTJPユニット単体で1,000LUTは、ちょっと大きいけど、上の様に避けられない理由であることが判ったので、これを許容します。
RAMマクロの消費数量について追加検討しておきます。
一応、最大仕様として漢字コード第二水準までカバーできる様に、64ビット×8KワードのフォントROMとしましたが、不要な文字コード領域は省いて、なるべく小さいROMに収める様な努力はすべきです(RAMマクロ数は有限なので)。
TD IDEのIP Generatorは賢くて、ROMデータとして喰わせるMIFファイルの中身に0x0000等(※)の固定データが連続した場合、その領域にはRAMマクロを配置せず、ちゃんとしたデータが詰まった部分にだけ、RAMマクロを配置します。
※0x0000以外の固定データの扱いについては、当方では確認しておりません。
この為、スクリプト(fontjp_gen_mif.olv)実行時、スクリプト先頭付近の変数max_datに所望のワード数を設定しておくと、最小限のRAMマクロだけを消費して回路を構成することが出来ます。
有意義な選択肢は以下2通りです。
var max_dat=5*1024; // 5Kワード=1区〜54区(第一水準+α)までカバー
または、
var max_dat=8*1024; // 8Kワード=1区〜87区(第二水準)までカバー
上の設定でMIFファイルを作ってコンパイル(論理合成・配置配線)した結果、使用した9KビットRAMマクロ数は、5Kワードで36個、8Kワードなら57個でした。MIFファイルを作り直すだけで、論理回路にも、ROMマクロにも、手を触れずにRAMマクロ数を削減することが出来るのがGoodです。
RAMマクロは全部で64個まで使えるので、第二水準までカバーする8Kワードを選択しても、UART8N1等のI/Oユニットで使う分くらいは確保できます。もっと沢山のRAMマクロをFONTJPユニット以外のI/Oユニットで使いたい場合には、第一水準だけで諦める等の妥協をすると、占有使用するRAMマクロ数を減らすことが可能です。
サンプルアプリケーションは、「#18-2 SPI通信する」で作成したプログラムのFONTJPユニット対応版です。FONTJPユニットからフォントデータを読み出して表示するように、ちょこっと改造するだけです。以下にソースファイルをリンクしておきます。
FONTJPユニット対応版プログラム:flash_mesg.asm
デフォルトでは従来通り、7ビットASCII(英数)フォントを使うようになっていて、以下の修正をしてアセンブルすると、FONTJPユニットを使って日本語(漢字)フォントを表示できるようになります。
def f,"" // fontjp unit: not use="" / use="#"
↓下の様に変更すると、日本語フォントが表示できる。
def f,"#" // fontjp unit: not use="" / use="#"
デザインがMCOC115JP0408に変わるだけで、FPGAボードと8×8 LEDなどとの接続に変更有りませんので、こちらを参考に、実験を進めてください。
あと、日本語(漢字)の文字列を送信する時、文字コードがS-JISである必要があるので、UTF-8になってしまうArduino IDEのシリアルモニタは使えません。Tera Termなど、S-JIS送信対応の通信ソフトウェアを使ってください。
以下、実験中の様子です。
8×8 LEDに「あ」と「花」を表示したところを撮影しました。上写真の方向で文字が読めるように、FONTJPユニットのアフィン変換機能を使って、時計回りに90°回転してあります(下コード)。
// fontjp setting
ldbiu r0,fnjp_rot_1 // 時計回りに90°回転
ldwi r7,fnjpctl
stw [r7],r0
実は、本命の実験はSPI-TFTへ日本語表示する「次回」なので、今回の実験はあっさりと以上で終了します。
次回、FPGAボード(Moscoviumマイコン)にSPI-TFTを接続して、日本語表示してみたいと思います。お楽しみに。