もくもく4thステップ

12ステップで作る組込みOS自作入門

12ステップで作る組込みOS自作入門

この本の読書メモ。

ROMの書き換え上限回数はメーカー保証で100回。コード書き換える毎に書きこんでたら直ぐに超えちゃう。
ROM上にはブートローダだけ書いておき、ブートローダがプログラムをRAMへダウンロードすれば毎度ROMを書き換えずに済む。
組み込み開発では、開発時はブートローダを使ってRAM上で開発し、出荷時にROMへ書きこむという手法が取られる→ROM化(そうしないと、再起動する度にプログラムをダウンロードしなきゃならない)。


シリアル経由でOSをロード出来るブートローダを作る。
プロトコルにはXMODEMを使う。

XMODEMの仕様

送信側
  1. NAK(0x15)を受け取ったら送信開始
  2. 固定長ブロックで送信 ブロックサイズ > 残りデータ量の場合はEOF(0x1a)で埋める
  3. ブロック送信毎にACK(0x06)・NAKを待つ ACKなら続き NAKなら再送
  4. 送信終了したらEOT(0x04) ACKを受け取り正常終了
  5. 中断時はCAN(0x18)を送信/受信
受信側
  1. 受信可能ならNAK
  2. SOH(0x01)から始まるブロックを受信 ACK/NAKを返す
  3. EOTを受けてACKを返す
  4. 中断時はCAN(0x18)を送信/受信
ブロック
a 1b SOH(0x01)
b 1b 1-255の連番 255の次が0
c 1b ブロック番号のビット反転
d 128b データ
e 1b checksum

ソースコード

ld.scr
MEMORY
{
	romall(rx)	: o = 0x000000, l = 0x080000 /* 512KB */
	vectors(r)	: o = 0x000000, l = 0x000100 /* top of ROM */
	rom(rx)		: o = 0x000100, l = 0x07ff00

	ramall(rwx)	: o = 0xffbf20, l = 0x004000 /* 16KB */
	buffer(rwx)	: o = 0xffdf20, l = 0x001d00 /* 8KB */
SECTIONS
{
	.vectors : {
		vector.o(.data)
	} > vectors

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

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

	.buffer : {
		_buffer_start = . ;
	} > buffer

.buffer == bufferがOSを読み込む位置。

main.c
    gets(buf);

    if (!strcmp(buf, "load")) {
      loadbuf = (char *)(&buffer_start);
      size = xmodem_recv(loadbuf);

プロンプトにloadって入力されてたら、xmodem_recv()を呼んでる。これが受信処理。

xmodem.c
long xmodem_recv(char *buf)
{
  int r, receiving = 0;
  long size = 0;
  unsigned char c, block_number = 1;

  while (1) {
    if (!receiving)
      xmodem_wait(); /* 受信開始されるまで送信要求を出す */

xmodem_wait()ではSOHが来るまでのNAK送信を行う

int serial_is_recv_enable(int index)
{
  volatile struct h8_3069f_sci *sci = regs[index].sci;
  return (sci->ssr & H8_3069F_SCI_SSR_RDRF);
}
static int xmodem_wait(void)
{
  long cnt = 0;

  while (!serial_is_recv_enable(SERIAL_DEFAULT_DEVICE)) {
    if (++cnt >= 2000000) {
      cnt = 0;
      serial_send_byte(SERIAL_DEFAULT_DEVICE, XMODEM_NAK);

SCIのssrレジスタにRDRFビット(受信完了)が立たない間はNAKを贈り続ける

    c = serial_recv_byte(SERIAL_DEFAULT_DEVICE);

一バイト受信して比較

    if (c == XMODEM_EOT) { /* 受信終了 */
      serial_send_byte(SERIAL_DEFAULT_DEVICE, XMODEM_ACK);
      break;

EOTならACKを送って正常終了

    } else if (c == XMODEM_CAN) { /* 受信中断 */
      return -1;

CANなら中断なので異常終了

    } else if (c == XMODEM_SOH) { /* 受信開始 */
      receiving++;
      r = xmodem_read_block(block_number, buf); /* ブロック単位での受信 */

SOHなら後に続くブロックを受信

static int xmodem_read_block(unsigned char block_number, char *buf)
{
  unsigned char c, block_num, check_sum;
  int i;

  block_num = serial_recv_byte(SERIAL_DEFAULT_DEVICE);
  if (block_num != block_number)
    return -1;

bフィールド(ブロック番号)の受信

  block_num ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE);
  if (block_num != 0xff)
    return -1;

cフィールド(ブロック番号の反転)の受信とブロック番号のチェック

  check_sum = 0;
  for (i = 0; i < XMODEM_BLOCK_SIZE; i++) {
    c = serial_recv_byte(SERIAL_DEFAULT_DEVICE);
    *(buf++) = c;
    check_sum += c;
  }

dフィールド(データ)128バイト受信、checksumを計算

  check_sum ^= serial_recv_byte(SERIAL_DEFAULT_DEVICE);
  if (check_sum)
    return -1;

eフィールド(checksum)の受信と比較