ORANGE pico で遊ぶ (3):シリアルブートローダを書き込む

(2016年6月5日)

 PIC32MX + Pinguino コンパイラの活用法を、iruka さんが精力的に研究されている。ORANGE pico の特設ページもある。これはぜひフォローして行きたい。

 まずは、UART(シリアル転送)経由のブートローダを作成してみた。ブートローダをうまく作ってやると、プログラムの書き換えにいちいち PICkit を持ち出さなくてよいので、快適に開発することができる。

1. 元ネタ

 iruka さんの GitHub ページから、ORANGE pico プロジェクトのファイルを丸ごとダウンロードする。samples ディレクトリの中に uart_bootloader があるので、これをとりあえず複製しておく。

2. Makefile を修正する

 まず、Pinguino 関連のディレクトリを変更する。また、PROC32MX270F256B とする。本当は 32MX170F256B だけど、違いは USB の有無だけで、バイナリのビルドには支障ない。また、コンパイラのファイル名が異なるので、BINPREFIX という変数を新たに導入。

--- ../uart_bootloader/Makefile	2016-05-15 18:42:13.000000000 +0900
+++ Makefile	2016-05-29 22:21:38.000000000 +0900
@@ -1,8 +1,9 @@
 
-SHELL			=	cmd.exe
-HOME			=   C:\pinguino-11
+#SHELL			=	cmd.exe
+PHOME			= /opt/Pinguino/v11
 BOARD			=	PIC32_PINGUINO_220
-PROC			=	32MX220F032B
+PROC			=	32MX270F256B
+BINPREFIX = mips-elf-

 # ------------------------------------------------------------------------------
 # Makefile.win32 \ 32-bit Pinguino

 パスの区切りをスラッシュに変更。また、rm, cp, mv を本来のコマンド名に変更。

@@ -14,21 +15,21 @@
 # ------------------------------------------------------------------------------
 
 SRCDIR		=	.
-P32DIR		=	$(HOME)\p32
-P32CORE		=	$(HOME)\compilers\p32
-BINDIR		=	$(P32CORE)\bin
-INCDIR		=	$(P32DIR)\include
-LKRDIR		=	$(P32DIR)\lkr
-OBJDIR		=	$(P32DIR)\obj\non-free
-
-INCLUDEDIRS	=	-I$(INCDIR)\non-free \
-				-I$(INCDIR)\pinguino\core \
-				-I$(INCDIR)\pinguino\libraries \
+P32DIR		=	$(PHOME)/p32
+P32CORE		=	$(PHOME)/compilers/p32
+BINDIR		=	$(P32CORE)/bin
+INCDIR		=	$(P32DIR)/include
+LKRDIR		=	$(P32DIR)/lkr
+OBJDIR		=	$(P32DIR)/obj/non-free
+
+INCLUDEDIRS	=	-I$(INCDIR)/non-free \
+				-I$(INCDIR)/pinguino/core \
+				-I$(INCDIR)/pinguino/libraries \
 				-I$(LKRDIR) \
 				-I$(PDEDIR) \
 				-I$(OBJDIR)
 
-LIBDIRS		=	-L$(OBJDIR)\usb
+LIBDIRS		=	-L$(OBJDIR)/usb
 
 # ------------------------------------------------------------------------------
 # Include object list $(OBJS)

@@ -88,12 +89,15 @@ # commands
 # ------------------------------------------------------------------------------
 
-CC				=	$(BINDIR)\p32-gcc.exe
-OBJC			=	$(BINDIR)\p32-objcopy.exe
+CC				=	$(BINDIR)/$(BINPREFIX)gcc
+OBJC			=	$(BINDIR)/avr-objcopy
 LIBS			=	-lm -lgcc -lc
