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"); } |