ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

伊勢海老カレー🦐🍛 の受付を開始します!

こんにちは、ふーが ( @fugakkbn ) です。

RubyKaigi 2022 において、弊社は「伊勢海老スポンサー」として協賛させていただく旨をお知らせしました。
RubyKaigi 2022 に伊勢海老スポンサーとして協賛します🦐 - esm アジャイル事業部 開発者ブログ

こちらはオンライン参加予定の皆様のご自宅に「伊勢海老カレー」をお届けし、ランチタイムに一緒に食べながら懇親しましょう!という趣旨です。

本日より申込み受付を開始します!先着順です!!

受付期間

本日 2022年8月9日(火)〜2022年8月15日(月) 23:59 まで。

※定員に達し次第、受付期間内であっても申込みを締め切らせていただく場合があります。

対象者

  • RubyKaigi に参加される方(リモート参加・現地参加どちらも OK!)
  • ハッシュタグ #esm_ise_ebi で Twitter のタイムラインを盛り上げてくださる方

お申し込み方法

以下の URL から参加登録の上、案内に従って入力フォームへ送信してください。

esminc.doorkeeper.jp

注意事項

  • ご入力いただいた配送先等に間違いがあった場合などは配送できない場合があります。入力の際はお気をつけください。
  • 簡単な調理(解凍、炊飯、ボイルなど)が必要です。
  • 白米と食塩をご自身でご用意いただく必要があります。
  • ご自宅など調理ができる環境でのお受け取りをお願いいたします。

ハッシュタグ

「伊勢海老スポンサー」としての協賛にあたり、ハッシュタグ #esm_ise_ebi を用意しました。
SNS への投稿の際はぜひこのハッシュタグを付けていただけるとうれしいです!

RubyKaigi のハッシュタグと一緒に使っていただくのはもちろん、受け取ったときや後日お召し上がりになる場合などにもぜひご活用ください。

ランチ会もやります!

RubyKaigi 2022 をより一層盛り上げるべく、オンラインランチ会場を用意いたします。お申し込みいただいた皆様には改めてご案内しますので、ぜひそちらにもご参加ください。
開催地三重の特産品である伊勢海老を堪能しつつ、セッションの感想や近況などで一緒に盛り上がりましょう!


永和システムマネジメントでは、RubyKaigi をはじめとする Ruby コミュニティと一緒に盛り上がる仲間を全国から募集しています!

agile.esm.co.jp

RubyKaigi 2022 に伊勢海老スポンサーとして協賛します🦐

こんにちは、 @ima1zumi です。

私たち永和システムマネジメントは、2022 年 9 月 8 日・9 日・10 日の 3 日間に渡って開催される RubyKaigi 2022伊勢海老スポンサーとして協賛します。

Rubyに関するテックトークが聞けて話せる、本当に素晴らしいカンファレンスの場を今年も作っていただけたことを大変ありがたく思っています。今年は三重とオンラインのハイブリッド開催の予定で、とても楽しみです。

私たちは伊勢海老スポンサーとして、三重の名物である伊勢海老を使用した伊勢海老カレー🦐🍛をみなさんにお送りします。オンラインでランチ会場も開きますので、一緒においしい伊勢海老を賞味しながら懇親しましょう!

  • 送付物: 伊勢海老カレー、サフランライスの素、スープの冷凍セット 2人前 (簡単な調理が必要です)
  • 配送先: 参加者の自宅 (会場では受け取れません)
  • 参加: 無料 (人数制限があります)

詳細については後日募集開始した際にお知らせします。

永和システムマネジメントは伊勢海老スポンサーとしてRubyKaigi2022を協賛します

伊勢海老カレー

弊社は前回までの RubyKaigi でも、さまざまな形のスポンサーとして開催のお力添えをさせていただいてきました。

今回の RubyKaigi も、関わるみなさまにとってより充実した場となり、ひいては Ruby をとりまくコミュニティ全体が活気に溢れた場であり続けられるようにという想いを込めて、微力ながらサポートさせていただきます。

