外部記憶装置

脳みそ小さすぎるからメモしとく

2019年を振り返って

ポエムです

2019年は色々あったので自分の中での振り返りを含めて適当に書いていきます

1月

年末年始は大学の実験で、論理ゲートを組み合わせて何かを作る課題でプロセッサを作ってました。 手でカルノー図を解いて一つ一つのゲートをKicadみたいな回路CAD上で配置して手でつなげる作業は苦行でした。

中旬・下旬はインフルエンザになりレポート・試験をインフルエンザとともに過ごす地獄でした。

2月

大学は春休みなのでほとんど毎日サークルに行ってロボコンの作業をやってた気がします。 それ以外の記憶がないだけですが。

ロボコンでの担当はLinux・ROS・通信まわりの担当でした。 UARTを利用したマイコン間の簡易的な通信プロトコルをスクラッチしたり、 EthernetでROSのデータを流し込んでマイコンを暴走させたりしてました。

3月

春休み後半もロボコンの作業をやってました。

中旬頃に作業をしながら、「未踏にこんなアイデアのやつ出したいんよね〜」 とか話してたら偶然メンバーの友人と話が盛り上がり、 締切10日前から資料を一気に作って応募しました。

4月

ロボコンの2次ビデオ審査の締切があり、 人が限界を超えると何が起こるのか目の当たりにしてました。 端的に辛かったです。

2次審査をパスし本戦出場が決まったときは本当に嬉しかったです。

5月

大学の実験やらロボコンの作業をしてたような気がします。

プロセッサ実験では、徹夜で実装したりマルチコア化してセマフォの制御でやらかしたりしてました。

実験内のソート速度コンテストでシングルコア歴代最速を叩き出したのですが、 ほとんどは一緒に作業していた相方の超高性能ソートアルゴリズムのおかげです。 シミュレーターやアセンブラも書いてくれたので本当に感謝しかないです。

ロボコン本番はまさか優勝するとは思ってなかったので 嬉しいというより、何が起きたのか分からんという困惑の方が大きかったように思います。

ABUでモンゴルに行くことにはなったけれどお金も場所も何もない状態で途方にくれてたような...

あと、未踏に採択されました。てんてこ舞い。

6月

ほとんど記憶がないのですが、おそらくロボコンと未踏の開発をやってたはずです。

ロボコンはLiDAR・ROSのデータを制御に反映する部分の改良をしたりしてましたが、 ほとんどの時間はひたすら走らせて問題ないか確認してました。

7月

ほとんど記憶がないのですが、おそらくロボコンと未踏の開発をやってたはずです。

ロボットの発送と試験と未踏の用事が重なって大変だったことは覚えてます。

8月

ABUロボコンでした。

大会中は色々トラブルがあり終始ピリピリしてしまい、チームメンバーには迷惑をかけてしまいました。

大会についてはNHKの放送を見て下さい。文章化は書くことが多すぎて大変なので省略しますが、 僕自身の甘さを痛感する大会でした。

9月

ほとんど記憶がないのですが、ロボコンの後片付けと未踏の開発をやってたはずです。

未踏は提案アイデアのPoCに成功して「あ〜うごいた〜」とか言ってました。

10月

未踏の開発を中心にやってました。

あと、NHKロボコン2020に向けて新チームで開発が始まりました。

11月

未踏の開発とロボコンの開発のサポートをしてました。

NF(11月祭)の展示を手伝ったり、大学の実験で初めてまともなWebアプリケーションを組んだりしてました。

12月

未踏の開発とロボコンの開発のサポートをしてました。

GPGPU無限に分からんとか無限に計算資源欲しいとか言ってました。

2019年総括

色々あって充実した楽しい1年でした。

NHK・ABUロボコンは喜怒哀楽すべてを詰め込んだ時間を過ごすことができました。

僕自身の甘さや未熟さ、一人では何も出来ないことを痛感する1年でもありました。 ロボコンや未踏のチームメンバー全員が人として本当に素晴らしい人ばかりで、 僕はそれに甘えたり迷惑をかけたりすることが多々ありました。 メンバーや友人には本当に感謝です。ありがとうございました。

2019年も残りわずかですが、みなさんよい年をお迎えください。

蛇足

