2009年6月アーカイブ




LPC2388のSD/MMCカードインターフェース続き。

なんとかセクタのR/Wが確認できるとこまで進みました...。CMD0の後、MMCカー ドインターフェースはCMD1、SDカードインターフェースはCMD41なのだけど、 CMD1がどうしてもうまくいかず。CMD41にしました(テストカードはSD/MMC)。 CMD41のレスポンスはCRC Failed でいい。これはデータシートにちょこっと書 いてある。
なんとかここまで来れば気を落ちつけて先に進める...。まだまだ厳しそうだけど。
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
dirent: 3/32
super block 512byte, inode 64byte, dirent 16byte
magic=1badface
data_start_byte=0x400
data_end_byte=0x1ffffff
from=ffffffff
to=ffffffff
from_backup=ffffffff
to_backup=ffffffff
fsname=
volume=
[inode index list](max 8)
2  48 2 2 (0) 2 493 0 0 2 4a48c01a 4a48bea9 4a48bea9
3  157 3 3 (1) 1 420 0 0 1 4a48c01a 4a48c01a 4a48c01a
4  157 4 4 (2) 1 420 0 0 1 0 0 0
5  157 5 5 (3) 1 420 0 0 1 0 0 0
6  157 6 6 (4) 1 420 0 0 1 0 0 0
7  157 7 7 (5) 1 420 0 0 1 0 0 0
total 6 i-node.
[dirent index list]
32 file entries.
.: sector:58448 + 2 -> 2 48byte. inode=2
..: sector:58448 + 2 -> 2 48byte. inode=2
a.txt: sector:58448 + 3 -> 3 157byte. inode=3
3 files.
a.txt: sector:58448 + 3 -> 3 157byte. inode=3
0x82000 CMD17 status = Undefined Error
0x82800 status = Undefined Error
0x82040 status changed 82040 Undefined Error
ohayo
4e 65 74 42 53 44 20 73 69 63 6b 6c 65 6d 6f 6f 
6e 20 35 2e 39 39 2e 31 34 20 4e 65 74 42 53 44 
20 35 2e 39 39 2e 31 34 20 28 53 49 43 4b 4c 45 
4d 4f 4f 4e 32 29 20 23 36 3a 20 54 75 65 20 4a 
75 6e 20 32 33 20 31 38 3a 31 35 3a 35 34 20 4a 
53 54 20 32 30 30 39 20 20 75 63 68 40 73 69 63 



LPC2388のSD/MMCカードインターフェース(MCI)に手をつけました。これは昨日、
H8用に書いたところくらいまでをコントローラが受けもってくれる。コントロー
ラとカード間のインターフェースはSPIモードではなくMMCモード。

LPC2388とSD/MMCカードまでは3.3Vなので直結。DAT0とCMD線だけ50kΩでプルアッ プしました。
これが超絶にハマり中。コントローラの中でどこまで良きに計らっているのか がわからない。CMD0は返ってくるのだけど、本当かなのかわからないし、(MMC モードだとCMD線だけでカードからのコマンド、それに対するレスポンスを双方 向に渡すので、SPIモードの時の様にDoutをオシロで見ればいいというわけにも いかない。)



ショックなことが。UD01、H8/3052ベースの開発機の拡張メモリがいかれてしまっ
た。うまく起動しないな...と何度かリセットを押すとついにだんまりに。拡張
メモリ確認プログラムを走らせてみたところ、完全にだめになってました。

PCのメモリなら買ってきて交換で済むけれど...これはパーツ買ってきて作るの 面倒なんだよ。基板の問題かもしれない。(一応チェックしたけれど)
一気に開発効率が落ちてしまいました。とはいえ、ファイルシステムサポート の基本部分は終わって、エラー時のタイムアウト処理を実装しようとしていた ところだったので、まだよかった。ここから先はマシン非依存だから。ARMでい い。というか最終的なターゲットはARMなのだけど。
上物のファイルシステムSysVBFSはx86ポートでそこそこ、このOSに馴染ませて おいたので、bi-endianサポートと、intの排除。H8ポートではintを16bitとし ているので、intが32bitを必要な部分は明示的にint32_tにしないといけない。
ファイルの読み書きできるようになりました。これでデータロガーの基本部分 までは見積りがついた。

MMCカード。

コマンドは6byte。最初がコマンド指定で、引数4byte(MSB 1st)、CRC7|ストッ
プビット。コマンド指定が0x40|コマンドで0x80が落ちているのがスタートビッ
トになる。CRC7|0x01にするのが、ストップビットになる。

レスポンスはコマンドレスポンスに3種類、R1,R2(ステータスレジスタの
み),R3(OCRのみ)。
書き込みあとのレスポンスが2種類、データレスポンスとエラーレスポンス。
レスポンスの区別は上位3bitで区別できる。

コマンドとコマンドの間にはダミークロックが必要(8クロック)。CS#の状態は
問わない。

void
md_mmc_activate ()
{

  md_mmc_putc (0xff);	//コマンドの前にダミークロックを入れることにした。
  *PB_DR &= ~1;	// CS# Assert;
  udelay (1);
}

void
md_mmc_deactivate ()
{

  *PB_DR |= 1;	// CS# Deassert.
}

データパケットを共なうコマンド。
bool
__mmc_data_command (int command, uint32_t arg, uint8_t *buf, size_t n)
{
  size_t i;

  md_mmc_activate ();
  if (!__mmc_command (mmc_set_command (command, arg)))
    return FALSE;
// ここでデータトークンスタートバイト(0xfe)が送られてくるのを待ちます。
  if (!__mmc_wait_data ())
    return FALSE;

  for (i = 0; i < n; i++)
    {
      buf[i] = md_mmc_getc ();
    }
// 最後にCRC16が送られてきます。
  __mmc_data_crc ();
  md_mmc_deactivate ();

  return TRUE;
}

データパケットを送るコマンド
bool
__mmc_data_write_command (int command, uint32_t arg, uint8_t *buf, size_t n)
{
  size_t i;
  uint8_t r;
  bool done = FALSE;

  md_mmc_activate ();
  if (!__mmc_command (mmc_set_command (command, arg)))
    return FALSE;
//ダミークロック
  md_mmc_putc (0xff);	// At least 1byte wait.

//まずデータトークンスタートバイトを送ります。
  // Data packet.
  md_mmc_putc (0xfe);	//Data token start byte.
  for (i = 0; i < n; i++)
    {
      md_mmc_putc (buf[i]);
    }
//SPIモードなのでCRCなし。
  md_mmc_putc (0x00);	// Dummy CRC
  md_mmc_putc (0x00);	// Dummy CRC
  DPRINTF ("send done\n");
//ここでレスポンスを待ちます。
  r = __mmc_response ();
//レスポンスは二種類あります。
  if ((r & 0xe0) == 0)
    __mmc_error_response (r);
  else
    done = __mmc_data_response (r);

  //  iprintf ("%x\n", (uint32_t)r);
//まだこの段階ではMMCカードは作業中。Doutが0xffになるまで待ちます。
  __mmc_wait_busy ();

  md_mmc_deactivate ();

  return done;
}

UD01 H8/3052にて。test.txtにあらかじめNetBSD側でdisklabelの内容を入れておき
それを表示、同じものをa.txtとして新しくファイルを作ります。

$ tip hmon
connected
stack_start: 0xfff10
ROM data: 0x7e60-0x82bc
RAM data: 0xfdf10-0xfe36c 1116byte
bss: 0xfe36c-0xffa9c 5936byte
NMI return address|CCR: 8000016e
delay_div=40
calibrstack_start: 0xfff10
ROM data: 0x7e60-0x82bc
RAM data: 0xfdf10-0xfe36c 1116byte
bss: 0xfe36c-0xffa9c 5936byte
NMI return address|CCR: 8000016e
delay_div=40
calibrated. delay_div=40
[0] DOS 3.0+ 16-bit FAT <32M [63 33264]
[1] NetBSD [33327 90657]
[2] unused [0 0]
[3] unused [0 0]
SysVBFS sector = 6 size = 65536
bfs super block + inode area = 1024byte
inode: 3/8
root inode: 2-2
bfs dirent area = 512byte
dirent: 4/32
super block 512byte, inode 64byte, dirent 16byte
magic=1badface
data_start_byte=0x400
data_end_byte=0x1ffffff
from=ffffffff
to=ffffffff
from_backup=ffffffff
to_backup=ffffffff
fsname=
volume=
[inode index list](max 8)
2  64 2 2 (0) 2 493 0 0 2 4a45e8a1 4a45aea4 4a45aea4
3  868 3 4 (1) 1 420 0 0 1 4a45da4b 4a45b0c0 4a45b0b8
4  868 5 6 (2) 1 420 0 0 1 4a45dd2d 0 0
total 3 i-node.
[dirent index list]
32 file entries.
.: sector:58448 + 2 -> 2 64byte. inode=2
..: sector:58448 + 2 -> 2 64byte. inode=2
test.txt: sector:58448 + 3 -> 4 868byte. inode=3
a.txt: sector:58448 + 5 -> 6 868byte. inode=4
4 files.
test.txt: sector:58448 + 3 -> 4 868byte. inode=3
# /dev/rld0d:
type: ld
disk: unknown
label: fictitious
flags:
bytes/sector: 512
sectors/track: 63
tracks/cylinder: 16
sectors/cylinder: 1008
cylinders: 123
total sectors: 124160
rpm: 7200
interleave: 1
trackskew: 0
cylinderskew: 0
headswitch: 0           # microseconds
track-to-track seek: 0  # microseconds
drivedata: 0 

6 partitions:
#        size    offset     fstype [fsize bsize cpg/sgs]
 a:      8737     33327     4.2BSD   1024  8192    16  # (Cyl.     33*-     41*)
 b:     16384     42064       swap                     # (Cyl.     41*-     57*)
 c:     90657     33327     unused      0     0        # (Cyl.     33*-    122)
 d:    123984         0     unused      0     0        # (Cyl.      0 -    122)
 e:     33264        63      MSDOS                     # (Cyl.      0*-     33*)
 f:     65536     58448    SysVBFS                     # (Cyl.     57*-    122)
bfs_file_delete: "a.txt" deleted.
bfs_file_create: start 5 end 6




SD/MMCカード + H8/3052、今日も激ハマり。これはH8のSCIを使ってSD/MMCが要
求するクロックを発生させるのが難しいからなのだ。その原因はSCIが受信でク
ロックを発生しないから。

MMCカードが、ここから送信しますよ。というところで、ダミーの送信をかけて SCIにクロックを発生させないといけない。SD/MMCカードはこのクロックでデータを 送ってくる。
昨日のsci0_getcだと、セクタをリードしようとすると8byteごとに0xf,0x4とゴ ミが定期的に入る。オシロで見てもSD/MMCカードから、定期的にゴミが来る。 この原因はきちっとクロックが入ってなかったからのようだ。受信バイト毎に ダミーライトでクロックを供給することでなんとかなった。
int8_t
sci0_getc3 ()
{
  uint8_t c;
  uint8_t r;
//受信用のクロックを発生させるために送信もイネーブル。
  *SCI0_SCR |= (SCR_TE | SCR_RE);

  do
    {
      r = *SCI0_SSR;
//これから受信するのに、受信バッファに何か入っていたらそれはゴミなので
//捨てます。
      if (r & SSR_RDRF)
	{
	  iprintf ("drain receive buffer.\n");
	  *SCI0_SSR &= ~SSR_RDRF;
	}
    }
  while ((r & SSR_TDRE) == 0);

//ここでダミーライトしてクロックを発生させます。MMCカードはこのクロック
//に乗せてデータを送ってくる。(ことを期待している)
  *SCI0_TDR = 0xff;
  *SCI0_SSR &= ~SSR_TDRE;

//送られてくるのを待ちます。以下は普通の受信。
  while (((c = *SCI0_SSR) & (SSR_RDRF | SSR_ERR_BITS)) == 0)
    ;
  if (c & SSR_ERR_BITS)
    {
      *SCI0_SSR &= ~(SSR_RDRF | SSR_ERR_BITS);
      iprintf ("error\n");
      return 0;
    }
  c = *SCI0_RDR;
  *SCI0_SSR &= ~SSR_RDRF;
  *SCI0_SCR &= ~(SCR_TE | SCR_RE);

// SD/MMCはMSB側からの送信、H8のSCIはLSB側からの受信なのでここで逆転。
  return bit_inverse (c);
}
これじゃほとんど調歩同期と変わらないけれど...
あと、昨日はSD/MMCのDoutをH8のRXD0に直結していた。ちょっとGNDが浮くのが 気になってはいたのだけど、これを74VHC125経由にすると完全に浮いてしまっ てデータがとれない。これはAKI-H8/3052ボードのRXD0がADM3202につながって いるから。このピンを上げてやればきっちりGNDに落ちる。