rubykaigi.org

ちなみに弊社からは、@koic, @fugakkbn, @ima1zumi の3名が登壇します。こちらもお楽しみに 💎


永和システムマネジメントでは、Ruby コミュニティと共生しながら成長したいエンジニアを絶賛募集しています。 agile.esm.co.jp

コードを読み解く技術【初級編】

こんにちは、ふーが(@fugakkbn)です。

暑さも厳しさを増して参りましたが、入社して5ヶ月弱となりました。
人にも環境にも恵まれて、毎日楽しくプラグラミングできていることに幸せを感じずにはいられない今日この頃です。

さて、この5ヶ月を振り返ってみると、とにかくプログラミングが楽しい!WEB エンジニア最高!!という気持ちが98%くらいなのですが、2%くらい「苦労したな…」と思うことがあります。
それがタイトルにもある「コードを読み解く」ことです。

何に苦労したのか

プロジェクトに入った当初は本当にコードが読めないし、何をどこから読んだらいいのかもわからない状態でアタフタしていました。
モデルは300近くあるし、テストは1万ケース以上あるし…規模の大きいプロジェクトが初めてだったこともあり、断崖絶壁を見せられて「登れ」と言われているような気持ちになりました。。。

しかし数ヶ月経った今、多少はコードを読めるようになり、修正や機能追加をしようとしたときに「あの辺を見れば良さそう」というアタリもつくようになってきました。
断崖絶壁だったのが、少し勾配の大きい坂道くらいに感じられるようになりました。

この状態になるまでに自分なりに工夫したことや、チームメンバーに助けてもらいながら取り組んだことをまとめてみようと思います。

1人でもできること

サービスを触りまくる

環境構築をした後、ローカルで立ち上げたサービスをたくさん触りました(本番も触るには触るのですが、ローカルと違って気を遣うので主にローカルで触っていました)。
どんなサービスなのか、何ができるのか、誰が使うのか、どういうビジネスなのか…などを意識しながら触っていました。

ひとしきり触った後は、「その画面がどのモデルから構成されているか」に着目して改めて触ってみるなどもしていました。
実際の画面とコードを見ながら、自分の頭の中でマッピングしていくよう意識していました。

処理の流れを書き出す

コードを読むにあたっては、なるべくユーザーストーリーと対応付けながら処理の流れを追って、手元でまとめるようにしました。
いろいろなコードを追っていると少し前に追ったコードを忘れてしまったりするので、対策としてメモを残していました。

まとめることで思考も整理されますし、実際にコードを書く時にも見返せてとても役に立ちました。
ユーザーストーリーを意識するのもその機能・画面がなんのためにあるのかを理解するのに有用でした。

bin/rails console でモデルをいじくりまくる

モデルがどんな関連を持っているのか、この情報は取れるのか、このメソッドを実行するとどうなるのか、といった部分は、bin/rails console で実際にモデルを触って確かめていました。
他にも特定のカラムの値を変えたらどうなるか試してみたり、登録・更新・削除などしたときに周辺のモデルがどういう挙動をするか、なども試してみました。

モデル同士がどのように関連し合っているのかを知ることで、どのタイミングでどのモデルに影響が出るのか、というのが少しずつ掴めてきました。

db/schema.rbとにらめっこする

モデルがどんな情報を持っていて、どのモデルと関連しているのかという情報は db/schema.rb からも読み取ることができます。
具体的には「このモデルからこの情報って取れるんだっけ?」という時に主に見るようにしていました。
見た上で bin/rails console でも確認すると、理解が深まりました。

コミットログと Pull Request を見る

「ここはなんでこういう設計・書き方をしているんだろう?」と感じた部分はコミットログや Pull Request を見て、経緯を確認するようにしていました。
一見「あれ?」と思うコードでも、プロジェクトのバックグラウンドやサービスの特性などからそうしていることもある、ということに気が付けました。

