FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

「半導体・ハードウェア開発 2017」Advent Calendarが終了しました

qiita.com

12/01から始めた「半導体・ハードウェア開発 2017」Advent Calendar、全ての日でエントリが埋まり、無事に終了しました。

まずはご協力頂いた皆様、どうもありがとうございました。皆様のご協力がなければ、25日全てを埋めることは不可能でした。改めて感謝致します。

25日の日程のうち、私は10日を担当しました。途中で仕事が忙しくなって結構苦しくなりましたが、何とか完走できてよかった。 ご協力してくださる方は非常にうれしかったですが、やはり去年のAdvent Calendarにも書いてくださった方が多いですね。 そういう意味では、私自身あまり進化していないかもしれない。。来年もやるかどうかは微妙ですね。

  • 終結果(2017/12/26 0:00時点)
    • カレンダー数 : 593
    • 25日完遂カレンダー数 : 230/ 593
    • 購読者数ランキング 114 / 230

ランキングで言うと非常に中途半端ですが、でも593カレンダーのうち完遂できたのはわずかに230カレンダーですので、その中で114位というのはかなり頑張った方だと思っています。

平日は夜中に頑張れますが、休日は別の用事もあったりして苦手で、他の方が支援して下さったのはありがたかったです。

今年はRISC-Vが徐々に知名度が上がってきたかなと思います。RISC-V関係のイベントで2件発表もさせてもらいました。 が、RISC-Vだけではネタもあまりないので、今年は少し範囲を広げた、という次第です。

やはり、去年と比べて機械学習や高位合成、などの話題が増えた、という印象ですね。

Google Voice Kitを使ってみる(3. 音声→文字を認識するアプリケーションの作成)

Google Voice Kitを使ってみる、続き。 前回、アプリケーションが動作するようになったので、自分で音声認識のプログラムなどを作ってみたい。

以下のサイトを参考にして、プログラムを作成した。ちなみ、Google Speech APIを使用するために課金の情報を入力する必要がある。 1年間の無料評価ライセンスがあるのでそれを使用しているが、1年間の間にちゃんとしっかり活用しないとなあ。。。

aiyprojects.withgoogle.com

  • /home/pi/src/motion.py
#!/usr/bin/env python3

import aiy.audio
import aiy.cloudspeech
import aiy.voicehat

def main():
    '''Start voice recognition when motion is detected.'''
    button = aiy.voicehat.get_button() 
    recognizer = aiy.cloudspeech.get_recognizer()
    recognizer.expect_phrase('repeat after me')
    aiy.audio.get_recorder().start()
    while True:
        print('Press the button and speak')
        button.wait_for_press() 
        print('Listening ...') 
        text = recognizer.recognize()  # 音声認識を実行する。
        print(text)
        aiy.audio.say('You said ')
        aiy.audio.say(text)

if __name__ == '__main__':
    main()

サンプルプログラムにはaiy.audio.say('You said ', text)と書いてあるが、実行中にエラーメッセージが出てしまうので修正しなければならなかった。

Press the button and speak
Listening ...
repeat after me
Unknown language: repeat after me
Valid languages:
en-US
en-GB
de-DE
es-ES
fr-FR
it-IT

Usage: pico2wave <words>
  -w, --wave=filename.wav     Write output to this WAV file (extension SHOULD be .wav)
  -l, --lang=lang             Language (default: "en-US")

Help options:
  -?, --help                  Show this help message
      --usage                 Display brief usage message
Traceback (most recent call last):
  File "./src/motion.py", line 22, in <module>
    main()
  File "./src/motion.py", line 19, in main
    aiy.audio.say('You said ', text)

これを実行して、音声認識を実行する。

$ ./src/motion.py 
Press the button and speak
Listening ...
what time is it now     # 認識結果
Press the button and speak
Listening ...

ある程度きちんと認識できる。ただし、"Turn on the light", "Turn off the light" の「ターン」の発音が下手くそすぎて認識されない。 スマートフォンGoogleに喋らせるとうまく認識するので、これは日本人の発音の問題かなあ。 日本語のAPIがきちんと使えるようになればいいのだが。

f:id:msyksphinz:20171224232547p:plain

アプリケーション実行中はボタンのLEDが点灯する。格好いい。

関連記事

Google Voice Kitを使ってみる(2. サンプルプログラムの実行)

Google Voice Kitを使ってみる、続き。 前回、組み立てが完了したので次にソフトウェアをダウンロードして実行する。

まず、MagPiのウェブサイトからISOファイルをダウンロードする。 MicroSDカード(4GB以上?)を用意し、EtcherでISOファイルを書き込む。

aiyprojects.withgoogle.com