なんとか最初のセクタがきちんと読めるのを確認しました。
stack_start: 0xbfffc
ROM data: 0x58d0-0x5ed0
RAM data: 0xbf000-0xbf600 1536byte
bss: 0xbf600-0xbfdd0 2000byte
NMI return address|CCR: 8000016e
delay_div=40
calibrated. delay_div=40
delay_div=40
calibrated. delay_div=40
H8/3052 ud01 Monitor Build Jun 24 2009 20:13:11
[ VIRTUAL VECTOR TABLE *OFF* ]
 OFF: overlay module, ON: standalone module.
>> l
~>Local file name? a.mot
308 lines transferred in 0 seconds 
!
Read 6823 byte. success
Start address: 0x80000
data: 0xfe100-0xfe16c 108byte
bss: 0xfe16c-0xfe36c 512byte
MCU mode: 1
Overlay mode.
>> 
>> mmc_reset
response = 1
>> mmc_init
response = 1
response = 1
response = 1
response = 1
response = 1
response = 0
>> cid
response = 0
datastart
1()50(P)41(A)53(S)30(0)36(6)34(4)42(B)41(A)45(E)59(Y)9a()97()0()45(E)d3(S)>> ocr
response = 0
ocr = 80
ocr = ff
ocr = 80
ocr = 0
>> csd
response = 0
datastart
0 5d 1 32 13 59 83 c9 f6 d9 cf ff 16 40 0 e7 >> read
response = 0
r=ff
datastart
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 
8 0 6 7 60 e4 27 0 0 0 d9 e4 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 aa 
crc0=ffffffc1, crc1=fffffffa
>> 



SD/MMCカード、激はまった。

H8/3664のI2CにSD/MMCのDINとDOUTをORしたのは早々にやめました。これは道を 余りも間違え過ぎてる気がして。H8は5V、SD/MMCは3.3V。I2Cの出力はオープン ドレインだからプルアップだけで直接継げるのに色気ついたのだけれど実際、 いじりはじめたら、I2CもMMCもかなり手強いことに気付いた。
そして次は開発機UD01 H8/3052。これはSCI0が空いている。今迄の配線をはずして 外に取り出しました。
これはきちっと5V-3.3Vの変換をしてやらないといけない。これは74VHC125を使 いました。これは5Vトレランスで、Vccが3.3Vだとしても、入力に5.5Vまでかけ ていい。これを使ってCS#,DIN,CLKをH8(5V)→SD/MMC(3.3V)に変換します。 SD/MMC(3.3V)→H8(5V)のDOUTは、H8のP9(ここで使うシリアルポート)の入力H電 圧の最低が2Vなので、そのまま直結でOK。
VHC系列はDIPがないので一番大きいSOPをDIP変換基板で。
これでもうまくいかなかった。実はこの前に分圧回路で5V→3.3Vにしていて、 それがだめなのかな...と思ってきっちりやってみたのだけど...。

久々にオシロの登場。やっぱこんなチープオシロでもいざって時には、あると ないとじゃ大違い。これはTXD。TXDは正論理、RXDは負論理。


クロックも負論理。R/Wがない限りHのまま。クロックはR/Wの時以外発生しない。 ここまででクロックもデータもSD/MMCまで行っているのは確認できたのだけど、 レスポンスが来る時点でRXDがピクリともしない。で悩んでいた。

うまくいかなかったのはなにかというと、SD/MMCカードは「電源立ちあげ後、 80 クロックのダミークロックの後、CS#をアサートしてコマンドを発行する」。 という手順なので、80クロック分delayで待っていた。しかしR/Wしないとクロッ クを発生しないので、ここでH8にクロックを発生させるためのダミーライトを 入れないといけなかったのだ。ダミーライトするデータも0xffじゃないとだめ。
ということでなんとかCMD0,CMD1まで進みました。

//CS#にはPB0を使った。これは74VHC125経由で3.3VでSD/MMCカードに。
  *PB_DDR = 0xff;	//Output.
  *PB_DR = 0x3;		// CS# deassert.

//通信に使うSCI0の設定。
  *P9_DDR = 0xf3;	// RXD0, RXD1 input, else output.
  *P9_DR = 0;

  *SCI0_SCR = 0;	// Internal clock.
  // Don't reorder SCR->SMR sequence.
// SMRの設定はSCRの設定の後でないといけいない。このボーレートの設定は
// 500bit/s (オシロスコープで見やすいように遅くした)
  *SCI0_SMR = SMR_CM | SMR_CKS0 | SMR_CKS1;	// 1/4 clock
  *SCI0_BRR = 155;


// ここの作業が「80クロックのダミークロック」にあたる。
// !!!これが重要!!!
  int i;
  *PB_DR |= 1;	// CS# Deassert.
  for (i = 0; i < 80; i++)
    sci0_putc (0xff);	// must be 0xff

//ここでカードを有効に。
  *PB_DR &= ~1;	// CS# Assert;
#if 0
// H8の同期シリアルはLSB側から乗せるのに対し、SD/MMCはMSB側から乗せる。
// なので、1byteごとにビットをひっくり返す必要がある。
  sci0_putc (0x40);
  sci0_putc (0x00);
  sci0_putc (0x00);
  sci0_putc (0x00);
  sci0_putc (0x00);
  sci0_putc (0x96);
#else
//ひっくり返してこう。
  sci0_putc (0x02);
  sci0_putc (0x00);
  sci0_putc (0x00);
  sci0_putc (0x00);
  sci0_putc (0x00);
  sci0_putc (0xa9);
#endif
  iprintf ("transimit done.\n");

// レスポンスが返ってくるのにはしばらくある。RXDは負論理なのでデータが
// なければ0xff。
  uint8_t r;
  while ((r = sci0_getc ()) == 0xff)
    ;
// ここで0x80が返ってくれば成功。(ビット反転で本当は0x01)
  iprintf ("response = %x\n", (uint32_t)r);
  *PB_DR |= 1;	// CS# Deassert.


//送信/受信ルーチン。まめにTE,REをON/OFFして、putcではTENDで確実に送信を
//確認しているけれど、ちょっと疑問がある。とりあえず動いたバージョン。

int8_t
sci0_getc ()
{
  uint8_t c;

  *SCI0_SCR |= SCR_RE;
  while (((c = *SCI0_SSR) & (SSR_RDRF | SSR_ERR_BITS)) == 0)
    ;
  if (c & SSR_ERR_BITS)
    {
      *SCI0_SSR &= ~(SSR_RDRF | SSR_ERR_BITS);
      return 0;
    }
  c = *SCI0_RDR;
  *SCI0_SSR &= ~SSR_RDRF;
  *SCI0_SCR &= ~SCR_RE;

  return c;
}

void
sci0_putc (int8_t c)
{

  *SCI0_SCR |= SCR_TE;
  while ((*SCI0_SSR & SSR_TDRE) == 0)
    ;
  *SCI0_TDR = c;
  *SCI0_SSR &= ~SSR_TDRE;
  while ((*SCI0_SSR & SSR_TEND) == 0)
    ;
  // transmit done.
  *SCI0_SCR &= ~(SCR_TE);
}



昨日、ついに起動不能となってしまったガレージのWindows2000マシン。どうも
ブートに必要なファイルの部分が物理的に壊れてしまったようだ...。仕方ない
ので、Vistaマシンでデータだけ吸いとりました。このVistaマシン。いつもは
NFSブートでNetBSDが立ちあがっている。それもカメラからのフラッシュを読み
とるだけのために。1年以上前からNetBSDのUSB mass storageはどうもおかしい。
動くけれどたまにIDEの方でパニックするとか、動かないとか。NFSルートで使
う分にはまったく問題ないので、たぶんディスクまわりでなにかおかしいんだ
ろうな...とは思いつつ。

そのVistaマシンをガレージにもっていくこととして、カメラのデータ吸いとり 用マシンをどうしようか... ということで、買って以来、環境整備だけしてまっ たく使ったことのないネットブック、工人舎のSA5ST08Aを使うことにしました。 最近はNetBSDでもsdmmc (SD/MMCコントローラ直結)が使えるようになってみた いだしね。
このマシンのコントローラもTIなので
Index: sdhc_pci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/sdhc_pci.c,v
retrieving revision 1.1
diff -u -r1.1 sdhc_pci.c
--- sdhc_pci.c	21 Apr 2009 03:00:29 -0000	1.1
+++ sdhc_pci.c	23 Jun 2009 13:00:39 -0000
@@ -83,6 +83,9 @@
 #define	SDHC_PCI_QUIRK_FORCE_DMA	(1U << 0)
 #define	SDHC_PCI_QUIRK_TI_HACK		(1U << 1)
 #define	SDHC_PCI_QUIRK_NO_PWR0		(1U << 2)
+  pci_product_id_t flash_controller_product;
+  u_int flash_controller_function;
+
 } sdhc_pci_quirk_table[] = {
 	{
 		PCI_VENDOR_TI,
@@ -90,7 +93,9 @@
 		0xffff,
 		0xffff,
 		4,
-		SDHC_PCI_QUIRK_TI_HACK
+		SDHC_PCI_QUIRK_TI_HACK,
+		PCI_PRODUCT_TI_PCI72111FM,
+		3
 	},
 
 	{
@@ -99,13 +104,27 @@
 		0xffff,
 		0xffff,
 		0,
-		SDHC_PCI_QUIRK_NO_PWR0
+		SDHC_PCI_QUIRK_NO_PWR0,
+		0,
+		0
+	},
+
+	{
+		PCI_VENDOR_TI,
+		PCI_PRODUCT_TI_PCIXX12SD,
+		0xffff,
+		0xffff,
+		3,
+		SDHC_PCI_QUIRK_TI_HACK,
+		PCI_PRODUCT_TI_PCIXX12FM,
+		2
 	},
 };
 
-static void sdhc_pci_quirk_ti_hack(struct pci_attach_args *);
+static void sdhc_pci_quirk_ti_hack(struct pci_attach_args *,
+    const struct sdhc_pci_quirk *);
 
-static uint32_t
+static const struct sdhc_pci_quirk *
 sdhc_pci_lookup_quirk_flags(struct pci_attach_args *pa)
 {
 	const struct sdhc_pci_quirk *q;
@@ -125,7 +144,7 @@
 
 			if ((q->subvendor == 0xffff)
 			 && (q->subproduct == 0xffff))
-				return q->flags;
+				return q;
 
 			id = pci_conf_read(pa->pa_pc, pa->pa_tag,
 			    PCI_SUBSYS_ID_REG);
@@ -136,13 +155,13 @@
 			 && (q->subproduct != 0xffff)) {
 				if ((vendor == q->subvendor)
 				 && (product == q->subproduct))
-					return q->flags;
+					return q;
 			} else if (q->subvendor != 0xffff) {
 				if (product == q->subproduct)
-					return q->flags;
+					return q;
 			} else {
 				if (vendor == q->subvendor)
-					return q->flags;
+					return q;
 			}
 		}
 	}
@@ -181,6 +200,7 @@
 	bus_space_handle_t ioh;
 	bus_size_t size;
 	uint32_t flags;
+	const struct sdhc_pci_quirk *q;
 
 	sc->sc.sc_dev = self;
 	sc->sc.sc_dmat = pa->pa_dmat;
@@ -192,14 +212,15 @@
 	aprint_naive("\n");
 
 	/* Some controllers needs special treatment. */
-	flags = sdhc_pci_lookup_quirk_flags(pa);
-	if (ISSET(flags, SDHC_PCI_QUIRK_TI_HACK))
-		sdhc_pci_quirk_ti_hack(pa);
-	if (ISSET(flags, SDHC_PCI_QUIRK_FORCE_DMA))
-		SET(sc->sc.sc_flags, SDHC_FLAG_FORCE_DMA);
-	if (ISSET(flags, SDHC_PCI_QUIRK_NO_PWR0))
-		SET(sc->sc.sc_flags, SDHC_FLAG_NO_PWR0);
-
+	if ((q = sdhc_pci_lookup_quirk_flags(pa)) != NULL) {
+		flags = q->flags;
+		if (ISSET(flags, SDHC_PCI_QUIRK_TI_HACK))
+			sdhc_pci_quirk_ti_hack(pa, q);
+		if (ISSET(flags, SDHC_PCI_QUIRK_FORCE_DMA))
+			SET(sc->sc.sc_flags, SDHC_FLAG_FORCE_DMA);
+		if (ISSET(flags, SDHC_PCI_QUIRK_NO_PWR0))
+			SET(sc->sc.sc_flags, SDHC_FLAG_NO_PWR0);
+	}
 	/*
 	 * Map and attach all hosts supported by the host controller.
 	 */