同時に、自分が書いたコミットログや Pull Request を同様の目的で見られる場合も想定して、丁寧にわかりやすく書く必要があるということにも気が付けました。

人に頼ること

15分コードを読んでわからなければ質問する

15分はあくまで目安ですが、調べずに質問しても意味がないし、調べるのに時間を掛けすぎるのもよくない、という意味で15分と書きました。
調べても理解の糸口が見つけられそうになければ、Slack やビデオ通話でチームメンバーに聞くようにしていました。
チームメンバーに声を掛けるとすぐに反応をもらえるので質問しやすくてありがたいです🙏

毎日ペアプロする

1ヶ月ほど、とある機能開発で毎日のようにペアプロをしていた時期がありました。
先輩メンバーと一緒にナビゲーター・ドライバーを交代しながらペアプロしている中で、プロジェクト内の横断検索で目的のコードをサッと見つけているのを見たり、コミットメッセージから素早く Pull Request に行き着くのを見たりして「なるほど!」と思いました。

どうやって処理を追っているのかを間近で見ることができ、調べ方の引き出しが増えました。

コードレビューする

コードレビューは先輩が書いたコードを見られるだけでなく、わからない部分を教えてもらい放題で大変お得です。
また、自分の考えを表明して「確かにそうだね」と受け入れてもらえたら素直に嬉しいですし、反対に「こういう意図だからこうなんだよ」と教えてもらえると「そういう考えもあるのか!」という気付きになります。

そもそもコードレビュー自体が「コードを読む」行為に他ならないので、読み解く力を培うのに打ってつけだと感じました。

定期的なオリエンテーション

プロジェクト内のわからないことを聞いたり、先輩メンバーから「これを知っておくといいよ」といったお話をしてもらう場として、隔週でオリエンテーションを実施してもらっています。
お客様との打ち合わせの中でわからないお話があったり、ちょっと調べてわからないけどすぐに聞くほどでもないな、というようなことをストックしておいてこの場で質問させてもらうことが多いです。

実装などを通じて知ることとは別軸で新たなインプットができる機会になっています。

おわりに

コードを読み解くためにやったことを思いつくままに書いてみました。
今後は紹介した方法+αを駆使して、より速く・正確にコードを読めるようになるともう1歩前進できそうだと感じています。

とはいえまだ把握できていない機能があったり、プロジェクトに関する知識が不足している点も多々あります。 今はまだ急勾配に感じられる坂道ですが、少しずつ緩やかに感じられるよう、引き続き精進していきたいと思います。


永和システムマネジメントでは、コードを読む・書く技術を磨き合いながら坂道をともに緩やかにしていける仲間を募集中です! agile.esm.co.jp

EC2上にIPFS NODEを作成する

概要

宮崎よりお送りします。最近、ひょんなことから ブロックチェーンに興味を持ちましたyoshinoです。ブロックチェーン関連技術の1つであるIPFS*1の特徴である「コンテンツ指向」、「分散型WEB」の理解を深めるために、EC2上にIPFSサーバーを作成して動作を確認しました。

今回EC2上に作成するIPFS サーバーには大きく2つの役割があり、1つはIPFSネットワークに参加して、他のIPFS NODE(ピア)とIPFSプロトコルで通信すること、もう1つはHTTPリクエストによってIPFSのコンテンツへアクセスできるように橋渡しをしてくれるIPFS Gatewayとしての機能です。

IPFS Gatewayに、あるコンテンツを問い合わせて、そのNodeがそのコンテンツを所有していなければ、IPFSネットワークの中でそのコンテンツを持っている他のNODEがないかを確認し、持っているNODEがいれば、それをコピーして、そのコンテンツをレスポンスとして返します。

結果として、コンテンツを保有しているのは、2つのNODEになり、現在のHTTPプロトコルによる「ロケーション指向」では、少数のドメインがコンテンツを独占的に所有していることと比較して、「分散的」であり、「コンテンツ指向」であることが確認できます。

