FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

AWS EC2 F1インスタンスを使ったハードウェア開発の勉強 (10. 整数行列計算回路の実装)

AWS F1インスタンス HDK の勉強を続けている。 目標としては、以下の部分にAXIマスタを接続してDRAMにアクセスし、データをフェッチする。

  1. DMAでホストからデータをDDR4メモリに格納する。
  2. AXIマスタデータをフェッチする
  3. 演算し、結果を格納する。

として、例えば行列積のアクセラレータをF1インスタンス上で動作させてみたい。

f:id:msyksphinz:20180516232156p:plain

というわけで、とりあえず簡易的な行列演算回路、というか Dot Product Accelerator を作ってみた。 これでシミュレーションを実行してみる。 まずは簡単に、16x16の行列積を計算するための、16要素同士の32bit整数のDot Productを計算する。これをホストから16×16回アドレスを変えながら実行し、行列積を実行する。

f:id:msyksphinz:20180524004101p:plain

とりあえず256要素分を全部計算すると時間がかかるので、以下の青色の部分だけを計算するようにプログラムを切り替えてシミュレーションを実行した。

f:id:msyksphinz:20180524004850p:plain

計算結果のログを抽出すると、以下のようになる。正しく計算できていることが確認できた。

今度は計算性能を上げる改造と、F1インスタンスを使ったFPGAでの動作を確認していく。

            35594000 : [matrix] result FIFO read     0000000000008a20
            38094000 : [matrix] result FIFO read     0000000000008aa8
            40878000 : [matrix] result FIFO read     0000000000008b30
            43270000 : [matrix] result FIFO read     0000000000008bb8
            45778000 : [matrix] result FIFO read     00000000000167a8
            48642000 : [matrix] result FIFO read     0000000000024730
            50750000 : [matrix] result FIFO read     00000000000328b8
            53262000 : [matrix] result FIFO read     0000000000040c40

Speculative Store Bypass に学ぶ現代プロセッサの高速メモリアクセスユニットの仕組み

Microsoft および Google から、新たなCPUの脆弱性であるVariant 4およびVariant 3aについてのアナウンスがあり、Intel, AMD, Arm が対応に迫られている。

これらの脆弱性は、現代のコンピュータアーキテクチャの飽くなき性能追及の結果発生したものが多く、詳細を調べるとコンピュータアーキテクチャの勉強(復習?)にもなるのである意味重宝している。

今回発表されたのは、Variant3a / Variant 4である。それぞれ、

  • Variant 3a: Rogue System Register Read (CVE-2018-3640)
  • Variant 4: Speculative Store Bypass (CVE-2018-3639)

と名付けられている。Variant 3aについては後日調べるとして、Variant 4について調査してみた。名前の示す通り、"Speculative Store Bypass"というのは、ロードとストアの順番を交換することにより速度向上を図ったものであるが、これにより副作用が発生するという問題だ。

一般論として、ロードとストアの順番を入れ替えるという技法を使うマイクロプロセッサは、デスクトップ向けか、モバイル向けでもかなり性能に重点を置いたものに限定されるだろうと思われる。 ArmについてはCortex-A57以降の世代、IntelはCore世代が中心のようだ。

  • Vulnerability of Speculative Processors to Cache Timing Side-Channel Mechanism

developer.arm.com

  • Q2 2018 Speculative Execution Side Channel Update

INTEL-SA-00115

ちなみに、筆者は例によってセキュリティの専門家ではないし、CPUアーキテクチャにしてもデスクトップクラスの本格的なものは設計経験がないので、いまいち本文から読み取れない部分があったりとか、間違っている部分があるかもしれない。 概念的な部分でしか読み取れないのはいつも悲しいところだ。。。

メモリアクセスのデータフロー

Variant-4 について調査するにあたり、様々な情報を調査してみたのだが、主に参考にしたのは主に以下の資料である。

  • Analysis and mitigation of speculative store bypass (CVE-2018-3639)

Analysis and mitigation of speculative store bypass (CVE-2018-3639) – Security Research & Defense

  • Kernel Side-Channel Attack using Speculative Store Bypass - CVE-2018-3639