@@ -277,16 +298,18 @@
 #define  MMC_SD_DIS			0x02
 
 static void
-sdhc_pci_quirk_ti_hack(struct pci_attach_args *pa)
+sdhc_pci_quirk_ti_hack(struct pci_attach_args *pa,
+    const struct sdhc_pci_quirk *q)
 {
 	pci_chipset_tag_t pc = pa->pa_pc;
 	pcitag_t tag;
 	pcireg_t id, reg;
 
-	/* Look at func 3 for the flash device */
-	tag = pci_make_tag(pc, pa->pa_bus, pa->pa_device, 3);
+	/* Look at function for the flash device */
+	tag = pci_make_tag(pc, pa->pa_bus, pa->pa_device,
+	    q->flash_controller_function);
 	id = pci_conf_read(pc, tag, PCI_ID_REG);
-	if (PCI_PRODUCT(id) != PCI_PRODUCT_TI_PCI7XX1_FLASH)
+	if (PCI_PRODUCT(id) != q->flash_controller_product)
 		return;
 
 	/*
これでOK。使えるようになった。
CR85ロガー計画続き。
テスト用につなげようとしているH8/3664はSCIが1チャネルしかないので、MMC カード用には使えない(ホストとの通信に使うので)。I2Cの同期シリアルが使え る。しかしI2Cはクロックとデータの2線のみ。MMCカードは3線。
H8/3664は5V駆動だけれど、I2CピンSDAとSCLはN-MOSオープンドレインなので、 3.3Vでプルアップするだけで、SD/MMCインターフェースに合うのでは? オープ ンドレインならSD/MMCのIN/OUTをORしてH8のSDAに継げてもいいし、気合いで R/W切り替えてなんとかならないかな? という目論見の元、ブレッドボードに 3.3Vの供給をして、配線してみました。プルアップは50kΩ。これは仕様で指定 されている下限。




数日前からオナガ(鳥)の群(4,5匹?)が、うちの庭に巣を作りはじめた。もとも
とこのあたりを散歩するカラスが一匹いたのだけど、それと朝っぱらからずっ
と喧嘩していて、オナガはギィーッギィーッととてもうるさい。

カラスのいるあたりを威嚇に飛びまわり、興奮すると二匹でカラスを追い廻し ている。オナガの気の強さと騒さには驚いた。一見優雅なのにね。
カラスが諦めたのか、ちょっと静かになった。
次の段階に進もうということで古いSD/MMCカードをブレッドボードまで配線し てみました。上についてるのはテスト用のH8/Tiny 3664。



昨日は飲み会だっのだけど、携帯を忘れて行ってしまい、落ち合うことが出来
ず。さっき携帯見たら着信履歴が多数...。申しわけない。

そのまま帰るのもなんだし、と、いつもの店に顔出し程度に...では済むことは なく。はしごまでしてしまい、家に帰ったのは4時。今日は微妙に二日酔い。
ここ最近は酒にも弱くて昔のように呑んでしまうと、数日体調が悪い。
Interface 2009/5月号の付録基板の続き。
ディレイ、タイマ、バッファシリアルコンソールをもりもり実装して、カーネルは フル実装できました。とりあえずここで一段落です。 今後の予定は
MMCカードの読み書き
↓
とりあえず水温かなんか楽にとれそうなデータをとれるように
↓
CR85に積む
このLPC2388はリトルエンディアンなので、ホストのx86側とファイルのやりと
りをする際にエンディアンを気にしなくていいのがいいし(H8はビッグ)、
SD/MMCカードインターフェースついてるし(H8/3052には専用インターフェース
はなく、SCIを使って実装することになる)、これをロガーのメインCPUにしよう
と思っています。


        


Interface 2009/5月号の付録基板の続き。

割り込みまわりのスレッド処理を実装します。
割り込みはARMの場合0x0-0x1cのに例外ベクタが配置される。LPC23XXの場合、
MEMMAPレジスタ(0xe01fc040)の設定によって、この位置のフェッチが起きた場
合に、フラッシュROMの0x0、フラッシュロムの0x7fffe000(ブートローダ)、内
蔵SRAMの0x40000000、外部拡張RAMの、4つの場所をマップすることができる。

リセット直後はブートローダがマップされているので、ブートローダのが使わ
れ、ブートローダによってユーザプログラムに移行されるとフラッシュロムの
0x0が使われるようになる。

今はフラッシュロムに乗せたモニタによって0x40000000のSRAMにプログラム
をロードしてプログラムを動かしている。このプログラムから割込みを使うの
にはMEMMAPによって0x40000000を例外ベクタとするのが楽だ。

ARMの分岐命令は+-32MBまでのオフセットなので、分岐命令では1G以上先の
SRAMのプログラムには届かない。MEMMAPはベクタ位置の32byteじゃなく、その先の
32byte、計64byteをマップしてくれるので、

	.section	.vector, "ax"	// "A"llocate. e"X"ecute.
	.align	2
	.global	vector_table
vector_table:
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	ldr	pc,	[pc, #24]
	.long	start
	.long	exception_und
	.long	swi_handler
	.long	exception_iabort
	.long	exception_dabort
	.long	0
	.long	irq_handler
	.long	fiq_handler

のようにすることで32bitアドレスどこにでも分岐できるようになっている。

ARMには実行モードによってレジスタバンクがあり、例外の発生とともに自動的
に切り替わる。そのモードはSYS, SVC, UND, ABT, FIQ, IRQの6つがある。モー
ドはさらにUSRがあるけれど、USRに例外で切り替わることはない。

FIQ以外のモードにはR13(sp)とR14(lr)とSPSRがバンクとして用意されている。
例外発生時には、CPSRをSPSRにコピーし、例外発生時のPCをR14にコピーし、例
外ベクタに入る。FIQはR14からR8までバンクがある。

例外ベクタに入った段階で、例外前のCPSR,R13,R14は切り替えられているので
モードを変更しない以外アクセスできない。

例外に入った時のR13は先にスタック位置を設定しておけば、そのまま実行できる。
プログラムの最初で
#define	CPU_MODE_SET(reg, mode)		\
	mrs	reg,	cpsr;		\
	orr	reg,	reg,	mode;	\
	msr	cpsr_c,	reg

	CPU_MODE_SET (r0, #0x13)	//SVC
	ldr	sp,	[r1]
	CPU_MODE_SET (r0, #0x17)	//ABT
	ldr	sp,	[r1]
	CPU_MODE_SET (r0, #0x1b)	//UND
	ldr	sp,	[r1]
	CPU_MODE_SET (r0, #0x11)	//FIQ
	ldr	sp,	[r1]
	CPU_MODE_SET (r0, #0x12)	//IRQ
	ldr	sp,	[r1]
	CPU_MODE_SET (r0, #0x1f)	//SYS
	ldr	sp,	[r1]

	b	machine_startup

.Lstack_start:
	.long	stack_start

のように設定。全部同じスタック。machine_startupの後で実行はスレッドに移
される。その際にスタックが変更されるので、この空いたブートストラップス
タックを利用(SMPだとかちあうけれどこれはUP)。実装ではIRQとSWIはこのスタッ
クを使わない。FIQだけこのスタック使うことにして、IRQ,SWIからのFIQのネス
トをできるようにした。他のUND, ABTはここで中断なので、どこであろうとス
タックがアクセスできればいい。

方針は、FIQは割込み処理だけ。IRQ,SWIは割込み終了後にスレッドの切り替え。

gccは割込みハンドラ用のattributeを用意している。
void irq (void) __attribute__ ((interrupt ("IRQ")));
void fiq (void) __attribute__ ((interrupt ("FIQ")));
void swi (void) __attribute__ ((interrupt ("SWI")));
void abt (void) __attribute__ ((interrupt ("ABORT")));
void undef (void) __attribute__ ((interrupt ("UNDEF")));


void
irq ()
{
}

void
fiq ()
{
}

void
swi ()
{
}

void
abt ()
{
}

void
undef ()
{
}

これはarm-elf-gcc -mcpu=arm7tdmi-s -O -S i.cすると

	.file	"i.c"
	.text
	.align	2
	.global	irq
	.type	irq, %function
irq:
	@ Interrupt Service Routine.
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	subs	pc, lr, #4
	.size	irq, .-irq
	.align	2
	.global	fiq
	.type	fiq, %function
fiq:
	@ Fast Interrupt Service Routine.
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	subs	pc, lr, #4
	.size	fiq, .-fiq
	.align	2
	.global	swi
	.type	swi, %function
swi:
	@ ARM Exception Handler.
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	movs	pc, lr
	.size	swi, .-swi
	.align	2
	.global	abt
	.type	abt, %function
abt:
	@ Interrupt Service Routine.
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	subs	pc, lr, #4
	.size	abt, .-abt
	.align	2
	.global	undef
	.type	undef, %function
undef:
	@ ARM Exception Handler.
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	movs	pc, lr
	.size	undef, .-undef
	.ident	"GCC: (GNU) 4.3.2"

のようになる。リターンの仕方がsubs pc, lr, #4か、movs pc, lrかがポイン
ト。このabtはプリフェッチアボート用。データアボートの時はpc, lr, #8のは
ず。これがARMのレジスタバンクをきちんと使った例。SUBS, MOVSが該当モード
のSPSRからCPSRの復帰をやってくれる。'S'がポイント。'S'がつくとデスティ
ネーションがPCならPSRを復帰する。

IRQハンドラからスレッドスイッチをする関係から、IRQに入った所でスレッド
スイッチ用の領域にコンテキストを設定するために、モードをSYSに変更します。
これはSYSに変更しないと例外発生時のモードのR13,R14をアクセスできないの
で。
そして、IRQからの復帰時にはこのコンテキストからの全復帰で戻ります。

	.balign 4
.Lcurrent_thread:
	.long	current_thread

----------------------------------------------------------------------
FIQ
----------------------------------------------------------------------
	.global	fiq_handler
fiq_handler:
	stmdb	r13!,	{r0-r7}
FIQモードではR13,R14の他、R8-R12もレジスタバンクになるので退避するのは
R0-R7だけでよい。
STMDBなので、R13からプレ(B)デクリメント(D)してR0-R7を退避、r13!なの
でこの命令の後のR13はr7をストアした所を差している。

	// user code here
ここにいずれハンドラを。

	ldmia	r13!,	{r0-r7}
LDMIA、R13の位置からポスト(A)インクリメント(I)で復帰してR0-R7を上方に復帰
	subs	pc,	r14,	#4
	b	md_thread_machdep_noreturn_assert	// for debug.

----------------------------------------------------------------------
IRQ
----------------------------------------------------------------------
	.global irq_handler
irq_handler:
この時点でIRQは禁止となるけれど、FIQは禁止とならない。が、FIQからスレッ
ドをいじることはないのでFIQを禁止する必要はない。
R13とR14はIRQモードのバンクに切り替わっている。R14には割り込まれた後に
復帰すべきPCが入っている。R13は自由に使える。

	adr	r13,	.Lcurrent_thread
	ldr	r13,	[r13]
	ldr	r13,	[r13]
割り込まれたcurrent_threadをとります。この時点のR13が*current_thread。

	// save r0-r12 (skip PSR area)
	stmib	r13,	{r0-r12}
STMIB、プレインクリメントでR0-R12を退避します。プレインクリメントなのは

struct reg
{
  uint32_t psr;		// 0x00
  uint32_t a1;		// 0x04
  uint32_t a2;		// 0x08
  uint32_t a3;		// 0x0c
  uint32_t a4;		// 0x10
  uint32_t v1;		// 0x14
  uint32_t v2;		// 0x18
  uint32_t v3;		// 0x1c
  uint32_t v4;		// 0x20
  uint32_t v5;		// 0x24
  uint32_t v6;		// 0x28
  uint32_t v7;		// 0x2c
  uint32_t fp;		// 0x30
  uint32_t ip;		// 0x34
  uint32_t sp;		// 0x38
  uint32_t lr;		// 0x3c
  uint32_t pc;		// 0x40
} __attribute__((packed)); //68Byte

なので。PSRの位置をスキップして、a1の位置からロード。r13に'!'がつかない
のでr13の値はインクリメントされず、*current_threadのまま。(psrを差している)

	// save psr
	mrs	r0,	spsr_all
	str	r0,	[r13]
これは上記のpsrの位置に割り込まれた時のPSRをストア。

	// set return address
	sub	r14,	r14,	#4	// IRQ,FIQ,Prefetch Abort.
	str	r14,	[r13, #0x40]
リターンアドレスを調整して、reg.pcにセット。

ここからモードをIRQからSYSに変換します。変換するとR13がバンク切換えで
変更されるのでR1にとっておきます。R0-R12は既に退避した。
	mov	r1,	r13
	// change to system-mode
	mrs	r2,	cpsr_all
	orr	r2,	#0x1f
	msr	cpsr_c,	r2

この段階で割り込まれた時点のR13,R14がアクセスできる

	// save original r13,r14
	str	r13,	[r1, #0x38]
	str	r14,	[r1, #0x3c]
	// now interrupted thread's stack.
これで、スレッドスイッチスタイルでコンテキストを復帰できる準備ができた。
SYSになったことで、これからのスタックは割り込まれた時点でのスタックに
なります。(ここまではスタックを使っていない。)

	bl	interrupt
これは実際の割り込みハンドラ。VICのステータスレジスタ見てディスパッチします。

ここから先はdo_thread_switchと同様。スレッドスイッチする必要があればスイッチ
します。
	// Switch thread if requested.
	bl	thread_context_switch
	adr	r0,	.Lcurrent_thread
	ldr	r0,	[r0]
	ldr	r0,	[r0]

	ldr	r1,	[r0], #4	// r1 = *r0++;
	msr	cpsr_all,r1		// restore all CPSR
	ldmia	r0,	{r0-r15}


----------------------------------------------------------------------
SWI
----------------------------------------------------------------------
ほとんどIRQと同じ。
	.global swi_handler
swi_handler:
	IRQ_DISABLE (r13)

IRQ,FIQでは該当する割り込みが禁止されるけれど、SWIではそれがない。IRQハ
ンドラ内ではスレッドの処理をするため、IRQのみ禁止する。元に戻すのは例外
発生時のPSRのリロードで完了するので明示的にする必要はない。

	adr	r13,	.Lcurrent_thread
	ldr	r13,	[r13]
	ldr	r13,	[r13]

	// save r0-r12 (skip PSR area)
	stmib	r13,	{r0-r12}

	// save psr
	mrs	r0,	spsr_all
	str	r0,	[r13]

	// set return address
ここでR14の調整がいらないのがSWI。__asm volatile ("swi #0123");のように
呼び出すので
	str	r14,	[r13, #0x40]
	mov	r1,	r13
	// change to system-mode
	mrs	r2,	cpsr_all
	orr	r2,	#0x1f
	msr	cpsr_c,	r2

	// save original r13,r14
	str	r13,	[r1, #0x38]
	str	r14,	[r1, #0x3c]
	// now interrupted thread's stack.

	//------------------------------
ここはSWI命令用のテスト。SWI命令は下位24bitがユーザが自由に使っていい領
域なので、例外発生時の命令を読みとり、下位24bitをexception_swiに渡して
います。
	ldr	r0,	[r1, #0x40]	// return address.
	sub	r0,	#4		// swi instruction address
	ldr	r0,	[r0]		// swi instruction.
	and	r0,	#0xffffff	// user area.
	bl	exception_swi
	//------------------------------

	// Switch thread if requested.
	bl	thread_context_switch
	adr	r0,	.Lcurrent_thread
	ldr	r0,	[r0]
	ldr	r0,	[r0]

	ldr	r1,	[r0], #4	// r1 = *r0++;
	msr	cpsr_all,r1		// restore all CPSR
	ldmia	r0,	{r0-r15}
	b	md_thread_machdep_noreturn_assert	// for debug.

.Lstack_start:
	.long	stack_start


Interface 2009/5月号の付録基板の続き。

今週中にマイOSのポートを終わらせたくて焦ってます。次のBS走行会までにCR85の クランク交換したいんだ。
UART0のクロックの設定、データシートの方法(昨日は手で追ってやった)をプロ グラムにまとめました。これで設定したPCLKに対して主なボードレートの設定 を吐きます。PCLK=18MHzなら230400bpsでも安定。これはユーザプログラムのロー ドが速くて楽です。
// LPC23xx UART clock configuration tool.

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

struct frac
{
  float f;
  int DivAddVal;
  int MulVal;
} frac [] =
  {
    { 1.000000, 0, 1 },
    { 1.066667, 1, 15 },
    { 1.071429, 1, 14 },
    { 1.076923, 1, 13 },
    { 1.083333, 1, 12 },
    { 1.090909, 1, 11 },
    { 1.100000, 1, 10 },
    { 1.111111, 1, 9 },
    { 1.125000, 1, 8 },
    { 1.133333, 2, 15 },
    { 1.142857, 1, 7 },
    { 1.142857, 2, 14 },
    { 1.153846, 2, 13 },
    { 1.166667, 1, 6 },
    { 1.166667, 2, 12 },
    { 1.181818, 2, 11 },
    { 1.200000, 1, 5 },
    { 1.214286, 3, 14 },
    { 1.222222, 2, 9 },
    { 1.230769, 3, 13 },
    { 1.250000, 1, 4 },
    { 1.266667, 4, 15 },
    { 1.272727, 3, 11 },
    { 1.285714, 2, 7 },
    { 1.285714, 4, 14 },
    { 1.300000, 3, 10 },
    { 1.307692, 4, 13 },
    { 1.333333, 1, 3 },
    { 1.357143, 5, 14 },
    { 1.363636, 4, 11 },
    { 1.375000, 3, 8 },
    { 1.384615, 5, 13 },
    { 1.400000, 2, 5 },
    { 1.416667, 5, 12 },
    { 1.428571, 3, 7 },
    { 1.428571, 6, 14 },
    { 1.444444, 4, 9 },
    { 1.454545, 5, 11 },
    { 1.461538, 6, 13 },
    { 1.466667, 7, 15 },
    { 1.500000, 1, 2 },
    { 1.533333, 8, 15 },
    { 1.538462, 7, 13 },
    { 1.545455, 6, 11 },
    { 1.555556, 5, 9 },
    { 1.571429, 4, 7 },
    { 1.571429, 8, 14 },
    { 1.583333, 7, 12 },
    { 1.600000, 3, 5 },
    { 1.615385, 8, 13 },
    { 1.625000, 5, 8 },
    { 1.636364, 7, 11 },
    { 1.642857, 9, 14 },
    { 1.666667, 10,15 },
    { 1.692308, 9, 13 },
    { 1.700000, 7, 10 },
    { 1.714286, 10,14 },
    { 1.714286, 5, 7 },
    { 1.727273, 8, 11 },
    { 1.733333, 11,15 },
    { 1.750000, 3, 4 },
    { 1.769231, 10,13 },
    { 1.777778, 7, 9 },
    { 1.785714, 11,14 },
    { 1.800000, 12,15 },
    { 1.818182, 9, 11 },
    { 1.833333, 10,12 },
    { 1.833333, 5, 6 },
    { 1.846154, 11,13 },
    { 1.857143, 12,14 },
    { 1.857143, 6, 7 },
    { 1.866667, 13,15 },
    { 1.875000, 7, 8 },
    { 1.888889, 8, 9 },
    { 1.900000, 9, 10 },
    { 1.909091, 10,11 },
    { 1.916667, 11,12 },
    { 1.923077, 12,13 },
    { 1.928571, 13,14 },
    { 1.933333, 14,15 },
  };

float fr_guess[] = { 1.5, 1.4, 1.6, 1.3, 1.7, 1.2, 1.8, 1.1, 1.9 };
int bps_table [] = { 9600, 19200, 38400, 57600, 115200, 230400 };

void calc_parameter (int, int);

int
main (int argc, char *argv[])
{
  int peripheral_clock, i;

  if (argc < 2)
    {
      fprintf (stderr, "Specify peripheral clock frequency (Hz).\n");
      return 1;
    }

  peripheral_clock = atoi (argv[1]);

  for (i = 0; i < sizeof bps_table / sizeof (bps_table[0]); i++)
    calc_parameter (peripheral_clock, bps_table[i]);

  return 0;
}

void
calc_parameter (int pclk, int br)
{
  float PCLK, BR, DLest, FRest;
  int iDLest;
  int i;

  PCLK = (float)pclk;
  BR = (float)br;

  DLest= PCLK / (16. * BR);
  if (DLest == (float)((int)PCLK / (16 * (int)BR)))
    {
      i = 0;
      iDLest = (int)DLest;
    }
  else
    {
      // Determne Fractional Divider.
      for (i = 0; i < sizeof fr_guess / sizeof (fr_guess[0]); i++)
	{
	  FRest = fr_guess[i];
	  iDLest = PCLK / (16 * BR * FRest);
	  FRest = PCLK / (16 * BR * iDLest);
	  if (FRest < 1.9 && FRest > 1.1)
	    break;
	}
      for (i = 0; i < sizeof frac / sizeof (frac[0]); i++)
	{
	  if (frac[i].f > FRest)
	    break;
	}
      // best fit.
      if (FRest - frac[i - 1].f < frac[i].f - FRest)
	i--;
    }

  FRest = frac[i].f;
  int DivAddVal = frac[i].DivAddVal;
  int MulVal = frac[i].MulVal;
  int iDMest = (int)(PCLK / (16 * BR * FRest - (float)iDLest)) / 256.;
  float bps = PCLK / ((16. * (256. * (float)iDMest + (float)iDLest) *
		       (1. + ((float)DivAddVal / (float)MulVal))));

  printf (" { %d, %d, %d | (%d << 4), 0 };"
	  " // PCLK %.4fMHz %.0fbps error %.2f%%\n",
	  iDMest, iDLest, DivAddVal, MulVal,
	  PCLK / 1000000, bps, (1 - BR / bps) * 100.);
}

さて、次は割り込みまわり。このあたりからガチでアセンブラで書かないとい けなくなる。コーリングコンベンションはr0-r3が最初の4つまでの引数で、 caller saved。他のは全部callee saved.
例によって
int
a (int a0, int a1, int a2, int a3)
{

  return a0 + a1 + a2 - a3;
}

int
b ()
{
  return a (0, 1, 2, 3);
}

int
main ()
{

  return a (0xaa55, 4, 3, 2);
}

をarm-elf-gcc -O -S a.cでアセンブラに落とす。

	.file	"a.c"
	.text
	.align	2
	.global	a
	.type	a, %function
a:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	@ link register save eliminated.
	add	r1, r1, r0
	add	r1, r1, r2
	rsb	r0, r3, r1
	bx	lr
	.size	a, .-a
	.align	2
	.global	b
	.type	b, %function
b:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	str	lr, [sp, #-4]!
	mov	r0, #0
	mov	r1, #1
	mov	r2, #2
	mov	r3, #3
	bl	a
	ldr	pc, [sp], #4
	.size	b, .-b
	.align	2
	.global	main
	.type	main, %function
main:
	@ args = 0, pretend = 0, frame = 0
	@ frame_needed = 0, uses_anonymous_args = 0
	str	lr, [sp, #-4]!
	mov	r0, #43520
	add	r0, r0, #85
	mov	r1, #4
	mov	r2, #3
	mov	r3, #2
	bl	a
	ldr	pc, [sp], #4
	.size	main, .-main
	.ident	"GCC: (GNU) 4.3.2"
これで大体の雰囲気をつかんで、割り込みの処理はPSRのI(IRQ)とF(FIQ)を同時に 扱うことにして、
FUNC (intr_status)
	mrs	r0,	cpsr
	and	r0,	r0,	#0xc0
	bx	lr

FUNC (intr_disable)
	mrs	r0,	cpsr
	orr	r0,	r0,	#0xc0
	msr	cpsr_c,	r0
	bx	lr

FUNC (intr_enable)
	mrs	r0,	cpsr
	bic	r0,	r0,	#0xc0
	msr	cpsr_c,	r0
	bx	lr

FUNC (intr_suspend)
	mrs	r1,	cpsr
	mov	r0,	r1
	orr	r1,	r1,	#0xc0
	msr	cpsr_c,	r1
	and	r0,	r0,	#0xc0
	bx	lr

FUNC (intr_resume)
	mrs	r1,	cpsr
	bic	r1,	r1,	#0xc0
	orr	r1,	r1,	r0
	msr	cpsr_c,	r1
	bx	lr
こんなとこで。
そしてスレッドを実装します。まずはsetjmp, longjmpから。 さすがARM
FUNC (cpu_regs_save)
	stmia	r0,	{r0-r15}
	bx	lr

FUNC (cpu_regs_load)
	ldmia	r0,	{r0-r15}
	bx	lr
これだけでいいだろう。 これを
void
ohayo_func ()
{
  iprintf ("ohayo\n");
  while (1)
    ;
}

uint32_t
test (int32_t argc, const char *argv[])
{
  uint32_t r[16];
  int i;

  memset (r, 0, sizeof r);
  iprintf ("%x\n", r);
  cpu_regs_save (r);
  for (i = 0; i < 16; i++)
    {
      iprintf ("%x\n", r[i]);
    }
  r[15] = (uint32_t)ohayo_func;

  cpu_regs_load (r);
  iprintf ("OHAYO\n");
}
のように使ってみて確認。"ohayo"と表示されて成功だ。
そして実装。ちょっとまだチェックが足りないのだけど、スレッドスイッチは
	.long	current_thread
FUNC (do_thread_switch)
	// save context.
	mrs	r1,	cpsr
	ldr	r0,	[pc, #-16]	// current_thread
pc-16で上の.long current_threadを指定しているのはパイプラインが3段だから。
	ldr	r0,	[r0]		// r0 = *current_thread
	str	r1,	[r0], #4	// *r0++ = r1
	stmia	r0!,	{r0-r15}	// 全部のレジスタ退避(r0-r3は余計)
	str	lr,	[r0, #-4]	// overwrite PC to LR. *(r0 -4) = lr
ここで退避されたPCはstrmia命令の2つ先。それを本来この関数が戻るべきアドレス
(lr)に設定。

	bl	thread_context_switch
	ldr	r0,	[pc, #16]
	ldr	r0,	[r0]
	ldr	r1,	[r0], #4	// r1 = *r0++;
	msr	cpsr_cf,r1		// restore all CPSR
	ldmia	r0,	{r0-r15}
ここでPCが更新されてジャンプするはず。
	bl	md_thread_machdep_noreturn_assert	// for debug.
しなかった時用のデバッグ。
	.long	current_thread
これで済む。ARMのアセンブラは条件実行とシフタがあるのでとてもパズル的で、 さらにアセンブラの書き方が直感的じゃない(上のコードでいうとstmiaの'!'と か。この! はr0のインクリメントの指定)。できる限りアセンブラで書きたくない な...と思っていたのだけど、
いざ実際書いてみるとちょっと楽しいかも。
なんとかそこそこスレッドも動いている様子だけれど、まだこの段階だと思いっ きり勘違いしている可能性大。


Interface 2009/5月号の付録基板の続き。

ロムモニタができました。H8用のと同じくモトローラSフォーマットのローダです。
$ tip umon
connected
stack_start: 0x40010000
ROM data: 0x3b70-0x3ff4
RAM data: 0x4000f800-0x4000fc84 1156byte
bss: 0x4000fc84-0x4000fd28 164byte
Clock: IRC, PLL NOT connected, PLL M:0 N:0, CCLKCFG 0
Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3
Checking RAM 40000000-4000f800
bss RAM check passed.
LPC2388 Simple Monitor Build Jun 17 2009 21:27:41
>> l
Send S-record file.
~>Local file name? a.mot
1544 lines transferred in 4 seconds 
!
Read 36221 byte. success
Start address: 0x40000200
data: 0x40002d98-0x400031f0 1112byte
bss: 0x400031f0-0x40003270 128byte
Hello world
Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3
user> 
user> led
user> led       
user> led
user> reset
stack_start: 0x40010000
ROM data: 0x3b70-0x3ff4
RAM data: 0x4000f800-0x4000fc84 1156byte
bss: 0x4000fc84-0x4000fd28 164byte
Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3
Clock: MAIN, PLL connected, PLL M:11 N:0, CCLKCFG 3
Checking RAM 40000000-4000f800
bss RAM check passed.
LPC2388 Simple Monitor Build Jun 17 2009 21:27:41
>> 
これでいくらでもガツガツとトライ&エラーができる。実はスタックがBSS潰し たりとか、BSS初期化忘れてたりと、そっと秘密にしておきたいような失敗をし て時間がかかってしまいました。
クロックについて。

LPC2388のクロックソースには、外部発振子(メイン 〜25MHz)、内部RC発振子
4MHz、外部RTCの3つが選べる。

リセット後には内部発振子の4MHzで駆動する。

リセット後に実行されるブートローダ(ISP)はこれをベースにPLLでクロックを
設定する。

ISPでボーレートの合わせ込みをした後にクロックを設定するのはこれのため。
UARTのクロックはCPUクロックをベースにして設定され、最低でも10MHzはない
と38400bpsで通信できない。実際のとこ12MHzに設定して115200bpsでいける。

PLLの入力は32kHzから25MHzまで。32kHzというのはRTCをソースにできるという
こと。

PLLを使わない場合は入力ソースがそのままシステムクロックになる。

入力->PLL->Divider->C(CPU)CLK->Divider->P(Peripheral)CLK

ユーザプログラムが直接起動した場合、内部RC発振子でPLLが接続されていない
状態になるので4MHzで動くことになる。

まずCPUクロックの設定。最大の72MHzにします。クロックを外部発振子12MHzに
変更して、PLLを設定し(288MHzにする)、Dividerを設定し(1/4にする)72MHzに
します。LPC23XXのユーザマニュアル6.14章 PLL setup sequenceをそのまま実
装します。

static inline void
__pll_feed ()
{

  *PLLFEED = 0xaa;
  *PLLFEED = 0x55;
}

#define	PLL_MVAL	11
#define	PLL_NVAL	0
#define	CCLKDIV		3

void
mcu_speed ()
{
  // User manual 6.14 PLL setup sequence.

  // 1. Disconnect the PLL with one feed sequence if PLL is already connected.

ソースクロックとCPUクロックはPLLを介して接続されるか、そのまま接続され
るかの二択。PLLを介している時にPLLの設定を変えることはできないので、ま
ずPLLの接続を外してソースクロック直結にします。

ユーザプログラムがリセット後直接起動された場合はISP(ブートローダ)によっ
てPLLは接続されていないけれども、ISPから直接ユーザプログラムにジャンプ
することもできる。その場合PLLが接続された状態で実行される。なので確認
する必要がある。

  if (*PLLSTAT & PLLSTAT_PLLC)	// PLL connected?
    {
      // Disconnect PLL. (Enable)
      *PLLCON = PLLCON_PLLE;
      __pll_feed ();
PLLCON, PLLCFGのレジスタの設定はフィードすることで初めて実行される。
    }

  // 2. Disbale the PLL with one feed sequence.
PLLを無効化します。PLLCFGはPLLが無効の時しか設定できないためです。
  *PLLCON = 0;
  __pll_feed ();

  // 3. Change the CPU Clock Divider setting to speed up operation
  // without the PLL, If desired
よくわからず。

  // 4. Write to the Clock Source Selection Control register to change
  // clock source.
  // Set main oscillator as PLL clock source.
ソースクロックをメイン(このボードの場合12MHz)に変更します。変更する
前に発振子が安定するのを確認して。
  // Enable main oscillator.
  *SCS |= SCS_OSCEN;
  // Wait until oscillator ready.
  while (!(*SCS & SCS_OSCSTAT))
    ;
  *CLKSRCSEL = CLKSRCSEL_OSC;

  // 5. Write to the PLLCFG and make it effective with one feed
  // sequence. The PLLCFG can only be updated when the PLL is disabled.
このPLLCFGの設定します。逓倍された周波数は275MHzから550MHzの範囲で
ないといけない。

  *PLLCFG = PLL_MVAL | (PLL_NVAL << PLLCFG_NSEL_SHIFT);
  // 2 * (MVAL+1) * Fin(12MHz) / (NVAL + 1)
  // 2 * 12 * 12000000 / 1 = 288MHz

MVALとNVALの値は上記のコメントのように設定した。いくつかのパターンは存
在するけれど、NVALを小さくするのがよいとデータシートにはあるのでこの設
定。周辺機器のクロックはCPUクロックからの分周で与えられ、その分周は
1/1,1/4,1/8に限られる(RTC,CAN1,CAN2に例外あり)。USBは48MHzでないといけ
ない。そのあたりを考慮して設定する。UARTは計算によってそこそこ自由に設
定できる。

  __pll_feed ();

  // 6. Enable the PLL with one feed sequence.
PLLの設定ができたのでPLLを動かします。まだソースとCPUの間には接続されて
いません。
  // Enable PLL, Disconnected.
  *PLLCON = PLLCON_PLLE;
  __pll_feed ();

  // 7. Change the CPU Clock Divider setting for the operation with
  // the PLL. It's critical to do this before connecting PLL.
  // CPU clock divider.
ソースクロックがPLLによって逓倍(この場合24倍で288MHz)されたのから、
何分周したのをCPUクロックとするかを設定します。72MHzにしたいので4。
  // 1/4 divider 288/4 = 72
  *CCLKCFG = 3;
  // 1/8 divider 288/8 = 36
  //  CCLKCFG = 7;

  // 8. Wait for PLL to achive lock.
PLLが安定するのを待ちます。
  while ((*PLLSTAT & PLLSTAT_PLOCK) == 0)
    ;
  // Now PLL is requested frequency.

  // 9. Connect PLL with one feed sequence.
PLLが安定したところで、ソースとCPUの間にPLLを接続します。

  *PLLCON = PLLCON_PLLE | PLLCON_PLLC;		/* enable and connect */
  __pll_feed ();
  while ((*PLLSTAT & PLLSTAT_PLLC) == 0)
    ;
  // Now PLL is connected.
}

