Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

16bit per pixel support #6

Open
IanSB opened this issue Apr 30, 2022 · 11 comments
Open

16bit per pixel support #6

IanSB opened this issue Apr 30, 2022 · 11 comments
Assignees
Labels
enhancement New feature or request

Comments

@IanSB
Copy link

IanSB commented Apr 30, 2022

I've been working on this vintage computer HDMI upscaler project for the Pi Zero:
https://github.com/IanSB/RGBtoHDMI/wiki

This takes up to 12 bpp digital inputs + sync and I was looking at sharpie7's test pattern generator fork of your project and it seems like it should be possible to modify your design for 16bpp support as I want to generate at least 12bpp digital test patterns for testing RGBtoHDMI boards.

Would it be possible to change this for 16bpp support and separate H & V syncs or are there any underlying bandwidth limitations etc?

@breakintoprogram
Copy link
Owner

The code has an option in config.h to compile a colour version; that splits the H and V syncs for the AD724 PAL encoder I use. With regards to upgrading it from 8bpp to 16bpp, the honest answer is I'm not 100% sure. It should just be a case of modding the colour version; increasing the number of pins allocated, modifying the resistor ladder, switching the pixel DMA from 8 to 16 bits, and doubling the malloc.

@IanSB
Copy link
Author

IanSB commented Jun 6, 2022

@breakintoprogram
@sharpie7

I spend a few hours yesterday modifying your code for 16bpp support after receiving the prototype PCBs for my RGBtoHDMI test jig and it all worked out extremely well:

Here's the test jig which has sockets for the various interfaces supported by RGBtoHDMI:

testboard

Here's the output displaying a test pattern (RGBtoHDMI can screencap the video source as a png):

capture0

The pixel data is on GPIOs 0-15 and the separate syncs are on GPIOs 16 & 17 although I'm mainly using 12 bits out of the 16bits of pixel data for 4 bits each of RGB.
I had to change the sync PIO to use 32 bit DMA and the video PIO to use 16 bit DMA and it all works well in 256 and 320 horizontal resolutions but appears to hang in 640 horizontal resolution so that may be too fast to do DMA on the pixel data at 16BPP and the sync nulls at 32bpp but 320 pixel is fine for my test jig at the moment.

One thing I noticed is that the default sync clock frequency seems to be producing slightly incorrect line timing with approx 64.5uS per line rather than 64uS.
#define piofreq_0 5.25f // Clock frequency of state machine for PIO handling sync
Here is the RGBtoHDMI source summary screen showing the sync timings using your defaults:
capture2

I changed that to:
#define piofreq_0 5.2083f // Clock frequency of state machine for PIO handling sync
which results in a 24Mhz PIO clock and 64uS lines.
Source summary screen again:
capture3

The other issue I had was that you are using separate clock frequencies for the sync and pixels and that results in the pixels jittering with respect to hsync. RGBtoHDMI does pixel perfect sampling by timing the sample points from the edge of hsync so any pixel jitter results in mis-sampling and resulting noise. I changed the 320 pixel mode clock divider also to 5.2083f which results in a pixel clock of 8Mhz and after that everything was jitter free. The pixel clock can be a different frequency but the relationship between the two clocks has to be carefully chosen to minimise or eliminate jitter.

Thanks for this very useful project.

@IanSB
Copy link
Author

IanSB commented Jun 8, 2022

@breakintoprogram

I'm having an issue with the relative phases of the hsync and pixel data which seems to start up randomly.
As mentioned RGBtoHDMI is very sensitive to phase offsets between hsync and the pixel data and I'm running both state machines at the same clock rate which keeps them frequency locked but the phase offset between the sync and pixels varies for each power up so I either get a clean or noisy image. It remains stable after the code has started but the phase offset on the next power up will be different.
I presume this is because the two state machines don't start up at the same time. I have changed the code so that the state machines are both started immediately one after the other but it still varies randomly.
Is there any way to start them reliably in sync? (I suspect the startup code varies in execution time due to cache misses and random cache allocation and I guess it only needs a few clock cycles difference in execution time for that to be an issue)