Kernel Side-Channel Attack using Speculative Store Bypass - CVE-2018-3639 - Red Hat Customer Portal

(個人的には、Redhatの説明が分かりやすかった)

ちなみに、これらのメモリアクセスのデータフローについては、Mike Johnsonの名著"Superscalar Microprocessor Design" が非常に詳細に説明している。 私もこの本を読み直しながら情報を読み解いていった。

www.amazon.com

ロード・ストアの入り混じった命令シーケンスでの実行順序

ここからは、Mike Johnson本の例を参考にしながら読み進めていく。 例えば、以下のようなロードストアの命令シーケンスが並んでいたとしよう。

STORE v  (1)
ADD      (2)
LOAD  w  (3)
LOAD  x  (4)
LOAD  v  (5)
ADD      (6)
STORE w  (7)

投機実行を行うプロセッサでは、後ろの命令が前の命令を追い越すことが可能であり、メモリアクセスについても基本的にそれは同じであるのだが、ストア命令については、「ストア命令よりも前の命令がすべて完了しないと実際のメモリアクセスを行ってはいけない」という原則がある。 なぜならば、メモリストアを投機的に実行してしまうと、ストア命令よりも前で例外が発生した場合にストア命令を破棄することができないからだ(いったんメモリに書いたものを取り消すのは非常に難しい)。

それからもう一つ、ロード命令とストア命令は基本的に順序の入れ替えは行われない。 なぜならば、最初にストアしたデータを次のロード命令で再びレジスタに持ってくる、というケースがあるからだ。もしも、「ストア[A]→ロード[A]」という順番を「ロード[A]→ストア[A]」と入れ替えてしまうと、誤った動作となってしまう。

1. バイパスをしないロードストアのシーケンス

この前提で、再び上記の命令列を考えてみる。 まず、何も考えずに、上記の命令列をそのままの順番で実行してみる。 デコーダは1サイクルで4命令をデコードすることができ、

  • サイクル1 = (1) - (4) の命令のデコード
  • サイクル2 = (5) - (8) の命令をデコード

とする。以下のような順番でメモリアクセスが実行される。図が長くて分かりにくいが、要するにすべて順番に実行される。

f:id:msyksphinz:20180523020740p:plain:w400
f:id:msyksphinz:20180523020805p:plain:w400
f:id:msyksphinz:20180523020816p:plain:w400

2. バイパスをした場合のロードストアシーケンス

次に、ストア命令よりも先にロード命令を先に発行することで投機的実行を行う方式を考えてみる。ロード命令がストア命令を追い抜かすためには、当然ロード用のリザベーションステーションとストア用のリザベーションステーションが別々に必要になる。

そして、ストアバッファを用意して、ストア命令を追い越すロード命令の発行アドレスを常にチェックしている。もしもストアバッファ内のアクセスアドレスが、ロード命令のアクセスアドレスと被っているならば、先にストア命令を発行しなければ整合性を取ることができない。この例では全体のサイクル数では大きな差はみられないが、それでもロード命令を先に発行することでレジスタにデータを取得するレイテンシが短くなり、速度向上に起因することができる。

f:id:msyksphinz:20180523020836p:plain:w400
f:id:msyksphinz:20180523020847p:plain:w400
f:id:msyksphinz:20180523020858p:plain:w400

3. 一般的な、Load命令の投機実行による弊害

これは今回の問題に限ったことではなく、Load命令を投機実行することによる弊害というものは一般に知られている。例えば、デバイス制御用のメモリマップドレジスタのようなもので、一度Loadアクセスを行うことでデバイスを制御するような装置では、投機的にマップされた当該レジスタにアクセスされると勝手にデバイスが動いてまうという弊害がある。このような場合には、一度メモリアクセスのシーケンスをSynchronizeするか、Pipelineを一度Flushした後に当該Load命令を発行させるなどの工夫を行う必要がある。

Speculative Store Bypass は、ロードストアの順番を入れ替える高速化技術を使った技法

