ゆけッ!やっせんぼ・とよちゃん! : サイクリング日記
HASH-TAG : プロジェクトびっくりちゃん



2019年11月25日(月) 22.0/11.9℃

Update: 05:54:09
サイクリング
今日もサイクリングは無しでした。
昨夜寝る前に考え事を始めちゃったらなかなか寝つかれなくて、今朝はなんとかかんとか 05:30 過ぎに起きれました。 もっと寝ていたいです。

Update: 18:25:51
プロジェクト「びっくりちゃん」
ここ2週間ほど趣味の工作関連についての書き込みがないけどどうしたのかなぁとご心配いただいている読者のかたがたがいるかどうかは知らないですが、実は新しいプロジェクトを発動して 結構色々とやっていたのですよ。
新しいプロジェクト名は「びっくりちゃん」です。\(^O^)/ジャジャーン!

プロジェクトの内容はですね、FPGA にカスタムでデザインした MCU を組み込んでやろうと言うものです。 MCU コアの名前も「Bikkurichan」です。

2週間ほど前に C6745 と MAX10 を載せた新しい DSP 基板と新しいデジタルアンプが動いて、「さてこいつを今運用中の Audio Processor に組み込んでやろうかな」と思ったとき、ふと気づいたのです、今 PIC16F でやっているロータリーエンコーダーとか赤外線リモコンの受信とか7セグメントLEDの表示コントロールとかの処理を全部 MAX10 でやればいいじゃんと。 FPGA 初心者の私でも PIC16F でやっている処理をそのまま MAX10 に載せるのは、うーんと頑張ればできると思うのですが、それじゃあ面白くもなんともないので、だったら MAX10 にマイコンを組み込んでその上で処理を動かしてやろうと思ったのです。 そう言うわけで、ここ2週間、簡単な MCU の命令セットをデザインして、MCU コアを MAX10 に組み込んで、ハンドアセンブラでチョー簡単な LED チカチカプログラムを書いて、巧く動かせるかどうか実験していたのでした。

なんでわざわざ自分で MCU をデザインすることにしたかと言いますと、PIC16 をそのまま組み込むのは糞ダサいし、かといって世の中にあるオープンソースの CPU アーキテクチャは重すぎてオーバースペックで使い物にならないし、と言う理由なのです。 というか趣味なので出来合いのものを使っちゃダメでしょ。 大人の趣味は、プラモデル製作とはレベルが違うのですよ。 そこはできる限り一から自分で作らないとですよ。

とりあえずでデザインしたプログラムはこんなです。 r01、r02、でカウントして r03 の内容を IO 出力するってものです。 r03 の 0 ビットが LED に繋がれていて  r03 がカウントされると LED がチカチカするって仕組みです。 MCU の動作クロックは、24.576MHz の 4倍 の 98.304MHz で、大体2秒サイクルでチカチカしてました。 LED が1サイクル点滅するには r03 が2回カウントされないといけないです。 r03 カウンタはループ 2457600 回に1回カウントしているので、逆算すると大体1命令 2.2 クロックで動作しています。 上出来ですね。


 loop: adi   r01, #1
       cmi   r01, #24576
       adi,z r02, #1
       add,z r01, r00, r00
       cmi   r02, #100
       adi,z r03, #1
       add,z r02, r00, r00
       out   r03
       jmp   loop

思ったより簡単に MCU が作れたので、自分でもびっくりです。 ただ、これからが大変なのですよ。
これから
 (1) MCU のアーキテクチャを固める
 (2) ちゃんとした命令セットをデザインする
 (3) コンパイラも作らないとね
と言う作業が待ってます。
(1) は、普通のタイプのプロセッサでは面白くないので、イベントドリブンなアーキテクチャにしてやろうと思っています。 割り込みみたいな従来のうざい仕組みは排除しようかなとも目論んでいます。 と言うか、イベントドリブンってこと自体が割り込み処理を包括しています。 当然ですが、今時はマルチコアですよね。
(2) は、RISC ベースで、2クロックに1命令を実行できるようにしたいなと思っています。 音声の信号処理を効率よく実行できる固定小数点の DSP 命令セットも組み込もうと思っています。
(3) が一番大変なんですよね。 C# で GUI ベースの簡単な IDE もどきを作って、VHDL の ROM コードを吐き出すようにしたいなぁ。

