目次

ブロックテストとシミュレータ

 基板に部品を半田付けしながら、ブロックごとに動作を
 テストしました。そのプロセスを備忘録として残します。

 測定機器としてアナログとデジタルのマルチメータを使います。

 デジタルマルチメータには、周波数カウンタがあるので
 クロックジェネレータの出力周波数を確認するのに利用
 しました。



 アナログマルチメータは、プログラムカウンタの出力を
 測定するのと、クロックジェネレータの出力を針の動き
 で見るのに使います。



 他にArduino基板を1枚使いました。



 プログラムカウンタや入力に与える信号を生成します。


電源

 モニタLEDがあるので、電池を接続し点灯を確認しました。  電池を接続して、2枚の基板を6ピンで接続すると、各基板  上のLEDが点灯します。

リセット

 タクトスイッチを利用して、nRST、RSTの電圧レベルを  確認しました。  出力回路にLEDがあるので、リセット時にLEDが消灯する  ことで動作確認できたとしました。  この動作テストに利用したプログラムは、以下。 0 MOV B,15 1 OUT B 2 JMP 1  常に出力に2進数で"1111"を出力するので  LEDが点灯し続けます。リセットで、全部  のレジスタをクリアするので、LED消灯に  なります。

クロックジェネレータ

 マルチメータが周波数カウンタを持っているので  周波数カウンタモードにして周波数を測定しました。  10Hzクロック  1Hzクロック

レジスタファイル

 レジスタAからCは、入力4ビットのうちの1ビットを0V  に接続し、ロード端子に'L'を与えて値を記憶させます。  マルチメータを電圧モードにし、4ビットの出力電圧を  測定して、動作を確認しました。  レジスタDは、PC(Program Counter)になっているので  クロック端子に、クロックジェネレータからクロック  を与え、各出力ビットが0と1を繰り返すことをアナログ  マルチメータで観測します。

出力回路

 次のプログラムをROMエミュレータに転送し、1Hzのクロックを  与えてLEDの点灯パターンで確認しました。 0 MOV A,0 1 MOV B,A 2 OUT B 3 ADD A,1 4 JMP 1  レジスタAをカウンタで使い、0から15までカウントするので  4ビットの出力でLEDの点灯パターンが変わっていきます。

入力回路

 次のプログラムをROMエミュレータに転送し、10Hzのクロックを  与えて、LEDの点灯パターンで確認しました。 0 IN A 1 MOV B,A 2 OUT B 3 JMP 0  4ピンにクリップワイヤーで、HかLレベルを与えました。

