目次: Raspberry Pi
Raspberry Pi 3のAudio Outの最後の謎がわかりました。
その6(2021年5月12日の日記参照)にてRaspberry Pi 3の回路図が間違っているのでは?と疑っていましたが、違いました。ケーブルに入っている抵抗のせいでした。
今まで測定に使用していたオーディオケーブルにはプラグ内に抵抗が入っています。そもそもなんでこんなの買ったんだろ……?プラグの見た目からはわかりませんので、テスターで各端子間の抵抗を計測した結果は下記のとおりです。
ミニL | ミニR | ミニG | RCA L | RCA G | RCA R | RCA G | |
---|---|---|---|---|---|---|---|
ミニL | --- | 294 | 147 | 46.7k | 147 | 46.7k | 147 |
ミニR | --- | 147 | 47.0k | 147 | 46.4k | 147 | |
ミニG | --- | 47.0k | 0 | 47.0k | 0 | ||
RCA L | --- | 47.0k | 94.0k | 47.0k | |||
RCA G | --- | 47.0k | 0 | ||||
RCA R | --- | 47.0k | |||||
RCA G | --- |
測定結果から想定される回路図です。左がミニジャック側、右がRCAプラグ側です。
この結果を踏まえてシミュレーションすると実測値とほぼ一致しました。
Audio Out回路のシミュレーション結果(125Hz矩形波を入力に設定)ケーブルの抵抗を考慮
Audio Out回路の実測値(黄色Audio Out、水色PWM信号125Hz矩形波)
気づいてみれば何とも初歩的なミスでしたが、ケーブルは0Ωと思い込んで見落としました。他人(RasPiの回路図)を疑う前に自分を疑えという良い教訓ですね〜。
目次: RISC-V
買い物メモです。先日(2021年5月28日の日記参照)SiFive HiFive Unmatchedを購入しました。このボードはmicroSDからブートしますが、追加のストレージとしてNVMe SSDが装着できます。
Western DigitalのWDS100T2B0C-ECを購入しました。Amazonで13,000円くらいでした。容量1TB、規格M.2 2280、接続NVMeです。コストパフォーマンス重視のWD Blueシリーズです。
WD BlueシリーズはWD Blackシリーズと比較すると速度で見劣りするものの、そもそもHiFive UnmatchedのCPUはそれほど速くないですしWD Blueで十分でしょう。きっと。
目次: OpenCL
引き続き、独自アクセラレータのテンプレート実装pocl/lib/CL/devices/accelの細かな問題を調べます。次の問題はOpenCLの初期化です。clGetPlatformIDs() から初期化関数pocl_accel_init() に辿り着いたところでabort() が呼ばれクラッシュします。
| GENERAL | accel: accelerator at 0x1000 with 0 builtin kernels Could not open /dev/mem
テンプレート実装の意図としては /dev/memをopen() してメモリマップされたハードウェアのレジスタを読み書きしたいようです。今回は実際のハードウェア相手ではないので、レジスタの読み書きではなく /dev/memの代わりにバイナリファイルを開いてもらうように書き換えます。
// pocl/lib/CL/devices/accel/accel.cc
cl_int pocl_accel_init(unsigned j, cl_device_id dev, const char *parameters) {
...
POCL_MSG_PRINT_INFO("accel: accelerator at 0x%zx with %zu builtin kernels\n",
D->BaseAddress, D->SupportedKernels.size());
int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
if (mem_fd == -1) {
POCL_ABORT("Could not open /dev/mem\n"); //★★このabortでクラッシュ
}
...
ファイル名を書き換えて突破するとデバイスが持っているメモリのサイズを取得し、メモリマップしようとする部分で怒られます。
| GENERAL | accel: accelerator at 0x1000 with 0 builtin kernels a.out: ../lib/CL/devices/accel/accel.cc:196: void MMAPRegion::Map(size_t, size_t, int): Assertion `Data != MAP_FAILED && "MMAPRegion mapping failed"' failed.
サイズを取得している箇所は下記のとおりです。
// pocl/lib/CL/devices/accel/accel.cc
cl_int pocl_accel_init(unsigned j, cl_device_id dev, const char *parameters) {
...
uint32_t ctrl_size = D->ControlMemory.Read32(ACCEL_INFO_CTRL_SIZE);
uint32_t imem_size = D->ControlMemory.Read32(ACCEL_INFO_IMEM_SIZE);
uint32_t dmem_size = D->ControlMemory.Read32(ACCEL_INFO_DMEM_SIZE);
uint32_t pmem_size = D->ControlMemory.Read32(ACCEL_INFO_PMEM_SIZE);
uint32_t max_region =
std::max(std::max(ctrl_size, imem_size), std::max(dmem_size, pmem_size));
D->InstructionMemory.Map(D->BaseAddress + max_region, imem_size, mem_fd);
...
バイナリファイルを書き換えて何か適当な値が読めるようにしてやりすごすか、面倒ならばD->ControlMemory.Write32(ACCEL_INFO_CTRL_SIZE, 0x2000); のように固定値を書いておくと次に進みます。今は実際のデバイスが相手ではないので、とりあえず先に進めて後で考えましょう。
目次: Linux
TwitterでGUIの話をしている人を見かけて思い出した話です。私はGNU/Debian LinuxのGUIをLightDM + GTKにして使っています。
GUIにこだわりはなく一時期デフォルトだったLightDMを使い続けているだけです。デフォルトになっただけあって、良くできていると思うし特に不満はないです……が、1点だけ言わせてもらえば、ウインドウサイズ変更の判定が厳しすぎませんか?
特に左辺、上辺、右辺が1pxしか反応してくれないので、マウス操作が非常にシビアです。手が震えてきます。
Twitterで上記の話をしていたところ、Alt + 右マウスボタンでウインドウサイズを変えられるよ、と教えてもらいました。今度からそうします。1pxに合わせるのは手が疲れる……。
目次: Zephyr
JTAGが繋がった(2021年6月5日の日記参照)記念にZephyrをHiFive Unleashedで動かしてみました。使うハードウェアはUARTだけ、コードもJTAGでITMにロードして動作させるだけなら楽勝だろと思いきや、全然UARTから文字が出力されず1日掛かってしまいました。
*** Booting Zephyr OS build zephyr-v2.6.0-39-g2bf63134e8f0 *** thread_a: Hello World from cpu 0 on hifive_unleashed! thread_b: Hello World from cpu 0 on hifive_unleashed! thread_a: Hello World from cpu 0 on hifive_unleashed! thread_b: Hello World from cpu 0 on hifive_unleashed! thread_a: Hello World from cpu 0 on hifive_unleashed! thread_b: Hello World from cpu 0 on hifive_unleashed!
原因はコアのPLL設定が間違っていて、コアクロックから分周して作るUARTのボーレートもおかしな周波数になっていたからです。
FU540のリセット直後は外部クロック源(33.33MHz)をそのまま使って動きます。起動後PLLを1GHz(33.33 / 1 * 120 / 4 = 999.9MHz)に設定し、コアクロックをPLL側に切り替えなければなりません。
PLLの設定はFSBLという2段目のブートローダーが行うので、通常はOSが気にする必要はありません。しかしRTOSは大抵ブートローダーを経由しませんから、ブートローダーに隠れた設定も拾ってきて実装しないと動かないことがあります。
ブートローダーがあって当たり前のリッチ系SoCでRTOSを動かそうとすると、大抵この「暗黙のうちに設定されている何か」が抜け落ちて動かなかったりおかしくなったり、何かと面倒が起きます。
幸いなことにSiFiveのブートローダーはコードが公開されており、完全ブラックボックスのSoCと比べれば、難易度は低い部類です。ありがたいですね。
メモ: 技術系の話はFacebookから転記しておくことにした。加筆。
目次: RISC-V
昔買ったHiFive Unleashedというボード、JTAGはちょっと特殊なコネクタが必要だと思っていて接続を諦めていました。ところが今日、ファンを掃除したあと動作確認をした際に、USB Serialと一緒にUSB JTAGも用意されていることに気づきました。
HiFive Unleashedはディスコンでもう手に入りませんし、後継機種のHiFive Unmatchedも買った今となっては、かなり今更感ありますが……。
ちょっと試したところ、簡単にOpenOCDが繋がりSMPモードにすると5コア(rv64imacなE51とrv64gcなU54 x 4)が見えました。
adapter speed 10000
adapter driver ftdi
ftdi_device_desc "Dual RS232-HS"
ftdi_vid_pid 0x0403 0x6010
ftdi_layout_init 0x0008 0x001b
ftdi_layout_signal nSRST -oe 0x0020 -data 0x0020
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x20000913
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME -rtos hwthread
target create $_TARGETNAME.1 riscv -chain-position $_TARGETNAME -coreid 1
target create $_TARGETNAME.2 riscv -chain-position $_TARGETNAME -coreid 2
target create $_TARGETNAME.3 riscv -chain-position $_TARGETNAME -coreid 3
target create $_TARGETNAME.4 riscv -chain-position $_TARGETNAME -coreid 4
target smp $_TARGETNAME.0 $_TARGETNAME.1 $_TARGETNAME.2 $_TARGETNAME.3 $_TARGETNAME.4
init
halt
SiFiveのSoCはFU540も後継のFU740も命令セットの違うコアを混載するのが好きですね。混載は良いとしてせめて同じ命令セットにしてほしかったです。単純なSMPしか持ってないOSだと制御できないじゃん……。
メモ: 技術系の話はFacebookから転記しておくことにした。加筆。
目次: OpenCL
引き続き、独自アクセラレータのテンプレート実装pocl/lib/CL/devices/accelの細かな問題を調べます。デバイス数の取得の問題を回避すると、次はデバイスのパラメータを渡す問題に遭遇します。
// pocl/lib/CL/devices/accel/accel.cc
void pocl_accel_init_device_ops(struct pocl_device_ops *ops) {
ops->device_name = "accel"; //★★デバイス名はaccel
ops->init = pocl_accel_init;
...
// pocl/lib/CL/devices/devices.c
cl_int
pocl_init_devices ()
{
...
dev_index = 0;
/* Init infos for each probed devices */
for (i = 0; i < POCL_NUM_DEVICE_TYPES; ++i)
{
if (pocl_devices_init_ops[i] == NULL)
continue;
str_toupper (dev_name, pocl_device_ops[i].device_name); //★★dev_nameはデバイス名を大文字に変換したACCELになる
assert(pocl_device_ops[i].init);
for (j = 0; j < device_count[i]; ++j)
{
...
/* Check if there are device-specific parameters set in the
POCL_DEVICEn_PARAMETERS env. */
POCL_GOTO_ERROR_ON (
(snprintf (env_name, 1024, "POCL_%s%d_PARAMETERS", dev_name, j) //★★環境変数名を生成する箇所
< 0),
CL_OUT_OF_HOST_MEMORY, "Unable to generate the env string.");
errcode = pocl_devices[dev_index].ops->init (
j, &pocl_devices[dev_index], getenv (env_name));
...
実装ではpocl_accel_init() にて環境変数の値をパースしてデバイスのパラメータを取得します。環境変数名はデバイス番号によって変化しますが、0番目のデバイスであればPOCL_ACCEL0_PARAMETERSという名前になります。環境変数名は上記にあるとおりpocl_init_devices() で決めています。
困ったことに環境変数が見つからないとabort() してしまうので、環境変数には最低でも何か1つ数値を渡す必要があります。なお1つ目の値はレジスタ領域のベースアドレスだと解釈されるようです。
他の実装(pthreadとcuda)は環境変数を使わないので、同様の問題は存在しません。最終的にはaccelも環境変数に頼らない実装に変えていく必要がありますが、今はそのままにしておきます。
< | 2021 | > | ||||
<< | < | 06 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | - | - | - |
合計:
本日: