iizukakの作業ログ

忘れる前にメモしよう

Go 言語で OpenGL (その1)

OpenGL を知りたいなと思って Go で OpenGL をさわってみた記事シリーズ第一弾です。

OpenGL のテキストで良いのがないかと色々ググってみたのですが、和歌山大学の先生が公開されている "GLFW による OpenGL 入門 - 和歌山大学" というフリーのテキストが内容がまとまっていて良さそうでした。このテキストでは、実装しつつ OpenGL (3D グラフィックプログラミング)を知ることができるようになっているようで、自分向きっぽい。

とりあえず章ごとくらいの粒度で、備忘録をブログを書いておきたいと思います。

また、実装には Go 言語を使います。あまり言語にはこだわりがないのですが、社内でけっこう流行っている模様。

リポジトリ

OpenGLTutorial にコードはまるっと置いておきます。ライブラリのバージョンやコードの実行方法についても README.md に書いておきます。

コード

4 章のコードはここです。

バーテックスシェーダとフラグメントシェーダ

4 章で最も重要なのは、バーテックスシェーダとフラグメントシェーダという概念です。3D 空間にある図形(頂点の集合)を 2D 平面に投影する、これをラスタライズといい、バーテックスシェーダはこのラスタライズを行う。フラグメントシェーダは 2D 平面に投影された頂点の情報をもとに、ディスプレイに表示する画素情報を生成する、というかんじっぽい。

このバーテックスシェーダおよびフラグメントシェーダは GLSL という言語で記述します。なので Go で書くというのは半分ウソで、シェーダ自体は C っぽい何かである GLSL で書きます。これを CPU で実行するプログラム内(Go で書いてる部分)でコンパイルして、GPU に送り込んで処理をする、という流れみたいですな。

4 章のバーテックシェーダの中身を見てみますと、

#version 150 core
in vec4 position;
void main()
{
    gl_Position = position;
}

で全部です。 in はシェーダへの入力を受け取る変数を宣言する構文。 vec4 は 4 要素のベクトル。 gl_Position = position; は、受け取った座標系を特に加工せずにレンダリングパイプラインの次に渡す、という意味。

次にフラグメントシェーダですが、

#version 150 core
out vec4 fragment;
void main()
{
    fragment = vec4(1.0, 0.0, 0.0, 1.0);
}

で、こちらは out で出力を定義しています。この vec4 では色を定義していて、 RGBA のベクトル。赤です。なので頂点の各画素を赤く表示するシェーダーになってると思います。

Go でシェーダをコンパイルする部分に関しては、 go-glサンプルリポジトリ のコードをまるっと持ってきました。基本的にはシェーダを文字列で gl.CompileShader に渡すだけです。GLSL のコンパイルエラーとか出るので、エラー処理はちゃんとやらないといけない。

   gl.CompileShader(shader)

    var status int32
    gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
    if status == gl.FALSE {
        var logLength int32
        gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)

        log := strings.Repeat("\x00", int(logLength+1))
        gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))

        return 0, fmt.Errorf("failed to compile %v: %v", source, log)
    }

実行結果

実行結果は以下の通り。

f:id:iizukak:20180406095351p:plain

えっ、ただの白い画面だけ? と一瞬思ってしまいましたが、それもそのはずで、まだ図形を定義していないです。処理する頂点が無いので画面が出るだけ。

所感

OpenGL というのはステートフルなツールだなぁと。最近のライブラリだったら、何らかのオブジェクトのインスタンスを作ってインスタンスのメソッドを呼んで〜というのが普通な気がしますが、 OpenGL は自分自身で状態を持っていて、状態を複数持ちたい場合は、整数値で指定する、みたいなことをやるっぽい。ほほう…

ディジタル回路設計とコンピュータアーキテクチャ[ARM版] 翻訳誤り一覧

