2017/06/28

[zybo]CLOCK_DEDICATED_ROUTE ?

以前、ボタンを押すとLEDが点灯するサンプルを真似して動かしたが、自力で書いてみたい。

module blink_led(
    output ld0,
    input btn
    );
    
    reg led = 0;
    assign ld0 = led;

    always @(btn) begin
        if (btn == 1) begin
            led = 1;
        end
        else begin
            led = 0;
        end
    end
endmodule

ld0にLEDを割り振っているのだが、直接代入させるとエラーが出た。
regみたいなやつじゃ無いとだめ、ということらしいが、reg変数をwire変数には突っ込めないということか。

image

btnを一度INPUTして、そのままOUTPUTしてld0に入っていくだけだ。
無駄無駄なのだが、if文を書いてみたかったのだ。

最初、if文をalways()の外に書いていたけどエラーになってね。。。
たぶん、always()の中に書いた文は順次処理文、外に書いた文は同時処理文と呼ばれているようなので、ifのように順番に処理するものはalways()の中に書かないといかんのだろう。


1回押したものが出ていくだけでは面白くないので、8回押したら点灯するようにしようとした。
always()の引数に書いた端子に変化があれば呼ばれるのだろうから、両エッジ割込みみたいなものか?

module blink_led(
    output ld0,
    input btn
    );
    
    reg [2:0] cnt = 0;
    reg led = 0;
    assign ld0 = led;

    always @(btn) begin
        if (btn) begin
            cnt <= cnt + 1;
        end
        if (cnt == 0) begin
            led = 1;
        end
        else begin
            led = 0;
        end
    end
endmodule

cntという3bitのreg変数を追加し、btnがHIGHだったらカウントし、ビットがあふれて0になったらLEDを点灯させようという考えだ。


そう悪くないと思うのだが、、エラーになってBitstream生成までのどこかでエラーになった。

[Place 30-574] Poor placement for routing between an IO pin and BUFG. If this sub optimal condition is acceptable for this design, you may use the CLOCK_DEDICATED_ROUTE constraint in the .xdc file to demote this message to a WARNING. However, the use of this override is highly discouraged. These examples can be used directly in the .xdc file to override this clock rule.
     < set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets btn_IBUF] >

    btn_IBUF_inst (IBUF.O) is locked to IOB_X0Y36
      and btn_IBUF_BUFG_inst (BUFG.I) is provisionally placed by clockplacer on BUFGCTRL_X0Y0

IOピンとBUFG間のルーティングが足りてないという意味か?
ルーティングというのは、配線のことだと思う。
btn_IBUFなどと書かれているが、btnはY16ピンに当てていて、前回から変えていない。


CLOCK_DEDICATED_ROUTEがどうのこうのと書かれているが。。。
検索すると、XilinxのPDFが出てきた。

CLOCK_DEDICATED_ROUTE を使用すると、 クロックソースがそのロードクロックバッファーに比べて不適切な箇所に配置されている場合に、 クロック配置の DRC をエラーから警告に変更できます。

注意:CLOCK_DEDICATED_ROUTE を False にすると、 クロック遅延に問題が出て、潜在的なタイ ミングおよびその他の問題が発生することがあります。

なにやら、制約を外すオプションらしい。
XDCファイルに、こんな感じで行を追加すればよいそうだ。

set_property CLOCK_DEDICATED_ROUTE value [get_nets net_name]

net_nameは他の設定を真似して書いた。

set_property CLOCK_DEDICATED_ROUTE false [get_nets btn]

が、これは途中でエラーになった・・・。
エラーメッセージに書かれている内容にすると通ったけど、warningが出てくるのよねぇ。
気にしなくてよいものか。

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets btn_IBUF]


そして、動作はもっと気に入らない。
LoadさせるとLEDが点灯状態で始まり、ボタンを押すと消えたのだが、ボタンを押した回数とLEDが点灯するタイミングが不定だ。
なんだ、私のどこが間違っているというのだ。。。

CLOCK_DEDICATED_ROUTEで回避するという方法ではダメだったのだろうか?


I/OピンとBUFGを最適に接続できないから出てくるエラーらしい。

Vivadoで[Place 30-574]エラーが発生した時の対処法 | 彩の国・さきたま研究所
PC-9801Vm Style CLOCK_DEDICATED_ROUTE てナニ?


BUFGなんて使ってないよ!と思ったが、最初の方に載せた図を見るとbtn_IBUFというやつがいるな。
この図は最初のverilogから生成しているのだが、それにもかかわらず最初はそんなエラーは出ていない。
ということは、ソースを追加したことで、この図に変化が生じたのだろうか?

生じていた・・・。

image


コンパイルした結果みたいなものだから、これを読み解けば何が起こったか分かるだろう。

まず、左上のbtnが入力となる端子で、右側のld0が出力となる端子だ。
btnはRTL_LATCHのGに入り、Dの方は丸っこいのの出力になっている。

丸っこいのは、プラスマークが入っているから、加算器だろう。
今回で言えば、cnt <= cnt + 1、の部分だ。
上側のI1というやつが、上から吊られているからHIGHの1bit分ということか(Input1、なのかな?)。
そして、下側はI0(Input0 ?)で、これが右辺のcntに当たるやつだ。

その出力がRTL_LATCHに入って、Qから出ていく。
これが3bit分のメモリのようなものか。
マウスオーバーで説明が出てくるのだが、Typeが"RTL Register"になっていた。

その出力は、RTL_ROMの入力になっていて、そのままld0につながっている。
RTL_ROMのTypeは"RTL Memory"だ。


えー、btn_IBUFがないやん!


CLOCK_DEDICATED_ROUTEをfalseにしたことで、クロックが仕事に専念(dedicate)しなくなったため、スイッチをクロック代わりに使おうとしても、スイッチのINPUTを定期的に読む人がおらず、不定期に反応してしまったとか、そういう現象だろうか?

だったら、何とかしてクロックを入れてやればよいのかもしれない。


ZYBOのPDFを見ると、PL部にはCLK125からL16へクロックが入っているように見える。
が、なんでEthernetのPHYからなんだ?
50MHzということはRMIIだろうか。

image

あるいは、PS_CLKへのINPUTからPLLが動かないとPL部にはクロックが入っていないことになるのだろうか?

ソフトにとってはクロックってINPUTというよりは別次元で餌が与え続けられているようなイメージなので気にしなかったのだが、明示的に何かしないといかんのかもしれん。
CLOCK_DEDICATED_ROUTEの説明も「クロックソースがそのロードクロックバッファーに比べて不適切な箇所に配置されている場合に」だったので、適切な位置に配置すればよいだけなのか。


と推測したけど、何をしてよいかわからん。
検索だ!

PiTs laboratory: ZYBOのPLだけを使う場合のクロックソース
PSが動いていないとFCLK_CLKは動かないようだが、L16の方はクロックとして使えるらしい。


らしいが・・・だからなんなのだ?
btn_IBUFとかいうInput用のバッファを使うためにclkがどうのこうのという話だと思うので、btn_IBUFとかいう部品にクロックを突っ込んでやればよいとかじゃないのか???


そういえば、最初に参照したサイトは、自分でXDCファイルを変更していたな。。。
【Zynq】ZyboでPLのみ使用してLチカしてみた

よくわからんが、btnをやめて、clkをコメントアウトし、btnの代わりにclkを使うようにしてみた。
カウント数は8では足りないだろうから、どこかのサイトを見て増やした。
(ブラウザを閉じてしまって、どのサイトかわからなくなってしまった。。。)


そうすると、動くのだよ、動いたのだよ!
btnもclkも割込みで動くようなイメージで考えていたのだけど、そこは考え方が間違っているようだ。
うーむ、奥が深い。

2017/06/27

[zybo]ZYBO向けのLinux (1)

