2021年11月19日金曜日

PYNQ-Z2向けのアクリル天板のレーザーカットを頼んでみた話



修論が忙しいのに何も手を付けていない中の人です。修論で自前のFPGAボード(PYNQ-Z2)を使ってるのですが、基本裸運用なのでショートやらなんやらが怖い状態です。今回はその対策としてアクリルの天板を作成してみた話です。

基板には元から足がついているので5mm程度浮いているが,
基本むき出しなので不安が残る





1.設計する


部品の配置図.
配置するエリアのサイズを入れることで,領域内に入りますよアピール.

天板の設計をするにあたり寸法図を探したのですが、ネットの海にはなかったようでした。そこでその辺にノギスが生えていたので各部を測定し寸法を割り出しました。寸法を割り出したのちFusion360で実際の設計を行い、dxfで出力しました。



2.発注する


完成予想図

設計ができたので次は発注です。今回は手間をあまりかけたくなかったので、レーザーカットのサービスを使うこととし、いろいろ探した結果いつも基板を頼んでいるElecrowのサービスを使うことにしました。
基板の発注と異なる点として、領域内であればいくつでも部品を置くことができます。無理に1つの部品にする必要はありませんでした。
先ほど作成したdxfのファイルとともに、カット時の部品の配置図と完成予想図をpdfで添付しました。完成予想図は正直に言っていらないのですが、たまたまあったので入れました。
発送業者に関しては、最近追加された佐川急便を使ってみました。

3.到着と組み立て


届いた天板とFPGA基板.合計5セット届く.

発注が11/10で、到着が11/18だったので1週間ちょっとぐらいでした。おそらく基板と同じ感覚で待てばよさそうです。
実際に組み立てるとこんな感じになりました。PYNQ-Z2に搭載されるチップであればそこまで発熱しないのでファンは不要なのですが、スペースが開いていたので付けました。
スペーサーは秋月、ねじ・ナット・ワッシャーは近所のホームセンターで購入しました。

組み立てた様子.この時はファンが外側についている.
ファンが外に出っ張っていると壊す恐れがあり,この後内側に移設した.

4.まとめ


初めてレーザーカットのサービスを使ってみましたが、特に詰まるところはありませんでした。簡単な形状であればプリント基板を作るよりも楽にできました。印字みたいなこともできるようなので、また機会があれば試してみたいです。


©2021 shts All Right Reserved.


2021年11月12日金曜日

ctypesを使ってPython3からC++の生配列(not std::vector)をいじる


修論がだんだん忙しくなってきた中の人です.研究でPythonからC++のコードをたたく必要が出てきたのでいろいろお試ししていたのですが,その際にいろいろ詰まったので書き残しておきます.今回のコードはこちらに上げてあります.



1.PythonからC++をさわる


Python側からC++のコードを実行する方法はPython.hやSWIG,Boost.Python,pybind11など様々なものがあります(参考1).今回の研究で使用している環境(HLSツールや実行環境)の問題で,以下の制約がありました.

  • 相互呼び出しの機能は不要(PythonからC++を実行できればOK)
  • STLコンテナが使えない(≒std::vectorなどを使う必要がない)
  • g++やCPythonに標準で入っているライブラリのみで行けると非常に楽

いろいろ調べた結果,とりあえずctypesを使ってみることにしました.

2.C++で共有ライブラリを作成する


今回は以下のC++コードをPythonから呼び出します.

// MAC_VEC.hpp
#include <cstdint>
namespace MAC_VEC{
    template<typename data_t>
    void mac_vec(uint32_t size, data_t a[], data_t x[], data_t b[], data_t y[]){
        for(uint32_t i = 0; i < size; i++){
            y[i] = a[i] * x[i] + b[i];
        }
    }
}

ctypesを使う場合テンプレートをそのまま呼び出すことはできないので,型を確定させるラッパーを作っておきます.

// MAC_VEC_WRAPPER.cpp
#include <cstdint>
#include "MAC_VEC.hpp"

extern "C" {
    void MAC_VEC_FP32(uint32_t size, float a[], float x[], float b[], float y[]){
        MAC_VEC::mac_vec<float>(size, a, x, b, y);
    }
    void MAC_VEC_INT16(uint32_t size, int16_t a[], int16_t x[], int16_t b[], int16_t y[]){
        MAC_VEC::mac_vec<int16_t>(size, a, x, b, y);
    }
    void MAC_VEC_UINT32(uint32_t size, uint32_t a[], uint32_t x[], uint32_t b[], uint32_t y[]){
        MAC_VEC::mac_vec<uint32_t>(size, a, x, b, y);
    }
}

とりあえずfloat(fp32),int16, uint32の3パターン準備しました.この時extern "C"をつけないと,コンパイラ側でシンボル名を書き換える操作を行うため,Python側から読めなくなることがあります(参考2).
この二つが準備できたら,g++で共有ライブラリ(.so)を生成します.

// generate_so.sh
g++ MAC_VEC_WRAPPER.cpp -shared -o MAC_VEC.so

コンパイルが通ると,MAC_VEC.soが生成されるはずです.

3.Pythonから動かす


先ほど作成したライブラリをPythonから動かしてみましょう.今回使用するctypesは,Python2.5から標準で入っているライブラリです.
この記事ではMAC_VEC_FP32のみを扱います.残りの二つのに関しては,こちらの実装を確認してください.

// C_API_TEST.py
import ctypes

# load .so file
libc = ctypes.cdll.LoadLibrary("./MAC_VEC.so")

# define params and src/dst data
len_vec = 8
coef = 1.1
vec_x = [i * coef for i in range(len_vec)]
vec_a = [2 * coef for i in range(len_vec)]
vec_b = [(i - 8) * coef for i in range(len_vec)]
vec_y = [0 for i in range(len_vec)]

# define data type and convert method
len_vec_type = ctypes.c_uint32
data_type = ctypes.c_float
vec_type = data_type * len_vec

# convert list-type data
_len_vec = len_vec_type(len_vec)
_vec_x = vec_type(*vec_x)
_vec_a = vec_type(*vec_a)
_vec_b = vec_type(*vec_b)
_vec_y = vec_type(*vec_y)

# execution
libc.MAC_VEC_FP32(_len_vec, _vec_a, _vec_x, _vec_b, _vec_y)

# convert results to list-type
result = list(_vec_y)
print(result)

配列の型を宣言するあたりの作法で詰まりました.配列のデータ型×配列の要素数で表現し,アンパックしたlistを投げ込むことで変換できるようです.
実行すると,こんな感じの結果が得られました.

実行結果.INT16とUINT32の結果も表示している.

とりあえず正しい結果が得られているようです.

4. まとめ


今回はPythonからC++の生配列をいじってみました.ライブラリの追加も最小限で済み,割と簡単に動かすことができました.少し凝ったデータのやり取りをしようとすると大変かもしれないですが,C/C++標準のデータ型であれば十分に使えると思います.


©2021 shts All Right Reserved.