這篇我們要講解用 RTL 實作一個轉換器將 one-hot code 轉成 binary number。
首先先定義這個轉換器的 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
第一種設計很直覺,其實就像下面的邏輯電路,我們只需要一個 MUX 它的輸入是 0 到 ONEHOT_WIDTH-1
然後我們就看 onehot_i 的第幾個 bit 是 1 就選擇對應的數字輸出給 binary_o 就行了。
因為我們想要將這個轉換器能夠處理的 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
第二種設計我是從 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