いわゆる「ハリス&ハリス本」の ARM 版 の評判が良いようなので読んでみました。組み合わせ回路と順序回路の解説から始まり、HDL の導入、ARM のマイクロアーキテクチャの紹介、HDL を用いたシングルサイクルの ARMv4 サブセットの実装と実に盛りだくさんの内容になっています。私は HDL を全く知らない状態で読み始めたのですが、特に詰まるところ無く読み進められました。このような素晴らしい書籍を日本語で読めることに対して、翻訳者の皆様に感謝致します。

ところで、このような専門書にはありがちなことだと思うのですが、本書にも多少なりとも誤訳が含まれているように思われます。ここに自分が違和感を感じた箇所を掲載しておきます。私のミスリードもあるかと思います。ご容赦ください。

p.2

薄い灰色示している

薄い灰色で示している

p.4

…数を表現するかを論じる

日本語として違和感

p.59

Qにはその否定

~Qにはその否定

p.125

コードに「SystemVerilog」という文字列があるが不必要

p.169

コンピュータで稼働するすべてのプログラムは、同じ命令セットを使っている。

「あるコンピュータで」でしょうか

オペランドはメモリから来る場合や、レジスタから来るか場合、あるいは命令自体から来る場合もある。

日本語に違和感。「来るか場合」>「来る場合」

命令を機械語(マシンランゲージ)と呼ばれる形式に符号化される。

符号化する

p.170

通常的ではない

日本語として違和感

人間が読み易くした

人間に読み易くした

命令が実施する演算

演算を実施するというのは一般的な用法か?

p.172

一時変数s

「s」?

p.181

であるという点で普通でないのが、関数からの戻りは〜

訳に違和感

p.183

siddofsums

diffofsums の間違い?

p.185

怖されれる

壊される

p.193

テクスト

テキスト

(SP)スタックの

(SP)をスタックの

GCC基づいている

GCCに基づいている

p.196

リテラルプールを作った

使った

p.201

備える、

備える。

p.204

表6.19

表6.19が何ページに記載されているのか分からなかった

FB08 CPU 製作記 [03 - データフロー図]

それほど進んでいないのですがメモしておきます。まず、現状のデータフローを図に書き起こしました。それが以下です。

f:id:iizukak:20170115153728p:plain

先週からいくつか差分があります。

  • SHIFTR, SHIFTL 命令を廃止しました
    • IC 数を減らす(アキュムレータのバスドライバがなくて済むようになった)のと、コントロールシグナルのピン数(16)がカツカツなので消しました。CPU の命令としてビットシフトできなくなってしまいますが、これはアキュムレータ自分自身の値を足すことで左シフトはできるのでどうにかなるといいな…というかんじ。右シフトをどうするかは要検討。
  • 論理演算命令を追加しました
    • AND, OR, XOR です
  • オペコードを 5bit にしました。
    • 先週までオペコードは 4bit でしたが、1bit 増やしました。これは、単に命令が 16 種類より増えてしまったから。また、SRAM のアドレスが 11bit なので、プログラム ROM のアドレスも 11bit にしてしまったほうが扱いが楽かなというのもあります。

74HC181 が届きました。

f:id:iizukak:20170114143138j:plain

まだまだ設計段階なので少し先走った注文でしたが、こうやってパーツが届くとやる気が少しアップするので、良いです。

以上です。地道に進めます。

FB08 CPU 製作記 [02 - 大雑把な設計]

前回に引き続き、FB08 CPU の設計について。

