ATmega328のメモリマップ

こんにちは、めのんです!

前回からスタートした命令セットシミュレータの開発について、もう少し具体的な内容を詰めていくことにします。
今回の内容は「メモリマップ」についてです。

メモリマップとは?

メモリマップとは何かというと、どのアドレスに何が配置されるかを表したものです。
アドレスが番地なので、地図になぞらえて「マップ」という言い方をしています。
普通はこのページの最初に掲載している図のような形で表します。

このページの冒頭にある図は、実際のATmega328のデータメモリのメモリマップです。
今回の記事で掲載するメモリマップの図は「picoPower®技術を持つAVR®マイクロ コントローラ」から引用させていただいています。

ハーバードアーキテクチャとフォンノイマンアーキテクチャ

ATmega328(というよりAVR)は「ハーバードアーキテクチャ」という構成になっていて、プログラム用のメモリとデータ用のメモリが物理的に分離されています。

これに対して、世の中の多くのCPUは「フォンノイマンアーキテクチャ」を採用しています。
略してノイマン型ともいわれますが、こちらはプログラムとデータのためのメモリを分離しません。
これについてはあまり詳しく解説しませんが、こういう違いがあることを念頭に置いておいてください。

ATmega328のメモリマップ

ちょっと横道にそれそうでしたので話をもとに戻します。

AVRはハーバードアーキテクチャですので、プログラム用とデータ用のメモリが分離されています。
そのため、メモリマップもプログラム用とデータ用の2種類が必要になります。
冒頭の図はデータ用のメモリマップになります。

データメモリのメモリマップ

まずはデータ用のメモリマップから見ていくことにしましょう。

ATmega328のデータメモリは、アドレスが16進で0x0000から0x08ffまで割り振られていて、サイズとしては2,048バイトあります。
図では16進数であることを示すのに\$を付けていますので、$001Fは0x001fの意味だと考えてください。

ATmega328のデータメモリはメガでもギガでもなくて単なる2,048バイトです。
しかもそのうちの256バイトは用途が決まっているので、残りの1,792バイトが自由に使える領域です。

すごく少ないですよね。
こんなにメモリが少ないので、できるだけ無駄が無いように切り詰めて使わないとすぐに枯渇してしまいます。

メモリマップの見方なんですが、バイナリデータをダンプしたときをイメージしていただければいいかと思います。
バイナリデータをダンプすると(ツールによって多少変わりますが)次のような内容が表示されますね。

00001510  00 00 48 89 d8 48 89 fa  48 39 d0 75 3b 45 88 e8  |..H..H..H9.u;E..|
00001520  67 41 8d 44 24 01 48 8d  4b 01 41 81 e0 ff 00 00  |gA.D$.H.K.A.....|
00001530  00 25 ff 00 00 00 48 8d  14 03 e8 d1 f6 ff ff 45  |.%....H........E|
00001540  88 e0 48 8d 53 01 48 8d  4e 01 41 81 e0 ff 00 00  |..H.S.H.N.A.....|
00001550  00 e8 ba f6 ff ff eb 39  45 88 e0 48 8d 53 01 48  |.......9E..H.S.H|
00001560  8d 4e 01 41 81 e0 ff 00  00 00 e8 a1 f6 ff ff 45  |.N.A...........E|
00001570  88 e8 67 41 8d 44 24 01  48 8d 4f 01 41 81 e0 ff  |..gA.D$.H.O.A...|
00001580  00 00 00 25 ff 00 00 00  48 8d 14 03 e8 7f f6 ff  |...%....H.......|
00001590  ff 67 43 8d 04 2c 88 03  90 48 8d 64 24 20 41 5d  |.gC..,...H.d$ A]|
000015a0  41 5c 5e 5f 5b c3 00 00  00 00 00 00 00 00 00 00  |A\^_[...........|
000015b0  53 48 8d 64 24 e0 0f b6  19 0f b6 02 29 c3 75 18  |SH.d$.......).u.|
000015c0  44 0f b6 01 4d 63 c0 48  83 c2 01 48 83 c1 01 e8  |D...Mc.H...H....|
000015d0  bc fb ff ff 89 c3 66 90  89 d8 90 48 8d 64 24 20  |......f....H.d$ |
000015e0  5b c3 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |[...............|
000015f0  48 8d 64 24 d8 48 85 c9  74 14 45 31 c0 48 ba ff  |H.d$.H..t.E1.H..|
00001600  ff ff ff ff ff ff 7f e8  14 fa ff ff eb 02 31 c0  |..............1.|
00001610  90 48 8d 64 24 28 c3 00  00 00 00 00 00 00 00 00  |.H.d$(..........|

上の例は抜粋なので0x1520番地から始まって0x161f番地で終わっていますが、メモリ全体をダンプすることももちろんできます。

メモリマップでは個々の具体的なデータまでは見ずに、あくまでも大まかにどこに何が配置されているのかをエリアに分けて表します。

冒頭の図のように、ATmega328のメモリマップであれば、0x0000から0x001fまでがレジスタファイル、0x0020から0x005fまでがI/Oレジスタ、0x0060から0x00ffまでが拡張I/Oレジスタ、残りが内蔵SRAMというように4つのエリアに分割されています。

個々のエリアの具体的な内容は今回は触れません。
メモリマップがこんな風にどこのアドレスにどのエリアが配置されるかを表していることだけを理解していただければ十分です。

プログラムメモリのメモリマップ

プログラムメモリのメモリマップの図も見てみましょう。

こちらはずいぶんシンプルですね。
0x0000から0x3fffまでのアドレスが割り振られていて、末尾にブートプログラムが格納されるようになっています。
具体的に何番地からがブートプログラムのエリアとは書かれていないので、おそらくはプログラムのサイズによって可変なんじゃないかと思います。

ひとつ注意しないとでけないのは、データメモリのアドレスはバイト単位(8ビット単位)で振られていましたが、プログラムメモリのアドレスはワード単位(16ビット単位)で振られていることです。

ですので、0x0000から0x3fffであればアドレスの数としては0x4000個(=16,384個)ありますが、サイズとしては16,384ワード(=32,768バイト)だということです。

AVRの命令はほとんどが16ビット長で、ときどき32ビット長のものがあります。
ですので、16ビット単位のアドレスで十分ということなのでしょう。

プログラムメモリにデータを格納することも一応はできるのですが、どちらかといえば特殊な使い方なので、命令を格納する用途に重点を置いた設計になっているのだと思います。

命令セットシミュレータでの扱い

命令セットシミュレータでこれらのメモリを扱う際には単純に2つの配列を用意してあげればよさそうです。
Cには8ビット符号無し整数型であるuint8_t型と16ビット符号無し整数型であるuin16_t型がありますので、次のようにすればよさそうです。

uint8_t data_memory[0x900];
uint16_t program_memory[0x4000];

各エリアの違いはこの段階では考慮しなくてもいいと思います。

命令セットシミュレータはこんな感じでCPUの構成要素を実際のコードに落とし込んでいきます。


今回はメモリマップについて考えました。
私が最初にメモリマップを勉強したときはなかなかその意味合いを理解できませんでした。
私が特別理解力が足りなかっただけかもしれませんけど、慣れないとわかりにくい概念だと思いますので質問があればコメントしていただけると幸いです。

次回はレジスタファイルについて見ていくことにします。
どうぞご期待ください!