あ、ちなみにですが、プロジェクト名「びっくりちゃん」は、AKB48 Team8 の行天優莉奈さんの愛称から頂きました。

#FPGA #MAX10 #Audio Processor #プロジェクトびっくりちゃん




2019年12月01日(日) 12.0/2.0℃

Update: 08:19:29
サイクリング
今日もサイクリングは無しでした。
03:00 に目が覚めて YouTube を観てたんですが、残念ながら 04:30 に目覚ましが鳴っても外に出る気にはなりませんでした。
いやぁ、寒いですよ。
なので、05:00 過ぎに二度寝しました。

Update: 18:52:38
プロジェクト「びっくりちゃん」
先日「FPGA にカスタムでデザインした MCU を組み込む」と書いてから、なんとなく構想が固まってきました。
やるべきことは、大きく分けて
 (1) MCU のアーキテクチャを固める
 (2) ちゃんとした命令セットをデザインする
 (3) コンパイラも作らないとね
でしたね。

(1) のMCUアーキテクチャ(最大型)は、こんな感じです。 あくまでラスボス的な最大型で(笑)、PIC のプログラムを組み込むくらいなら MCU コアを含むイベント制御ブロック1個と GPIO だけで十分です。
ちなみに、私が組み込もうとしている MCU では レジスター(REGISTERS)は、32ビット(16ビットもありかもね)で、256個あります。 ほとんどの処理はレジスターだけで事足りるので、イベント制御ブロックの中の RAM はオプションになります。 スタックとか配列処理とか必要になったら組み込みます。


(2) と (3) については、LLVM を使わせてもらうことにしました。 LLVM については、clang というと知っている人が多いと思います。 clang は、GCC の何か新げなやつと思っている人もいるかもしれないですが、ライブラリの互換性はあるものの、全く違うものと思って良いと思います。
LLVM の基本的なアイデアは、コンパイラーやその他ツール類をモジュール化して、色々と柔軟性を持たせようというものです。
構成としては、(間違っているかもしれないですが)大まかには、
 (A) 多種多様な高級言語をハード依存のない中間言語に翻訳する「フロントエンド」
 (B) 中間言語を最適化する「最適処理」
 (C) 中間言語を機械語に翻訳する「バックエンド」
 (D) 翻訳された機械語をライブラリと結合して最終的な実行形式にする「リンカー」
 (E) その他のツール
のようになっています。
前記の clang は、(A) の部分に入っていて、C、C++、Mac でお馴染みの Objective-C などをコンパイルしてくれるプログラムです。

LLVM を使わせてもらうことの最大の利点は、Eclipse 等の統合環境(IDE)があるところですね。 C言語で開発して、最適化された中間言語コードまで半自動で出力してくれます。 最高ですよね。 私がやらなくちゃいけないのは、バックエンドを書くことです。 これは大したことではないですね。
私の場合のバックエンドの処理は、中間言語コードを自分で設計した MCU の機械語に変換して、FPGA の ROM イメージ(VHDLとか)に出力することです。 上図の ROM ってところに組み込むコードですね。

それでは、その中間言語ってどなものなのかという点ですが、LLVM では LLVM Intermediate Representation(IR) というアセンブラに似た言語になります。
例として、ロータリーエンコーダーの回転方向をチェックしてボリュームを上げ下げする処理を書きますと、C言語ではこんな感じですかね。 この re_event_handler() は、例えば 10[ms] のタイマーイベントとか、GPIO ポートの pahse-A のビットが変化したというイベントとかが発生したときに イベント制御から実行されるようにします。 GPIO ポートのアドレスは、仮に 0x80000000 としておきました。


#include <stdio.h>
#include <stdlib.h>