これでCPUを72MHzで設定できました。そしてUARTの設定。UARTのクロックはCPUの
クロックの分周から設定されるので、ここからの影響を受けます。


void
com0_init ()	// UART0
{
  // Pin select
ピンはGPIOを多重化されているのでUARTに使うように設定します。
  mcu_pin_select (0, 2, 1); // Port0.2 : TXD0
  mcu_pin_select (0, 3, 1); // Port0.3 : RXD0

  /* Clock supply */
  // CCLK 72MHz PCLK 18Hz
CPUクロックを72MHzにしたので1/4の18MHzをPCKにします。
  mcu_peripheral_clock (PCLK_UART0, CCLK4);	// 72/4=18
  // CCLK 36MHz PCLK 18Hz
CPUクロックを36MHzにしたのであれば、
  //mcu_peripheral_clock (PCLK_UART0, CCLK2);	// 36/2=18

  /* Power supply */
電源を入れます。
  mcu_peripheral_power (PCONP_PCUART0, TRUE);

  /* Line speed. */
パラメタの設定について。
  /*
    PCLK 18MHz 115200bps
に設定したいとする。

    DLest = 18000000/(16*115200) = 9.77
を計算する。これが整数でないので

iDLest = PCLK / (16 * 115200 * 1.5);
を計算する。(1.5がFRest)
FRest = PCLK / (16 * BR * iDLest);
を計算する。

    FRest 1.5 がguessで FRest = 1.627になった。

この計算が1.1から1.9に収まらなければFRestのguessを変化させて収まるようにする。

ここでユーザマニュアルの16章テーブル363を参照する。
    From Table 16.363
    1.625 DivAddVal/MulVal = 5/8
このテーブルにある一番近い値は1.625なので、これを参照して
MULVAL=8,DIVADDVAL=5にする。

DLMとDLLはどうやって決定するか。
    18000000/(16*(256 * DLM + DLM)* (1+ 5/8))
この値がbpsになるので、いいところを探す。

    18000000/(16*(256 * 0 + 6)* (1+ 5/8))=115384
このあたりだろう。なので
    DLM = 0, DLL = 6

これなら誤差2%程度。
    115384/115200=1.002

というように決める。

    PCLK 18MHz 57600bps
    DLest = 18000000/(16*57600) = 19.53
    FRest = 1.5 -> FRest = 1.5024
    1.500 DivAddVal/MulVal = 1/2
    18000000/(16*(256 * 0 + 13)* (1+ 1/2)) = 57692
    DLM = 0, DLL = 13
    57692/57600=1.002

    PCLK 18MHz 9600bps
    DLest = 18000000/(16*9600) = 117.2
    FRest = 1.500000 =>1.502404
    1.500 DivAddVal/MulVal = 1/2
    18000000/(16*(256 * 0 + 78)* (1+ 1/2))=9615
    DLM = 0, DLL = 78
    9615/9600=1.002
   */


  // Line Control Register
  *U0LCR = ULCR_DLAB;	// Enable Divisor Latche access.
#define	B_115200
#ifdef B_115200
  // 115200bps
  // DLM=0, DLL=4, DIVADDVAL=5, MULVAL=8
  *U0DLM = 0;	// Divisor Latch MSB
  *U0DLL = 6;	// Divisor Latch LSB
  *U0FDR = 5 | (8 << 4);	// Fractional Divider Register
#elif defined B_57600
  // 57600bps
  *U0DLM = 0;		// Divisor Latch MSB
  *U0DLL = 13;	// Divisor Latch LSB
  *U0FDR = 1 | (2 << 4);	// Fractional Divider Register
#elif defined B_9600
  // 9600bps
  *U0DLM = 0;		// Divisor Latch MSB
  *U0DLL = 78;	// Divisor Latch LSB
  *U0FDR = 1 | (2 << 4);	// Fractional Divider Register
#else
#error
#endif
  // Set line format. 8N1.
  *U0LCR = ULCR_8BIT | ULCR_PARITY_NONE | ULCR_STOP1 | ULCR_BREAK_DISABLE;
  *U0IER = 0;	// Interrupt disable.
}


