Skip to content

Commit bb74185

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 4efc06a commit bb74185

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
@@ -803,7 +803,7 @@ impl ScanlineTimingBuffer {
803803
if vsync {
804804
value |= 1 << 1;
805805
}
806-
value |= (period - 6) << 2;
806+
value |= (period - FIXED_CLOCKS_PER_TIMING_PULSE) << 2;
807807
value | command << 16
808808
}
809809
}
@@ -1690,6 +1690,9 @@ static PIXEL_DATA_BUFFER_ODD: LineBuffer = LineBuffer::new_odd();
16901690
/// ```
16911691
static TEXT_COLOUR_LOOKUP: TextColourLookup = TextColourLookup::blank();
16921692

1693+
/// How many fixed clock cycles there are per timing pulse.
1694+
const FIXED_CLOCKS_PER_TIMING_PULSE: u32 = 5;
1695+
16931696
// -----------------------------------------------------------------------------
16941697
// Functions
16951698
// -----------------------------------------------------------------------------
@@ -1710,28 +1713,41 @@ pub fn init(
17101713
let (mut pio, sm0, sm1, _sm2, _sm3) = pio.split(resets);
17111714

17121715
// This program runs the timing loop. We post timing data (i.e. the length
1713-
// of each period, along with what the H-Sync and V-Sync pins should do)
1714-
// and it sets the GPIO pins and busy-waits the appropriate amount of
1715-
// time. It also takes an extra 'instruction' which we can use to trigger
1716-
// the appropriate interrupts.
1716+
// of each period, along with what the H-Sync and V-Sync pins should do) and
1717+
// it sets the GPIO pins and busy-waits the appropriate amount of time. It
1718+
// also takes an extra 'instruction' which we can use to trigger the
1719+
// appropriate interrupts.
1720+
//
1721+
// Note that the timing period value should be:
1722+
//
1723+
// timing_period = actual_timing_period - FIXED_CLOCKS_PER_TIMING_PULSE
1724+
//
1725+
// This is because there are unavoidable clock cycles within the algorithm.
1726+
// Currently FIXED_CLOCKS_PER_TIMING_PULSE should be set to 5.
17171727
//
17181728
// Post <value:32> where value: <clock_cycles:14> <hsync:1> <vsync:1>
17191729
// <instruction:16>
17201730
//
17211731
// The SM will execute the instruction (typically either a NOP or an IRQ),
1722-
// set the H-Sync and V-Sync pins as desired, then wait the given number
1723-
// of clock cycles.
1732+
// set the H-Sync and V-Sync pins as desired, then wait the given number of
1733+
// clock cycles.
17241734
//
17251735
// Note: autopull should be set to 32-bits, OSR is set to shift right.
17261736
let timing_program = pio_proc::pio_asm!(
17271737
".wrap_target"
1728-
// Step 1. Push next 2 bits of OSR into `pins`, to set H-Sync and V-Sync
1738+
// Step 1. Push next 2 bits of OSR into `pins`, to set H-Sync and V-Sync.
1739+
// Takes 1 clock cycle.
17291740
"out pins, 2"
17301741
// Step 2. Push last 14 bits of OSR into X for the timing loop.
1742+
// Takes 1 clock cycle.
17311743
"out x, 14"
1732-
// Step 3. Execute bottom 16-bits of OSR as an instruction. This take two cycles.
1744+
// Step 3. Execute bottom 16-bits of OSR as an instruction.
1745+
// This take two cycles, always.
17331746
"out exec, 16"
17341747
// Spin until X is zero
1748+
// Takes X + 1 clock cycles because the branch is conditioned on the initial value of the register.
1749+
// i.e. X = 0 => 1 clock cycle (the jmp when x = 0)
1750+
// i.e. X = 1 => 2 clock cycles (the jmp when x = 1 and again when x = 0)
17351751
"loop0:"
17361752
"jmp x-- loop0"
17371753
".wrap"

0 commit comments

Comments
 (0)