2016年9月5日

Verilog HDL: ビットスワップ

SoCの仕事してると、区間によってはエンディアンが揃ってなくて、単にbig/littleが揃ってないだけならまだしも、byte単位ではlittle、word単位でbig、みたいな変則的な並びになってるデータを取り扱うことがよくあり、しかもそれが検証序盤でシステム仕様を巻き込んだ大騒動になったりすることもしばしば。

で、もっと柔軟に入れ替えできないもんかな、と以前ちょっと考えてみた回路を書いてみた。
案外あっさりと動いた。


module bitswap_recursive (
    A,
    Y,
    P
);

parameter P_WIDTH = 64;
parameter P_DEPTH =  3;

parameter P_SPLIT_WIDTH_L =            P_WIDTH/2 ;
parameter P_SPLIT_WIDTH_H = P_WIDTH - (P_WIDTH/2);
parameter P_SPLIT_DEPTH   = P_DEPTH-1;

input  [P_WIDTH-1:0] A;
output [P_WIDTH-1:0] Y;
input  [P_DEPTH-1:0] P;

wire   [P_SPLIT_WIDTH_L-1:0] lower;
wire   [P_SPLIT_WIDTH_H-1:0] higher;

generate begin : gene
    if ( P_DEPTH == 1 ) begin
        assign lower  = A[                          P_SPLIT_WIDTH_L-1:0];
        assign higher = A[P_WIDTH-1:P_SPLIT_WIDTH_L                    ];
    end
    else begin
        bitswap_recursive
        #(  .P_WIDTH( P_SPLIT_WIDTH_L ),
            .P_DEPTH( P_DEPTH-1   )
        )
        ins_lower (
            .A( A[P_SPLIT_WIDTH_L-1:0] ),
            .Y( lower                  ),
            .P( P[P_SPLIT_DEPTH  -1:0] )
        );

        bitswap_recursive 
        #(  .P_WIDTH( P_SPLIT_WIDTH_H ),
            .P_DEPTH( P_DEPTH-1   )
        )
        ins_higher (
            .A( A[P_WIDTH-1:P_SPLIT_WIDTH_L] ),
            .Y( higher                       ),
            .P( P[P_SPLIT_DEPTH-1:0]         )
        );
    end
end //
endgenerate //

assign Y=(P[P_DEPTH-1])? { lower, higher } : { higher, lower };

endmodule //


使い方はこんな感じ。

  • P_WIDTHでデータのビット幅、P_DEPTHで深さを指定。P_WIDTHを(2^P_DEPTH)分割した単位で入れ替えるので、P_WIDTH=64, P_DEPTH=3なら byte単位の入れ替えということになる。 
  • 入れ替えのパタンは 入力ポートPで指定し、MSBが 1であれば 入力ポートAの上半分と下半分を入れ替える。これを再帰的にP_DEPTH=1まで繰り返すだけ。
  • 一応記述では P_WIDTHが2^nに乗っていない場合もカバーしたつもりだけど、まったく未確認、というかたぶん使い道ないのでP_WIDTHは2^nに合わせた方が吉。
  • P_DEPTHは1~log2(P_WIDTH)の範囲で動くはず。これ以上大きくするとビット未満のスワップという意味分からない状態になってしまうはずなので設定禁止。



シミュレータでコンパイル通って、思った通りに動いていることまでは確認したが、これ、論理合成できるの??という。
やりたいことが何となくできちゃったので、ここから先の検証とかやらずこのまま寝かせちゃう。