Python3でVerilog用画像入力テストベンチ自動生成
Verilogでグレイスケール画像群を読み込みモジュールに1画素ずつストリームに転送するためのテストベンチ、hexファイル、Makefileを自動生成するpythonスクリプトを作成しました。(ゴリ押しですが...)
画像群をROMに格納して自分のモジュールにinputします。
実行環境はいかになります。
まずself.pathで画像ディレクトリを指定、self.extentionで拡張子を指定したverilogで読みたい画像達をいれます。
このサンプルでは./image
ディレクトリにpng
の拡張子をもつ解像度200x200,8bitグレイスケールの画像を2枚入れています。
#! /usr/local/bin/ env python3 # -*- coding: utf-8 -*- import numpy as np from PIL import Image import math import os class ImageRom: """make images to loading stream imput""" def __init__(self, bit, clk, topmodule): """create bit width""" self.bit = bit self.hexbit = bit // 4 self.clk = clk self.topmodule = topmodule self.path = './image' self.extention = 'png' def makereadh(self): """convert images to .hex file""" maxword = 0 filelist = [os.path.join(self.path,f) for f in os.listdir(self.path) if f.endswith(self.extention)] wf = open("img.hex", 'w') for filename in filelist: pilin = Image.open(filename) maxcol, maxrow = pilin.size maxword += maxcol * maxrow frame = np.asarray(pilin) for i in range(maxrow): for j in range(maxcol): hexcon = hex(frame[i, j]) hexsplit = "{0}".format(hexcon.replace('0x', '')).rjust(int(self.hexbit), '0') print(hexsplit) wf.write("{0}\n".format(hexsplit)) wf.close() self.word = maxword self.wordadr = math.ceil(math.log2(maxword)) print("success making hexfile") def makeTestbench(self): """ make testbench.v """ wf = open("tbench.v", 'w') testbench = """`timescale 1ns/1ps `define P_PXADDR {0} `define P_IMGDEPTH {1} module tbench; parameter P = {2}; reg clk, rst; wire [`P_IMGDEPTH-1:0] q; initial begin $dumpfile("./test.vcd"); $dumpvars(0, {3}); clk <= 0; rst <= 0; # {4} rst <= 1; # {5} $finish(); end always #(P/2) clk <= ~clk; imgROM imgROM(.clk(clk), .rst(rst), .q(q)); // add your instance module endmodule module imgROM(input clk, input rst, output [`P_IMGDEPTH-1:0] q); parameter P_maxword = `P_PXADDR'd{6}; reg [`P_IMGDEPTH-1:0] mem [0:P_maxword]; reg [`P_PXADDR-1:0] pxADDR; initial begin $readmemh("img.hex", mem, `P_PXADDR'd0, `P_PXADDR'd{6}); end always @(posedge clk or negedge rst) begin if(!rst | pxADDR == P_maxword) pxADDR <= `P_PXADDR'd0; else pxADDR <= pxADDR + `P_PXADDR'd1; end assign q = mem[pxADDR]; endmodule """.format(self.wordadr, self.bit, self.clk, self.topmodule, self.clk * 2, self.word * self.clk, self.word - 1) wf.write(testbench) wf.close() print("success making tbench.v") def makeMakefile(self): """ make Makefile """ wf = open("Makefile", 'w') text ="""TBENCH = ./tbench.v #VERILOG = ./ # add your .v file all: \tiverilog -o ./test.vpp $(TBENCH) $(VERILOG) \t./test.vpp \tgtkwave ./test.vcd """ wf.write(text) wf.close() print("success making Makefile") if __name__ == '__main__': test = ImageRom(8, 100, "reverseq") # bit幅, クロック周波数, 自分のトップモジュールのインスタンス名 test.makereadh() test.makeTestbench() test.makeMakefile()
上記のpython3を実行すると以下のファイルが作成されるはずです。
- Makefile
- tbench.v # testbenchファイル
- img.hex # 画素値をhexにしたもの
自分の作成したモジュールを追加します。流れてきた画素値を反転するモジュールを追加します。(ファイル名はreverseq.vとします)
module reverseq(input clk, input [7:0] q, output reg [7:0] revq); always @(posedge clk) begin revq <= ~q; end endmodule
作成したreverseq.vをMakefileとtestbench.vに追加します。
まずMakefileに
TBENCH = ./tbench.v #以下の行をコメントアウトを外して追加 VERILOG = ./reverseq.v # add your .v file all: iverilog -o ./test.vpp $(TBENCH) $(VERILOG) ./test.vpp gtkwave ./test.vcd
そしてtbench.vに自作モジュールをインスタンス化します。(観測用のwireも追加)
`timescale 1ns/1ps `define P_PXADDR 17 `define P_IMGDEPTH 8 module tbench; parameter P = 100; reg clk, rst; wire [`P_IMGDEPTH-1:0] q, revq; initial begin $dumpfile("./test.vcd"); $dumpvars(0, reverseq); clk <= 0; rst <= 0; # 200 rst <= 1; # 8000000 $finish(); end always #(P/2) clk <= ~clk; imgROM imgROM(.clk(clk), .rst(rst), .q(q)); // add your instance module reverseq reverseq(.clk(clk), .q(q), .revq(revq)); endmodule module imgROM(input clk, input rst, output [`P_IMGDEPTH-1:0] q); parameter P_maxword = `P_PXADDR'd79999; reg [`P_IMGDEPTH-1:0] mem [0:P_maxword]; reg [`P_PXADDR-1:0] pxADDR; initial begin $readmemh("img.hex", mem, `P_PXADDR'd0, `P_PXADDR'd79999); end always @(posedge clk or negedge rst) begin if(!rst | pxADDR == P_maxword) pxADDR <= `P_PXADDR'd0; else pxADDR <= pxADDR + `P_PXADDR'd1; end assign q = mem[pxADDR]; endmodule
あとはターミナル上でmake
を実行すると自動でGTKWAVEが起動するはずです。
実行結果は以下のようになります。
クロックの周期は\`timescale
を書き換えるかpythonのクラスに渡す因数で調整してください。
もっと効率の良い方法をご存じの方はご教授いただけるとものすごく嬉しいです。