๐Ÿ“บ ็ฌฌ2่ฏพ๏ผšVGAไฟกๅท

็†่งฃVGAๆ—ถๅบ็š„็ง˜ๅฏ†โ€”โ€”ๅƒ็ด ๆ—ถ้’Ÿใ€ๅŒๆญฅ่„‰ๅ†ฒใ€ๆถˆ้šๅŒบ

๐Ÿ† ๆˆๅฐฑ๏ผšไฟกๅท็ŒŽๆ‰‹ โœ… Verilator้ชŒ่ฏ

๐Ÿ“บ
ไฟกๅท็ŒŽๆ‰‹
Verilatorไปฟ็œŸ่พ“ๅ‡บๅฎŒๆ•ด็š„VGAๆ—ถๅบๆณขๅฝข

๐Ÿ“ก VGAๆ˜ฏไป€ไนˆ๏ผŸ

VGAๅชๆœ‰5ๆ น็บฟ๏ผšR/G/Bไธ‰่‰ฒๆจกๆ‹Ÿไฟกๅท + HSYNCๆฐดๅนณๅŒๆญฅ + VSYNCๅž‚็›ดๅŒๆญฅใ€‚ๆ˜พ็คบๅ™จไปŽๅทฆไธŠ่ง’้€ๅƒ็ด ๆ‰ซๆ๏ผŒ็”ปๅฎŒไธ€่กŒๆขไธ‹ไธ€่กŒ๏ผŒ็”ปๅฎŒไธ€ๅธงๅ›žๅˆฐๅทฆไธŠ่ง’้‡ๆ–ฐๅผ€ๅง‹ใ€‚

๐Ÿ“ 640ร—480@60Hz ๆ—ถๅบๅ‚ๆ•ฐ

ๅ‚ๆ•ฐๆฐดๅนณ๏ผˆ่กŒ๏ผ‰ๅž‚็›ด๏ผˆๅธง๏ผ‰
ๅฏ่งๅƒ็ด /่กŒ640480
ๅŒๆญฅ่„‰ๅ†ฒ962
ๅŽ่‚ฉ4833
ๅ‰่‚ฉ1610
ๆ€ป่ฎก800525

ๅธง็އ้ชŒ่ฏ๏ผš25MHz รท (800 ร— 525) โ‰ˆ 59.52Hz โ‰ˆ 60Hz โœ“

๐Ÿ“ Verilogๅฎž็Žฐ๏ผšVGAๆ—ถๅบๅ‘็”Ÿๅ™จ

Verilogvga_sync.v โ€” VGAๆ—ถๅบๅ‘็”Ÿๅ™จ
module vga_sync (
    input  wire clk,        // 25MHzๅƒ็ด ๆ—ถ้’Ÿ
    input  wire rst_n,
    output reg  hsync, vsync,
    output reg  [9:0] hcount, vcount,
    output wire video_on
);
    localparam H_TOTAL=800, H_VISIBLE=640, H_FRONT=16, H_SYNC=96, H_BACK=48;
    localparam V_TOTAL=525, V_VISIBLE=480, V_FRONT=10, V_SYNC=2,  V_BACK=33;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) hcount <= 0;
        else if (hcount == H_TOTAL-1) hcount <= 0;
        else hcount <= hcount + 1;
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) vcount <= 0;
        else if (hcount == H_TOTAL-1) begin
            if (vcount == V_TOTAL-1) vcount <= 0;
            else vcount <= vcount + 1;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) hsync <= 1;
        else hsync <= ~((hcount >= H_VISIBLE+H_FRONT) &&
                        (hcount < H_VISIBLE+H_FRONT+H_SYNC));
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) vsync <= 1;
        else vsync <= ~((vcount >= V_VISIBLE+V_FRONT) &&
                        (vcount < V_VISIBLE+V_FRONT+V_SYNC));
    end

    assign video_on = (hcount < H_VISIBLE) && (vcount < V_VISIBLE);
endmodule

๐Ÿง  ๆฆ‚ๅฟตๆฃ€ๆŸฅๆธ…ๅ•

