機械語から理解するMN-Core

もうすぐコンテストがあるそうなので、予習してみました。(書きかけです)

https://www.preferred.jp/ja/news/pr20240805/

## 機械語の読み方

引数を設定して`vsm`ファイルをアセンブラに投げると読みやすくなる

`--print-asm`をつけるとコードの後ろに元の命令がコメントで付く
```
$ /assemble3 hello.vsm --print-asm
i 01000000...000000000000001100000000000000 # imm ui"0" $n0
```

`--print-csv`をつけると、フィールドごとに分割してくれる
```
i,ll,pe.mrc.imr,pe.mrc.wl,...,l2bm.darwrite,wd
m,valid,mmode,tmode,...,adrb,nw,priority
i,="01",="00000",="0",="0",="0",="0",...,="000",="11",="0000",="0",="0",="00000000",
```
一行目は`i`命令のコラム名一覧で、二行目は`m`命令のコラム名一覧になる。

`i`命令はPE命令のauto strideモードの機械語
`m`命令はMV命令の機械語

ここでは`i`命令だけに注目する

## PE命令のフィールド

PE命令は以下のフィールドを持つ

<table>
<thead>
<tr><td colspan="13">PE命令</td></tr>
<tr>
<td rowspan="2">i</td>
<td rowspan="2">ll</td>
<td colspan="8">pe</td>
<td rowspan="2">l1bm</td>
<td rowspan="2">l2bm</td>
<td rowspan="2">wd</td>
</tr>
<tr>
<td>mrc</td>
<td>trc</td>
<td>rfc0</td>
<td>rfc1</td>
<td>lm0</td>
<td>lm1</td>
<td>mauc</td>
<td>aluc</td>
</tr>
</thead>
<tbody>
<tr>
<td>i</td>
<td>2bit</td>
<td>20bit</td>
<td>3bit</td>
<td>41bit</td>
<td>41bit</td>
<td>32bit</td>
<td>31bit</td>
<td>29bit</td>
<td>17bit</td>
<td>40bit</td>
<td>28bit</td>
<td>8bit</td>
</tr>
</tbody>
</table>

各フィールドに対応するコンポーネントは以下のようになる
| フィールド | コントローラ |
|:-:|:-:|
| pe.mrc  | マスクレジスタ  |
| pe.trc  | Tレジスタ(t)  |
| pe.rfc0  |  RF0レジスタ(r) |
| pe.rfc1  | RF1レジスタ(s)  |
| pe.lm0  | LM0メモリ(m)  |
| pe.lm1  | LM1メモリ(n)  |
| pe.mauc  | MAU  |
| pe.aluc  | ALUC  |
| l1bm  | L1BM(b)  |
| l2bm  | L2BM(c)  |


さらに細かいフィールドは各コンポーネントごとに記述する

## PE命令の複数の出力オペランド

PE命令は複数の出力オペランドを指定できる。これは以下のような仕組みでコード化する。

例としてALUの計算結果を複数の出力先(t, r, s, m, n)に保存する命令は

<table>
<thead>
<tr><td colspan="13">PE命令</td></tr>
<tr>
<td rowspan="2">i</td>
<td rowspan="2">ll</td>
<td colspan="8">pe</td>
<td rowspan="2">l1bm</td>
<td rowspan="2">l2bm</td>
<td rowspan="2">wd</td>
</tr>
<tr>
<td>mrc</td>
<td>trc</td>
<td>rfc0</td>
<td>rfc1</td>
<td>lm0</td>
<td>lm1</td>
<td>mauc</td>
<td>aluc</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan='2'>i</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td>write=1</td>
<td>write=1</td>
<td>write=1</td>
<td>write=1</td>
<td>write=1</td>
<td rowspan='2'>-</td>
<td rowspan='2'>ALUOP</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
</tr>
<tr>
<td>isel=ALU</td>
<td>isel=ALU</td>
<td>isel=ALU</td>
<td>isel=ALU</td>
<td>isel=ALU</td>
</tr>
</tbody>
</table>

