081226

|


割り込みの入口と出口のアセンブラのマクロを作ろうとしたのだれど、ステートメント
のセパレータが必要だ。h8300-hms-binutils/work/binutils-2.12.1/gas/doc/c-h8300.texiによると
@samp{$} can be used instead of a newline to separate statements.
となっているので$を使ってみてもだめ。 h8300-hms-binutils/work/binutils-2.12.1/gas/config/tc-8300.cを見ると
const char comment_chars[] = ";";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = "";
セパレータないの!? .....マクロ組めない。仕方ないので、該当部分を #include することにしました。
	.h8300h
	.section .text
	.globl _sci1_rxi
_sci1_rxi:
#include <asm/intr_enter.s>
	mov.l	@_sci_recv_asm_p, er0
	mov.b	@0xbd:8, r1l
	mov.b	r1l, @(0x04, er0) ; sci_recv_asm_p->data = *SCI1_RDR;
	bclr	#6,@0xbc:8	; *SCI1_SSR &= ~SSR_RDRF

	mov.l	@(0x00, er0), er0 ; r0l = sci_recv_asm_p->th
	jsr	_thread_wakeup	; (id)

	sub.l	er0, er0	; dont' rotate ready queue.
	jsr	@_thread_context_switch
#include <asm/intr_exit.s>
こんな感じで。intr_enter.s, inter_exit.sはこのOSのスレッドシステムのカ レントスレッドのレジスタ領域にレジスタを退避して、exitではスケジューリ ングし直します。このルーチンはSCI1のホストからの入力をとるところです。 データを保存して、このデータを処理するスレッドを起こします。処理するス レッドは割り込み後処理用の一番優先度の高いキューにつながれるので、割り 込み終了後はまず先にそれが動いて、寝た所でもう一度スケジューリングされ ます。(そこまでに割りこみからスケジュールの変更がなければ、ここで割り込 まれたスレッドが動きはじめます)
インラインアセンブラの場合はセパレータは'\n'でいけます。
  // Jump to main with changing stack to thread local storage.
  __asm volatile ("mov.l %0, sp \n jmp @%1"::
		  "r"(current_thread->stack_bottom), "r"(board_main));
これはスタートアップが終了して、スタックをスレッドローカルストレージに 変更して、アプリのメインにジャンプするところです。
今日はシリアルコンソールを書きあげました。効率の面と、リソースの消費量 では?だけれど、これはスレッドシステムのデバッグを兼ねてます。
#define	SCI_FIFO_SIZE	16
FIFOサイズはちょっと小さめ(SCI自体にFIFOがないし)。これでリングバッファ
をいじめます。

#define	SCI_STACK_SIZE	THREAD_STACK_DEFAULT
THREAD_STACK_DEFAULTは256。256Byteあれば今のとこ十分。

STATIC struct sci_thread
{
  uint8_t tls[THREAD_STACK_SIZE (SCI_STACK_SIZE)];
  uint8_t buf[RINGBUFFER_SIZE (SCI_FIFO_SIZE)];
  ringbuffer_t rb;
  thread_t th;
  bool started;
}
  sci_recv __attribute ((aligned (4))),
  sci_send __attribute ((aligned (4)));

スレッドローカルストレージ(thread local storage tls)は、スレッドを登録
する側が確保することにしました。スレッド毎にスタックの大きさを変えたい
こともあるし。tlsの先頭は4byteアラインである必要があるので__attribute
((aligned(4)))で確定させます。

受信、送信で同時に待ちに入ることがあるので別スレッドです。(この時点でか
なり富豪主義的です)

STATIC struct sci_recv_asm
{
  thread_t th;		// 0x00
  uint8_t data;		// 0x04
} sci_recv_asm;
struct sci_recv_asm *sci_recv_asm_p = &sci_recv_asm;	// global for asm.