f:id:msyksphinz:20171223223403p:plain

SDカードをRaspberry-Pi3に差し込み、ブートすると、ディスプレイにデスクトップ画面が現れる。

f:id:msyksphinz:20171223230907p:plain

ワイヤレスLANを接続する。

f:id:msyksphinz:20171223235114p:plain

Voice / Microphone テストを実行しよう。"Check Audio"プログラムをクリックする。

f:id:msyksphinz:20171223235428p:plain

マイクから、"Front, Centre"と聞こえる。次に、"Testing 1,2,3..." とスピーカーに喋りかけると、その声がリピートされる。

成功だ!ちゃんと動作していることを確認できた。

関連記事

Google Voice Kitを使ってみる(1. 組み立て)

Google Voice Kit、前から欲しかったのだが、ついにAIY Projectsから発送され、自宅に到着した。 実際には先週には到着していたのだが、RISC-V Dayなど忙しさが重なり、試行できなかった。 土日でようやくトライできるようになったので、早速やってみよう。

ホーム - Raspberry Pi Shop by KSY

これが内容物一覧。

f:id:msyksphinz:20171223223702p:plain

Raspberry-Pi3が必要。

f:id:msyksphinz:20171223223815p:plain

Raspberry-Pi3に接続することでくみ上げる。

f:id:msyksphinz:20171223223857p:plain

ちなみに、組み立てるためには、マニュアルには書かれていない細目のスクリュードライバーが必要なので注意。

f:id:msyksphinz:20171223224833p:plain

あと、マイクの接続には両面テープが必要と書いてある。別に両面テープでなくてもいいのだが、普通のテープだと少し格好悪い。 はんだ付けの角が出っ張ってしまい、マイクが浮いているのが少し残念。

f:id:msyksphinz:20171223232114p:plain

完成形は以下のようになる。

f:id:msyksphinz:20171223224027p:plain

関連記事

「スターウォーズ エピソード8 最後のジェダイ」を見てきた

スターウォーズ大好きなんです。。。

starwars.disney.co.jp

本当は公開当日に行きたかったけど、忙しかったので一週間遅れ。レイトショーで見てきた。2時間30分って長いよ!

ストーリーはエピソード7ほど大きくは動かなかったような気がする。いろんな人が死んだけど。 事前情報は相変わらず何もないので、結構ドキドキハラハラしながら見ていた。 なんか旧三部作よりも、ファースト・オーダーとレジスタンスの戦力差が大きく見えて、ファースト・オーダー強すぎ!って感じがするね。

そんな中、どうやってエピソード9につなげていくのか、気になるところ。 今回はそんな、繋ぎの回って感じだった。

サントラ出たら買おうかなあ。DVDもまた買おうかな。エピソード7、ローグワン両方買っちゃったんだよね。

7th RISC-V Workshop の Vector Extension Proposal 概要

RISC-V 7th Workshopでは、Vector Extensionの説明がなされている。

https://content.riscv.org/wp-content/uploads/2017/12/Wed-1330-RISCVRogerEspasaVEXT-v4.pdf

f:id:msyksphinz:20171222012224p:plain

資料を読みながらまとめてみる。追記の可能性あり。


RISC-V Vector Extensionの特徴としては、

  • なるべくコンパクトにすること。
  • ベクトルレジスタの処理にマスクを加え、任意の要素のみ演算が適用されるようにする。
  • スカラー、ベクトル、行列の形式などをサポートする(ただし行列形式はオプションであり、ベースの仕様には追加されない)。
  • 型を混在させることが出来る。
  • (自称)これまでのベクトル命令の中で最高の出来である。

ベクトルレジスタ

ベクトルレジスタはV0からV31までの32本が定義されている。

  • 各ベクトルレジスタには"Type"とよばれるレジスタが付与される。これは16bit長のベクトルレジスタである。
  • 各ベクトルレジスタには"Type"が付けられ、ベクトルレジスタ毎に異なる"Type"を設定することが出来る。
  • "Type"はvdcfgレジスタにより設定することが出来る。16bit×32本で512bitあるのだが、これをどのように設定することが出来るのかは詳細が語られていない。
  • おそらくvdcfgレジスタが複数作られ、csrrw命令により設定されていくものと思われる。

ベクトル算術演算の実行

  • 実装されているベクトルレジスタの長さはMVLで定義する。一方で実行中の演算が適用されるベクトルの要素の長さはVLで定義する。
  • MVLの長さは全てのベクトルレジスタにおいて同一である。
  • ベクトルレジスタの長さと、演算のレイテンシは別物として考える。vadd命令を実行した場合その命令を1サイクルで実行する必要はない。何サイクル化に分けて実行して構わない。