このように`aluc`で計算を指定し、各々の出力先(trc, rfc0, rfc1, lm0, lm1)の書き込みを有効にし(write=1)、入力セレクタにALUを指定(isel=ALU)する。

同様にMAUとALUで計算して結果を(r, s, t, m, n)に分けて保存したい場合は

<table>
<thead>
<tr><td colspan="13">PE命令</td></tr>
<tr>
<td rowspan="2">i</td>
<td rowspan="2">ll</td>
<td colspan="8">pe</td>
<td rowspan="2">l1bm</td>
<td rowspan="2">l2bm</td>
<td rowspan="2">wd</td>
</tr>
<tr>
<td>mrc</td>
<td>trc</td>
<td>rfc0</td>
<td>rfc1</td>
<td>lm0</td>
<td>lm1</td>
<td>mauc</td>
<td>aluc</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan='2'>i</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td>write=1</td>
<td>write=1</td>
<td>write=1</td>
<td>write=1</td>
<td>write=1</td>
<td rowspan='2'>MAUOP</td>
<td rowspan='2'>ALUOP</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
</tr>
<tr>
<td>isel=MAU</td>
<td>isel=ALU</td>
<td>isel=MAU</td>
<td>isel=ALU</td>
<td>isel=MAU</td>
</tr>
</tbody>
</table>

のようにコード化される。このコード化により複数出力オペランドが可能となる。

## PE命令の入力オペランド

ALUは2つの入力オペランドAとBを持つ。命令によってオペランド数は0、1、2の場合がある。
MAUは3つの入力オペランドAとBとCを持つ。命令によってはBは行列となり、行列レジスタxかyを使う。

例えば、ALUの入力オペランドAが`r`の場合は次のようにコード化する。

<table>
<thead>
<tr><td colspan="13">PE命令</td></tr>
<tr>
<td rowspan="2">i</td>
<td rowspan="2">ll</td>
<td colspan="8">pe</td>
<td rowspan="2">l1bm</td>
<td rowspan="2">l2bm</td>
<td rowspan="2">wd</td>
</tr>
<tr>
<td>mrc</td>
<td>trc</td>
<td>rfc0</td>
<td>rfc1</td>
<td>lm0</td>
<td>lm1</td>
<td>mauc</td>
<td>aluc</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan='2'>i</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>radr=アドレス</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td>ALUOP</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
</tr>
<tr>
<td>isela=r</td>
</tr>
</tbody>
</table>

このようにコード化する。出力オペランドのときとは逆にALU側のiselを設定し、データソース側はアドレスを指定する。

ALUとMAUが`r`から同じデータを受け取って計算する場合は次のようにコード化する。

<table>
<thead>
<tr><td colspan="13">PE命令</td></tr>
<tr>
<td rowspan="2">i</td>
<td rowspan="2">ll</td>
<td colspan="8">pe</td>
<td rowspan="2">l1bm</td>
<td rowspan="2">l2bm</td>
<td rowspan="2">wd</td>
</tr>
<tr>
<td>mrc</td>
<td>trc</td>
<td>rfc0</td>
<td>rfc1</td>
<td>lm0</td>
<td>lm1</td>
<td>mauc</td>
<td>aluc</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan='2'>i</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>radr=アドレス</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td>MAUOP</td>
<td>ALUOP</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
<td rowspan='2'>-</td>
</tr>
<tr>
<td>isela=r</td>
<td>isela=r</td>
</tr>
</tbody>
</table>

このように同じアドレスを読む限りで、MAUとALUに同時に`r`から入力可能となる。

### ALUのポートA

ALUのポートAは以下の入力をiselに指定できる。このポートは`$peid`などの固定値を指定できる。(TODO: 15個しかないので1つ忘れてるかも？？？)

| 入力 |
|:-:|
| r  |
| s  |
| t  |
| m  |
| n  |
|$peid|
|$l1bid|
|$l2bid|
|$msb1|
|$subpeid|
|$mabid|
|$mauf|
|$aluf|
|$lbf|
|$mreadf|

### ALUのポートB

ALUのポートBは以下の入力をiselに指定できる。Aと異なり固定値は指定できないのと`$mreadf`も指定できない。

