/* - horizontal sync pulse generation this machine commands the timing for the other machines too - vertical sync pulse generation counts the horizontal sync pulses and generated a vertical sync pulse accordingly - pixel machine horizontal sync machine also sends an interrupt when the back porch is finished, thus triggering the pixel machine to start shifting out the pixels */ /* Pixel Machine Wait for the "start-of-visible-line" interrupt signal, and output 640 pixels */ .program vga_pixel entrypoint_vga_pixel: // pull configuration into Y register // Y = number of visible pixels in the line pull mov y, osr .wrap_target mov x, y wait irq 7 // loop lasts 4 SM clocks, which corresponds to 1 VGA pixel loop: out pins, 1 [2] jmp x-- loop .wrap /* Horizontal Sync Machine A line is composed by 840 pixels: - 64 sync pulse - 120 back porch - 640 visible area - 16 front porch */ .program vga_hsync entrypoint_vga_hsync: // pull configuration into ISR and OSR registers // ISR = sync pulse duration - 2 (64 pixel) // OSR = visible line + front porch - 1 (640 + 16 pixel) // Note: configuration contains desired value -1/-2, // because of how end-of-loop check is performed // and how time is spent in overhead instructions pull mov isr, osr pull // once configured, no more data is pulled from the TX FIFO, // so program wraps around here .wrap_target set pins, 0 [1] // start pulse low irq set 0 // trigger "start-of-line" interrupt signal mov x, isr // load register X with sync pulse duration // +4 SM clocks elapsed (first VGA pixel) // loop for the duration of the low pulse (remaining 63 pixels) hsync_pulse: jmp x-- hsync_pulse [3] set pins, 1 [1] // start pulse high (.5 pixel) // hardcoded backporch duration: 120 pixel => 120 x 4 = 480 SM clocks // but one pixel is already in the surronding logic, then only 476 clocks are cycled // where 476 = (27 + 1) * (16 + 1) set y, 27 // another .25 pixel hsync_back_porch: jmp y-- hsync_back_porch [16] irq set 7 // trigger "start-of-visible-line" interrupt signal // (.25 more pixel) mov x, osr [3] // load register X with remaining line duration (visible line + front porch) // also, just make this last 4 SM clocks (1 pixel) hsync_idle: jmp x-- hsync_idle [3] .wrap /* Vertical Sync Machine A frame is composed by 500 lines: - 3 vertical sync pulse - 16 back porch - 480 visible lines - 1 front porch */ .program vga_vsync entrypoint_vga_vsync: // Pull configuration into ISR and OSR registers // ISR = sync pulse assert duration - 1 (3 lines) // OSR = sync pulse idle duration - 1 (497 lines) pull mov isr, osr pull // once configured, no more data is pulled from the TX FIFO, // so program wraps around here .wrap_target mov x, isr // load register X with low pulse duration vsync_pulse: wait irq 0 // wait for "start-of-line" interrupt from hsync machine set pins, 0 // start pulse low jmp x-- vsync_pulse // trigger "start-of-frame" interrupt // (actually, 3 lines of the back porch are already passed) irq set 1 mov x, osr // load register X with high pulse duration vsync_idle: wait irq 0 set pins, 1 jmp x-- vsync_idle .wrap