f:id:msyksphinz:20171221011520p:plain

  • 4サイクルで実行する場合

f:id:msyksphinz:20171221011615p:plain

  • 2サイクルで実行する場合

f:id:msyksphinz:20171221011634p:plain

マスク付きの実行

ベクトル命令は、マスクを使ってエレメント毎に演算の有効無効を指定できる。例えば、vadd v5, v3, v4, v1.tとする場合、以下のようにv1.tで指定されるレジスタの値に依存してマスクがかけられる。

f:id:msyksphinz:20171221012156p:plain

メモリアクセス

ロード命令・ストライド付きロード命令

ロード命令は非常にシンプルなロード命令。Unalignedも許可するが、非常に低速なことが前提。

ストライド付きロード命令は、いわゆるGather命令。RISC-Vの仕様であるGather命令はさらにえげつない。 f:id:msyksphinz:20171221013344p:plain

Gather命令

Gather命令は、各ベクトルエレメントで、ロードするメモリアドレスのオフセットを自由に設定できる。 結構実装が大変になる気がするが。。。

f:id:msyksphinz:20171221014131p:plain

ベクトルレジスタの型、型変換

  • 異なる"Type"が設定されたレジスタ同士の演算でも、ある程度ルールの中ならば演算を適用させることが出来る。
vcvt v1(F32) --> v0(F16)
vcvt v1(u8) --> v0(F32)
vcvct v1(F32) --> v0(I32)

この場合のベクトルレジスタのサイズが非常にややこしいことになる。 各ベクトルレジスタは異なる型を持つことが出来るが、MVL(ベクトルレジスタ当たりのエレメントの数)は常に同一である。 したがって、以下のようなことが出来るようになる。

  • プログラムコードが2つのF16ベクトルレジスタおよび2つのF32ベクトルレジスタを必要としている場合、
    • F16(=2byte)×2 + F32(=4byte)×2 = 12byteが必要となる。

このとき、ベクトルレジスタのハードウェアとしては  4\text{byte}\times 4\text{element}\times 32\text{本}=512\text{byte} 保持しているとすると、 \text{MVL}=512\text{byte} / (12\text{byte} + 4\text{byte(使用しないレジスタ)}) = 32となり、1ベクトルレジスタ当たり32エレメントを持つことが出来るようになる。

これをどのようにベクトルレジスタに当てはめるかは実装に任されている。例えば、以下のような構成が可能となる。

上位の 4\text{byte}\times 4\text{element}\times 4\text{本}\times 2\text{レジスタ}(=128\text{byte}) をF16のベクトルレジスタとして使い、 次の4\text{byte}\times 4\text{element}\times 8\text{本}\times 2\text{レジスタ}(=256\text{byte}) をF32のベクトルレジスタとして使う。

f:id:msyksphinz:20171222012123p:plain

以上のように、ベクトルレジスタのハードウェアを実際のアーキテクチャ的などのレジスタに割り当てるかは非常に柔軟に持つことが出来るようになる。 その分、かなり制限を加えないと実装が大変そうだが... (検証も大変そうだ)。

と思ったら、命令コード表には入っていた。師匠に指摘していただきました。ありがとうございます。

f:id:msyksphinz:20171226010836p:plain

RISC-VのCompression命令仕様の理解とISSの実装

RISC-Vのハードウェア仕様の中で、RV64GCとか、RV32IMACとか出てくるが、その中で"C"というのは"Compressed"命令ということで、命令長が16bitの短縮命令のことを指す。

サポートしているアーキテクチャの中で"C"が入っていれば、このCompressed 命令を動かすことが出来るということで、命令コードサイズの短縮につながる。 ARMのThumb命令、MIPSのMIPS16eに近いものだ。

似て非なる"C"命令と"E"命令

時々混乱するのだが、Compressed命令群と雰囲気が似ている命令で"E"命令群が存在する。これはRV32Eと呼ばれ、RV32I(32bit整数命令)のハードウェア的な縮退版を指す。

主な変更点としては、RV32Iでは32本ある整数レジスタを16本に減らしている。使用できる命令自体はRV32Iと同一で、ただしx16-x31までの搭載していない汎用レジスタにアクセスすると例外を発生させる。 また、カウンタレジスタ(rdcycle[h],rdtime[h], rdinstret[h])は無理に実装しなくても良いという制限がある。

というわけでRV32Eは、命令長自体を短縮させるわけではなく、ハードウェア量を減らすための実装なので、そこは注意。

Compressed命令の概要

まず命令フォーマットとして、16bit長命令であることを示すのに、下位の2bitを使う。下位の2bitが2'b11であれば、32ビット長命令以上になるが、それ以外では16bit長命令となる。 非常にリッチである。