ZYBOのような組込み開発用のボードでOSが載る場合、ほぼメーカーがボード用のOSをリリースしていると思っている。
昔は「自分でがんばってね」という感じだったのだけど、提供するところが増えてきた今、使ってもらうためには「今すぐ使えます」を売りにしないといかんのだろう。

ZYBOはCortex-Aが載っているし、SDカード無しでもLinuxが動いているから、OSを動かしたいならLinuxがよいだろう。
ARMのCortex-Aだから、アドレスさえうまくやっておけばどのLinuxでもいいとは思うのだが、初っぱなから失敗したくないので、動くOSがあるならそれをまずは使いたい。


Zybo [Reference.Digilentinc]

これがZYBOを販売している会社のページで、右側のDesign Resourcesにいろいろファイルがある。

image

ここにPetalinux BSPというのがある。
XilinxはPeta Linuxというディストリビューションを使っているらしい。

https://github.com/Digilent/petalinux-bsps
ここからOSがダウンロードできるのかと思ったけど、Linuxが動きそうなサイズでもないし、かといってスクリプトしか載っていないようなサイズでもない。
そして、READMEにも何も書かれていない。。。


PetaLinux ツール
Xilinxのページで、右の「クイックリンク」にPetaLinuxがダウンロードできそうな項目があるのだけど、クリックしても行き当たらない。
英語ページだったら行くかと思ったが、そうでもなさそうだ。


では、ZYBOのリンクにあるPetalinux BSPが使えるのだろうと思ったけど・・・と、考えがぐるぐる回ってしまったのだ。


ここに、Xilinxが認識している動作OSが載っていた。
https://japan.xilinx.com/products/design-tools/software-zone/embedded-computing.html#os

いろいろある。

Xilinxのgithubにはkernelが置いてあった。
今の最新は、4.9.0のようだ。
https://github.com/Xilinx/linux-xlnx/blob/master/Makefile

kernelだけあっても困るので、ディストリビューションになっているものがよい。
PetaLinuxがよいのか、FPGAマガジン No.12ではYocto ProjectのLinuxを動かしているので、それがよいのか。。。


まあ、試すのは無料だから、Yocto Projectをまずは試すことにする。

2017/06/25

[xilinx]Vivadoの種類

Xilinxの開発環境は「Vivado(ヴィヴァドゥ?)」というようだ。
インストールしたものの、選択肢としてどれがよいのかさっぱりわからん。

いくつかインストールしたついでにAbout画面のスクリーンショットを撮ったので、載せておこう。


Vivado HLS 2017.2

image


Vivado 2017.2

image


xsdk 2017.2

image



image

Design EditionとSystem Editionがあるのだけど、どっちがどうなのだろう?
Systemの方が上っぽい感じはするが。

Vivado Design Suite
Design EditionとSystem Editionの両方とも「パーシャルリコンフィギュレーション」というやつが追加費用無しでできるが(保証期間内だけ?)、WebPACK Editionは追加費用がかかるらしい。
が、これではわからんなぁ。。。


Vivado Design Suite 評価版および WebPACK
一覧表があった。
違いは、"System Generator for DSP"の有無のようだ。
画面の選択肢に書いてあるとおりだな。


いや、そもそも私はDesign Editionを選択したのだけど、実はよくある30日間の無料試用版なのだろうか。
Installing Vivado [Reference.Digilentinc]
ZYBOの販売先にあるインストール説明でも、WebPACKと書いてあるし。


今回、ZYBOはSDSoCのライセンス(購入したボードのみという制限はあるが)もセットにしたので、SDx開発環境というものもインストールした。
というよりも、先にこちらをインストールして使っていたのだが、後述する問題点があったので2017版をインストールしたのだ。



Xilinx_SDSoC_2016.4_sdx_0310_1_Win64.exe

image

インストール画面。
ZYBOにはZynq-7010が載っているから、それだけ選べばよいよね?
これは何回目かのインストールしなおしをした画面なのでCable Driverのチェックは外している。

インストールすると、Vivadoとxsdkも一緒にインストールされた。



Vivado 2016.4

image


image


で、SDx開発環境の何が問題だったかというと、SDx IDE 2016.4だ。

Windows10のバージョンによるのかもしれないが、うまく起動できなかったのだ。
ZYBOのチュートリアルに書かれてある起動方法では、スプラッシュ画面が出てしばらくすると落ちる(%APPDATA%\Xilinx\Vivadoにhs_err_pid***.logというファイルを見ると、Java自体が落ちている)し、単体だと起動できるのだけどもFPGAに関する機能が使えないのだ。

image

たぶん、ツールバーの空白になっているところにFPGA関連のボタンが出るはずなのだが、無い。
Xilinxメニューにも、何も出てこない。
プラグインが起動できなかったとか、そういう理由だろうか。

image

こちらが、2017.2のXSDK。
違いは明らかじゃろう。


ただ、「SDSoC」という名前が入っているのはSDx IDEだけなので、C/C++でFPGAのコード?が書けるというSDSoCという機能を使いたかったらこれしかないのかもしれん。


SDx IDEはeclipseで、パースペクティブは「SDx」というものになっていたのでcustomize画面を開いた。

image

toolbar1というやつが表示されていないだけか・・・?
3つともチェックして再起動すると、アイコンが出てきたし、押すとチュートリアルにある画面も出てきた。

image


VivadoからのLaunchはできないけど、これでSDSoCの機能も使えるのであれば問題ないと思う。
問題があるとすれば、私がSDSoCが何なのかわかっていないことと、そもそもFPGAがよくわかっていないということだろう。
FPGAマガジンNo.16にはSDSoCのことも書いてありそうだから、買ってみるか。

2017/06/24

[zybo]ZYBOを動かしてみよう

いきなりだが、XilinxのFPGAであるZYNQ7010が搭載されたZYBOというDIGILENT社のボードを動かしてみた。
むかしから、FPGAというものを動かしてみたかったのだけど、仕事でしか見たことがなかったので、個人で使えるような時代になっていると思っていなかったのだ。

しかも、Linuxが動くARMのCortex-Aが載ったボードまであると言うことで、そろそろやってみてもよい時期だろうと判断したのだ。
判断したというか、やってみたい熱が高まっただけだがね。


ZYBOは、秋月通商さんで購入。

image

比較のためPaSoRiと一緒に撮影したが、小さいねぇ。
Cortex-Mに比べると大きいけど、そこと比べるものでもないだろう。


勢いで購入したものの、そもそもFPGAを自分で構成したことなどないので、まずは一連の流れをやっているところを探しました。

6. Zynq Verilog-HDLをZynqに書き込みFPGAを使う – yuki-sato.com
【Zynq】ZyboでPLのみ使用してLチカしてみた

この2箇所を見て、動かすことができた。
私はVerilogの方を使ったが、ハードウェア記述言語(HDL)としてはVerilogかVHDLが使えるようだ。
Wikipediaによると、厳密には"Verilog"はエミュレータのことらしい。
VHDLはVerilog HDLの略ではなく、別の言語。
誰がこんな紛らわしい名前にしたんだ・・・と思ったが、VHDLの"V"はveryらしいし、Verilogは名前の由来が分からないので、どっちがどうのという話でもなさそうだ。


なお、Windows10にインストールする際はここを参考にした。
VC++のランタイムって、該当するバージョンが入っているだけではダメなのか?
謎は残ったが、今回は困らないので削除した(Vivadoの2017版は関係なく動いたが、SDxは2016版が自動的にインストールされるので、そういう制約になってしまうようだ)。
FPGAの部屋 Vivado や Vivado HLS が Visual Studio 2012 Visual C++ Runtimeのインストールダイアログが出て起動できない


やっていることを、整理してみよう。


まず、プロジェクトを作成する。
「RTL Project」を選ぶそうだが、RTLとはなんだ?

Register Transfer Levelの略
レジスタ転送レベル - Wikipedia

