情報工学実験II(再々履修)

はじめに

MICRO-1 の FPGA 実装をしました. 最近 SystemVerilog を書き始めたことと,HDL 開発におけるテスト手法を調べたことで,何か成果を作りたくなったためです.

成果

MICRO-1 との相違点

MICRO-alpha は MICRO-1 で実行できる制御命令を全てサポートしたのですが,実際に実装するにあたって,設計の細かな部分を変更しました.

機械語の入出力命令はカードリーダとラインプリンタを扱うのですが,流石にそんなものは身の回りにありません. そこで UART を使ったシリアル通信で代用しました.

入力命令は R0 レジスタにシリアル入力を書き込むように変更しました. ただし入力が無い場合が想定されるので,その場合は R0 をゼロクリアします. 入力がないケースは少なくともテキストでは想定されていないので,やむを得ずこのような対処をしています.

出力命令は R0 レジスタをシリアル出力するように変更しました.

テキストでは,主記憶装置が「比較的大容量の記憶装置である」と書かれていますが,MICRO-1 の主記憶装置は現代の感覚ではかなり小さい(16bit x 64K)です. なので FPGA 実装では Block RAM を主記憶装置に充てました.

また細かいところですが,制御装置の動作周波数はテキストの 20MHz ではなく 100MHz としました. これは実装した FPGA ボードのクロック入力と同じ動作周波数にしています.

遊び方

(追記)v1.1.4 から CLI 上で Vivado プロジェクトを作成できるようにしました. v1.1.4 で遊ぶ場合は「Vivado プロジェクトを作成(v1.1.4 以降)」を参照してください.

Vivado プロジェクトを作成(v1.1.4 以前)

以前の記事で紹介したツールをインストールしたマシンで回路合成します. MICRO-alphaのリポジトリをクローンして,そのディレクトリ内にてリリースのページで公開している Vivado プロジェクト (micro-alpha-arty-a7-100.tar.gz) を解凍します.

$ git clone https://github.com/Kenta11/micro-alpha
$ cd micro-alpha
$ wget https://github.com/Kenta11/micro-alpha/releases/download/v1.0.0/micro-alpha-arty-a7-100.tar.gz
$ tar zxvf micro-alpha-arty-a7-100.tar.gz
$ tree -L 1
.
├── LICENSE
├── Makefile
├── README.md
├── micro-alpha-arty-a7-100.tar.gz
├── run.py
├── micro-alpha
├── script
├── src
├── tb

マイクロプログラムを制御記憶に書き込むための COE ファイルを作成します.

$ curl -s http://www.ced.is.utsunomiya-u.ac.jp/lecture/2022/jikkenb/micro/chap5/MICROONE | iconv -f sjis -t utf8 | tr -d "\32" > MICROONE
$ rm1masm MICROONE -o MICROONE.o
$ python script/obj2coe.py MICROONE.o micro-alpha/micro-alpha.srcs/sources_1/ip/control_memory/control_program.coe

続いて機械語プログラムを制御記憶に書き込むための COE ファイルを作成します.

$ cat calculator 
; This program is distributed under MIT LICENSE.
; Copyright (c) 2023 Kenta Arai

; 逆ポーランド記法電卓プログラム
; 
; 実行例((3+4)*(4-2))
; (CALCULATOR)>> 3 4 + 4 2 - *
; 14
TITLE CALCULATOR
             ORG  140
CALCULATOR0: LA   1, PROMPT
             BSR  PRINT
             LA   1, INPUT
             BSR  RWLINE
             LA   0, INPUT
             BSR  INTERPRET
             BSR  PUTINT
             B    CALCULATOR0
             HLT

; データ領域
PROMPT:      DC   ' (
             DC   ' c
             DC   ' a
             DC   ' l
             DC   ' c
             DC   ' )
             DC   ' >
             DC   ' >
             DC   '  
             DC   0
INPUT:       DS   32

; 文字列を出力装置に書き込む関数
; 入力: {r1: 文字列の先頭アドレス}
; 出力: なし
PRINT0:      WIO  LPT
             LEA  1, 1(1)
PRINT:       LX   0, (1)
             OR   0, (0)
             BNZ  PRINT0
             RET

; 入力装置から行を読む関数
; 入力: {r1: 行の書込み先アドレス}
; 出力: なし
; NOTE: 読んだ文字は都度出力装置に書き込む
RWLINE0:     STX  0, (1)
             LEA  1, 1(1)
RWLINE:      BSR  READWORD
             WIO  LPT
             LC   2, X"0D
             CMP  0, (2)
             BNZ  RWLINE0
             WIO  LPT
             LC   0, X"0A
             WIO  LPT
             LC   0, 0
             STX  0, (1)
             RET

; 入力式を計算する
; 入力: {r0: 入力文字列}
; 出力: {r1: 計算結果}
INTERPRET:   LX   1, (0)
             CMP  1, 0       ; NULL
             BZ   INTERPRET5
             CMP  1, X"20    ; SP
             BZ   INTERPRET4
             CMP  1, X"2B    ; +
             BNZ  INTERPRET0
             POP  3, 2
             ADD  2, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET0:  CMP  1, X"2D    ; -
             BNZ  INTERPRET1
             POP  3, 2
             SUB  2, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET1:  CMP  1, X"2A    ; *
             BNZ  INTERPRET2
             POP  3, 1
             POP  1, 1
             MULT 1, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET2:  CMP  1, X"2F    ; /
             BNZ  INTERPRET3
             POP  3, 2
             LC   1, 0
             DIV  1, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET3:  BSR  ISDIGIT    ; '0'-'9'
             CMP  1, 0
             BZ   INTERPRET6
             BSR  ATOI
             PUSH 1, 1
INTERPRET4:  ADD  0, 1
             B    INTERPRET
INTERPRET5:  POP  1, 1
INTERPRET6:  RET

; 整数を出力装置に書き込む関数
; 入力: {r1: 数値}
; 出力: なし
PUTINT:      LC   0, X"30
             WIO  LPT
             LC   0, X"78
             WIO  LPT
             LEA  2, (1)    ; r1[15:12] を出���
             SC   2, 4
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LEA  2, (1)    ; r1[11:8] を出力
             SC   2, 8
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LEA  2, (1)    ; r1[7:4] を出力
             SC   2, 12
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LEA  2, (1)    ; r1[3:0] を出力
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LC   0, X"0D
             WIO  LPT
             LC   0, X"0A
             WIO  LPT
             RET
DIGIT:       DC   X"30
             DC   X"31
             DC   X"32
             DC   X"33
             DC   X"34
             DC   X"35
             DC   X"36
             DC   X"37
             DC   X"38
             DC   X"39
             DC   X"41
             DC   X"42
             DC   X"43
             DC   X"44
             DC   X"45
             DC   X"46

; 入力装置から文字を読む関数
; 入力: なし
; 出力: {r0: 読んだ文字}
; NOTE: MICRO-alpha は入力が無い場合にヌル文字を読んだ扱いとする
;       この関数はヌル文字以外を読むまで,入力装置から文字を読み続ける
READWORD:    RIO  CR
             OR   0, (0)
             BZ   READWORD
             RET

; 入力文字列を数値に変換する
; 入力: {r0: 入力文字列}
; 出力: {r0: 入力文字列(読み進めたアドレス), r1: 変換後の数値}
ATOI:        LC   1, 0
ATOI0:       PUSH 1, 1
             LX   1, (0)
             BSR  ISDIGIT
             CMP  1, 0
             POP  1, 1
             BNZ  ATOI1
             RET
ATOI1:       MULT 1, 10
             LX   1, (0)
             SUB  1, X"30
             ADD  1, (2)
             ADD  0, 1
             B    ATOI0

; 入力文字が数字('0'-'9')であるかを判定する
; 入力: {r1: 入力文字}
; 出力: {r1: 判定結果(1: 数字である,0: 数字でない)}
ISDIGIT:     CMP  1, X"30
             BM   ISDIGIT0
             CMP  1, X"39
             BP   ISDIGIT0
             LC   1, 1
             RET
ISDIGIT0:    LC   1, 0
             RET
END
$ rm1asm calculator -o calculator.b
$ python script/obj2coe.py calculator.b micro-alpha/micro-alpha.srcs/sources_1/ip/main_memory/machine_program.coe

Vivado プロジェクトを作成(v1.1.4 以降)

  • 注意:Basys 3 で動かす場合は以下のように読み替えてください
    • fpga/arty-a7-100/ -> fpga/basys-3/
    • Makefile の変数 SCRIPT を Basys3 用に切り替える

以前の記事で紹介したツールをインストールしたマシンで回路合成します. MICRO-alphaのリポジトリをクローンしてください.

$ git clone https://github.com/Kenta11/micro-alpha
$ cd micro-alpha
$ tree -L 1
.
├── LICENSE
├── Makefile
├── README.md
├── fpga
├── script
├── src
└── tb

5 directories, 3 files

マイクロプログラムを制御記憶に書き込むための COE ファイルを作成します.

$ curl -s http://www.ced.is.utsunomiya-u.ac.jp/lecture/2022/jikkenb/micro/chap5/MICROONE | iconv -f sjis -t utf8 | tr -d "\32" > MICROONE
$ rm1masm MICROONE -o MICROONE.o
$ python script/obj2coe.py arty-a7-100 MICROONE.o fpga/arty-a7-100/control_program.coe

続いて,機械語プログラムを主記憶に書き込むための COE ファイルを作成します.

$ cat calculator 
; This program is distributed under MIT LICENSE.
; Copyright (c) 2023 Kenta Arai

; 逆ポーランド記法電卓プログラム
; 
; 実行例((3+4)*(4-2))
; (CALCULATOR)>> 3 4 + 4 2 - *
; 14
TITLE CALCULATOR
             ORG  140
CALCULATOR0: LA   1, PROMPT
             BSR  PRINT
             LA   1, INPUT
             BSR  RWLINE
             LA   0, INPUT
             BSR  INTERPRET
             BSR  PUTINT
             B    CALCULATOR0
             HLT

; データ領域
PROMPT:      DC   ' (
             DC   ' c
             DC   ' a
             DC   ' l
             DC   ' c
             DC   ' )
             DC   ' >
             DC   ' >
             DC   '  
             DC   0
INPUT:       DS   32

; 文字列を出力装置に書き込む関数
; 入力: {r1: 文字列の先頭アドレス}
; 出力: なし
PRINT0:      WIO  LPT
             LEA  1, 1(1)
PRINT:       LX   0, (1)
             OR   0, (0)
             BNZ  PRINT0
             RET

; 入力装置から行を読む関数
; 入力: {r1: 行の書込み先アドレス}
; 出力: なし
; NOTE: 読んだ文字は都度出力装置に書き込む
RWLINE0:     STX  0, (1)
             LEA  1, 1(1)
RWLINE:      BSR  READWORD
             WIO  LPT
             LC   2, X"0D
             CMP  0, (2)
             BNZ  RWLINE0
             WIO  LPT
             LC   0, X"0A
             WIO  LPT
             LC   0, 0
             STX  0, (1)
             RET

; 入力式を計算する
; 入力: {r0: 入力文字列}
; 出力: {r1: 計算結果}
INTERPRET:   LX   1, (0)
             CMP  1, 0       ; NULL
             BZ   INTERPRET5
             CMP  1, X"20    ; SP
             BZ   INTERPRET4
             CMP  1, X"2B    ; +
             BNZ  INTERPRET0
             POP  3, 2
             ADD  2, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET0:  CMP  1, X"2D    ; -
             BNZ  INTERPRET1
             POP  3, 2
             SUB  2, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET1:  CMP  1, X"2A    ; *
             BNZ  INTERPRET2
             POP  3, 1
             POP  1, 1
             MULT 1, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET2:  CMP  1, X"2F    ; /
             BNZ  INTERPRET3
             POP  3, 2
             LC   1, 0
             DIV  1, (3)
             PUSH 2, 1
             B    INTERPRET4