そういや自作OSとかFPGAで何かするやつ全くしてない....

Zen言語でTCP/IPプロトコルスタックをスクラッチしたかった

この記事は 自作 OS アドベントカレンダー 2019 の 8 日目の記事です

adventar.org

近況報告

こんにちは。VTb(@PiBVT)です。最近はtwitterで発言する機会が少なくなり表舞台からは消えてますが、一応生きてます。 このブログも去年のアドカレ以来の更新になります...放置しすぎた。

今年はロボコンやら未踏やらなんやらかんやらで、てんてこ舞いになり自作OS界隈から離れてしまっていたため自作OSとはあまり関係のない内容になります。ご了承ください。

ちなみに、未踏ではプログラムを暗号化された状態で実行するプラットフォームを3人のチームで開発してます。 専用のISA,CPU,コンパイラも自作してるので、低レイヤネタとしてプロジェクト終了後にまた記事にしたいと考えています。

Zen言語とは

Zen言語は京都にある株式会社コネクトフリーで開発されているプログラミング言語です。 組み込みなどのベアメタル環境においてもメモリー安全かつ確実なエラーハンドリング、HashMapなどの一般的なコレクション(Zen言語ではcontainer)群が提供されています。 zen-lang.org

僕も京都の大学に通学してる都合上、 何度か会社の方にお邪魔させてもらったことがありました。 そういったつながりでZen言語を使ってみたいと思っていたので、Zen言語でプロトコルスタック(一部)を書いてみました。 (インターン等には行ってないのでZen言語に関してはど素人です)

今回作ったもの

TAPデバイス経由で流れてきたパケットを処理してpingに応答するソフトウェアです。 f:id:PiBVT:20191208012451p:plain

github.com

本当は level-ip のようにソケットライクなAPIを実装してガンガンTCPパケット流したかったのですが、いかんせん時間がなかったので断念しました。無念。 github.com

※注: ベアメタル開発はペリフェラルドライバ開発にかなり時間を使うため、今回はLinux上でのシステムプログラミングをしてみました。 おそらく、この分野はZen言語ではあまり想定されていない(主眼を置いていない)と思われるので、 ベアメタル開発とは開発の勝手がかなり違う可能性があります。

できること

L2パケットを処理してpingに対して応答する。 f:id:PiBVT:20191208012451p:plain

以上です。

全体の構成

プロトコルとしてはEthernet,ARP,IPv4,ICMPv4で、pingに対して応答できる最低限の実装になってます。

色々とひどいのでソースコードは全く参考にならないと思います。

TAPデバイス

TAPはL2デバイスをエミュレートした仮想デバイスです。(TUNはL3) OpenVPNとかでよく用いられているアレです。

TAPを使うことで仮想NICを作成したことになるので、今回は仮想NICにつながる先の部分を書いたことになります。

似たようなことをするときにRaw Socketも使えるのですが、これは既存のNICに生のEthernetフレームを流すものなのでTAPデバイスとは全く異なるものです。

ちなみに、Raw Socketを作成するのにはroot権限が必要です。一方でpingはRaw Socketでパケットを流していますが、root権限なしでも利用出来ます。 この仕組みについては、Linux Capability を調べると分かります。(常識?)

TAPデバイスの初期化部分はLinuxシステムプログラミングの領域なので、C言語と相性の良いZen言語なら楽にかけます(もちろんlibcはリンクしてます)

pub fn create_netdev() NetDevError!std.os.fd_t {
    const sock = try std.os.openC(c"/dev/net/tun", std.os.O_RDWR, 0);
    errdefer {
        std.os.close(sock);
        std.debug.warn("Failed to open /dev/net/tun\n");
    }

    const ifr_addr = tun.ifreq_addr{
        .ifr_flags = tun.IFF_TAP | tun.IFF_NO_PI,
    };

    const zero = ([1]u8{0}) ** 12;

    var ifr = tun.ifreq{
        .ifr_name = "test" ++ zero,
        .addr = ifr_addr,
    };

    const err = tun.ioctl(sock, tun.get_tunsetiff(), &ifr);
    if (err < 0) {
        std.debug.warn("{}", err);
        return NetDeviceError.FailedToCreateTUN;
    }
    std.debug.warn("Successfully Created TUN Device:{}\n", ifr.ifr_name);
    _ = tun.system(c"ip a add 10.0.0.2/24 dev test");
    _ = tun.system(c"ip link set test up");
    return sock;
}

