delayを実装しました。今迄内蔵RAMで動かしていた時は
ハードの設計に失敗したよ。このボードはSRAM 256Kビット SRM2B256SLMX55 (55ns)を8bitで接続してモード5(1Mアドレス)。高速SRAM 4Mビット CY7C1041DV33 (10ns)を16bitで接続して2ステートアクセス、モード6(16Mアド レス)にしておけばよかった。0.8mmピッチのハンダ付けにビビったというのが 最初の理由だったのだけど...。
しかたないのでキャリブレーションすることにしました。
キャリブレーション可能なdelayルーチンの核はこれ。
このルーチンをループせずに抜けるだけでも、拡張RAMで5.76us、内蔵RAMで3.36usかかる ので、10us以下はインラインアセンブラでnop差しこんだ方がいい。
mulxu,divxuを使うのでdelayの引数は16bitに制限されてしまう。32bitになると CPU命令で処理できなく、udivsi3やmulsi3を呼ぶことになるので、それはオーバーヘッドの点で避けたい。(呼ぶとさらに15usecくらいかかる)
こうなるとusec単位のdelayだけでは足りないので、usecオーダーのudelayとmsecオーダーのmdelayに分けることにしました。
キャリブレーションは、
次は割り込み。強制的にON/OFFするのは
さらに、現在の割り込み禁止/許可状態の如何にかかわらず、割り込みを禁止し て、元に戻すルーチンを作っておきます。
.globl _delay_2state _delay_2state: /* one loop 24cycle: 25Mhz-> .96us */ a: sub.l #1, er0 /* 6 */ nop /* 2 */ nop /* 2 */ nop /* 2 */ nop /* 2 */ nop /* 2 */ nop /* 2 */ nop /* 2 */ bne a /* 4 */ rts /* 10 */これでOKだった。H8/300Hシリーズプログラミングマニュアルの2.6命令実行ステート章の 式と表からステート数はみつもれる。この場合、内蔵RAMが16bit 2ステートなので nopは2ステートで実行できるのだけれど、拡張RAMは8bit 3ステートなので、nopの実行に 6ステートもかかってしまう。なのでこのdelayでは3倍の時間がかかってしまうのだ。
ハードの設計に失敗したよ。このボードはSRAM 256Kビット SRM2B256SLMX55 (55ns)を8bitで接続してモード5(1Mアドレス)。高速SRAM 4Mビット CY7C1041DV33 (10ns)を16bitで接続して2ステートアクセス、モード6(16Mアド レス)にしておけばよかった。0.8mmピッチのハンダ付けにビビったというのが 最初の理由だったのだけど...。
しかたないのでキャリブレーションすることにしました。
キャリブレーション可能なdelayルーチンの核はこれ。
/* void __delay (uint16_t r0, uint16_t r1, uint16_t r2) */ ___delay: mulxu.w r0, er2 /* r2 = r0 * r2 */ divxu.w r1, er2 /* r2 = r2 / r1 */ dloop: sub.w #1, r2 bpl dloop rtsmulxu.wは16bitどうしのかけ算を32bitのレジスタに入れてくれる命令。 divxu.wは32bitを16bitで割って、商を下位16bitに、余りを上位16bitに入れて くれるという命令。実行は20ステート。コンパイラだとうまくdivxu.wを使えな いのでアセンブラで。
このルーチンをループせずに抜けるだけでも、拡張RAMで5.76us、内蔵RAMで3.36usかかる ので、10us以下はインラインアセンブラでnop差しこんだ方がいい。
mulxu,divxuを使うのでdelayの引数は16bitに制限されてしまう。32bitになると CPU命令で処理できなく、udivsi3やmulsi3を呼ぶことになるので、それはオーバーヘッドの点で避けたい。(呼ぶとさらに15usecくらいかかる)
こうなるとusec単位のdelayだけでは足りないので、usecオーダーのudelayとmsecオーダーのmdelayに分けることにしました。
キャリブレーションは、
extern uint16_t DELAY_CNT;
ここにキャリブレーションされた値が入る。
uint16_t __delay (uint16_t, uint16_t, uint16_t);
#define udelay(n) __delay((n), DELAY_CNT, 125)
125というマジックナンバーは計測に25MHz/2(8/100 usec)を使ったので、式から
わかリやすいところで。
uint16_t DELAY_CNT;
void
delay_calibrate (bool verbose)
{
uint16_t t0, t1;
*ITU_TSTR = 0; // All timer stop.
// Internal clock/2, Don't clear TCNT.
*ITU0_TCR = ITU__TCR_ICLK2; // .08us (CPU 25MHz) ->OVF 5.242 msec
*ITU0_TIOR = 0; // no I/O
*ITU0_TIER = 0; // no interrupt.
__timer_start ();
__delay (0, 1, 1);
t0 = __timer_stop ();
ここで、サブルーチンに入って帰ってくるまでのオーバーヘッドを測定。
__timer_start ();
__delay (1000, 1, 1);
t1 = __timer_stop ();
DELAY_CNT = (t1 - t0) / 100;
オーバーヘッドを抜いたとこで計算。
1000回のループで t1-t0 カウント
1カウント0.8us
1ループは(8*(t1-t0))/100000
1usecにするには100000/(8*(t1-t0))まわす。
->125 / ((t1-t0)/100)
}
extern inline void
__timer_start ()
{
*ITU0_TCNT = 0;
*ITU_TSTR |= ITU_TSTR_STR0;
}
extern inline uint16_t
__timer_stop ()
{
uint16_t t = *ITU0_TCNT;
*ITU_TSTR &= ~ITU_TSTR_STR0;
return t;
}
void
mdelay (uint32_t n)
{
while (n--)
udelay (1000); //1msec
}
これでキャリブレーションすると、拡張RAM上でDELAY_CNTは120(なんとか125よ
り小さいのでよかった。)、内蔵メモリ上で40。
これで、delayはなんとかなったけれども、ここまでタイミングが違うと開発段階
からロムに移した時のタイミングプロブレムがとても心配です。
とりあえずdelayはこれでいいかな。
次は割り込み。強制的にON/OFFするのは
#define intr_enable() asm volatile ("andc.b #0x7f, ccr")
#define intr_disable() asm volatile ("orc.b #0x80, ccr")
これでいい。H8の命令セットはなかなか便利です。ただ一つ疑問なのが1ビット
シフトの命令しかないところだ。
さらに、現在の割り込み禁止/許可状態の如何にかかわらず、割り込みを禁止し て、元に戻すルーチンを作っておきます。
.h8300h
.section .text
.global _intr_suspend
/*
uint8_t
intr_suspend ()
{
r0 = *ccr & 0x80;
*ccr |= 0x80;
return r0;
}
*/
_intr_suspend:
stc.b ccr, r0l
and.b #0x80, r0l
orc #0x80, ccr
rts
.global _intr_resume
/*
uint8_t
intr_resume (uint8_t r0)
{
r1 = *ccr & 0x7f;
r0 |= r1;
*ccr = r0;
return r0;
}
*/
_intr_resume:
stc.b ccr, r1l
and.b #0x7f, r1l
or.b r1l, r0l
ldc.b r0l, ccr
and.b #0x80, r0l
rts
このくらいあれば十分かな。