セレクタ

 テストクリップを用意し、マイコンに制御信号とデータを与えます。  制御信号とデータは、Arduinoから与えます。  スケッチは以下。 #include <MsTimer2.h> #define OFF 0 #define ON OFF+1 #define LED_BIT 5 /* function prototype */ void rs_putchar(char x); void rs_puts(char *x); void crlf(); void show_help(); void update_trigger(); byte get_hex(char x); /* global variables */ byte xcnt ; byte uflag ; byte sindex ; byte sbuf[8] ; byte cmd ; byte xadr ; byte xdat ; void setup() { /* initialize serial port */ Serial.begin(9600); sindex = 0 ; /* set initial state */ PORTB = 0x00 ; PORTC = 0x00 ; PORTD = 0x01 ; /* set pin directions */ DDRB = 0xff ; DDRC = 0xff ; DDRD = 0xfe ; /* clear flags */ uflag = OFF ; /* initialize */ xcnt = 0 ; /* 200ms period */ MsTimer2::set(200,update_trigger); /* enable */ MsTimer2::start(); } void loop() { /* serial handling */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* new line */ crlf(); /* get command */ cmd = *(sbuf+0) ; /* judge */ if ( cmd == '?' ) { show_help(); } /* impress */ if ( cmd == 'P' ) { /* get selector */ xadr = get_hex( *(sbuf+1) ) ; /* get data */ xdat = get_hex( *(sbuf+2) ) ; /* impress selector */ PORTB &= 0xf0 ; PORTB |= (xadr & 0x03); /* impress data */ PORTC = (xdat & 0x0f) ; } } } void rs_putchar(char x) { Serial.write(x); } void rs_puts(char *x) { while ( *x ) { rs_putchar(*x); x++ ; } } void crlf(void) { rs_putchar('\r'); rs_putchar('\n'); } void show_help(void) { rs_puts("? help"); crlf(); rs_puts("P impress data"); crlf(); } void update_trigger() { PORTB &= ~(1 << LED_BIT); if ( xcnt & ON ) { PORTB |= (1 << LED_BIT); } xcnt++ ; } byte get_hex(char x) { byte result ; result = 0 ; if ( '0' <= x && x <= '9' ) { result = x - '0' ; } if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; } if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; } return result ; } /* receive interrupt */ void serialEvent() { char ch ; if ( Serial.available() > 0 ) { /* get 1 character */ ch = Serial.read(); /* store */ *(sbuf+sindex) = ch ; /* increment */ sindex++ ; /* judge */ if ( ch == '\r' ) { sindex = 0 ; uflag = ON ; } } }  データセレクタをテストするコマンドは、'P'のみです。  コマンド1文字に続けて、0から3でどのビット選択するかを  指定します。次にデータを0から9、AからFの16進数で指定  します。

命令デコーダ

 命令デコーダは、レジスタファイルのロード信号を  生成する回路になります。  ROMの4ビット出力を、74LS139、74HC10で信号を生成  するので、ROMエミュレータからアドレスを出力して  74LS139、74HC10の出力信号を確認します。  信号はアナログマルチメータで、電圧を測定して  確認します。

ALU

 ALUは、加算処理だけなので、4ビットのデータを  2つ用意します。  Arduinoのスケッチでテストしました。 #include <MsTimer2.h> #define OFF 0 #define ON OFF+1 #define LED_BIT 5 /* function prototype */ void rs_putchar(char x); void rs_puts(char *x); void crlf(); void show_help(); void update_trigger(); byte get_hex(char x); /* global variables */ byte xcnt ; byte uflag ; byte sindex ; byte sbuf[8] ; byte cmd ; byte xadr ; byte xdat ; void setup() { /* initialize serial port */ Serial.begin(9600); sindex = 0 ; /* set initial state */ PORTB = 0x00 ; PORTC = 0x00 ; PORTD = 0x01 ; /* set pin directions */ DDRB = 0xff ; DDRC = 0xff ; DDRD = 0xfe ; /* clear flags */ uflag = OFF ; /* initialize */ xcnt = 0 ; /* 200ms period */ MsTimer2::set(200,update_trigger); /* enable */ MsTimer2::start(); } void loop() { /* serial handling */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* new line */ crlf(); /* get command */ cmd = *(sbuf+0) ; /* judge */ if ( cmd == '?' ) { show_help(); } /* adder */ if ( cmd == 'A' ) { /* get data A */ xadr = get_hex( *(sbuf+1) ) ; /* get data B */ xdat = get_hex( *(sbuf+2) ) ; /* impress data A */ PORTB &= 0xf0 ; PORTB |= (xadr & 0x0f); /* impress data B */ PORTC = (xdat & 0x0f) ; } /* selector */ if ( cmd == 'P' ) { /* get selector */ xadr = get_hex( *(sbuf+1) ) ; /* get data */ xdat = get_hex( *(sbuf+2) ) ; /* impress selector */ PORTB &= 0xf0 ; PORTB |= (xadr & 0x03); /* impress data */ PORTC = (xdat & 0x0f) ; } } } void rs_putchar(char x) { Serial.write(x); } void rs_puts(char *x) { while ( *x ) { rs_putchar(*x); x++ ; } } void crlf(void) { rs_putchar('\r'); rs_putchar('\n'); } void show_help(void) { rs_puts("? help"); crlf(); rs_puts("A add "); crlf(); rs_puts("P impress data"); crlf(); } void update_trigger() { PORTB &= ~(1 << LED_BIT); if ( xcnt & ON ) { PORTB |= (1 << LED_BIT); } xcnt++ ; } byte get_hex(char x) { byte result ; result = 0 ; if ( '0' <= x && x <= '9' ) { result = x - '0' ; } if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; } if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; } return result ; } /* receive interrupt */ void serialEvent() { char ch ; if ( Serial.available() > 0 ) { /* get 1 character */ ch = Serial.read(); /* store */ *(sbuf+sindex) = ch ; /* increment */ sindex++ ; /* judge */ if ( ch == '\r' ) { sindex = 0 ; uflag = ON ; } } }  ポートB、Cを使い、ワイヤーでArduinoと74LS181を接続し  加算結果とキャリーをマルチメータの電圧モードで確認して  います。