CPUのレジスタとは違うもので、ごくごく小さな回路を「これはレジスタね」と決めて、そういうレジスタからレジスタへのINPUTとOUTPUTを書いていくもののようだ。

Lはlanguageじゃなくて、levelなのが気になる。
Wikipediaのはそこまで書かれていないが、「ゲートレベルよりも抽象的な記述レベル」と書いてあるので、何かのものを指す用語ではなく、切り口というか考え方というか、そういうもののようだ。
ゲートレベルがアセンブラだったら、レジスタ転送レベルはC言語、みたいな感じかもしれん。


プロジェクトを作ったら、HDLで内容を書く。
こちらを参考にVerilogで書いた。
引数?が昔のC言語っぽいので気になったが、今のC言語っぽく引数内に型?を一緒に書いてもよいようだ。

module led(output led, input button);
    assign led = button;
endmodule

C言語っぽく、{}で囲みたいところだが、それは我慢だ。

なお、ZYBOに転送すると Vivadoの画面がHardware Managerになって、Verilogのファイルが載っていたナビゲーションが消えてしまう。
元に戻したい場合は、左側の"Flow Navigator"からProject Managerをクリックするとソースファイルが入ったツリーが出てくる。


次は、Verilogのoutputやinputで付けた名前と、実際のポートを割り付ける作業だ。
"RTL Analysis"内の"Elaborated Design"を選択して、レイアウトを"I/O Planning"にして行っているのが、それだ。

画面には、FPGAのBGAっぽいものが表示されるが、そっちは見方がわからん。
画面のしたにI/O Portsという一覧が表示されていて、Verilogの引数にしたものが出てくるようだった。
「I/O StdをLVCMOS25などに切り替えましょう」と説明されていたが、「Low Voltage CMOS 2.5V」という意味なんじゃなかろうか。
ZYBOのドキュメントには、3.3Vで吊られている図が載っていたので、2.5Vくらいにしようぜ、ということかもしれん。


その後は、「Run Synthesis」「Run Implementation」「Generate Bitstream」を実行すると、FPGAのPLに転送するデータができるようだ。
ツールバーに3つ並んでいる。

image

しかし、用語のそれぞれがわからん。

まず、Synthesis。
単語としては、合成とか組み立てとか。
Xilinx Synthesis Technologyを略してXSTと呼んでいて、合成ツールという位置づけのようだ。

合成は、論理合成を指しているのだと思う。
だからXilinx専用の言葉ではなさそうだ。
Wikipediaの説明では、HDLで書かれたものをRTLまで変換するツールのようだ。
HDLがRTLかと思っていたけど、もう少し高級ということか。
HDLがC言語としたら、RTL相当なものがアセンブラで、それを論理ゲートレベルまで変換したら機械語、くらいなのかな。


次はImplementationだが、ソフトでいえば実装だが、FPGAではこのページの「デザインインプリメンテーション」に当たるのかな。
半導体技術解説:いまさら聞けない FPGA入門 (3/3) - MONOist(モノイスト)

詳しいことはわからんが、コンパイル後の最適化みたいなものか。
そういえばGCC4から中間言語に変換して最適化するようになったが、あれもRTLと呼ばれていたな。
GCCのRTLは、Register Transfer Languageのようだ。
GCC 4 について学ぶ


そしてBitstream。
Monoistのページではデザインインプリメンテーションの中に入っているが、FPGAに書込むことができるデータを作る工程のようだ。
拡張子はbitで、プロジェクト名がprojだった場合、proj/proj.runs/impl_1/proj.bit、というところにできているようだ。


そして、これを"Program and Debug"を使ってFPGAに転送すれば動くようになる。
どうも、FPGAに焼くというよりは、転送する、という感じがしている。
電源を入れ直すと動かなかったからだ。
PL部だけ電源OFFにすることもできるようなので、PS部から毎回転送するイメージなのかもしれん。

2017/06/22

[c/c++]引数が多いことに対する抵抗が薄れてきた

関数を作るとき、あまり引数が多いのはよろしくない、と教えられて育って(?)きた。

もちろんそれには理由があり、C/C++コンパイラはある程度の数までは引数をCPUのレジスタで引き渡し、それ以上になるとスタックメモリに入れて引き渡すからだ。
C言語の規格書を読んだことがないので決まりなのかどうかは知らないが、メーカー製のコンパイラ仕様なんかを読むとだいたいそうなっていた。


これはARM Cortex-AのARMv8-Aのようだが、Argument registersということで8つ確保してある。。。のだと思う。
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch09s01s01.html

別の環境で、Cだと4つまで、C++だと3つまではレジスタ渡しするというのも読んだことがある(thisで1つ取られるからね)。
CPUによってレジスタの数や使い方が違うだろうから、全部同じではないだろう。


以前はけっこう気にしていたのだが、そんなに効果があるのかも気になっていた。
引数をたくさん与えないといけない関数は、だいたい使用頻度が少ないものが多かったからだ。
使用頻度があまりにも多い場合は、マクロ関数なりinline関数にするとそこそこ対応できるし(ROM容量との兼ね合いにもなるが)、みんなが使うのでstaticなどでグローバル変数にしているから渡す必要がなかったり。

引数を渡すということは、モジュールと切り離したいという意思が強いだろうから、そういうのはstructにして渡せるようにし、グローバル変数みたいにして保持するときもstructのままで持っておくと渡しやすい。


いや、何の話をしているかというと、引数が多いのはよろしくない→じゃあstructを用意して代入してポインタで渡す、などということを自分でやっていたからだ。
それって、スタックにためて渡すのとあんまり内容として変わらないけど、実装の手間だけ増えてるよな、と漠然と思っていたのだ。

そんなことをするくらいだったら引数が多いままの関数を作ってもよいし、そもそもそういう関数を作らなくていいような構造にできるした方が効率いいんじゃないの、という「引数が多いのはよろしくない」という警告だったのかもしれない。


そんなことを自分で作った引数8個の関数を見ながら思った。

[mbedTLS]2.5.1リリース

mbedはmbedでも、mbedTLSだ。

mbed TLS 2.5.1, 2.1.8 and 1.3.20 released - Tech Updates


私にとって特に大きな機能追加はないのですが、楕円曲線モジュールがハードウェアアクセラレーションに対応したようだ。
ECCを持っているマイコンだと重宝しそうだ。
Nordicのチップだと、micro-eccというライブラリを使っていたと思うから、あれには載ってないのだろう。

だいたい、楕円曲線モジュールの略語がよくわからん。
ECC(Elliptic Curve Cryptography)のような気もするが、mbedTLSはECP(Elliptic Curve Point)だ。
ECCだと、メモリの方が引っかかりやすいので、個人的にはECPの方がありがたいのだけど、どうなんだろう?

それに、楕円曲線暗号と一言で言っても、曲線の種類はたくさんあるから、それが全部マイコンに載っていることはないだろう。
どういう使い方をするのかは気になるな。


ともかく、mbedTLSはバージョン系統が3つくらいあるようだから、それぞれ最新版を利用するようにした方が良かろう。

2017/06/21

[linux]ほどよい通信用ライブラリはないだろうか?

ずっとやっている、Linuxでソケット通信でマルチスレッドなのがうまくいかないシリーズ。

安定してきた気はするものの、あっちでmutexのlock競合、こっちでlockとlockの隙間に割り込まれる、など、自分のまぬけな実装が目に余る次第だ。

こういうのって、MQTTのときにpahoを使ったように、ほどよいライブラリがないものだろうか?


似たような質問があった。
Best C/C++ Network Library - Stack Overflow

いいねぇ、君。
しかし、多いよ! この人が挙げただけでこんなにあるのか。
3年前だから、今だとまだあるのかもしれん。


自分が作っていたライブラリの部分はCで書きたいのだけど、通信とかアプリの部分はC++でもよい。
そうなると、ここの全部が選択できる対象になってしまうのだ。

使ったことがあるのは、POCOとlibcurlくらいか。
今回はTCPでプロトコルを作るので、libcurlはそぐわないと思う。
POCOはえらく多機能だった記憶があるくらいだ。
ここから見てみるか。

POCO C++ Libraries - Reference Library
うわあ、なんでもあるわ、この子。。。
普段の組込みライブラリからすると、目が回りそうだ。
Socketも使えるようだし、サーバもやってくれるようだから、これで事足りそうな気もする。


いや、待て・・・。
こんだけ便利なライブラリがあるのだから、逆に今回みたいに特定用途に特化したライブラリもあるのかもしれない。

いろいろ考えたが、これってP2Pというやつでよいんじゃなかろうか。
相手から受け付けるし、相手にも接続を求めるし、でも接続関係は1対1だし。


P2P library for C++ - Stack Overflow

一番上はlibtorrentだが、これはbittorrentの実装らしいから、汎用P2Pではないのだろう。
boost-asioは、P2Pで使うと便利だよ、ということかな。
libboostって、15年前くらいに見てそれっきりだな・・・。


その次のlibniceも、RFC5245の実装などと書いてあるから、特定のP2P実装なのだろう。
MsgConnectというミドルウェアも紹介されていたが、これは製品のようだ。
うーむ。


いや、これは私の考え方が悪かったようだ。
P2Pのフレームワークが欲しい、といった場合、だいたいは「P2Pのネットワークが欲しい」と思うだろう。
やりたいことは、P2Pで何かをする、であって、P2P環境を作るための何か、ではないのだ。


とすると、やはり最初に載せたC/C++の通信ライブラリと、何かスレッド間通信を便利にするライブラリを組み合わせるのがよいということだろうか。

定番が見つけにくい分野って、大変ですわ。。。

2017/06/20

[bc]秘密鍵を渡すのか…

先週出ていたニュースリリース。

【news release】仮想通貨とFeliCaを連携させた決済手法の開発と検証を実施-NEWS


うーーーーん・・・・・・・。

Blockchainに関わる実装として、いくら安全だからといって秘密鍵を渡すというのはどうなんだろうか?
FeliCa的な文化であればそれでよいのかもしれないけど、うちだと案が出た時点で却下されるな。
タンパ性がどうとかじゃなくて、Trustlessじゃないという理由だ。
まあ、Trustlessって何を指すんだよ、という話はあるだろうけど、秘密鍵を相手に渡す行為は十分相手を信用した結果しかできないよなぁ、と思う。


たぶんFeliCa内でトランザクションの署名はできないだろうから、FeliCaを絡めたければこうなるのだろう。

Type-Bだったらどうなんだろうか?
マイナンバーカードの中には、RSAの署名をする機能があったはずだ。
マイナンバーカードでSSHする - AAA Blog


それと、通常はアドレスを1回使うと、新しくアドレスを作り直してお釣りを転送すると思うが、そこはどうしたんだろうか?
testnetで実験するときは同じアドレスに送金することもあるのだけど、unspentなアドレスを使い続けると秘密鍵が解析されやすくなるんじゃなかろうか。

通常のウォレットは、seedを決めたら、あとは鍵の世代だけで秘密鍵を管理できるようになっている。
だから、seedが漏れたらおしまいなのだけど、秘密鍵の1つが解析されて見つけられたとしても他の秘密鍵には影響がないようになっている・・・らしい。
実装はしたことがあるのだけど、理論はよくわからんかった。


「パソコンやスマホのウォレットだって、PC内に置いてるだけやん」といわれればそれまでだし、Blockchainに関する実験としては他に比べるとかなり考えられているように感じるから、FeliCaでトランザクションの署名ができるようになるまでがんばってほしいものだ。

[linux]スレッド間のやりとりをバッファ経由に変更

なんだか、何のために記事を書いているのかわからなくなってきた・・・。

やりたいのは複数プロセス間のソケット通信。
プロセス間は1本のソケットでつないでいる。
直接通信できるのは接続した相手だけだが、転送してもらうことができる。


起きているのは、双方から同時に転送が行われると、どこかで止まるという現象。
排他をかけたり、状態を持って止めたり、あれこれ悩んでいる。
おかげで、元のソースに戻れないくらいだ。。。


前回考えた最後の図を載せよう。

image5

書いてある内容は無視して、線だけ見ていく。
A-B間、B-C間はソケット通信だ。
では、Bの中の点線は何かというと、スレッド間のやりとりになる。

スレッド間といっても、相手にソケットを送信したいだけなので、データパケットを作って相手のsocket descriptorを使って送信していた。
が、これはなんとなくよろしくない感触がある。
いくらsdがわかって送信できたとしても、やはり相手の通信路は相手のスレッドに処理してもらうのが筋なんじゃなかろうか。


しかし、スレッド間通信ってどうやるものなのだろう?
今回はソケット受信をpoll()しているスレッドがあったので、そこでタイムアウトするようにし、他スレッド処理要求ありフラグがあったらその処理をする、ということにした。
以前は関数呼び出しだったのでメモリを直接見せていたが、今回は時差が発生するので別にメモリを確保するようにした。

あー、キュー方式にして、キューに入っていたら処理をする、というやり方の方が良いな。。。
他スレッドからの要求なんていくつやってくるか決められないし、数を決めたとしてもあふれるようなら待たせないといかん。


まだフラグを立てるやり方だが、この方式に変更したところ、急に安定しだした。
困ったことに、動かないときは動かない場所を見ればよかったのだが、動き出したように見えると流れが期待通りかどうか確認するのが難しい。
一方向の通信だったらもともとうまく動いていたので、期待するタイミングでピタッと衝突できないと確認できんのだが、それってできるきがしない。。。


ともかく、みんなこんなプログラムを作るときどうやってるんだろう??
私が知らないだけで、実は便利なフレームワークやライブラリがあるのだろうか。

2017/06/18

[linux]通信による衝突はどうやって回避するとよいのかがわからん

昨日の続きだが、わからんシリーズだ。


マルチスレッドで並行に動かしたいわけではないので、終わるまで待たせればいいやん、という結論になった。
今回のプロトコルでは待たせても、処理さえしてくれれば問題ないのだ。

ただ、相手に再送してもらうわけにはいかないので、自分で処理が終わるまで待たせることにした。
mutexではなく、状態変数を持たせ、処理中だったら1秒ごとにチェックして終わったかどうかをループで監視することで待たせることにした。
volatileにしてwhileで回せばいいだろう。


そしたら・・・こういう現象が起きた。

image

あー、そうだね。。。
mutexだろうと状態変数だろうと、待たせるということはそのコンテキストが止まってしまうのだ。
コンテキストを止めたいのではなく、コンテキストで行われる処理を後回しにしたいだけなので、これではダメなのだよ。



じゃあ・・・どうしたらよいのだ?
Cが送信するタイミングではBからデータが来るなんてことはわからない。
だから送信して、このコンテキストはC→B→Aの送信だ、と考える。

しかし、Bからすると先にAから受信しているのでA→B→Cと動くのが自然なので、そういうコンテキストだと考える。
どちらも間違ってはいないのだが、このままではBはCからの受信を待機させるし、CはBからの受信を待機させて、これはこれで流れが止まってしまう。

Aが送信する前にCへ連絡できればよいのだけど、そういうルートはない。
だから、事前に知らせる方法はない。


相手と自分間でどちらかに送信に対する優先度を持たせるとどうだろうか。
幸い、A, B, Cはユニークな識別子を持っていて、その識別子は相手も知っているため、機械的に大小を決めることができる。

  • A>B, B>C : BはCより強いので、Aからの転送を優先させる
  • A<B, B<C : CはBより強いので、Aからの転送は待たせてCの転送を優先させる
  • A<B, B>C : BはCより強いので、Aからの転送を優先させる
  • A>B, B<C : CはBより強いのでB-C間はCの転送を優先させたいが、A-B間はAの方が強いので、Aの転送を優先させたくなる。AとCの強さで決めるしかないか?

