第2世代 Xiaoの検討 最低限のソースコード

作成日:2020/07/12
最終更新日:2020/07/13

第2世代 Xiaoの検討 最低限のソースコード

作成日:2020/07/12
最終更新日:2020/07/13

概要

第2世代 Xiaoは第1世代のXiaoとの互換性は考えない予定です。 しかしながら、再利用できる部分は使いたいので一旦main関数を呼び出すところまで簡略化します。

開発環境

開発環境はGNUツールチェーンを使用します。GNU makeを使ってビルドを自動化します。

ソース1. Makefile
PREFIX  = /usr/local
ARCH    = arm-none-eabi
BINDIR  = $(PREFIX)/$(ARCH)/bin
ADDNAME = $(ARCH)-

AR      = $(BINDIR)/$(ADDNAME)ar
AS      = $(BINDIR)/$(ADDNAME)as
CC      = $(BINDIR)/$(ADDNAME)gcc
LD      = $(BINDIR)/$(ADDNAME)ld
NM      = $(BINDIR)/$(ADDNAME)nm
OBJCOPY = $(BINDIR)/$(ADDNAME)objcopy
OBJDUMP = $(BINDIR)/$(ADDNAME)objdump
RANLIB  = $(BINDIR)/$(ADDNAME)ranlib
STRIP   = $(BINDIR)/$(ADDNAME)strip

OBJS += reset.o main.o
OBJS += vector.o

TARGET = xiao

CFLAGS = -Wall -mcpu=cortex-m0 -mthumb -nostdinc -nostdlib -fno-builtin
CFLAGS += -I.
#CFLAGS += -g
CFLAGS += -Os
CFLAGS += -DXIAO
CFLAGS += -DCPU_CLOCK=12000000L

LFLAGS = -static -T ld.scr -L.

.SUFFIXES: .c .o
.SUFFIXES: .s .o
.SUFFIXES: .S .o

all :		$(TARGET)

$(TARGET) :	$(OBJS)
		$(CC) $(OBJS) -o $(TARGET) $(CFLAGS) $(LFLAGS)
		cp $(TARGET) $(TARGET).elf
		$(STRIP) $(TARGET)

.c.o :		$<
		$(CC) -c $(CFLAGS) $<

.s.o :		$<
		$(CC) -c $(CFLAGS) $<

.S.o :		$<
		$(CC) -c $(CFLAGS) $<

$(TARGET).mot :	$(TARGET)
		$(OBJCOPY) -O srec $(TARGET) $(TARGET).mot

$(TARGET).hex :	$(TARGET).mot
		$(OBJCOPY) -I srec -O ihex $(TARGET).mot $(TARGET).hex

image :		$(TARGET).hex
		lpc21isp xiao.hex /dev/ttyUSB0 115200 12000
clean :
		rm -f $(OBJS) $(TARGET) $(TARGET).elf $(TARGET).mot $(TARGET).hex
		rm -f *~

ソースコード

リンカスクリプトはXiaoのものをとりあえずそのまま利用します。リンカスクリプトのファイル名は12ステップ本を踏襲します。

ソース2. ld.scr
OUTPUT_FORMAT("elf32-littlearm")
OUTPUT_ARCH(arm)

MEMORY
{
	romall(rx)	: o = 0x00000000, l = 0x00008000 /* 16KB */
	romvector(r)	: o = 0x00000000, l = 0x00000200 /* top of ROM */
	rom(rx)		: o = 0x00000200, l = 0x00007e00

	ramall(rwx)	: o = 0x10000000, l = 0x00001000 /* 4KB */
	data(rw)	: o = 0x10000000, l = 0x00000800 /* 2KB */
	userstack(rw)	: o = 0x10000800, l = 0x00000400 /* 1KB */	
	stack(rw)	: o = 0x10000C00, l = 0x00000400 /* end of RAM */
}

SECTIONS
{
	.romvector : {
		_romvector_start = . ;
		*(.vector)
		. = ALIGN(4);
		_romvector_end = . ;
	} > romvector

	.text : {
		_text_start = . ;
		*(.text)
		_etext = . ;
	} > rom

	.rodata : {
		_rodata_start = . ;
		*(.strings)
		*(.rodata)
		*(.rodata.*)
		_erodata = . ;
	} > rom

	_data_org = . ;
	.data : {
		_data_start = . ;
		*(.data)
		. = ALIGN(4);
		_data_end = . ;
		_edata = . ;
	} > data AT> rom

	.bss : {
		_bss_start = . ;
		*(.bss)
		*(COMMON)
		. = ALIGN(4);
		_bss_end = . ;
		_ebss = . ;
	} > data AT> rom

	. = ALIGN(4);
	_end = . ;

	.freearea : {
		_freearea = . ;
	} > data

	.userstack : {
		_userstack = . ;
	} > userstack

	.stack : {
		_process_stack = . + 0x0100;
		_main_stack    = . + 0x0200;
        } > stack
}

必要な定義は一旦defines.hにまとめます。追々ファイルを整理していきます。

ソース3. defines.h
/**
  defines.h
  
  Copyright (c) 2013-2020 Akihisa ONODA
  
  This software is released under the MIT License.
  http://opensource.org/licenses/mit-license.php
*/

#ifndef __DEFINES_H__
#define __DEFINES_H__

#define NULL ((void *)0)

#define SYSMEMREMAP		((volatile unsigned long *)0x40048000)
#define SYSAHBCLKCTRL		((volatile unsigned long *)0x40048080)

#define AHB_IOCON		(1 << 16)

#endif /* __DEFINES_H__ */