それでは、実際に確認してみることにします。

AWS EC2インスタンスを作成する

EC2インスタンスを作成します。IPFS Gatewayは8080ポート、IPFS Nodeは4001ポートを利用できるようにします。内容としてはこれだけです。今回利用したEC2の具体的な内容などは、こちらのリポジトリで参照いただけます。

IPFSサーバーを動かす

sshでEC2インスタンスに接続します。

ssh -i <SSH KEY> <Public IPv4 DNS>

IPFSコマンドのインストールから試していきたいという方は、こちらの記事が参考になるかと思います。今回はDockerを利用してIPFSサーバーを動かします。

sudo apt update
sudo apt install docker.io

IPFSのコンテンツを置いておくディレクトリ(/data/ipfs-staging)と設定ファイルを置いておくディレクトリ(/data/ipfs)を作成して、volume マウントします。

sudo mkdir -p /data/ipfs-staging
sudo mkdir -p /data/ipfs
export ipfs_staging=/data/ipfs-staging
export ipfs_data=/data/ipfs

IPFSをDocker image ipfs/go-ipfs を利用して動かします。

sudo docker run -d --name ipfs_host -v $ipfs_staging:/export -v $ipfs_data:/data/ipfs -p 4001:4001 -p 0.0.0.0:8080:8080 ipfs/go-ipfs:latest

お疲れさまでした。IPFS NPDEの完成です🎉

試してみる

コンテンツをアップロードする

コンテンツを適当に作成します。

mkdir blog
touch blog/20220709.txt
touch blog/20220710.txt
echo 'hello! 2022/07/09' > blog/20220709.txt
echo 'hello! 2022/07/10' > blog/20220710.txt

ipfs add でIPFS nodeにアップロードします(ディレクトリの場合は-rオプションをつける)

>> cp -r blog $ipfs_staging
>> sudo docker exec ipfs_host ipfs add -r /export/blog

added QmQ5ZdQnE8gszp4GdKzkz4ecQYR8ZnSigAMT4p1mRJsF2r blog/20220709.txt
added QmV8bR6LrPCk246df8p79UVY7B6XBQ2bTX4jfUHu6ZX4kB blog/20220710.txt
added QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S blog

QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S がblogディレクトリ以下を含むコンテンツのIDとなり、コンテンツ内容に対して一意となります。

今回作成した、EC2のGateway経由でのコンテンツは以下のようにアクセスすることができます。

http://<Public IPv4 DNS>:8080/ipfs/QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S

コンテンツを持っているIPFS NODEを確認する

先ほど作成したコンテンツを持っているNODEのIDを表示します。

>> sudo docker exec ipfs_host ipfs dht  findprovs QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S

12D3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

12D3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxというNODE のIDが1つだけこのコンテンツを持っていることを表します。 もちろん、このNODE IDは先ほどEC2上に作成したIPFSのNODEにほかなりません。

以下のようにNODE ID を確認することができます。

>> sudo docker exec ipfs_host ipfs id
"ID": "12D3xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

コンテンツを他のGatewayから取得する

ローカルでIPFSサーバーを起動します*2

ipfs daemon

http://localhost:8080/ipfs/QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S にアクセスして、先ほどEC2のIPFS NODEにアップロードしたコンテンツと同じものが見れることを確認します。

このコンテンツを持っているNODEのIDをもう一度確認してみます。

>> ipfs dht  findprovs QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S                                           ✘ 1

Qmdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
12D3xxxxxxxxxxxxxxxxxxxxxxxxxxxx

今度は先ほどのEC2のIPFS NODEのID12D3xxxxxxxxxxxxxxxxxxxxxxxxxxxx に加えて、ローカルのIPFS NODEQmdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx が確認することができました。

EC2に作成したIPFS NODEから、ローカルで起動したIFPS NODEにコンテンツが複製されていることが確認できます。