こんな感じになります。TUNSETIFFマクロだけはZen言語単体では無理だったので、Cから呼び出してます。 TUNデバイスを作成したらIPv4アドレスを割り当ててリンクを上げます。(エラー処理してませんが。) これでTUNデバイスを使えるようになったので、あとはwrite,readでパケット投げ放題です。

TUNSETIFFマクロも追いかけてみると結構楽しかったのですが、話が脱線するので割愛します。

ARP(arp.zen)

RFC 826 を参考に実装しました tools.ietf.org

pub const pkt_hdr = packed struct {
    hwtype: u16,
    protype: u16,
    hwsize: u8,
    prosize: u8,
    op: u16,
    sha: u48,
    spa: u32,
    tha: u48,
    tpa: u32,
};

今回はpingに反応するだけなので、ARP Request に対して ARP Reply で応答すると同時にARPテーブルを更新するだけの処理になっています。

本来はパケット送信時にARP Requestを投げたり、テーブルのキャッシュクリア等をする必要があるのですが、pingへの応答には必要なかったので実装しませんでした(バッサリ)

C言語ではsha,tha等はuint8_t[6]で実装するのですが、Zen言語では自由にビット幅を設定できるのでu48一発でいけます。 (むしろ[6]u8使うと謎にバグることが多くて辛かった)

IPv4(ipv4.zen)

RFC 791 を参考に実装しました tools.ietf.org

pub const pkt_hdr = packed struct {
    header_length: u4,
    version: u4,
    tos: u8,
    total_len: u16,
    identification: u16,
    frags: u16,
    ttl: u8,
    protocol: u8,
    chksum: u16,
    src_addr: u32,
    dst_addr: u32,
    payload: [*]u8,
};

単純にIPv4ヘッダーを読んでプロトコルを判断するだけです。 フラグメント,デフォルトゲートウェイ等は一切実装していません。気にしたら負けです。

RFC上では、version, header_length となっているのですが、エンディアンの関係か、逆の順番でないと正常に動作しませんでした。 ココらへんをC言語で書こうとすると

struct header {
#if __BYTE_ORDER == __LITTLE_ENDIAN
    uint8_t len :4;
    uint8_t ver  :4;
#elif __BYTE_ORDER == __BIG_ENDIAN
    uint8_t ver  :4;
    uint8_t len :4;
#endif
} __attribute__((packed));

こんな感じで対応できていたので、Zen言語でも対応できると嬉しいです。

ICMP(icmp.zen)

RFC 792 を参考に実装しました tools.ietf.org

Echo Request に対して Echo Reply を返すだけです。

ちなみに、pingに応答するためにはペイロードを丸々コピーしないといけないようです。本当はどうなのか分かりませんが、とりあえず動いてるのいいでしょう。(おい)

Zen言語を触ってみて

良いところ

  • Cと相性がいい。何も考えずにリンクできる
  • ビルドシステム
  • テストフレームワーク
  • 自由なビット幅整数型
  • エラーが日本語

C言語と何も考えずに普通にリンクできるので、少しずつZenで書き直していくという戦略は有効に感じました。が、C言語と同じ書き方は出来ないことを理解しておく必要はあります。

ビルドシステムやテストフレームワークは、まだ十分に使ってないのでその力を引き出せてはいないのですが、C言語のときは毎回悩みの種だったので予め標準で備わっているのは非常に強い点です。

個人的には自由なビット幅整数型が便利に感じました。エンディアンには気をつける必要があるのですが、 そこさえ気をつければ今までuint8_t配列で処理していたものをまとめて固めることができるので有効に使っていきたいです。

エラーが日本語なだけで安心感が段違いです。(英語だと読まない人がいるので...)

微妙なところ

  • 配列とポインタの関係が分かりにくい
  • ドキュメントがところどころ不十分
  • Zenコンパイラがたまにクラッシュする
  • コミュニティ,相談場所が存在しない