#define  _RE_PHASEA    (0x00000001)
#define  _RE_PHASEB    (0x00000002)
unsigned int*  GPIO = (unsigned int*)0x80000000u;

volatile static unsigned int  re_oPhaseA;
volatile static unsigned int  volume = 0;

int  re_event_handler()
{
    unsigned int  v;

    // check the phase-A for the value change
    v = *GPIO & _RE_PHASEA;
    if (re_oPhaseA != v) {
        // save the current value
        re_oPhaseA = v;

        // check the phase-B at phase-A rising-up timing
        if (v) {
            // check the phase-B for the rotation direction
            if ((*GPIO & _RE_PHASEB) == _RE_PHASEB) {
                // volume up
                ++volume;
            } else {
                // volume down
                --volume;
            }
        }
    }

    return 0;
}

で、このプログラムを clang にかけて、中間言語で最適化するとこんな感じのコードが生成されます。 実際に自分で設計した MCU コードに落とす段階で もうちょっと最適化が必要ですが、ここまで自動でやってくれるのなら、あとは屁でもないですよね。

@GPIO = dso_local local_unnamed_addr global i32* inttoptr (i64 2147483648 to i32*), align 8
@re_oPhaseA = internal global i32 0, align 4
@volume = internal global i32 0, align 4

; Function Attrs: norecurse nounwind uwtable
define dso_local i32 @re_event_handler() local_unnamed_addr #0 {
  %1 = load i32*, i32** @GPIO, align 8, !tbaa !2
  %2 = load i32, i32* %1, align 4, !tbaa !6
  %3 = and i32 %2, 1
  %4 = load volatile i32, i32* @re_oPhaseA, align 4, !tbaa !6
  %5 = icmp eq i32 %4, %3
  br i1 %5, label %16, label %6

; <label>:6:                                      ; preds = %0
  store volatile i32 %3, i32* @re_oPhaseA, align 4, !tbaa !6
  %7 = icmp eq i32 %3, 0
  br i1 %7, label %16, label %8

; <label>:8:                                      ; preds = %6
  %9 = and i32 %2, 2
  %10 = icmp eq i32 %9, 0
  %11 = load volatile i32, i32* @volume, align 4, !tbaa !6
  br i1 %10, label %14, label %12

; <label>:12:                                     ; preds = %8
  %13 = add i32 %11, 1
  store volatile i32 %13, i32* @volume, align 4, !tbaa !6
  br label %16

; <label>:14:                                     ; preds = %8
  %15 = add i32 %11, -1
  store volatile i32 %15, i32* @volume, align 4, !tbaa !6
  br label %16

; <label>:16:                                     ; preds = %6, %0, %14, %12
  ret i32 0
}

なんでこれが屁でもないのかが分からないという人のために解説します。(笑)
なんかごちゃごちゃした魔法みたいな訳の分からないコードに見えますが、アセンブラがわかる人ならすぐに理解できますが、基本的に処理するべき命令は
 load
 store
 and
 icmp
 br
 label(命令じゃないけど)
しかないんですよ。 あとは全部飾りです。(笑) つまり、上記の5つの命令を組み込めば、ロータリーエンコーダーの処理ができちゃうんですよ。 屁でもないでしょ!(笑)
実際のところ、マイコンでやっている処理なんて 上記の5つくらいの命令(あと足し算と、引き算と、掛け算が欲しいかも)がごちゃごちゃ組み合わされている程度のものなのですよ。 屁でもないでしょ?(笑)

#FPGA #プロジェクトびっくりちゃん #Audio Processor




2019年12月08日(日) 10.0/0.5℃

Update: 09:15:32
サイクリング
今日もサイクリングは無しでした。
昨日は、今巷で面白いと噂の「鬼滅の刃」を Amazon Prime で見つけてしまって、26話一気観してしまいました。
毎朝どんどん寒くなっていませんかねぇ。 今朝なんか、水道の水が「冷たい」を通り越して「痛い」になってましたよ。(┳◇┳)

