FPGA開発日記

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

Common Cellsのcc_onehotの実装詳細解析:One-Hotの検出回路

github.com

module cc_onehot #(
  parameter int unsigned Width = 4
) (
  input  logic [Width-1:0] d_i,
  output logic is_onehot_o
);
...
    localparam int LVLS = $clog2(Width) + 1;

    logic [LVLS-1:0][2**(LVLS-1)-1:0] sum, carry;
    logic [LVLS-2:0] carry_array;

基本的な設計方針としては、ビットwiseのリダクション加算をしていくと、one-hotであればsum=1, carry=0となるはず。そうでなければsum=0, carry=0, もしくは carry=1となるはず。

Width=4の場合のパラメータ

Width = 4
LVLS = $clog2(4) + 1 = 2 + 1 = 3

配列サイズ:

  • sum[2:0][3:0] (3レベル × 4ビット)
  • carry[2:0][3:0]
  • carry_array[1:0] (2ビット)

レベルごとの動作

レベル0 (入力レベル)

入力データをそのままsum[0]に割り当てる

    // Extend to a power of two.
    assign sum[0] = $unsigned(d_i);
    
    // 例: d_i = 4'b0010 の場合、sum[0] = 4'b0010

レベル1 (i=1)

まずは隣同士との半加算を行い、sum と carryを作っていく。4ビット列だったのが、2ビットに集約される。

LVLWidth = 2**3 / 2**1 = 4

sum[1][0] = sum[0][0] ^ sum[0][1]
sum[1][1] = sum[0][2] ^ sum[0][3]

carry[1][0] = sum[0][0] & sum[0][1]
carry[1][1] = sum[0][2] & sum[0][3]

// キャリー集約:
carry_array[0] = |carry[1][1:0]

例: d_i = 4'b0010 の場合

  • sum[1][0] = 0 ^ 1 = 1
  • sum[1][1] = 0 ^ 0 = 0
  • carry[1][0] = 0 & 1 = 0
  • carry[1][1] = 0 & 0 = 0
  • carry_array[0] = 0 | 0 = 0

レベル2 (i=2)

さらに隣同士との半加算を行う。sumの場合は

LVLWidth = 2**3 / 2**2 = 2

****sum[2][0] = sum[1][0] ^ sum[1][1]
carry[2][0] = sum[1][0] & sum[1][1]

**// キャリー集約**
carry_array[1] = |carry[2][0:0] = carry[2][0]

例: d_i = 4'b0010 の場合

  • sum[2][0] = 1 ^ 0 = 1
  • carry[2][0] = 1 & 0 = 0
  • carry_array[1] = 0

最終出力

sum[2][0] は、すべての入力ビットのXORをとっている。つまり1のビット数が奇数個。さらにすべてのレベルでの carry_array が0となっているはず。つまりすべてのレベルでキャリーが発生していない。

**is_onehot_o = sum[LVLS-1][0] & ~|carry_array
              sum[2][0] & ~(carry_array[0] | carry_array[1])**

例: d_i = 4'b0010 の場合

  • is_onehot_o = 1 & ~(0 | 0) = 1 & 1 = 1