Skip to content

Commit 4de6df9

Browse files
committed
Fix a timing bug in the video generation.
All the timing periods (front porch, sync pulse, back porch, visible video) were one clock cycle too short thanks to an off-by-one error when I was counting the overhead of the timing PIO function. This is what led to rolling video noise and slightly fuzzy video capture - monitors and capture devices were trying to grab 640 pixels out of a period that was only 639 clock cycles in length. Now the video looks much sharper.
1 parent cc54299 commit 4de6df9

File tree

1 file changed

+25
-9
lines changed

1 file changed

+25
-9
lines changed

src/vga/mod.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ impl ScanlineTimingBuffer {
936936
if vsync {
937937
value |= 1 << 1;
938938
}
939-
value |= (period - 6) << 2;
939+
value |= (period - FIXED_CLOCKS_PER_TIMING_PULSE) << 2;
940940
value | command << 16
941941
}
942942
}
@@ -1754,6 +1754,9 @@ static PIXEL_DATA_BUFFER_ODD: LineBuffer = LineBuffer::new_odd();
17541754
/// ```
17551755
static TEXT_COLOUR_LOOKUP: TextColourLookup = TextColourLookup::blank();
17561756

1757+
/// How many fixed clock cycles there are per timing pulse.
1758+
const FIXED_CLOCKS_PER_TIMING_PULSE: u32 = 5;
1759+
17571760
// -----------------------------------------------------------------------------
17581761
// Functions
17591762
// -----------------------------------------------------------------------------
@@ -1774,28 +1777,41 @@ pub fn init(
17741777
let (mut pio, sm0, sm1, _sm2, _sm3) = pio.split(resets);
17751778

17761779
// This program runs the timing loop. We post timing data (i.e. the length
1777-
// of each period, along with what the H-Sync and V-Sync pins should do)
1778-
// and it sets the GPIO pins and busy-waits the appropriate amount of
1779-
// time. It also takes an extra 'instruction' which we can use to trigger
1780-
// the appropriate interrupts.
1780+
// of each period, along with what the H-Sync and V-Sync pins should do) and
1781+
// it sets the GPIO pins and busy-waits the appropriate amount of time. It
1782+
// also takes an extra 'instruction' which we can use to trigger the
1783+
// appropriate interrupts.
1784+
//
1785+
// Note that the timing period value should be:
1786+
//
1787+
// timing_period = actual_timing_period - FIXED_CLOCKS_PER_TIMING_PULSE
1788+
//
1789+
// This is because there are unavoidable clock cycles within the algorithm.
1790+
// Currently FIXED_CLOCKS_PER_TIMING_PULSE should be set to 5.
17811791
//
17821792
// Post <value:32> where value: <clock_cycles:14> <hsync:1> <vsync:1>
17831793
// <instruction:16>
17841794
//
17851795
// The SM will execute the instruction (typically either a NOP or an IRQ),
1786-
// set the H-Sync and V-Sync pins as desired, then wait the given number
1787-
// of clock cycles.
1796+
// set the H-Sync and V-Sync pins as desired, then wait the given number of
1797+
// clock cycles.
17881798
//
17891799
// Note: autopull should be set to 32-bits, OSR is set to shift right.
17901800
let timing_program = pio_proc::pio_asm!(
17911801
".wrap_target"
1792-
// Step 1. Push next 2 bits of OSR into `pins`, to set H-Sync and V-Sync
1802+
// Step 1. Push next 2 bits of OSR into `pins`, to set H-Sync and V-Sync.
1803+
// Takes 1 clock cycle.
17931804
"out pins, 2"
17941805
// Step 2. Push last 14 bits of OSR into X for the timing loop.
1806+
// Takes 1 clock cycle.
17951807
"out x, 14"
1796-
// Step 3. Execute bottom 16-bits of OSR as an instruction. This take two cycles.
1808+
// Step 3. Execute bottom 16-bits of OSR as an instruction.
1809+
// This take two cycles, always.
17971810
"out exec, 16"
17981811
// Spin until X is zero
1812+
// Takes X + 1 clock cycles because the branch is conditioned on the initial value of the register.
1813+
// i.e. X = 0 => 1 clock cycle (the jmp when x = 0)
1814+
// i.e. X = 1 => 2 clock cycles (the jmp when x = 1 and again when x = 0)
17991815
"loop0:"
18001816
"jmp x-- loop0"
18011817
".wrap"

0 commit comments

Comments
 (0)