FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chiselで再帰を使ってPopCountモジュールを実装する

https://cdn-ak.f.st-hatena.com/images/fotolife/m/msyksphinz/20170830/20170830023047.png

Arianeの実装を見ていると、PopCountの実装がSystem Verilog再帰を使って実装してあった。

github.com

f:id:msyksphinz:20190820225744p:plain
Popcountの実装
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)
}