実際、Speculative Store Bypass ではこのロードストアの入れ替えにより機密データを読み取るテクニックであり、Spectre が分岐予測による投機実行での機密データ読み取りであるのに対し、こちらはロードストアの入れ替えのテクニックを活用している。これによりキャッシュに投機的ロードした結果が副作用として残ってしまい、これを検査することで機密データのサイドチャネル攻撃が可能になってしまう。

RedHatの技術解説にも、以下のような記述がある。

The Memory Disambiguator(MD) predicts which of the loads do not depend on an earlier store instruction. Such load(read) instructions are then speculatively executed to load data from L1 data cache, even when the address of the earlier store is not known, thus bypassing the Store instruction. This adds to the overall performance by avoiding load latency. At the end, if the prediction was wrong and conflict between load(read) and store(write) instructions is detected, all instructions since (and including) the speculative load are re-executed.

Amazon AWS F1インスタンス上で動作するRISC-VプロセッサRocket-Chipの環境FireSimがオープンソース化

UCBにて開発されているRISC-VのRocket-Chipを、Amazon AWS F1インスタンス上で動作させるための環境FireSimがISCA 2018にアクセプトされたとのこと。 おめでとうございます。

さらに、FireSimの環境がオープンソースとして公開され、GitHub上でアクセスできるようになったようだ。 論文も公開されているのでチェックしてみよう。

github.com

  • FireSim WebSite

fires.im

  • FireSim Manual

Welcome to FireSim’s documentation! — FireSim documentation

  • FireSim Paper

https://sagark.org/assets/pubs/firesim-isca2018.pdf

# なんか文献を並べただけのつまらない記事になってしまったが、後で追記しよう...

C++で記述された軽量CNN実装 mojo-cnn 試行 (4. RISC-V on FPGAのデバッグ)

f:id:msyksphinz:20170925003926p:plain

FPGAでCNNのプログラムを動かそうとしたのだが、C++のコードをそのままRISC-Vでコンパイルして走らせてもどうもうまく行かない。 そもそもの問題だが、例えばRISC-V on FPGAで何かを動かしたい場合、printfなどは独自のsyscalls.c を使ってPS (=ARM)に表示させるように改造しなければならない。 つまり、std::cout などは現状では未対応というように考えればよい?

つまり、std::cout とすると一応spike で動かすことはできるのだが、これをどのようにしてFPGAで動かすプログラムとしてコンパイルしなおせばよいのか不明なのだ。むう。 ただしspikeでstd::coutを動かすことができているので、出来ないはずはない。spikeがstd::coutをどのように処理しているのか要調査だな...

syscalls.c というのは、以下をそのまま使っている。

github.com

とりあえず、シンプルにriscv64-unknown-elf-g++ を使って簡単なC++のコードをコンパイルして、RISC-V on FPGAで動かしてみようとしたのだが、いろんなライブラリを削除してコンパイルしているため結構つらい。syscalls.cに載っていない関数は基本的に使えないし、C++独特のname manglingに対応させるために色々とてこづった。

結果的に、以下のようなtest.cpp を用意して(まあほぼCのコードなのだが)、g++でコンパイルし、gccコンパイルしたcrt.Sとsyscalls.c とくっつけてバイナリを作ることはできた。

// #include <iostream>
#include <stdio.h>

int main ()
{
  printf ("Hello World\n");

  return 0;
}
test_ideal: test.cpp syscalls.o crt.o makefile
        $(CC) $(CFLAGS) -mcmodel=medany -fno-builtin-printf -static -std=gnu99 -O2 -ffast-math -fno-common -nostdlib -nostartfiles -ffast-math -lgcc $< crt.o syscalls.o -o $@ -T ./common/test.ld

crt.o: ./common/crt.S
        $(CC)  -c $(CFLAGS) ./common/crt.S

syscalls.o: common/syscalls.c
        $(CC) $(CFLAGS) -mcmodel=medany -c $< -o $@
f:id:msyksphinz:20180521015029p:plain
図. C++のコード(というかほぼC)をRISC-V向けにコンパイルしてRocket-Chipで動かした図

でもこれでは実用には全く耐えられないので、どうにかしていろんなライブラリを動かす必要がある。この部分はまだ要調査だ...

RISC-V におけるメモリモデルについて

f:id:msyksphinz:20180421185728p:plain

RISC-Vのコンパイル時に登場する謎のメモリモデルについて調査したのでまとめておく。

以下の資料を参考にした。

  • All Aboard, Part 4: The RISC-V Code Models

www.sifive.com

RISC-Vはコード内をジャンプするための手法としては複数の手段があるのだが、他のプロセッサアーキテクチャと比較すると決しては多いわけではない。

RISC-Vの場合は以下の3種類に限定される。

  • PC相対 (auipc / jal / br*命令)
  • レジスタ相対 (jalr / addi命令)
  • 絶対 (lui命令)

gccによりプログラムがコンパイルされる際には、ジャンプ先やメモリアクセス先はどこに配置されるのかは分からない。 これは、実際にリンクしてみるまで分からないので、オブジェクトを作成する段階では、とりあえずアクセス先の情報を空に設定しておき、リンカによる再設定を行う。

gccでは、オブジェクトのコンパイルされてからリンクされるまでのコードの変遷を見るための便利なオプションが用意されている。--save-tempsというオプションだ。

例えば、上記のサイトで紹介されている以下のようなプログラムをコンパイルしてみる。

  • cmodel.c
long global_symbol[2];

int main() {
  return global_symbol[0] != 0;
}

以下のようにしてコンパイルをしてみる。

$ riscv64-unknown-elf-gcc cmodel.c -o cmodel -O3 --save-temps

結果、以下のようなファイルが生成された。

$ ls -1 | grep cmodel
cmodel
cmodel.c
cmodel.i
cmodel.o
cmodel.s

cmodel.iファイルはプリプロセッサを通過させただけなので面白くない。次にcmodel.sはどのようになっているだろうか。

    .file   "cmodel.c"
    .option nopic
    .section    .text.startup,"ax",@progbits
    .align  1
    .globl  main
    .type   main, @function
main:
    lui     a5,%hi(global_symbol)
    ld      a0,%lo(global_symbol)(a5)
    snez    a0,a0
    ret
    .size   main, .-main
    .comm   global_symbol,16,8
    .ident  "GCC: (GNU) 7.2.0"

オブジェクト形式として保存されたcmodel.oをダンプしてみると、以下のようになっていることが分かった。

$ riscv64-unknown-elf-objdump -dtr cmodel.o

cmodel.o:     file format elf64-littleriscv
...
0000000000000010       O *COM*  0000000000000008 global_symbol
...
0000000000000000 <main>:
   0:   000007b7                lui     a5,0x0
                           0: R_RISCV_HI20 global_symbol
                           0: R_RISCV_RELAX        *ABS*
   4:   0007b503                ld      a0,0(a5) # 0 <main>
                           4: R_RISCV_LO12_I       global_symbol
                           4: R_RISCV_RELAX        *ABS*
   8:   00a03533                snez    a0,a0
   c:   8082

実際にglobal_symbolのアドレスは決まっていないため、アセンブラはこの変数の場所をまだ決めることが出来ず、 その代わりに上記のような目印を配置している。 そしてオブジェクトにリロケーションテーブルを作成し、最終的にリンカがアドレスを決める際にどの命令のどの場所の 変数のアドレス情報を置き換えれば良いかが分かるようになっている。

最終的に生成された実行ファイルを同様にobjdumpすると、以下のようにアドレスが確定されていることが見て取れる。

$ riscv64-unknown-elf-objdump -dtr cmodel
Disassembly of section .text:

0000000000010330 <main>:
   10330:       67c9                    lui     a5,0x12
   10332:       0387b503                ld      a0,56(a5) # 12038 <global_symbol>
   10336:       00a03533                snez    a0,a0
   1033a:       8082                    ret

ここまでが、RISC-Vにおけるデフォルトのコード生成モデルであるmedlowの試行結果である。 一方で、RISC-Vにはもう一つのコード生成モデルであるmedanyというモデルがある。

これも同様にコンパイルして、その結果をダンプしてみよう。

$ riscv64-unknown-elf-gcc -mcmodel=medany cmodel.c -o cmodel -O3 --save-temps
$ riscv64-unknown-elf-objdump -dtr cmodel.o

