Close

Generating video, one cycle at a time

A project log for VGA Blinking Lights

Pseudo Random VGA blinking lights generator

danjovicdanjovic 11/25/2015 at 00:230 Comments

The base of the project is the cycle-by-cycle generation of VGA video signal borrowed from Tic Tac X, a previous project of mine.

A standard VGA frame (640x480@60Hz) has 525 lines of video. Each line takes 31.77us to happen.

When using a PIC running at 20MHz each instruction takes 200ns.

Divinding the numbers and rounding up to the next integer we have 159 cycles. Such amount of clock cycles shall be precisely counted in order to have a jitter free picture.

To form a frame you have to issue

The sum of it all gives exactly 525 lines or 83475 clock cycles

Considering that the back porch, top border and bottom border lines are all blank lines (1 hsync and black video after) we can rearrange the frame as:

The next trick is to split the 480 lines of visible video into 2 halves of 240 lines. Now that fits into an 8 bit counter:

Next challenge was harder: Cascading loops! After finishing a loop (for example between the two halves of the visible we must load again a new value on the counter and it takes 2 instructions. The problem is that we have only one cycle time free that is caused by the skip of the 'goto' instruction that close the loop. I have spent some time trying to figure out a solution until it was given me by the mindset I got after TRIZ training: Use of 'preliminary action' as well as 'the other way out': I've inverted the order of the loading of a new value on the W register prior to test if the loop reached zero. Then if there is still more iterations this value is not used, otherwise I only spend 1 instruction time to save the pre-loaded value on the register used for counting.

The last problem is on the frame loop. I still have only one spare cycle and I need 2 for the 'goto' instruction that closes the loop.

The solution is philosophically similar: The last line was not called from a loop, so I do not have to test for the end of the loop (with decfsz) thus saving one instruction time that I need to jump again to the beginning of the loop.

(full explanation - in portuguese - here)

My final VGA frame is then:

Talk is cheap, show me the code

Well, there it goes:

; ** Render a VGA Frame
DO_VGA:
    movlw 2 ; Initialize amount of Vsync Lines

VGA_Frame:
    movwf Conta 
Vsync_loop:
    call Vsync_line
    movlw .33         ; amount of lines for next loop. Was placed here to 
    decfsz Conta,f   ; equalize timing of all loops that make the frame
    goto Vsync_loop  ; each loop takes exactly 8 cycles + call time
                   ; at 20Mhz we have 159 cycles per VGA line (31.8us)
VBackPorch:
    movwf Conta      ; 25lines backporch plus 8 lines top border
VBackPorch_Loop:
    call Blank_line
    movlw .240        ; first half of vilible lines
    decfsz Conta,f
    goto VBackPorch_Loop

First_half:
    movwf Conta      ; 240 lines
VFirst_Visible_Loop:
    call Visible_line
    movlw .240        ; second half of vilible lines
    decfsz Conta,f
    goto VFirst_Visible_Loop

Second_half:
    movwf Conta      ; 240 lines
VSecond_Visible_Loop:
    call Visible_line
    movlw .9       ; last 10 blank lines minus one
    decfsz Conta,f
    goto VSecond_Visible_Loop

VFront_Porch
    movwf Conta      ; 8 lines bottom plus 2 frontporch 
VFrontPorch_Loop:  ; minus one to equalize timing
    call Blank_line
    movlw .2        ;  dummy
    decfsz Conta,f
    goto VFrontPorch_Loop

                     
    nop              ; dummy for equalize timing
    call Blank_line  ;  last is called outside a loop 
    movlw .2         

    goto VGA_Frame ;

Discussions