-RM				=	@del
-CP				=	@copy
-MV				=	@move
+#RM				=	@del
+#CP				=	@copy
+#MV				=	@move
+RM = rm
+CP = cp
+MV = mv

 # ------------------------------------------------------------------------------
 # flags

 オリジナルはボーレートを 500000 としているが、無難な 115200 に変更。また、README を読むとわかる通り、iruka さんは 8 MHz 水晶を追加して実験している。ORANGE pico type S は水晶無しなので、この設定を変更する。

@@ -108,6 +112,7 @@
 # Option: CHANGE BAUDRATE
 #CFLAGS		+=	-DBAUDRATE=115200
 
+CFLAGS += -DUSE_INTERNAL_FRC_OSC
 
 LDFLAGS		=	$(LIBDIRS) $(LIBS)
 
@@ -118,7 +123,7 @@
 				-Wl,--defsym,_min_heap_size=$(HEAP_SIZE) \
 				-Wl,-Map=$(SRCDIR)\output.map \
 				-T$(LKRSCRIPT) \
-				-T$(LKRDIR)\elf32pic32mx.x
+				-T$(LKRDIR)/elf32pic32mx.x
 
 #-------------------------------------------------------------------------------
 #	rules   
@@ -133,21 +138,19 @@
 
 all: $(OBJS)
 	@echo $(CC) *OBJS* -o $(SRCDIR)\main32.elf
-	@$(CC) $(ELF_FLAGS) $(LDFLAGS) $(CFLAGS) -o $(SRCDIR)\main32.elf  \
-		$(OBJS)	\
-        $(HOME)/user/source/obj/non-free/$(PROC).o \
-		-lm -lgcc -lc
-	$(OBJC) -O ihex $(SRCDIR)\main32.elf $(SRCDIR)\main32.hex
-	hex2dump main32.hex >main32.dmp
-	$(BINDIR)\p32-objdump -S main32.elf > main32.lss
-	hex2pickit3 main32.hex pickit3.hex
+	@$(CC) $(ELF_FLAGS) $(LDFLAGS) $(CFLAGS) -o $(SRCDIR)/main32.elf \
+		$(OBJS)	$(OBJDIR)/$(PROC).o -lm -lgcc -lc
+	$(OBJC) -O ihex $(SRCDIR)/main32.elf $(SRCDIR)/main32.hex
+	#hex2dump $(SRCDIR)/main32.hex >$(SRCDIR)/main32.dmp
+	$(BINDIR)/$(BINPREFIX)objdump -S $(SRCDIR)/main32.elf > $(SRCDIR)/main32.lss
+	hex2pickit3 $(SRCDIR)/main32.hex $(SRCDIR)/pickit3.hex
 
 clean:
 	-$(RM) main32.elf
 	-$(RM) *.o
 
 exec:
-	$(OBJC) -O ihex $(SRCDIR)\main32.elf $(SRCDIR)\main32.hex
+	$(OBJC) -O ihex $(SRCDIR)/main32.elf $(SRCDIR)/main32.hex
 
 correct:
 
@@ -159,4 +162,3 @@
 	@echo $(CC) -c $< -o $@
 	@$(CC) $(ELF_FLAGS) $(CFLAGS) -c $< -o $@

2. ファームウェアのソースを修正する

 config.c: 内蔵 RC 発振を使って 48 MHz 動作させるための設定。

--- ../uart_bootloader/config.c	2016-05-15 18:42:13.000000000 +0900
+++ config.c	2016-05-29 18:08:05.000000000 +0900
@@ -26,8 +26,8 @@
 #ifdef	USE_INTERNAL_FRC_OSC	// RC OSC
 #define	CONFIG_FPLLIDIV			IDIV_1	/* 4 MHz ->	4 MHz */	
 #define	CONFIG_UPLLIDIV			IDIV_1	/* 4 MHz ->	4 MHz */	