...

Disassembly of section .text.startup:

0000000000000000 <main>:
   0:   00000797                auipc   a5,0x0
                           0: R_RISCV_PCREL_HI20   global_symbol
                           0: R_RISCV_RELAX        *ABS*
   4:   00078793                mv      a5,a5
                           4: R_RISCV_PCREL_LO12_I .L0
                           4: R_RISCV_RELAX        *ABS*
   8:   6388                    ld      a0,0(a5)
   a:   00a03533                snez    a0,a0
   e:   8082                    ret

先程の結果と少し変わっている。 medlowのコンパイルではR_RISCV_HI20とされていたものが、medanyではR_RISCV_PCREL_HI20と変わっているし、 R_RISCV_LO12_I とされていたものが R_RISCV_PCREL_LO12_I と変わっている。

まとめると、以下のようになる。

  • コードモデルmedlowの場合 lui / ld が生成される
  • コードモデルmedanyの場合 auipc / ld が生成される

-mcmodel=medlowの場合

medium-lowコードモデルであることを示す。アドレス参照の範囲は、絶対アドレスとして-2GBから+2GBまでの間である。 命令生成時には、lui/addi 命令を用いてアドレスが生成される。

-mcmodel=medanyの場合

medium-anyコードモデルであることを示す。アドレス参照の範囲は現在のPCの位置から2GBの範囲に限定される。 命令生成時には、 auipc / ld命令が使用される。

ここで注意しなければならないのは、コードモデルはABIとは異なるということだ。 ABIは関数呼び出しのレジスタ使用などの規約を意味しており、コンパイル時に関数の引数の受け渡しを統一するために必要であるが、 コードモデルの場合は異なるコードモデルの関数同士をリンクして一つの実行ファイルにすることができる。 関数同士のインタフェースとは無関係のため、コンパイル時に自由に決めることが可能となる。

AWS EC2 F1インスタンスを使ったハードウェア開発の勉強 (9. 整数行列計算回路の実装)

AWS F1インスタンス HDK の勉強を続けている。 目標としては、以下の部分にAXIマスタを接続してDRAMにアクセスし、データをフェッチする。

  1. DMAでホストからデータをDDR4メモリに格納する。
  2. AXIマスタデータをフェッチする
  3. 演算し、結果を格納する。

として、例えば行列積のアクセラレータをF1インスタンス上で動作させてみたい。

f:id:msyksphinz:20180516232156p:plain

前回、データを自由にフェッチすることができるようになったので、次にフェッチしたデータを使って計算を行ってみたい。

f:id:msyksphinz:20180520003345p:plain

FIFOを挿入してAXIからのデータを受け取り、FIFOにデータが挿入されるたびに積和演算を実行する。

logic fifo_wr, fifo_empty, fifo_full;
assign fifo_wr = (rcv_state == rcv_state_col) & 
                 cl_axi_mstr_bus.rvalid & cl_axi_mstr_bus.rready;
logic [63: 0] fifo_rd_data;

assign cl_axi_mstr_bus.rready = !fifo_full;

fifo u_fifo
(
 .CLK   (clk),
 .nRST  (pipe_rst_n),
 .D     (cl_axi_mstr_bus.rdata[31:0]),
 .Q     (fifo_rd_data),
 .WR    (fifo_wr),
 .RD    (!fifo_empty),
 .FULL  (fifo_full),
 .EMPTY (fifo_empty)
 );

