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