-#define	CONFIG_FNOSC			FRC		/* RC  */	   
-#define	CONFIG_POSCMOD			0		/* Primary Osillator is	EC (External)	*/	  
+#define	CONFIG_FNOSC			FRCPLL		/* RC + PLL  */	   
+#define	CONFIG_POSCMOD			2		/* Primary Osillator is	HS mode	*/	  
 #define	CONFIG_FSOSCEN			0		/* Secondary Osillator is Disabled */	 
 #define	CONFIG_OSCIOFNC			0		/* CLKO	output Enabled	 */	   
 #define	CONFIG_FCKSM			CSECMD	/* Clock Switching Enabled	 */	   

 main.c: InitializeFRC() の呼び出しを削除。この関数はどこにも定義されていない。

--- ../uart_bootloader/main.c	2016-05-15 18:42:13.000000000 +0900
+++ main.c	2016-05-30 23:44:55.000000000 +0900
@@ -209,7 +210,7 @@
 	SoftwareKey = (unsigned int *)0xA0000000;
 
 #ifdef	USE_INTERNAL_FRC_OSC	// RC OSC
-	InitializeFRC();
+/*	InitializeFRC(); */
 #endif
 
 #if	1

3. ホスト側ツールの Makefile, ソースを修正する

 Makefile: _LINUX_ を定義。また、rs232c-unix.c は完全に新設。なお、Mac で 64 ビットモードでコンパイルすると fprintf の表示が一部乱れるので、-arch i386 で強制的に 32 ビットモードでコンパイルしている。

--- ../uart_bootloader/hostpc/Makefile	2016-05-15 18:42:13.000000000 +0900
+++ hostpc/Makefile	2016-06-04 15:08:55.000000000 +0900
@@ -1,6 +1,6 @@
 CC    = gcc
-EXECS = uartmon32.exe
-EXECF = uartflash32.exe
+EXECS = uartmon32
+EXECF = uartflash32
 
 #
 #	uartmon32
@@ -16,7 +16,7 @@
 	mips-opc.o	\
 	mips16-opc.o	\
 	usb-uart.o \
-	rs232c.o	\
+	rs232c-unix.o	\
 
 #
 #	uartflash32
@@ -26,15 +26,18 @@
 	util.o      \
 	codebuf.o   \
 	usb-uart.o  \
-	rs232c.o	\
+	rs232c-unix.o	\
 
 #
-CFLAGS  = -DWIN -DWINVER=0x0500 -Wall
-WIN32LIB= -lkernel32 -luser32 -lgdi32 -lsetupapi 
+#CFLAGS  = -DWIN -DWINVER=0x0500 -Wall
+#WIN32LIB= -lkernel32 -luser32 -lgdi32 -lsetupapi 
 
 #forDEBUG
 #
-CFLAGS += -DDEBUG
+#CFLAGS += -DDEBUG
+
+CFLAGS = -D_LINUX_ -O2 -g
+CFLAGS += -arch i386
 
 all: $(EXECS) $(EXECF)
 
@@ -43,10 +46,10 @@
 	$(CC) $(CFLAGS) -c $*.c
  
 $(EXECS): $(OBJS)
-	$(CC) $(OBJS) -s $(LDFLAGS) $(WIN32LIB) -o $(EXECS)
+	$(CC) $(OBJS) $(CFLAGS) $(LDFLAGS) $(WIN32LIB) -o $(EXECS)
 
 $(EXECF): $(OBJF)
-	$(CC) $(OBJF) -s $(LDFLAGS) $(WIN32LIB) -o $(EXECF)
+	$(CC) $(OBJF) $(CFLAGS) $(LDFLAGS) $(WIN32LIB) -o $(EXECF)
 
 clean:
 	rm -f $(EXECS) $(EXECF) *.o core

 uartflash.c: Unix 系では、シリアルデバイス名を指定する必要がある。そこで、ファイル名の前にデバイス名を指定するように変更。(あと、デバッグ用に verbose という変数を導入して fprintf() をあちこち挿入してあるが、これは本質的ではない)

