平成26年9月20日
初歩的な疑問
PICマイコンといえばPIC16のようにごく小規模なシステムを前提に考案されたイメージがありますが、PIC32はMIPSコアですので内部の構成も本格的なCPUになります。その分システムの全体像が分からないと、"なぜこんなになっているんだ??"という疑問が多数発生します。
PIC32に関する基本的な情報はマイクロチップ社のデータシートから得られますが、それだけではよく分からなかった疑問点を順不同で書いてみます。多分、資料のどこかには書いてあるとは思いますが...
PIC32に関する基本的な情報は先にデータシートや他のHPからある程度学習されていると仮定して細かな説明はしていません。
・ブートROMと通常のプログラムROMの違いは
CPUの機能として動作モードが追加されています。この動作モードのうちカーネルモードでのみアクセスできるアドレス領域に配置されているのがブートROM。CPUがユーザーモードのときは一切のアクセスが出来ません。プログラムROMはユーザーモードからもアクセスできるように設定することができます。
・CPUのリセット直後の動作
CPUはリセットされるとカーネルモードで動作を開始します。i80x86などに慣れている人には不自然に感じるようです。i80x86ではリセット直後はユーザーモードに相当するリアルモードで動作を開始します。これは既存製品であるi8086との互換性を取るために、そのようになっています。が、本来の動作を考えればカーネルモードで起動するべきなのは明白です。
プログラムの開始アドレスはCPUの仮想アドレスで0xBFC00000, 物理アドレスで0x1FC00000に固定されています。このアドレスは先のブートROMの先頭アドレスです。つまり、リセット直後はブートROMから処理を開始します。なお、このアドレスは後述するキャッシュをサポートしないKSEG1領域です。
・Cコンパイラによるメモリ配置
CPUの構成が複雑になった分、Cコンパイラの内部処理に関係する部分でも複数の実装方法をとりえます。XC32 Cコンパイラがデフォルトではどのように設定されているのかが気になります。下記のような疑問が沸いてきます。
- メモリの種類として従来からあるプログラムROMと新設されたブートROMがあり、これらをどう使い分けしているか
- キャッシュ機能をサポートするKSEG0領域としないKSEG1領域のどちらを使用しているか
- ユーザーモードとカーネルモードの違いに関してなんらかのサポートがあるか
この疑問を理解するためにはPIC32の機能について少し説明が必要です。
キャッシュ機能のサポート
PIC32のカーネルモードでは、同じプログラムROMの内容がKSEG0, KSEG1と呼ばれる二つのアドレス領域に配置されています。つまり、おなじROM
内容が二つの異なるアドレス上に存在しています。
両者の違いはKSEG0はキャッシュの対象なのに対してKSEG1はキャッシュされません。従って通常のプログラムならキャッシュが有効なKSEG0のアドレス領域でプログラム動作して欲しい。プログラムにおけるメモリ上の配置決定はコンパイラ(正確にはリンカ)の仕事です。リセット直後はKSEG1のブートROMアドレスで動作を開始しますので、どのタイミングかでKSEG0に移動するようになっていて欲しい。
ユーザーモードとカーネルモード
本格的なOSの実装が可能なように、CPUの機能全てを使用できるカーネルモードと一部の機能を制限したユーザーモードがあります。
ユーザーモードでは下記のような制限が加えられています。
- 特定のアドレス領域へのアクセスは禁止されています。この禁止領域にはブートROM・プログラムROMおよびRAMの大半(設定によって調整可能)・周辺機能レジスタSFRが配置されています。
- CPUからみて重要度の高い特定の命令実行は禁止されています。
常識的には、カーネルモードで動作するプログラムを出力してくれることを期待しますが、以前ルネサス社製RXマイコン用のCコンパイラであるkpit
Cコンパイラを使ったときには、スタートアップルーチン内でユーザーモードに移行するコードが含まれていました。この辺りの実装は念のため確認しておいた方が良さそうです。
確認のために簡単なプログラムをコンパイルしてみました。プログラムは下記のとおり。
#include <xc.h>
#include <sys/attribs.h>
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_1, FWDTEN = OFF
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPBDIV = DIV_2
int main(void)
{
TRISD = 0;
for(;;){
LATD=0;
LATD=0xffff;
}
return 0;
}
void __ISR (_TIMER_1_VECTOR, IPL0SRS) Timer1Handler (void)
{
_nop();
}
次にこれをコンパイルしたときのmapファイルの一部を示します。全体では非常に大きなファイルです。
kseg0 Program-Memory Usage
section address length [bytes] (dec) Description
------- ---------- ------------------------- -----------
.text.general_exception 0x9d000000 0xdc 220
.text 0x9d0000dc 0x9c 156 App's exec code
.text.main_entry 0x9d000178 0x4c 76
.text._bootstrap_except 0x9d0001c4 0x48 72
.text._general_exceptio 0x9d00020c 0x48 72
.text 0x9d000254 0x44 68 App's exec code
.vector_default 0x9d000298 0x38 56
.text 0x9d0002d0 0x18 24 App's exec code
.dinit 0x9d0002e8 0x10 16
.text._on_reset 0x9d0002f8 0x8 8
.text._on_bootstrap 0x9d000300 0x8 8
Total kseg0_program_mem used : 0x308 776 0.1% of 0x80000
kseg0 Boot-Memory Usage
section address length [bytes] (dec) Description
------- ---------- ------------------------- -----------
Total kseg0_boot_mem used : 0 0 <1% of 0x970
kseg1 Boot-Memory Usage
section address length [bytes] (dec) Description
------- ---------- ------------------------- -----------
.reset 0xbfc00000 0x1f0 496 Reset handler
.bev_excpt 0xbfc00380 0x10 16 BEV-Exception
Total kseg1_boot_mem used : 0x200 512 43.8% of 0x490
--------------------------------------------------------------------------
Total Program Memory used : 0x718 1816 0.3% of 0x81e00
--------------------------------------------------------------------------
kseg1 Data-Memory Usage
section address length [bytes] (dec) Description
------- ---------- ------------------------- -----------
Total kseg1_data_mem used : 0 0 <1% of 0x20000
--------------------------------------------------------------------------
Total Data Memory used : 0 0 <1% of 0x20000
--------------------------------------------------------------------------
.reset 0xbfc00000 0x1f0
*(.reset)
.reset 0xbfc00000 0x8 c:/microchip/xc32/v1.33/bin/bin/../../lib/gcc/pic32mx/4.5.2/../../../../pic32mx/lib/./proc/32MX795F512L/crt0_mips32r2.o
0xbfc00000 _reset
*(.reset.startup)
.reset.startup
0xbfc00008 0x1e8 c:/microchip/xc32/v1.33/bin/bin/../../lib/gcc/pic32mx/4.5.2/../../../../pic32mx/lib/./proc/32MX795F512L/crt0_mips32r2.o
.text 0x9d0000dc 0x9c
.text 0x9d0000dc 0x9c build/default/production/main.o
0x9d0000dc main
0x9d00010c Timer1Handler
分かりにくいのですが、Cのスタートアップルーチンcrt0はKSEG1のブートROMに配置されています。".reset"というラベルがそれでアドレスが0xbfc00000であることからKSEG1のブートROM領域に配置されていると分かります。同様にmain関数はKSEG0に配置されていす。アドレス0x9d0000dcはKSEG0のプログラムROM領域です。
このことからCPUが起動するとキャッシュが無効なKSEG1のブートROM領域でCのスタートアップルーチンを処理し、この処理の最後にキャッシュが有効なKSEG0のプログラムROM領域に配置されたmain関数を呼び出していることが分かります。
全体を通してCPUの動作モードはカーネルモードで動作しているようです。
XC32のデフォルト設定では、PIC32を従来のPICと同じように扱うときに不便がないように設定されていることが分かります。
逆にPIC32の機能を有効に使おうとすると、特にリンカの設定を変更する必要が出てきます。例えば、
- ブートROMにブートストラップローダーを配置して、プログラム更新できるようにする
- 起動直後の処理を高速にするため、Cのスタートアップ処理をキャッシュが有効なKSEG0領域で行う
- 本格的なRTOSを導入する(ユーザーモードとカーネルモードを使い分ける)
などではリンカ設定の変更が必要になります。