Interface 2009/5月号の付録基板の続き。

フラッシュロムの書き込みプログラム、4KBあればしばらく使えるだろう..と思っ ていたのだけど、すぐに限界に達した。32bitのRISCだからね。H8の16bitの CISCの感覚だとすぐにあふれてしまう。Thumbは落ちついてから。
ということでフラッシュロムライタをフルスペックにまで書きました。懸案の Autobaudのところでダンマリになってしまうのは、ノンブロックモードにして からリードにして、ボードのリセットを促すようにしました
  fcntl (fd ,F_SETFL, O_NONBLOCK);	// Non-block mode.
  do
    {
      if (retry++)
	{
  ここの処理はダンマリになってからtipで接続してみて、ネゴシエーションを
開始できるようになるのを模しています。
	  buf[0] = '\r';
	  buf[1] = '\n';
	  write (fd, buf, 2);
	  tcdrain (fd);
	  usleep (100000);
	}

      buf[0] = '?';
      if (write (fd, buf, 1) == -1)
	{
	  perror ("'?' write failed.");
	  return 1;
	}
      tcdrain (fd);

      if ((sz = read (fd, buf, sizeof buf)) == -1)
	{
	  perror ("'?' read failed. PLEASE RESET AGAIN.");
	}
    }
  while (sz != 14);
  fcntl (fd ,F_SETFL, 0);	// Block mode.
