目次
前
次
ブロックテストとシミュレータ
基板に部品を半田付けしながら、ブロックごとに動作を
テストしました。そのプロセスを備忘録として残します。
測定機器としてアナログとデジタルのマルチメータを使います。
デジタルマルチメータには、周波数カウンタがあるので
クロックジェネレータの出力周波数を確認するのに利用
しました。
アナログマルチメータは、プログラムカウンタの出力を
測定するのと、クロックジェネレータの出力を針の動き
で見るのに使います。
他に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種用意しています。
上から、次の機能を持ちます。
- Load アセンブリ言語で記述したソースコードをロード
- Step 1クリックで1命令を実行
- Reset プログラムカウンタ(PC)をゼロに、全レジスタ値をゼロで初期化
- Exit シミュレータを終了
プログラムを「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では、標準バンドルされているので
インストール作業は不要。
目次
前
次