最後に

IPFS Gatewayを無料で公開する企業もでてきており、IPFSも気軽に利用できるようになっています。例えば、InfuraPintafleekなどがあります。

infuraのIPFS Gatewayを利用してみます。

https://ipfs.infura.io/ipfs/QmeZ26UugrZNRHYBKxUoUD7Q78kFgUFayvmhpiSJQPFc8S

また、infuraはIPFSにアップロードできるAPIも公開しているので、無料で、IPFSネットワークにアップロードすることを試すこともできます(infuraのNODEにアップロードしても、そのコンテンツが永続的に残ることはなく、定期的に削除されてしまいます。そのため、永続的にIPFSネットワークに残しておくために、例えば、今回紹介したような形で自前のIPFS NODEが必要になるかもしれません)。

今後もIPFSをはじめとしたブロックチェーンまわりの動向に注目していきたいです。

リファレンス

*1:IPFSに関してはIPFSとは何か?がわかりやすいです

*2:ipfsコマンドのインストールはIPFSのインストールを参照ください

Rails / OSS パッチ会オンライン 2022年7月のお知らせ

2022年7月の Rails / OSS パッチ会を 7月13日(水)に Discord でオンライン開催します。

この会をひとことでいうと、日頃のお仕事で使っている Rails をはじめとする OSS について、upstream にパッチを送る会です。

会には Ruby と Rails のコミッターである顧問の a_matsuda もいますので、例えば Rails に送るパッチのネタがあるけれど、パッチを送るに適しているかの判断やパッチを送る流れが悩ましいときなど a_matsuda に相談して足がかりにするなどできます。

開催時間は 17:00-19:00 となりますがご都合のあう方はぜひご参加下さい。

Discord の Rails/OSS パッチ会サーバーへの招待 URL は以下です👇

discord.gg

以下、前回の活動が関わる成果です。

sanfrecce-osaka: Active Admin

github.com

弊社メンバーの fugakkbn は Kaigi on Rails 2022 のオーガナイザーチームのメンバーでもあるため、Kaigi on Rails 2022 の CFP へのプロポーザルについて悩みを持ち寄っても良さそうです。

これからパッチ会に参加してみようという方も、ぜひどうぞ。Discord でお会いしましょう。


永和システムマネジメントでは、Ruby とアジャイルソフトウェア開発を通じてコミュニティと成長したいエンジニアを絶賛募集しています。

agile.esm.co.jp

Rust でもできる!RISC-Vシミュレータの作りかた 〜 From Ruby To Rust 〜

最近 ESM と RISC-V のロゴの配色が似ていると感じている @wat-aro です。

はたけやまさんblog.agile.esm.co.jp はおもしろい記事でしたね。
この記事は最後こう締めくくられています。

以上、Rubyを使ったシンプルなRISC-Vシミュレータの作り方のご紹介しました。

皆さんも梅雨の時期のおうち時間にお好きなプログラム言語でCPUシミュレータ自作なんていかがですか?

なのでこの記事を参考に Rust で RISC-V シミュレータを作成しました。

https://github.com/wat-aro/rv32sim.rs

処理の流れはだいたい元記事と同じですのでもっと詳しく知りたい人は元記事からどうぞ。

メモリ

まずメモリの定義です。
writeread と、初期データの読み込み用の initialize メソッドを持っています。
32 bit のデータを 8 bit ずつ分けて data に格納します。

#[derive(Debug)]
pub struct Memory {
    data: Vec<u8>,
}

const MEMORY_SIZE: u32 = 1024 * 1024;

impl Memory {
    pub fn new() -> Self {
        Memory {
            data: vec![0; MEMORY_SIZE as usize],
        }
    }