これでなんとかリセットボタンを何度か押すだけでフラッシュに書き込めるよ うになった。とはいえ不満。もっといい解決方があるはずなのだけど..。 (Flash Magicはずっと確実)
次はシリアルI/O。これは16550互換なのね。インターフェース6月号のサンプルを 見てポーリングのgetc,putcまで作って、シェルまでつなげました。
19200bps 8N1で単にエコーバックするエッセンスはこれ。
void
func2 ()
{
  // UART0 Echoback test.
  // 19200bps 8N1
#define	PINSEL0  ((volatile uint32_t *)0xe002c000)
  *PINSEL0 = (*PINSEL0 & 0xffffff0f) | 0x50;
#define	CLK_PCLKSEL0 ((volatile uint32_t *)0xe01fc1a8)
  *CLK_PCLKSEL0 &= 0xffffff3f;
#define	POW_PCONP	((volatile uint32_t *)0xe01fc0c4)
  *POW_PCONP |= 8;
#define	COM_LCR	((volatile uint32_t *)0xe000c00c)
#define	COM_DLL	((volatile uint32_t *)0xe000c000)
#define	COM_DLM	((volatile uint32_t *)0xe000c004)
#define	COM_FDR	((volatile uint32_t *)0xe000c028)
#define	COM_IER	((volatile uint32_t *)0xe000c004)
  *COM_LCR = 0x80;
  *COM_DLL = 3;
  *COM_DLM = 0;
  *COM_FDR = (12 <<4)|1;
  *COM_LCR = 3;
  *COM_IER = 0;
#define	COM_LSR	((volatile uint32_t *)0xe000c014)
#define	COM_THR	((volatile uint32_t *)0xe000c000)
#define	COM_RBR	((volatile uint32_t *)0xe000c000)
  while (/*CONSTCOND*/1)
    {
      uint8_t c;
      while ((*COM_LSR & 0x01) == 0)
	;
      c = *COM_RBR;
      while ((*COM_LSR & 0x20) == 0)
	;
      *COM_THR = c;
    }
}
プログラムの書き込み。
../../lpc23xxwrite/lpc23xxwrite test.bin
test.bin: 12908 byte
'?' read failed. PLEASE RESET AGAIN.: Resource temporarily unavailable
'?' read failed. PLEASE RESET AGAIN.: Resource temporarily unavailable
この表示でリセットをかける。何度も。
'?' read failed. PLEASE RESET AGAIN.: Resource temporarily unavailable
'?' read failed. PLEASE RESET AGAIN.: Resource temporarily unavailable
'?' read failed. PLEASE RESET AGAIN.: Resource temporarily unavailable
Baudrate synchronized.
Frequency 14746 KHz
echo off
U 23130 done.
P 0 3 done.
E 0 3 done.
W 1073742336 4096 done.
0x0: sector 0x0 offset 0x0
P 0 0 done.
C 0 1073742336 4096 done.
W 1073742336 4096 done.
0x1000: sector 0x1 offset 0x0
P 1 1 done.
C 4096 1073742336 4096 done.
W 1073742336 4096 done.
0x2000: sector 0x2 offset 0x0
P 2 2 done.
C 8192 1073742336 4096 done.
W 1073742336 620 done.
0x3000: sector 0x3 offset 0x0
P 3 3 done.
C 12288 1073742336 1024 done.
echo on
Success.

$ tip umon19200
connected

stack_start: 0x4000fff0
ROM data: 0x2de4-0x326c 1160byte
RAM data: 0x40000000-0x40000488 1160byte
bss: 0x40000488-0x4000051c 148byte
stack check: 4000ffd8
LPC2388 Simple Monitor Build Jun 15 2009 22:09:20
>> help         
avaliable command: help reset l 
>> reset
RESET
OK。あとはちまちま実装だ。


Interface 2009/5月号の付録基板の続き。

昨日の実験をもとに、バイナリイメージから、ベクタ領域のチェックサムを調 整して、LPC23XX用のUU-encodeしてターゲットに送りこむコマンドを作成しま した。今のところフラッシュの1セクタ(4KB)までの限定。
http://www.vnop.net/~uch/h8/w/arm/lpc23xxwrite/
起動時のバッファのフラッシュが足りないので、だんまりな時はUSBケーブルの 抜き差しないとだめ。これはおいおい。
これでとりあえずNetBSD上でmake一発でフラッシュの書き込みまでできる環境 ができた。


Interface 2009/5月号の付録基板の続き。
昨日、ユーザ定義セクション(.vector)を
	 .vector :
	 {
		*(.vector)
	 } > vectors
としてLOADが立たないのは、ソース側で
	.section	.vector
としていたためだった。
	.section	.vector, "ax"
	.align	2
のように"a"でallocateを明示的に立てないとだめ。"x"はテキスト。これはな
くてもいい。
LPC2388のブートローダ(ISP)をNetBSDのtipから操作してみました。なんとかフ ラッシュの書き込みまでできるのを確認しました。
uslsa0 at uhub0 port 3
uslsa0: Silicon Labs CP2102 USB to UART Bridge Controller, rev 1.10/1.00, addr 4
ucom0 at uslsa0: Silicon Labs CP210x

/etc/remote

umon:dv=/dev/dtyU0:br#115200:pa=none:dc:

$ tip umon
connected
Synchronized
OKnchronized

'?'を叩くと、自動ボーレート合わせこみが完了しSynchronizedと返事が
来る。そこ後ホスト側からもSynchronizedと叩く。OKと返事が来る。
周波数を設定するために12000と叩く(12MHz)


'K'コマンド(Read Boot Code version number)
0	CMD_SUCCESS
3	Major
3	Minor

'J'コマンド(Read Part Identification number)
0		CMD_SUCCESS
402718517	Table 29.543 LPC2300 part Identification numbers (LPC2388)


'U'コマンド (Unlock)
12	PARAM_ERROR

U 23130
0	CMD_SUCCESS

'R' (Read Memory)
R 0 64

M&$"?Y1!0G^4`8)3E!F`%X`!@A.4$\!_E0.#_?_^___\`@/\_````````````
3````````````````````````````
4797

R 0 32

0 0 32
@&$"?Y1!0G^4`8)3E!F`%X`!@A.4$\!_E0.#_?_^_____
4351

この後ホスト側からOKを返してコマンド終了。

0 0 4
$&$"?Y>7E
476

$&$"?Y>7Eをuudecodeすると、0x18,0x40,0x9f,0xe5。0x18+0x40+0x9f+0xe5=476

例えば0x0からを読み出し、uudecodeして
arm-elf-objcopy -I binary -O elf32-littlearm test.bin test.elf
arm-elf-objdump -m arm -D test.elf
したもの。arm-elf-objdumpのバグで一度ELFにしないとバイナリから直接ディ
スアセンブルできない。

