この記事は、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のシミュレーションをするには、GHDLやNVCが使えそうです。 ただ、今回は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をプログラムして、動作を確認してみます。

0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。