ROMエミュレータ

 ROMエミュレータは、PersonalComputerとATtiny2313を  接続し、端末ソフトのTeraTermでコマンドを与えてEEPROM  の中にデータを格納し、それを表示させました。  シリアルインタフェースで、EEPROMの内容を確認すると以下。  機械語の入力で、逆アセンブルできます。  全体は、次のように変化します。  さらに、PCの値(4bit)を与えて出力値を確認しました。

シミュレータ

 Tcl/Tkで記述したシミュレータのソースコードは、以下。 #!/usr/local/bin/wish wm title . "TD4 simulator" set theFileName "" set fd_in "" set pgm(0) "" set pgmcode "" set xpc 0 set rega 0 set regb 0 set xcarry 0 set inxval 0 set outxval 0 set decodexval "" ####################################### # define objects ####################################### #label label .lblFileNameLabel -text "FileName" label .lblFileName -textvariable theFileName label .lblPC -text "PC" label .lblCode -text "code" label .lblRegA -text "regA" label .lblRegB -text "regB" label .lblCarry -text "carry" label .lblIN -text "IN" label .lblOUT -text "OUT" label .lblDecode -text "decode" label .lblIndexV -textvariable xpc label .lblCodeV -textvariable pgmcode label .lblRegAV -textvariable rega label .lblRegBV -textvariable regb label .lblCarryV -textvariable xcarry label .lblINV -textvariable inxval label .lblOUTV -textvariable outxval label .lblDecodeV -textvariable decodexval #entry box entry .txtINV -textvariable inxval #list box listbox .lstAsmcode -yscrollcommand ".scbLadderV set" listbox .lstBCode -yscrollcommand ".scbLCodeV set" #scroll bar scrollbar .scbLadderV -orient vertical -command ".lstAsmcode yview" scrollbar .scbLCodeV -orient vertical -command ".lstBCode yview" # button button .btnExit -text "Exit" -command "exit" button .btnLoad -text "Load" -command { # set theFileName [tk_getOpenFile -filetypes {{"assembly code file" {.asm}}}] # clear list box .lstAsmcode delete 0 end .lstBCode delete 0 end # open set fd_in [open $theFileName "r"] # read context set i 0 while { [gets $fd_in sbuf] >= 0 } { # store set pgm($i) [string tolower $sbuf] # display #set tmp [format " %s" $pgm($i)] set tmp [format " %s" [string tolower $pgm($i)]] .lstAsmcode insert end $tmp # display .lstBCode insert end " 0x[getCode $pgm($i)] : [string tolower $pgm($i)]" # increment incr i } set last $i # close close $fd_in # initialize set xpc 0 set rega 0 set regb 0 set xcarry 0 set inxval 0 set outxval 0 # first code set pgmcode $pgm($xpc) # set first code decode doDecode [getCode $pgm($xpc)] } button .btnRst -text "Reset" -command { set xpc 0 set rega 0 set regb 0 set xcarry 0 set outxval 0 set pgmcode $pgm($xpc) # set code decode doDecode [getCode $pgm($xpc)] } button .btnStep -text "Step" -command { doScan } ####################################### # procedures ####################################### proc doDecode {x} { global decodexval # get instruction set car [string index $x 0] set tmp "" # MOV A,Im if { $car == "3" } { set tmp "dst(register A) src(ROM lower nibble)" } # MOV A,B if { $car == "1" } { set tmp "dst(register A) src(register B)" } # ADD A,Im if { $car == "0" } { set tmp "dst(register A) src(ALU output)" } # IN A if { $car == "2" } { set tmp "dst(register A) src(input)" } # MOV B,Im if { $car == "7" } { set tmp "dst(register B) src(ROM lower nibble)" } # MOV B,A if { $car == "4" } { set tmp "dst(register B) src(register A)" } # ADD B,Im if { $car == "5" } { set tmp "dst(register B) src(ALU output)" } # OUT Im if { $car == "b" } { set tmp "dst(output = register C) src(ROM lower nibble)" } # OUT B if { $car == "9" } { set tmp "dst(output = register C) src(register B)" } # IN B if { $car == "6" } { set tmp "dst(register B) src(input)" } # JMP if { $car == "F" } { set tmp "dst(PC = register D) src(ROM lower nibble)" } # JNC Im if { $car == "E" } { set tmp "dst(PC = register D) src(ROM lower nibble) no carry" } # update set decodexval $tmp } proc doScan {} { global pgm pgmcode xpc rega regb xcarry inxval outxval # get instrcution set code [string tolower $pgm($xpc)] set pgmcode $code set opcode [lindex $code 0] set oprandx [split [lindex $code end] ","] # add if { $opcode == "add" } { set xcarry 0 set tmpx [lindex $oprandx 0] set tmpy [lindex $oprandx end] if { $tmpx == "a" } { set result [expr $rega + $tmpy] set rega [expr $result % 16] if { $result > 15 } { set xcarry 1 } } if { $tmpx == "b" } { set result [expr $regb + $tmpy] set regb [expr $result % 16] if { $result > 15 } { set xcarry 1 } } } # mov if { $opcode == "mov" } { set xcarry 0 set tmpx [lindex $oprandx 0] set tmpy [lindex $oprandx end] if { $tmpx == "a" } { set result $tmpy if { $tmpy == "b" } { set result $regb } set rega $result } if { $tmpx == "b" } { set result $tmpy if { $tmpy == "a" } { set result $rega } set regb $result } } # in if { $opcode == "in" } { set xcarry 0 set tmpx $oprandx if { $tmpx == "a" } { set rega $inxval } if { $tmpx == "b" } { set regb $inxval } } # out if { $opcode == "out" } { set xcarry 0 set tmpx $oprandx set result $oprandx if { $tmpx == "b" } { set result $regb } set outxval $result } # jnc if { $opcode == "jnc" } { if { $xcarry == 0 } { set xpc $oprandx } else { set xpc [expr $xpc + 1] set xcarry 0 } } # jmp if { $opcode == "jmp" } { set xpc $oprandx set xcarry 0 } # PC if { $opcode != "jmp" && $opcode != "jnc" } { set xpc [expr $xpc + 1] } # get instrcution set code [string tolower $pgm($xpc)] set pgmcode $code # set code decode doDecode [getCode $pgm($xpc)] } proc getCode {x} { # get command set instructionx [string tolower [lindex $x 0] ] # get data set oprandx [string tolower [lindex $x end]] # splite set xcnt [string first "," $oprandx] if { $xcnt > 0 } { set oprandx [split $oprandx ","] } # default (JMP 15) set opcode 15 set oprand 15 # instruction ADD if { $instructionx == "add" } { set oprand [lindex $oprandx end] set tmpx [lindex $oprandx 0] if { $tmpx == "a" } { set opcode 0 } if { $tmpx == "b" } { set opcode 5 } } # instruction MOV if { $instructionx == "mov" } { set tmpx [lindex $oprandx 0] set tmpy [lindex $oprandx end] if { $tmpx == "a" } { set opcode 3 set oprand $tmpy if { $tmpy == "b" } { set opcode 1 set oprand 0 } } if { $tmpx == "b" } { set opcode 7 set oprand $tmpy if { $tmpy == "a" } { set opcode 4 set oprand 0 } } } # instruction IN if { $instructionx == "in" } { set oprand 0 if { $oprandx == "a" } { set opcode 2 } if { $oprandx == "b" } { set opcode 6 } } # instruction OUT if { $instructionx == "out" } { set opcode 11 set oprand $oprandx set tmpx [lindex $oprandx 0] if { $tmpx == "b" } { set opcode 9 set oprand 0 } } # instruction JNC if { $instructionx == "jnc" } { set opcode 14 set oprand $oprandx } # instruction JMP if { $instructionx == "jmp" } { set opcode 15 set oprand $oprandx } # concatenate return [format "%02X" [expr $opcode * 16 + $oprand]] } ####################################### # window area placing ####################################### grid .lblFileNameLabel -column 0 -row 0 grid .lblFileName -column 0 -row 1 grid .lstAsmcode -column 0 -row 2 grid .lblPC -column 0 -row 3 grid .lblCode -column 0 -row 4 grid .lblRegA -column 0 -row 5 grid .lblRegB -column 0 -row 6 grid .lblCarry -column 0 -row 7 grid .lblIN -column 0 -row 8 grid .lblOUT -column 0 -row 9 grid .lblDecode -column 0 -row 10 grid .scbLadderV -column 1 -row 2 -sticky ns grid .lstBCode -column 2 -row 2 grid .lblIndexV -column 2 -row 3 grid .lblCodeV -column 2 -row 4 grid .lblRegAV -column 2 -row 5 grid .lblRegBV -column 2 -row 6 grid .lblCarryV -column 2 -row 7 grid .txtINV -column 2 -row 8 grid .lblOUTV -column 2 -row 9 grid .lblDecodeV -column 2 -row 10 grid .btnLoad -column 3 -row 0 grid .scbLCodeV -column 3 -row 2 -sticky ns grid .btnStep -column 3 -row 3 grid .btnRst -column 3 -row 5 grid .btnExit -column 6 -row 11  GUIでは、次のようなイメージになります。  ボタンは4種用意しています。  上から、次の機能を持ちます。  プログラムを「Step」で実行していくと、PC、regA、regB、  carry、OUTの値が変化する様子がわかります。  「IN」の右欄は、レジスタA、Bに外部からデータを入力する  場合に利用します。10進の1桁で指定します。  「Load」ボタンを操作し、アセンブリ言語のソースコードを  入力すると、次の画面になります。  左にアセンブリ言語のソースコード、右にアセンブルした  機械語を16進数で付加して表示します。  シミュレータの中にアセンブラをもち、ソースコードに  間違いがないかを確認できます。  プログラムカウンタ(PC)の値で、実行する命令を「code」の  右に表示します。  「decode」の右には、データの発信と受信がどこになるのか  を表示してあります。  CPU動作が、値をレジスタからレジスタへの送受信と等価で  途中で加工しているという事実がわかるようにしています。  「Step」ボタンをクリックすると、命令を実行し  各レジスタの値更新します。またPCの値を更新し  次の命令を表示します。  「Reset」ボタンをクリックすると、PCをゼロにし  各レジスタをゼロで初期化します。  シミュレータの起動は簡単です。  ディレクトリ中にある「td4sim.tcl」をダブルクリックするだけ。  Tcl/Tkは、Windowsでは「Active Tcl」をインストールして  利用します。Unix系OSでは、標準バンドルされているので  インストール作業は不要。

目次

inserted by FC2 system