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 = 1sum[1][1] = 0 ^ 0 = 0carry[1][0] = 0 & 1 = 0carry[1][1] = 0 & 0 = 0carry_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 = 1carry[2][0] = 1 & 0 = 0carry_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