Update: 23:59:58
プロジェクト「びっくりちゃん」
実は、今週末は金曜日に休みをもらって、3連休でした。 金曜日は何もせずに一日だらだらして、昨日の土曜日は鬼滅の刃を一気観して、今日はプロジェクト「びっくりちゃん」のバックエンドの開発をやっていました。

プロジェクト「びっくりちゃん」のバックエンドは、
 (1) 今 PIC で動作しているプログラムを FPGA に載せた時に実行させるプログラムに書き換えて、
 (2) それを clang でコンパイルして LLVM-IR を吐かせて、
 (3) 生成されたアセンブラを Bikkurichan 用に変換できるようにするバックエンドを開発する
というなんともややこしい方法をとっています。 この方が Bikkurichan に組み込むプログラムもバックエンドも同時に開発できるし、どうしたら良いか分からなくて仕様がなかなか固まらい部分がイメージできるようになって効率が良いのですよ。
でもまだ完成までは先が長そうです。

Update: 23:59:59
FPGA のプログラミング
FPGA の開発では、Hardware Description Language (HDL) というハードウエアをプログラムみたいに記述する言語で書くのですが、代表的なのに VHDL と Verilog というのがあるんです。 歴史的には VHDL の方が古くて、仕様的にはガチガチの堅苦しい言語なのに対して、Verilog はC言語みたいにやわやわ系なんですよ。 今までは、私は VHDL を使っていたのですが、今週から Verilog に切り替えました。 そういうわけで、Bikkurichan も Verilog で開発します。
というどうでも良い宣言でした。( ̄∀ ̄)

#FPGA #プロジェクトびっくりちゃん #Audio Processor




2019年12月19日(木) 9.5/6.0℃

Update: 06:20:49
サイクリング
今日もサイクリングは無しでした。
起きたら 06:00 過ぎでした。
早起きできなくなってしまいました。
というか、早寝できなくなってしまいました。

Update: 19:06:32
プロジェクト「びっくりちゃん」
「屁でもない」って書いた割には時間かかってます。(≧Δ≦)
とりあえず、今 PIC16F で動いているファームウエアを LLVM-IR にして FPGA に組み込むアセンブラの仕様を決めているところです。
ほとんどの部分は何も考えることないんですけど、
 (1) サブルーチンコール
 (2) Phi ノード
の2点でつまづいています。

(1) については、Bikkurichan Core はスタックを持っていないしローカルメモリもレジスタにアサインされているので、従来の言語が持っているようなサブルーチンコールの機能をどうやって実現しようかと悩んでいます。
スタックを持たせようと思えばできるんですけど、そんなのなんか糞ダサいんですよね。

(2) については、アセンブラとしては概念が高級かつ抽象的なんですよね。
どの経路で分岐してきたかによって代入する値を変えるような時に使うみたいなんですが、Phi ノード命令を実行するためには実行履歴を保持しなくちゃいけないんですよ。 ループ処理の時によく出てくるんですよね。
アセンブラにするには、結局ジャンプ命令の組み合わせにするしかないんですよね。 何か他にいい方法がないのか思案中です。

他には、select 命令や switch 命令っていうのがあって、if 文とか switch-case 文で出てくるみたいなんですが、これもジャンプ命令の羅列にするしかないよなぁと思っています。

とは言っても本当に大したことなくて、演算命令や比較命令については signed と unsigned があって命令数が倍になっちゃうんですけど、今のところ組み込む予定の命令の数は 60 弱ってとこです。 命令コードは8ビットとってあって 256 命令までいけるのですが、余裕のよっちゃんですわ。
余った分は、拡張命令やマクロ命令みたいに使えるようにできたらいいなと思っています。

命令長については、32ビットの固定長にしたかったのですが、16、32、48 ビットの可変長にすることにしました。 でも、常に48ビットをフェッチするようにして、命令フェッチは1クロックで済ませます。( ̄∀+ ̄) 当然ですが、命令実行中に次の命令をフェッチしますよ。

#FPGA #プロジェクトびっくりちゃん #Audio Processor