この世のCPUを食べ尽くすのだ! 第1章 第4節

~あんたCPUなんか創ってどうするのよ?! Vol.3~

2018/10/8
技術書典5 け39

第1章 第4節 ハーバード・アーキテクチャの欠点

ではハーバード・アーキテクチャの欠点は何でしょうか?

大きく3つの欠点があります。

回路が複雑になる
ROM用のアドレスバスとデータバスと制御回路、そしてRAM用のアドレスバスとデータバスと制御回路が必要になりますから、回路が複雑になります。これはTD4の必要最小限のICでCPUを作るという方針とは合いません。
幸い、TD4の場合は回避策があるのでほとんど問題になりませんが、どの程度のICが必要になるかは設計者の腕次第です。
ROMアクセス命令とRAMアクセス命令が必要となる
ROMとRAMが別々の回路に接続されているのですから、同じ命令で両方にアクセスすることはできません。MOVやLDやSTなどのニーモニックは同じにすることができますが、命令のバイナリコードは同じにはできません。必ずどこかにROMへのアクセスかRAMへのアクセスかを示すビットが必要になります。これは命令数の増大を招くため不利となります。
PICシリーズではこの問題を避けるために、プログラムからはROMにアクセスできないという乱暴な解決策を採用しました。例えばPIC16シリーズにはイミディエイトデータは存在しますが、文字列定数や定数配列などの機能はありません。
いっぽうAVRシリーズではROMにアクセスするための専用命令が用意されています。LPMという命令を使うことでROMからデータを読み出すことができます。命令数の増大は避けられましたが、回路は複雑になっていることが予想できます。
高級言語との相性の悪さ
ハーバード・アーキテクチャはROMとRAMのアドレス空間が別々なので、C言語などの高級言語との相性が悪くなります。
再びprintf()関数を例に考えてみましょう。
printf("Hello World!");
文字列定数を表示する場合、printf()関数にはROM内にある文字列のアドレスが渡されます。ところがprintf()関数の中からは、渡されたアドレスがROMなのかRAMなのかを調べる方法がありません。引数として渡されるのは文字列のアドレスだけですから、printf()関数は文字列がROM上にあると仮定して動作する事になり、関数内部ではROMへのアクセス命令が使用されます。
いっぽう同じprintf()関数をRAM内の文字列の表示に使った場合はどうなるでしょうか。
char s[] = "Hello World!";
printf(s);
この場合printf()関数にはRAM内にある文字列変数sのアドレスが渡されます。ところがprintf()関数の中からは文字列がRAM内にあることを知る方法はありません。前出の文字列定数の表示でprintf()関数の内部ではROMへのアクセス命令を使うことにしましたので、RAM内の文字列にアクセスすることができません。RAM内の文字列を表示するためには別途RAM専用のprintf()関数が必要になります。
PIC16シリーズでは元々CPUに文字列定数機能が無いので、printf()関数は文字列がRAM内にあると仮定して動作します。従ってPIC16では文字列定数を画面に出力することができません。もし文字列定数に対してprintf()関数を使おうとすると、コンパイラが自動的に以下のようにイミディエイトデータに変換してしまいます。
printf("Hello World!");
  ↓
putc('H');
putc('e');
putc('l');
putc('l');
putc('o');
putc(' ');
putc('W');
putc('o');
putc('r');
putc('l');
putc('d');
putc('!');
これに気付いた時、私は思わずブチ切れました。それ以来PIC16シリーズは使っていません。
本プロジェクトで製作するTD4EX3もIC数の削減を優先し、プログラムからはROMにアクセスできない事にします。