最後のパターンでAの方が強かったとしても、CはAを知らないので、「俺はBより強いんだ」と考えてしまうだろう。
データに「あちらのお客様からです」というような情報があれば比較もできるが、それもない。
ダメか・・・。


ここでは一部だけ切り出しているが、実際のプロトコル仕様としてはシーケンスがクロスすることについては規定していないし、クロスしても特に問題はない。
実装の都合でブロックしようと考えただけだ。
もしかすると、ブロックできないことがわかっているから、そういう書き方をしているのかもしれない。

最初はブロックせずに実装していたのだけど、シーケンスとして「お互いの送受信状況を比較する」というようなものがあり、それが相手の転送によって割り込まれると、相手は転送データを受け取っているがこちらはまだ、というタイミングが生じてしまい、比較NGになってしまうのだ。

image


今回のように幅広くブロックするんじゃなくて、比較するぞ、という区間だけ転送を止める方が良いのか。
相手から比較データを送ってくるのは、相手がsendした後だということが決まっているので、相手のsendを受け取ってから比較が終わるまでの間だけ転送をブロックすればよいかもしれん。


いやー、難しい、難しいですよ。。。

2017/06/17

[linux]ある区間はスレッドを切り替えて欲しくない場合

まだまだLinuxのマルチスレッドで悩んでいる。


今起きているのは、こういうやつ。

image

丸いのは、mutex_lockだ。
ソケット通信で相手に送るプロセスと、それを受信したらもう片方に中継して送信するプロセスがいて、中継するプロセスはスレッドで相手と接続している。
相手に送信する前にmutex_lockするようにして、lockできたら送信している。


通常はそれでよさそうなのだけど、図のBがほぼ同じタイミングで送信データを受け取ったときに問題が発生した。
Aからの送信をBのA側スレッドが受け取り、ロックを掛けてC側のスレッドに渡そうとするのだが、その直前にスレッド処理がC側スレッドに切り替わり、そちらもロックを掛けてA側のスレッドに渡そうとする、という状況が起きてしまった。。。


こういうときって、どうするのだろうか?
各スレッドを貫くようなmutexを用意して、切り替わって欲しくないタイミングでlockするのがよさそうな気がする。

ただ、こうも思ったのだ。
「スレッドが切り替わらなければいいだけやん」と。
割り込み処理中に多重割込みにならないように割込み禁止にするようなイメージだ。
ディスパッチ禁止とかクリティカルセクションとか、そんな用語でよいのだろうか。


スピンロック、が一番それっぽい気がするが、これはkernelの中でしか使えないのか?
ぐるぐるとループを回して、条件が合うまで抜けない、というシンプルなしくみらしい。
RCUとかいうのもあるが、そもそも読んでいるのが『Linuxカーネル2.6解読室』だから、kernelで使えるしくみを紹介しているのだろう。


などと書いていって気がついたが、そもそも上記の処理ってマルチスレッドにしたのは複数の相手と接続したかったからであって、接続した相手と同時に通信をする必要がないことに気付いた。

つまり、これでいいやん。

image

プロセスとしてmutexを持っていて、誰か最初に処理を始めた人が全部握ってしまえばよいのだ。
終わってから動いたとしてもそれほど問題ない気がする。

整理するって、大切ですね。

[linux]ソケット送信した回数と受信する回数は関係がない

Linuxで、マルチスレッドで、ソケット通信するアプリを作っている。
ああ、慣れていない、慣れていないのだよ。。。


バグがいくつも出てきて困っているのだが、その1つに「こちらは送信したのに相手が受け取っていない」という現象があった。
write()は失敗していないし、そんなに取りこぼすようなところでもないし。

ログを見直していると、思ったより大きいデータ長の受信を行っているところがあった。
まさか・・・と計算すると、2つwrite()したデータ長と同じでした。
えー、write()って2回呼んだら、送信も2回で行ってくれるもんじゃないの??



Linux ソケット・プログラミングの 5 つの落とし穴

これの5番目、「落とし穴 5. TCP におけるフレーミング前提」か。
ええ、前提にしていましたとも。

今回はデータ長が載っているプロトコルでは無いので、解析しながらじゃないとデータ長が分からない。
が、今は受信したデータを解析する、というように作っているので、大幅な改造が必要になりそうだ。
ああ、慣れていない、慣れていないのだよ。。。

2017/06/14

[mbedtls]keypairはスレッド間で共用するものではない

数日前、mbedTLSの楕円曲線署名と検証は排他した方が良いのかもしれん、という記事を書いた。
(実は、こそっと追記した。)

http://hiro99ma.blogspot.com/2017/06/mbedtls.html


しかし、mbedTLSの設定に MBEDTLS_THREADING_CやMBEDTLS_THREADING_PTHREADがあるので、少なくともLinuxで両オプションを有効にしていたら大丈夫で、自分のコーディングがよろしくないんじゃなかろうか、という感触が少なからずあった。
少なからずというか、今までの経験上、よく使われているライブラリで結果が変だったときは、だいたい自分の書き方が悪いときだったというだけだが。

Thread Safety and Multi Threading: concurrency issues - Knowledge Base - mbed TLS (Previously PolarSSL)


モヤモヤしていたので見直しを行ったところ、mbedtls_ecp_keypairを共用している(自分が)ことが分かった。
ただ、mbedtls_ecp_group_load()でgrpメンバを設定して使う、という使い方しかしていないので、グローバル変数でもよいだろうと思っていたのだ。

が・・・実装しているうちに「署名の検証ってmbedtls_ecdsa_read_signature()にmbedtls_ecp_keypairをmbedtls_ecdsa_contextでキャストすれば使えるやん、ということに気付いてしまった。

//mbedtls/ecdsa.h
typedef mbedtls_ecp_keypair mbedtls_ecdsa_context;

そう、楕円曲線の検証だけgrpではなくkeypair全体を使っていたのだ。
そして、mbedtls_ecp_keypair構造体はメンバとして秘密鍵や公開鍵を持つようになっている。
実装までは確認していないが、署名の検証にそれらが使われているのは当然だろう。


いくらスレッドセーフだったとしても、書き換える対象の変数を共用していては意味が無い、というわけだ。


では、mbedtls_ecp_groupだったら共用させても大丈夫かなー、と思ったのだが、mbedTLSのインターフェースとしてはconst付きとconstなしがある。

さすがにconstなしのところを共用するのは同じ事態を引き起こすことになるだろうから、共用は止めて毎回スタックで設定するようにした。


共用で気にしているのは、mbedtls_entropy_contextとmbedtls_ctr_drbg_contextだ。
RNGが使いたかったし、毎回設定するようなものでもないと思ったのでグローバル変数として持っている。
mbedTLSのサイトでは、EntropyとCTR-DRBGはリストに載っているのだが、これは何のリストだ?
init/free()があって、メインスレッドから呼びなさい、と言っているようだが・・・。

mutexの初期化と解放をそこで行っているからメインスレッドから呼べ、ということかな?
だったら、他のところは別スレッドから呼んでもよいということになろう。

読む解くのが苦手ですわ。。。

2017/06/11

[c/c++]同じidでmsgsnd()してmsgrecv()すると、自分で読める

Linuxで2つのプロセスを作っていて、片方がサーバのように待ち受け、片方の要求に対して応答するようなしくみを作っていた。
どういう方法がよいのかわからなかったので、メッセージキューというものが見つかったので、msgsnd()とmsgrcv()でやろうと考えた。


最初は、クライアント側からサーバ側に要求してサーバ側の標準出力に結果を出していたのだが、それだと面倒なので、結果をクライアントに返してもらうように変更した。

