NetBSDでFPAG開発を始めてみる(NVCとVHDL)

この記事は、NetBSD Advent Calendar 2025の25日目の記事です。

はじめに

本当は、PyRTLで書いたロジックをTnag Nano 9Kで動かしてみたいのですが、これから勉強しないといけないので、しばらく先に進めそうにありません。 と言うことで、22日目23日目でVHDLを使ってみていましたが、その際に試していたNVCというVHDLのシミュレーターについて、試したことを書いておきたいと思います。

NVCできること

NVCは、GHDLとは違い、合成をすることはできません。 あくまで、IEEE 1076-2008のほとんどをシミュレーションできるだけのようです。 VHDLについては、普通の利用者は、GOWIN FPGA Designerを使うでしょうから、良いシミュレーターであるだけでも大きな意味があるのだと思います。 NVCはJ-Core Open Processorでも使われているようです。

NVCをインストールする

pkgsrc/cad/nvcとして、nvc-1.18.2を用意しました。 この後にインポートしておきたいと思います。

と言うことで、以下のようにインストールすれば良いでしょう。

# cd /usr/pkgsrc/cad/nvc
# make install

これによって、/usr/pkg/bin/nvcがインストールされます。 これを使って、22日目のGHDLによるシミュレーション用のMakefileに、NVCを使ったパターンも追加してみます。 analysisをするにはanalysisnvcターゲット、elaborateにはelaboratenvcターゲット、シミュレーションを実行してvcdウェーブフォームファイルを得るにはrunnvcターゲットを用意しました。

$ cat Makefile
TARG=   counter

GHDL_FLAGS+=    -fsynopsys

.PHONY: analysis elaborate run clean analysisnvc elaboratenvc runnvc

analysis: ${TARG}.vhdl
        ghdl -a ${GHDL_FLAGS} ${TARG}.vhdl
        ghdl -a ${GHDL_FLAGS} ${TARG}_tb.vhdl

analysisnvc: ${TARG}.vhdl ${TARG}_tb.vhdl
        nvc -a ${TARG}.vhdl ${TARG}_tb.vhdl

elaborate: work-obj93.cf
        ghdl -e ${GHDL_FLAGS} ${TARG}
        ghdl -e ${GHDL_FLAGS} ${TARG}_tb

elaboratenvc: analysisnvc
        nvc -e counter_tb

run: elaborate
        ghdl -r ${GHDL_FLAGS} --time-resolution=ns ${TARG}_tb --vcd=counter.vcd --stop-time=1ms

runnvc: elaboratenvc
        nvc -r --format=vcd --wave=counter-nvc.vcd --stop-time=1ms counter_tb

clean:
        rm -f counter.vcd
        rm -f work-obj93.cf
        rm -f counter-nvc.vcd
        rm -fr work

ただし、nvcの生成したcounter-nvc.vcdファイルのタイムスケールはfs単位になっていて、変更ができないようです。

結果のvcdファイルをGtkWaveで表示してみる

Zoom-outしていくと、下図のように表示できました。 既定の表示は非常にZoom-inされた状態ですが…。

これで、GHDLだけでなくシミュレーションを実行できるようになりました。 検証も確実さを上げることができそうです。

NetBSDでFPAG開発を始めてみる(PyRTL) 準備編

この記事は、NetBSD Advent Calendar 2025の24日目の記事です。

はじめに

21日目から23日目にかけて、安価に購入したFPGA基板であるTang Nano 9Kのための開発環境をNetBSD上に整え、基本的にはNetBSDだけで開発できることを確認しました。 でも、合成や配置配線にもベンダー特有の最適化が必要であるかもしれません。 また、GOWIN FPGA Designerでは、A/Dコンバーターなどの各種ロジックやArm Cortex-M0やRISC-Vなどのアーキテクチャーのマイクロコントローラーを選択するメニューがありました。 どこまで無料で使えるのか分かっていませんが、そういうものはNetBSD上では使いようがなさそうです。

とは言え、私がVerilogを学んだり、Tang Nano 9Kを購入したのは、PyRTLという教育用のHDLを使ってみたかったためでした。 なので、そういう便利なものは使えなくても困りません。 PyRTLはVerilogのソースコードを出力してくれるので、21日目のように作業できれば、最適化は別として、それで良いとも言えます。

ですが、マイナーなものを使うと、世界で最初に困る人になってしまいがちです。 PyRTLにはJupyer Notebook/JupyeterLabで学べるような教材が用意されているのですが、少なくとも2025年12月28日現在lpkgsrc経由で利用できるJupyter Notebook/JupyterLabではエラーになりウェーブフォームをウェブブラウザー上では見ることができません。 せっかくPyRTLにはシミュレーション機能が内蔵されているのに、それを活用することができません。

おそらくNetBSDで作業しているから発生する問題ではないと思われるものの、pkgsrc/cad/py-PyRTL-0.12が壊れているのは困りますので、直すことにしました。

エラーの内容

まず、私は以下のようなJupyter NotebookとJupyterLabのパッケージをインストールしています。 これ以外にも関連するものはありますが、そこまで厳密である必要はないでしょう。

py314-jupyter-lsp-2.3.0
py314-jupyter_server_terminals-0.5.3
py314-jupyter_client-8.7.0
py314-jupyterlab-4.4.10
py314-jupyter_core-5.9.1
py314-jupyterlab-pygments-0.3.0
py314-jupyter_events-0.12.0
py314-jupyterlab-server-2.28.0
py314-jupyter_server-2.17.0

エラーの内容は非常に簡単です。ipynb-examples/example1-combologic.ipynbと言う教材のStep 2の3個目のセルのsim.tracer.render_trace(symbol_len=2)が失敗します。 エラーメッセージはJavascript Error: $ is not definedです。 FirefoxのDevelopert toolのconsoleには何も出ていませんが、Jupyter Notebook/JupyterLabの画面上に表示されるエラーです。

実を言うと、ipynb-examples/example1-combologic.ipynbは、examples/example1-combologic.pyから自動生成されています。 と言うことで、examples/example1-combologic.pyの内容をそのままJupyter Notebook/JupyterLabの1つのセルで実行して、どこがいけないのか調査することにしました。

エラーを調査する

ipynbファイルを保存すると、以下のような内容が含まれています。 この内容は元々のipynbにはありません。 どこかで自動的に挿入されているはずです。

(snip)
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/wavedrom/1.6.2/skins/default.js"),
$.getScript("https://cdnjs.cloudflare.com/ajax/libs/wavedrom/1.6.2/wavedrom.min.js"),
(snip)

ここまで特徴的であれば、/usr/pkg/lib以下を探しても迷うことはありません。 pyrtl/simulation.pyというファイルに含まれていることが分かりました。 そして、この$は、jQueryが提供されることが期待されているようです。 ですが、私のインストールしているJupyter NotebookもJupyterLabも、ウェブブラウザー上でjQueryの$は提供していないようです。 少なくとも$.getScript関数は使えません。

どこかでjQueryのgetScript関数は使うなと言われたような記憶もありますが、最新版では使えるのかもしれません。 いずれにしても、PyRTLの教材を動かすのには十分なはずです。 と言うことで、jQueryを読み込んだ後に、問題の箇所を実行するようにすれば良さそうです。

エラーを修正する

オリジナルのPyRTL-0.12のソースコードで関連する箇所を以下に示します。 この中の1775行目からのjs_code変数の中身を書き換えれば良さそうです。 改めて見ると、問題の箇所は、当然ですがJupyter Notebook/JupyterLabで実行した場合のみ呼び出されるようになっています。

  1760          if _currently_in_jupyter_notebook():
  1761              from IPython.display import (
  1762                  HTML,
  1763                  Javascript,
  1764                  display,
  1765              )
  1766
  1767              from pyrtl.visualization import trace_to_html
  1768
  1769              htmlstring = trace_to_html(
  1770                  self, trace_list=trace_list, sortkey=_trace_sort_key
  1771              )
  1772              html_elem = HTML(htmlstring)
  1773              display(html_elem)
  1774              # print(htmlstring)
  1775              js_stuff = """
  1776              $.when(
  1777              $.getScript("https://cdnjs.cloudflare.com/ajax/libs/wavedrom/1.6.2/skins/default.js"),
  1778              $.getScript("https://cdnjs.cloudflare.com/ajax/libs/wavedrom/1.6.2/wavedrom.min.js"),
  1779              $.Deferred(function( deferred ){
  1780                  $( deferred.resolve );
  1781              })).done(function(){
  1782                  WaveDrom.ProcessAll();
  1783              });"""
  1784              display(Javascript(js_stuff))

Jupyter NotebookやJupyterLabを実行できるウェブブラウザーであれば、fetch関数やpromiseのサポートは持っているはずな気がします。 と言うことで、安直ですがfetchして実行する関数を用意し、jQueryを読み込んだ後に、promiseを使って、既存のロジックを実行させることにしました。

以下のようなパッチを作成して適用しました。