割りこみハンドラはアセンブラで書くので、そこからアクセスする変数はアク
セスしやすいようにまとめておきました。別にしておけば本体のストラクチャ
が変更されたら、アセンブラ側でオフセットを変更しないといけないことから
逃れられるので

STATIC void sci_recv_thread (uint32_t);
STATIC void sci_send_thread (uint32_t);
STATIC void sci_thread_start (struct sci_thread *, void (*)(uint32_t),
			      const char *);
STATIC void sci_buf_putc (int8_t);
STATIC uint8_t sci_buf_getc (void);
void (*sci_putc)(int8_t);
uint8_t (*sci_getc)(void);

void
sci_boot_console_init ()
{
これはブートして一番最初に呼ばれて、printfを有効にするルーチン。
  //  console for booting. don't buffered.

  /* 115200bps 8N1 */
  *SCI1_SCR = 0;	/* CKS1=0 CKS=0 : 1/1 clock (n = 0)*/
  *SCI1_SMR = 0;	/* 8N1 */
  //  *SCI1_BRR = 0xd;	/* 25*1000000/(64*2^(2*0 - 1)*57600) - 1 = 12.56 ->0xd*/
  *SCI1_BRR = 6;	/* 25*1000000/(64*2^(2*0 - 1)*115200) - 1 = 5.78 */
  //  udelay (18);		/* wait 1bit cycle 1/57600*1000000=17.36us */
  udelay (9);		/* wait 1bit cycle 1/115200*1000000=8.68us */
  *SCI1_SCR |= (SCR_TE | SCR_RE);

H8/3052 25MHzなら115200bpsでいけます。

  sci_putc = isci_putc;
  sci_getc = isci_getc;

まだスレッドは使えないので、直にレジスタをポーリングするルーチンを
printfに供出します。スレッドが使えるようになっても割り込みコンテキスト
からは待ちに入ることはできないので、そこからはこのisci_putc, isci_getc
を使ってprintfします。
}

extern inline void
sci_putc1 (int8_t c)
{
ポーリングによるシンプルな書きこみです。

  while ((*SCI1_SSR & SSR_TDRE) == 0)
    ;
  *SCI1_TDR = c;
  *SCI1_SSR &= ~SSR_TDRE;
}


void
isci_putc (int8_t c)
{

CRLFの変換をいれます。このあたりのラインディシプリンもまた泥沼の入口。
でもここには絶対に足を踏み入れません。踏み入れたが最後、通信するのにま
ずは呪文を延々と唱えないといけなくなる。
  cfsetispeed(&t, B57600);
  cfsetospeed(&t, B57600);
  t.c_cflag &= ~(CSIZE|PARENB);
  t.c_cflag |= CS8;
  t.c_iflag &= ~(ISTRIP|ICRNL);
  t.c_oflag &= ~OPOST;
  t.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO);
  t.c_cc[VMIN] = 1;
  t.c_cc[VTIME] = 0;
こんなような...

  if (c == '\n')
    sci_putc1 ('\r');
  sci_putc1 (c);
}

uint8_t
isci_getc ()
{
  uint8_t c;

これも素直なポーリングによるホストからの入力を受けとるルーチンです。こ
れはホストからの入力を延々とポーリングしているので効率悪いけれど、これ
が必要な時も少なからずあります。

  while (((c = *SCI1_SSR) & (SSR_RDRF | SSR_ERR_BITS)) == 0)
    ;
  if (c & SSR_ERR_BITS)
    {
      *SCI1_SSR &= ~(SSR_RDRF | SSR_ERR_BITS);
      return 0;
    }

  c = *SCI1_RDR;
  *SCI1_SSR &= ~SSR_RDRF;

  return c;
}