何も考えず、ftok()でkeyを作り、msgget()でidを取得し、それを使っていたのだ。
が・・・動いたり動かなかったりする。
特に複雑なことはしておらず、サーバ側はmsgrcv()で待ち、クライアントがmsgsnd()したものを受信したら、結果をmsgsnd()で返してまたmsgrcv()で待ち状態になる、というだけだ。


悪かったのはいつものように私で、サーバがmsgsnd()して結果を書込んでからmsgrcv()にさっさと移行してしまうため、クライアント側がmsgrcv()で受信する前に自分で読み取ることがあったのだ。
だから、うまく行ったり行かなかったりという不安定な動きになっていた。

msgrcv()するとキューから消えてしまうので、mtypeで見分けても、もう遅い。
こんなことだったら、共有メモリにした方がやりやすかったんじゃなかろうか。


今回は、ここはまだ重要なところではないので後から見直すとしても、取りあえずでよいので回避できればよい。
どうするかというと・・・

msgrecv();
受信後の処理();
msgsnd(結果);
sleep(1);

うん、1秒くらい置いておけば相手は読んでくれるからよいだろう。

あー、ださださだ...

2017/06/10

[c/c++]3項演算子とifはそこまで変わらない

判断して代入するだけ、という処理はしばしば書くと思う。

2択の場合、私は三項演算子を使うことが多い。
ただ、3項演算子で書くと1行が長くなりすぎることがあり、そういうときは改行したりする。

そういうとき、改行するくらいだったらif文で書けばよいんじゃないの?

という声がよぎるのだ。

やることは同じなので、結果として同じバイナリになるのだったら、その時の気分だったりコーディング規約で書けばよいのだけど、もし高速になる要素があるのだったら推していくのもありだろう。

gccで確認しよう。


int main(int argc, char *argv[])
{
    int vol;
    if (argc == 1) {
        vol = 2;
    } else {
        vol = 1;
    }
    printf("vol = %d\n", vol);
    return 0;
}


int main(int argc, char *argv[])
{
    int vol = (argc == 1) ? 2 : 1;
    printf("vol = %d\n", vol);
    return 0;
}


これといった特徴はないコードだ。
argcを使ったのは、なんとなく最適化されなさそうだと思っただけだ。

結果を先に言うと、Bash on Windowsのgcc(gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609)では、最適化あり(-O1以上)にすると同じになった。
最適化するのであれば、どっちでもよいと思う。
何らかの理由で最適化しないのであれば、三項演算子の方が短くなった。



最適化無し(-O0)でコンパイルした結果。
右がif、左が三項演算子。

image

1と比較して一致しないならL2にジャンプして1を代入するという処理は同じだが、ifは直接-4(%rbp)に突っ込んでるのに対し、三項演算子は%eaxに突っ込んだ後で-4(%rbp)に代入している。
三項演算子のそれぞれで処理してレジスタに入れて、それを代入させる、ということだから、うん、確かにそういう動きですな。

2017/06/08

[ios]NFCNDEFPayloadはベタのpayloadなのかな

iPhoneを持ってないし、今のところ買う予定もないのだけど、NFC関連のAPIがiOS11から載りそうだということなので、少し見ておく。
なお、いま(2017/06/08)はまだβ版なので、正式版になる頃には状況が変わっていると思う。

NFCが載ったiOSならどれでもいけるのかと思ったが、iPhone7以降ということで、iPhone6はダメらしい。
なんでだろう、ハード的にそうなってるのかな?


見ておく、といったものの、iOSの挙動を知らないし、Swiftも知らないので、雰囲気だけで読んでいきます。
(間違い多いと思うけど許してね、の別表現。)


https://developer.apple.com/documentation/corenfc

大きく4つに分かれている。

  • Reader Sessions
    • class NFCNDEFReaderSession
    • protocol NFCNDEFReaderSessionDelegate
    • protocol NFCReaderSessionDelegate
    • class NFCReaderSession
  • NFC Tags
    • protocol NFCTag
    • class NFCTagCommandConfiguration
  • NDEF Messages
    • class NFCNDEFMessage
    • class NFCNDEFPayload
    • enum NFCTypeNameFormat
  • Errors
    • struct NFCReaderError


classとprotocolというのが並んでいる。。。
Swiftのprotocolは、Javaのinterfaceみたいなものらしい。

全部羅列したら、何か分かるだろうか?

[Reader Sessions]
  class NFCNDEFReaderSession
    init()
    protocol NFCNDEFReaderSessionDelegate

  protocol NFCNDEFReaderSessionDelegate
    readerSession(didDetectNDEFs)
    readerSession(didInvalidateWithError)

  protocol NFCReaderSessionDelegate
    isReady
    begin()
    invalidate()

  protocol NFCReaderSessionProtocol
    isReady
    begin()
    invalidate()

  class NFCReaderSession
    delegate
    sessionQueue


[NFC Tags]
  protocol NFCTag
    isAvailable
    session
    type
    enum NFCtagType
      ISO15693

  class NFCTagCommandConfiguration
    maximumRetries
    retryInterval


[NDEF Messages]
  class NFCNDEFMessage
    record

  class NFCNDEFPayload
    identifier
    payload
    type
    typeNameFormat

  enum NFCTypeNameFormat
    absoluteURI
    empty
    media
    nfcExternal
    nfcWellKnown
    unchanged
    unknown


[Errors]
  struct NFCReaderError
    省略

わからんわな。

NDEFは、TNFまでは見分けてくれるけど、あとは自分で解析してね、というスタンスのようだ。
なお、NDEFのフォーマットというのはこういう感じだ。

image

まあ、ほぼそのままを渡してくれるので、あとはこれからURIだのTEXTだのSmartPosterだのを自分で解析するのだろう。
自由度が高いが、まあ単なるデータだからね。。。

BLEのペアリングだったり、WiFiのSSIDなんかもタグで読めるだろうけど、iOSってアプリからそういう設定ってできなさそうな気がするから、ありがたみは薄いのかな。
コピー・ペーストができるなら、貼り付けるだけで済むようにできそうだけど、なんとなくそれも許してなさそうな気がする。

2017/06/07

[mbedTLS]マルチスレッドでは署名と検証は排他した方が良いのか?

全然そのつもりがなかったのだが、mbedTLSを使って作っていたライブラリをマルチスレッドで動かすことにした。
楕円曲線の署名して送信し、受信したら検証して別のデータをまた送信、というようなことを各スレッドで行うと思っておくれ。


ようやくベースができたので、手動でちょこちょこ通信させていたのだが、たまに検証に失敗することがある。
毎回ならロジックがどこかおかしいと思うのだが、たまに、なのだ。
まれに、というほどでもなく、比較的置きやすいのだが、少なくとも毎回ではない。

なんだ??


検証に失敗するからには、

  • 作った署名が間違っている
  • 署名の送信か受信が間違っている
  • 受信した署名までは正しいが検証で失敗している

のどれかなのだが、ログを出していてもどのスレッドが出しているのかうまく整理できず、何が悪いのかはっきりしない。。。
こういうときって、標準出力ではなく、せめてスレッドごとにログを吐くようにしておけばよいのだろうね。


mbedTLSのマルチスレッド動作については、ページがある。
Thread Safety and Multi Threading: concurrency issues - Knowledge Base - mbed TLS (Previously PolarSSL)

大ざっぱに読んで、MBEDTLS_THREADING_CとMBEDTLS_THREADING_PTHREADを有効にしたライブラリを作ればよいのだろう、と判断。
ただ、ecp.cにはTHREADINGのマクロがあるのだが、ecdsa.cの方にはない。
ヘッダを見ると、mbedtls_ecdsa_write_signature()は同じコンテキストだったらスレッドセーフじゃない、と書いてある。
ecp.cのmbedtls_ecp_mul()なんかもスレッドセーフじゃないというコメントが入っているが、こっちはmbedtls_mutex_lock()で排他しているのだ。

うーーん。。。。