配列とポインタの関係については、C言語ほど単純なものではないので概要を理解しておく必要があります。 プログラムを書いてる時間の大半を配列とポインタ周りのバグでこじらせてたので... 逆に言うと、配列とポインタの組み合わせはすべきではない。ということかもしれません(今気づいた)

ドキュメントの不十分さやZenコンパイラのクラッシュは、公開されて半年もしていないので当然のことなのですが.. 標準ライブラリの実装が参考になりました。 一般的に使われるようになるにはまだ時間がかかりそうですが、これからに期待です。

個人的に一番困ったのが、相談できる場所がないことです。 クラッシュ,バグ報告をしようとしても、それらの再現コードの貼付ける場所が存在しないため 現状では積極的に改善要求を出すことがつらい状況にあるように感じました。 せめてGitHubリポジトリが存在すれば、再現コードを貼ってissueを投げることが出来たのですが....

まとめ

パケットの処理系をZen言語で書いてみた一番の印象は、Zen言語はC言語との相性はいいけれど、C言語では決してないと感じました。

Zen言語ではベアメタル開発においても容易で安全なプログラミング環境を提供する方針で言語仕様自体やライブラリが整備されており、 遊びでC言語を書くノリで書こうとすると安全でないコードになるので、コンパイラが自動的に弾いたり実行時にエラーを吐いてくれます。 慣れないうちはモヤモヤするのですが、慣れれば快適に書けるようになると思います。

僕自身は15時間ほどしか書いてないので全く慣れてないのですが、今後も少しずつ書いていきたいとは思っています。

蛇足・独り言

本当はQCOW2フォーマットのディスクをブロックデバイスとして直に認識させるLinuxカーネルモジュールを書くつもりだったのですが、時間の都合上断念しました。 nbdを利用したものとのパフォーマンスの違いを計測したかったんですよね...

パケット処理系ネタはもともとは2018年Seccamp後にNICデバドラを実装してガバガバTCP処理系を実装したのが最初でした。 その後、今年の3月にはSTM32F767上にデバドラからUDP処理系までを書いてみたりしていたのですが、それ以降は全くやっていなかったので今回ネタとして取り上げてみました。 いい加減ガッツリ、バッファリング周りから全部設計して常用できるTCP/IPスタックを書きたいところではあります。

ロボコンではそこらへんの担当(マイコンの通信プログラム,Ethernet,PCの管理)をしてたのですが、まさか世界大会に行くことになるとは。人生何があるか分からんもんです。

Android-x86でLinux Kernelのドライバにパッチを当てた

この記事は、自作OS Advent Calendar 2018

adventar.org

の 12/20 の記事として書かれました。

はじめに

怒涛の2018年もあと残すところ10日ほどになりました。皆様いかがお過ごしでしょうか?

僕は10月から大学の講義が始まって以来、講義やサークルのロボット製作などが忙しく、自作OSの方はxv6をx86-64ネイティブに完全移植する作業を開始した事ぐらいしか進捗はありません。

今回の自作OS Advent Calendar 2018では、xv6を64bitUEFIで起動したことやNICのデバドラ・プロトコルスタックの実装をしたことを書いても良かったのですが、どれも中途半端な進展なので あえてアドカレに書くために新しくネタを見つけてきました。

Android-x86とは?

Android-x86とは、その名前の通りAndroidx86のプロセッサを積んだPCで動かそうというプロジェクトです。

www.android-x86.org

Android自体はオープンソースなプロジェクトでかつ、Linux Kernelを改造したものが用いられているため、 x86で動かすこと自体は困難ではないようです。 が、x86とはいっても主に使われるハードウェアがWindowsを搭載したタブレットや2in1ノートのような、 ハードウェア構成が特殊なものが多いため全ての機能を完動させることは難しいようです。

今回利用したタブレットも中国製のWindows搭載のタブレットCube iWork10(いわゆる中華Winタブ)で、タッチパネルのドライバに問題がありました。 折角の機会なので、今回はこのタッチパネルを正常に動作させるためのパッチを作成しました。

今回の環境

今回の環境は以下のとおりです。

  • ハード: Cube iWork10
  • OS: Android-x86 8.1-rc2
  • Kernel: Linux Kernel 4.18.14

問題の症状

今回発生したタッチパネルの問題とは、タッチ位置と認識される位置が反転していることです。 横方向をx,縦方向をyとすると、x座標が反転している感じです。

