• (转载)如何使用Verilog將YCbCr轉RGB? (SOC) (Verilog) (DE270)


    Abstract
    由於不少視訊設備採用的是YCbCr信號(如DVD、DV),但VGA使用的RGB信號,若要將input結果經過影像處理後,output在VGA,就必須將YCbCr信號轉成RGB才可處理。

    Introduction
    最近正在研究DE2-70 CD中的DE2_70_TV範例,由於DE2-70_TV的input是YCbCr信號,需要轉成RGB信號後才能輸出到VGA,範例中提供了YCbCr2RGB.v提供轉換,小美與阿帥研究了很久,還是不太了解其中的意義,最後決定請教Lab中玩DE2-70很久的無雙學長,想徹底了解其中的奧秘。

    『我知道YCbCr轉RGB的公式如下:』

    R = 1.164(Y-16) + 1.596(Cr-128)
    G
    = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128
    )
    B
    = 1.164(Y-16) + 2.018(Cb-128)


    『但整個YCbCr2RGB.v看起來與這個公式差很遠,到底Verilog是怎麼辦到的呢?』小美一臉無助地發問。
    YCbCr2RGB.v / Verilog

    1 module YCbCr2RGB (
    2   input
            iCLK,
    3   input
            iRESET,
    4   input
            iDVAL,
    5   input  [7:0
    ] iY,
    6   input  [7:0
    ] iCb,
    7   input  [7:0
    ] iCr,
    8   output reg
       oDVAL,
    9   output [9:0
    ] Red,
    10   output [9:0
    ] Green,
    11   output [9:0
    ] Blue
    12 
    );
    13 

    14 // Internal Registers/Wires
    15 reg  [9:0]  oRed,oGreen,oBlue;
    16 reg  [3:0
    ]  oDVAL_d;
    17 reg  [19:0
    ] X_OUT,Y_OUT,Z_OUT;
    18 wire [26:0
    ] X,Y,Z;
    19 

    20 assign Red   = oRed;
    21 assign Green =
    oGreen;
    22 assign Blue  =
    oBlue;
    23 

    24 always@(posedge iCLK) begin
    25   if (iRESET) begin
    26     oDVAL   <= 0;
    27     oDVAL_d <= 0
    ;
    28     oRed    <= 0
    ;
    29     oGreen  <= 0
    ;
    30     oBlue   <= 0
    ;
    31   end

    32   else begin
    33     // Red
    34     if (X_OUT[19])
    35       oRed <= 0
    ;
    36     else if (X_OUT[18:0] > 1023
    )
    37       oRed <= 1023
    ;
    38     else

    39       oRed <= X_OUT[9:0];
    40 

    41     // Green
    42     if (Y_OUT[19])
    43       oGreen<=0
    ;
    44     else if (Y_OUT[18:0] > 1023
    )
    45       oGreen<=1023
    ;
    46     else

    47       oGreen<=Y_OUT[9:0];
    48 
         
    49     // Blue

    50     if (Z_OUT[19])
    51       oBlue<=0
    ;
    52     else if (Z_OUT[18:0] > 1023
    )
    53       oBlue<=1023
    ;
    54     else

    55       oBlue<=Z_OUT[9:0];
    56 

    57     // Control
    58     {oDVAL, oDVAL_d} <= {oDVAL_d, iDVAL};
    59   end

    60 end
    61 
    62 always@(posedge iCLK) begin
    63   if (iRESET) begin
    64     X_OUT <= 0;
    65     Y_OUT <= 0
    ;
    66     Z_OUT <= 0
    ;
    67   end

    68   else begin
    69     X_OUT <= ( X - 114131 ) >>7;
    70     Y_OUT <= ( Y + 69370  ) >>7
    ;
    71     Z_OUT <= ( Z - 141787 ) >>7
    ;
    72   end

    73 end
    74 
    75 // Y 596, 0, 817
    76 MAC_3 u0 (
    77 
      iY, iCb, iCr,
    78   17'h00254, 17'h00000, 17'h00331,

    79   X, iRESET, iCLK
    80 
    );
    81 
     
    82 // Cb 596, -200, -416

    83 MAC_3 u1 (
    84 
      iY, iCb, iCr,
    85   17'h00254, 17'h3FF38, 17'h3FE60,

    86   Y, iRESET, iCLK
    87 
    );
    88 

    89 // Cr 596, 1033, 0
    90 MAC_3 u2 (
    91 
      iY, iCb, iCr,
    92   17'h00254, 17'h00409, 17'h00000,

    93   Z, iRESET, iCLK
    94 
    );
    95 

    96 endmodule


    『對啊!!尤其69行更詭異,114131、69370、141787是怎麼來的?公式裡完全沒出現這3個數字,而且為什麼最後又要 >> 7呢?』阿帥在旁邊也趕緊附和。

    69行

    X_OUT <= ( X - 114131 ) >>7;
    Y_OUT
    <= ( Y + 69370  ) >>7
    ;
    Z_OUT
    <= ( Z - 141787 ) >>7;


    無雙學長最近正埋首幫教授編寫DE2-70教材與畢業論文,不過看到小美與阿帥的好學,心裡還是非常感動,就打算暫停手邊的工作,先解決學弟妹的問題。

    『這個問題很多人問過我,主要是因為YCbCr轉RGB的公式牽涉到浮點運算,在Verilog並不容易實現,所以使用了一些技巧重新推導了整個公式,我在(原創) 如何計算浮點數? (SOC) (Verilog)曾經提過這個技巧,當時也有考慮直接拿YCbCr2RGB.v來解講,不過怕這個公式太複雜而模糊了焦點,所以才另外寫了攝氏溫度轉華氏溫度的範例,但在實務上,YCbCr轉RGB應該是最多人第一次遇到浮點運算的機會,趁這個機會,我就把整個module講解一次。』

    無雙學長拿了一張紙,準備將公式重新推導一次。

    『這是原本YCbCr轉RGB的公式,我們可以發現這需要浮點運算,Verilog雖然有real型別,但只能用在testbench,不能用在RTL。』

    R = 1.164(Y-16) + 1.596(Cr-128)
    G
    = 1.164(Y-16) - 0.391(Cb-128) - 0.813(Cr-128
    )
    B
    = 1.164(Y-16) + 2.018(Cb-128)


    『現在將整個算式展開』

    R = 1.164Y                + 1.596Cr - 222.912
    G
    = 1.164Y - 0.391Cb - 0.813Cr + 135.488
    B
    = 1.164Y + 2.018Cb                - 276.928


    『因為Verilog無法處理浮點數,所以我們打算使用(原創) 如何計算浮點數? (SOC) (Verilog)提到的技巧:將整個算式先放大處理,最後再縮小,現在將算式左右兩邊放大512倍,也就是2^9,相當於<<9。』

    R >> 9 = 596Y          + 817Cr - 114131
    G >>
    9 = 596Y - 200Cb  - 416Cr + 69370
    B >>
    9 = 596Y + 1033Cb         - 141787


    『為什麼要*512呢?不可以乘其他數字嗎?』細心的小美馬上發現問題。

    『當然可以,你也可以*1024,也就是<<10,只要最後記得>>10還原就好。』

    『114131、69370、141787這些整數都出現了耶,為什麼75行要使用megafunction呢?』阿帥又發現了另外一個問題。

    75行

    // Y 596, 0, 817
    MAC_3 u0 (
      iY, iCb, iCr,
     
    17'h00254, 17'h00000, 17'h00331,

      X, iRESET, iCLK
    );
     
    // Cb 596, -200, -416

    MAC_3 u1 (
      iY, iCb, iCr,
     
    17'h00254, 17'h3FF38, 17'h3FE60,

      Y, iRESET, iCLK
    );

    // Cr 596, 1033, 0

    MAC_3 u2 (
      iY, iCb, iCr,
     
    17'h00254, 17'h00409, 17'h00000,

      Z, iRESET, iCLK
    );


    『596 * Y + 817 * Cr這種乘加運算,當然也可以自己用Verilog寫,並自己處理pipeline以提高Fmax,DE2-70 CD中的範例有個特色,就是工程師對Altera的megafunction相當的熟悉,CD範例中到處可看到工程師巧妙使用megafunction之處,這裡使用了ALTMULT_ADD做乘加,透過megawizard設定就可自動幫你處理pipeline了。阿帥啊,你也自己用Verilog而不用megafunction寫寫看當練習。』

    『69行為什麼要減114131呢?又為什麼要>>7呢?』這是小美原本百思不解之處。

    69行

    X_OUT <= ( X - 114131 ) >>7;
    Y_OUT
    <= ( Y + 69370  ) >>7
    ;
    Z_OUT
    <= ( Z - 141787 ) >>7;


    『根據新推導的算式:R >> 9 = 596Y + 817Cr - 114131,X已經MAC_3算出來了,所以還要減去114131才行,至於 >> 7,這是個簡略的寫法,我將完整寫法還原,你們就一目了然了』。

    X_OUT <= (( X - 114131 ) >> 9) << 2;
    Y_OUT
    <= (( Y + 69370  ) >> 9) << 2
    ;
    Z_OUT
    <= (( Z - 141787 ) >> 9) << 2;


    『原本算式經過 <<9 放大,最後要用 >>9 還原,這很合理,但別忘了output [9:0] Red是10 bit,而input  [7:0] iY是8 bit,所以最後還得放大 <<2 才行,一來一往就變成 >> 7了』。

    『啊!!原來是這樣啊!!』小美恍然大悟。

    『這樣看起來就很完美了,但為什麼34行還要對output做些判斷呢?』阿帥果然思考敏捷,立刻又提出了新的問題。

    34行

    // Red
    if (X_OUT[19])
      oRed
    <= 0
    ;
    else if (X_OUT[18:0] > 1023
    )
      oRed
    <= 1023
    ;
    else

      oRed
    <= X_OUT[9:0];

    // Green

    if (Y_OUT[19])
      oGreen
    <=0
    ;
    else if (Y_OUT[18:0] > 1023
    )
      oGreen
    <=1023
    ;
    else

      oGreen
    <=Y_OUT[9:0];
         
    // Blue

    if (Z_OUT[19])
      oBlue
    <=0
    ;
    else if (Z_OUT[18:0] > 1023
    )
      oBlue
    <=1023
    ;
    else

      oBlue
    <=Z_OUT[9:0];


    『由於做了放大再縮小的運算,難免會造成overflow的狀況,所以最後多加了判斷,若大於1023,就當1023記,若小於0,就當成0,這樣結果才合理。』

    『無雙學長,我還有最後一個問題,為什麼在DE2-70的範例常常看到58行這種寫法,這倒底是什麼意思呢?』這也是小美困惑很久的問題。

    58行

    // Control
    {oDVAL, oDVAL_d} <= {oDVAL_d, iDVAL};


    『這是友晶工程師一種技巧的寫法,讓code可以一行完成,看起來比較精簡,學弟妹們可以將這種技巧學起來,事實上它就相當於以下寫法。』

    oDVAL_d <= iDVAL;
    oDVAL  
    <= oDVAL_d;


    『因為多了一些運算,所以希望data valid的信號多一個clock做delay,若寫成兩行大家就都能看的懂了。』無雙學長做了以上的解釋。

    『謝謝學長,我總算看懂整個module在幹什麼了。』

    Conclusion
    無雙學長最後做了以下結論:『演算法多多少少都會牽涉到浮點運算,不可能剛剛好都是整數,夠過這個技巧,就能將很多演算法從C語言改用Verilog實現,並配合硬體的parallel、concurrent、pipeline等特性做硬體加速。』

    完整程式碼下載见無雙大哥的博客

    原始网页:http://www.cnblogs.com/oomusou/archive/2008/12/09/verilog_ycrcb2rgb.html#commentform

  • 相关阅读:
    Invalid bound statement (not found)解决方法
    MySQL的sum()函数
    关于Mybatis的java.lang.UnsupportedOperationException异常处理
    博客迁移
    Building Blog(个性化博客)2
    走一波服务器
    JZ高中OJ 1036. [SCOI2009]迷路
    JZ初中OJ 2296. [noip普及组2]神殿
    JZ初中OJ 2295. [noip普及组2]栈
    JZ初中OJ 2298. [noip普及组2]异或
  • 原文地址:https://www.cnblogs.com/just4fun/p/1931165.html
Copyright © 2020-2023  润新知