--- ../uart_bootloader/hostpc/uartflash.c	2016-05-15 18:42:13.000000000 +0900
+++ hostpc/uartflash.c	2016-06-03 00:53:09.000000000 +0900
@@ -8,6 +8,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <math.h>
+#include <unistd.h>
 
 #ifndef	_LINUX_
 #include <windows.h>
@@ -23,13 +24,20 @@
 #define DEFAULT_SERIAL	"*"
 #define	END_MASK	0xfffff
 
+extern int verbose;
 
 char HELP_USAGE[]= {
 	"* UARTflash32 Ver 0.1 (" __DATE__ ")\n"
 	"Usage is\n"
+#ifdef _LINUX_
+	"  " CMD_NAME " [Options] devicename <hexfile.hex>\n"
+#else
 	"  " CMD_NAME " [Options] <hexfile.hex>\n"
+#endif
 	"Options\n"
+#ifndef _LINUX_
 	"  -p[:XXXX]   ...  Select serial number (default=" DEFAULT_SERIAL ").\n"
+#endif
 	"  -r          ...  Run after write.\n"
 	"  -v          ...  Verify.\n"
 	"  -E          ...  Erase.\n"
@@ -432,7 +440,7 @@
 #else
 		UsbEraseTargetROM(FLASH_BASE+addr , pages);
 #endif
-		Sleep(8);
+		usleep(8000);
 		Flash_Lock();
 	}
 	return 0;
@@ -691,6 +699,7 @@
  *	メイン
  *********************************************************************
  */