f:id:msyksphinz:20171220020108p:plain
(RISC-V User Instruction Manulより引用)

注意すべきは、RISC-Vのサポートしているモードによって実行される命令の意味が異なるということだ。例えば、RV32/RV64ではC.FLDであり、RV128ではC.LQ として解釈されて実行される。

命令フォーマットは以下に示すようになっている。レジスタ本数は基本モードと同様に32本利用できるが、32本をフルにアクセスできるのはCR(レジスタ間演算)、CI(レジスタ、即値)、CSS(スタック操作命令)のみとなる。

f:id:msyksphinz:20171220020226p:plain

それ以外の命令は、以下の表に従ってアクセスできるレジスタが制限される。 ロードストアに関しても同様だが、スタック相対のアクセスの場合のみ、CSS(Stack-relative Store)により専用命令が用意されている。この場合は任意のレジスタのデータをスタックに積むことが出来る。

f:id:msyksphinz:20171220020347p:plain

大きく分けてCompressed命令の仕様は以下のように分類される。

  • CR : レジスタ間演算命令。2オペランドを取り、1オペランドを上書きする形の命令が用意されている。
  • CI : レジスタ、即値演算命令。即値とレジスタの演算、または即値を代入する演算。
  • CSS : スタック相対メモリアクセス命令。スタックポインタをベースとしてメモリアクセスを行う。ロードとストアがあり、整数レジスタ用と浮動小数レジスタ用が用意されている。
  • CIW : 即値が広くとることが出来るレジスタアクセス命令。スタックポインタの更新などに使用される。
  • CL : ロード命令。
  • CS : ストア命令。
  • CB : 分岐命令。レジスタ比較を行うが、比較対象のレジスタはゼロレジスタに限定される。PC相対分岐を行う。
  • CJ : ジャンプ命令。PC相対ジャンプを行う。JumpとJump And Linkが用意されている。

RISC-V の命令セットシミュレータへのCompressed 命令実装の検討

自作命令セットシミュレータにRISC-Vを実装する検討をしているのだが、いくつか問題があった。まず、命令のモード毎に有効な命令が異なるということだ。 たとえば、下記の命令リストにおいて、C.FLWC.LDは命令コード的に全く同じなので、RISC-Vのサポートしている命令形態によって意味が異なる。

f:id:msyksphinz:20180603234957p:plain

したがって、とりあえずは命令デコードテーブルは重複している命令の片方を無効化して生成を行った。

  • 自作RISC-Vシミュレータの命令デコードテーブルの一部
$arch_table.push(Array['c.addi4spn', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX0', '00X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
$arch_table.push(Array['c.fld     ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX0', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
# $arch_table.push(Array['c.lq      ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX0', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])   # RV128
$arch_table.push(Array['c.lw      ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX0', '10X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
$arch_table.push(Array['c.flw     ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX0', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
# $arch_table.push(Array['c.ld      ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX0', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])   # RV64/RV128
$arch_table.push(Array['c.fsd     ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX1', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
# $arch_table.push(Array['c.sq      ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX1', '01X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
$arch_table.push(Array['c.sw      ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX1', '10X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
$arch_table.push(Array['c.fsw     ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX1', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])
# $arch_table.push(Array['c.sd      ', 16, 'XXXXX', 'XX',     'XXXXX', 'XXXX1', '11X', 'XXXXX', 'XXXXX00', 'COMPRESS', "", ""])   # RV64/RV128
  • Compressed命令のテストパタンの解析

Compressed命令のテストパタンを流してみると、どうも4-byteアラインではなく2-byteアラインに命令が配置されていてもジャンプできなければならないらしい!?

RISC-Vの例外仕様では、Instruction Address Misaligned例外は4-byteにアラインされていないことが条件かと思っていたけど、RVCに関してはそうではないらしい。

RISC-Vのメーリングリストを探していると見つけた。

An instruction address misaligned exception is generated on a taken branch or unconditional jump if the target address is not four-byte aligned. No instruction fetch misaligned exception is generated for a conditional branch that is not taken.

with a commentary note:

Instruction fetch misaligned exceptions are not possible on
machines that support extensions with 16-bit-aligned
instructions, such as the compressed instruction set extension,
C.

なるほどー、つまりCompressed命令をサポートしている実装では、Instruction Fetch Misaligned 例外は発生しないということか。これはびっくり。

というわけで例外的に命令フェッチアライン例外を無効にして自作ISSでRVC命令を流すようにすると、とりあえずデコードは出来たようだ。

f:id:msyksphinz:20180605001433p:plain

あとは中身の実装をしていかなければ。