在系統設計的過程中,經常需要根據輸入數據的值,對相關信號的值進行改變。如果輸入數據的邊界值數量比較少,可以用條件操作符、if...else、case等結構實現。但是如果數據邊界值的數量很多,使用條件操作符和if...else會導致最差情況下的延時增加,使用case會導致代碼量巨大。在這種情況下,使用不同的Verilog代碼可以對系統的資源和速度產生很大影響。
比如在某MP3解碼系統中,存在如下代碼:
always@(Sample_reg_Temp0)
begin
if(Sample_reg_Temp0==0||Sample_reg_Temp0==1)
Is_Exp_Temp=5'b0;
else if(Sample_reg_Temp0==2)
Is_Exp_Temp=5'b00010;
else if(Sample_reg_Temp0==3||Sample_reg_Temp0==4)
Is_Exp_Temp=5'b00011;
else if((Sample_reg_Temp0>4)&&(Sample_reg_Temp0<9))
Is_Exp_Temp=5'b00100;
else if((Sample_reg_Temp0>8)&&(Sample_reg_Temp0<14))
Is_Exp_Temp=5'b00101;
//中間部分省略
else if((Sample_reg_Temp0>2435)&&(Sample_reg_Temp0<4097))
Is_Exp_Temp=5'b10000;//save 2^15
else if((Sample_reg_Temp0>4096)&&(Sample_reg_Temp0<6889))
Is_Exp_Temp=5'b10001;//save 2^14
else
Is_Exp_Temp=5'b10010;//save 2^13
end
由于if...else結構是按照順序的方法對每個條件進行一次判斷,所以如果采用以上代碼,在最差情況下Is_Exp_Temp的數據需要經過17級(中間部分省略)多路選擇器的延時才能有效,這樣會嚴重拖慢整個系統的速度。而且按照上述代碼的寫法,Is_Exp_Temp用到了33個16bit的比較器,也消耗了很多的系統資源。
方法一:二分法
Sample_reg_Temp0是16bit的,所以我們可以通過16級多路選擇器,確定Sample_reg_Temp0的值的范圍,根據其范圍對Is_Exp_Temp進行賦值。這樣最終會產生2^16=65536種可能性,代碼量巨大。但是由于實際中Sample_reg_Temp0只有17個固定的邊界,當Sample_reg_Temp0的值確定在一定范圍內時就可以將該范圍內的數據進行合并。
舉例來說,當Sample_reg_Temp0 >= 6889時Is_Exp_Temp = 5'b10010,而Sample_reg_Temp0[15]==1'b1時,Sample_reg_Temp0的范圍是[32768,65535],所以Sample_reg_Temp0[15]==1'b1時Is_Exp_Temp = 5'b10010,無需再對Sample_reg_Temp0[14:0]的值進行判斷,可以大大減少代碼的編寫量。
采用這種方法,數據輸出需要經過16級多路選擇器的延時,在速度上改進不大,但是這種方法不需要用到比較器,對每一位的判斷都需要一個多路選擇器,而且每一級對下級一數據進行判斷時需要用到2個多路選擇器,增加了多路選擇器的數量,最終的資源占用和實際的數據邊界有很大關系。
方法二:優化哈夫曼樹
通過對代碼的分析,可以得知和Is_Exp_Temp有關的Sample_reg_Temp0邊界值x有以下幾個:
0
1
2
3
5
9
14
23
39
65
108
182
305
513
862
1449
2436
4097
6889
65536
當x[i] =< Sample_reg_Temp0 < x[i+1] (0<i<17)時,Is_Exp_Temp有一個固定的值。所以以邊界值的序號做二分,建立一個哈夫曼樹。然后使用if...else對每一個節點的值進行判斷,最終確定Sample_reg_Temp0的范圍,完成對Is_Exp_Temp的賦值。最終代碼如下所示:
//為了減少占用的版面,去除了所有的begin...end關鍵字,只使用縮進來表示層次關系。
always @(Sample_reg_Temp0)
if(Sample_reg_Temp0 < 182)
if(Sample_reg_Temp0 < 23)
if(Sample_reg_Temp0 < 9)
case(Sample_reg_Temp0) //synopsys parallel_case
0, 1 : Is_Exp_Temp <= 5'b0;
2 : Is_Exp_Temp <= 5'b00010;
3, 4 : Is_Exp_Temp <= 5'b00011;
5, 6, 7, 8 : Is_Exp_Temp <= 5'b00100;
default : Is_Exp_Temp <= 5'b11111;
endcase
else
if(Sample_reg_Temp0 < 14) //9-13
Is_Exp_Temp <= 5'b00101;
else //14-22
Is_Exp_Temp <= 5'b00110;
else
if(Sample_reg_Temp0 < 65)
if(Sample_reg_Temp0 < 39) //23-38
Is_Exp_Temp <= 5'b00111;
else //39-64
Is_Exp_Temp <= 5'b01000;
else
if(Sample_reg_Temp0 < 108) //65-108
Is_Exp_Temp <= 5'b01001;
else //108-181
Is_Exp_Temp <= 5'b01010;
else
if(Sample_reg_Temp0 < 1449)
if(Sample_reg_Temp0 < 513)
if(Sample_reg_Temp0 < 305) //182-304
Is_Exp_Temp <= 5'b01011;
else //305-512
Is_Exp_Temp <= 5'b01100;
else
if(Sample_reg_Temp0 < 862) //513-861
Is_Exp_Temp <= 5'b01101;
else //862-1448
Is_Exp_Temp <= 5'b01110;
else
if(Sample_reg_Temp0 < 4097)
if(Sample_reg_Temp0 < 2436) //1449-2435
Is_Exp_Temp <= 5'b01111;
else //2436-4096
Is_Exp_Temp <= 5'b10000;
else
if(Sample_reg_Temp0 < 6889) //4097-6889
Is_Exp_Temp <= 5'b10001;
else //6889+
Is_Exp_Temp <= 5'b10010;
在對小于9的值進行判斷時,為了減少使用的if...else級數,使用了并行case對有限的幾個值進行進行多路選擇,這樣就可以把if...else的層數控制在4層。
采用以上方法,使用14個16bit比較器、14個雙路選擇器、一個9選擇的多路選擇器,就實現了Is_Exp_Temp的改變,相對于最初的33個比較器,17個雙路選擇器,大大減少了所用的資源,同時將Is_Exp_Temp的有效延時從17級多路選擇器減少到4級,提升了系統的運行速度。