UMEHOSHI ITA TOP PAGE

アセンブリ言語で作る「 EEPROM上で動作するLチカ プログラム」(リセットスイッチでスタートさせる
  (基板入手直後で実験できます)

このページは、パワーONでないリセット操作で実行するEEPROM上で起動するプログラムの作り方です。
似ていますが、RAM上でプログラムをリセットで起動する方法のページで、紹介していますの比較してください。
リセットに関係なく、"R00〜"の実行アドレスを指定した起動用のコマンド文字列で実行させる方法のリンクはこちらです

D1のLEDのチカチカ点滅を永遠に行う例を示します。

D1のLEDは、下記回路図の赤の接続線で示しています。赤マルのRB15に繋がっていますが、 これは制御チップで[PORTB REGISTER]のBit15をを意味しています(資料:DS60001168K TABLE 11-4: PORTB REGISTER MAP)。
このRB15の出力端子がLowで消え、HiにすればLEDに電流が流れて点灯します。

 UMEHOSHI ITAの起動時の初期化の概要を下記に示します。
if(  パワーオンリセットか? ){
      マクロ用の関数へのポインタなど各種変数の初期化や_HANDLES配列の初期化を行う。
      「ウメ・エディットプログラム」で使われるマクロ関数が登録される。そして、
        _HANDLES[_IDX_HANDLE_USER_SET_FUNC] = dummy_init_function;  の関数が登録される。
         (上記関数へのポインタは、_handle_user_set_func()のマクロの関数で実行できる)
        _HANDLES[_IDX_INIT_SUB_FUNC] = dummy_init_function;  の関数が登録される。
         (上記関数へのポインタは、_init_sub_func()のマクロの関数で実行できる)
        power_on_flag = 1;
}
_handle_user_set_func(); // 起動時のマクロ関数呼び出し(ここで、_HANDLESの要素の変更などを行う)

SYS_Initialize ( NULL );// ここでUSBや指定IOの初期化が行われる

if(power_on_flag == 1){
        power_on_start();
} else {
        void (* func)(void) = (void *) 0x9D020000;// EEPROM 領域
        if( *((uint32_t *)func) != 0xffffffff ){// EEPROMの記憶内容が0xffでない?
            func();// EEPROMの0x9D020000番地の関数を呼び出す(リセットで自動的に起動)
        }	
}
_init_sub_func(); // 起動時のマクロ関数呼び出し(「ウメ・エディットプログラム」の起動関数)

この後で、『USB受信文字列処理用のループ』に進みます。

上記より、パワーオン時にマクロ関数用配列の_HANDLES[機能識別添え字] 群に関数を記憶しています。
利用者の「ウメ・エディットプログラム」で、このはそのマクロ関数をAPIの呼び出す手法でプログラミングします。
この関数記憶で、_HANDLES[_IDX_INIT_SUB_FUNC]に起動関数を登録しています。
その後で、この起動関数の_init_sub_func()を呼び出しています。

さてEEPROM領域の0x9D020000番地に関数が存在する場合、 パワーONを伴わないリセット時に、_0x9D020000番地の関数が実行される仕組みになっています。
(つまりROM領域の起動関数はRAM領域と異なり、_0x9D020000番地の関数を実行してから _init_sub_func()を実行する構造になっています。)

下記C言語ソースは、上記仕組みを利用して、_0x9D020000番地に起動関数を配置しています。
これは、「umehoshiEdit」ツールで、ビルド実行できます。 右リストは逆アセンブルしたコードで、これを参考に、後述のアセンブリリストを作っています。

#include <xc.h>	// test.c
#include "common.h"
// __attribute__((address( 0x80005000 ))) void start_main (void);

// ROM領領域置は、絶対アドレスを指定する必要があります。(書かないとRAM領域になります。)
__attribute__((address( 0x9D020000 ))) void start_main (void);
void start_main(){
	__asm__ ("NOP");
	for(;;){
		PORTBSET = 0x8000;	// D1 LED 点灯(RB15を1):これは、0xbf886128番地への設定です.
		__asm__ ("NOP");	// チョット待つ 
		wait(0x003f0000);
		PORTBCLR = 0x8000;	// D1 LED 消灯(RB15を0):これは、0xbf886124番地への設定です.
		__asm__ ("NOP");	// チョット待つ
		wait(0x003f0000);
	}
}
// ROM領領域置は、可能な絶対アドレスを指定する必要があります。(書かないとRAM領域になります。)
__attribute__((address( 0x9D020050 ))) void wait(int n);
void wait(int n){// nカウント待つ
	while(n--) ;
}

/*
EEPROM領域におけるリセットで起動するプログラムは 0x9D020000番地からに決まっており、
 このアドレスのワード内容が0xffffffffでない場合だけ、このアドレス先の関数を実行する。

なお、EEPROMの領域(0x9D020000〜0x9D03FFFF)の領域に配置する関数は、
 全て「__attribute__((address( 絶対番地 ))) 〜関数宣言表現;」
 つまり、上記のように2つ関数でも、翻訳されたコード範囲が重ならないように、アドレスを指定する必要がある。
(後述の絶対アドレス指定は余裕のある大きな値にすると良い。適正値は生成されるa.lst.txtからヒント値得られる。)
(リンケージエディタの機能を手動で指定する必要があるが訳だが、mylinkerscript.ldの設定で一部を自動化できる)
*/
9d020000 <start_main>:
9d020000:	27bdffe8 	addiu	sp,sp,-24
9d020004:	afbf0014 	sw	ra,20(sp)
9d020008:	afbe0010 	sw	s8,16(sp)
9d02000c:	03a0f021 	move	s8,sp
9d020010:	00000000 	nop

9d020014 <.L2>:
9d020014:	3c02bf88 	lui	v0,0xbf88
9d020018:	34038000 	li	v1,0x8000
9d02001c:	ac436128 	sw	v1,24872(v0)
9d020020:	00000000 	nop
9d020024:	0f408014 	jal	9d020050 <wait>
9d020028:	3c04003f 	lui	a0,0x3f
9d02002c:	3c02bf88 	lui	v0,0xbf88
9d020030:	34038000 	li	v1,0x8000
9d020034:	ac436124 	sw	v1,24868(v0)
9d020038:	00000000 	nop
9d02003c:	0f408014 	jal	9d020050 <wait>
9d020040:	3c04003f 	lui	a0,0x3f

9d020044 <.LBE2>:
9d020044:	0b408005 	j	9d020014 <.L2>
9d020048:	00000000 	nop

9d020050 <wait>:
9d020050:	27bdfff8 	addiu	sp,sp,-8
9d020054:	afbe0004 	sw	s8,4(sp)
9d020058:	03a0f021 	move	s8,sp
9d02005c:	afc40008 	sw	a0,8(s8)
9d020060:	00000000 	nop
9d020064:	8fc20008 	lw	v0,8(s8)
9d020068:	2443ffff 	addiu	v1,v0,-1
9d02006c:	afc30008 	sw	v1,8(s8)
9d020070:	1440fffc 	bnez	v0,9d020064 <wait+0x14>
9d020074:	00000000 	nop
9d020078:	03c0e821 	move	sp,s8
9d02007c:	8fbe0004 	lw	s8,4(sp)
9d020080:	27bd0008 	addiu	sp,sp,8
9d020084:	03e00008 	jr	ra
9d020088:	00000000 	nop

  以下に、上記の実際に動作した、アセンブリリストを示します。上のC言語の逆アセンブルリストをコードにしてみました。

(UMEHOSHI ITA基板で使っている制御チップのPIC32MX270F256B用(MIPS32系)アセンブリに必要な情報の概要です。)


  上記で編集したアセンブリソースを、左のボタンで「アッセンブル」できます。
番地から実行するUME専用Hexコマンドを、 最後に追加埋め込みする場合にチェックする==>

上記で生成した[UME専用Hexコマンド]のテキストを コピー(CTRL+A CTRL+C)してUMEHOSHI ITAへ転送して動かすことができます。
上記のEEPROMへ書き込む[UME専用Hexコマンド]は、その領域byteが0xffになっていなければなりません。
つまり、書き込み領域を使っている場合は、その領域を消去してから書き込み処理する必要があります。
その場合は、次の、0x9D020000〜0x9D020400の1024byteを消去する処理をしてから行ってください。
#include <xc.h>	// test.c
#include "common.h"

// RAMで動かす場合の絶対アドレス範囲は、(0x80005000〜0x80008000)とします。
__attribute__((address( 0x80005000 ))) void start_main (void);
void start_main(){
	_nvm_erase_page(0x9D020000);
}

/*

EEPROMの0x9D020000番地から1024byteの領域を0xFFに消去する

nvm_erase_pageの引数のアドレスは
x9D020400,0x9D020800,0x9D020c00,0x9D021000,・・
	・・,9D03F800,9D03FC00 の何れかを指定する。
*/
80005000 <start_main>:
80005000:	27bdffe8 	addiu	sp,sp,-24
80005004:	afbf0014 	sw	ra,20(sp)
80005008:	afbe0010 	sw	s8,16(sp)
8000500c:	03a0f021 	move	s8,sp
80005010:	3c02a000 	lui	v0,0xa000
80005014:	344240f0 	ori	v0,v0,0x40f0
80005018:	8c420000 	lw	v0,0(v0)
8000501c:	3c049d02 	lui	a0,0x9d02
80005020:	0040f809 	jalr	v0
80005024:	00000000 	nop
80005028:	03c0e821 	move	sp,s8
8000502c:	8fbf0014 	lw	ra,20(sp)
80005030:	8fbe0010 	lw	s8,16(sp)
80005034:	27bd0018 	addiu	sp,sp,24
80005038:	03e00008 	jr	ra
8000503c:	00000000 	nop
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
#include <xc.h>	// 0x9d020000番地のページ消去(0xffに設定)
	.set noreorder #アセンブラに命令の順序を自動変更させない。
	.section	start_main,address(0x80005000),code
	.ent	start_main
	addiu	sp,sp,-8
	sw	ra,4(sp)
	lui	v0,0xa000
	ori	v0,v0,0x40f0 # hex(0xA0004000+60*4)==>0xa00040f0より算出
	lw	v0,0(v0)
	lui	a0,0x9d02 # 消去対象のアドレス
	jalr	v0
	nop
	lw	ra,4(sp)
	addiu	sp,sp,8
	jr	ra
	nop	
左のコードで生成される[UME専用Hexコマンド]は、 0x9d020000番地のページを削除するコードで、アセンブルして使います。
実行するためには、「アッセンブル」のボタン操作前で、
[0x80005000]番地から起動するコード埋め込みのチェックをして、生成してください。
そして「command.txt」の名前でファイル化して、pythonで実行させます。
なお、「command.txt」の先頭行に、「G109D0200000059」の行を追加することで、
実行前の0x9d020000番地の16byteの内容を確認することができます。

上記コードを、Pythonで[UMEHOSHI ITA]基板で転送して実行する手順の紹介

予め、pythonが動作するターミナル(コマンドプロンプト)を、開いて準備して置くとよいでしょう。
以下では、R:\workを作業位置(カレントディレクトリ)にして説明しています。
また、pythonプログラムでは、serialモジュールを追加しておく必要があります。
「pip show serial」のコマンド操作で、インストールされているか確認できます。
インストールされていない場合は、「pip install pyserial」の操作で、インストールしておくと良いでしょう。

【1】 [UMEHOSHI ITA]基板で転送するテキストファイル「command.txt」を用意します。

上記の「アセンブラ編集部」の直下にある「アセンブル」ボタンをクリックします。
(このボタン右下の、指定アドレスから実行させるコード埋め込み用のチェックボックスがチェックされていること確認して行う)

「アセンブラ編集部」のソースにエラーが無ければ、[UME専用Hexコマンド]のテキストが、その下の TextArea に表示されます。
(エラーがあれば、エラーが無くHEXコードが生成されるまで、「アセンブラ編集部」の修正とアセンブル」を繰り返します)

メモ帳などで、「command.txt」のファイルを生成して開き、
上記操作で得られたTextAreaの[UME専用Hexコマンド]のテキストを、 コピー(CTRL+A CTRL+C)操作し、
  それを「command.txt」編集画面で貼り付け((CTRL+V)して、保存します。
保存位置は、pythonが動作するターミナルの作業位置(カレントディレクトリ)です。(後述例では、R:\workで示しています)

【2】 [UMEHOSHI ITA]基板へ[command.txt」を転送するpythonファイルを用意します。

「instruct.py」の名前のソースファイルを、[command.txt」と同じ位置に作ります。
(一度作れば、次回は使うだけで、作る必要はありません。(CTRL+A CTRL+C)操作で、コピー可能)


【3】 [UMEHOSHI ITA]基板へ[command.txt」を転送して実行させる。

上記で作った「instruct.py」と[command.txt」が存在する位置で、 pythonが動作するターミナル(コマンドプロンプト)を開きます。
以下では、この作業位置(カレントディレクトリ)が、R:\workである場合の例で説明しています。

まず [UMEHOSHI ITA]基板と、PCをUSBで接続します。

次にターミナルプロンプトを『powershell』にして、
『Get-CimInstance Win32_PnPEntity | Where-Object {$_ -like "*(COM*"} | Select-Object Caption』
のコマンド操作で、USBのシリアル デバイスのCOM番号を調べます。

実行例
R:\work>powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

新しいクロスプラットフォームの PowerShell をお試しください https://aka.ms/pscore6

PS R:\work> Get-CimInstance Win32_PnPEntity | Where-Object {$_ -like "*(COM*"} | Select-Object Caption

Caption
-------
Bluetooth リンク経由の標準シリアル (COM10)
USB シリアル デバイス (COM4)
Bluetooth リンク経由の標準シリアル (COM11)
PS R:\work>

この実行例から、USB シリアル デバイス がCOM4が使える状態になっていることが分かります。
このリンクページで示すように、デバイスマネージャで確認することもできます)
この番号の4を記憶して次の転送プログラム実行に進むのですが、 そのUSB シリアル デバイス が見つからない場合、次の点が考えられます。

いずれにしても、接続でUSB シリアル デバイスCOM番号が得られないと、次に進めません。

続いて、以下の『python command.txt』の操作で、転送実行プログラム(上記のアセンブラ用の例)を実行します。
実行例 (最初に上記で得られた番号の4をキー入力しています。)
R:\work>python instruct.py
USB シリアル デバイスで、使用するCOMの番号を入力>>4
S109D0200000088BF023C00800334286143AC00000000  AFチェックサム: 0
USB受信● b'S109D0200000088BF023C00800334286143AC00000000AF\r\n'
USB受信● b'SET:9D020000\r\n'
S109D020010001480400F3F00043C88BF023C00800334  90チェックサム: 0
USB受信● b'S109D020010001480400F3F00043C88BF023C0080033490\r\n'
USB受信● b'SET:9D020010\r\n'
S109D02002000246143AC1480400F3F00043C0080400B  9Bチェックサム: 0
USB受信● b'S109D02002000246143AC1480400F3F00043C0080400B9B\r\n'
USB受信● b'SET:9D020020\r\n'
S049D0200300000000000  C7チェックサム: 0
USB受信● b'S049D0200300000000000C7\r\n'
USB受信● b'SET:9D020030\r\n'
S109D02005000F8FFBD270400BFAF21B88000FFFFF726  DAチェックサム: 0
USB受信● b'S109D02005000F8FFBD270400BFAF21B88000FFFFF726DA\r\n'
USB受信● b'SET:9D020050\r\n'
S109D02006000FEFFE016000000000800BD270800E003  7Dチェックサム: 0
USB受信● b'S109D02006000FEFFE016000000000800BD270800E0037D\r\n'
USB受信● b'SET:9D020060\r\n'
S049D0200700000000000  C3チェックサム: 0
USB受信● b'S049D0200700000000000C3\r\n'
USB受信● b'SET:9D020070\r\n'
終了確認の Enter >>>

R:\work>

[command.txt」の転送が終わって、終了確認の Enter >>>のプロンプトが出て、ENTERキーを 入力することで終了しますが、 まだ「 Lチカ 」は実行しません。
実行するには、パワーオンの後でリセットボタンを押すと、登録関数実行で「 Lチカ 」が始まります。

上記のプログラムは、USBで電源を供給するとすぐ動作するものではありません。
電源を供給後に、リセットボタン(SW1)を押すことで、起動する作品です。
電源を供給後にすぐ起動させるプログラムは、こちらのページで紹介しています。