f:id:PiBVT:20181220144704p:plain
問題の症状

問題となるドライバの調査

まず、タッチパネルに使われているドライバ(kernel module)を探すことにします。

幸いなことに、Android-x86にはターミナルエミュレータがあり、各種コマンドでタッチパネルのドライバを調査しました。

f:id:PiBVT:20181220124959p:plain
lsmod
f:id:PiBVT:20181220124949p:plain
modinfo goodix
以上より、タッチパネルのドライバはgoodixであるようなので、goodix.cにパッチをあてることにします。

linux/drivers/input/touchscreen/goodix.cの140行目付近にある

/*
 * Those tablets have their coordinates origin at the bottom right
 * of the tablet, as if rotated 180 degrees
 */
static const struct dmi_system_id rotated_screen[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
    {
        .ident = "WinBook TW100",
        .matches = {
            DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
            DMI_MATCH(DMI_PRODUCT_NAME, "TW100")
        }
    },
    {
        .ident = "WinBook TW700",
        .matches = {
            DMI_MATCH(DMI_SYS_VENDOR, "WinBook"),
            DMI_MATCH(DMI_PRODUCT_NAME, "TW700")
        },
    },
#endif
    {}
};

や、700行目付近の

if (dmi_check_system(rotated_screen)) {
        ts->prop.invert_x = true;
        ts->prop.invert_y = true;
        dev_dbg(&ts->client->dev,
            "Applying '180 degrees rotated screen' quirk\n");
}

あたりが参考になりそうです。これは、タッチパネルを180°回転させるための処理のようなので、この処理を参考にx座標だけを反転させる処理を追加することにします。

パッチの方針

以下の方針でパッチを適用することにしました。

  1. rotated_screenと同様にDMI(SMBIOS)を利用した端末認識を行う
  2. invert_x=true,invert_y=falseの処理をrotated_screenと同様の処理で行う。

DMI(SMBIOS)の確認

DMI(SMBIOS)はハードのベンダーや型番などの固有情報をもつ領域らしいです。

Linuxから確認する場合はdmidecodeが使えるようなのですが、Android-x86にはインストールされていなかったので、Windows 10側から確認することにします。

Windowsの場合は、DirectX診断ツール(dxdiag)から確認できます。 スタートメニューの検索欄で「dxdiag」と検索することで起動できます。

f:id:PiBVT:20181220125517p:plain
DirectX診断ツール
このように各種情報が取得できました。今回必要な情報である、製造元は「cube」,型番は「i15-T」であることが分かります。

パッチ作成

rotated_screenを参考に以下のパッチを作成しました。

diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 6a4ffe800194..4f646607e3f7 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -145,6 +145,18 @@ static const struct dmi_system_id rotated_screen[] = {
        {}
 };
 
+static const struct dmi_system_id x_inverted_screen[] = {
+#if defined(CONFIG_DMI) && defined(CONFIG_X86)
+       {
+               .ident = "cube i15-T",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "cube"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "i15-T")
+               }
+       },
+#endif
+       {}
+};

@@ -707,6 +719,11 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
                ts->prop.invert_y = true;
                dev_dbg(&ts->client->dev,
                        "Applying '180 degrees rotated screen' quirk\n");
