平成26年10月 8日
MPLAB Harmonyの使い方(3)
先のプロジェクトを変更して、ライブラリ機能を追加してみます。
最初に先のプロジェクトのバックアップコピーをとっておきます。
次に下図のように、当初チェックを外したDevice Control下のuse Device Control System Service?にチェックを入れて、System
Clock FrequencyとMaximum Paripheral Bus Frequencyに各々30,000,000(30MHz)を入力します。
このDevice Control System Serviceなる機能、名前だけでは何をするのか分からない。
次にGenerateボタンを押すと確認画面が現れますが、肯定側のボタンを選択すると上図右のProjectウインドウのようにソースファイルのFramework以下にファイルが追加されます。他には図には表れていないですがHeaderファイルとしても追加されたファイルがあります。
主要なファイルの変更箇所を示します。追加部分は太字部分です。
SYSTEM_CONFIG.H
#ifndef _SYSTEM_CONFIG_H
#define _SYSTEM_CONFIG_H
#define SYS_BUFFER false
#define SYS_QUEUE false
#define SYS_DEVCON_SYSTEM_CLOCK 30000000
#define SYS_DEVCON_PIC32MX_MAX_PB_FREQ 30000000
#endif // _SYSTEM_CONFIG_H
SYS_DEFINITIONS.H
#ifndef _SYS_DEFINITIONS_H
#define _SYS_DEFINITIONS_H
#include <stddef.h>
typedef struct
{
SYS_MODULE_OBJ sysDevcon;
} SYSTEM_OBJECTS;
extern SYSTEM_OBJECTS sysObj;
#endif /* _SYS_DEFINITIONS_H */
system_init.c
#include "system_config.h"
#include "app.h"
#include "system_definitions.h"
/*** DEVCFG0 ***/
#pragma config DEBUG = ON
#pragma config ICESEL = ICS_PGx2
#pragma config PWP = 0xff
#pragma config BWP = OFF
#pragma config CP = OFF
/*** DEVCFG1 ***/
#pragma config FNOSC = PRIPLL
#pragma config FSOSCEN = OFF
#pragma config IESO = OFF
#pragma config POSCMOD = HS
#pragma config OSCIOFNC = OFF
#pragma config FPBDIV = DIV_1
#pragma config FCKSM = CSDCMD
#pragma config WDTPS = PS1048576
#pragma config FWDTEN = OFF
/*** DEVCFG2 ***/
#pragma config FPLLIDIV = DIV_4
#pragma config FPLLMUL = MUL_15
#pragma config FPLLODIV = DIV_2
#pragma config UPLLIDIV = DIV_12
#pragma config UPLLEN = OFF
/*** DEVCFG3 ***/
#pragma config USERID = 0xffff
#pragma config FSRSSEL = PRIORITY_7
#pragma config FMIIEN = OFF
#pragma config FETHIO = OFF
#pragma config FCANIO = OFF
#pragma config FUSBIDIO = OFF
#pragma config FVBUSONIO = OFF
// *****************************************************************************
// Section: Library/Stack Initialization Data
// *****************************************************************************/
// *****************************************************************************
// Section: Driver Initialization Data
// *****************************************************************************
// *****************************************************************************
// Section: System Data
// *****************************************************************************
/* Structure to hold the object handles for the modules in the system. */
SYSTEM_OBJECTS sysObj;
// *****************************************************************************
// Section: Module Initialization Data
// *****************************************************************************
const SYS_DEVCON_INIT sysDevconInit =
{
.moduleInit = {0},
};
// *****************************************************************************
// Section: Static Initialization Functions
// *****************************************************************************
// *****************************************************************************
// Section: System Initialization
// *****************************************************************************
void SYS_Initialize ( void* data )
{
/* Core Processor Initialization */
sysObj.sysDevcon = SYS_DEVCON_Initialize(SYS_DEVCON_INDEX_0, (SYS_MODULE_INIT*)&sysDevconInit);
SYS_DEVCON_PerformanceConfig(SYS_DEVCON_SYSTEM_CLOCK);
/* System Services Initialization */
/* Initialize Drivers */
/* Initialize System Services */
/* Initialize Middleware */
/* Initialize the Application */
APP_Initialize();
}
sysytem_tasks.c
#include "system_config.h"
#include "system_definitions.h"
#include "app.h"
// *****************************************************************************
// Section: System "Tasks" Routine
// *****************************************************************************
void SYS_Tasks ( void )
{
/* Maintain the state machines of all library modules executing polled in
the system. */
/* Maintain system services */
SYS_DEVCON_Tasks(sysObj.sysDevcon);
/* Maintain Device Drivers */
/* Maintain the application's state machine. */
APP_Tasks();
}
幾つか重要な項目が見えています。まず、MPLAB Harmony Configratorの画面で設定したパラメータ項目の多くはsystem_config.hファイルに書き出されます。ここではSystem
Clock FrequencyとMaximum Paripheral Bus Frequencyが追加されています。例外はConfigrationBitsの設定で、こちらはsystem_init.cに書き出されています。
これは、このフレームワークをMPLAB Harmony Configratorを介さずに使用する場合に必要な作業内容を示しています。
動作上での変化では、system_init.cファイル中のSYS_Initialize()関数に二つの関数が追加されています。
2つ目のSYS_DEVCON_PerformanceConfig()関数をHelpファイルで調べると、
- フラッシュROMアクセスへのウエイト数の最適化
- 内臓RAMアクセスへのウエイト数の最適化
- プリフェッチ機能の有効化
- 内部バスアクセスへのウエイト数の最適化
が行われるようです。
問題は最初の関数です。というかDevice Control System Serviceなるものは何?という疑問です。
主な機能は二つあり、一つは上記のようにCPUから周辺機能へのアクセスの最適化を管理します。もう一つの説明をHelpファイルから探すと、次のように英語で書かれています。ここでは翻訳ソフトを併用した文章を示します。
(以下、help_harmony_v1_00.pdfの英文の引用です。ただし、翻訳してあります)
Device Control System Serviceは、どんな特定の周辺装置にも結び付けられないかもしれない装置に特有のオペレーションのコレクションである、多数の周辺装置、あるいはどんな周辺装置も全く含んでいないかもしれない。
このサービスは、システム・ロックおよびロック解除を行なう機能、およびCPUへのデータおよび指示処理能力を最適化するための一般化された機能を提供する。
PIC32装置内の多くのオペレーションが、オペレーションが行なわれる前にシステムがロックを解除されることを要求する。
これは遍歴するコードが重大なセッティングを誤って変更するのを防ぐ安全機能である。
unlockシーケンスは、特定の登録に一連の「マジックナンバー」を順に書くことから成る。
その後、重大なオペレーションは行なわれる。また、システムは同じ登録に書くことにより再ロックされる。
このサービスは、行なうすべてのPIC32装置を横切って働く便利なAPIを提供する‥‥この‥‥ロック/シーケンスのロックを解除する。
(引用、ここまで)
要は誤書き込み防止のための書き込みシーケンスを管理する機能のようです。
が、それでも疑問は残ります。この誤書き込み防止機能はSYS_DEVCON_PerformanceConfig()関数内部で使用されています。
sys_devcon_pic32mx.cの一部を引用
void __attribute__((nomips16)) SYS_DEVCON_PerformanceConfig( unsigned int sysclk )
{
bool int_flag = false;
register unsigned long tmp;
int pbclk = (sysclk > SYS_DEVCON_PIC32MX_MAX_PB_FREQ) ? 2 : 1;
asm("mfc0 %0,$16,0" : "=r"(tmp));
tmp = (tmp & ~7) | 3;
asm("mtc0 %0,$16,0" :: "r" (tmp));
if (PLIB_OSC_PBClockDivisorGet(OSC_ID_0, OSC_PERIPHERAL_BUS_1) != pbclk)
{
SYS_DEVCON_SystemUnlock();
PLIB_OSC_PBClockDivisorSet (OSC_ID_0, OSC_PERIPHERAL_BUS_1, pbclk);
SYS_DEVCON_SystemLock();
}
/* TODO: use PCACHE_ID_0 after plib gen is fixed */
if (PLIB_PCACHE_ExistsWaitState(/*PCACHE_ID_0*/0))
{
int ws;
if (sysclk <= 30000000)
ws = 0;
else if (sysclk <= 60000000)
ws = 1;
else
ws = 2;
if (PLIB_INT_IsEnabled (INT_ID_0))
{
int_flag = true;
PLIB_INT_Disable(INT_ID_0);
}
/* TODO: use PCACHE_ID_0 after plib gen is fixed */
PLIB_PCACHE_WaitStateSet(/*PCACHE_ID_0*/0, ws);
if (int_flag)
{
PLIB_INT_Enable(INT_ID_0);
int_flag = false;
}
}
if (PLIB_INT_IsEnabled (INT_ID_0))
{
int_flag = true;
PLIB_INT_Disable(INT_ID_0);
}
/* TODO: use PCACHE_ID_0 after plib gen is fixed */
if (PLIB_PCACHE_ExistsPrefetchEnable(/*PCACHE_ID_0*/0))
{
/* TODO: use PCACHE_ID_0, PLIB_PCACHE_PREFETCH_ENABLE_ALL after plib gen is fixed */
PLIB_PCACHE_PrefetchEnableSet(/*PCACHE_ID_0*/0, /*PLIB_PCACHE_PREFETCH_ENABLE_ALL*/3);
}
if (PLIB_BMX_ExistsDataRamWaitState(BMX_ID_0))
{
PLIB_BMX_DataRamWaitStateSet(BMX_ID_0, PLIB_BMX_DATA_RAM_WAIT_ZERO);
}
if (int_flag)
{
PLIB_INT_Enable(INT_ID_0);
int_flag = false;
}
}
明らかにPLIB_OSC_PBClockDivisorSet()関数での書込み操作に対して誤書き込みを解除・禁止しています。
ではPLIB_OSC_PBClockDivisorSet()関数の中身はというと、OSCCONレジスタ(PBDIV)への書き込み操作をしています。
しかし、OSCCONレジスタに対して誤書き込みを禁止する機能はありません。なぜ、こんなコードが存在するのか理解できません。
(念のため、PIC32MZも調べましたが、やはり無い)
SYS_DEVCON_SystemUnlock()・SYS_DEVCON_SystemLock()関数の内部処理も追ってみたのですが、結局何もしない処理になっているようです。
結論としては、Device Control System Serviceなる機能の目的は良く分かりません。このような意味不明のコードがプログラム中に紛れ込むのは非常に気持ちの悪いものです。単にウエイト数の調整だけなら自分で類似のコードを書いて使用した方が安心できます。
なお、このプログラムを動作させたときのポート出力波形は下記のようになり、無駄なだけで悪さはしていないようです。
ウエイトの削除によっておよそ6倍まで高速化しています。フラッシュROMに対するアクセスでは8倍になりますが、実際にはROM以外へのアクセスやプログラムの分岐時間など高速化できない時間も含まれていることを加味すれば、まあ妥当な数字でしょう。当然、変更によってプログラムの処理すべき項目も少しだけ増加しています。
MPLAB Harmony Configratorを使ってみての感想
主要なプログラム部分の雛形を自動で生成してくれることの便利さは感じられます。
反面、そこで追加される関数や変数といったコードの処理内容を説明する文書がしっかりしていないと非常に扱い難い代物になってしまいます。今回もいきなりおかしな処理内容に苦労しました。参考になる文書がHelpファイルのみなのは不安です。この文書だけでは内容的にも不十分です。ライブラリ中の関数について、全てを説明しているのではないようです。結局は、プログラムを追跡する苦労が伴います。