Try   HackMD

RTL 實作 One-Hot to Binary Converter

這篇我們要講解用 RTL 實作一個轉換器將 one-hot code 轉成 binary number。

Definition

首先先定義這個轉換器的 interface,主要就是輸入一個 ONEHOT_WIDTH 長度的 one-hot code onehot_i (onehot_i 一定會有一個 bit 是 1) 然後輸出轉換後的 binary number binary_o,它的 bit 數為 BIN_WIDTH

module onehot2bin_v1 #( parameter ONEHOT_WIDTH = 16, parameter BIN_WIDTH = ONEHOT_WIDTH == 1 ? 1 : $clog2(ONEHOT_WIDTH) )( input [ONEHOT_WIDTH-1 : 0] onehot_i, output reg [BIN_WIDTH-1 : 0] binary_o ); endmodule

Design 1

第一種設計很直覺,其實就像下面的邏輯電路,我們只需要一個 MUX 它的輸入是 0 到 ONEHOT_WIDTH-1 然後我們就看 onehot_i 的第幾個 bit 是 1 就選擇對應的數字輸出給 binary_o 就行了。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

因為我們想要將這個轉換器能夠處理的 one-hot code 的長度參數化 (ONEHOT_WIDTH),所以 RTL 的做法是用一個 for loop 來選擇要給 binary_o 的值,程式碼如下:

module onehot2bin_v1 #( parameter ONEHOT_WIDTH = 16, parameter BIN_WIDTH = ONEHOT_WIDTH == 1 ? 1 : $clog2(ONEHOT_WIDTH) )( input [ONEHOT_WIDTH-1 : 0] onehot_i, output reg [BIN_WIDTH-1 : 0] binary_o ); integer i; always @(*) begin binary_o = 0; for (i=0; i<ONEHOT_WIDTH; i=i+1) begin if (onehot_i[i]) binary_o = i; end end endmodule

Design 2

第二種設計我是從 pulp-platform 裡的 common_cells 這個 repo 看到的,這個設計只用到邏輯閘 AND 跟 OR。

這個設計的思維是,我要知道要輸出的 binary num 的每一個 bit 是 0 或 1。那要如何知道呢? 我們用 one-hot code 長度為 8 來舉例。

One-Hot code 長度為 8 代表輸出的 binary num 可能為 0 到 7,下面我們列出每個值的十進制跟二進制對照表

十進制  二進制
0    -> 000
1    -> 001
2    -> 010
3    -> 011
4    -> 100
5    -> 101
6    -> 110
7    -> 111

若要知道每個 binary num 的每個 bit 是 0 或 1,我們需要對每個上面列的把每個十進制值對應的二進制的每個 bit 做成 mask 然後跟 one_hot_i 做 bit-wise AND,每個 bit 的 mask 如下面所示:

bit-2 mask = 11110000
bit-1 mask = 11001100
bit-0 mask = 10101010

        bit-2 mask  bit-1 mask  bit-0 mask
0    ->      0    |      0    |      0     
1    ->      0    |      0    |      1     
2    ->      0    |      1    |      0     
3    ->      0    |      1    |      1     
4    ->      1    |      0    |      0     
5    ->      1    |      0    |      1     
6    ->      1    |      1    |      0     
7    ->      1    |      1    |      1     

為什麼要這麼做呢? 因為我們只需要知道 onehot_i 第 i 個 bit 為 1 對應的值的二進制的每個 bit 是 0 或 1。

假設 onehot_i = 8'b00001000 也就是 bit-3 為 1 的話,它跟每個 bit 的 mask 做 bit-wise AND,我們可以預期會得到下面這種結果

                              _
bit-3 mask & onehot_i => 0000|0|000
bit-1 mask & onehot_i => 0000|1|000
bit-0 mask & onehot_i => 0000|1|000
                              ^
                      onehot_i 對應的 binary num

我們只留下 bit-3 為 1 對應的值的二進制,其他 bit 都是 0。

最後我們把每個 bit-wise AND 的結果做 logic OR 就可以知道對應的 bit 是 0 或 1。

完整的程式碼如下:

module onehot2bin_v2 #( parameter ONEHOT_WIDTH = 16, parameter BIN_WIDTH = ONEHOT_WIDTH == 1 ? 1 : $clog2(ONEHOT_WIDTH) )( input [ONEHOT_WIDTH-1 : 0] onehot_i, output reg [BIN_WIDTH-1 : 0] binary_o ); reg [BIN_WIDTH-1 : 0] bin_num; reg [ONEHOT_WIDTH-1 : 0] mask; integer i, j; always @(*) begin for (i=0; i<BIN_WIDTH; i=i+1) begin for (j=0; j<ONEHOT_WIDTH; j=j+1) begin bin_num = j; mask[j] = bin_num[i]; // transpose end binary_o[i] = |(mask & onehot_i); end end endmodule