+       }else if (dmi_check_system(x_inverted_screen)) {
+               ts->prop.invert_x = true;
+               ts->prop.invert_y = false;
+               dev_dbg(&ts->client->dev,
+                       "Applying 'x-axsis mirrored  screen' quirk\n");
        }
 
        error = input_mt_init_slots(ts->input_dev, ts->max_touch_num,

処理の内容としては、DMIの製造元(DMI_SYS_VENDOR)がcube,型番(DMI_PRODUCT_NAME)がi15-Tに一致する場合は、タッチスクリーンのx座標を反転するという内容です。

Android-x86をビルドする

いよいよ、パッチをあてたLinux Kernelを含めたAndroid-x86をビルドします。 詳細な手順は

www.android-x86.org

ここにあります。

ビルド環境の構築

まずは、ビルド環境を構築します。 大量のソースコードコンパイルする必要があるので、今回は逸般の誤家庭には必ず1台あるラックサーバーを使うことにしました。

  • ハード:VMWare ESXi on FUJITSU RX300S7
  • CPU: Xeon E5-2630相当 10コア
  • Memory:24GB
  • HDD:200GB *OS: Ubuntu 18.10

ソースコードは約40GBほどあるので、余裕をみて200GBほどHDDを確保しておくことをおすすめします。 ビルドにあたり、以下のパッケージをインストールする必要があります。

repo
git
build-essential
libncurses5
m4
curl
openjdk-8-jdk
bison
libc6:i386
libncurses5:i386
libstdc++6:i386
bison:i386
libssl-dev
python-mako
libxml2-utils
isohybrid

インストールします。

$ sudo dpkg --add-architecture i386
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install repo git build-essential libncurses5 m4 curl openjdk-8-jdk bison libssl-dev python-mako libxml2-utils isohybrid
$ sudo apt install libc6:i386 libncurses5:i386 libstdc++6:i386 bison:i386

以上でビルド環境の構築は終了です。

ソースコードのダウンロード

公式の手順に従って、ソースコードをダウンロードします

$ mkdir android-x86
$ cd android-x86
$ repo init -u git://git.osdn.net/gitroot/android-x86/manifest -b $branch
$ repo sync --no-tags --no-clone-bundle

約30〜40GBほどダウンロードするので、3時間程かかります。就寝前など時間があるときにダウンロードしましょう。

パッチの適用

Linux Kernelは、android_x86/kernelにあるので、android_x86/kernel/drivers/input/touchscreen/goodix.c に先程のパッチを適用します。

ビルド

Android-x86をビルドします。 ビルドオプションには、eng,user,userdebugの3種類ありますが、今回はuserdebugでビルドすることにします。当初はuserでビルドしたのですが、永久に再起動をする症状が出てしまいました。

$ . build/envsetup.sh
$ lunch android_x86_64-userdebug
$ m -j10 iso_img

f:id:PiBVT:20181220010306p:plain
ビルド開始
これでビルドが始まります。これも大体3.5時間かかるので、時間があるときに行いましょう。

ビルドしたイメージを実機で動かす

f:id:PiBVT:20181220144714p:plain
ビルド完了
ビルドが完了すると、完成したイメージはandroid_x86/out/target/product/x86_64/android_x86_64.isoに保存されます。 このイメージを適当にdd等でUSBメモリに書き込み、タブレットで起動してみましょう。

正常にタッチパネルが動作するようになりました!

まとめ

自作OSについて何か書くつもりだったのですが、Linuxの話になってしまいました。 自作OSも楽しいのですが、既存のOSを改造する(今回はドライバでしたが)というのも楽しいもので、勉強になります。 来年はネットワークスタックを書き上げてHTTPサーバーを動作させたり、ファイルシステムの実装をしたいと思っています。 2018年も残すところわずかとなりましたが、年末の休みを利用してまた何か楽しいことをやるつもりです。

64bitUEFIでxv6を動かす方法(xv6-前編)

どうも、PiBVTです。今回は、ブートローダーから起動したxv6側でCompatibility modeからProtected modeへ移行する部分を解説していきたいと思います。 前編ではカーネルのmain関数の直前までで、後編はmain関数からの解説となります。

続きを読む

64bitUEFIでxv6を動かす方法(ブートローダー編)

セキュリティキャンプ2018で32bitのxv6を64bitUEFIから起動したPiBVTです。(長い)

セキュリティキャンプの参戦記録ではなく、どのようにしてxv6を64bitUEFIから起動したのか書いていきたいと思います。 ブートローダー編とxv6編の2本立てで、今日はブートローダー編です。

続きを読む

リレー式計算機で円周率を計算する 番外編

どうもお久しぶりです。PiBVTです。約4ヶ月ぶりの更新となります。

リレー計算機について

リレー計算機についてなのですが、一旦このプロジェクトは凍結したいと思います。 理由は以下のとおりです。

  • サークルのロボコンが忙しくなった。
  • Seccamp参加後、OS開発のほうが楽しくなっちゃった。

身勝手な理由であることは重々承知しているのですが、ご了承ください。

リレー計算機プロジェクトの再開時期としては、来年の春休み(2,3月)あたりを考えています。 それまでは、しばしお待ちください。