所定の計算結果である 0x00008a20 が出力されていることが確認できた。次は全要素分これを実行しなければ。

            34762000 : [axi_mstr_cfg_bus W] ADDR=00000500     
            34766000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=7 ADDR=0000000400000000
            34766000 : [axi_mstr_cfg_bus W] ADDR=00000500
            34782000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010004
            34798000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010044
            34814000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010084
            34830000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000100c4
            34846000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010104
            34862000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010144
            34878000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010184
            34894000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000101c4
            34910000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010204
            34926000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010244
            34942000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010284
            34958000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000102c4
            34974000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010304
            34986000 : [Waiting] rvalid = 1, rready = 1
            34986000 : [cl_axi_mstr_bus  R] DATA=000000100000000f0000000e0000000d0000000c0000000b0000000a000000090000000800000007000000060000000500000004000000030000000200000001
            34990000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010344
            34994000 : [Waiting] rvalid = 1, rready = 1
            34994000 : [cl_axi_mstr_bus  R] DATA=000000730000007200000071000000700000006f0000006e0000006d0000006c0000006b0000006a000000690000006800000067000000660000006500000064
            35000000 : [matrix  0] mult = 00000001 x 00000064
            35006000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010384
            35014000 : [Waiting] rvalid = 1, rready = 1
            35014000 : [cl_axi_mstr_bus  R] DATA=000000830000008200000081000000800000007f0000007e0000007d0000007c0000007b0000007a000000790000007800000077000000760000007500000074
            35020000 : [matrix  1] mult = 00000002 x 00000074
            35022000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=00000004000103c4
            35026000 : [Waiting] rvalid = 1, rready = 1
            35026000 : [cl_axi_mstr_bus  R] DATA=000000930000009200000091000000900000008f0000008e0000008d0000008c0000008b0000008a000000890000008800000087000000860000008500000084
            35032000 : [matrix  2] mult = 00000003 x 00000084
            35038000 : [cl_axi_mstr_bus AR] LEN=  0 SIZE=2 ADDR=0000000400010404
            35046000 : [Waiting] rvalid = 1, rready = 1
            35046000 : [cl_axi_mstr_bus  R] DATA=000000a3000000a2000000a1000000a00000009f0000009e0000009d0000009c0000009b0000009a000000990000009800000097000000960000009500000094
            35050000 : [Waiting] rvalid = 1, rready = 1
            35050000 : [cl_axi_mstr_bus  R] DATA=000000b3000000b2000000b1000000b0000000af000000ae000000ad000000ac000000ab000000aa000000a9000000a8000000a7000000a6000000a5000000a4
            35052000 : [matrix  3] mult = 00000004 x 00000094
            35056000 : [matrix  4] mult = 00000005 x 000000a4
            35058000 : [Waiting] rvalid = 1, rready = 1
            35058000 : [cl_axi_mstr_bus  R] DATA=000000c3000000c2000000c1000000c0000000bf000000be000000bd000000bc000000bb000000ba000000b9000000b8000000b7000000b6000000b5000000b4
            35064000 : [matrix  5] mult = 00000006 x 000000b4
            35074000 : [Waiting] rvalid = 1, rready = 1
            35074000 : [cl_axi_mstr_bus  R] DATA=000000d3000000d2000000d1000000d0000000cf000000ce000000cd000000cc000000cb000000ca000000c9000000c8000000c7000000c6000000c5000000c4
            35080000 : [matrix  6] mult = 00000007 x 000000c4
            35090000 : [Waiting] rvalid = 1, rready = 1
            35090000 : [cl_axi_mstr_bus  R] DATA=000000e3000000e2000000e1000000e0000000df000000de000000dd000000dc000000db000000da000000d9000000d8000000d7000000d6000000d5000000d4
            35096000 : [matrix  7] mult = 00000008 x 000000d4
            35106000 : [Waiting] rvalid = 1, rready = 1
            35106000 : [cl_axi_mstr_bus  R] DATA=000000f3000000f2000000f1000000f0000000ef000000ee000000ed000000ec000000eb000000ea000000e9000000e8000000e7000000e6000000e5000000e4
            35112000 : [matrix  8] mult = 00000009 x 000000e4
            35138000 : [Waiting] rvalid = 1, rready = 1
            35138000 : [cl_axi_mstr_bus  R] DATA=00000103000001020000010100000100000000ff000000fe000000fd000000fc000000fb000000fa000000f9000000f8000000f7000000f6000000f5000000f4
            35144000 : [matrix  9] mult = 0000000a x 000000f4
            35158000 : [Waiting] rvalid = 1, rready = 1
            35158000 : [cl_axi_mstr_bus  R] DATA=000001130000011200000111000001100000010f0000010e0000010d0000010c0000010b0000010a000001090000010800000107000001060000010500000104
            35164000 : [matrix 10] mult = 0000000b x 00000104
            35174000 : [Waiting] rvalid = 1, rready = 1
            35174000 : [cl_axi_mstr_bus  R] DATA=000001230000012200000121000001200000011f0000011e0000011d0000011c0000011b0000011a000001190000011800000117000001160000011500000114
            35178000 : [Waiting] rvalid = 1, rready = 1
            35178000 : [cl_axi_mstr_bus  R] DATA=000001330000013200000131000001300000012f0000012e0000012d0000012c0000012b0000012a000001290000012800000127000001260000012500000124
            35180000 : [matrix 11] mult = 0000000c x 00000114
            35184000 : [matrix 12] mult = 0000000d x 00000124
            35186000 : [Waiting] rvalid = 1, rready = 1
            35186000 : [Waiting] rvalid = 1, rready = 1
            35186000 : [cl_axi_mstr_bus  R] DATA=000001430000014200000141000001400000013f0000013e0000013d0000013c0000013b0000013a000001390000013800000137000001360000013500000134
            35192000 : [matrix 13] mult = 0000000e x 00000134
            35202000 : [Waiting] rvalid = 1, rready = 1
            35202000 : [cl_axi_mstr_bus  R] DATA=000001530000015200000151000001500000014f0000014e0000014d0000014c0000014b0000014a000001490000014800000147000001460000014500000144
            35208000 : [matrix 14] mult = 0000000f x 00000144
            35218000 : [Waiting] rvalid = 1, rready = 1
            35218000 : [cl_axi_mstr_bus  R] DATA=000001630000016200000161000001600000015f0000015e0000015d0000015c0000015b0000015a000001590000015800000157000001560000015500000154
            35224000 : [matrix 15] mult = 00000010 x 00000154
            35226000 : [matrix] result = 0000000000008a20
            35230000 : [matrix] result = 0000000000008a20