void
sci_console_init ()
{
  // buffered console. need threading support.
スレッドシステムが起動したら、バッファリングするputc, getcにしてみます。

  sci_thread_start (&sci_recv, sci_recv_thread, "sci recv");
  sci_thread_start (&sci_send, sci_send_thread, "sci send");
  sci_recv_asm_p->th = sci_recv.th;
  thread_priority (sci_recv.th, PRI_HIGH); // this is interrupt thread.
ホストからの入力は割り込みでとるので、それを処理するスレッドは割り込み
スレッドとして、一番高い優先度とします。

  // Install buffered putc/getc.
  sci_putc = sci_buf_putc;
  sci_getc = sci_buf_getc;

  // Enable 'Receive data full' and 'Receiver error' interrrupt.
  *SCI1_SCR |= SCR_RIE;
受信データがあふれた時(と受信に失敗した時)の割り込みだけとります。

  //  *SCI1_SCR |= SCR_TIE | SCR_TEIE;
}

void
sci_thread_start (struct sci_thread *st, void (*func)(uint32_t),
		  const char *name)
{
  memset (st, 0, sizeof (struct sci_thread));

  st->rb = ringbuffer_init (st->buf, SCI_FIFO_SIZE);
受信、送信それぞれのリングバッファを用意します。

  st->th = thread_create (st->tls, SCI_STACK_SIZE, name, func, (uint32_t)st);
  thread_start (st->th);
スレッドを作ってスタートします。

  // make sure that ringbuffer_read is waiting.
  while (!st->started)
    thread_rotate_ready_queue (); // XXX wrong -uch

モニタは先にWAIT、次にSIGNALなので、WAITになるまで待ちます。ここで
rotate_ready_queueなのは、このスレッドと、作ったスレッドの優先度が同じ
であることによっています。優先度が同じなら同じレディキューなので、まわ
すことでいつかはターゲットのスレッドに制御が移って、そこでWAITで寝るこ
とで、ここに戻ってくることを期待しています。
 セマフォで待てばいいんだけどね。やっぱりセマフォは必要かな。

}

void
sci_recv_thread (uint32_t arg)
{
  struct sci_thread *st = (struct sci_thread *)arg;
  int s;
  uint8_t c;
  int8_t cr = '\n';

  IPRINTF ("RECV%x\n", arg);
  st->started = TRUE;
  while (/*CONSTCOND*/1)
    {
      thread_sleep (st->th);
      s = intr_suspend ();
      c = sci_recv_asm_p->data;
      intr_resume (s);

割り込みハンドラでストアされたデータをコピーします。割り込みハンドラと
共有データなので割り込みでロックします。本当は、割り込みハンドラからこ
こにくるまでにもう一回割り込みがくるかもしれないので、ここで次の割りこ
みをイネーブルしないといけないのだけど、それはそうしないといけないよう
になったら。

      ringbuffer_write (st->rb, &c, 1);
      if (c == '\r')
	ringbuffer_write (st->rb, &cr, 1);

リングバッファにつっこみます。フルだったらここでずっと寝る。割りこみ
ハンドラから起床されても起きないので、データは落ちます。

    }
}

void
sci_send_thread (uint32_t arg)
{
  struct sci_thread *st = (struct sci_thread *)arg;
  uint8_t buf[SCI_FIFO_SIZE];
  int i;

  IPRINTF ("SEND%x\n", arg);
  st->started = TRUE;
  while (/*CONSTCOND*/1)
    {
      size_t sz = ringbuffer_read (st->rb, buf, SCI_FIFO_SIZE);
      for (i = 0; i < sz; i++)
	sci_putc1 (buf[i]);
    }

ここはリングバッファに入ってたら起きるので、それを書くだけです。
ringbufferにwaterlinとflushを入れたい...。
}

void
sci_buf_putc (int8_t c)
{
  int8_t cr = '\r';

  if (c == '\n')
    ringbuffer_write (sci_send.rb, &cr, 1);
  ringbuffer_write (sci_send.rb, &c, 1);
}

uint8_t
sci_buf_getc ()
{
  uint8_t c;

  if (ringbuffer_read (sci_recv.rb, &c, 1) == 1)
    return c;

  return 0;
}
バッファ版のputcとgetcはリングバッファのアクセスのみです。