    pub fn write(&mut self, addr: u32, word: u32) {
        let index = addr as usize;
        self.data[index] = (word & 0xff) as u8;
        self.data[index + 1] = ((word >> 8) & 0xff) as u8;
        self.data[index + 2] = ((word >> 16) & 0xff) as u8;
        self.data[index + 3] = ((word >> 24) & 0xff) as u8;
    }

    pub fn read(&self, addr: u32) -> u32 {
        let index = addr as usize;
        self.data[index] as u32
            | (self.data[index + 1] as u32) << 8
            | (self.data[index + 2] as u32) << 16
            | (self.data[index + 3] as u32) << 24
    }

    pub fn initialize(&mut self, data: Vec<u8>) {
        self.data.splice(..data.len(), data);
    }
}

命令デコーダ

元記事とは違いデコーダオブジェクトを作らずに、デコードした結果どの命令かわかるようにした Instruction を作成しました。

#[derive(Debug, PartialEq)]
pub enum Instruction {
    Add { rd: u32, rs1: u32, rs2: u32 },
    Sub { rd: u32, rs1: u32, rs2: u32 },
    Or { rd: u32, rs1: u32, rs2: u32 },
    And { rd: u32, rs1: u32, rs2: u32 },
    Addi { rd: u32, rs1: u32, imm: u32 },
    Slli { rd: u32, rs1: u32, imm: u32 },
    Beq { rs1: u32, rs2: u32, imm: u32 },
    Lw { rd: u32, rs1: u32, imm: u32 },
    Sw { rs1: u32, rs2: u32, imm: u32 },
}

こういうデータを扱いたい場合に enum は便利ですね。
Instruction::decodeInstruction を返すようにしました。
元記事のほうでは NOP はデコーダの特別な状態になっていましたが、ここでは命令のひとつとして Instruction::Nop を実装しています。

impl Instruction {
    pub fn decode(inst: u32) -> Result<Instruction, Error> {
        let opcode = inst & 0x0000007f;
        let rd = (inst & 0x00000f80) >> 7;
        let funct3 = (inst & 0x00007000) >> 12;
        let rs1 = (inst & 0x000f8000) >> 15;
        let rs2 = (inst & 0x01f00000) >> 20;
        let funct7 = (inst & 0xfe000000) >> 25;

        match opcode {
            ...,
            // I-Type
            // I-Type
            0b0010011 => {
                let imm = (inst & 0xfff00000) >> 20;

                match funct3 {
                    0x0 => Ok(Addi { rd, rs1, imm }),
                    0x1 => Ok(Slli { rd, rs1, imm }),
                    _ => Err(Error::IllegalInstruction(inst)),
                }
            },
            ...
        }
    }
}

https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf にはお世話になりました。

CPU

まずはレジスタの定義から

#[derive(Debug)]
pub struct XRegisters {
    data: [u32; 32],
}

const REGISTERS_COUNT: u32 = 32;

impl XRegisters {
    pub fn new() -> Self {
        let data = [0; REGISTERS_COUNT as usize];

        XRegisters { data }
    }

    pub fn read(&self, addr: u32) -> u32 {
        assert!(addr < 32);

        self.data[addr as usize]
    }

    pub fn write(&mut self, addr: u32, value: u32) {
        assert!(addr < 32);

        if addr != 0 {
            self.data[addr as usize] = value;
        }
    }
}

メモリと同じように readwrite ができますが、32 個しかないため assert! を使っています。

CPU の構造体には元記事同様 program counter x_registers memory serial nop_count を持たせています。

pub struct Cpu {
    pub pc: u32,
    pub x_registers: XRegisters,
    memory: Memory,
    serial: Serial,
    nop_count: u8,
}

CPU は初期データをメモリに投入し、命令列を取得・デコードし、そして実行します。

impl Cpu {
    pub fn initialize_memory(&mut self, data: Vec<u8>) {
        self.memory.initialize(data);
    }

    pub fn fetch(&self) -> u32 {
        self.memory.read(self.pc)
    }

