Arianeの実装を見ていると、PopCountの実装がSystem Verilogの再帰を使って実装してあった。
module popcount #( parameter int unsigned INPUT_WIDTH = 256, localparam POPCOUNT_WIDTH = $clog2(INPUT_WIDTH)+1 ) ( input logic [INPUT_WIDTH-1:0] data_i, output logic [POPCOUNT_WIDTH-1:0] popcount_o ); ... //Recursive instantiation to build binary adder tree if (INPUT_WIDTH == 2) begin : leaf_node assign left_child_result = padded_input[1]; assign right_child_result = padded_input[0]; end else begin : non_leaf_node popcount #(.INPUT_WIDTH(PADDED_WIDTH / 2)) left_child( .data_i(padded_input[PADDED_WIDTH-1:PADDED_WIDTH/2]), .popcount_o(left_child_result)); popcount #(.INPUT_WIDTH(PADDED_WIDTH / 2)) right_child( .data_i(padded_input[PADDED_WIDTH/2-1:0]), .popcount_o(right_child_result)); end ... endmodule
これはそのままChiselで実装できる。生成できるVerilogは階層化されて出力される。
import chisel3._ import chisel3.util._ import chisel3.Bool class popcount(INPUT_WIDTH:Int = 256) extends Module { val POPCOUNT_WIDTH:Int = log2Ceil(INPUT_WIDTH) + 1 val io = IO(new Bundle { val data_i = Input(UInt(INPUT_WIDTH.W)) val popcount_o = Output(UInt(POPCOUNT_WIDTH.W)) }) val PADDED_WIDTH :Int = 1 << log2Ceil(INPUT_WIDTH) val padded_input = Wire(UInt(PADDED_WIDTH.W)) val left_child_result = Wire(UInt((POPCOUNT_WIDTH-1).W)) val right_child_result = Wire(UInt((POPCOUNT_WIDTH-1).W)) //Zero pad the input to next power of two padded_input := io.data_i //Recursive instantiation to build binary adder tree if (INPUT_WIDTH == 2) { // leaf_node left_child_result := padded_input(1) right_child_result := padded_input(0) } else { // : non_leaf_node val left_child = Module(new popcount (PADDED_WIDTH / 2)) left_child.io.data_i := padded_input(PADDED_WIDTH-1, PADDED_WIDTH/2) left_child_result := left_child.io.popcount_o val right_child = Module(new popcount (PADDED_WIDTH / 2)) right_child.io.data_i := padded_input(PADDED_WIDTH/2-1, 0) right_child_result := right_child.io.popcount_o } //Output assignment io.popcount_o := left_child_result + right_child_result }
Parameterの依存関係を記述するための手法
Verilogでは、以下のような記述によってパラメータの依存関係を記述できる。
module test #( parameter A = 2, parameter B = A * 2 ) ( input logic [A-1:0] in_a, output logic[B-1:0] out_b );
ところが、ChiselではParameterを上記のように依存関係を持って記述するとエラーになる。
class test(A:Int = 2, B:Int = A*2) extends Module { val io = IO(new Bundle { val in_a = Input(UInt(A.W)) val out_b = Output(UInt(B.W)) }) io.out_b := Cat(io.in_a, io.in_b) }
popcount.scala:62:31: not found: value A [error] class test(A:Int = 2, B:Int = A*2) extends Module [error] ^
そこでどうしようか迷ったのだが、冷静に考えるとクラスの宣言とio
の宣言の間に入れればよいのだった。
class test(A:Int = 2) extends Module { val B:Int = A*2 val io = IO(new Bundle { val in_a = Input(UInt(A.W)) val out_b = Output(UInt(B.W)) }) io.out_b := Cat(io.in_a, io.in_a) }