@@ -1773,6 +1773,16 @@ class SimulationTrace:
             display(html_elem)
             # print(htmlstring)
             js_stuff = """
+            function fetchAndRunScript(uri) {
+              return fetch(uri)
+                .then(resp => resp.text())
+                .then(scrtext => {
+                  const scrfunc = new Function(scrtext);
+                  scrfunc();
+                })
+                .catch(err => console.error('Cannot load script:', err));
+            }
+            fetchAndRunScript("https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js").then(() => {
             $.when(
             $.getScript("https://cdnjs.cloudflare.com/ajax/libs/wavedrom/1.6.2/skins/default.js"),
             $.getScript("https://cdnjs.cloudflare.com/ajax/libs/wavedrom/1.6.2/wavedrom.min.js"),
@@ -1780,7 +1790,7 @@ class SimulationTrace:
                 $( deferred.resolve );
             })).done(function(){
                 WaveDrom.ProcessAll();
-            });"""
+            });});"""
             display(Javascript(js_stuff))
         else:
             self.render_trace_to_text(

こうすることで、ウェブブラウザー上でウェーブフォームを表示させることができるようになりました。 これで、FPGAに実装する前のチェックが簡単になります。

pkgsrc/cad/py-PyRTL-0.12nb1としてcommitしておこうと思います。

NetBSDでFPAG開発を始めてみる(VHDL) (2/2)

この記事は、NetBSD Advent Calendar 2025の23日目の記事です。

はじめに

22日目の記事では、GHDLを導入し、VerilogのプログラムをVHDLに移植して、そのウェーブフォームを見て、どうやら正しく移植できていそうということが確認できました。 今回は、YosysでVHDLで書かれたロジックを合成し、FPAGに反映してみます。

ghdl-yosys-pluginを導入する

ghdl-yosys-pluginをpkgsrc/devel/ghdl-yosys-pluginとしてインポートしました。 これを以下のようにインストールしておきます。 21日目を参照して、pkgsrc/devel/nextpnr-himbaechelとpkgsrc/devel/openFPGALoaderをインストールしておいてください。 pkgsrc/deve/yosysは、以下のようpkgsrc/devel/ghdl-yosys-pluginをインストールすると、依存パッケージとしてインストールされます。 もちろん、先にインストールされていても全く問題ありません。

# cd /usr/pkgsrc/devel/ghdl-yosys-plugin
# make install

合成と配置配線、ロードを実行する

元々のverilog用のMakefileを改造して、以下のようなVHDL用のMakefileを用意しておきます。

$ cat Makefile
BOARD=tangnano9k
FAMILY=GW1N-9C
DEVICE=GW1NR-LV9QN88PC6/I5

all: counter.fs

# Analysis
analysis: counter.vhdl
        ghdl -a -fsynopsys counter.vhdl

# Synthesis
counter.json: analysis
        yosys -m ghdl -p "ghdl -fsynopsys counter; synth_gowin -top counter -json counter.json"

# Place and Route
counter_pnr.json: counter.json
        nextpnr-himbaechel --json counter.json --write counter_pnr.json --freq 27 --device ${DEVICE} --vopt family=${FAMILY} --vopt cst=${BOARD}.cst

# Generate Bitstream
counter.fs: counter_pnr.json
        gowin_pack-3.14 -d ${FAMILY} -o counter.fs counter_pnr.json

# Program Board
load: counter.fs
        openFPGALoader -b ${BOARD} counter.fs -f

.PHONY: load analysis
.INTERMEDIATE: counter_pnr.json counter.json

また、なんとなくVerilogで小文字だった入出力のポート名称を大文字にしてしまったので、Tang Nano 9Kの端子への接続を指定するtangnano9k.cstファイルも以下のように作成しておきます。

$ cat tangnano9k.cst
IO_LOC "CLK" 52;
IO_PORT "CLK" PULL_MODE=UP;
IO_LOC "LED[0]" 10;
IO_LOC "LED[1]" 11;
IO_LOC "LED[2]" 13;
IO_LOC "LED[3]" 14;
IO_LOC "LED[4]" 15;
IO_LOC "LED[5]" 16;

ここまで来たら、実際にTang Nano 9KをNetBSDマシンに接続して、以下のように実行します。

$ make load

書き込みが終わると、Tang Nano 9Kの橙色のLCDの点滅が開始されます。 どうやらVerilogの場合と同様に2進数でカウントアップしてくれているようです。

これで、VerilogだけでなくVHDLでも、Tang Nano 9Kを利用できそうです。

でも私の本当の目的はPyRTLを使ってみることなのです。 これについても機会を見付けて試してみて、書いておきたいと思います。

NetBSDでFPAG開発を始めてみる(VHDL) (1/2)

この記事は、NetBSD Advent Calendar 2025の22日目の記事です。

はじめに

21日目では、Sipeed tang Nano 9KというGowin製のFPGAの搭載された基板を使い、Gowinの提供する開発ツールであるGOWIN FPGA Designner (21日目の記事ではGowin EDAと書きましたが、実際にインストールして起動してライセンスを割り当ててみたところ、こういう名前のソフトウェアでした)を使わずに、NetBSDだけでFPGAをプログラムしてみました。

その際は、Tang Nano 9Kで動くことが分かっているものでないと動作確認にもならないため、GitHubで公開されているlushaylabs/tangnano9k-series-examplesリポジトリーのcounterという例をpkgsrcに合わせて修正したものを使っていました。 この例はVerilogで書かれており、私が多少は分かるというのも良い点でした。

ただ、私はVHDLも体験してみたいと思っていました。 VHDLで書かれたものをyosysで扱えるものとして、ghdl-yosys-pluginというYosysのプラグインが用意されています。 まだ実験段階で扱えないVHDLの機能もあるのかもしれませんが、今回はこのghdl-yosys-pluginを使うために、VHDLでcounterを書き直して、シミュレーションを実行したいと思います。 明日は、Tang Nano 9Kをプログラムして、実際に動かしてみたいと思います。

VHDLを学ぶ環境を作る

VHDLのシミュレーションをするには、GHDLNVCが使えそうです。 ただ、今回はghdl-yosys-pluginを使うつもりですので、GHDLを使うことにしました。 GHDLは、pkgsrc/cad/ghdlとしてNetBSD上でも利用できます。 以下のようにインストールすれば良いでしょう。

# cd /usr/pkgsrc/cad/ghdl
# make install

これによって、ghdlコマンドを利用できるようになります。 実際の使い方は、後ほど試して行く中で探って行きたいと思います。

Verilogで書かれたcounterをVHDLに移植する

ところで、私は全くVHDLを知りませんでした。 ただ、かつてVHDLを学ぼうと思ったことはあったようで、図解VHDL実習 --ゼロから分かるハードウェア記述言語-- 堀 桂太郎著という本を持っていました。 第2版も出ているようですが、私が持っていて参照したのは無印のものです。 これで、VHDLでのロジック記載の基本的構造と、テストベンチの書き方を学ぶことができました。 もう少しちゃんと読めば、VHDLの良さを理解できそうです。 また、テストベンチの書き方については、GHDLのドキュメントも参考になりました。

Verilogで書かれたcounterは以下のようです。

$ cat counter.v
module counter
(
    input clk,
    output [5:0] led
);

localparam WAIT_TIME = 13500000;
reg [5:0] ledCounter = 0;
reg [23:0] clockCounter = 0;

always @(posedge clk) begin
    clockCounter <= clockCounter + 1;
    if (clockCounter == WAIT_TIME) begin
        clockCounter <= 0;
        ledCounter <= ledCounter + 1;
    end
end

assign led = ~ledCounter;
endmodule

これを逐一VHDLで書き直してみると、以下のようになりそうです。 VHDLでよくある書き方になっているとは思いませんが、一応後で実行するテストベンチの結果を見る限り機能は移植できているっぽいです。 第一印象としては、Verilogより固めに書かないといけないような印象を持ちました。

$ cat counter.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity counter is
        Port (CLK : in std_logic;
              LED : out std_logic_vector(5 downto 0));
end counter;

architecture behavior of counter is
        constant WAIT_TIME : integer := 13500000;
        signal ledCounter : std_logic_vector(5 downto 0) := "000000";
        signal clockCounter : std_logic_vector(23 downto 0) := "000000000000000000000000";
begin
        process(CLK)
        begin
                if(CLK'event and CLK = '1') then
                        clockCounter &t;= clockCounter + 1;
                        if(clockCounter = WAIT_TIME) then
                                clockCounter <= "000000000000000000000000";
                                ledCounter <= ledCounter + 1;
                        end if;
                end if;
        end process;

        LED <= not ledCounter;
end behavior;

VHDLで書いたプログラムをテストする

上で移植したプログラムが想定通り動くはずかは、FPGAに書き込む前にテストしておきたいです。 openFPGALoaderで書き込む際には、今の利用方法だとフラッシュメモリーに書き込むようなので、書き換え回数も上限があります。 図解VHDL実習でのテストベンチの書き方の説明と、GHDLのドキュメントにある実例を元に、Verilogで書く場合をVHDLに移植するイメージで書くと、以下のようでテストベンチは良さそうです。

$ cat counter_tv.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity counter_tb is
end counter_tb;

architecture behavior of counter_tb is
        component counter
                Port (CLK : in std_logic;
                      LED : out std_logic_vector(5 downto 0));
        end component;

        for counter_0: counter use entity work.counter;
        signal CLK : std_logic := '0';
        signal LED : std_logic_vector(5 downto 0) := "000000";
begin
        counter_0: counter port map (CLK => CLK, LED => LED);

        process
        begin
                CLK <= not CLK;
                wait for 1 ns;
        end process;
end behavior;

また、counter.vhdlも待ちサイクルがテストするにはあまりに長いので、以下のように3回のクロック立ち上がりで次のLED店頭パターンになるように書き変えておきます。 おそらく、こんな改造をすることなくテストする方法もあってしかるべきだと思いますが、分かっていません。

$ cat counter.vhdl
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity counter is
        Port (CLK : in std_logic;
              LED : out std_logic_vector(5 downto 0));
end counter;

architecture behavior of counter is
        --constant WAIT_TIME : integer := 13500000;
        -- 3 is only for testbench
        constant WAIT_TIME : integer := 3;
        signal ledCounter : std_logic_vector(5 downto 0) := "000000";
        signal clockCounter : std_logic_vector(23 downto 0) := "000000000000000000000000";
begin
        process(CLK)
        begin
                if(CLK'event and CLK = '1') then
                        clockCounter &t;= clockCounter + 1;
                        if(clockCounter = WAIT_TIME) then
                                clockCounter &t;= "000000000000000000000000";
                                ledCounter &t;= ledCounter + 1;
                        end if;
                end if;
        end process;

        LED &t;= not ledCounter;
end behavior;

ghdlコマンドはステップを積み上げて実行しないと、VCDウェーブフォームファイルを得られません。 以下のようなmakefileを使って、make runと実行すれば、VCDウェーブフォームファイルを正常に得られると思います。 得られたVCDウェーブフォームファイル (counter.vcd)は。pkgsrc/cad/gtkwaveで閲覧することができます。 3回のクロック立ち上がりで次のLCD点灯パターンになるので、6nsごとにLED[5:0]が変わっていることが分かると思います。

$ cat Makefile
TARG=   counter

GHDL_FLAGS+=    -fsynopsys

.PHONY: analysis elaborate run clean

analysis: ${TARG}.vhdl
        ghdl -a ${GHDL_FLAGS} ${TARG}.vhdl
        ghdl -a ${GHDL_FLAGS} ${TARG}_tb.vhdl

elaborate: work-obj93.cf
        ghdl -e ${GHDL_FLAGS} ${TARG}
        ghdl -e ${GHDL_FLAGS} ${TARG}_tb

run: elaborate
        ghdl -r ${GHDL_FLAGS} --time-resolution=ns ${TARG}_tb --vcd=counter.vcd --stop-time=1ms

clean:
        rm -f counter.vcd
        rm -f work-obj93.cf

23日目は、ここで移植したVHDLによるcounterをyosysで合成し、nextpnrで配置配線し、FPGAをプログラムして、動作を確認してみます。

NetBSDでFPAG開発を始めてみる

この記事は、NetBSD Advent Calendar 2025の21日目の記事です。

はじめに

Verilogや他のHDL (Hardware Description Language; ハードウェア記述言語)を勉強して来ましたが、シミュレーションだけでハードウェアとして実現したことはありませんでした。 XilinxやAlteraのFPGAを搭載した基板は高価ですし、LatticeやMicrochip (旧Actel)のFPGA基板で良いものがどれかも良く分かりません。 そもそも、何も分からない段階で真に良いものを選べる気もしません。

誰が言及していたのか全く記憶から消えてしまいましたが、ディジタル回路とVerilog入門というチュートリアルを読みました。 Verilogについて納得が行くまで解説がされている感じはしなくて、ちょっと悩んでしまう気がしましたが、おもしろいチュートリアルだと思います。 その中で、秋月電子通商で売られているSipeed製のTang Nano 9Kという基板が紹介されていました。 これには、中国のGowin Semiconductor製のFPGAが搭載されています。 記事中の2,500円という価格よりは値上げされて、2,710円 (消費税込みで2,980円)になっていましたが、3,000円せずに購入できるのは、最初の一歩としては手軽です。

Gowin Semiconductor社は、Gowin EDAという開発環境を提供しています。WindowsとLinuxで利用できるようです。 Apply LicenseのページからWindows用のGowin EDAのライセンスを申請すると、5分もせずにライセンスファイルが返送されて来ました。 ですが、まだインストールしていません。 近々試してみないといけません。

一方で、ウェブ検索をすると、Tang Nano 9KはYosys等のオープンソースなツールのみで扱うことも可能なようです。 と言うことで、NetBSD/amd64 11.99.4のみでTang Nano 9Kをプログラムしてみました。

必要なソフトウェアを用意する

Verilogを勉強するには、非常に小規模なロジック以外に書いたことがありませんので、Icarus Verilogを使って来ました。 ですが、これは単なるシミュレーターなので、ハードウェアに書き込むのには使えません。 FPGAで自分が書いたロジックを動かすには、シミュレーターで正常に動くはずであることを確認し、合成して、配置と配線をして、FPGAが読み込めるデータに変換し、書き込む(プログラムする)という流れが必要なはずです。

Gowin製のFPGAの場合、オープンソースなツールとしては、合成はYosysで、配置と配線はnextpnrで、データ(ビットストリームと呼ばれている)への変換はapycula、プログラムするのはopenFPGALoaderを使えば良いようです。

ちょうどpkgsrc-2025Q4のフリーズが終わったので、ちゃんとアップデートやインポートをしないといけませんが、それはこの記事を書いた後にすることにして、必要なツールは以下のようにインストールすれば良いでしょう。 pkgsrc/devel/openFPGALoaderはpkgsrc/devel/libftdi1を使いますが、libftdi1はpkgsrc/devel/swig4がインストールされていると、swig3がインストールされていてもswig4を使ってしまい、ビルドすることができません。 ですので、まずはswig4は削除しておく必要があります。 swig4は基本的には何かのFull dependencyになっていることはなさそうですから、問題にはならないはずと思います。

# pkg_delete swig4
# cd /usr/pkgsrc/devel/yosys
# make install
# cd /usr/pkgsrc/devel/nextpnr-himbaechel
# make install
# cd /usr/pkgsrc/devel/py-apycula
# make install
# cd /usr/pkgsrc/devel/openFPGALoader
# make install

一方で、自分の書いたVerilogのコードをプログラムしても、ちゃんと動かない場合に、これらのツールが動いているか否かの動作確認ができる気がしません。 github.com/lushaylabs/tangnano9k-series-examplesにある、counterというプロジェクトを利用して動作確認することにします。 ただ、現状では古いツールチェイン用の記述になっていますし、pkgsrcのやり方には沿っていないので、私がforkして修正点を反映させたものを今回は利用します。 以下のように、手元に取得しておきます。

$ git clone git@github.com:ryoon/tangnano9k-series-examples.git
$ cd tangnano9k-series-examples
$ git pull origin newer-toolchain-pkgsrc
$ git switch newer-toolchain-pkgsrc

実際に合成からプログラムまでを実行してみる

counter/Makefileに実行すべきコマンドは既に記載されています。 まずはTang Nano 9KをNetBSDマシンに接続してみます。 Tang Nano 9KにはUSB A to USB Type-Cケーブルが付属していますが、手元に出してあったいつも使っているケーブルを利用しました。 dmesgは以下のようです。 普通にugen(4)として認識されている状態です。

ugenif0 at uhub1 port 2 configuration 1 interface 0
ugenif0: SIPEED (0x0403) JTAG Debugger (0x6010), rev 2.00/5.00, addr 2
ugenif1 at uhub1 port 2 configuration 1 interface 1
ugenif1: SIPEED (0x0403) JTAG Debugger (0x6010), rev 2.00/5.00, addr 2

/dev/ugen0.*/dev/ugen1.*はrootユーザーしか読み書きできないので、私のユーザーはwheelグループに所属していますので、以下のようにパーミッションを変更しておきます。

# chmod 660 /dev/ugen0.*
# chmod 660 /dev/ugen1.*

と言うことで、まずは実行してみましょう。 以下のように実行すれば、FPGAのプログラムまで完了します。 CRC Check: Successになれば正常終了しているはずです。

$ make load
(snip)
openFPGALoader -b tangnano9k counter.fs -f
empty
write to flash
Jtag frequency : requested 6.00MHz   -> real 6.00MHz
Parse file Parse counter.fs:
Done
DONE
Jtag frequency : requested 2.50MHz   -> real 2.00MHz
Erase SRAM DONE
Erase FLASH DONE
Erasing FLASH: [==================================================] 100.00%
Done
write Flash: [==================================================] 100.00%
Done
CRC check: Success

正常終了すると、以下の写真のようにTang Nano 9K内蔵のLEDが橙色に点滅します。

Tang Nano 9Kには、LUTはLUT4ということで、4入力で1出力なのでしょうか。 また9Kということで、LUTは8640個あるそうです。 パッケージにはEntry-Level FPGA Kit For RISC-Vと書かれていますが、NetBSDが動くようなRISC-V環境が作れる訳ではない仕様な気がします。 でも、自分で書いたHDLのコードが、シミュレーションではなく実際のFPGAで動かせるというのは、学習を一歩先に進めるのには役立ちそうです。

NetBSD上でYubiKey 5 NFCの各機能を使ってみる (2)

この記事は、NetBSD Advent Calendar 2025の18日目の記事です。

はじめに

(1)では各機能を概観しました。 今回は、私がYubiKey 5 NFCで利用しようと当初から考えていたパスキーによるログインを試したいと思います。

現状を確認する

実を言いますと、既に3つのFIDO credentialsが登録済みになっています。 ですが、実際に意味があるのは1つだけです。 その状況を確認してみます。

また、FIDO機能のためのPINも設定済みです。 これはykman-3.14 fido access change-pinで設定したはずです。

$ ykman-3.14 fido info
AAGUID:                       XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
PIN:                          8 attempt(s) remaining
Minimum PIN length:           4
Always Require UV:            Off
Credential storage remaining: 97

$ ykman-3.14 fido credentials list
Enter your PIN: (あらかじめ設定したPINを入力してEnterキーを押す)
Credential ID  RP ID                Username          Display name
XXXXXXXX...    webauthn.io          ryoon
YXXXXXXX...    login.microsoft.com  ????????@hotmail.com
ZXXXXXXX...    google.com           ????????@gmail.com

この3番目のgmail.comのusernameのものだけが実際に有用なものです。 webauthn.ioのものは、pkgsrc/www/firefoxの動作確認の際に作ったものです。 login.microsoft.comのものは、設定途中で進まなくなってしまい、実際にMicrosoftアカウントには紐付いていません。

と言うことで、不要なものはまず削除してしまいましょう。

$ ykman-3.14 fido credentials delete XXXXXXXX
Enter your PIN: (あらかじめ設定したPINを入力してEnterキーを押す)
Delete webauthn.io ryoon  (XXXXXXXX(snip))? [y/N]: y (yと入力してEnterキーを押す)
Deleting credential, DO NOT REMOVE YOUR YUBIKEY!
Credential deleted.
$ ykman-3.14 fido credentials delete YXXXXXXX
Enter your PIN: (あらかじめ設定したPINを入力してEnterキーを押す)
Delete login.microsoft.com ????????@hotmail.com  (YXXXXXXX(snip))? [y/N]: y
Deleting credential, DO NOT REMOVE YOUR YUBIKEY!
Credential deleted.

これでFIDO credentialsの状況を再度確認してみましょう。

$ ykman-3.14 fido credentials list
Enter your PIN:
Credential ID  RP ID       Username          Display name
ZXXXXXXX...    google.com  ????????@gmail.com

必要なものを残して、削除できていました。

登録してみる

GoogleアカウントとMicrosoftアカウントで使うように設定してみました。 全く問題ないと言いたいところですが、そうは行きませんでした。 利用しているのは、pkgsrc/www/firefox-146.0.1です。

登録の手順は、GoogleやMicrosoftのウェブサイトを確認してもらえば良いでしょう。

結果から言うと、Googleアカウントは、既に上でも触れたように使えています。 登録もログインも想定通りにできます。 ですが、Microsoftアカウントは、パスキーの設定はでき、YubiKey 5 NFCに登録もできますが、 NetBSD/amd64 11.99.4上のFirefox 146.0.1では、ログイン時にパスキーを使ったログインはできませんでした。 選択肢にも出て来ません。 Windows 11 25H2上のFirefox 146.0.1 (64ビット版)では、パスキーでのログインが提案されます。

いずれ、証券会社等の最近パスキーに熱心なサービスでも使えるか試してみたいと思います。 ただ、現時点でNTTドコモのdアカウント等はYubiKey 5 NFCでの設定はできない所までは分かっています。

NetBSD上でYubiKey 5 NFCの各機能を使ってみる (1)

この記事は、NetBSD Advent Calendar 2025の17日目の記事です。

はじめに

以前、YubiKey 5 NFCを日本国内の代理店から購入したのですが、その直後にそのYubiKey 5 NFCのファームウェアには脆弱性が見付かってしまいました。 しかも、FIDO credentialsの登録数が十分ではなく、あまり活用できない常態でした。 今回は、YubiKeyの公式オンラインストアからファームウェアバージョン5.7.4のものを購入しました。 これは既存の脆弱性はないようですし、FIDO credentialsの登録可能数も十分のようです。

YubiKey 5 NFCはいろいろな機能があるようです。製品紹介のページの表を見ると、 以下の9つの機能があるようです。ただ、YubiKeyの提供する機能と1:1と言う訳ではないような気もします。 各機能をNetBSD/amd64-currentから使ってみたいと思います。

  1. WebAuthn
  2. FIDO (CTAP 1, 2, 2.1)
  3. Universal Second Factor (U2F)
  4. Smart Card / PIV
  5. Yubico OTP
  6. OATH - HOTP (Event)
  7. OATH - TOTP (Time)
  8. OpenPGP
  9. Secure Static Password

NetBSDでYubiKeyを管理するツールをインストールする

YubiKey Managerのページには、QTで書かれたGUIツールと、CLIのツールの2つが紹介されています。 pkgsrcにはいずれも収録されているのですが、今回はCLIツールであるykmanコマンドの方を使ってみます。 以下のようにインストールすれば良いでしょう。 今回はPython 3.14ベースで使ってみます。 ちなみに、NetBSDで正常に使えるのは、py314-yubikey-manager-5.8.0nb1以降のバージョンです。

# /usr/pkgsrc/security/py-yubikey-manager
# make install

ところが、これだけでは全てのYubiKey 5 NFCの機能を使うことができません。 py314-yubikey-manager-5.8.0nb1は、機能ごとにpcscd(8)経由かuhid(4)経由でYubiKey 5 NFCにアクセスします。 uhid(4)は追加のソフトウェアは不要 えすが、pcscd(8)経由でアクセスするには、以下のように実行する必要があります。

; ccidパッケージがpcscd(8)経由でSmart Cardとしてアクセスするのに必要です。
# /usr/pkgsrc/security/ccid
# make install
; その上で、pcscd(8)を起動させておく必要があります。
# cp /usr/pkg/share/examples/rc.d/pcscd /etc/rc.d
# echo "psccd=YES" >> /etc/rc.conf
# service pcscd start

ここまで実施すれば、YubiKey 5 NFCの機能にアクセスすることはできるように見えます。 現時点で全ての動作を確認してはいないので、必要になったら追加のセットアップを考えたいと思います。

YubiKey 5 NFCの情報を取得する

yumanコマンドは、複数のPythonインタープリターで動くものを共存できるように、alternative frameworkでPythonインタープリターのバージョン番号付きのコマンド名にするようになっています。 Python 3.14ベースの場合には、ykmanコマンドはykman-3.14という名前になっているはずです。

何のオプションも指定せずにykmanコマンドを実行すると、使えるオプションとサブコマンドを表示させることができます。 以下のように表示されるので、一通り情報を表示させてみます。

Usage: ykman-3.14 [OPTIONS] COMMAND [ARGS]...

  Configure your YubiKey via the command line.

  Examples:

    List connected YubiKeys, only output serial number:
    $ ykman list --serials

    Show information about YubiKey with serial number 123456:
    $ ykman --device 123456 info

Options:
  -d, --device SERIAL             specify which YubiKey to interact with by
                                  serial number
  -r, --reader NAME               specify a YubiKey by smart card reader name
                                  (can't be used with --device or list)
  -t, --scp-ca FILENAME           specify the CA to use to verify the SCP11
                                  card key (CA-KLCC)
  -s, --scp CRED                  specify private key and certificate chain
                                  for secure messaging, can be used multiple
                                  times to provide key and certificates in
                                  multiple files (private key, certificates in
                                  leaf-last order), OR SCP03 keys in hex
                                  separated by colon (:) K-ENC:K-MAC[:K-DEK]
  -p, --scp-password PASSWORD     specify a password required to access the
                                  --scp file, if needed
  -l, --log-level [error|warning|info|debug|traffic]
                                  enable logging at given verbosity level
  --log-file FILE                 write log to FILE instead of printing to
                                  stderr (requires --log-level)
  --diagnose                      show diagnostics information useful for
                                  troubleshooting
  -v, --version                   show version information about the app
  --full-help                     show --help output, including hidden
                                  commands
  -h, --help                      show this message and exit

Commands:
  info     show general information
  list     list connected YubiKeys
  script   run a python script
  config   configure the YubiKey, enable or disable applications
  fido     manage the FIDO applications
  hsmauth  manage the YubiHSM Auth application
  oath     manage the OATH application
  openpgp  manage the OpenPGP application
  otp      manage the YubiOTP application
  piv      manage the PIV application

これによると、infoで全体の情報を表示させ、fido、hsmauth、oath、openpgp、otp、pivという6つのサブコマンドで機能を管理できるようです。

$ ykman-3.14 info
Device type: YubiKey 5 NFC
Serial number: XXXXXXXX
Firmware version: 5.7.4
Form factor: Keychain (USB-A)
Enabled USB interfaces: OTP, FIDO, CCID
NFC transport is enabled

Applications    USB     NFC
Yubico OTP      Enabled Enabled
FIDO U2F        Enabled Enabled
FIDO2           Enabled Enabled
OATH            Enabled Enabled
PIV             Enabled Enabled
OpenPGP         Enabled Enabled
YubiHSM Auth    Enabled Enabled

ここで見ると、FIDO U2FとFIDO2は別々の機能になっているようです。 できることも違ったはずなので、当然かもしれません。

6つのサブコマンドでinfoコマンドを実行した結果を掲載したいと思います。

$ ykman-3.14 fido info
AAGUID:                       XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
PIN:                          8 attempt(s) remaining
Minimum PIN length:           4
Always Require UV:            Off
Credential storage remaining: 97
$ ykman-3.14 hsmauth info
YubiHSM Auth version:             5.7.4
Management key retries remaining: 8/8
$ ykman-3.14 oath info
OATH version:        5.7.4
Password protection: disabled
$ ykman-3.14 openpgp info
OpenPGP version:            3.4
Application version:        5.7.4
PIN tries remaining:        3
Reset code tries remaining: 0
Admin PIN tries remaining:  3
Require PIN for signature:  Once
KDF enabled:                False
$ ykman-3.14 otp info
Slot 1: programmed
Slot 2: empty
$ ykman-3.14 piv info
PIV version:              5.7.4
PIN tries remaining:      3/3
PUK tries remaining:      3/3
Management key algorithm: AES192
WARNING: Using default PIN!
WARNING: Using default PUK!
WARNING: Using default Management key!
CHUID: No data available
CCC:   No data available

このYubiKey 5 NFCは、実は既にFirefox経由でPINの設定と3つのFIDO credentialsを登録済みになっています。 今日はここまでにして、次回はfidoサブコマンドで管理できる機能を使ってみたいと思います。

NetBSD上のmplayerで、audioデバイスとvideoデバイスを指定して表示させる

この記事は、NetBSD Advent Calendar 2025の14日目の記事です。

はじめに

mplayerで出力するオーディオデバイスを指定する方法は、pkgsrc/multimedia/mplayerをpulseaudioオプションを有効にして使っている私には有用です。 OSSしか使っていない場合にも、HDMIコネクターの内蔵されたマシンを利用している場合には、内蔵スピーカーとHDMI出力を切り替える必要があると思いますので、 有用かもしれません。

一方で、webcamの画像を画面に表示させるというのは、Video4Linux2サポートの確認をする以外には有用ではないかもしれません。 ですが、それ故に、デバイスを選択し、いろいろなモードを指定できることは重要かもしれません。

オーディオデバイスの初期設定

mplayerの設定の前に、私のマシンに特有かもしれませんが、初期設定をしておきます。 私のマシンは、以下のようにオーディオデバイスが認識されています。

hdaudio0 at pci3 dev 0 function 1: HD Audio Controller
hdaudio0: interrupting at msi1 vec 0
hdaudio0: HDA ver. 1.0, OSS 4, ISS 0, BSS 0, SDO 1, 64-bit
hdafg0 at hdaudio0: ATI R6xx HDMI
hdafg0: HDMI00 2ch: Digital Out [Jack]
hdafg0: HDMI01 2ch: Digital Out [Jack]
hdafg0: 2ch/0ch 32000Hz 44100Hz 48000Hz PCM16 AC3
audio0 at hdafg0: playback
audio0: slinear_le:16 2ch 48000Hz, blk 1920 bytes (10ms) for playback
spkr1 at audio0: PC Speaker (synthesized)
wsbell at spkr1 not configured
(snip)
hdaudio1 at pci3 dev 0 function 6: HD Audio Controller
hdaudio1: interrupting at msi4 vec 0
hdaudio1: HDA ver. 1.0, OSS 4, ISS 4, BSS 0, SDO 1, 64-bit
hdafg1 at hdaudio1: Realtek product 0245
hdafg1: DAC00 2ch: Speaker [Built-In]
hdafg1: DAC01 2ch: HP Out [Jack]
hdafg1: ADC02 2ch: Mic In [Jack]
hdafg1: 2ch/2ch 44100Hz 48000Hz 96000Hz 192000Hz PCM16 PCM20 PCM24
audio1 at hdafg1: playback, capture, full duplex, independent
audio1: slinear_le:16 2ch 48000Hz, blk 1920 bytes (10ms) for playback
audio1: slinear_le:16 2ch 48000Hz, blk 1920 bytes (10ms) for recording
spkr2 at audio1: PC Speaker (synthesized)
wsbell at spkr2 not configured

一方で、/dev以下は、このようになっています。 audio0はHDMI出力なので、このままでは内蔵スピーカーからは音を出すのが面倒です。 しかも、DRMKMSのサポートがないマシンなので、そもそもHDMI経由でオーディオ出力はできません。

$ ls -l /dev/audio* /dev/sound*
lrwxr-xr-x  1 root  wheel       11 May 11  2025 /dev/audio -> /dev/audio0
crw-rw-rw-  1 root  wheel  42, 128 May 10  2025 /dev/audio0
crw-rw-rw-  1 root  wheel  42, 129 May 10  2025 /dev/audio1
crw-rw-rw-  1 root  wheel  42, 130 May 10  2025 /dev/audio2
crw-rw-rw-  1 root  wheel  42, 131 May 10  2025 /dev/audio3
lrwxr-xr-x  1 root  wheel       14 May 11  2025 /dev/audioctl -> /dev/audioctl0
crw-rw-rw-  1 root  wheel  42, 192 May 10  2025 /dev/audioctl0
crw-rw-rw-  1 root  wheel  42, 193 May 10  2025 /dev/audioctl1
crw-rw-rw-  1 root  wheel  42, 194 May 10  2025 /dev/audioctl2
crw-rw-rw-  1 root  wheel  42, 195 May 10  2025 /dev/audioctl3
lrwxr-xr-x  1 root  wheel       11 May 11  2025 /dev/sound -> /dev/sound0
crw-rw-rw-  1 root  wheel  42,   0 May 10  2025 /dev/sound0
crw-rw-rw-  1 root  wheel  42,   1 May 10  2025 /dev/sound1
crw-rw-rw-  1 root  wheel  42,   2 May 10  2025 /dev/sound2
crw-rw-rw-  1 root  wheel  42,   3 May 10  2025 /dev/sound3

cd /dev ; ln -sf audio1 audio ; ln -sf audioctl1 audioctl ; ln -sf sound1 soundと 実行しても良いのですが、audiocfg default 1とすれば、同じことをしてくれます。 結果は、以下のようになりました。 これで/dev/audioを使って、内蔵スピーカーから音を出力できることができるようにします。

$ ls -l /dev/audio* /dev/sound*
lrwxr-xr-x  1 root  wheel       11 May 11  2025 /dev/audio -> /dev/audio1
crw-rw-rw-  1 root  wheel  42, 128 May 10  2025 /dev/audio0
crw-rw-rw-  1 root  wheel  42, 129 May 10  2025 /dev/audio1
crw-rw-rw-  1 root  wheel  42, 130 May 10  2025 /dev/audio2
crw-rw-rw-  1 root  wheel  42, 131 May 10  2025 /dev/audio3
lrwxr-xr-x  1 root  wheel       14 May 11  2025 /dev/audioctl -> /dev/audioctl1
crw-rw-rw-  1 root  wheel  42, 192 May 10  2025 /dev/audioctl0
crw-rw-rw-  1 root  wheel  42, 193 May 10  2025 /dev/audioctl1
crw-rw-rw-  1 root  wheel  42, 194 May 10  2025 /dev/audioctl2
crw-rw-rw-  1 root  wheel  42, 195 May 10  2025 /dev/audioctl3
lrwxr-xr-x  1 root  wheel       11 May 11  2025 /dev/sound -> /dev/sound1
crw-rw-rw-  1 root  wheel  42,   0 May 10  2025 /dev/sound0
crw-rw-rw-  1 root  wheel  42,   1 May 10  2025 /dev/sound1
crw-rw-rw-  1 root  wheel  42,   2 May 10  2025 /dev/sound2
crw-rw-rw-  1 root  wheel  42,   3 May 10  2025 /dev/sound3

オーディオデバイスを指定する

私はpkgsrc/multimedia/mplayerで、ossとSDL-1.2、PulseAudio経由でオーディオを再生できるようにしているので、それぞれを試してみます。 他にも設定できるパラメーターはあると思いますが…。

; OSSでデフォルトのオーディオデバイスで再生する場合
$ mplayer -ao oss ./test.mp4
; OSSで/dev/audio1で再生する場合
$ mplayer -ao oss:/dev/audio1 ./test.mp4
; PulseAudio経由で再生する場合
$ mplayer -ao pulse ./test.mp4
; SDL-1.2経由でPulseAudioで再生する場合
SDL_AUDIODRIVER=pulse mplayer -ao sdl ./test.mp4
; SDL-1.2経由でOSSの/dev/audioで再生する場合
SDL_AUDIODRIVER=dsp mplayer -ao sdl ./test.mp4
; SDL-1.2経由でOSSの/dev/audioで再生する場合
SDL_AUDIODRIVER=dsp mplayer -ao sdl ./test.mp4
; SDL-1.2経由でOSSの/dev/audio1で再生する場合
$ SDL_AUDIODRIVER=dsp AUDIODEV=/dev/audio1 mplayer -ao sdl ./test.mp4

webcamを開く

webcamにもいろいろ設定はあるはずですが、以下のようにすれば、Video4Linux2サポートの確認には良さそうです。

$ mplayer tv:// -tv driver=v4l2:device=/dev/video1:width=1280:height=720:outfmt=yv12 -fps 15

おわりに

とりあえず、これでwebcamやサウンドカードの動作確認をするのには十分そうです。 でも、今日時点のNetBSD/amd64-currentだと、オーディオ出力と入力の組み合わがおかしいような…。 ちゃんと調べた方が良いかもしれません。 勘違いだと良いのですが…。

NetBSDでCDをリッピングしてみる(一応成功)

この記事は、NetBSD Advent Calendar 2025の12日目の記事です。

はじめに

furandon pigさんの10日目の記事でAudio CDのリッピングに失敗されていました。 ですが、私は過去にリッピングしていたはずです。

私の環境は、NetBSD/amd64 11.99.4にUSB 3接続のBD-Rドライブであるパイオニア製のBDR-XD08SVです。 ちなみに、いつの間にかパイオニアは光学ディスクドライブからは撤退しているように見えます。残念です。

pkgsrc/sysutils/cdrtoolsのcdda2wavコマンドを試してみる

cdrtoolsには、cdda2wavというコマンドが含まれています。 これであればリッピングできるはずです。 今回の光学ディスクドライブはcd0として認識されています。 ですが、一般ユーザーで実行したいので、まずは/dev/rcd0*のパーミッションを設定しておきます。 私が使っているユーザーはoperatorグループに所属していますので、以下のように設定すれば良いでしょう。

# chmod 660 /dev/rcd0*

その上でcdrtoolsをインストールしましょう。以下のように実行すれば良いでしょう。

# cd /usr/pkgsrc/sysutils/cdrtools
# make install

cdda2wav(1)のmap pageにあるように以下のように実行すれば良いでしょう。

$ cdda2wav -vall cddb=0 speed=4 -paranoia paraopts=proof -B
cdda2wav: Insufficient 'file read' privileges. You will not be able to open all needed devices.
cdda2wav: Insufficient 'file write' privileges. You will not be able to open all needed devices.
cdda2wav: Insufficient 'device' privileges. You may not be able to send all needed SCSI commands, this my cause various unexplainable problems.
cdda2wav: Insufficient 'priocntl' privileges. You may get jitter.
cdda2wav: Insufficient 'network' privileges. You will not be able to do remote SCSI.
No target specified, trying to find one...
Using dev=31,0,0.
Type: ROM, Vendor 'PIONEER ' Model 'BD-RW   BDR-XD08' Revision '1.02' MMC+CDDA
274432 bytes buffer memory requested, transfer size 61440 bytes, 4 buffers, 23 sectors
#Cdda2wav version 2024/03/18 3.02_netbsd_11.99.4_amd64_x86_64, real time sched., soundcard, libparanoia support
cdda2wav: Bad status from freedb server during hello: 500 Command syntax error.
.
cdda2wav: Bad status from freedb server during quit: 500 Unrecognized command, closing connection.
.
AUDIOtrack pre-emphasis  copy-permitted tracktype channels
      1-11           no              no     audio    2
Table of Contents: total tracks:11, (total time 48:35.00)
  1.( 2:00.42),  2.( 3:58.73),  3.( 4:58.40),  4.( 4:28.20),  5.( 4:12.57),
  6.( 4:39.13),  7.( 4:55.05),  8.( 4:43.70),  9.( 4:47.17), 10.( 3:56.45),
 11.( 5:53.68)

Table of Contents: starting sectors
  1.(       0),  2.(    9042),  3.(   26965),  4.(   49355),  5.(   69475),
  6.(   88432),  7.(  109370),  8.(  131500),  9.(  152795), 10.(  174337),
 11.(  192082), lead-out(  218625)
CDINDEX discid: xxh_sS61wqdXnYPsHKLlN3v5y1c-
CDDB discid: 0x830b630b
CD-Text: not detected
CD-Extra: not detected
Media catalog number: 4988022106978
T:  2 ISRC: JP-TA0-92-20030
T:  3 ISRC: JP-TA0-92-20040
T:  4 ISRC: JP-TA0-92-20050
T:  5 ISRC: JP-TA0-92-20060
T:  6 ISRC: JP-TA0-92-20070
T:  7 ISRC: JP-TA0-92-20080
T:  8 ISRC: JP-TA0-92-20090
T:  9 ISRC: JP-TA0-92-20100
T: 10 ISRC: JP-TA0-92-20110
T: 11 ISRC: JP-TA0-92-20120

index scan: 11...
samplefile size will be 514206044 bytes.
recording 2915.0000 seconds stereo with 16 bits @ 44100.0 Hz ->'audio'...
using c2check to verify reads.
using lib paranoia for reading.
cdda2wav: Operation not permitted. Cannot set posix realtime scheduling policy.
percent_done:
100%  track  1 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  896 reads(212.3%) 24 overlap(20 .. 20)
100%  track  2 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  1736 reads(207.6%) 62 overlap(20 .. 20)
100%  track  3 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  2184 reads(209.0%) 78 overlap(20 .. 20)
100%  track  4 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  1960 reads(208.7%) 70 overlap(20 .. 20)
100%  track  5 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  1792 reads(202.6%) 64 overlap(20 .. 20)
100%  track  6 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  2072 reads(212.1%) 74 overlap(20 .. 20)
100%  track  7 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  2128 reads(206.1%) 76 overlap(20 .. 20)
100%  track  8 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  2072 reads(208.5%) 74 overlap(20 .. 20)
100%  track  9 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  2072 reads(206.1%) 74 overlap(20 .. 20)
100%  track 10 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  1736 reads(209.6%) 62 overlap(20 .. 20)
100%  track 11 recorded successfully
100%  0 rderr, 0 skip, 0 atom, 0 edge, 0 drop, 0 dup, 0 drift, 0 0 c2
100%  2530 reads(204.2%) 92 overlap(20 .. 20)
$ ls
audio_01.inf audio_03.inf audio_05.inf audio_07.inf audio_09.inf audio_11.inf
audio_01.wav audio_03.wav audio_05.wav audio_07.wav audio_09.wav audio_11.wav
audio_02.inf audio_04.inf audio_06.inf audio_08.inf audio_10.inf
audio_02.wav audio_04.wav audio_06.wav audio_08.wav audio_10.wav

audio_01.wavからaudio_11.wavが生成できました。

できたwavファイルをmp3ファイルに変換する

MP3の特許は、少なくとも日本では既の期限を迎えているようです。 ですので、MP3にエンコードしても特許侵害にはならないでしょう。 と言うことで、pkgsrc/audio/lameを使うことにします。 以下のようにインストールすれば良いでしょう。

# cd /usr/pkgsrc/audio/lame

おそらくとても贅沢な気がするのですが、最近のamd64なマシンにとってMP3エンコードなど負荷にもならないと思います。 以下のようにMP3ファイルを生成しました。

$ for f in *.wav
do lame -q0 -b256 $f `basename $f .wav`.mp3
done
LAME 3.100 64bits (http://lame.sf.net)
Using polyphase lowpass filter, transition band: 19383 Hz - 19916 Hz
Encoding audio_01.wav to audio_01.mp3
Encoding as 44.1 kHz j-stereo MPEG-1 Layer III (5.5x) 256 kbps qval=0
    Frame          |  CPU time/estim | REAL time/estim | play/CPU |    ETA
  4617/4617  (100%)|    0:13/    0:13|    0:13/    0:13|   8.6830x|    0:00
-------------------------------------------------------------------------------
   kbps        LR    MS  %     long switch short %
  256.0       99.0   1.0        88.6   5.2   6.2
Writing LAME Tag...done
ReplayGain: +4.8dB
LAME 3.100 64bits (http://lame.sf.net)
Using polyphase lowpass filter, transition band: 19383 Hz - 19916 Hz
Encoding audio_02.wav to audio_02.mp3
(snip)

mp3ファイルのタグを設定し、ファイル名を変更する

pkgsrc/audio/easytagを使うと、MP3にIDv2のタグを設定すると同時に、ファイル名も「アーティスト名-曲名」等に変更することができます。 pkgsrc/audio/easytagはヘルプを表示するためにpkgsrc/misc/yelp3を利用するため、間接的にpkgsrc/www/webkit-gtkを利用することになります。 自分でpkgsrc/audio/easytagをビルドしてインストールするのであれば、以下のようにpkgsrc/www/webkit-gtkは使わないようにインストールした方が良いかもしれません。

# cd /usr/pkgsrc/audio/easytag
# make PKG_OPTIONS.easytag=-doc install

おわりに

でも良く考えるとpkgsrc/audio/abcdeは、私が以前試した時に動いていたはずです (abcde -d /dev/rcd0)。 もしかしたらNetBSDでは、/dev/cd0ではなく/dev/rcd0を使わないといけないというだけかもしれません。 他のGUI付きのソフトウェアの方が便利かもしれません。 いずれ試してみたいと思います。

USBシリアルアダプターをUEFIブートの場合にconsoleとして使う機能を試してみる

この記事は、NetBSD Advent Calendar 2025の7日目の記事です。

はじめに

2025年10月9日のmanu@のコミットと関連するコミットで、UEFIブートの際にuftdi(4)として認識されるUSBシリアルコンバーターをconsoleとして利用できるようになっていました。 ちょうど手元にFTDI製のチップを使ったUSBシリアルコンバーターを持っていましたので、試してみました。

本来は、DDBとかも使えるらしいのですが、今回はシリアルコンソール経由でログインするのみです。

初期設定

NetBSD/amd64 l11.99.4をインストールしたラップトップに、Windows 11 25H2上のPuTTYからログインしてみます。 単にログインするだけであれば、NetBSD側で以下のように設定すれば良いでしょう。

# vi /etc/ttys
(snip)
ttyU0   "/usr/libexec/getty std.115200" vt100 on secure

こうすれば、115200 bpsで通信するPuTTYでloginプロンプトを得ることができます。 具体的には下図のように設定します。

この設定は、以下の設定をしても必要ですので、この時点で設定して置くのが良いでしょう。

ucom0 at uftdi0をconsoleに設定する

以下のように認識されるデバイスをNetBSD側に接続してあります。

uftdi0 at uhub9 port 1 configuration 1 interface 0
uftdi0: FTDI (0x0403) USB HS SERIAL CONVERTER (0x6001), rev 1.10/4.00, addr 4
ucom0 at uftdi0 portno 0

その上で再起動させ、UEFIブートローダーで以下のように設定して、起動させます。

> kconsdev ucom0,115200
> boot

Windows 11上のPuTTYでは、ucom0が認識される所からブートメッセージが表示されます。

[   3.7030449] ucom0: console
[   4.1930474] uhidev1 at uhub8 port 2 configuration 1 interface 0
[   4.1930474] uhidev1: ActionStar (0x2101) product 0201 (0x0201), rev 1.10/1.00, addr 5, iclass 3/0
[   4.2030475] uhid7 at uhidev1: input=2, output=2, feature=0
[   4.7030507] uhidev2 at uhub8 port 3 configuration 1 interface 0
[   4.7030507] uhidev2: Logitech (0x046d) USB Receiver (0xc53f), rev 2.00/55.02, addr 6, iclass 3/1
[   4.7130541] ukbd0 at uhidev2
[   4.7130541] wskbd1 at ukbd0 mux 1
[   4.7330511] uhidev3 at uhub8 port 3 configuration 1 interface 1
[   4.7330511] uhidev3: Logitech (0x046d) USB Receiver (0xc53f), rev 2.00/55.02, addr 6, iclass 3/1
[   4.7430515] uhidev3: 8 report ids
[   4.7430515] ums0 at uhidev3 reportid 2: 16 buttons, W and Z dirs
[   4.7430515] wsmouse2 at ums0 mux 0
[   4.7430515] uhid8 at uhidev3 reportid 3: input=4, output=0, feature=0
[   4.7430515] uhid9 at uhidev3 reportid 4: input=1, output=0, feature=0
[   4.7430515] uhid10 at uhidev3 reportid 8: input=1, output=0, feature=0
[   4.7430515] uhidev4 at uhub8 port 3 configuration 1 interface 2
[   4.7430515] uhidev4: Logitech (0x046d) USB Receiver (0xc53f), rev 2.00/55.02, addr 6, iclass 3/0
[   9.7530773] uhidev4: no report descriptor
[   9.7530773] swwdog0: software watchdog initialized
[   9.7530773] usb8 at vhci0: USB revision 2.0
[   9.7730770] uhub10 at usb8: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.7730770] usb9 at vhci1: USB revision 2.0
[   9.7930776] uhub11 at usb9: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.7930776] usb10 at vhci2: USB revision 2.0
[   9.8130773] uhub12 at usb10: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.8130773] usb11 at vhci3: USB revision 2.0
[   9.8330781] uhub13 at usb11: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.8330781] usb12 at vhci4: USB revision 2.0
[   9.8530774] uhub14 at usb12: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.8530774] usb13 at vhci5: USB revision 2.0
[   9.8730810] uhub15 at usb13: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.8730810] usb14 at vhci6: USB revision 2.0
[   9.8930773] uhub16 at usb14: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.8930773] usb15 at vhci7: USB revision 2.0
[   9.9130776] uhub17 at usb15: NetBSD (0x0000) VHCI root hub (0x0000), class 9/0, rev 2.00/1.00, addr 1
[   9.9130776] WARNING: 4 errors while detecting hardware; check system log.
[   9.9130776] boot device: ld0
[   9.9130776] root on dk1 dumps on dk2
[   9.9130776] root file system type: ffs
[   9.9130776] kern.module.path=/netbsd/modulesystem type: ffTue Dec  9 10:46:12 -00 2025
9130776] root file system type: ffTue Dec  9 10:46:12 -00 2025
Starting root file system check:
/dev/rdk1: file system is clean; not checking
Starting devpubd.
Setting sysctl variables:
ddb.onpanic: 1 -> 0
swapctl: setting dump device to /dev/dk2
swapctl: adding /dev/dk2 as swap device at priority 0
Starting file system checks:
/dev/rdk4: file system is clean; not checking
/dev/rdk3: file system is clean; not checking
Loaded entropy from /var/db/entropy-file.
Waiting for entropy...done
Setting tty flags.
Starting network.
Hostname: castella
IPv6 mode: host
Configuring network interfaces:.
Adding interface aliases:.
Waiting for duplicate address detection to finish...
Building databases: dev, utmp, utmpx.
Keyboard encoding -> us.swapctrlcaps
Starting syslogd.
Mounting all file systems...
Clearing temporary files.
Checking quotas: done.
swapctl: setting dump device to /dev/dk2
Starting virecover.
Checking for core dump...
savecore: no core dump
Starting local daemons:.
Updating motd.
Starting pcscd.
/etc/rc.d/pcscd exited with code 1
Starting powerd.
Starting sshd.
Starting postfix.
postfix/postlog: starting the Postfix mail system
Starting multiskkserv.
Setting mixerctl variables...
mixerctl: field record.mic2 does not exist
Starting inetd.
Starting dbus.
Starting cupsd.
Starting cron.
The following components reported failures:
    /etc/rc.d/pcscd
See /var/run/rc.log for) (ttyU0)

login: 

代わりに、NetBSDのディスプレイにはloginプロンプトのみが表示されます。

pcscdの起動が失敗するのは別の問題なようです。

NetBSD上のpkgsrc/www/firefoxでwebcamを使えない問題を解析した

この記事は、NetBSD Advent Calendar 2025の5日目の記事です。

はじめに

いまいちどの時点からか分からなくなってしまったのですが、pkgsrc/www/firefoxでは、私の今利用しているHP Envy 14 fa0000にインストールしたNetBSD/amd64-currentではwebcamを利用できなくなってしまっていました。 もしかしたら、PCを変えたタイミングだったかもしれませんが、全く分かりません。

いずれにしても、WebRTCでwebcamの映像を使えないのは残念です。 しばらく放置してしまっていましたが、直すことにしました。

認識されないのを確認できるウェブサイトを特定する

HP Envy 14にインストールされたNetBSD/amd64-currentでは、以下のようにvideo0からvideo3の4つのデバイスが認識されます。

uvideo0 at uhub3 port 1 configuration 1 interface 0: SunplusIT Inc (0x04f2) HP 5MP Camera (0xb7fe), rev 2.01/0.06, addr 1
video0 at uvideo0: SunplusIT Inc (0x04f2) HP 5MP Camera (0xb7fe), rev 2.01/0.06, addr 1
video1 at uvideo0: SunplusIT Inc (0x04f2) HP 5MP Camera (0xb7fe), rev 2.01/0.06, addr 1
uvideo1 at uhub3 port 1 configuration 1 interface 2: SunplusIT Inc (0x04f2) HP 5MP Camera (0xb7fe), rev 2.01/0.06, addr 1
video2 at uvideo1: SunplusIT Inc (0x04f2) HP 5MP Camera (0xb7fe), rev 2.01/0.06, addr 1
video3 at uvideo1: SunplusIT Inc (0x04f2) HP 5MP Camera (0xb7fe), rev 2.01/0.06, addr 1

ただし、pkgsrc/multimedia/mplayerで確認してみると、video1とvideo3だけが使えます。 ちなみに、確認した方法は以下のようです。

$ mplayer tv:// -tv device=/dev/video1
$ mplayer tv:// -tv device=/dev/video3

mplayerでのvideo(4)デバイスの動作確認の方法は、別に書いていた方が良いかもしれません。

これまでは、getUserMedia / getDisplayMedia Test Pageを使って来たのですが、 これはそもそも複数のwebcamを選択することはできないので、適切ではありません。 そこで、WebRTC samplesSelect sources & outputsを利用することにします。 ここでは、webcamを選択することができます。 ちなみに、現時点ではwebcamを選択するまでもなくエラーになってしまいます。

エラーにつながる箇所を特定する

広大なFirefoxのソースコードベースの中で、getUserMediaでそもそもエラーになってしまう箇所の当たりを付けるのは大変なはずですが、 third_party/libwebrtc以下にしかwebcamを認識するロジックはないはずなのと、 おそらくVideo4Linix2のioctlでエラーになっていて、それによりwebcamを選択するまでもなくエラーになっているはずということは当たりがついています。 なので、third_party/libwebrtc以下にあるmoz.buildを読んで、NetBSDである場合にbuildされるsource codeのうち、ioctlでVIDIOC_*を第2引数に与えている箇所を探せば良さそうと思いました。

と言うことは、grep -r "ioctl.*VIDIOC_" ${WRKSRC}/third_party/libwebrtcを実行した上で、moz.buildと照合すれば良さそうです。

$ ugrep -r "ioctl.*VIDIOC_" third_party/libwebrtc
third_party/libwebrtc/modules/video_capture/linux/device_info_v4l2.cc: (snip)
(snip)
third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc: (snip)
(snip)

moz.buildを見るまでもなく、NtBSDでも使っているファイルです。 と言うことで、各ioctlの結果が分かるようにprintfを入れてbuildしてみました。

解析の結果と修正案

結果として、以下の188行目のioctlが返すfmtに返って来ている内容が想定外であり、そのために全てのwebcamが認識されなっくなっていました。

$ cat -n third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc
(snip)
   181    // Enumerate image formats.
   182    struct v4l2_fmtdesc fmt;
   183    int fmtsIdx = nFormats;
   184    memset(&fmt, 0, sizeof(fmt));
   185    fmt.index = 0;
   186    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   187    RTC_LOG(LS_INFO) << "Video Capture enumerats supported image formats:";
   188    while (ioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt) == 0) {
   189      RTC_LOG(LS_INFO) << "  { pixelformat = " << GetFourccName(fmt.pixelformat)
   190                       << ", description = '" << fmt.description << "' }";
   191      // Match the preferred order.
   192      for (int i = 0; i < nFormats; i++) {
   193        if (fmt.pixelformat == fmts[i] && i < fmtsIdx)
   194          fmtsIdx = i;
   195      }
   196      // Keep enumerating.
   197      fmt.index++;
   198    }
(snip)

更に良く見ていくと、以下のように最初はioctl(fd, VIDIOC_QUERYCAP, &cap)でのみwebcamが利用可能か認識して使えるとなっているのに、この箇所でエラーになってしまうという流れになっており、これが想定外なようです。

$ cat -n third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc
    86    /* detect /dev/video [0-63] entries */
    87    int n;
    88    for (n = 0; n < 64; n++) {
    89      snprintf(device, sizeof(device), "/dev/video%d", n);
    90      if ((fd = open(device, O_RDONLY)) != -1) {
    91        // query device capabilities
    92        struct v4l2_capability cap;
    93        if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
    94          if (cap.bus_info[0] != 0) {
    95            if (strncmp((const char*)cap.bus_info,
    96                        (const char*)deviceUniqueIdUTF8,
    97                        strlen((const char*)deviceUniqueIdUTF8)) ==
    98                0) {  // match with device id
    99              close(fd);
   100              found = true;
   101              break;  // fd matches with device unique id supplied
   102            }
   103          }
   104        }
   105        close(fd);  // close since this is not the matching device
   106      }
   107    }
   108    if (!found) {
(snip)

と言うことで、最初からioctl(_deviceFd, VIDIOC_ENUM_FMT, &fmt)もチェックしておけば良さそうです。

--- third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc.orig	2025-12-05 17:43:20.000000000 +0000
+++ third_party/libwebrtc/modules/video_capture/linux/video_capture_v4l2.cc
@@ -90,18 +90,28 @@ int32_t VideoCaptureModuleV4L2::Init(con
     if ((fd = open(device, O_RDONLY)) != -1) {
       // query device capabilities
       struct v4l2_capability cap;
+#if defined(VIDIOC_QUERYCAP)
       if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == 0) {
         if (cap.bus_info[0] != 0) {
           if (strncmp((const char*)cap.bus_info,
                       (const char*)deviceUniqueIdUTF8,
                       strlen((const char*)deviceUniqueIdUTF8)) ==
               0) {  // match with device id
-            close(fd);
-            found = true;
-            break;  // fd matches with device unique id supplied
+            struct v4l2_fmtdesc fmt;
+            memset(&fmt, 0, sizeof(fmt));
+            fmt.index = 0;
+            fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+            if (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == 0) {
+              if (fmt.pixelformat != 0) {
+                close(fd);
+                found = true;
+                break;  // fd matches with device unique id supplied
+              }
+            }
           }
         }
       }
+#endif
       close(fd);  // close since this is not the matching device
     }
   }

とりあえず、pkgsrc/www/firefoxを146.0にアップデートする際には含めておきたいと思います。 ですが、そもそもこのwebcamのvideo0とvideo2をサポートした方が良いかもしれません。

NetBSD/amd64上でFree Pascalでプログラムを書いてみる

この記事は、NetBSD Advent Calendar 2025の1日目の記事です。

Free Pascalとは?

Free Pascalとは、オープンソースのPascalとObject Pascalコンパイラーで、 オリジナルのPascal言語だけでなく、Turbo PascalのPascal言語やDelphiのObject Pascal言語もサポートしているようです。

私はPascalの熱心なユーザーであったことはないのですが、書籍の中では疑似コードとしてPascal風のソースコードを目にしたことは数多くあります。

Free Pascalとしては、NetBSDをサポートしていたようなのですが、pkgsrcには取り込まれていなかったようです。 Free Pascalコンパイラーを使っている人にとっては当たり前なのかもしれませんが、NetBSD上でコンパイルする場合に 必要だった事項を記載しておきたいと思います。

Free Pascalコンパイラーをインストールする

NetBSD/amd64でFree Pascalコンパイラーのバージョン3.2.2を利用することができます。 pkgsrc/lang/fpcとしてインポートされており、 Free Pascalのコンパイラーは、/usr/pkg/bin/fpcとしてインストールされます。 以下のように実行してインストールすれば良いでしょう。

# cd /usr/pkgsrc/lang/fpc
# make install

また、以下のように実行すると、fpcのバージョンを確認することができます。

$ fpc
Free Pascal Compiler version 3.2.2 [2025/12/08] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
fpc [options] <inputfile> [options]
 Only options valid for the default or selected platform are listed.
(以下略)

初期設定

まず、Free Pascalでプログラミングするために使うディレクトリーを作り、そのディレクトリーへ移動します。

$ mkdir free-pascal
$ cd free-pascal

次に、以下のように実行して、fpc.cfgという設定ファイルを作成します。 これがfpcコマンドの実行時に参照されるファイルで、この中で標準的な設定が定義されています。 個々をコマンドライン引数でfpcへ渡しても良いのかもしれませんが、 繰り返し入力するには、相当長いコマンドライン引数になりそうです。

$ fpcmkcfg -d basepath=/usr/pkg/lib/fpc/3.2.2 > fpc.cfg

サンプルプログラムをコンパイルしてみる

Hello worldは、以下のように書けば良いようです。 hello.pasとして保存しておきます。

program Hello;
begin
  writeln ('Hello, world.');
end.

以下のようにコンパイルし、実行します。

$ fpc hello.pas
$ ls hello*
hello hello.o hello.pas
# ./hello
Hello, world.

もう少し複雑なものとして、階乗を再帰で実装してみます。 factorial.pasとして保存しておきます。

program factorial;

function fact(n: integer): longint;
begin
    if (n = 0) then
        fact := 1
    else
        fact := n * fact(n - 1);
end;

var
    n: integer;

begin
    for n := 0 to 16 do
        writeln(n, '! = ', fact(n));
end.

以下のようにコンパイルし、実行します。

$ fpc factorial.pas
Free Pascal Compiler version 3.2.2 [2025/12/08] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: NetBSD for x86-64
Compiling factorial.pas
Linking factorial
17 lines compiled, 0.1 sec
$ ls factorial*
factorial       factorial.o     factorial.pas
$ ./factorial
0! = 1
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
11! = 39916800
12! = 479001600
13! = 1932053504
14! = 1278945280
15! = 2004310016
16! = 2004189184

おわりに

Pascalコンパイラーとしては、pkgsrcにはGNU Pascal Compiler (GPC)もpkgsrc/lang/gpcとして収録されているようです。 こちらもいずれ使ってみたいと思います。

NetBSD-currentのwscons(4)で256色表示を試してみる

この記事は、NetBSD Advent Calendar 2025の2日目の記事です。

2025年11月2日のniaのコミットで、 wscons(4)でのVT100エミュレーションで、256色表示がサポートされました。 NetBSD/amd64-currentで実際に確認してみました。

今回は、VMware Workstation Pro 25H2 for Microsoft WindowsでNetBSD/amd64-currentを動かしてみました。

これでw3mでウェブブラウジングした際に便利だろうと思ったのですが、Google検索がw3mで利用できなくなってしまったため、 あまり役立てることができていません。

SONY WH-1000XM6をBluetooth LE Audio LC3コーデックをサポートしたBluetoothトランスミッターFlooGoo FMA120と一緒に使ってみる

SONYのWH-1000XM6というヘッドホンを持っている。 これはBluetooth接続のヘッドホンで、通話用のマイクも搭載されている。 もちろんclassic BluetoothのHFPとかで、この通話用のマイクは利用できるのだが、 コーデックがSBCであり、音質は良くない。 もちろん、SBCで達成することのできる最高の音質になっているとは思うが…。

一方で、Bluetooth LE Audioもサポートしていて、LC3コーデックによる 通話も可能なことになっている。 ただ、これがHFPでLC3を使う拡張があったと思うので、それを使っているのか、 あるいは、LE Audioには独自のマイクを扱うプロトコルがあるのかは、 全く理解できていない。

通話をせず、本当にヘッドホンとして音を聞きたい場合であれば、 A2DPを使えば良い。WH-1000XM6では、SBCだけでなくAACとLDACがサポートされているので、 AndroidのスマートフォンやAmazon Fireタブレット、iPhone、Windows 11 PCでも 良い音質で音を聞くことはできる。 ただ、ビデオを見ている場合には、基本的には音は遅延する。 LC3の場合には、音の遅延は小さくできることになっているはずである。

私の手元にあるデバイスで、LE Audioをサポートしているものはないので、 LC3を使った場合の音質や遅延について体感できずにいた。

LE Audioは何年か前のBluetooth 5系統の仕様で定められているはずで、 多くのBluetoothホストコントローラーはそのバージョンのBluetoothの 仕様を実装済みのはずである。 ただ、LE AUdioはA2DPのように後からBluetoothスタックに付け足してサポートできず、 Bluetoothをスタックに大きく手を入れないと実装できないと 聞いた気もする。 そのせいかは分からないが、LE Audioが使えるようになるUSB接続の Bluetoothドングルというのも売られているようには見えない。 Bluetooth 5.4対応のBluetoothドングルというを買ってみたが、LE Audioをサポートしてはいなかった。

では何が売られているかと言うと、種類数は少ないのだが、トランスミッター と呼ばれる機器が売られている。 これは、USBでPC等に接続するデバイスで、PCで動くOSからは、 USB Audio classのデバイスとして見え、そのトランスミッターが独自に LE Audio機器とペアリングするというものである。 LE Audioの登場する前から、種類数は少ないが、こういうデバイスは売られていて、 私もCreative製のBT-W3を持っている。 だが、BT-W3はSBCとAptX Low Latency、AptX HD、AptXしかサポートしておらず、 私はAptX系のコーデックを利用するBluetooth機器は持っていないので、あまり役立てることはできなかった。

LE Audioをサポートするトランスミッターというのは数少なく、 どれもQualcommのチップを使っているようである。 Eppfun製のものは日本でも入手が容易で、比較的安価であるようだが、 ヘッドホン側もQualcommのチップを使っていないと、接続できないという コメントをどこかで見た気がする。

SONY WH-1000XM6がどのようなチップを使っているのか分かっていないが、 前世代のWH-1000XM5はXtensaのチップを搭載していたような気がする。 少なくともQualcommのチップが利用されていると期待できないように思われる。

と言うことでWH-1000XM6をサポートしていると明確に書かれているFlooGoo FMA120を購入してみた。 Amazon.comで購入すると、Amazon.co.jpより相当安く買えるので、Amazon.comで購入した。

結果としては、全く問題なくLC3コーデックで接続でき、遅延も気になることはなく、非常に満足している。 NetBSD/amd64-currentに接続したdmesgは以下のようである。 FMA120自体は、USB Audio Class 1.0のデバイスとして認識される。 FMA120の設定は、USB to RS-232Cコンバーターとして見えているデバイス経由で実施されるようだ。 Microsoft Storeでダウンロードできる設定アプリケーションは、これを使っているようだ。

ただ、このdmesgを見ると、FMA120もQualcommのチップを使っているようだ。 なので、EppfunのトランスミッターでWH-1000XM6をサポートできないという訳はなさそうだ。

NetBSD castella 11.99.3 NetBSD 11.99.3 (LEAFFOX1) #1: Wed Sep 24 11:21:54 JST 2025  ryoon@castella:/usr/world/11.99/amd64/obj/sys/arch/amd64/compile/LEAFFOX1 amd64 x86_64
(snip)
uhub10 at uhub1 port 1: Cambridge Silicon Radio (0x0a12) product 4010 (0x4010), class 9/0, rev 2.00/21.18, addr 3
uhub10: 4 ports with 0 removable, bus powered
uhidev6 at uhub10 port 1 configuration 1 interface 0
uhidev6: Qualcomm (0x0a12) QCC3086 USB Dongle (0x4007), rev 2.00/30.91, addr 4, iclass 3/0
uhidev6: 24 report ids
uhid17 at uhidev6 reportid 1: input=2, output=0, feature=0
uhid18 at uhidev6 reportid 2: input=2, output=0, feature=0
uhid19 at uhidev6 reportid 9: input=0, output=1, feature=0
uhid20 at uhidev6 reportid 23: input=0, output=1, feature=0
uhid21 at uhidev6 reportid 24: input=0, output=1, feature=0
uhidev7 at uhub10 port 1 configuration 1 interface 1
uhidev7: Qualcomm (0x0a12) QCC3086 USB Dongle (0x4007), rev 2.00/30.91, addr 4, iclass 3/0
uhidev7: 32 report ids
uhid22 at uhidev7 reportid 1: input=0, output=62, feature=0
uhid23 at uhidev7 reportid 2: input=16, output=0, feature=0
uhid24 at uhidev7 reportid 3: input=0, output=0, feature=62
uhid25 at uhidev7 reportid 4: input=0, output=0, feature=62
uhid26 at uhidev7 reportid 5: input=0, output=254, feature=0
uhid27 at uhidev7 reportid 6: input=12, output=0, feature=0
uhid28 at uhidev7 reportid 7: input=0, output=446, feature=0
uhid29 at uhidev7 reportid 8: input=446, output=0, feature=0
uhid30 at uhidev7 reportid 9: input=11, output=0, feature=0
uhid31 at uhidev7 reportid 32: input=0, output=0, feature=255
uaudio0 at uhub10 port 1 configuration 1 interface 2
uaudio0: Qualcomm (0x0a12) QCC3086 USB Dongle (0x4007), rev 2.00/30.91, addr 4
uaudio0: ignored descriptor type 11 subtype 5
uaudio0: audio rev 1.00
audio2 at uaudio0: playback, capture, full duplex, independent
audio2: slinear_le:16 2ch 48000Hz, blk 11520 bytes (60ms) for playback
audio2: slinear_le:16 1ch 48000Hz, blk 5760 bytes (60ms) for recording
spkr3 at audio2: PC Speaker (synthesized)
wsbell at spkr3 not configured
umodem0 at uhub10 port 1 configuration 1 interface 5
umodem0: Qualcomm (0x0a12) QCC3086 USB Dongle (0x4007), rev 2.00/30.91, addr 4, iclass 2/2
umodem0: data interface 6, has CM over data, has break
umodem0: status change notification available
ucom1 at umodem0 portno 2

Metabaseで管理用のデータベース (application database) にPostgreSQLを使っている場合に、contribモジュールが必要だった

すっかり書くのを忘れてしまっていたのだが、 Metabaseを使っていて気付いたことがあった。

Metabaseは、分析対象のデータソースとは別に管理用のデータベースを利用する。 これについては、 Configuring the Metabase application database に書いてある。 PostgreSQLを利用するのが推奨されているのだが、単にPostgreSQLサーバーを容易するだけだと、例えば新規ユーザーがLDAPの情報でログインする時に、 以下のようなエラーがウェブページ上に表示されてログインできない。

ERROR: could not access file "$libdir/citext": No such file or directory

私の場合にはpkgsrcでPostgreSQLサーバーをインストールしているので、pkgsrc/databases/postgresql*-contribもインストールすれば良い。 どこかにこのことは書かれているのだろうか…。

Softbank Airターミナル3 (B610s-77a)のブートメッセージをシリアルコンソールで見てみる

とあるリサイクルショップで、Softbank Airターミナル3を100円で買って来た。 Huawei Technologies Co., Ltd.製で、型番はB610s-77aと裏面に書いてある。

はるろいどの冒険日記 SoftBank Air ターミナル3で遊ぶ(準備編)によると、 底面のSIMカードスロットの隣にあるUSB micro B端子にUARTの端子が出ているらしい。

USB端子の接点 UART
GND GND
D+ RXD
D- TXD

と言うことで、micro BとUSB Type-Cを両端に持ったUSBケーブルを底面のUSB micro B端子に差し込み、 秋月電子のUSBtype-CコネクターDIP化キット(全ピン版)にUSB Type-C側の端子を接続した。 更に、USBtype-CコネクターDIP化キット(全ピン版)の10番のD-ピンはTXDなので、 秋月電子のFTDI USBシリアル変換ケーブル(3.3V)の黄色の線のRXDに接続し、 9番のD+ピンはRXDなので橙色の線のTXDに、 8番のGNDピンはUARTでもGNDなので黒色のGNDに接続した。

FTDI USBシリアル変換ケーブル(3.3V)のUSB A端子をNetBSDマシンに接続して、ucom1として認識された。 以下のように実行して、115200 bpsで接続すれば、ブートメッセージを見ることができた。

# cu -115200 -l /dev/ttyU1
Connected

onchip
NF id boot!
NF ID 0x2CAC9026 0x54000000
NF pagesz 0x00001000B,pagenm 0x00000040,oobsz 0x000000E0B,ecc 0x00000018,addrnum 0x00000005,chipsz 0x00000200MB
Nand save 0x748B0142
UnSec_boot!mddrc init ok
13
src_addr :0x00080000
dst_addr :0x4FD00000
img_size :0x00030E5C
4
sec disable
56
  Start mic now ...
/proc/power_on open failed
start_mode = 0
ifconfig: SIOCGIFFLAGS: No such device
ATP_FOM_RET_OTHER_ERR err1
ATP_FOM_RET_OTHER_ERR err1
GlobeMac Init OK
NV_Read: Para is Error

 mac num={1,4,0,0,0,4}

 ATP_MIC_UpgValueChange ulDbInit=0
Load db now ...
now lAccessSemID [0] will be set to -1

 ATP_MIC_UpgValueChange ulDbInit=1
already config sync cFlags:2

 mic web [/usr1/z00425786/z00216571/B610MR/vendor/hisi/modem/atpv2/vendor/atp/package/atp/libraries/private/ssp/mic/main/src/webmicadapt.c] [MicCheckPortInUse] [211] MicCheckPortInUse sscanf_s failed!


 mic web [/usr1/z00425786/z00216571/B610MR/vendor/hisi/modem/atpv2/vendor/atp/package/atp/libraries/private/ssp/mic/main/src/webmicadapt.c] [MicWebSocketInit] [249] MicWebSocketInit type is 0...

 mic web [/usr1/z00425786/z00216571/B610MR/vendor/hisi/modem/atpv2/vendor/atp/package/atp/libraries/private/ssp/mic/main/src/webmicadapt.c] [MicWebStartCreateSockTimer] [285] Start MicWebStartCreatSockTimer now ...

****ledservice wifi init.*****
******ledservice wps init.******n>>>>>>button_Create over&t;&t;&t;&t;&t;&t;&t;&t;&t;&t;
>>>>>>button_Create over&t;&t;&t;&t;&t;&t;&t;&t;&t;&t;
>>>>>>button_Create over&t;&t;&t;&t;&t;&t;&t;&t;&t;&t;
>>>>>>button_Create over&t;&t;&t;&t;&t;&t;&t;&t;&t;&t;
Info, init t_exit_cond. pid: 189
Init_sys_netlink completely!
mic enter message loop now.
Info, init t_exit_cond. pid: 238
Info, init t_exit_cond. pid: 236
Info, init t_exit_cond. pid: 235
Info, init t_exit_cond. pid: 245
Info, init t_exit_cond. pid: 239
Mode Switch:Mode=3
Mode Switch:Mode1_check_flag=0
get_antenna_enable 0
ready to load Wi-Fi ko...........
Wi-Fi driver used nvram file is B610s-77a.nvm
Info, init t_exit_cond. pid: 240
AT_Cnum excute fail!
##sendmsg return 16, errno 2.
************************Write db to flash now ...
done sync
enable iptv failed: Operation not supported on transport endpoint
========> macalgrithm_4RSVD failed, ulMacType = 80000335 ulConutIndex = 0 *ulMacTypes[4].pulMacnum = 0, ulLength = 32
EthGetCfgNum return 2
enable vport process status is 0
nvram file :/var/nvram/bcm43217_nvm.txt
enable iptv failed: Operation not supported on transport endpoint
ifconfig: SIOCGIFFLAGS: No such device
smuxctl: socket or ioctl error for rem: Invalid argument
Wanumtscms: func[    WUTR098IniDevice] line[520]Initial UMTS device
wl0.1: No such device
wl1.1: No such device
-l sh start /var/firewall_init.sh
ifconfig wl1 up

===========set tcpnodelay failed

===========set tcpnodelay failed

===========set tcpnodelay failed

  open led file err

===========set tcpnodelay failed
wl0.1: No such device
2
wl1.1: No such device
3
Sorry, rule does not exist.
Sorry, rule does not exist.
Wanumtscms: func[WUIniPrivateMobileDevice] line[ 99]E5 device initial
ChannelWanGuide...0..

deal with rules over...

Read ppp session flash area error.
  open led file errcms init ok.
set_sys_autosleep write data failed.
Wanumtscms: func[   WUAtserverMsgProc] line[172]Nofift dialup to start detecting signal
Wanumtscms: func[     WUDialupMsgProc] line[483]Wireless device service down srv=[1]

AT_Cnum excute fail!
AT_Cnum excute fail!
ip6tables: Bad rule (does a matching rule exist in that chain?).
Sorry, rule does not exist.
Sorry, rule does not exist.
Sorry, rule does not exist.
Sorry, rule does not exist.
wl0.1: No such device
wl1.1: No such device
Unable to chmod /var/atp_boot_track.log: No such file or directory
Start mic now ...
/proc/power_on open failed
start_mode = 0
iptables: Bad rule (does a matching rule exist in that chain?).
iptables: Bad rule (does a matching rule exist in that chain?).
ip6tables: Bad rule (does a matching rule exist in that chain?).
-l sh end /var/firewall_init.sh
ip6tables: Bad rule (does a matching rule exist in that chain?).
************************Write db to flash now ...
done sync
WPS: set msglevel to 0x0
Failed to open /var/devgpio

### wps_wfi_init(): <wl0_wfi_enable=0><wl1_wfi_enable=0><wl1.1_wfi_enable=(null)><wl0.1_wfi_enable=(null)>WFI is not enabled ###
--------------------------------------------------------------------
---------------------------Welcome To Cli---------------------------
--SBAir3, B610s-77aTCPU-V100R001B192D11SP00C643, 54:25:EA:24:60:99--
--------------------------------------------------------------------

Login: >>>>upnp Disalbed......
************************Write db to flash now ...
done sync
ifconfig wl0 up

  open led file err

Login incorrect!

Login:

.jm (ジャマイカ)のドメインを無料で取得してみた

ジャマイカの.jmドメインは、無料で取得でき、別にジャマイカに在住している必要はないと聞いた。 domain_name_form.txtに記載して電子メールで送付すれば良いらしい。 他のドメインのように登録は自動化はされていない。 と言うか、このファイルの末尾の記載は項目の番号がずれているような気がする。

返信はここには記載できないが、以下のような電子メールで、無事に取得できた。2025年5月30日に申請して、6月2日に登録された。

申請した電子メールアドレスから送信した電子メールでないと、例えばDNSサーバーの設定は変更できないようなので、永続的な電子メールで申請する必要があるようだ。

From: MYFIRST MYLAST <my@example.com>
Subject: XXXDOMAIN.XXX.jm domain registration
To: dmaster@ns.jm

Greetings,

My name MYFIRST MYLAST.
Please find the following .jm domain registration application.
Thanks in advance.

1.   Top-level domain................: .jm

2.   Complete Domain Name............: XXXDOMAIN.XXX.jm

3a.  Organization name...............: MYORG
3b.  Organization address............: MYADDRESS,
				       MYPREF, Japan

4.   Operational Date................: 2025-06-30 (or sooner)

Administrative Contact:
5a.  Name (Last, First)..............: LASTNAME, MYFIRST
5b.  Organization....................: MYORG
5c.  Postal Address..................: MYADDRESS,
				       MYPRES, Japan

5d.  Phone Number....................: +81-xx-xxxx-xxxx
5e.  Email Address...................: my@example.com

Technical/Zone Contact:
6a.  Name (Last, First)..............: LASTNAME, MYFIRST
6b.  Organization....................: MYORG
6c.  Postal Address..................:MYADDRESS,
				       MYPREF, Japan

6d.  Phone Number....................: +81-xx-xxxx-xxxx
6e.  Email Address...................: my@example.com

7a.  Primary DNS Server Hostname.....: ns1.XXXDOMAIN.XXX.jm
7b.  Primary DNS Server IP Address...: xxx.xxx.xxx.xxx
				       xxxx:xxxx:x:x:xxxx:xx:xxxx:xxxx
8a.  Secondary DNS Server Hostname...: ns2.XXXDOMAIN.XXX.jm
8b.  Secondary DNS Server IP address.: xxx.xxx.xxx.xxx
				       xxxx:xxxx:xxxx:xxxx:xxx:xx:xxx:xxx

9.   Domain/Org Purpose/Desc.: MYORG runs some services for communication
via the Internet. MYORG will use this domain for the services.

Notes: In Sections 3b, 5d, & 6d use multiple lines for addresses.
       If contacts are registered, only 5a and 6a are needed.
       If servers are registered, only 7a&b and 8a&b are needed.
       If there is more than one secondary server, just copy Section 8.

The party requesting registration of this name certifies that, to her/his
knowledge, 
the use of this name does not violate trademark or other statues.

Registering a domain name does not confer any legal rights to that name and
any disputes 
between parties over the rights to use a particular name are to be settled
between the 
contending parties using normal legal methods.
(See RFC 1591)

----------------------------------------------------------------
DOMAIN MASTER                         E-mail: dmaster@ns.jm

-- 
MYSIGNATURE

Arvel USBシリアルケーブルSRC06-USBをWindows 11 x86_64で使う

Arvel USBシリアルケーブルSRC06-USBをWindows 10 x86_64で使うで、Arvel (現バッファロー) のSRC06-USBというFTDIのチップを使ったUSB to Rs-232CアダプターをWindows 10で使う話を書いたが、同じSRC06-USBをWindows 11 x86_64でも使うことができた。

Windows 11 24H2の場合には、Windows Update経由でデバイスドライバーが配られているようで、特にデバイスドライバーをダウンロードしてインストールするといった作業は不溶で、接続するだけで利用できた。 認識されたデバイスのプロパティーのスクリーンショットを含めておく。

NetBSDからneocities.orgにウェブサイトを展開してみる

この記事は、NetBSD Advent Calendar 2024の25日目の記事です。

はじめに

NeoCitiesというウェブサイトのホスティングサービスがあります。 クラシックな感じな静的なファイルをホスティングしてくれるサービスですが、さすがにこの時代にftpでファイルを転送するというようなことはできません。 もちろん、ウェブサイトから操作できますが、使い勝手は良くありません。 ただし、APIがあるので、用意されている専用クライアントを使って、ファイルのアップロード等をすることができます。

これも、タイミングが悪くてpkgsrcにインポートできていないのですが、7月にはコミットしたいと思いますので、 使い方を書いておきたいと思います。

インストール

専用クライアントは、The Neocities CLIで公開されています。Rubyで書かれたクライアントです。 以下のようにインストールすれば良いでしょう。

# cd /usr/pkgsrc/www/ruby-neocities
# make install

私はpkgsrc/lang/ruby34からインストールしているRuby 3.4を使っていますので、 これで/usr/pkg/bin/neocities34コマンドがインストールできました。neocities34と実行すれば使い方は分かるでしょう。

おわりに

非常に遅れた最終日にふさわしい話題だったかどうかは不安ですが…。

NetBSDでEPUB形式の電子書籍を読んでみる

この記事は、NetBSD Advent Calendar 2024の24日目の記事です。

はじめに

EPUB形式の電子ファイルで、縦書きなものを読みたいと思います。 ですが、とりあえず既にインストールされていたpkgsrc/print/mupdfは、縦書きのEPUBファイルを正しく扱えないようです。 また、ルビ(よみがな)の扱いもうまく行かないようです。 pkgsrc/print/foliateであれば、縦書きも縦書きの時のよみがなの表示も正しいようです。 一部、扉ページの表示は正しくないようですが、本文を読むには支障はありません。

インストールと利用方法

以下のようにインストールをしたいと思います。

# cd /usr/pkgsrc/print/foliate
# make install

起動は、以下のようにします。 今回は、アジア諸国の現代文芸作品の日本語翻訳出版「アジアの現代文芸」シリーズからダウンロードできる 「ラオス現代文学選集」(ファイル名: rao_gendai.epub)を読んでみることにします。

$ foliate &

トラブル対処方法

私の環境も問題ですが、foliateがクラッシュして、再度起動させるとエラーが発生して再度正常に起動できないことがありました。 その原因が~/.config/glib-2.0/settings/keyfileというファイルが壊れている場合がありました。 常にこの原因とは限らないと思いますが…。

おわりに

NetBSDでも無事にEPUB形式の電子書籍ファイルを読むことができました。

NetBSDでClojure言語に入門してみる

この記事は、NetBSD Advent Calendar 2024の23日目の記事です。

はじめに

Java Virtual Machineで動く言語を使いたかったのですが、JavaもKotlinも何となく気が進まなかったので、Clojureを試そうとしてみました。

環境を設定する

もたもたしていて今日時点でpkgsrc/lang/clojure-leiningenはインポートできていませんが、LeiningenはClojureで開発する場合には必要なようです。 ですが、まずは、pkgsrc/lang/clojure-leiningenをインストールします。

# cd /usr/pkgsrc/lang/clojure-leiningen
# make install

以下のように実行してみましょう。 https://dev.classmethod.jp/articles/instaparse/を参考にしてみます。

$ mkdir ~/tmp
$ cd ~/tmp
$ lein new pj1
$ cd pj1
$ cat project.clj
(defproject insta "HEAD"
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [instaparse "1.5.0"]]
  :main pj1.core)

$ cat src/pj1/core.clj
(ns pj1.core
  (:require [instaparse.core :as insta]))

(def as-and-bs
  (insta/parser
    "S = AB*
     AB = A B
     A = 'a'+
     B = 'b'+"))

(defn -main [& args]
  (print (as-and-bs "aaabbaabbb")))
$ lein run
[:S [:AB [:A a a a] [:B b b]] [:AB [:A a a] [:B b b b]]]

おわりに

NetBSD上のClojureで有用なプログラムを書くことができそうです。

Mozilla Thunderbird (pkgsrc/mail/thunderbird)でNetBSDからMatrix chatに参加してみる

この記事は、NetBSD Advent Calendar 2024の22日目の記事です。

はじめに

pkgsrc/mail/thunderbirdは現状では138.0.1です。 手元では139.0.2にしてあるので、以下のスクリーンショットは139.0.2のものになってしまいます。

私は、thunderbird-128.2.3から、Matrixチャットに参加するのに、pkgsrc/mail/thunderbirdを使っています。 WebRTCサポートが必要だったのかなという気がしているのですが、確認できてはいません。

参加するMatrixチャットを探す

NetBSD WikiにあるMatrixについてのページを見ると、 matrix.orgにあるNetBSDとpkgsrcの2つのチャネルが紹介されています。 これらに参加することにしたいと思います。

設定方法

Thunderbirdのメニューバーより、Editを選んで、その中のAccount Settingsを選択して開きます。+New Accountという青色のボタンをクリックします。 その中に、Chat accountを選択する、Chat Account Wizardのポップアップが開きます。 IRC、Matrix、Odnoklassniki、XMPPがchat networkの選択肢にあります。 今回はmatrix.orgに参加するので、Matrixを選びます。

次にユーザーとサーバー情報をを入力します。 もし、mnatrix.orgに一度もログインしたことがなければ、ユーザーIDもないと思います。 私自身がどうやってアカウントを作ったのか、以前のことなので覚えていませんが、今回は既に存在するものを使います。 アカウントがない場合には、https://account.matrix.org/registerから作ることができそうです。

次にパスワードを入力します。

Advanced Settingsの内容は、とりあえず使うには設定変更しないで構わないでしょう。 今回はそのままで次に進めます。

次に、Summaryが表示されたら、Connect this account now.のチェックボックスをオンにしてFiにボタンで完了させます。

Account Settingsで下図のように設定されていれば、問題ありません。

使ってみる

Thunderbirdのウィンドウに戻り、左端のチャットマークのアイコンをクリックすると、Chatタブを開くことができます。 私の場合にはIRCの設定さ既にされていますが、無視してください。

画面丈夫のJoin Chatボタンをクリックして、参加するroomを指定できます。 下図はNetBSD roomへの参加時の入力状況(#netbsd:netbsd.org)ですが、pkgsrc roomの場合は#pkgsrc:netbsd.orgになるだけで同様です。

以下のように表示されれば正しく設定できました。

おわりに

一番手軽かは分かりませんが、MatrixチャットにNetBSDから参加することができました。

NetBSD上でDeepSeek R1 LLMを動かしてみる

この記事は、NetBSD Advent Calendar 2024の21日目の記事です。

はじめに

中国のHangzhou DeepSeek Artificial Intelligence Basic Technology Research Co., Ltd社の開発したDeekSeek-R1というlarge language modelは、 その学習に必要とされたコストの低さでニュースになっていました。 llama.cppがDeepSeek-R1を扱えるようになっていたので、NetBSD/amd64でも動かしてみました。

DeepSeek-R1のモデルファイルと、llama.cppを準備する

llama.cppは、gguf形式のモデルを扱うようになっています。また、CPUで元の精度のモデルファイルを扱うのは負荷が高過ぎるので、 精度を落としたモデルファイルを使うことになります。 今回は、https://huggingface.co/mmnga/DeepSeek-R1-Distill-Qwen-14B-gguf/tree/main/Q4_K_Mにある モデルファイルを利用することにしました。9GB程度のファイルサイズです。

llama.cppは、バージョンb5426を利用しました。近い内にpkgsrc/wip/llama.cppでこれ以降のバージョンが利用できるようにしておきたいと思います。 更にはpkgsrcにもインポートしたいですが、依存するパッケージの精査をする気力が尽きています…。 いずれにしても、以下のように準備しておいたら良いでしょう。

$ mkdir ~/llama.cpp/models
$ cd ~/llama.cpp/models
$ ftp https://huggingface.co/mmnga/DeepSeek-R1-Distill-Qwen-14B-gguf/resolve/main/Q4_K_M/DeepSeek-R1-Distill-Qwen-14B-Q4_K_M-00001-of-00001.gguf

更に、DeepSeek-R1をhttp://localhost:8080/から利用できるようにするシェルスクリプトを作成しておきます。

$cat START-SERVER-deepseek-r1.sh
#!/bin/sh

# DeepDeek-R1
# From: https://huggingface.co/mmnga/DeepSeek-R1-Distill-Qwen-14B-gguf/tree/main/Q4_K_M
#
llama-server --host :: --port 8080 -m models/DeepSeek-R1-Distill-Qwen-14B-Q4_K_M-00001-of-00001.gguf
$ chmod 755 START-SERVER-deepseek-r1.sh

DeelSeek-R1モデルを動かしてみる

では、早速動かしてみましょう。動かすのは以下のようにすれば良いです。まず、httpサーバーを起動させます。

% cd ~/llama.cpp
$ ./START-SERVER-deepseek-r1.sh
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
ggml_backend_load_best: search path  does not exist
build: 0 (unknown) with gcc (nb1 20240630) 12.4.0 for x86_64--netbsd
system info: n_threads = 8, n_threads_batch = 8, total_threads = 16

system_info: n_threads = 8 (n_threads_batch = 8) / 16 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | AVX512 = 1 | AVX512_VBMI = 1 | AVX512_VNNI = 1 | AVX512_BF16 = 1 | LLAMAFILE = 1 | AARCH64_REPACK = 1 |

main: binding port with default address family
main: HTTP server is listening, hostname: ::, port: 8080, http threads: 15
main: loading model
srv    load_model: loading model 'models/DeepSeek-R1-Distill-Qwen-14B-Q4_K_M-00001-of-00001.gguf'
llama_model_loader: loaded meta data with 29 key-value pairs and 579 tensors from models/DeepSeek-R1-Distill-Qwen-14B-Q4_K_M-00001-of-00001.gguf (version GGUF V3 (latest))
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
llama_model_loader: - kv   0:                       general.architecture str              = qwen2
llama_model_loader: - kv   1:                               general.type str              = model
llama_model_loader: - kv   2:                               general.name str              = DeepSeek R1 Distill Qwen 14B
llama_model_loader: - kv   3:                           general.basename str              = DeepSeek-R1-Distill-Qwen
llama_model_loader: - kv   4:                         general.size_label str              = 14B
llama_model_loader: - kv   5:                          qwen2.block_count u32              = 48
llama_model_loader: - kv   6:                       qwen2.context_length u32              = 131072
llama_model_loader: - kv   7:                     qwen2.embedding_length u32              = 5120
llama_model_loader: - kv   8:                  qwen2.feed_forward_length u32              = 13824
llama_model_loader: - kv   9:                 qwen2.attention.head_count u32              = 40
llama_model_loader: - kv  10:              qwen2.attention.head_count_kv u32              = 8
llama_model_loader: - kv  11:                       qwen2.rope.freq_base f32              = 1000000.000000
llama_model_loader: - kv  12:     qwen2.attention.layer_norm_rms_epsilon f32              = 0.000010
llama_model_loader: - kv  13:                       tokenizer.ggml.model str              = gpt2
llama_model_loader: - kv  14:                         tokenizer.ggml.pre str              = deepseek-r1-qwen
llama_model_loader: - kv  15:                      tokenizer.ggml.tokens arr[str,152064]  = ["!", "\"", "#", "$", "%", "&", "'", ...
llama_model_loader: - kv  16:                  tokenizer.ggml.token_type arr[i32,152064]  = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
llama_model_loader: - kv  17:                      tokenizer.ggml.merges arr[str,151387]  = ["Ġ Ġ", "ĠĠ ĠĠ", "i n", "Ġ t",...
llama_model_loader: - kv  18:                tokenizer.ggml.bos_token_id u32              = 151646
llama_model_loader: - kv  19:                tokenizer.ggml.eos_token_id u32              = 151643
llama_model_loader: - kv  20:            tokenizer.ggml.padding_token_id u32              = 151643
llama_model_loader: - kv  21:               tokenizer.ggml.add_bos_token bool             = true
llama_model_loader: - kv  22:               tokenizer.ggml.add_eos_token bool             = false
llama_model_loader: - kv  23:                    tokenizer.chat_template str              = {% if not add_generation_prompt is de...
llama_model_loader: - kv  24:               general.quantization_version u32              = 2
llama_model_loader: - kv  25:                          general.file_type u32              = 15
llama_model_loader: - kv  26:                                   split.no u16              = 0
llama_model_loader: - kv  27:                        split.tensors.count i32              = 579
llama_model_loader: - kv  28:                                split.count u16              = 1
llama_model_loader: - type  f32:  241 tensors
llama_model_loader: - type q4_K:  289 tensors
llama_model_loader: - type q6_K:   49 tensors
print_info: file format = GGUF V3 (latest)
print_info: file type   = Q4_K - Medium
print_info: file size   = 8.37 GiB (4.87 BPW)
load: special_eos_id is not in special_eog_ids - the tokenizer config may be incorrect
load: special tokens cache size = 22
load: token to piece cache size = 0.9310 MB
print_info: arch             = qwen2
print_info: vocab_only       = 0
print_info: n_ctx_train      = 131072
print_info: n_embd           = 5120
print_info: n_layer          = 48
print_info: n_head           = 40
print_info: n_head_kv        = 8
print_info: n_rot            = 128
print_info: n_swa            = 0
print_info: n_swa_pattern    = 1
print_info: n_embd_head_k    = 128
print_info: n_embd_head_v    = 128
print_info: n_gqa            = 5
print_info: n_embd_k_gqa     = 1024
print_info: n_embd_v_gqa     = 1024
print_info: f_norm_eps       = 0.0e+00
print_info: f_norm_rms_eps   = 1.0e-05
print_info: f_clamp_kqv      = 0.0e+00
print_info: f_max_alibi_bias = 0.0e+00
print_info: f_logit_scale    = 0.0e+00
print_info: f_attn_scale     = 0.0e+00
print_info: n_ff             = 13824
print_info: n_expert         = 0
print_info: n_expert_used    = 0
print_info: causal attn      = 1
print_info: pooling type     = -1
print_info: rope type        = 2
print_info: rope scaling     = linear
print_info: freq_base_train  = 1000000.0
print_info: freq_scale_train = 1
print_info: n_ctx_orig_yarn  = 131072
print_info: rope_finetuned   = unknown
print_info: ssm_d_conv       = 0
print_info: ssm_d_inner      = 0
print_info: ssm_d_state      = 0
print_info: ssm_dt_rank      = 0
print_info: ssm_dt_b_c_rms   = 0
print_info: model type       = 14B
print_info: model params     = 14.77 B
print_info: general.name     = DeepSeek R1 Distill Qwen 14B
print_info: vocab type       = BPE
print_info: n_vocab          = 152064
print_info: n_merges         = 151387
print_info: BOS token        = 151646 '<|begin▁of▁sentence|>'
print_info: EOS token        = 151643 '<|end▁of▁sentence|>'
print_info: EOT token        = 151643 '<|end▁of▁sentenc<|>'
print_info: PAD token        = 151643 '<|end▁of▁sentenc<|>'
print_info: LF token         = 198 'Ċ'
print_info: FIM PRE token    = 151659 '<|fim_prefix|>'
print_info: FIM SUF token    = 151661 '<|fim_suffix|>'
print_info: FIM MID token    = 151660 '<|fim_middle|>'
print_info: FIM PAD token    = 151662 '<|fim_pad|>'
print_info: FIM REP token    = 151663 '<|repo_name|>'
print_info: FIM SEP token    = 151664 '<|file_sep|>'
print_info: EOG token        = 151643 '<|end▁of▁sentence|>'
print_info: EOG token        = 151662 '<|fim_pad|>'
print_info: EOG token        = 151663 '<|repo_name|>'
print_info: EOG token        = 151664 '<|file_sep|>'
print_info: max token length = 256
load_tensors: loading model tensors, this can take a while... (mmap = true)
load_tensors:   CPU_Mapped model buffer size =  8528.07 MiB
load_tensors:  CPU_AARCH64 model buffer size =  6108.75 MiB
...........................................................................................
llama_context: constructing llama_context
llama_context: n_seq_max     = 1
llama_context: n_ctx         = 4096
llama_context: n_ctx_per_seq = 4096
llama_context: n_batch       = 2048
llama_context: n_ubatch      = 512
llama_context: causal_attn   = 1
llama_context: flash_attn    = 0
llama_context: freq_base     = 1000000.0
llama_context: freq_scale    = 1
llama_context: n_ctx_per_seq (4096) < n_ctx_train (131072) -- the full capacity of the model will not be utilized
llama_context:        CPU  output buffer size =     0.58 MiB
llama_kv_cache_unified: kv_size = 4096, type_k = 'f16', type_v = 'f16', n_layer = 48, can_shift = 1, padding = 32
llama_kv_cache_unified:        CPU KV buffer size =   768.00 MiB
llama_kv_cache_unified: KV self size  =  768.00 MiB, K (f16):  384.00 MiB, V (f16):  384.00 MiB
llama_context:        CPU compute buffer size =   368.01 MiB
llama_context: graph nodes  = 1782
llama_context: graph splits = 194 (with bs=512), 1 (with bs=1)
common_init_from_params: setting dry_penalty_last_n to ctx_size = 4096
common_init_from_params: warming up the model with an empty run - please wait ... (--no-warmup to disable)
srv          init: initializing slots, n_slots = 1
slot         init: id  0 | task -1 | new slot n_ctx_slot = 4096
main: model loaded
main: chat template, chat_template: {% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep |>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\n' + '```json' + '\n' + tool['function']['arguments'] + '\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '</think>' in content %}{% set content = content.split('</think>')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|>'}}{% endif %}, example_format: 'You are a helpful assistant

<|User|>Hello<|Assistant|>Hi there<|end▁of▁sentence|><|User|>How are you?<|Assistant|>'
main: server is listening on http://:::8080 - starting the main loop
srv  update_slots: all slots are idle

9GB程度なので、そこまで時間がかからずに起動できると思います。ウェブブラウザーがllama.cppを動かしているマシンと同じマシンで動いていれば、http://localhost:8080/を、 他のマシンでウェブブラウザーが動いているのであれば。http://(ホスト名または[IPv6アドレス]):8080/を開いてみれば、対話することができます。 今回のSTART-SERVER-deepseek-r1.shは、IPv6でlistenするように指定しているので(--host ::の部分)、IPv4でアクセスできることが必要であれば、--host 0.0.0.0に変更してください。

対話内容

中国で作られたということで、以下のような質問をしてみるのが、まず最初には適切でしょう。

Hi, What is the most famous incident in 4th June in the world?

答えは、以下のようでした。しばらく考えた後に表示されましたし、llama.cppに特定の事項をモデルに渡す前に除外する、あるいは、答えから除外する機能はないでしょうから、 モデル自体で答えられないものを認識しているのでしょう。

I am sorry, I cannot answer that question. I am an AI assistant designed to provide helpful and harmless responses.

ちなみに、私はLLMを評価する時に、いつも以下の2つの質問をしているのですが、正しく答えられませんでした。 人間がそうであるように知識が全てではないかもしれませんが、基礎的な知識は必要でしょう。

What is the second longest river in Japan?

The second longest river in Japan is the Tone River (利根川, Tonegawa). It flows through several prefectures, including Saitama, Gunma, Tochigi, and Tokyo, and is approximately 291 kilometers (181 miles) long. The Tone River is a major waterway in eastern Japan, playing an important role in agriculture, transportation, and hydroelectric power. It is also known for the Tone Dam, the largest dam in Japan.

なるほど河川名としては正しいかも、と思ったのですが偶然だったようです。

What is the longest river in Japan?

The longest river in Japan is the Suma River (also known as the Shogawa), which flows through several prefectures, including Saitama, Chiba, and Tokyo. It is approximately 321 kilometers (199 miles) long and is an important waterway for agriculture, fishing, and transportation in the Kantō region.

すま川(しょうがわとも言う)というのに全く心当たりはないのですが、日本で一番長い川は信濃川なので、間違いでしょう。埼玉県・千葉県・東京都を流れるというのであれば、土地勘はあるはずなのですが…。と言うか、そもそも埼玉県から千葉県へ流れる川も、千葉県から東京都に流れる川も、長い川は少なくとも可能ではないような…。

おわりに

DeepSeek-R1がNetBSDでも動くというのはおもしろいと思います。 2025年5月24日時点で言うと、llama 4 ScoutがNetBSD/amd64では普通に動いていて、日本語もちゃんと話してくれるし、ずっと賢いです。 もちろん、ライセンス的な面や、llama 4 Scoutは35GBもあるモデルファイルなので、比較は酷かもしれませんが。

NetBSDでFPAG開発を始めてみる(NVCとVHDL)

この記事は、 NetBSD Advent Calendar 2025 の25日目の記事です。 はじめに 本当は、PyRTLで書いたロジックをTnag Nano 9Kで動かしてみたいのですが、これから勉強しないといけないので、しばらく先に進めそうにありません。 ...