@sharpie7
Copy link
Contributor

sharpie7 commented Jun 8, 2022

@IanSB

Thanks for tagging me on this. I had seen the horizontal jitter in the output but hadn't been able to find the cause or even measure it properly, so I convinced myself it was a problem of my imagination or my display. I will try your clock fix.

For the synchronization of state machines this discussion seems relevent. It seems you can hold state machines in reset and start them together in blocks:
raspberrypi/pico-feedback#65

@IanSB
Copy link
Author

IanSB commented Jun 8, 2022

@sharpie7

For the synchronization of state machines this discussion seems relevent.

Yes, the problem is that it's the state machines' clock dividers that are out of sync rather than the state machines themselves and is the same type of issue that another user had recently on stardot with a BBC mode7 clock divider:
https://stardot.org.uk/forums/viewtopic.php?p=358299#p358299

I first used pio_clkdiv_restart_sm_mask then started the state machines:

pio_clkdiv_restart_sm_mask(pio_0, (1u << sm_data) | (1u << sm_sync)); 
pio_sm_set_enabled(pio_0, sm_data, true);	// Enable the PIO state machine    
pio_sm_set_enabled(pio_0, sm_sync, true);	// Enable the PIO state machine

But then I noticed that this call does all of the above simultaneously:

pio_enable_sm_mask_in_sync(pio_0, (1u << sm_data) | (1u << sm_sync));	

After making the above change it always starts up with the pixels and sync in the same phase and I now have a stable phase locked test source.

I will try your clock fix

As mentioned above, the clocks don't have to be the same but to avoid jitter the pixel clock must have a whole number of cycles in the exact time taken for the sync pio to produce one line. This time is theoretically 512 cycles of 8 Mhz, however I'm not sure the clock divider is producing exactly 24 Mhz (8*3) nominal for the pio because the fractional divider has limited bits so you would need to work out the exact nominal frequencies that both pio dividers were producing to be able to check for jitter free operation.

@IanSB
Copy link
Author

IanSB commented Jun 10, 2022

Just realised why 640 pixel mode wont work: 640x256x2 (16 bits per pixel) = 327680 bytes which is more RAM than the pico has.

@runnerpack
Copy link

Just realised why 640 pixel mode wont work: 640x256x2 (16 bits per pixel) = 327680 bytes which is more RAM than the pico has.

Maybe there's a compression algorithm fast enough to squeeze it in there.

@IanSB
Copy link
Author

IanSB commented Feb 8, 2023

@sharpie7
@runnerpack

Maybe there's a compression algorithm fast enough to squeeze it in there.

I did get it effectively working at 640x256x16bpp to allow some 80 column text with my test pattern by sending the first 16 lines out and then repeating each line after that twice so that the video memory buffer was only 640x136 (16 lines at full vertical resolution and 120 lines at half vertical resolution for a total of 174080 bytes)

This worked well for my test pattern:
640x256

As the start of each video line is under software control you could dynamically vary the vertical resolution to match the displayed image e.g. with the above pattern I only really needed 16 lines for the text and 6 lines for the rest repeated many times but I left it at half resolution so there was some flexibility for more complex test patterns in future.

@runnerpack
Copy link

runnerpack commented Feb 8, 2023

@sharpie7 @runnerpack

Maybe there's a compression algorithm fast enough to squeeze it in there.

I did get it effectively working at 640x256x16bpp to allow some 80 column text with my test pattern [...snip...]

That's a really clever solution! 👍🏻

@breakintoprogram
Copy link
Owner

Hi @IanSB I've finally gotten around to updating this project - will take a look at some of your suggestions regarding the jitters.

@breakintoprogram breakintoprogram self-assigned this Sep 26, 2024
@breakintoprogram breakintoprogram added the enhancement New feature or request label Sep 26, 2024
@breakintoprogram
Copy link
Owner

I've implemented the simultaneous PIO state machine start thanks. Will review the horizontal dot frequency as reducing the value I put in leads to a loss of sync. I suspect that's more down to running it on a breadboard. Will review that when the PCB comes in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants