오늘은 앞서 배운 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입니다.
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
'디스플레이' 카테고리의 다른 글
[디스플레이] 감마 보정 (Gamma Correction) (0) | 2023.03.11 |
---|---|
[디스플레이] VESA timing standard (0) | 2023.01.08 |
[디스플레이] Video Synchronize (Vsync, Hsync, DE) (2) | 2023.01.07 |
[디스플레이] 주사율 (Refresh Rate) (1) | 2023.01.07 |
댓글