00000000 <_binary_test_bin_start>:
   0:	e59f4018 	ldr	r4, [pc, #24]	; 20 <_binary_test_bin_start+0x20> r4 = 0x3fff8000 PARTCFG REGISTER
   4:	e59f5010 	ldr	r5, [pc, #16]	; 1c <_binary_test_bin_start+0x1c> r5 = ~0x4000
   8:	e5946000 	ldr	r6, [r4]	; r6 = *((int32_t *)0x3fff8000)
   c:	e0056006 	and	r6, r5, r6	; r6 &= ~0x4000
  10:	e5846000 	str	r6, [r4]	; *((int32_t *)0x3fff8000) = r6
  14:	e51ff004 	ldr	pc, [pc, #-4]	; 18 <_binary_test_bin_start+0x18> jump to 0x7fffe040
  18:	7fffe040 	svcvc	0x00ffe040
  1c:	ffffbfff 	undefined instruction 0xffffbfff
  20:	3fff8000 	svccc	0x00ff8000
	...
  40:	ffffffff 	undefined instruction 0xffffffff
PARTCFG レジスタの14bitを落としてからISPコードを実行する。PARTCFGレジスタの
詳細は不明。



'G' コマンド
G 0 A
で0x0から実行。この場合ISPが最初から動くことになる。


ISPはRAM 0x40000120-0x400001ffまでを使う、スタックはRAM top -32から。最
大256byte使う。

ISPとのデータのやりとりはuuencodeされたもので受けわたしする。rawdata
45byte づつなので、通常のuuencodeでよい。異なるポイントは20ラインごとに
チェックサムを送ること。最後の0byteラインを入れない。

通常のuuencode(/usr/bin/uuencode)
JH.,`((/E_SW@XS\Q0^(`(*#C`""#Y?\]X.,O,4/B`""@XP`@@^7^___J
` ←これはこのラインは0ということ。uudecodeの場合これを終端と認識する。
end

の所を

JH.,`((/E_SW@XS\Q0^(`(*#C`""#Y?\]X.,O,4/B`""@XP`@@^7^___J
41994 ←チェックサムを入れる。

のようにしないといけない。

これらを使ってバイナリをRAMにロードする手順は、
まず、JP2をショートしてリセットをかける。
tipで接続。
以下、ホストから叩く順
?			自動合わせこみに失敗したらもう一度リセットから。通常1個叩けばよい。
Synchronized		返答
12000			初期状態のクロックを設定これは12MHzに設定。
A 0			エコーバックするとISP側の処理が間にあわないのでエコーオフ
W 1073742336 852	0x40000200(内蔵SRAM)から852byte(これはraw dataの量)書き込み。0x40000000-0x400001ffまではISPが使っているから。
~> test.uu		送りこみ。OKが出れば完了。失敗するとRESENDと表示される。
A 1			エコーバックするように戻す
U 23130			アンロック。G コマンド、及びフラッシュロムの書き込みコマンドはロックがかかっているので。
P 0 0			フラッシュ0セクタを準備
E 0 0			フラッシュ0セクタを消去
P 0 0			フラッシュ0セクタを準備
C 0 1073742336 1024	フラッシュ0セクタに0x40000200(内蔵SRAM)から1024byte書き込み
G 1073742336 A		0x40000200にジャンプ (確認。しなくともよい)

これでRAM上に乗せたプログラムが実行される。ARMの分岐はPC相対なので場所
はどこでも動く。RAMとROMで読み出しスピードが違うのでディレイのループは
ROMの10倍以上とる。ファイルの転送は115200bpsで大丈夫。エコーバックする
と9600bpsでも失敗する。

これでJP2をオープンにしてリセットをかけてLEDが点滅すれば成功。点滅せず、
tipから'?'を叩いてSynchronizedと出れば、ベクターのチェックサムが間違っ
ていてISPモードに落ちたということ。このチェックサムは

	.section	.vector, "ax"
	.align	2
	.global	vector_table
vector_table:
	b	func0		// 0xea00007e
	.long	0x00
	.long	0x00
	.long	0x00
	.long	0x00
	.long	0x69e23484	//チェックサムの調整部分。
//	.long	0x07
	.long	0xac1dcafe
	.long	0x00

0xea00007e+0xac1dcafe + 0x69e23484 = 0になるようにする。

0xea+0x7e+0xac+0x1d+0xca+0xfe + 0x7 = 0x400ではだめ。

Synchronized
OKnchronized
OK000
0 0
0
~>Local file name? test.uu
20 lines transferred in 0 seconds 
!
OK
0
0 23130
0 1073742336 A

フラッシュロムのセクタサイズはアドレスによって異なる。Section 29,
Table 527 参照。セクタ0は4KB。書き込みの際は4KB書かなくてもよい。


バイナリイメージからのuuencodeには
http://www.wotsit.org/download.asp?f=uuencode&sc=298186266
のサンプルを改造して、20ラインごとにチェックサムを出力し、最後の0byteを
出力しないようにした。

/*
 * uuencode.c -
 *  Simple uuencode utility
 *  Jim Cameron, 1997
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>

#define	MAX_LINELEN 45
#define	ENCODE_BYTE(b) (((b) == 0) ? 0x60 : ((b) + 0x20))

int
main (int argc, char *argv[])
{
  FILE *infile	= NULL;
  FILE *outfile = NULL;
  int linelen;
  int linecnt;
  uint8_t inbuf[MAX_LINELEN];
  uint8_t *inbytep;
  char outbuf[5];
  int i;

  if (argc != 3)
    {
      fprintf (stderr, "Syntax: uuencode <infile> <outfile>\n");
      exit (1);
    }

  /* Try and open the input file (binary) */

  infile = fopen (argv[1], "rb");
  if (infile == NULL)
    {
      fprintf (stderr, "uuencode: Couldn't open input file %s\n", argv[1]);
      exit (1);
    }

  /* Try and open the output file (text) */

  outfile = fopen (argv[2], "wt");
  if (outfile == NULL)
    {
      fprintf (stderr, "uuencode: Couldn't open output file %s\n", argv[2]);
      exit (1);
    }

  int checksum, nline;
  checksum = 0;
  nline = 0;
  do
    {
      /* Read a line from input file */
      linelen = fread (inbuf, 1, MAX_LINELEN, infile);
      if (!linelen)
	break;
      for (i = 0; i < linelen; i++)
	checksum += inbuf[i];

      /* Write the line length byte */
      fputc (ENCODE_BYTE (linelen), outfile);

      /* Encode the line */
      for (linecnt = linelen, inbytep = inbuf; linecnt > 0;
	   linecnt -= 3, inbytep += 3)
	{
	  /* Encode 3 bytes from the input buffer */
	  outbuf[0] = ENCODE_BYTE ((inbytep[0] & 0xFC) >> 2);
	  outbuf[1] = ENCODE_BYTE (((inbytep[0] & 0x03) << 4) +
				   ((inbytep[1] & 0xF0) >> 4));
	  outbuf[2] = ENCODE_BYTE (((inbytep[1] & 0x0F) << 2) +
				   ((inbytep[2] & 0xC0) >> 6));
	  outbuf[3] = ENCODE_BYTE (inbytep[2] & 0x3F);
	  outbuf[4] = '\0';

	  /* Write the 4 encoded bytes to the file */
	  fprintf (outfile, "%s", outbuf);
	}

      fprintf (outfile, "\n");
      if (++nline == 20)
	{
	  fprintf (outfile, "%d\n", checksum);
	  nline = checksum = 0;
	}
    }
  while (linelen != 0);
  if (nline)
    fprintf (outfile, "%d\n", checksum);

  fclose (infile);
  fclose (outfile);

  return 0;

}



Interface 2009/5月号の付録基板に手をつけました。
リセットからのブートシーケンスについて。
ARMは0x0-0x1cにベクタテーブルがある。0x0がリセットベクターで、リセット後は
ここの命令を実行する。

LPC23XXはリセット後まず内蔵のブートローダが実行される。このブートローダは
フラッシュロムの0x7e000からの8KBに納されている。(なのでユーザが使えるのは
512-8の504KB)

このブートローダは0x7fffe000にマップされる。ユーザプログラムからこのブート
ローダの関数を呼ぶにはここを使う。ブートローダもこのアドレスで動く。

さらにこの8KBの下の64Bは0x00000000にマップされる。このマップによってCPUは
ブートローダのリセットベクタからブートローダに実行を移せる。

リセット時にP2.10ピンがGNDに落ちていれば、強制的にブートローダのISPモードに
なる。これはホストからフラッシュロムにアクセスするためのモード。

P2.10ピンがプルアップされていた場合、ブートローダのコードはフラッシュロ
ム上のベクタエリアをチェックし、0x0-0x1cまでの2の補数のチェックサムが0
になるのであれば、ユーザプログラムと認識して、フラッシュロムの0x0の命令
を実行する。そうでなければISPモードに移行する。

チェックサムは0x14のARMの予約領域を利用して、ここで調整する。

----------------------------------------------------------------------
コンパイラはgcc-4.3.2とbinutils-2.18。arm-elfで作成。

----------------------------------------------------------------------
テストプログラムは、基板上のLEDを点灯。
// 64KB Local on-chip SRAM. まず単純にRAMを使いたいので直指定。
#define	RAM	(*(volatile unsigned int *)0x40000000)

これは点滅バージョン
void
func0 ()
{

  *(volatile unsigned int *)0xe01fc1a0 |= 1;	// SYS_SCS
  *(volatile unsigned int *)0x3fffc020 = 0x40000;
  *(volatile unsigned int *)0x3fffc030 = 0;
  while (1)
    {
      *(volatile unsigned int *)0x3fffc034 = 0;
      for (RAM = 0; RAM < 10000; RAM++)
	;
      *(volatile unsigned int *)0x3fffc034 = 0x40000;
      for (RAM = 0; RAM < 10000; RAM++)
	;
    }
}

これは点灯するだけ。
void
func1 ()
{
  *(volatile unsigned int *)0xe01fc1a0 |= 1;	// SYS_SCS
  *(volatile unsigned int *)0x3fffc020 = 0x40000;
  *(volatile unsigned int *)0x3fffc030 = 0;
  *(volatile unsigned int *)0x3fffc034 = 0;
  while (1)
    ;
}

----------------------------------------------------------------------
LDSCRITは、

OUTPUT_FORMAT ("elf32-littlearm")
OUTPUT_ARCH (arm)

MEMORY {
       start_vector	: o = 0x00000, l = 0x20
       rom		: o = 0x00200, l = 0x10000
}

SECTIONS {
	 .start_vector :
	 {
直接B命令を埋め込み。これはどうもうまくELFファイルでロードフラグが
立てれなかったので強引に。二つ用意しているのはB命令が本当に効いている
のか確かめるため。
	 	LONG (ABSOLUTE (0xea00007e))	/* b 200 (func0)*/
/*	 	LONG (ABSOLUTE (0xea0000b4))*/	/* b 2d8 (func1)*/
	 } > start_vector

まだスタックもデータもBSSも使えません。
	 .text :
	 {
	 	 *(.text*)
		 *(.rodata*)
		 . = ALIGN (4);
	 } > rom
}

----------------------------------------------------------------------
Makefileは

SHELL	= /bin/sh
TOOLDIR	= /usr/local
GNUARCH	= arm-elf

PREFIX	= ${TOOLDIR}/bin/${GNUARCH}

AS	= ${PREFIX}-gcc
CPP	= ${PREFIX}-cpp
CC	= ${PREFIX}-gcc
C++	= ${PREFIX}-g++
LD	= ${PREFIX}-ld
OBJCOPY	= ${PREFIX}-objcopy
OBJDUMP	= ${PREFIX}-objdump
NM	= ${PREFIX}-nm
AR	= ${PREFIX}-ar
RANLIB	= ${PREFIX}-ranlib

CFLAGS = -fomit-frame-pointer

DEPEND_DIR	=	.deps
DEPEND_UPDATE	=	-Wp,-MD,$(DEPEND_DIR)/$(*F).P


.c.o:
	${CC} ${CFLAGS} ${DEPEND_UPDATE} -c -o $@ $<
.S.o:
	${AS} ${INCLUDES} ${ASFLAGS} ${DEPEND_UPDATE} -c -o $@ $<

OBJS	= a.o

all:	test

clean:
	rm -f ${OBJS} test test.elf test.hex

test:	${OBJS}
	${LD} -T ldscript -o $@.elf  ${OBJS}
	${OBJCOPY} -Oihex $@.elf $@.hex
	${OBJDUMP} -x $@.elf
	${OBJDUMP} -D $@.elf

DEPS_MAGIC := ${shell mkdir ${DEPEND_DIR} > /dev/null 2>&1 || :}
-include ${DEPEND_DIR}/*.P

----------------------------------------------------------------------
これでできたhexファイルをWindows VistaからFlash Magicを使って転送。
このプログラムではベクタ領域のチェックサムを調整していないのだけど、動いた。
Flash Magicが設定してくれている様子。


一番最初はベクタ領域全部0にしてテストした。これならチェックサムは0、0は
実質nopになるので本体の0x200までnopが続いて実行になる。



MCFAJ2戦富士の決勝、風来坊さんの撮影です。ありがとうございます。ここは、
ネッツ? だよね?









DRO01_IOのケースの続き。φ3のドリルで適当に打ち抜いた後にφ3のエンドミ ルでケガキ線まで切削。

ワークの固定は木にネジ止め。

完成。動作も問題なし。これでフライス、旋盤ともにDRO化完了です。長かった。

設置場所はここにしました。I/Oユニットは100mm x 100mm x 45mmと小さいので 場所は自由。ワークを見ながらちょっと上目で確認できて、それでいてそんな に切削油が飛びちらないあたりで。

インターフェース5月号の付録に、若松で買ってきた追加部品をハンダづけしま した。これはゆっくりやろう。かなりの手間数になりそう。




DRO01_IOのケースを作ります。DRO02はブルースモークだったけれど、これはブ
ラウンスモーク。ハンズにあった100mm x 100mmのアクリル板。

アクリルだとポンチは微妙なので、1mmのドリルでガイド穴を。アクリル相手なら 1mmでも余裕。

1mmの後、3mm、その後7mmにしたら割れてしまった。下の穴は3mm、5mm、6mm、 7mmで開けたもの。そこそこ成功。いざという時のために7mmのエンドミルも用 意してたのだけど、使わず。本当はドリルをロウソク型にするのがいいのだろ うとは思う。

例によって廃棄PCケースからケースを切り出します。折り目が台形なのはメタルブレーキで曲げる時のため。曲げ線から両側7mmづつよけておけばよい。

今迄とはちょっと違うデザインにしました。上面全部アクリルで、左右は別体 のパネルをネジ止めします。レトロモダンな感じ?



KOU@zaiさんの撮影、筑波選手権2戦、予選の写真です。ありがとうございます。
あの薄暗い中で。素晴しいです。



この頃は綺麗なカウルだった...TT。カウル補修しないと。久しく塗装してなかっ たうちに塗装部屋は木工部屋になってしまった。庭は菜園になってしまったし。 どうしようかな。シンナーって強烈に枯れるんだよね。

夜、菜園の見回りをしていたら、モンシロチョウが二匹トウモロコシで寝てい た。隣はチンゲンサイだからね。いつもチンゲンサイのまわりでとびまわって いる。卵も産みつけられて、青虫もいるのだけど、枯れない程度なら御自由に ということで駆除もしてません。
寝てる蝶は本当に無防備で、触っても起きません。

DRO01 I/Oユニットを進めました。H8/3694のBポートが4つ余っているので、こ こを取り出してジャンパで符号を逆転できるようにしてみました。
後はケース。いつも思うのだけどケースはもっと前に作るべき。この段階までに 外に出したRS-232Cポートの配線がプラプラしてハンダ付けし直すこと度々。
あともうちょっと、あともうちょっと、で同じ失敗をしてしまう。

秋葉原にいろいろ買い出し(ファクトリーギアにも)にいったついでに若松で Interfaceの付録のARM基板用の追加部品セット買ってきました。
このARM7TDMIベースのこれ、ちょっと仕様調べたら申し分ないね。H8じゃなく これでよかったのでは...という気がしている。実際いじってみてどうか。



ジャガイモを3株ほど収穫しました(スイカを植えるスペースを作るため)。
一株はアリにやられていた。探り掘りして、ちょくちょく食べていたので
少ないです。



種採り用チンゲンサイは全長80cm近くまで育った。まだまだ延々と花が咲く様 子。

まだ残っている苗達。スイカは育ちのいいのから定植したのだけど、苗の方が 良く育ってしまって困惑しています。根が喰われて枯れてしまったのもあり、 定植した中でなんとなく駐車場の脇に植えておいたのが一番良く育っていたり。 トマトはどの苗も割りと均等。花が咲いてからずっとポットに残しているのは 成長がストップして下の葉から枯れはじめている。

筑波2戦の転倒は、復帰以来はじめてのマジゴケ(今迄転倒もあったけどマシン トラブル)。ちょっと感慨深いです。思うに、フロントフォークのセットの悪さ だと思う。最終コーナーのチャタ対策でフロントは残ストが5mm程度(イニシャ ルかけるとチャタるので)。CXを思いっきり切り返すと、お釣りでウォブるのは わかっていて、マシンが直立になったところでフォークが落ちつくのを待って から倒しこみにしていた。熱くなって思いっきりやってしまった。もうちょっ とフォークのセットを残スト残す方向で試しておけばよかった。ちょっと伸び 減衰下げてみようかな。伸び減衰は高めなんだ。
転倒後、CXの走りを見学してたところ(ここはまたとない見学ポイント。またあっ たら困る)、定常円旋回をつなげるように走った方がいいのかしら。そしてスムー ズに。僕はダンロップに入った後で開けるところで割と直線に走っている。確 かに立ち上がりだめなんだ。スムーズというのも難しくて、ノンビリになっちゃ うんだよね。



怒涛の三日間が終わりました。あぁ明日はサーキットに行かなくていい。そん
な気分だ。最後はユンケルファンティに頼りました。アリナミンRは毎日飲んで
いました。


MCFAJ2戦富士、予選決勝通してタイムは2'15.695までと、まったくだめでした。
マシンがいい加減だったな。
予選は17:34。ロング過ぎになっていた。6速がほとんど意味なし状態。11000rpm までしか廻らないというか、11000rpmに落ちてそのまま。昔は6速が0.96だった (今は0.92)といえ、このファイナルできっちり走ったのだけど...。
決勝は16:33に変更。16:33にするとインフィールドが2速だとショートだけど3 速だとロングになってしまう。13コーナーは3速じゃないと無理。最終も2速だ とギャンギャン過ぎ(←これは僕がロング好きということもある) コカコーラも 3速じゃだめで4速じゃないとだめだけど、かなりの半クラに。
ストレートはどうだったかというと...思ったほど効果もなく。Max179.6km/h しか出ませんでした。昔は186,7km/hは出ていたのだけど。
これは筑波でも同じで昔、163,4km/hは出てたのに最近は160km/h未満。


なんと25番ピット(割り当て)をひとりで使いました。といってもハイエースリ アハッチでの作業に慣れてるので、基本はハイエース裏。ウォーマーと、カッ プ麺用のポットの電源にしか使わなかった。
ピット無しに慣れ過ぎて、使っていいとなってもオロオロしてしまう。

決勝、3周目に13コーナーでブーツを擦った時にブーツのチャックが全部開いて しまった。(新しく買ったブーツじゃなく前のブーツにしていた。これはチャッ クが半分壊れていて、ガムテープでとめていた)
開いたところが風圧でガバーっと開いてストレートは思いっきりステップ踏ん でないとブーツが脱げそうな勢い。たまにちょこちょこ左手でベルクロをつけ ようとするもうまくいかず。
黒旗出されませんように...と祈りながら、走ってました。やっぱりチャックが 壊れたブーツはだめね。

3位でした。



決勝9周目のCX切り返し後にフロントをすくわれて転倒リタイヤでした。後続の
選手も転倒させてしまい申しわけない。



ステップ、カウルステー、クラッチレバー、カウルを交換。キャブ、クランク室、 チェーン洗って、火入れの確認して明日の富士です。




特別スポーツ走行に行ってきました。筑波の走行は4/19のMCFAJ1戦以来。新し
いブーツを履いて出走したところ、これが問題。たまにステップから足が滑り
落ちるし、シフト感がつたわってこなくて、とても走り辛い。ナラシにツーリ
ングにでも行かないとだめかも。

タイムも出ず。
スタート練習を3本して、二本目の走行。明らかにパワーが落ちてる。6速入れ たら失速する感じ。ポツポツ降りはじめた雨もちょっと多くなってきたので途 中で終了、速攻で帰宅しました。
今日はテンションが低かった。スタート練習も、交差点の信号を見るかのよう な落ちつき。

新品ピストンがお釈迦。スタート練習の時にやっちゃったかな。ということで 明日は新品で予選となってしまった。ピストンがハズレでなければいいけど。

ピストン、スロットルワイヤ、タイヤ新品。
Rサス 92ノーマル。
1.8/1.8
15:34
10:05 490m 23.9℃ 69.7% 1004.1hPa 曇
best 8.9

14:10 489m 23.4℃ 71.9% 1002.9hPa 曇



新品スリックに履き替えて整備終了。また使うことないかもしれないけどね。
長靴と雨ガッパを買ってきて、雨対策は万全。(いつもグチャグチャになった運
動靴で、今度こそ長靴買わなくちゃと思いつつ、喉元過ぎればなんとやらで同
じ事を繰り返していた。)

計量は59kg。ギリギリの線だ。
今年一番の山場。長い三日間が初まるぞ。




朝起きたら、右手の人差指の根本から親指にかけての甲側が痺れていた。寝違
えたかな...と思っていたのだけれど、いつまでたっても感覚が戻らない。結局
今になっても直らない。

ここ数日右肩の肩凝りがひどくて首や肩をグリグリまわしていて神経を圧迫し てしまったのか...、それとも右肘の古傷か...(ちょっと右肘の裏は神経がなく なっている)、それともここ数ヶ月練習していた新技のペンまわしのせいか。 (時計廻り、空中5,6回転廻しを毎日練習していた。勢いをつけるために負担が 多い。)
動きには問題がないのでまぁいいか。
CR85はピストンまわりを全部新品にして、久々にスロットルワイヤーも交換し ました。後はオイル交換。まだまだ綺麗だったけど、今週末はハードだからね。

モニタ(同期プリミティブ もうちょっと別な名前だったらよかったのに。セマ フォはいいネーミングだった。これがシグナルなんて名前だったら苦労したは ずだ。モニタはタブレットなんて名前にすればよかったのでは?)
そのデバッグのために久々にUD01をひっぱりだしてきた。これも全部忘れた。 MD0,MD1,MD2/FWEの設定。このマシンはモード5で動かすので、この写真の 設定がノーマル。ROMの書き込みはこの状態からFWE/MD2のスイッチを上げる。
そしてモニタの使い方も忘れていてしばらく格闘してました...。デフォルトで は仮想ベクタテーブルが無効になっているので、ベクタテーブルを使うモジュー ルを読み込む前に'v'コマンドで有効にしないといけない。
ROM data: 0x5774-0x5d74
RAM data: 0xbf000-0xbf600 1536byte
bss: 0xbf600-0xbfdc8 1992byte
NMI return address|CCR: 800006da
delay_div=40
calibrated. delay_div=40
delay_div=40
calibrated. delay_div=40
H8/3052 ud01 Monitor Build Jun  3 2009 20:54:57
vector override: off
>> v
vector override on
>> l
~>Local file name? a.mot
2618 lines transferred in 5 seconds 
!
Read 58842 byte. success
Start address: 0x80000
stack_start: 0xfff10
ROM data: 0x84cbc-0x850bc
RAM data: 0xfe100-0xfe500 1024byte
bss: 0xfe500-0xff2c8 3528byte
NMI return address|CCR: 80080000
delay_div=120
calibrated. delay_div=120
>
もうちょっと思い出すように表示を変更しておきました。




DRO01_IO、DRO01と接続して動作するとこまで進めました。DRO01のプログラムも
DRO01_IOに都合のいいように多少変更しました。

D Z:::000
D X::-584
D X::-583
D Y:::000
こういう形でDRO01はDRO01_IOにコマンドを送ります。DRO01_IOは
zero 0
zero 1
zero 2
のようにゼロリセットコマンドをDRO01に送ります。

しかし、問題をみつけた。シリアルの入出力が激しくなると
>monitor_wait: [2] stealed lock from [1]
Asssertion failed. ../../kernel/monitor.c, 210 caller:0x2412
で止まってしまう、あるいはデッドロックしてしまう。どこかモニタ(同期プリ ミティブ) にバグがある。
ちょっと追い切れず、シェルにエコーバックしないモードをつけてごまかして しまった。エコーバックしたのは相手先で無効なコマンドになるので、しないのが 正しいのだけど。しても動かないとおかしい。ここは後で調べよう。
DRO01_IO(H8/3694)はRAM2KBなのに4スレッド。H8/Tinyで富豪的アプローチの限 界かな。
void
board_main (uint32_t arg)
{

  shell_command_register (&D_cmd);

  // Application thread.
  thread_t app_th = thread_create (app_tls, THREAD_STACKSIZE_DEFAULT, "display",
				   display_thread, 0);
  thread_start (app_th);

  shell_set_device (SERIAL, SERIAL, FALSE);
  shell_prompt ();
ここでDRO01からの入力を待って、登録された'D'コマンドならば下のD ()を実行。
  /* NOTREACHED */
}

void
display_thread (uint32_t arg)
{
  thread_priority (current_thread, PRI_HIGH);	// highest
  while (/*CONSTCOND*/1)
    {
LEDを点灯して、プッシュボタンをポーリングし続けるだけ。
      display ();
      poll_switch ();
    }
  // NOTREACHED
}

uint32_t
D (int32_t argc, const char *argv[])
{

  if (argc < 2)
    return -1;

DRO01からのコマンドからLCDに送り出すバッファを作成。
  prepare_digit_buffer (argv[1]);

  return 0;
}

void
display ()
{
#define	DELAY_USEC	1000
  int32_t i, j, k;
18桁のLEDダイナミック点灯。
  for (k = j = 0; k < 3; k++)
    {
      uint8_t *p = display_buffer[k];
      for (i = 0; i < 6; i++, j++)
	{
	  *PDR8 = p[i];
	  *commons[j].reg = commons[j].bit;
	  timer_sleep (DELAY_USEC);
ここで寝ている間にシェルが動く。
	  *commons[j].reg = 0;
	}
    }
}
$ tip hmon57600
connected
stack_start: 0xff80
ROM data: 0x2f90-0x3018
RAM data: 0xf780-0xf808 136byte
bss: 0xf808-0xff2c 1828byte
NMI return address|CCR: 8000039a
delay_div=252
calibrated. delay_div=250
---Monitor---
W ringbuffer
        lock : 
        event: 3(sci send) 1(root) 
U ringbuffer
        lock : 
        event: 
---Ready Queue---
<0>: 
<1>: 
<2>: 
<3>: 1 
---Thread Status---
id   pri(used/total)
[4] W 0 (96/256) display
[3] W 1 (110/256) sci send
[2] W 0 (122/256) sci recv
[1] R 3 (182/384) root
---Monitor---
W ringbuffer
        lock : 
        event: 3(sci send) 1(root) 
U ringbuffer
        lock : 
        event: 
---Ready Queue---
<0>: 
<1>: 
<2>: 
<3>: 1 
---Thread Status---
id   pri(used/total)
[4] W 0 (96/256) display
[3] W 1 (110/256) sci send
[2] W 0 (122/256) sci recv
[1] R 3 (198/384) root
zero 0
zero 0
zero 1
zero 1
zero 1
zero 1
zero 2
zero 2
zero 2
zero 2
~
[EOT]
$ 

MonotaRO(モノタロウ)
あわせて読みたい