s平面の左側

左側なので安定してます(制御工学の話は出てきません)

PHP + Swoole で論理回路シミュレータを作ってみた

こちらはウィルゲート Advent Calendar 2019 の 18 日目の記事です。

昨日は @msm_2 さんによる「SwiftUIについて調べたこと」でした。

sinnderu.hatenablog.com


これは今年 10 月に開催された「PHP カンファレンス沖縄 2019」の懇親会 LT ネタをもとにしています。

phpcon.okinawa.jp

背景

PHPカンファレンス沖縄2019前夜祭リジェクトコンにてCPUに関する発表がありました。

いつもどおりtwitter実況していると、その発表者である @tomzoh さんから返信が。

その後直接お話をするなかで「回路シミュレータまで来るとイベント駆動的なアプローチのほうが相性良いかもね」という言葉を受けた私は、その晩のうちに論理回路シミュレータを作ってしまいました。

github.com

実装に際しては、イベント駆動・コルーチンベースの非同期処理 PHP ライブラリである Swoole を使いました。

github.com

なお「論理回路シミュレータ」といっても単発ネタのため JK フリップフロップ(以下、JK-FF) と呼ばれる回路の挙動を再現しただけです。

ja.wikipedia.org

JK-FF についての解説

JK-FF には JK およびクロックの入力端子があります。

JK-FF の出力値はクロックのタイミングで変わり、入力値およびその前の出力値に依存します。

J K 出力
0 0 前の出力値を保持
1 0 1
0 1 0
1 1 前の出力値を反転

実際の動作

こちらが実際に論理回路シミュレータを動作させた様子です。

論理回路シミュレータを実行している様子。クロックの立ち上がりで入力値および前の出力値に応じた出力をしていることが確認できます。
論理回路シミュレータを実行している様子

クロック(T)の立ち上がり(0→1 に変わるタイミング)で入力値および前の出力値に応じた出力をしていることが確認できます。

最終的に以下のような出力になります。

 step   | T       J       K     | Q
--------+-----------------------+-------
 0      | 0       0       0     | 0
 1      | 1       0       0     | 0
 2      | 0       0       0     | 0
 3      | 1       0       0     | 0
 4      | 0       1       0     | 0
 5      | 1       1       0     | 1
 6      | 0       0       0     | 1
 7      | 1       0       0     | 1
 8      | 0       0       1     | 1
 9      | 1       0       1     | 0
 10     | 0       0       0     | 0
 11     | 1       0       0     | 0
 12     | 0       1       1     | 0
 13     | 1       1       1     | 1
 14     | 0       1       1     | 1
 15     | 1       1       1     | 0
 16     | 0       1       1     | 0
 17     | 1       1       1     | 1
 18     | 0       1       1     | 1
 19     | 1       1       1     | 0
 20     | 0       0       0     | 0

実装解説

前述のとおり、イベント駆動の非同期処理なので Swoole を利用しています。

「入力値の変化」をイベントとして、変化の内訳を伝達させています。

<?php

use Swoole\Coroutine\Channel;

const LEVEL_LOW = false;
const LEVEL_HIGH = true;

$ch = new Channel(10);

// 略

// クロックの変化(反転)
$ch->push(['t' => 'invert']);

// J, K 端子への入力値の変化
$ch->push([
    'j' => LEVEL_HIGH,
    'k' => LEVEL_LOW,
]);

はじめはクロックと入力端子を分けようとしたのですが、難しかったので断念しました。

勢いを大事にして一晩で作ったので、かなり雑な作りになっており

  • 入力値の変化のタイミングはベタ書き & コピペ
  • 出力も printf() してるだけ

というお粗末な実装です。

なお、入力のタイミングを制御するために、最初は PHP 組み込みの sleep() を使おうとしていました。 実はこれでうまくいかず、Swoole では Swoole\Timer::after() を使う必要がある、ということを学びました。

JavaScript の setTimeout() と同じ要領ですね。

これに詰まったことによりカンファレンス前夜の睡眠時間を数時間削るハメになりました。

懇親会 LT での反響

翌日のカンファレンスの懇親会 LT にて、この論理回路シミュレータを紹介したところ会場のみなさまから様々な反応をいただくことができました。

感想と今後の展望

カンファレンス本体でも発表を控えている中で、発表練習もほどほどにほぼ徹夜で実装をしていた私はいったい何をやっていたのか......。

でも、あのタイミングを逃したら一生やっていなかったと思うので、やってよかったと思います!勢い大事!

今後の展望としては

  • JK フリップフロップ以外も作る
  • ちゃんとした実装にする
    • 回路素子などをきちんとクラスにする
    • 論理回路としての動きと表示を分ける
  • 素子同士を繋げられるようにする
    • カウンタなどを作れるようにする

あたりが挙げられるのですが、果たしてその「今後」はいつ来ることやら。


ウィルゲート Advent Calendar 2019」、明日の記事は @zoe302 さんによる「マイクラサーバ運用でやっている5tips」です。乞うご期待!

blog.zoe.tools