INTERPRET3:  BSR  ISDIGIT    ; '0'-'9'
             CMP  1, 0
             BZ   INTERPRET6
             BSR  ATOI
             PUSH 1, 1
INTERPRET4:  ADD  0, 1
             B    INTERPRET
INTERPRET5:  POP  1, 1
INTERPRET6:  RET

; 整数を出力装置に書き込む関数
; 入力: {r1: 数値}
; 出力: なし
PUTINT:      LC   0, X"30
             WIO  LPT
             LC   0, X"78
             WIO  LPT
             LEA  2, (1)    ; r1[15:12] を出���
             SC   2, 4
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LEA  2, (1)    ; r1[11:8] を出力
             SC   2, 8
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LEA  2, (1)    ; r1[7:4] を出力
             SC   2, 12
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LEA  2, (1)    ; r1[3:0] を出力
             AND  2, X"F
             LA   3, DIGIT
             ADD  2, (3)
             LX   0, (2)
             WIO  LPT
             LC   0, X"0D
             WIO  LPT
             LC   0, X"0A
             WIO  LPT
             RET
DIGIT:       DC   X"30
             DC   X"31
             DC   X"32
             DC   X"33
             DC   X"34
             DC   X"35
             DC   X"36
             DC   X"37
             DC   X"38
             DC   X"39
             DC   X"41
             DC   X"42
             DC   X"43
             DC   X"44
             DC   X"45
             DC   X"46

; 入力装置から文字を読む関数
; 入力: なし
; 出力: {r0: 読んだ文字}
; NOTE: MICRO-alpha は入力が無い場合にヌル文字を読んだ扱いとする
;       この関数はヌル文字以外を読むまで,入力装置から文字を読み続ける
READWORD:    RIO  CR
             OR   0, (0)
             BZ   READWORD
             RET

; 入力文字列を数値に変換する
; 入力: {r0: 入力文字列}
; 出力: {r0: 入力文字列(読み進めたアドレス), r1: 変換後の数値}
ATOI:        LC   1, 0
ATOI0:       PUSH 1, 1
             LX   1, (0)
             BSR  ISDIGIT
             CMP  1, 0
             POP  1, 1
             BNZ  ATOI1
             RET
ATOI1:       MULT 1, 10
             LX   1, (0)
             SUB  1, X"30
             ADD  1, (2)
             ADD  0, 1
             B    ATOI0

; 入力文字が数字('0'-'9')であるかを判定する
; 入力: {r1: 入力文字}
; 出力: {r1: 判定結果(1: 数字である,0: 数字でない)}
ISDIGIT:     CMP  1, X"30
             BM   ISDIGIT0
             CMP  1, X"39
             BP   ISDIGIT0
             LC   1, 1
             RET
ISDIGIT0:    LC   1, 0
             RET
END
$ rm1asm calculator -o calculator.b
$ python script/obj2coe.py arty-a7-100 calculator.b fpga/arty-a7-100/machine_program.coe

make コマンドを実行すると,Vivado のプロジェクトが作成されます.

$ make all
$ tree -L 2 vivado
vivado
└── arty-a7-100
    ├── arty-a7-100.cache
    ├── arty-a7-100.gen
    ├── arty-a7-100.hw
    ├── arty-a7-100.ip_user_files
    ├── arty-a7-100.srcs
    └── arty-a7-100.xpr

7 directories, 1 file

FPGA 上で逆ポーランド記法プログラムを動作させる

Vivado で回路を合成し,Arty A7-100 に書き込みましょう. シリアル通信で Arty A7-100 にキーボード入力をしましょう.

$ sudo screen /dev/ttyUSB1 115200
(calc)>> 3 4 + 4 2 - *
0x000E
(calc)>> 

逆ポーランド記法の電卓で計算ができました!

おわりに

今年度は MICRO-1 で散々遊びました. もう遊びたくないですね.

何の役に立つのか分からない実装ですが,誰かしらに貢献できたら良いですね. コンピュータ・アーキテクチャの歴史を学ぶ上で少しは参考になるのではないでしょうか. なるのか? ならなそう.