スレッドシステムまわりも全体的にいじりました。スレッド、モニタは呼び出 し側からリソースを設定することにしたので、数の限界はなくなりました。最 初そうしなかったのは、動的に登録だとそれぞれをリストでとっておかないと いけないのが最初は辛いかなと思ったから。
トグルスイッチの割り込みで、スレッド、モニタの状況もわかるようにしました。
---Ready Queue---
<0>: 
<1>: 
<2>: 
<3>: 7 
---Thread Status---(remain/total)
[7] R 3 (88/256) button polling
[6] W 1 (104/256) button 2
[5] W 1 (100/256) button 1
[4] W 1 (100/256) button 0
[3] W 1 (144/256) sci send
[2] W 0 (164/256) sci recv
[1] W 3 (52/256) root
CCR_PC=82b6a
---Monitor---
W button
        lock : 
        event: 4(button 0) 
U ringbuffer
        lock : 
        event: 
W ringbuffer
        lock : 
        event: 3(sci send) 
W ringbuffer
        lock : 
        event: 1(root) 7(button polling) 
スレッドスタックをどこまで使っているかの見積もりは、スレッド作成時にス タックには0xaaを書きこんでおいて、時折りスレッドトップから0xaaがどこま で続いてるかをカウントして調べています。
これでダンマリ状況の状態を探れます。
なんとかこれでいいかな。

ということで次の段階に。ケースの中から伸びてきている線が端子につながって いるのは、LCD用のクロック。これが何KHzなのかを調べます。この端子からは コンパレータ経由で3.3Vになって、IRQ5につながれています。
ここで基板の設計ミスにぶちあたりました。外部電源を入れてコンパレータから 信号が出力されている状態だと、シリアルの信号をじゃまして、プログラムが ロードできなかったりします。
 H8/3052 Advanced Mode Monitor Ver. 3.0A
 Copyright (C) 2003 Renesas Technology Corp.

: l
~>Local file name? test.mot
1910 lines transferred in 4 seconds 
!
  ********  Check Sum Error  ********  
: g

プログラムをロードする前に外部電源を切って、ロードし終わったら外部電源 を入れて、それから実行しないとだめなのだ....。電源別のスイッチにしてお いてよかったよ。
この信号は
uint16_t la_t0;

void
la_test ()
{
  *ITU_TSTR = 0;	// All timer stop.
  *ITU0_TCR = ITU__TCR_ICLK1;
  *ITU0_TIOR = 0; // compare match. don't output.
  *ITU0_TIER = ITU__TIER_OVIE;
  *ITU0_TCNT = 0;
  *ITU_TSTR |= ITU_TSTR_STR0;	// timer 0 start
  *ISCR |= 0x30;		// Falling edge.
  *IER |= 0x30;
  intr_enable ();

  while (1)
    {
      IPRINTF ("%d\n", (uint32_t)la_t0);
    }
}

	.globl _irq5
_irq5:
	mov.l	er0, @-sp
	mov.w	@0xff68:16, r0
	mov.w	r0, @_la_t0	; la_t0 = *ITU0_TCNT
	sub.w	r0, r0
	mov.w	r0, @0xff68:16	; *ITU0_TCNT = 0
	bclr	#2, @0xfff67:8	; *ITU0_TSR &= ~ITU__TSR_OVF
	mov.l	@sp+,er0
	rte
これで計測。結果は
13875
13883
13844
13869
13887
13881
13852
13861
13851
13878
13863
13900
13866
13869
13869
13875
13871
13852
13869
13869
13869
13882
13844
カウンタはシステムクロックでまわしているので、25MHz. 13850カウントで554usec.→1.8KHz。まぁいいとこだ。このLCDは1KHz-3KHzだから。
ちょこっと作るつもりだったのが結局全力投球で書いてしまったRTOSは これ