中で呼んでいる関数のどこかで排他されているから大丈夫、というパターンかもしれないが、mbedtls_ecp_mul()なんかは「同じグループだとスレッド-セーフじゃない」といっているから、同じmbedtls_ecp_groupを使い回しているところが全体として危ないのかもしれん。
今回、mbedtls_ecp_groupは全員同じものを使っていいんじゃないの?と思い、開始時に設定したら解放せずに全員が同じものにアクセスするように作り込んでいたのだ。

その考え方自体が間違っているのかもしれんが、MBEDTLS_THREADING_Cなんて用意してあるくらいだから、そんなのしなくてもよいのかも。。。


わからんので、mbedtls_ecdsa_sign_det()やmbedtls_ecdsa_read_signature()は、mbedtls_mutex_lock/unlock()で囲むことにした。


そのおかげか、今のところ安定している。
でも、実はECDSAは直接関係なくて、その付近で使っている変数が問題だったり、あるいは単にタイミングが発生しにくくなっただけという可能性もある。


mbedtls_ecp_groupを共有していることの危険性が分かってないのよねぇ。
暗号化関係って、よくわからない計算をたくさんしているだけに見えてしまい、深く追いたくないのだ、そうもいっていられんのか。


2017/06/14

気になったので、mbedtls_ecp_keypair構造体の構成を見てみた。
mbedtls_ecp_groupではなくmbedtls_ecp_keypairなのは・・・ソースを見直すとkeypairの変数をグローバルで確保し、最初にloadして使い回していたからだ。

typedef struct
{
    mbedtls_ecp_group grp;      /*!<  Elliptic curve and base point     */
    mbedtls_mpi d;              /*!<  our secret value                  */
    mbedtls_ecp_point Q;        /*!<  our public value                  */
}
mbedtls_ecp_keypair;

あー、秘密鍵とか公開鍵をこの中で保持しているじゃないか!
こんな変数を使い回していたら、そりゃよろしくないですわな。。。
コメントにも「Members purposefully in the same order as struc mbedtls_ecdsa_context」となっているので、コンテキストが異なるスレッドからは使ってはいけない代物なのだ。


そういうわけで、毎回スタックでmbedtls_ecp_group_load()して使うようにした。
なんか、暗号化ライブラリって重たい処理をしていそうなので、使い回せるところは使い回した方がよいのではないかと考えてしまったのだ。
でも、それはそれでセキュリティ的によろしくないだろうから、考え直さないと。

[ios]Core NFC?

いつかなー、いつかなー、と思っていたiOSでのNFC APIだが、そろそろ使えるようだという話が出ている。

Apple adds support for NFC tags to iPhone 7 and Apple Watch • NFC World


APIはこちら。
まあ、実機を持ってないし、開発環境も無いので、見てみただけだ。

Core NFC | Apple Developer Documentation

Reading NFC NDEF tags is supported on iPhone 7 and iPhone 7 Plus.

だそうだ。


それはともかく、タイトルだけ見る限りはタグの検出とNDEFの読み出しができそうだ。

気になるのは「types 1 through 5」というところ。
えっ、いつの間にType5が出たの??

Type 5 Archives - NFC Forum | NFC Forum
あー、AndroidでいうところのNFC-Vか。


NFCは技術として好きなのだけど、NFC Forumが仕様書を会員以外には販売方式にしてから、どうにもやる気がなくなってしまった。
せめて、NDEFの仕様書とTypeの仕様書は公開してくれてもよいと思うのだが。。

2036年問題?

何気なく、2038年問題のことを調べていた。
正確にいえば、「2037年だっけ、2038年だっけ」を調べていたのだが、その途中で「2036年問題」というものを見つけた。


2036年問題とは - IT用語辞典 Weblio辞書

  • NTPは1900年1月1日を起点とした秒数で時間を表している
  • 範囲は、32桁の2進数
  • オーバーフローするのは、2036年2月6日0時54分54秒


あれ?
2038年問題の方を見てみよう。


2038年問題とは - IT用語辞典 Weblio辞書

  • UNIX環境では1970年1月1日0時0分0秒を起点とした秒数で時間を表している
  • 範囲は、31桁の2進数
  • オーバーフローするのは、2038年1月19日


ああ、符号付きかどうかの違いで70年近く差が出てくるのね。

マイナスになることもなさそうなのに、なぜ符号付きにしたのだろう?
当時の事情は知らないのだが、そういうコンピュータが多かったのかもしれん。


C言語だとtime_tで扱うのだけど、今は64bitになっているものが多いと思う。
もしかしたら、64bitのOSしか使っていないせいかも。
ただ、環境依存になることは仕方あるまい。

RTCなんかは割り切ってて、100年計測できるようになってるものが多かったように思うので、日付についてはアプリ管理だ。
昔はLinuxも32bitで扱ってて、どうせRTCチップ用にドライバ作るんだからということで、時計回りは別で扱うようになっていた。
便利なAPIは使えなくなってしまったのだけど、まあ、そういうのもありですな。


time_tの変数をポインタにして、uint32_tなんかでキャストし、またtime_tで直接ポインタから元に戻そうとすると変な値になってしまうから注意したまえ(2回くらいやらかした。。。)。

2017/06/06

[cortex-m]SWVとRTTは別物だろうか

How to perform runtime error checking on Cortex-M devices


ARM Cortex-MにはSerial Wire Viewerというリアルタイムイベントを拾うことができる機能があるらしい。
最近は組込みから遠のいているものの、手元にあるのはCortex-Mが載ったマイコンかESP8266くらいしかないので、デバッグ手段は知っておきたいものだ。
Hard Faultなんかが拾えるような感じで書かれているので、それができるならありがたい。



CoreSight
Keilでは、Serial Wire Serialの略がSWVになっている。何でだ?
ともかく、SWOピンを使うようだ。
SWOだから、Outputだろう。


ARM Information Center
ARMのサイトも見たが、これといった情報はない。
SWOとなんか命令がセットになって使えるようなもののようだ。


KeilのページではULINKなどしか載っていなかったが、ARMのしくみならJ-Linkでも使えるはずだ。
SEGGER - The Embedded Experts - J-Link Debug Probes - Software - J-Link SWO Viewer
こちらは、SWO Viewerというアプリ。
コードを足してやらんといかんようだが、SWOの出力を拾えるようだ。
Keilとかだったら、printf()で書けるのかもしれん(見てない)。


そうなると、RTTとの違いが気になる。
RTTはSEGGERのJ-Linkでしか使えなかったと思うが、nRFシリーズではソースが入っていたし、手っ取り早く使える。
SEGGER - The Embedded Experts - J-Link Debug Probes - Real Time Transfer
これもSWOを使うようだ。


SWVとRTTの違いはどういうところにあるのか探していると、Atollicさんのブログがあった。
Using the SEGGER Real-time terminal (RTT) interface with Atollic TrueSTUDIO
ぱっと見て気付いたのは、双方向ということと、パフォーマンスが高い、あとはJ-Linkで使える、というところか(RTTのページにも書いてあるが。。。)。
J-Linkのみという制限はあるが、上記の特典が得られる、という見方がよいのかな。

キーボードなどからの入力は試したことがないので、やってみたいところだが、用途が思いつかない。。。
ときどきログを出力させたいとか、全部ログに出すと重たすぎるので、分割させたいとかか?

[c/c++]たぶん構造体メンバをvolatileにしても有効(弱気)

タイトルが弱気だが、致し方ない。


何がしたかったかというと、pthread_create()で動かしているスレッドでwhile()させてぐるぐる動かしている状態を、外部から止めたかったのだ。

スレッドで、whileで、変数監視となると、volatileになる。
普段はグローバル変数で監視させていたのだが、今回はスレッドをぽこぽこ作るようにしていたので、各スレッドが使う変数を構造体でまとめて初期値で与えつつ、そのままスレッドと共有する領域として使おうという計画だ。
おじさんはね、OSを使ったプログラムに慣れていないので、他に方法が思いつかなかったのだ。