    pub fn run(&mut self) -> Result<Status> {
        let raw_inst = self.fetch();

        let inst = Instruction::decode(raw_inst)?;

        // Addi { rd: 0, rs1: 0, imm: 0 } を NOP として扱う
        if let Instruction::Addi {
            rd: 0,
            rs1: 0,
            imm: 0,
        } = inst
        {
            self.nop_count += 1;
        } else {
            self.nop_count = 0;
        }

        if self.nop_count >= 5 {
            return Ok(Status::Finished);
        }
        self.execute(inst)?;

        Ok(Status::Processing)
    }
}

命令を enum で定義したためパターンマッチで記述しやすくなりました。
加算・減算はオーバーフローを無視して値を返してほしいため、 wrapping_add wrapping_sub を使っています。

impl Cpu {
    pub fn execute(&mut self, inst: Instruction) -> Result<()> {
        match inst {
            Instruction::Add { rd, rs1, rs2 } => {
                let value = self
                    .x_registers
                    .read(rs1)
                    .wrapping_add(self.x_registers.read(rs2));
                self.x_registers.write(rd, value);
                self.pc += 4;
                Ok(())
            }
            ...,
            Instruction::Addi { rd, rs1, imm } => {
                // imm は 12bit の signed int なので 12bit 目が 0 なら正、1なら負
                let num = match (imm & 0x80) == 0 {
                    true => imm,
                    false => 0xfffff000 | imm, // 13bit目以降を 1 で埋める
                };
                let value = self.x_registers.read(rs1).wrapping_add(num);
                self.x_registers.write(rd, value);
                self.pc += 4;
                Ok(())
            }
            ...,
        }
    }
}

シミュレータ

CPU を実行するシミュレータを定義します。 CPU が NOP を 5 回処理して終了状態を返すかエラーが起きるまで実行を繰り返します。

pub struct Simulator {
    cpu: Cpu,
}

impl Simulator {
    pub fn new() -> Simulator {
        Self { cpu: Cpu::new() }
    }

    pub fn start(&mut self) -> Result<()> {
        loop {
            match self.cpu.run() {
                Ok(status) => match status {
                    Status::Processing => {}
                    Status::Finished => {
                        break;
                    }
                },
                Err(e) => {
                    return Err(e);
                }
            }
        }

        Ok(())
    }
}

後は main を用意すればシミュレータを動かすことができます。
4649.romhello.rom を実行した結果は以下。

$ rv32sim 4649.rom
--------------------------------------------------------------------------------
x00 = 0x0 (0)   x01 = 0x46 (70) x02 = 0x49 (73) x03 = 0x0 (0)
x04 = 0x0 (0)   x05 = 0x0 (0)   x06 = 0x0 (0)   x07 = 0x0 (0)
x08 = 0x0 (0)   x09 = 0x0 (0)   x10 = 0x0 (0)   x11 = 0x0 (0)
x12 = 0x0 (0)   x13 = 0x0 (0)   x14 = 0x0 (0)   x15 = 0x0 (0)
x16 = 0x0 (0)   x17 = 0x0 (0)   x18 = 0x0 (0)   x19 = 0x0 (0)
x20 = 0x0 (0)   x21 = 0x0 (0)   x22 = 0x0 (0)   x23 = 0x0 (0)
x24 = 0x0 (0)   x25 = 0x0 (0)   x26 = 0x0 (0)   x27 = 0x0 (0)
x28 = 0x0 (0)   x29 = 0x0 (0)   x30 = 0x0 (0)   x31 = 0x0 (0)
--------------------------------------------------------------------------------
pc = 0x18 (24)
$ rv32sim hello.rom
HELLO WORLD!!
--------------------------------------------------------------------------------
x00 = 0x0 (0)   x01 = 0x0 (0)   x02 = 0x0 (0)   x03 = 0x10000000 (268435456)
x04 = 0x0 (0)   x05 = 0xa (10)  x06 = 0x0 (0)   x07 = 0x0 (0)
x08 = 0x0 (0)   x09 = 0x0 (0)   x10 = 0x0 (0)   x11 = 0x0 (0)
x12 = 0x0 (0)   x13 = 0x0 (0)   x14 = 0x0 (0)   x15 = 0x0 (0)
x16 = 0x0 (0)   x17 = 0x0 (0)   x18 = 0x0 (0)   x19 = 0x0 (0)
x20 = 0x0 (0)   x21 = 0x0 (0)   x22 = 0x0 (0)   x23 = 0x0 (0)
x24 = 0x0 (0)   x25 = 0x0 (0)   x26 = 0x0 (0)   x27 = 0x0 (0)
x28 = 0x0 (0)   x29 = 0x0 (0)   x30 = 0x0 (0)   x31 = 0x0 (0)
--------------------------------------------------------------------------------
pc = 0x88 (136)

