メモリマップとは?組み込み開発でのROM・RAM配置をわかりやすく解説

目次

メモリ領域解説シリーズ

本記事は「メモリ領域解説」シリーズの1つです。

メモリ領域解説シリーズの全体像はこちら

組み込み開発では、プログラムや変数がどこに配置されるかを理解することが重要です。
その配置を示したものが メモリマップ です。

メモリマップを理解すると、

・text / data / bss の配置
・ROMとRAMの役割
・スタートアップコードの動作
・リンカスクリプトの意味

が一気につながります。

この記事では、メモリマップの基本をわかりやすく解説します。

ROM・FLASH・RAMの違いについてはこちらの記事で解説しています。

メモリ領域の全体像についてはこちらの記事で解説しています。

初心者さん

メモリマップって何ですか?

エンジニアくん

プログラムや変数をメモリのどこに置くかを決めた配置図だよ。

初心者さん

配置って?

エンジニアくん

例えばコードはROM、変数はRAMに置くよね。
それをアドレス単位で決めたものがメモリマップ。

初心者さん

なるほど…メモリの設計図みたいなものですね。

エンジニアくん

そうそう、それが一番近いイメージだね。

メモリマップとは?

メモリマップとは、
ROMやRAMのどのアドレスに何を配置するかを示したものです。

メモリ領域の全体像

例えば、次のように配置されます。

ROM(FLASH)

text
rodata
data(初期値)

RAM

data
bss
heap
stack

この配置が「メモリマップ」です。

メモリマップの例

実際の組み込みシステムでは、次のようにメモリが配置されます。

ROM (Flash)
0x08000000  -----------------
            text
            rodata
            data(初期値)
0x08010000  -----------------

RAM
0x20000000  -----------------
            data
            bss
            heap
               ↓
               ↑
            stack
0x20008000  -----------------

このように、ROMとRAMは別のアドレス空間に配置されます。

初心者さん

heapとstackが向かい合ってますけど?

エンジニアくん

heapは上に伸びる
stackは下に伸びる
からぶつからないようにしてるんだ。

初心者さん

ぶつかったら?

エンジニアくん

それがスタックオーバーフローやメモリ破壊になるんだ。

heapとstackの衝突の危険についてはこちらの記事で解説しています。

ROM側のメモリ配置

ROMには「実行前から決まっているデータ」が配置されます。

text
rodata
data(初期値)

それぞれ役割が違います。

text領域(プログラムコード)

ここにはプログラムの命令が配置されます。

つまり

・関数の中身
・処理の流れ
・実行されるコード

が入ります。

void func()
{
    int a = 1;
}

この処理の命令が text 領域に配置されます。


rodata領域(constデータ)

変更しないデータが配置されます。

const int value = 10;
const char str[] = "hello";

これらは書き換えないため
ROMに配置されます。


data領域(初期値)

ここが少しややこしいです。

int g_value = 10;

この変数は

・初期値がある
・実行時に書き換わる

という特徴があります。

そのため

初期値 → ROM
変数本体 → RAM

に分かれます。


ROMにあるdataは
初期値保存用の領域です。

データ領域についてはこちらの記事で解説しています。

RAM側のメモリ配置

RAMには「実行中に変化するデータ」が配置されます。

stack
heap
bss
data

それぞれ役割が違います。


data領域(初期値あり変数)

初期値付きのグローバル変数が配置されます。

int g_value = 10;

この変数は

・初期値がある
・実行中に書き換わる

ため、RAMに配置されます。

ただし初期値はROMに保存されており、
起動時にROMからRAMへコピーされます。

データ領域についてはこちらの記事で解説しています。


bss領域(未初期化変数)

初期値を持たないグローバル変数が配置されます。

int g_value;
static int counter;

これらは起動時に
すべて0で初期化されます。

そのためROMに保存する必要がなく、
RAMのみ使用します。

BSS領域についてはこちらの記事で解説しています。


heap領域(動的確保メモリ)

mallocなどで動的に確保する領域です。

int *p = malloc(sizeof(int));

実行時にサイズが決まるため、
RAMのheap領域が使われます。

特徴

・実行時に確保
・解放が必要
・断片化が発生する

ヒープ領域についてはこちらの記事で解説しています。


stack領域(関数呼び出し用メモリ)

関数呼び出し時に使用される領域です。

void func()
{
    int a = 10;
}

この a は stack に配置されます。

用途

・ローカル変数
・関数引数
・戻りアドレス

関数終了時に自動で解放されます。

スタック領域についてはこちらの記事で解説しています。


なぜdata領域はROMとRAM両方にあるのか

初期値付き変数は次のように扱われます

ROM
初期値を保存

RAM
実行時の変数

つまり

ROM → RAM にコピーされる

この処理はスタートアップコードが行います。


スタートアップコードとの関係

電源投入時、スタートアップコードは次を実行します

① stack初期化
② data領域コピー(ROM→RAM)
③ bss領域ゼロクリア
④ main() 呼び出し

この動作はメモリマップに基づいて行われます。

スタートアップコードについてはこちらの記事で解説しています。


メモリマップは誰が決めている?

ここまで見てきたメモリ配置は、
自動的に決まっているわけではありません。

実はこの配置は リンカスクリプト によって決められています。

リンカスクリプトでは、例えば次のように指定します。

・textはROMに配置
・dataはRAMに配置
・bssはRAMに配置
・stackの開始アドレス

リンカはこの設定に従って、
プログラムや変数をメモリに配置します。

つまり

メモリマップ
= リンカスクリプトの結果

という関係になります。

リンカスクリプトについては
こちらの記事で詳しく解説します。

👉 リンカスクリプトとは?組み込みC言語のメモリ配置の仕組みを解説

まとめ

メモリマップとは

・ROMとRAMの配置図
・プログラムと変数の配置を示す
・スタートアップコードが利用する
・リンカスクリプトで決定される

組み込み開発では非常に重要な概念です。

この記事が参考になった方へ

メモリ領域については他の記事でも解説しています。

メモリ領域解説シリーズの全体像はこちら

技術に関するご相談・開発・自動化ツール作成・記事執筆などのご依頼も承っています。

小さなご相談からでもお気軽にご連絡ください。

お問い合わせはこちら

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする


目次