본문 바로가기
디스플레이

[디스플레이] Video Synchronize (Design with Verilog)

by 스테고사우르스 2023. 1. 8.

오늘은 앞서 배운 Video Synchronize VESA timing standard를 참고하여 설계하고자 합니다.

 

Vertical / Horizontal / Active / Front porch / Back porch 등의 단어가 생소하다면

이전 글의 이론 및 VESA timing standard를 읽어보는 것을 추천드려요!

 

이론: https://metastable.tistory.com/21

VESA timing standard: https://metastable.tistory.com/22

 

바로 코드부터 보겠습니다.

 

코드 전체를 보고 싶으신 분은 맨 아래쪽을 참고해주세요.


Verilog Code

 

DUT

`timescale 1ns / 1ps

module vgen(
input             i_clk,
input             i_reset,
input      [11:0] i_vsize,        //vertical active ( vsync == 0)
input      [11:0] i_hsize,        // == de active
input       [7:0] i_vblank_fp,    //front porch
input       [7:0] i_vblank_bp,    //back porch
input       [7:0] i_hblank_fp,
input       [7:0] i_hblank_bp,
input       [7:0] i_vs_width,     //length of ( vsync == 1 )

output reg        o_vsync,
output reg        o_de,
output reg  [7:0] o_red,
output reg  [7:0] o_green,
output reg  [7:0] o_blue
    );

우선 input과 output을 만들어줍니다.

Sync width. Front Porch, Back Porch, V/H Addr 등 VESA spec을 참조할 값들은 parameter처럼 쓸 예정입니다.

 

전자제품은 굉장히 다양한 종류가 있죠?

그런데 이 제품마다 다 다른 칩을 사용한다면 인력 낭비, 자원 낭비가 심할 것입니다.

그래서 하나의 칩이 여러 제품에 탑제될 수 있게 만들어 주는 것이 좋습니다.

이런 것은 보통 EEPROM에 제품 사양에 맞는 값을 넣어두는 것으로 알고 있습니다.

 

저는 EEPROM이 없기 때문에 테스트벤치에서 넣어주었습니다.

 

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        pixel_cnt <= 12'd0;
    end
    else if (pixel_cnt >= i_hsize + i_hblank_fp + i_hblank_bp) begin
        pixel_cnt <= 12'd0;
    end
    else begin
        pixel_cnt <= pixel_cnt + 12'd1;
    end
end

 

가로줄을 세는 counter를 만들어 주었습니다.

이제 이 카운터를 이용해서 DE를 생성할 수 있겠죠?

 

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        dummy_de <= 1'd0;
    end
    else if (pixel_cnt < i_hblank_bp) begin
        dummy_de <= 1'd0;
    end
    else if ( (pixel_cnt >= i_hblank_bp) && (pixel_cnt < i_hsize + i_hblank_bp) ) begin
        dummy_de <= 1'd1;
    end
    else if ( (pixel_cnt >= i_hsize + i_hblank_bp) && (pixel_cnt < i_hsize + i_hblank_bp + i_hblank_fp) ) begin
        dummy_de <= 1'd0;
    end
    else begin
        dummy_de <= dummy_de;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_de <= 1'd0;
    end
    else if (line_cnt < i_vblank_bp + i_vs_width) begin
        o_de <= 1'd0;
    end
    else if ( (line_cnt >= i_vblank_bp + i_vs_width) && (line_cnt < i_vsize + i_vblank_bp + i_vs_width) ) begin
        o_de <= dummy_de;
    end
    else if ( (line_cnt >= i_vsize + i_vblank_bp + i_vs_width) && (line_cnt < i_vsize + i_vblank_bp + i_vblank_fp + i_vs_width) ) begin
        o_de <= 1'd0;
    end
    else begin
        o_de <= o_de;
    end 
end

 

저는 dummy_de를 만들었어요.

vertical porch에서는 de가 출력되지 않아야 하는데, de를 생성했다가 없앴다가 하는 것은 비효율적입니다.

따라서 계속 생성되는 dummy_de를 만들고 필요할 때에만 뽑아 썼습니다.

 

 

DE를 만들었으면 이제 Vsync를 만들 차례입니다.

 

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        line_cnt <= 12'd0;
    end
    else if (line_cnt >= i_vsize + i_vblank_fp + i_vblank_bp + i_vs_width) begin
        line_cnt <= 12'd0;
    end
    else if (de_fall == 1'd1) begin
        line_cnt <= line_cnt + 12'd1;
    end
    else begin
        line_cnt <= line_cnt;
    end
end

 

Vsync의 한 주기에는 화면이 한 장 출력 됩니다.

 

라인 수를 세어주어서 주기를 정해주어야 합니다.

 

역시 카운터를 만들어서 라인 수를 측정했습니다.

 

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_vsync <= 1'd0;
    end
    else if (line_cnt < i_vs_width) begin
        o_vsync <= 1'd1;
    end
    else if (line_cnt >= i_vs_width) begin
        o_vsync <= 1'd0;
    end
    else begin
        o_vsync <= o_vsync;
    end
end

카운터 값을 기반으로 Vsync를 생성하였습니다.

 

 

Vsync와 DE를 만들었으면 이제 실제로 출력할 데이터를 넣어주어야겠죠?

 

 

저는 따로 넣어 줄 이미지가 없어서, 그냥 1 씩 증가하는 gray 패턴을 이용했습니다.

 

다양한 패턴을 출력해보면서 카운터에 대한 감을 익히시는 걸 추천드려요!

 

 

DUT는 이렇게 구성 할 수 있고 이제 Testbench를 보겠습니다.

 

 

 

Testbench

Testbench는 간단하게 구성할 수 있습니다.

 

우선 Testbench에 넣어 줄 VESA의 timing standard입니다.

 

VESA timing standard FHD 60Hz

FHD 60Hz 기준 VESA timing standard 입니다.

 

FHD 60Hz 모니터를 사용한다고 가정하고 Sync 값들을 넣어주겠습니다.

 

`timescale 1ns / 1ps

module tb_vgen();

reg              i_clk;
reg              i_reset;
reg     [11:0]   i_vsize;
reg     [11:0]   i_hsize;
reg      [7:0]   i_vblank_fp;
reg      [7:0]   i_vblank_bp;
reg      [7:0]   i_hblank_fp;
reg      [7:0]   i_hblank_bp;
reg      [7:0]   i_vs_width;

wire             o_vsync;
wire             o_de;
wire     [7:0]   o_red;
wire     [7:0]   o_green;
wire     [7:0]   o_blue;

vgen vgen      ( .i_clk           (i_clk)        ,
                 .i_reset         (i_reset)      ,
                 .i_vsize         (i_vsize)      ,
                 .i_hsize         (i_hsize)      ,
                 .i_vblank_fp     (i_vblank_fp)  ,
                 .i_vblank_bp     (i_vblank_bp)  ,
                 .i_hblank_fp     (i_hblank_fp)  ,
                 .i_hblank_bp     (i_hblank_bp)  ,
                 .i_vs_width      (i_vs_width)   ,
            
                 .o_vsync         (o_vsync)      ,
                 .o_de            (o_de)         ,
                 .o_red           (o_red)        ,
                 .o_green         (o_green)      ,
                 .o_blue          (o_blue)       );
            
            
always #5 i_clk = ~i_clk;
initial begin
    i_clk = 1'd0; i_reset = 1'd1;
    i_hsize = 12'd1920;
    i_vsize = 12'd1080;
    i_hblank_fp = 8'd40;
    i_hblank_bp = 8'd40;
    i_vblank_fp = 8'd17;
    i_vblank_bp = 8'd6;
    i_vs_width = 8'd8;                 #100_0000
    i_reset = 1'd0;                    #100_0000
    i_reset = 1'd1;                    #3000_0000  //~~30ms
    
    $finish;
end

endmodule

저는 Hsync를 따로 만들지 않았기 때문에 Hor Sync 값을 Hblank FP에 합쳐서 넣어줬습니다.

 

 

이렇게 시간을 설정하고 시뮬레이션을 돌리면 결과가 나옵니다.

 

 

Simulation

 

 

세어보면 Vsync 한 주기 안에 1080개의 DE가 출력되고 있습니다.

 

 

조금 더 확대해 볼까요?

Ver Sync width는 8 line

Ver blank back porch는 6 line 으로 설정해주었습니다.

 

정확히 line_cnt가 0~13인 동안은 blank 구간이고,

14부터 DE가 출력되는 것을 볼 수 있습니다.

 

 

이렇게 Video Sync를 Verilog로 간단하게 구현해보았습니다.

 