終わりに

シミュレータ作りを通して今まで避けていたビット演算と少し仲良くなれました。
梅雨はもう明けてしまいましたが、避暑がわりに CPU シミュレータを作ってみませんか?


永和システムマネジメント アジャイル事業部では Rust のお仕事もお待ちしています!
また一緒に CPU の勉強をしたいメンバーも絶賛募集中です!

agile.esm.co.jp

アジャイル事業部運営を支える技術

こんにちは、平田です。

普段私はマネージャーとして、事業部の運営やお客様との折衝を担当しています。業務でプログラミングをすることはほとんどないのですが、運営の中で必要な業務の自動化や省略化のためにコードを書くことがあります。そのいくつかを紹介したいと思います。

勤怠承認の省力化

メンバーの勤怠を月末に確認して承認する必要があります。以前、あまり使い勝手の良くない勤怠管理ソフトが導入されていた時代に、30名近くのメンバーの勤怠確認をしやすくするために、 CSV ファイルを読み込んで、ターミナル上で確認できるように作ったプログラムです。

その後、全社の勤怠管理ツールが変更になった後もその変更に追随したり、社内ルールに対応した形で重点チェックすべきデータのハイライト機能などを地味に追加したりしています。

Gem としては、対話型で確認できるようにするために pry 、ターミナル上でハイライトするために color_echo を使ったりしています。

全社で同じ勤怠管理ソフトを使っていることもあり、隣の部署のマネージャーにもプレゼントしようとしたのですが、普通の管理職は手元に Ruby が入っていないらしく、不要と言われてしまいました。

今後の計画としては、自分が暗黙的に注意して確認しているところを、プログラムレベルでチェックできるようにして、人間の確認にかかる時間をもっと短くしたいと思います。

収益情報の共有

管理部門が作ってくれている収益に関する Excel から情報を抽出して、見やすい形でメンバーにスプレッドシートで共有するスクリプトです。

roo を使って読み込んだ Excel ファイルから、自部署の計画値や実績値を抽出して、 Google スプレッドシートに書き出します。書き出した先のスプレッドシートでは、他の KPI 数値と一緒に収益情報もグラフ化して表示されるようにしています。

今後の計画としては、まずは直近で管理部門作成の Excel が Google スプレッドシートに変わるということでそれに対応していきたいと考えています。さらに KPI 情報の共有場所を Google データポータルのようなものに移行することができれば、プログラムなしでデータが共有できるようになるかもしれません。

その他にも事業部内の日報の盛り上がりを確認するために esa の記事数をカウントするためのスクリプト(設計が悪くてすぐにAPI の limit を超えてしまうので運用停止中…)等を書いたり捨てたりしています。 引き続き、コンピュータに任せられる部分は任せていって、楽しい事業部運営に注力していきたいと思っています。


最後に、株式会社永和システムマネジメントでは、Ruby とアジャイルソフトウェア開発を通じてコミュニティと共生しながら成長しつつ、マネージャの書くコードにツッコミを入れてくれるエンジニアを絶賛募集しています。

agile.esm.co.jp