5.デバッグ
H8プロジェクトとは、違いデバッグは難しいです。というのは、H8はマイクロコードで記述しており、基本的に1命令で命令シーケンスが完結します。つまり一命令のデバッグを行えば、命令間の影響は考えなくてよいのですが、パイプラインの場合、パイプライン中に5命令が存在し相互に影響があります。また、命令のフォワーディングを行っているので、さらに広範囲で影響します。
以上のように相互に影響するので最初から命令シーケンスを作るアセンブラやコンパイラがないとつらいのです。加えて、ディレイドブランチや遅延ロードが存在するので、その辺も勝手にNOPを入れてくれるものが必要です。
デバッグの手法としては、簡単なCソースを書いてデバッグしました。コンパイラは、Plasmaで使用したものを使います。また、プログラムの初期ローディングもPlasmaで作成したものを流用しました。
デバッグ中のWaveformview

デバッグ中のログ

5.1割り込みのデバッグ
割り込みコントローラは、現在実装していませんが、RS232Cの通信には、割り込みは必須なので、原始的な割り込み機構を搭載しています。将来割り込みコントローラを記述しようと思いますが、とりあえず、RS232Cでホスト端末と通信できればよいので、RX受信割り込みしか対応していません。
そのデバッグ方法は、UARTのエコーバックにより自分自身で割り込みを発生させるものです。
これで、のUARTと割り込みのテストベンチ記述量を削減できます。
割り込みの記述
通常、割り込みルーチンは、アセンブラで記述しますが、敢えてCで書いてみました。逆アセンブラで、使用レジスタや、スタック確保量を見てそれをFeedBackした記述にします。ルーチンの最後にあるだろう jr R31は実行されずに、Interrupt退避の jr R26が実行されてリターンすることになります。
void print_uart(unsigned char* ptr)//Verilog Test Bench Use
{
while (*ptr) {
*(volatile unsigned char*)uart_wport=*(ptr++);//FIFO容量まではOverflowしない
}
*(volatile unsigned char*)uart_wport=0x00;//Write Done
}
inline unsigned char read_uart()//Verilog Test Bench Use
{
return *(volatile unsigned char*)uart_rport;//受信FIFOはなし、すぐにサービスすること。
}
inline void set_interrupt_address()
{
*(volatile unsigned long*)int_set_address=(unsigned long)interrupt;//割り込みサービスルーチンのアドレスをセットする。
}
// インタラプトサービスルーチン
//#pragma interrupt
void interrupt(void)
{
char c;
char buf[10];
//割り込みサービス前処理
asm(" sw $a0,-32($sp)");//使用しているレジスタ退避
asm(" sw $v0,-28($sp)");
asm(" sw $v1,-24($sp)");
asm(" sw $a1,-20($sp)");
asm(" sw $s0,-16($sp)");
asm(" sw $s1,-12($sp)");
asm(" sw $s2,-8($sp)");
//割り込みサービス本体の開始
c=read_uart();//uart read port から1バイト読み込み
print("\n Got Interrupt:: val=");
buf[0]=c;
buf[1]=0;
print(buf);
print("\n\n");
//割り込みサービス終了処理
asm("lw $s0,-16($sp)");
asm("lw $s1,-12($sp)");
asm("lw $s2,-8($sp)");
asm("lw $v0,-28($sp)");
asm("lw $v1,-24($sp)");
asm("lw $a1,-20($sp)");
asm("lw $a0,-32($sp);");//使用したレジスタを復元
asm("lw $ra,36($sp);");//逆アセンブルリストを見て調整する。
asm("addiu $sp,$sp,40 ;");//逆アセンブルリストを見て調整する
asm("jr $26");//Return Interrupt
asm("nop");//遅延スロット
//
//逆アセンブルリストを見て調整する
//Interrupt 内部でサブルーチンをCALLしている場合、Return AddressをSTACKから元に戻す。
//スタック使用量の分を戻す
//Interuupt されて実行されなかった命令は、$26に入っている。
//上記のように書くと、アセンブラが遅延SLOTに置いてくれるようだ。
}
void main()
{
int i;
set_interrupt_address();//割り込みVector Set
print_uart("Hello! YACC world. Jul.7.2004\n\n");//UART TXを送信
...
|
5.2 long longは使えるか?
乗算器は、32x32になっていますが、64ビットの足し算や引き算はできるのでしょうか?キャリーフラグは、仕様上ありませんし、OverflowのTrapも実装していません。 これで、掛け算と足し算、引き算はできてしまっています。どなたかgccの吐くコードを解説していただけないでしょうか?
5.3 乗除算器のデバッグ
CでCheckします。YACC用GCCでないGCCで結果を比較し、合致していることを確認します。
適当な数の演算を行います。
以下は、RTLシミュレーションログです。
F:\xilinx\nahitafu\first_sample\yacc\yacc_test_ram4k.v(3)::yacc_test Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。 ------------- シミュレーションを開始します。-------------------- 105/31=00000003 [Hex] 3[Dec] -105/31=fffffffd [Hex] -3[Dec] (-105)/(-31)=00000003 [Hex] 3[Dec] (-105)/(31)=fffffffd [Hex] -3[Dec] (1050)/(-31)=ffffffdf [Hex] -33[Dec] 105%31=0000000c [Hex] 12[Dec] -105%31=fffffff4 [Hex] -12[Dec] (-105)%(-31)=fffffff4 [Hex] -12[Dec] (-105)%(31)=fffffff4 [Hex] -12[Dec] (1050)%(-31)=0000001b [Hex] 27[Dec] 2147483647/13213210=000000a2 [Hex] 162[Dec] (-2147483647)/1654760=fffffaef [Hex] -1297[Dec] (-2147483647)/(-14320)=000249cb [Hex] 149963[Dec] (+2147483647)/(-153420)=ffffc953 [Hex] -13997[Dec] 2147483647%14324320=00c8ce1f [Hex] 13159967[Dec] (-2147483647)%14320=ffffcb51 [Hex] -13487[Dec] (-2147483647)%(-1540)=fffffa95 [Hex] -1387[Dec] (+2147483647)%(-143220)=0000a7d7 [Hex] 42967[Dec] -17/(1)=ffffffef [Hex] -17[Dec] 117/(-1)=ffffff8b [Hex] -117[Dec] -(-13432234)/(-4323437)=fffffffd [Hex] -3[Dec] -(-143243243)/(743243)=000000c0 [Hex] 192[Dec] -123459*(-3454)=196ac3fa [Hex] 426427386[Dec] -1321*(73213)=fa3c417b [Hex] -96714373[Dec] +543541*(7213)=e9af0451 [Hex] -374406063[Dec] +1432*(-7322)=ff600290 [Hex] -10485104[Dec] -165*(-7232)=00123540 [Hex] 1193280[Dec] -(1321)*(-7111)=008f55df [Hex] 9393631[Dec] -(-1543)*(-743243)=bba4daf3 [Hex] -1146823949[Dec] |
YACCでないGCCのプログラムです。
実は、下のYACCソースから”をとっただけなのですが。
calculator_test(int a)
{
printf(" %d" ,a);
}
main()
{
calculator_test( 105/31 );
calculator_test( -105/31 );
calculator_test( (-105)/(-31) );
calculator_test( (-105)/(31) );
calculator_test( (1050)/(-31) );
calculator_test( 105%31 );
calculator_test( -105%31 );
calculator_test( (-105)%(-31) );
calculator_test( (-105)%(31) );
calculator_test( (1050)%(-31) );
calculator_test( 2147483647/13213210 );
calculator_test( (-2147483647)/1654760 );
calculator_test( (-2147483647)/(-14320) );
calculator_test( (+2147483647)/(-153420) );
calculator_test( 2147483647%14324320 );
calculator_test( (-2147483647)%14320 );
calculator_test( (-2147483647)%(-1540) );
calculator_test( (+2147483647)%(-143220) );
calculator_test( -17/(1) );
calculator_test( 117/(-1) );
calculator_test( -(-13432234)/(-4323437) );
calculator_test( -(-143243243)/(743243) );
calculator_test( -123459*(-3454) );
calculator_test( -1321*(73213) );
calculator_test( +543541*(7213) );
calculator_test( +1432*(-7322) );
calculator_test( -165*(-7232) );
calculator_test( -(1321)*(-7111) );
calculator_test( -(-1543)*(-743243) );
}
|
YACCのソースです。
Cコンパイラがあるとデバッグも容易です。電卓プログラムを流用しました。
//YACC Project on CYCLONE 4KRAM
//Jul.15.2004 Simple 32bit calculator
//
//Verilog Specific Routines
#define print_port 0x0ff0
#define print_char_port 0x0ff1
#define print_int_port 0x0ff2
#define print_long_port 0x0ff4
#define print_long_port 0x0ff4
//
#define uart_wport 0x0fff
#define uart_rport 0x0ffc
#define int_set_address 0x0ff8
#define BUFFER_SIZE 120
unsigned char * read_ptr;
char buffer[BUFFER_SIZE];//="abc" はNG
char result_buffer[8];//8+1
unsigned char sym;
unsigned char* char_ptr;
long term(void);
long factor(void);
long expression(void);
void calculator();
int volatile int_flag=0;//volatile は必須。ないとcalculatorに行かない
char buf[2];
#define ECHO
//#define DEBUG
void print_uart(unsigned char* ptr)//
{
while (*ptr) {
*(volatile unsigned char*)uart_wport=*(ptr++);
}
//*(volatile unsigned char*)uart_wport=0x00;//Write Done
}
void putc_uart(unsigned char c)//
{
*(volatile unsigned char*)uart_wport=c;
}
unsigned char read_uart()//Verilog Test Bench Use
{
return *(volatile unsigned char*)uart_rport;
}
void print(unsigned char* ptr)//Verilog Test Bench Use
{
#ifdef DOS
printf("%s ",ptr);
#else
while (*ptr) {
*(volatile unsigned char*)print_port=*(ptr++);
}
*(volatile unsigned char*)print_port=0x00;//Write Done
#endif
}
/*
void print_char(unsigned char val)//Little Endian write out 16bit number
{
#ifdef DOS
printf("%x ",val);
#else
*(volatile unsigned char*)print_char_port=(unsigned char)val ;
#endif
}
void print_short(short val)//Little Endian write out 16bit number
{
#ifdef DOS
printf("%x",val);
#else
*(volatile unsigned short*)print_int_port=val ;
#endif
}
*/
void print_long(unsigned long val)//Little Endian write out 32bit number
{
#ifdef DOS
printf("%x",val);
#else
*(volatile unsigned long*)print_long_port=val;
#endif
}
// インタラプトサービスルーチン
//#pragma interrupt
//0D/0A が着たら、0をbufferに書き込み、計算する。
//それ以外は、READ_PTRをUP
//Overflow だったらMessageOUT
void interrupt(void)
{
char c;
#define SAVE_REGISTERS (13*4)
asm("addiu $sp,$sp,-52 ;");//SAVE_REGISTERS
asm(" sw $a0,($sp)");//使用しているレジスタ退避
asm(" sw $v0,4($sp)");
asm(" sw $v1,8($sp)");
asm(" sw $a1,12($sp)");
asm(" sw $s0,16($sp)");
asm(" sw $s1,20($sp)");
asm(" sw $s2,24($sp)");
asm(" sw $a3,28($sp)");
asm(" sw $s4,32($sp)");
asm(" sw $s5,36($sp)");
asm(" sw $s6,40($sp)");
asm(" sw $s7,44($sp)");
asm(" sw $a2,48($sp)");
c=read_uart();//uart read port から1バイト読み込み
if ( c == 0x0a || c==0x0d )
{
*read_ptr = 0;//string 終端
read_ptr=buffer;//read_ptr 初期化
putc_uart(0x0a);
putc_uart(0x0d);
if (int_flag) print("PError!\n");
else int_flag=1;
} else if ( c == '\b' && read_ptr > buffer ){//バックスペース処理
putc_uart('\b');
read_ptr--;
}else if ( read_ptr>= buffer+BUFFER_SIZE){// overflow
//とりあえず
*read_ptr = 0;//string 終端
read_ptr=buffer;//read_ptr 初期化
print_uart("Sorry Overflow..!\n");
}else {//ポインタインクリメント
putc_uart(c);
*(read_ptr++) = c;
}
#ifdef DEBUG
print(buffer);
print("\n\n");
#endif
//使用したレジスタを復元
asm(" lw $a0,($sp)");
asm(" lw $v0,4($sp)");
asm(" lw $v1,8($sp)");
asm(" lw $a1,12($sp)");
asm(" lw $s0,16($sp)");
asm(" lw $s1,20($sp)");
asm(" lw $s2,24($sp)");
asm(" lw $a3,28($sp)");
asm(" lw $s4,32($sp)");
asm(" lw $s5,36($sp)");
asm(" lw $s6,40($sp)");
asm(" lw $s7,44($sp)");
asm(" lw $a2,48($sp)");
asm("addiu $sp,$sp,52 ;");//SAVE_REGISTERS
//割り込みルーチンをいじったら必ず調整すること
asm("lw $ra,20($sp);");//逆アセンブルリストを見て調整する。
asm("addiu $sp,$sp,24 ;");//逆アセンブルリストを見て調整する
asm("jr $26");//Return Interrupt
asm("nop");//遅延スロット
//
//逆アセンブルリストを見て調整する
//Interrupt 内部でサブルーチンをCALLしている場合、Return AddressをSTACKから元に戻す。
//スタック使用量の分を戻す
//Interuupt されて実行されなかった命令は、$26に入っている。
//上記のように書くと、アセンブラが遅延SLOTに置いてくれるようだ。
}
inline void set_interrupt_address()
{
*(volatile unsigned long*)int_set_address=(unsigned long)interrupt;
read_ptr=buffer;
}
void print_longlong(long long val)//Little Endian write out 32bit number
{
#ifdef DOS
printf("%x",val);
#else
*(volatile unsigned long*)print_long_port=val>>32;
*(volatile unsigned long*)print_long_port=val;
#endif
}
/*
void long_long_check_test()
{
long ii;
long n;
long i;
print("Testing longlong test");
n=10;
ii =-1;
for (i=2; i<=n;i++){
ii -=i;
}
print("1+2+3...+100=");
print_longlong(ii);
print("\n");
print("n*(n+1)/2=");
print_longlong(-n*(n+1)/2);
}
*/
/*
void mod_test(long i)
{
if (i<0) i=-1;
print_long(i%10);
print_long(i/10);
}
*/
void getsym()
{
while ( *char_ptr==' ' ||
*char_ptr=='\n' ||
*char_ptr=='\r' ) char_ptr++;
if (*char_ptr ==0) {
sym=0;
}else {
sym=*(char_ptr++);
}
}
inline void init_parser()
{
char_ptr=buffer;
getsym();
}
long evaluate_number(void)
{
long x ;
x=sym-'0';
while(*char_ptr >='0' && *char_ptr <='9') {
x = x * 10 + *char_ptr - '0';
char_ptr++;
}
getsym();
return x;
}
long expression(void)
{
long term1,term2;
unsigned char op;
op=sym;
if (sym=='+' || sym=='-') getsym();
term1=term();
if (op=='-') term1=-term1;
while (sym=='+' || sym=='-') {
op=sym;
getsym();
term2=term();
if (op=='+') term1= term1+term2;
else term1= term1-term2;
}
return term1;
}
long term(void)
{
unsigned char op;
long factor1,factor2;
factor1=factor();
while ( sym=='*' || sym=='/' || sym=='%'){
op=sym;
getsym();
factor2=factor();
switch (op) {
case '*': factor1= factor1*factor2;
break;
case '/': factor1= factor1/factor2;
break;
case '%': factor1= factor1%factor2;
break;
}
}
return factor1;
}
inline long parse_error()
{
print_uart("\n parse error occurred\n");
return 0;
}
long factor(void)
{
int i;
if (sym>='0' && sym <='9') return evaluate_number();
else if (sym=='('){
getsym();
i= expression();
if (sym !=')'){
parse_error();
}
getsym();
return i;
}else if (sym==0) return 0;
else return parse_error();
}
/* 文字列の並びを逆順にする */
char *strrev(char *s) {
char *ret = s;
char *t = s;
char c;
while( *t != '\0' )t++;
t--;
while(t > s) {
c = *s;
*s = *t;
*t = c;
s++;
t--;
}
return ret; /* 並べ替えた文字列の先頭へのポインタを返す */
}
/* 整数を文字列に変換する */
void itoa(int val, char *s) {
char *t;
int mod;
if(val < 0) {
*s++ = '-';
val = -val;
}
t = s;
while(val) {
mod = val % 10;
*t++ = (char)mod + '0';
val /= 10;
}
if(s == t)
*t++ = '0';
*t = '\0';
// print(s);
strrev(s);
// print(s);
}
void calculator()
{
long result;
//パーサ初期化
init_parser();
//計算
result=expression();
//結果表示
#ifdef DEBUG
print("\n");
print(buffer);
print("=");
print_long(result);
print("[Hex] ");
itoa(result,result_buffer);
print(result_buffer);
print("[Dec]\n");
#else
print_uart(buffer);
putc_uart('=');
print_uart(result_buffer);
putc_uart(0x0a);
putc_uart(0x0a);
putc_uart(0x0d);
#endif
}
void strcpy(char* dest,char* source)
{
char* dest_ptr;
dest_ptr=dest;
while(*source) {
*(dest++) =*(source++);
} ;
*dest=0;//Write Done
}
void calculator_test(char* ptr)
{
strcpy(buffer,ptr);
calculator();
}
void main()
{
unsigned i;
long long k;
set_interrupt_address();
calculator_test("105/31");
calculator_test("-105/31");
calculator_test("(-105)/(-31)");
calculator_test("(-105)/(31)");
calculator_test("(1050)/(-31)");
calculator_test("105%31");
calculator_test("-105%31");
calculator_test("(-105)%(-31)");
calculator_test("(-105)%(31)");
calculator_test("(1050)%(-31)");
calculator_test("2147483647/13213210");
calculator_test("(-2147483647)/1654760");
calculator_test("(-2147483647)/(-14320)");
calculator_test("(+2147483647)/(-153420)");
calculator_test("2147483647%14324320");
calculator_test("(-2147483647)%14320");
calculator_test("(-2147483647)%(-1540)");
calculator_test("(+2147483647)%(-143220)");
calculator_test("-17/(1)");
calculator_test("117/(-1)");
calculator_test("-(-13432234)/(-4323437)");
calculator_test("-(-143243243)/(743243)");
calculator_test("-123459*(-3454)");
calculator_test("-1321*(73213)");
calculator_test("+543541*(7213)");
calculator_test("+1432*(-7322)");
calculator_test("-165*(-7232)");
calculator_test("-(1321)*(-7111)");
calculator_test("-(-1543)*(-743243)");
putc_uart(0x0a);
putc_uart(0x0d);
print_uart("Welcome to YACC World.Jul.15.2004 www.sugawara-systems.com");
putc_uart(0x0a);
putc_uart(0x0d);
print_uart("YACC>");
label:
if (int_flag){
int_flag=0;
calculator();
print_uart("YACC>");
}
goto label;
// mod_test(109);
// long_long_check_test();
// strcpy(buffer,"2+4");
print("$finish");
}
|