실제로 사용할 때는 더 복잡하겠지만 기초부터 차근차근 한다면 훌륭한 설계자가 될 수 있다고 생각합니다.

 


전체 Verilog Code

`timescale 1ns / 1ps

module vgen(
input             i_clk,
input             i_reset,
input      [11:0] i_vsize,        //vertical active ( vsync == 0)
input      [11:0] i_hsize,        // == de active
input       [7:0] i_vblank_fp,    //front porch
input       [7:0] i_vblank_bp,    //back porch
input       [7:0] i_hblank_fp,
input       [7:0] i_hblank_bp,
input       [7:0] i_vs_width,     //length of ( vsync == 1 )

output reg        o_vsync,
output reg        o_de,
output reg  [7:0] o_red,
output reg  [7:0] o_green,
output reg  [7:0] o_blue
    );
    
reg [11:0] pixel_cnt;             //count pixel == count horizontal
reg [11:0] line_cnt;              //count line == count vertical

reg dummy_de;
reg de_delay;

assign de_fall = (~dummy_de) & de_delay;

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        de_delay <= 1'd0;
    end
    else begin
        de_delay <= dummy_de;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        pixel_cnt <= 12'd0;
    end
    else if (pixel_cnt >= i_hsize + i_hblank_fp + i_hblank_bp) begin
        pixel_cnt <= 12'd0;
    end
    else begin
        pixel_cnt <= pixel_cnt + 12'd1;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        line_cnt <= 12'd0;
    end
    else if (line_cnt >= i_vsize + i_vblank_fp + i_vblank_bp + i_vs_width) begin
        line_cnt <= 12'd0;
    end
    else if (de_fall == 1'd1) begin
        line_cnt <= line_cnt + 12'd1;
    end
    else begin
        line_cnt <= line_cnt;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        dummy_de <= 1'd0;
    end
    else if (pixel_cnt < i_hblank_bp) begin
        dummy_de <= 1'd0;
    end
    else if ( (pixel_cnt >= i_hblank_bp) && (pixel_cnt < i_hsize + i_hblank_bp) ) begin
        dummy_de <= 1'd1;
    end
    else if ( (pixel_cnt >= i_hsize + i_hblank_bp) && (pixel_cnt < i_hsize + i_hblank_bp + i_hblank_fp) ) begin
        dummy_de <= 1'd0;
    end
    else begin
        dummy_de <= dummy_de;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_de <= 1'd0;
    end
    else if (line_cnt < i_vblank_bp + i_vs_width) begin
        o_de <= 1'd0;
    end
    else if ( (line_cnt >= i_vblank_bp + i_vs_width) && (line_cnt < i_vsize + i_vblank_bp + i_vs_width) ) begin
        o_de <= dummy_de;
    end
    else if ( (line_cnt >= i_vsize + i_vblank_bp + i_vs_width) && (line_cnt < i_vsize + i_vblank_bp + i_vblank_fp + i_vs_width) ) begin
        o_de <= 1'd0;
    end
    else begin
        o_de <= o_de;
    end 
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_vsync <= 1'd0;
    end
    else if (line_cnt < i_vs_width) begin
        o_vsync <= 1'd1;
    end
    else if (line_cnt >= i_vs_width) begin
        o_vsync <= 1'd0;
    end
    else begin
        o_vsync <= o_vsync;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_red <= 8'd0;
    end
    else if (o_de == 1'd0) begin
        o_red <= 8'd0;
    end
    else if (o_de == 1'd1) begin
        o_red <= o_red + 8'd1;              //gray pattern
    end
    else begin
        o_red <= o_red;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_green <= 8'd0;
    end
    else if (o_de == 1'd0) begin
        o_green <= 8'd0;
    end
    else if (o_de == 1'd1) begin
        o_green <= o_green + 8'd1;            //gray pattern
    end
    else begin
        o_green <= o_green;
    end
end

always @ (posedge i_clk or negedge i_reset) begin
    if (!i_reset) begin
        o_blue <= 8'd0;
    end
    else if (o_de == 1'd0) begin
        o_blue <= 8'd0;
    end
    else if (o_de == 1'd1) begin
        o_blue <= o_blue + 8'd1;                //gray pattern
    end
    else begin
        o_blue <= o_blue;
    end
end
    
endmodule

댓글