設計の進んだ分をメモしておきます。

  • データバス幅を 8bit とした
    • 前回までは 4bit 幅にしようと思っていましたが、8bit にしたところで言うほど回路の複雑度が増さない & IC 数も増ええない & 8bit にすると実装できるプログラムの幅が大幅に広がるため、8bit 幅とすることとしました
  • ALU に 74HC181 を使用することにした
    • eBay で 74HC181 のデッドストックが 出品されていた ため、購入しました。もともとは EEPROM を使ったルックアップテーブル方式で ALU を構成しようと思っていましたが、変更になります
  • プログラム ROM として、29F1615 を使用することとにした
    • 若松通商MX29F1615PC-10 という EEPROM があるようで、これが 1word 16 bit なのでプログラム ROM を収める ROM としてちょうどよい(最長の命令フォーマットを 16 bit にしようと思っていたため)
  • 命令デコーダにも 29F1615 を使うこととした
    • 命令デコーダ論理回路で組むのが本筋なのでしょう。しかしこれが面倒なので、 EEPROM で作ってしまうことにしました
  • 1オペランド方式の CPU とすることにした
    • 最近の CPU ではなかなか無いとおもいますが、 1 オペランド方式とします。ひとつに CPU の簡素化のため、もうひとつにデバッグの容易さのためです。 自作アーキテクチャ CPU 一号機ですし、バグがふんだんに盛り込まれていることは確実です。ですから、CPU そのものも、CPU の上で動くプログラムもどちらもデバッグしやすくなければいけません。ロジック IC で作る CPU では、デバッグというと LED を光らせるとか電圧を測定することになります。1 オペランド方式の CPU の場合、様々な値がアキュムレータを経由することになるため、アキュムレータの値を LED に表示しておけば、デバッグが楽であろうという目論見です

命令フォーマット

命令のフォーマットを、以下の 3 つとします。

1: Addressed
[OPCODE 4bit][ADDRESS 12bit]

2: immediate
[OPCODE 4bit][IMMEDIATE 8bit]

3: Opcode Only
[OPCODE 4bit]

命令セット

OPCODE を 4bit としたため、命令の種類は 16 種類です。現在のところ、以下の命令セットでどうかな、とおもっていますが、恐らく変更すると思います。

転送

1. LDI (2)
イミディエイトデータをアキュムレータに転送

2. IN (3)
IN ポートからアキュムレータに転送

3. LD (1)
アキュムレータから RAM に転送

4. LDR (1)
RAM からアキュムレータに転送

5. OUT (3)
アキュムレータから OUT ポートに転送


ジャンプ

6. JMP (1)
無条件ジャンプ

7. JC (1)
キャリーフラグ有効でジャンプ

8. JNC (1)
キャリーフラグ無効でジャンプ

9. JZ (1)
ゼロフラグ有効でジャンプ

10. JNZ (1)
ゼロフラグ無効でジャンプ


演算

11. ADD (1)
アキュムレータ = アキュムレータ + RAM

12. SUB (1)
アキュムレータ = アキュムレータ - RAM

13. SHIFTR (3)
アキュムレータを右シフト

14. SHIFTL (3)
アキュムレータを左シフト


デバッグ
15. CLK_AUTO (3)
システムクロックを有効にする

16. CLK_MANUAL (3)
システムクロックを無効にする(マニュアルクロックを使用する)

アキュムレータ <--> RAM 間のデータ転送命令のネーミングが決心がついていません。CLK_AUTO と CLK_MANUAL はステップ実行のデバッグ用です。CKL_MANUAL でブレークポイントをはってマニュアルクロックでデバッグしたあと、CLK_AUTO でシステムクロックを有効にして処理を継続します。ステップ実行、絶対必要。

お正月休みの進捗はこんなところ。。

2017年の当面の目標。CPU 作りについて

去年「CPUの創りかた」を読んで、実際に TD4 (この本をまるまる一冊かけて解説されている CPU ) を実装してみたところ、これがなかなか面白い体験でした。それからというもの、「コンピュータアーキテクチャのエッセンス」などを読んで、CPUについて思いを馳せていたのですが、理論を学ぶより作るほうが楽しかった(笑)

そこで、今年の当面の目標として、独自設計の CPU を作ってみたいと思っています。昨今では、CPU を自作するとなると手ハンダではなく FPGA を使うが主流と思います。しかし、「CPU の創りかた」を読んで慣れているのもありますし、ロジック IC に謎の愛着があるのもあって、とりあえずひとつロジック IC を使った CPU を作ってみることにしました。