````

「30日でできる!OS自作入門」を読み始めた (27日目 LDT / GDTと環境の改善)

30日でできる! OS自作入門

30日でできる! OS自作入門

27日目はLDTの改造と環境の改善だ。GDTに対する理解が甘いので、LDTについて読んでもなんかまだしっくり来ない。復習が必要だなあ。。。

  • Global Descriptor Table[GDT] : 全てのプログラムから共通にアクセスするセグメントを定義する
  • Local Descriptor Table[LDT] : タスク単位に存在する
f:id:msyksphinz:20180518013001p:plain
図. 複数アプリケーションも立ち上げることができるHaribote-OS環境

あと、環境の整理を行った。API群はライブラリとして定義するので、arコマンドを使ってHaribote-OSのAPIをライブラリ化した。

github.com

OBJS_API = api001.o api002.o api003.o api004.o api005.o api006.o \
                        api007.o api008.o api009.o api010.o api011.o api012.o \
                        api013.o api014.o api015.o api016.o api017.o api018.o \
                        api019.o api020.o

apilib.lib : Makefile $(OBJS_API)
        ar rcs $@ $(OBJS_API)

%.o:%.nas Makefile
        nasm -felf32 $< -o $@ -l $@.lst

最終的にディレクトリ構成は以下のようになった。

.
├── Makefile
├── a
│   ├── Makefile
│   └── a.c
├── a_nask.h
├── apilib
│   ├── Makefile
│   ├── api001.nas
│   ├── api002.nas
│   ├── api003.nas
│   ├── api004.nas
...
├── app_common.mk
├── beepdown
│   ├── Makefile
│   └── beepdown.c
├── bootpack.map
├── color
│   ├── Makefile
│   └── color.c
├── color2
│   ├── Makefile
│   └── color2.c
├── haribote
│   ├── Makefile
│   ├── a_nask.nas
│   ├── asmhead.nas
│   ├── timer.c
│   └── timer.h
├── hello
│   ├── Makefile
│   └── hello.nas
├── hello2
│   ├── Makefile
│   └── hello2.nas
├── hello3
....

これでハリボテOSとしてはほぼ完成だ。28日目は日本語関係を対応しよう。