電源投入された時、一番初めにvector.cに定義したvectorの0番目にスタックポインタのアドレスを定義し、1番目のアドレスに飛びます。ここでアドレスに+1しているのはARM Cortex-M0の仕様です。ブートストラップ処理を書いていきます。LPC1114FN28/102はRAMが4 kbyteと少ないので第1世代のXiao同様にブートローダは実装せずに直接ブートする仕様にします。

ソース4. vector.c
/**
  vecter.c
  
  Copyright (c) 2013-2020 Akihisa ONODA
  
  This software is released under the MIT License.
  http://opensource.org/licenses/mit-license.php
*/

extern unsigned long _main_stack[];

void reset_entry(void);

const void *vector[] __attribute__ ((section(".romvector"))) =
{
	_main_stack,		// MSP
	reset_entry + 1,	//  1: Reset
	reset_entry + 1,	//  2: NMI
	reset_entry + 1,	//  3: HardFault
	reset_entry + 1,	//  4: reserved
	reset_entry + 1,	//  5: reserved
	reset_entry + 1,	//  6: reserved
	reset_entry + 1,	//  7: reserved
	reset_entry + 1,	//  8: reserved
	reset_entry + 1,	//  9: reserved
	reset_entry + 1,	// 10: reserved
	reset_entry + 1,	// 11: SVC
	reset_entry + 1,	// 12: reserved
	reset_entry + 1,	// 13: reserved
	reset_entry + 1,	// 14: PendSV
	reset_entry + 1,	// 15: SysTick
	reset_entry + 1,	// 16: External Interrupt PIO0_0
	reset_entry + 1,	// 17: External Interrupt PIO0_1
	reset_entry + 1,	// 18: External Interrupt PIO0_2
	reset_entry + 1,	// 19: External Interrupt PIO0_3
	reset_entry + 1,	// 20: External Interrupt PIO0_4
	reset_entry + 1,	// 21: External Interrupt PIO0_5
	reset_entry + 1,	// 22: External Interrupt PIO0_6
	reset_entry + 1,	// 23: External Interrupt PIO0_7
	reset_entry + 1,	// 24: External Interrupt PIO0_8
	reset_entry + 1,	// 25: External Interrupt PIO0_9
	reset_entry + 1,	// 26: External Interrupt PIO0_10
	reset_entry + 1,	// 27: External Interrupt PIO0_11
	reset_entry + 1,	// 28: External Interrupt PIO1_10
	reset_entry + 1,	// 29: External Interrupt C_CAN
	reset_entry + 1,	// 30: External Interrupt SPC/SSP1
	reset_entry + 1,	// 31: External Interrupt I2C0
	reset_entry + 1,	// 32: External Interrupt
	reset_entry + 1,	// 33: External Interrupt
	reset_entry + 1,	// 34: External Interrupt
	reset_entry + 1,	// 35: External Interrupt
	reset_entry + 1,	// 36: External Interrupt
	reset_entry + 1,	// 37: External Interrupt
	reset_entry + 1,	// 38: External Interrupt
	reset_entry + 1,	// 39: External Interrupt
	reset_entry + 1,	// 40: External Interrupt
	reset_entry + 1,	// 41: External Interrupt
	reset_entry + 1,	// 42: External Interrupt
	reset_entry + 1,	// 43: External Interrupt
	reset_entry + 1,	// 44: External Interrupt
	reset_entry + 1,	// 45: External Interrupt
	reset_entry + 1,	// 46: External Interrupt
	reset_entry + 1,	// 47: External Interrupt
	reset_entry + 1,	// 48: External Interrupt
	reset_entry + 1,	// 49: External Interrupt
	reset_entry + 1,	// 50: External Interrupt
	reset_entry + 1,	// 51: External Interrupt
	reset_entry + 1,	// 52: External Interrupt
	reset_entry + 1,	// 53: External Interrupt
	reset_entry + 1,	// 54: External Interrupt
	reset_entry + 1,	// 55: External Interrupt
	reset_entry + 1,	// 56: External Interrupt
	reset_entry + 1,	// 57: External Interrupt
	reset_entry + 1,	// 58: External Interrupt
	reset_entry + 1,	// 59: External Interrupt
	reset_entry + 1,	// 60: External Interrupt
	reset_entry + 1,	// 61: External Interrupt
	reset_entry + 1,	// 62: External Interrupt
	reset_entry + 1,	// 63: External Interrupt
};

今回は、reset_entryに飛ぶようにしているので、reset.cに定義されたreset_entry()が呼び出されます。アセンブラはインラインアセンブラでできるだけ記述するようにし、*.cファイルで済むようにします。

ソース5. reset.c
/**
  reset.c
  
  Copyright (c) 2013-2020 Akihisa ONODA
  
  This software is released under the MIT License.
  http://opensource.org/licenses/mit-license.php
*/

#include "defines.h"

__attribute__ ((section(".text.startup")))
void
reset_entry(void)
{
	extern volatile char _data_org[], _data_start[], _data_end[];
	extern volatile char _bss_start[], _bss_end[];
	volatile char *s, *d;

	asm("ldr	r0, =_process_stack\n"
	    "msr	psp, r0\n");

	asm("mov	r0, #2\n"
	    "msr	control, r0\n");

	asm("memmap_init:\n");
	*SYSMEMREMAP = 2;

	asm("clock_init:\n");
	*SYSAHBCLKCTRL |= AHB_IOCON;

	for(d = _data_start, s = _data_org; d < _data_end; *d++ = *s++);

	for(d = _bss_start; d < _bss_end; *d++ = 0);

	asm("b main\n");
}

reset_entry()の最後でmain.cに定義されたmain()を呼び出してブートストラップが完了します。

ソース6. main.c
/**
  main.c
  
  Copyright (c) 2013-2020 Akihisa ONODA
  
  This software is released under the MIT License.
  http://opensource.org/licenses/mit-license.php
*/

#include "defines.h"

int
main(void)
{
	return 0;
}