+
 void getopt_p(char *s)
 {
 	if (s) {
@@ -735,19 +744,27 @@
  *	メイン
  *********************************************************************
  */
+extern int verbose;
+
 int main(int argc,char **argv)
 {
 	int errcnt, ret_val;	//,retry;
 	int dev_flash_size=0;
 	int dev_flash_used=0;
 
+#ifdef _LINUX_
+	int required_nargs = 3;
+#else
+	int required_nargs = 2;
+#endif
+	
 	//オプション解析.
 	Getopt(argc,argv,"i");
 	if(IsOpt('h') || IsOpt('?') || IsOpt('/')) {
 		usage();
 		exit(EXIT_SUCCESS);
 	}
-	if((argc<2)&& (!IsOpt('r')) && (!IsOpt('E')) && (!IsOpt('p')) ) {
+	if((argc 0)
+				fprintf(stderr, "Reading hex file %s...\n", argv[required_nargs - 1]);
+			read_hexfile(argv[required_nargs - 1]);		//	HEXファイルの読み込み.
 			modify_start_addr( opt_s & 0x7ffff );
 
 			if(IsOpt('v')==0) {			// ベリファイのときは書き込みをしない.
+				if (verbose > 0)
+					fprintf(stderr, "Erasing flash.\n");
 				erase_flash();
 				Flash_Unlock();
+				if (verbose > 0)
+					fprintf(stderr, "Start writing hex data.\n");
 				write_hexdata();			//  書き込み.
 
 				// 合否判定.

 util.c: Sleep() など、未実装の関数を実装する。また、UartInit() をデバイス名付きで呼ぶために UartInitWithName() という関数を新設。

--- ../uart_bootloader/hostpc/util.c	2016-05-15 18:42:13.000000000 +0900
+++ hostpc/util.c	2016-06-03 01:02:33.000000000 +0900
@@ -27,6 +27,7 @@
 #include <stdlib.h>
 #include <memory.h>
 #include <time.h>
+#include <unistd.h>
 
 #include "monit.h"
 #include "portlist.h"
@@ -38,6 +39,8 @@
 
 #define	VERBOSE	0
 
+int verbose = 0;
+
 typedef	struct {
 	uint	start,size;
 } Region;
@@ -61,6 +64,40 @@
 	0xa0000000,
 };
 
+#if defined(_LINUX_)
+void Sleep(int msec)
+{
+	usleep(msec * 1000);
+}
+
+void strupr(char *s)
+{
+	while (*s != 0) {
+		if (*s >= 'a' && *s <= 'z')
+			*s -= 'a' - 'A';
+	}
+}
+
+int	stricmp(char *s1,char *s2)
+{
+	return strcasecmp(s1, s2);
+}
+
+int getch(void)
+{
+	extern int	RS_keyInput(void);
+	return RS_keyInput();
+}
+
+int kbhit(void)
+{
+	extern int RS_keyavailable(void);
+	return RS_keyavailable();
+}
+
+
+#endif
+
 //static	
 int	check_region(int addr,int size)
 {
@@ -800,8 +837,8 @@
 
 #define	BUFF_SIZE		256
 #define DEBUG_PKTDUMP  	0		// パケットをダンプする.
-//#define	BAUDRATE		115200
-#define	BAUDRATE		500000
+#define	BAUDRATE		115200
+//#define	BAUDRATE		500000
 
 
 static	int dev_id        = 0;	// ターゲットID: 0x25 もしくは 0x14 だけを許容.
@@ -998,6 +1036,19 @@
 	return flash_get_status();
 }
 
+int UartInitWithName(char *devname)
+{
+	if (verbose > 0)
+		fprintf(stderr, "Initializing Uart %s.\n", devname);
+	RS_init(devname, BAUDRATE); /* No return on error */
+	if (verbose > 0)
+		fprintf(stderr, "Sending 0xaa/0x55 sequence.\n");
+	RS_putc(0xaa);
+	RS_putc(0x55);
+	return flash_get_status();
+}
+
+
 /*********************************************************************
  *	終了
  *********************************************************************
@@ -1008,7 +1059,7 @@
 		unsigned char buf[BUFF_SIZE];
 		memset(buf , 0xff, sizeof(buf) );
 		RS_putdata(	buf , PACKET_SIZE+1 );
-		Sleep(1);
+		usleep(1000);
 	}
 	RS_exit();
 	return 0;
--- ../uart_bootloader/hostpc/util.h	2016-05-15 18:42:13.000000000 +0900
+++ hostpc/util.h	2016-05-29 19:44:47.000000000 +0900
@@ -18,6 +18,7 @@
 int		hidCommand(int cmd,int arg1,int arg2,int arg3);
 int		hidasp_cmd(const unsigned char cmd[4], unsigned char res[4]);
 int	 	UartInit(int com_n);
+int		UartInitWithName(char *devname);
 int 	UartExit(void);
 
 #define	ZZ	printf("%s:%d: ZZ\n",__FILE__,__LINE__);

 gr.c: グラフィック関係のダミー関数を定義する。

--- ../uart_bootloader/hostpc/gr.c	2016-05-15 18:42:13.000000000 +0900
+++ hostpc/gr.c	2016-06-04 00:29:43.000000000 +0900
@@ -731,4 +731,31 @@
 	init_bitmap(bpp);
 }
 #else
+
+/*  Dummy functions  */
+int	gr_break(void)
+{
+	return 0;
+}
+
+void gr_close(void)
+{
+}
+
+void gr_init(int width,int height,int bpp,int color)
+{
+}
+
+void gr_pset(int x,int y,int color)
+{
+}
+
+void gr_puts(int x,int y,char *s,int color,int bkcolor,int fontsize)
+{
+}
+
+void gr_vline(int x1,int y1,int x2,int y2,int c)
+{
+}
+
 #endif

4. hex2pickit3 のビルド

 Makefile の中に hex2pickit3 の呼び出しがある。これは、hex ファイルを PICkit3 での書き込みに適するようにアドレス変換するもので、iruka さんのウェブサイトにある。ダウンロード: hex2pickit3.zip

 そのまま make すると、hex2pickit3.exe という実行ファイルができる。これを hex2pickit3 にリネームし、パスの通ったディレクトリ(例えば /usr/local/bin)に置いておく。

5. ビルド結果

 生成されたファームウェアの hex は下の通り。ブートローダ本体は 0x1FC0000C0x1FC00BBB で、3 KB のブートローダ領域に収まっている。実行開始位置の 0x1FC00000 にはブートローダ先頭へのジャンプ命令が入っている。

:020000041FC01B
:10000C0000A01D3C0020BD270060624101A01C3CEB ← プログラム本体
:10001C0000809C270260094020582001801E2A7D08
:10002C008449497D0260894000E0DC4102608B40DC
:10003C0000A0083C0400082500A0093CF00229257A
:10004C00000000AD040008252B080901FCFF20145A
:10005C0000000000C09F083CBC0B082500A0093C18
(中略)
:100B500000016330FDFF6010000000000060604194
:100B600080BF023C206044AC0800E003206060418C
:100B7000F56424670068071080811F1ED10204D528
:100B8000049501490148A2E8F7617564A0E8006591
:100B9000F46405671F1AB400246702EA066106B40C
:100BA000B1671F1AFD00D0670110006850677464B8
:080BB000A0E80065200000A090
:040BB8000000000039
:020000041FC01B
:0C000000C09F1A3C0C005A270800400367 ← lui k0,0x9FC0; addiu k0,k0,12; jr k0
:040BF000FFFFFFCF35                 ← コンフィグレーションレジスタ
:040BF400F878F9FF95
:040BF800594A60FFF7
:040BFC00EFFFFF7F89
:04000005BFC0000078                 ← スタートアドレス 0xBFC00000
:00000001FF

 ホスト側ツールは、hostpc でビルドする。書き込みツールの uartflash32 とモニタツールの uartmon32 ができる。

6. 使ってみる

 ファームウェア pickit3.hex を PICkit3 で ORANGE pico に書き込む。

2016-06-06T00:05:18+0900 - Loading hex file. Please wait...
Loading code from /Users/(中略)/uart_bootloader_TN/pickit3.hex...
2016-06-06T00:05:19+0900 - Hex file loaded successfully.

2016-06-06T00:05:30+0900 - Programming...

The following memory area(s) will be programmed:
boot config memory
configuration memory

Device Erased...

Programming...
Programming/Verify complete
2016-06-06T00:05:35+0900 - Programming complete
Pass Count: 4

 USB-シリアルを JP7 に接続し、ORANGE pico の電源を入れてから uartmon32 を立ち上げる。

MacBook:hostpc nagata$ ./uartmon32 /dev/cu.usbserial-A1032L8X 
TARGET DEV_ID=32 VER=1.1(Bootloader) FLASH=9d000000,40000
MIPS> l 1fc00000 1fc00040   # 逆アセンブル
1fc00000 3c1a9fc0 	lui	k0,0x9fc0
1fc00004 275a000c 	addiu	k0,k0,12
1fc00008 03400008 	jr	k0
1fc0000c 3c1da000 	lui	sp,0xa000
1fc00010 27bd2000 	addiu	sp,sp,8192
1fc00014 41626000 	di	v0
1fc00018 3c1ca001 	lui	gp,0xa001
1fc0001c 279c8000 	addiu	gp,gp,-32768
1fc00020 40096002 	mfc0	t1,$12,2
1fc00024 01205820 	add	t3,t1,zero
1fc00028 7d2a1e80 	ext	t2,t1,0x1a,0x4
1fc0002c 7d494984 	ins	t1,t2,0x6,0x4
1fc00030 40896002 	mtc0	t1,$12,2
1fc00034 41dce000 	wrpgpr	gp,gp
1fc00038 408b6002 	mtc0	t3,$12,2
1fc0003c 3c08a000 	lui	t0,0xa000
1fc00040 25080004 	addiu	t0,t0,4
1fc00044 3c09a000 	lui	t1,0xa000
MIPS> d 1fc00bf0 1fc00bff   # メモリダンプ
1fc00bf0 ff ff ff cf f8 78 f9 ff  59 4a 60 ff ef ff ff 7f
MIPS>