スパイス  組み込み制御装置の受注製作

PIC32の導入
平成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コンパイラがデフォルトではどのように設定されているのかが気になります。下記のような疑問が沸いてきます。  この疑問を理解するためにはPIC32の機能について少し説明が必要です。
    キャッシュ機能のサポート
 PIC32のカーネルモードでは、同じプログラムROMの内容がKSEG0, KSEG1と呼ばれる二つのアドレス領域に配置されています。つまり、おなじROM 内容が二つの異なるアドレス上に存在しています。
両者の違いはKSEG0はキャッシュの対象なのに対してKSEG1はキャッシュされません。従って通常のプログラムならキャッシュが有効なKSEG0のアドレス領域でプログラム動作して欲しい。プログラムにおけるメモリ上の配置決定はコンパイラ(正確にはリンカ)の仕事です。リセット直後はKSEG1のブートROMアドレスで動作を開始しますので、どのタイミングかでKSEG0に移動するようになっていて欲しい。

    ユーザーモードとカーネルモード
 本格的なOSの実装が可能なように、CPUの機能全てを使用できるカーネルモードと一部の機能を制限したユーザーモードがあります。
ユーザーモードでは下記のような制限が加えられています。
  1. 特定のアドレス領域へのアクセスは禁止されています。この禁止領域にはブートROM・プログラムROMおよびRAMの大半(設定によって調整可能)・周辺機能レジスタSFRが配置されています。
  2. 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の機能を有効に使おうとすると、特にリンカの設定を変更する必要が出てきます。例えば、
などではリンカ設定の変更が必要になります。


目次へ  前へ  次へ