| 入力 |
|:-:|
| r  |
| s  |
| t  |
| m  |
| n  |
|$mauf|
|$aluf|
|$lbf|

### MAUのポート

MAUのポートは以下の入力をiselに指定できる。ALUのポートBと同じ。

| 入力 |
|:-:|
| r  |
| s  |
| t  |
| m  |
| n  |
|$mauf|
|$aluf|
|$lbf|

### TRC

Tレジスタの制御を行う。

フィールドは以下の通り。
<table border="1"><tbody>
<tr>
<td colspan='2' rowspan='1'>pe</td>
</tr>
<tr>
<td colspan='2' rowspan='1'>trc</td>
</tr>
<tr>
<td colspan='1' rowspan='1'>write</td>
<td colspan='1' rowspan='1'>isel</td>
</tr>
<tr>
<td colspan='1' rowspan='1'>1bit</td>
<td colspan='1' rowspan='1'>2bit</td>
</tr>
</tbody></table>

`write=1`のときは`isel`で指定した入力から書き込みを行う。そうでないときは読み出しのみ行う。

アドレス指定やワード長指定はなく、2長語×4サイクルのアクセスで固定。(TODO: iselの計算結果が1長語のときを調べる)

`isel`に指定できる入力

| isel |
|:-:|
| MAU  |
| ALU  |
| L1BM  |
| MREAD  |

ハザード: 書き込み後は1ステップ休む

### RFC0とRFC1

レジスタファイルの制御を行う。

フィールドは以下の通り。

<table border="1"><tbody>
<tr>
<td colspan='16' rowspan='1'>pe</td>
</tr>
<tr>
<td colspan='8' rowspan='1'>rfc0/rfc1</td>
</tr>
<tr>
<td colspan='1' rowspan='1'>write</td>
<td colspan='1' rowspan='1'>isel</td>
<td colspan='1' rowspan='1'>wadr</td>
<td colspan='1' rowspan='1'>wadri</td>
<td colspan='1' rowspan='1'>wwl</td>
<td colspan='1' rowspan='1'>radr</td>
<td colspan='1' rowspan='1'>radri</td>
<td colspan='1' rowspan='1'>rwl</td>
</tr>
<tr>
<td colspan='1' rowspan='1'>1bit</td>
<td colspan='1' rowspan='1'>2bit</td>
<td colspan='1' rowspan='1'>9bit</td>
<td colspan='1' rowspan='1'>8bit</td>
<td colspan='1' rowspan='1'>2bit</td>
<td colspan='1' rowspan='1'>9bit</td>
<td colspan='1' rowspan='1'>8bit</td>
<td colspan='1' rowspan='1'>2bit</td>
</tr>
</tbody>
</table>

`write=1`のときは`isel`で指定した入力から書き込みを行う。そうでないときは読み出しのみ行う。

`wadr`と`wadri`と`wwl`で書き込みアドレス、書き込みアドレス増分、書き込みワード長を指定する。
`radr`と`radri`と`rwl`で読み出しアドレス、読み出しアドレス増分、読み出しワード長を指定する。

アクセスするアドレスはサイクルごとに

| サイクル | アドレス |
|:-:|:-:|
| 0  | adr + adri * 2 * wl * 0  |
| 1  | adr + adri * 2 * wl * 1  |
| 2  | adr + adri * 2 * wl * 2  |
| 3  | adr + adri * 2 * wl * 3  |

コード例(TODO: 動作確認して)
| コード | adr | adri | wl | アドレス |
|:-:|:-:|:-:|:-:|:-:|
| `$lr4`  | 4  | 0  | 1  | (4, 4, 4, 4) | 
| `$lr4v`  | 4  | 1  | 1  | (4, 6, 8, 10) |
| `$lr4v2`  | 4  | 1  | 1  | (4, 6, 8, 10) |
| `$llr4v4`  | 4  | 1  | 2  | (4, 8, 12, 16) |

`isel`に指定できる入力

| isel |
|:-:|
| MAU  |
| ALU  |
| L1BM  |
| MREAD  |

ハザード: 書き込み後は1ステップ休む(アドレスごとに独立)。

続きはまた書く