(脱線しますが、ロジック IC って、どことなく LEGO ブロックに似ていませんか。どちらも基本的な要素を組み合わせて、複雑なものを作り上げられるかんじが。)

まず作成する CPU の名前を決めました。「FB04」です。「CPU の創りかた」では、16 ステップのプログラムしか ROM に格納できなかった関係上、ラーメンタイマあたりがプログラミングの限界でした。FB04 では、ぜひとも FizzBuzz を実行したい。これが "FB" の意味です。"04" はデータバス幅が 4bit の意味です。主な仕様としては、

  • ハーバードアーキテクチャ
  • FizzBuzz が実行可能であること
  • データバスは 4bit 幅とする
  • プログラム ROM は EEPROM を使って実装する
  • 命令デコーダは EEPROM を使って実装してもよい(妥協)
  • FizzBuzz を実行する関係上、アルファベットが表示可能な、キャラクタ液晶を搭載する

いま決めているのは以上です。FizzBuzz を実行したいのは、FizzBuzz には、プログラミングにおいて不可欠な要素(条件分岐、四則演算、比較、ループ、データの出力)が揃っていて、かつシンプルで CPU の性能を示すために良いと思うからです。

三が日を使って、簡単な回路図もどきを描きました。

f:id:iizukak:20170103223647j:plain

次は、使用予定のロジック IC の仕様書とにらめっこしつつ、KiCad で回路図を書こうと思います。お正月休みが終わるまでに、できるだけ進捗を出しておきたいところです。

HD44780 キャラクタ液晶を、LiquidCrystal ライブラリ無しで使う

Arduino でキャラクタ液晶を使う場合は、LiquidCrystal という Arduino IDE 付属のライブラリを使うのが一般的ですが、ライブラリなしで使ってみようという試みです。

キャラクタ液晶には、だいたい HD44780 互換コントローラが乗っています。記事後方のスクリプトも HD44780 互換コントローラが載っている液晶モジュールを想定します。ちなみに、今回は秋月電子で買った SD1602HUOB という小型の16桁x2行のモジュールを使っています。

お正月ということで、新年のメッセージを表示してみました。HD44780 にはカタカナのフォントも搭載されています。

f:id:iizukak:20170101081039j:plain

f:id:iizukak:20170101081122j:plain

配線は下記のスクリプトのコメントの通りです。Arduino では、いくつかのピンをまとめて扱うことができて、複数のビットをまとめて送ることができるので便利です。今回は PORTD を使っています。

コントラスト調整用の抵抗には、 10K の可変抵抗を使いました。

Makeblock Me Orion からブレッドボードやユニバーサル基盤を使う

引き続き Makeblock Me Orion で遊んでいます。Orion は入出力のための Arduino Uno のようなピンついておらず、RJ25 モジュラージャックが 8 つあるのみです。これは MakeBlock の各モジュールと接続することが前提としてあるのでこうなっているのだと思うのですが、やはり MakeBlock のモジュールだけでは十分ではありません。ユニバーサル基板やブレッドボードで使いたいと思うのが人情です。

どうするかというと、秋月の6極6芯モジュラージャックDIP化キット を使って、Orion の RJ25 ジャックを 2.54mm ピッチに変換することができます。Orion のピン配置は ここ にあります。

さて、ブレッドボードで L チカしてみます。Orion の 3 番ポートを以下の画像のようにつなぎます。6芯のうち、一番左が Uno でいう 13 番ピンなので、一番左に LED のプラスを接続、適当な抵抗を挟んで右から 3 番目の GND の芯に接続します。これで、Arduino IDE から Blink を書き込むと L チカできます。

f:id:iizukak:20161015165445p:plain

秋月のおかげで簡単に Orion 用のモジュールが作れそうです。