その構造体の中に、ループを制御するための変数を持とうと思ったが、ちょっと迷った。
volatileって、構造体のメンバに指定しても反映してくれるのだろうか?


こういうときは、環境依存になってもよいから実験するに限る。


#include <stdio.h>

struct vol {
    int     abc;
    volatile int     def;
} volvol;


int main(void)
{
    while (volvol.def) {
        ;
    }
    return 0;
}

$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

こんな環境で試した。
なお、Ubuntuは16.04で、VirtualBox上で動かしている。
コンパイルはこのようにして、O2で最適化してアセンブラ出力させた。

$ gcc -O2 -o vol.S -S vol.c


まず、volatileありの場合。

	.file	"vol.c"
	.section	.text.unlikely,"ax",@progbits
.LCOLDB0:
	.section	.text.startup,"ax",@progbits
.LHOTB0:
	.p2align 4,,15
	.globl	main
	.type	main, @function
main:
.LFB23:
	.cfi_startproc
	.p2align 4,,10
	.p2align 3
.L2:
	movl	volvol+4(%rip), %eax
	testl	%eax, %eax
	jne	.L2
	rep ret
	.cfi_endproc
.LFE23:
	.size	main, .-main
	.section	.text.unlikely
.LCOLDE0:
	.section	.text.startup
.LHOTE0:
	.comm	volvol,8,8
	.ident	"GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
	.section	.note.GNU-stack,"",@progbits

ふーん。

次、volatile無しの場合。

	.file	"vol.c"
	.section	.text.unlikely,"ax",@progbits
.LCOLDB0:
	.section	.text.startup,"ax",@progbits
.LHOTB0:
	.p2align 4,,15
	.globl	main
	.type	main, @function
main:
.LFB23:
	.cfi_startproc
	movl	volvol+4(%rip), %eax
	.p2align 4,,10
	.p2align 3
.L2:
	testl	%eax, %eax
	jne	.L2
	rep ret
	.cfi_endproc
.LFE23:
	.size	main, .-main
	.section	.text.unlikely
.LCOLDE0:
	.section	.text.startup
.LHOTE0:
	.comm	volvol,8,8
	.ident	"GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
	.section	.note.GNU-stack,"",@progbits


差分は、ここ。
左がvolatileありで、右がvolatileなしだ。

image

L2ラベルでループしていて、volatileありはmovlがループ内に、volatile無しはmovlがループ外になった。

効いてる! 効いてるんだ!!


まあ、そもそもなんでvolatileが効かないかもしれないと思ったのか、という気もしてくる。
単に不安になっただけだ。
そういうことってあるだろう?


そういえば、gccとかって中間言語に吐き出してからリンカに掛けるとかだったから、アセンブラで安心するのは早いかもしれん。。。
いや、さすがにそこまでは疑わなくてもよいか。

2017/06/02

[bc]スクリプト

たまにはBitcoinの記事も書いておかねば。
べ、別にネタに困ってるわけじゃないんだからね!


Bitcoinのブロックチェーンに載せるデータは、トランザクションが最小単位になる。
メッセージとしてはping/pongやgetdataなどいろいろとあるのだが、ブロックチェーンに載るものとしてはtxメッセージで送信するトランザクションが基本になるだろう。

トランザクションには、だいたいこういうデータが載っている。

    • トランザクションのバージョン
    • 送金元の情報
    • 送金先の情報
    • どのくらい待ってからマイニングできるようになるか


「送金元の情報」は、その前のトランザクションの「送金先の情報」とつながっている。
この意味に慣れるまで、私はけっこう時間がかかった。。。

銀行の送金だと、送金元が「いくら、誰誰に送金する」と指定して、送金先の人にその額が入る。
Bitcoinの場合、送金元が「いくらは誰誰に送金し、残りはこれこれに送金する」という記載の仕方をする。
例えば、誰かに0.1BTC送金する必要があったとしよう。
そして手元に1BTC使えるトランザクションを持っていたとする。
そうすると、

  • 0.1BTCを送金したい人に
  • 残りの0.9BTCを自分のお釣りに

という送金の仕方になる。

お釣りを、送金元と同じアドレスに指定することもできるのだが、セキュリティ的には推奨されない。
だから、だいたいは見た目として2つのアドレスに送金しているように見える。
初めてトランザクションを見たときには「なんで全部送らないといけないのよぉぉ」と思ったものだ。

そしてまた、トランザクションを作るときには手数料(FEE)がかかる。
先ほど0.1BTC + 0.9BTCのような書き方をしたが、実際にはこれにFEEが引かれて、0.89999BTCみたいな額になっていることだろう。

FEEの決め方はよくわかっていないが、トランザクションのバイト数が基準になっている。


で、スクリプトだ。

BitcoinにはEthereumほどの柔軟性はないにしても、スクリプトを書いて送金条件を作ることができる。
逆ポーランド記法というか、スタックにためながら演算していくのだ。


その演算は、大ざっぱに言えば、「前のトランザクションの送金先情報」と「それを送金元として使うトランザクションの送金元情報」がセットになって解くことができるスクリプトになっている。
私のイメージとしては、昔のウェスタンな取引で、お札をギザギザに破って取引相手に渡しておき、現場でそのギザギザがお互いに一致するかどうかで見分けるアレだ。


今のところ、この辺が使えるようである。
Script - Bitcoin Wiki

「今のところ」がつくのは、Bitcoinがトランザクションのバージョンによって使えるスクリプトが変わるからである。
例えば、NOP2やNOP3のような命令が、バージョン2ではOP_CLTVやらOP_CSVとして使えたりする。
Script - Bitcoin Wiki - Locktime


こういう、意味の使い方が変えられるという自由さが、仮想通貨のよいところだったり悪いところだったりするのだろう。

2017/06/01

[c/c++]キャストで逃げる

コーディングするたびに発生して、毎回悩むのに答が出ない問題がいろいろある。

キャストで逃げるというのも、まだ私の中で解決していない。
たとえば、ポインタとサイズをセットにした構造体を作ったとしよう。

struct xxx {
  int len;
  uint8_t *ptr;
};

C言語だと、アドレスは分かってもサイズが分からないからね。


malloc()などでRAMを使う場合はこれでもよいのだが、const値のようにROMで持っているデータを使いたいとなると悩んでしまう。
ptrはconstが付いていないため、書き換え可能なように見えてしまうからだ。
構造体のインスタンスにconstを付けても、lenやptr自体は書き換えできないが、ptrが指す先はconstではないからね。
(最近もこういう話をどこかで書いたな。。。)


今回は、こうやって回避した。

struct const_xxx {
  int len;
  const uint8_t *ptr;
};

constになっている型をもう1つ用意したのだ。
これがあれば、RAMであっても書き換えるつもりがないことを表すこともできるし、大丈夫だ!


と思っていたのだが、また揺らぎだした。

これ、ポインタがプリミティブ型だからよいようなものの、構造体だったらどうだろうか?
その構造体の中にポインタが入っていて、それがconst無しだったら強制力がないだろう。

だったらもう、constにした構造体の中身は下の方に至るまで書き換えるなよ?とした方が潔くないだろうか。
証明するのは大変だが、テストコードで書き換え不可のアドレスを渡して不正メモリアクセスなどの例外が発生しないことを確認するくらいしかないか。

これにconstのアドレスを渡したい場合は、そのまま渡すとwarningが出るので、const外しキャストで逃げることになろう。
格好はよくないのだけど、他に方法が思いつかない。
warningのままにして気付かせるようにしておくのもありだが、warningをerror扱いにしたいこともあるし、何よりうっとうしい。
const_cast<>がないので検索もしづらいのだ。


うーん、いろいろめんどうだ。
何かC言語仕様の見落としがあるかなぁ。。。