โœ… VGAๆœ‰ๅ“ช5ๆ นไฟกๅท็บฟ๏ผŸ
โœ… ไธบไป€ไนˆ640ร—480้œ€่ฆ800ร—525็š„ๆ—ถๅบ๏ผŸ
โœ… HSYNCๅ’ŒVSYNCๆ˜ฏ้ซ˜ๆœ‰ๆ•ˆ่ฟ˜ๆ˜ฏไฝŽๆœ‰ๆ•ˆ๏ผŸ
โœ… ๅธง็އๅฆ‚ไฝ•่ฎก็ฎ—๏ผŸ

๐Ÿ“ VGAๆ—ถๅบ่ฏฆ็ป†ๅ›พ่งฃ

ไธ€่กŒๆ—ถๅบ (ๆฐดๅนณๆ–นๅ‘๏ผŒๅ…ฑ800ๅƒ็ด ๆ—ถ้’Ÿ)๏ผš โ”Œโ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๅฏ่งๅŒบๅŸŸ 640ๅƒ็ด  โ”‚ โ”‚โ–ผโ–ผโ”‚ โ—„โ”€โ”€ๅŽ่‚ฉ48โ”€โ”€โ–บ โ”‚ โ”‚ โ”‚HSโ”‚ โ”‚ โ”‚ โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€ |โ—„96โ–บ|โ—„โ”€โ”€48โ”€โ”€โ–บ|โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€640โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ|โ—„16โ–บ| ่„‰ๅ†ฒ ๅŽ่‚ฉ ๆ˜พ็คบๅŒบๅŸŸ ๅ‰่‚ฉ |โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 800ๅƒ็ด ๆ—ถ้’Ÿ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ| ไธ€ๅธงๆ—ถๅบ (ๅž‚็›ดๆ–นๅ‘๏ผŒๅ…ฑ525่กŒ)๏ผš โ”Œโ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚VSโ”‚ โ—„โ”€โ”€ๅŽ่‚ฉ33โ”€โ”€โ–บ โ”‚ ๅฏ่งๅŒบๅŸŸ 480่กŒ โ”‚ โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€ |โ—„2โ–บ|โ—„โ”€โ”€33โ”€โ”€โ–บ|โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€480โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ|โ—„10โ–บ| ่„‰ๅ†ฒ ๅŽ่‚ฉ ๆ˜พ็คบๅŒบๅŸŸ ๅ‰่‚ฉ |โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 525่กŒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ| ๆถˆ้šๅŒบไธๆ˜ฏๆตช่ดน๏ผๅฏไปฅๅœจๆถˆ้šๆœŸๆ›ดๆ–ฐๅŒ็ผ“ๅ†ฒใ€ๆ‰ง่กŒๆธธๆˆ้€ป่พ‘ใ€‚

๐Ÿงช VerilatorๅฎŒๆ•ดๆต‹่ฏ•ๅฐ

SystemVerilogvga_sync_tb.sv โ€” ๅฎŒๆ•ดๆ—ถๅบ้ชŒ่ฏ
module vga_sync_tb;
    logic clk,rst_n; logic hsync,vsync,video_on;
    logic [9:0] hcount,vcount;
    vga_sync uut(.*);
    initial clk=0; always #20 clk=~clk; // 25MHz

    // HSYNCไฝŽ่„‰ๅ†ฒๅฎฝๅบฆ็ปŸ่ฎก
    int hsync_low, hsync_edges;
    logic hsync_prev;
    always @(posedge clk) begin
        hsync_prev<=hsync;
        if(!hsync) hsync_low++;
        else if(hsync&&!hsync_prev&&hsync_low>0) begin
            if(hsync_low!=96) $display("FAIL:HSYNC=%0d",hsync_low);
            else $display("  HSYNC่„‰ๅ†ฒ=%0dๅƒ็ด  โœ“",hsync_low);
            hsync_low<=0; hsync_edges++;
        end
    end

    // VSYNCไฝŽ่„‰ๅ†ฒ็ปŸ่ฎก
    int vsync_low;
    logic vsync_prev;
    always @(posedge clk) begin
        vsync_prev<=vsync;
        if(!vsync) vsync_low++;
        else if(vsync&&!vsync_prev&&vsync_low>0) begin
            if(vsync_low!=1600) $display("FAIL:VSYNC=%0d",vsync_low);
            else $display("  VSYNC่„‰ๅ†ฒ=%0dๅƒ็ด ๆ—ถ้’Ÿ(2่กŒ) โœ“",vsync_low);
            vsync_low<=0;
        end
    end

    // ๅธง่ฎกๆ•ฐๅ’Œๆœ‰ๆ•ˆๅƒ็ด ็ปŸ่ฎก
    int frame_count=0, video_on_count;
    logic vsync_fell;
    always @(posedge clk) begin
        vsync_fell<=!vsync&&vsync_prev;
        if(video_on) video_on_count++;
        if(vsync_fell) begin
            if(frame_count>0) begin
                if(video_on_count==640*480)
                    $display("  ๅธง%0d: ๆœ‰ๆ•ˆๅƒ็ด =%0d โœ“",frame_count,video_on_count);
                else
                    $display("  FAIL: ๅธง%0dๅƒ็ด =%0d",frame_count,video_on_count);
            end
            video_on_count<=0; frame_count++;
        end
    end

    initial begin
        $dumpfile("vga_sync.vcd");$dumpvars(0,vga_sync_tb);
        rst_n=0;#100;rst_n=1;
        $display("========== VGAๆ—ถๅบ้ชŒ่ฏ ==========");
        wait(frame_count==4);
        $display("==================================");
        $display("  VGAๆ—ถๅบ้ชŒ่ฏๅฎŒๆˆ๏ผ");
        $display("  - HSYNC: 96ๅƒ็ด  โœ“");
        $display("  - VSYNC: 2่กŒ โœ“");
        $display("  - ๆœ‰ๆ•ˆ: 640ร—480 โœ“");
        $display("==================================");
        $finish;
    end
    initial #200_000_000 $finish;
endmodule

๐Ÿ“Š ไธๅŒVGAๆจกๅผๅ‚ๆ•ฐ

ๆจกๅผๅˆ†่พจ็އๅƒ็ด ๆ—ถ้’Ÿๅˆทๆ–ฐ็އH_TOTALV_TOTAL
VGA640ร—48025.175MHz60Hz800525
SVGA800ร—60040MHz60Hz1056628
XGA1024ร—76865MHz60Hz1344806
720p1280ร—72074.25MHz60Hz1650750
1080p1920ร—1080148.5MHz60Hz22001125

๐Ÿ“Š ไธๅŒVGAๆจกๅผๅ‚ๆ•ฐ

ๆจกๅผๅˆ†่พจ็އๅƒ็ด ๆ—ถ้’Ÿๅˆทๆ–ฐ็އH_TOTALV_TOTAL
VGA640ร—48025.175MHz60Hz800525
SVGA800ร—60040MHz60Hz1056628
XGA1024ร—76865MHz60Hz1344806
720p1280ร—72074.25MHz60Hz1650750
1080p1920ร—1080148.5MHz60Hz22001125

๐Ÿงช Verilatorๆต‹่ฏ•ๅฐ

SystemVerilogvga_sync_tb.sv
module vga_sync_tb;
    logic clk,rst_n; logic hsync,vsync,video_on;
    logic [9:0] hcount,vcount;
    vga_sync uut(.*);
    initial clk=0; always #20 clk=~clk; // 25MHz
    // ๆฃ€ๆต‹HSYNCไฝŽ่„‰ๅ†ฒๅฎฝๅบฆ(ๅบ”ไธบ96)
    int hsync_low;
    always @(posedge clk) begin
        if(!hsync) hsync_low++;
        else if(hsync_low>0 && hsync_low!=96)
            $display("FAIL: HSYNC=%0d",hsync_low);
        else if(hsync_low==96) $display("HSYNC=96 โœ“");
    end
    initial begin rst_n=0; #100; rst_n=1; #200_000_000; $finish; end
endmodule

๐Ÿ’ก ไปฟ็œŸๅŠ ้€ŸๆŠ€ๅทง

Verilatorไปฟ็œŸVGAไธ€ๅธง้œ€่ฆ420,000ไธชๆ—ถ้’Ÿๅ‘จๆœŸใ€‚ๅฏไปฅๅ…ˆๆŠŠH_TOTAL/V_TOTAL็ผฉๅฐ้ชŒ่ฏ้€ป่พ‘๏ผŒ็กฎ่ฎคๅŽๅ†็”จๆ ‡ๅ‡†ๅ‚ๆ•ฐใ€‚

๐Ÿ“ VGAๆ—ถๅบ่ฏฆ็ป†ๅ›พ่งฃ

ไธ€่กŒๆ—ถๅบ (ๆฐดๅนณๆ–นๅ‘๏ผŒๅ…ฑ800ๅƒ็ด ๆ—ถ้’Ÿ)๏ผš โ”Œโ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ ๅฏ่งๅŒบๅŸŸ โ”‚ โ”‚โ–ผโ–ผโ”‚ โ—„โ”€โ”€ๅŽ่‚ฉโ”€โ”€โ–บ โ”‚ 640ๅƒ็ด  โ”‚ โ”‚HSโ”‚ โ”‚ โ”‚ โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€ |โ—„96โ–บ|โ—„โ”€โ”€48โ”€โ”€โ–บ|โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€640โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ|โ—„16โ–บ| ่„‰ๅ†ฒ ๅŽ่‚ฉ ๆ˜พ็คบๅŒบๅŸŸ ๅ‰่‚ฉ |โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 800ๅƒ็ด ๆ—ถ้’Ÿ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ| ไธ€ๅธงๆ—ถๅบ (ๅž‚็›ดๆ–นๅ‘๏ผŒๅ…ฑ525่กŒ)๏ผš โ”Œโ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚VSโ”‚ โ—„โ”€โ”€ๅŽ่‚ฉโ”€โ”€โ–บ โ”‚ ๅฏ่งๅŒบๅŸŸ โ”‚ โ”‚ โ”‚ โ”‚ 480่กŒ โ”‚ โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€ |โ—„2โ–บ|โ—„โ”€โ”€33โ”€โ”€โ–บ|โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€480โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ|โ—„10โ–บ| ่„‰ๅ†ฒ ๅŽ่‚ฉ ๆ˜พ็คบๅŒบๅŸŸ ๅ‰่‚ฉ |โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ 525่กŒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ| ๅธง็އ = 25MHz / (800 ร— 525) = 59.52Hz โ‰ˆ 60Hz โœ“

๐Ÿ” ๆถˆ้šๅŒบไธบไป€ไนˆๅญ˜ๅœจ๏ผŸ

CRTๆ—ถไปฃ็”ตๅญๆžชไปŽไธ€่กŒๆœซๅฐพ่ทณๅˆฐไธ‹ไธ€่กŒๅผ€ๅคด้œ€่ฆๆ—ถ้—ด๏ผŒไปŽๅบ•้ƒจ่ทณๅ›ž้กถ้ƒจไนŸ้œ€่ฆๆ—ถ้—ดใ€‚ๆถˆ้šๅŒบๅฐฑๆ˜ฏ็ป™็”ตๅญๆžช"็งปๅŠจๆ—ถ้—ด"ใ€‚่™ฝ็„ถLCDไธ้œ€่ฆ็”ตๅญๆžช๏ผŒไฝ†ไธบๅ…ผๅฎนไฟ็•™่‡ณไปŠใ€‚

ๆถˆ้šๅŒบไธๆ˜ฏ"ๆตช่ดน"โ€”โ€”ๆˆ‘ไปฌๅฏไปฅๅˆฉ็”จๅฎƒ๏ผๆฏ”ๅฆ‚VGAๆถˆ้šๅŒบๅฏไปฅ็”จๆฅๆ›ดๆ–ฐๅŒ็ผ“ๅ†ฒใ€ๆ‰ง่กŒๆธธๆˆ้€ป่พ‘็ญ‰๏ผŒไธๅฝฑๅ“็”ป้ขๆ˜พ็คบใ€‚