c0pperdragon / Amiga-Digital-Video

Add a digital video port to vintage Amiga machines
299 stars 30 forks source link

Amiga Support #3

Closed c0pperdragon closed 4 years ago

c0pperdragon commented 4 years ago

@IanSB

I was thinking about to the possibility to create an internal modification for the Amiga using your RGBtoHDMI software. As you have stated, it is possible to pump enough data into the Pi to allow the EGA+ mode with 6bit per pixel. The Amiga (original screen modes only) has basically the same data rate. Twice the bits per pixel, but with half the pixel clock (about 14..2 MHz). So I assume this should be possible with the current system and without resorting to DMA.

The beauty of this mod lies in the fact that the only non-standard part necessary is a simple adapter board that level-shifts the output signals of the graphics chip to 3.3V and exposes them via a 2x20 pin socket (very much like my C64 adapter board). The RPi Zero can then directly be plugged into this socket to sit above the graphics chips. The HDMI output then needs to be transfered somehow outside of the case (probably with a mini-HDMI to HDMI extender cord).

This would be such a cost effective solution to upgrade the Amiga to perfect HDMI (without sound though), that I could imagine quite some people will want such a solution.

One small unsolved issue remains, though. The system clock is only 7.1 MHz, and the graphics chips changes the output signals on both edges of this clock (very much like the atari 8-bit machines). Would this be manageable by the RPi software, or is some additional circuitry necessary? I can imagine to add a latch to keep 12 pixels of one half of the clock and then provide 24 pixels together at half the pixel rate. But only if it can not be solved in software already.

If you think this is possible, I will try to create a device profile - but I will surely need some help for this.

c0pperdragon commented 4 years ago

I just realized that the graphics chips even has a second clock signal with 7.1 MHz that is phase-shifted by 90 degrees. So I could easily create a 14.2 MHz pixel clock with a simple XOR gate.

IanSB commented 4 years ago

One small unsolved issue remains, though. The system clock is only 7.1 MHz, and the graphics chips changes the output signals on both edges of this clock (very much like the atari 8-bit machines).

It already does that. The Psync signal that the CPLD outputs to clock the data on the 12 bit GPIO bus is at half rate, with 12 bit data changing on both edges of the clock so there is no problem there.

This would require a 16 bit frame buffer. At the moment the software only supports 4bit and 8 bit buffers but it should be possible to add 16 bit support and do some tests using the existing analog output of an amiga into the existing analog input of RGBtoHDMI (so the pixel clock is right) but capturing 12bpp and writing to a 16 bit frame buffer. The colours would all be messed up but if that works without glitches then it would be worth creating the hardware as a next step. If it is very marginal, you can overclock the Pi zero so that might help too.

The other issue is the Amiga's interlaced video mode. With simple capture, you would get weave lines and high quality deinterlacting in real time might be difficult. The teletext deinterlacing I wrote uses character recognition tricks which wouldn't work in for general deinterlacing.

As previously mentioned, I've been looking at using the GPU to capture which might remove all the marginal timing issues if it works.

c0pperdragon commented 4 years ago

Just for experimentd, I could easily set up my A-Video board to output any signal pattern in its GPIO1 port. Once you receive your parcel, so can you ;) No need to have an actual Amiga on the bench during this stage.

De-Interlacing would of course be the second big bonus of this solition. The most straight-forward approach ist probably the best: The framebuffer has double height and the even and odd lines are just updated alternatingly.

IanSB commented 4 years ago

I've done some tests and it looks like this will actually work. The timing is close to the limits but it seems to be stable.

As well as inputting 12 bits per pixel directly to the Pi's GPIOs, one other option that also looks like it will work would be to alternate the low and high 6 bits of the 12 bit pixel value at double the pixel clock rate (so ~28.2Mhz) which could be connected to the existing 6 bit digital input on the CPLD. This would allow use of an EGA style 9 pin D-type connector for the digital output on the Amiga and allow use of the menu buttons / functionality by keeping the converter external.

One question: In order to process interlaced (640x400 etc) and progressive (640x200 etc) correctly the converter needs to be able to determine which is currently being output. Does the Amiga output progressive sync with progressive video and only output interlaced sync with interlaced video? (Interlaced video must always have interlaced sync but progressive can have either)

c0pperdragon commented 4 years ago

The csync that is generated by the graphics chip varies with progressive / interlaced mode. In progressive it uses exactly the same pattern as my C64 mod does (which I already copied from the PlayStation, so it seems to be industry standard for 288p). In interlaced mode it follows the proper standard for TVs, namely having the line sync pulses shifted by half a horizontal line in every other frame. With a continously incrementing vertical deflection (all this was done with analog means, back in the day), this causes the lines of odd frames to be drawn between the lines of the even frames.

Multiplexing the 12 bits to 6 lines would in theory be possible. It would not even be so hard to make a circuit for that from a few 74-logic parts. At least on my machine, my measurements show that the graphics chip is driven by an 7.1 Mhz pixel clock (which for some reasons is not the CPU clock, but is 90 degrees earlier). The chip changes the color outputs in a way that the change is apparent about 30ns after the clock has changed (both edges are used). So with two simple multiplexer parts (something like 74HCT157) this would work. The output timing will largely depend on the delay the graphics chips really has and you can not expect perfect even pixel lengths. You also don't get a pixel clock from this. With your CPLD solution and a bit of tweaking this could work anyway.

Of course I for myself would much more prefer the direct solution with 12 data bits. In fact it would be easy to use the exact same RPi software in both variants, as your CPLD outputs 12 bits anyway. Is there a specific timing requirement for when the signals into the RPi need to change in relation to each other? I imagined it would be most straight-forward for programming to have the clock change a bit later (about 5ns) than the color signals. By this the program just needs to poll the port until the clock changes and then it already has all the valid and stable color bits also. Yesterday I came up with a nice digital circuit (2 8-bit flip-flops and a quad XOR) what would produce exactly that.

c0pperdragon commented 4 years ago

One more detail: I found that the falling edge of the csync signal follows the general timing of the color pins (about 30ns after the clock), but for some reason the rising edge of csync happens very close to the time of the clock change. So it probably can happen that the rising csync detection is off by a pixel from time to time. So for proper timing you need to use the falling edge of csync, as it is officially specified anyway.

IanSB commented 4 years ago

Got that working!

capture22 capture21 capture23 capture30 capture24

I'm going via the CPLD at the moment using a modified 8BPP board to give 12BPP

c0pperdragon commented 4 years ago

That is absolutely amazing! How is your setup working? Do you actually use a D-SUB-9 plug? How can you reliably transfer the signals at 30Mhz?

c0pperdragon commented 4 years ago

Any ideas yet about interlaced mode? ;-)

IanSB commented 4 years ago

How is your setup working? Do you actually use a D-SUB-9 plug? How can you reliably transfer the signals at 30Mhz?

I'm feeding all 12 bits into the CPLD at the moment, 9 bits into the main header (The normal 8 bits + using the Vsync input for the 9th bit, two more bits are fed to unused CPLD inputs and the last bit is fed to a CPLD input that was previously used to switch an internal CPLD divider but that function has been moved to an internal register instead.

I've also added a multiplex clock signal which replaces the CPLD clamp output in this mode and that can be used to switch a multiplex circuit as an alternative to multiplexing in the Amiga (although in this case the latter would be preferable as it reduces the number of wires from the Amiga) amiga

Any ideas yet about interlaced mode? ;-)

I've already implemented it but it's untested at the moment. It supports bob and weave deinterlacing but only the weave option will work with the current pixel clock rate as bob requires twice the memory bandwidth.

c0pperdragon commented 4 years ago

That is really a hack, just soldering wires to the pins! It is cool that you actually get reliable signal transmission. It seems the HC245 drivers produce a "gentle" enough signal slope so you do not run into all this messy reflection issues. With AC or LVC parts, the ringing would wreck havoc with your signals.

I would best like an interlacing scheme that would produce a 100% still output image for non-moving input images. Like for working with workbench applications. Games and such are probably never interlaced. So weave deinterlacing is probably the best option.

I am not 100% sure about the exact definition of weave deinterlacing according to https://en.wikipedia.org/wiki/Deinterlacing Reading this, it seems the progressive output image changes only once every 2 frames. I would very much prefer a solution that changes every frame by just updating the newly received lines (even or odd) and keeping the other lines from the previous frame. This would keep the display more responsive, I guess. Of course, combing artefacts are still present but maybe less noticable as the image changes more quickly.

IanSB commented 4 years ago

That is really a hack, just soldering wires to the pins!

Of course! but if I couldn't get this to work due to timing issues on the Pi then there wouldn't be any point in pursuing this idea any further. After this test, it looks totally viable so it's going to be worth designing a suitable PCB.

Reading this, it seems the progressive output image changes only once every 2 frames.

My implementation will update on every field change so the output will change on every frame at 50Hz or 60Hz as appropriate and consist of the current field with the previous field woven in.

If you are still thinking about an internal Pi option, you need to also think about the best way to access the menus as they will be needed to change resolution, scaling type and many other preference and configuration options. Also it is useful to access the Pi's SD card to retrieve screen caps and do software updates.

c0pperdragon commented 4 years ago

Yes, I really think heavily about an internal upgrade. To also appeal to as many users as possible, the thing should be simple to install and use. I guess it would be possible to provide a no-solder solution that just consists of the adapter board (goes under the Denise) and an SD card with preinstalled software for the Pi. The rest are just off-the-shelf parts: Pi Zero and whatever HDMI cables the user cares to use. To make it truly no-solder, a Pi with pre-installed headers can be used - that means, I will design the adapter board so the Pi will be plugged into it upside down.

The price for simplicity is of course less features: No screen capturing, no tweaking of anything. No screen centering or color adjustment or anything like that. Only one mode for de-interlacing (your method for weaving is pretty perfect in my opinion).

Ideally the RPi should detect the target screen resolution and automatically switch the output signal accordingly. If that is not possible, fixed 1920x1080 is absolutely OK. If resolution detection is supported, I personally would love to use my 1680x1050 screen. Another resolution that would be good to support is: 1280x1024. This would pixel-perfectly fit the Amiga Workbench. Other than that, I see no reason to bother. Screens with higher resolutions should always be fine with 1080p input and will show it with proper aspect ratio.

IanSB commented 4 years ago

Ideally the RPi should detect the target screen resolution and automatically switch the output signal accordingly

That is problematic. There is no call in the limited bare metal Mailbox API to change physical resolution and you have to specify the resolution in the config.txt file which is only read at bootup. You can specify auto detected resolution but that will always select the 60 Hz version of the monitor's resolution when in most cases 50Hz is required. Getting the output resolution to match the monitor's physical resolution is important for integer scaling so it is something that needs to be set correctly. I will have a look at implementing some form of auto detection but it will involve the pi in doing multiple reboots during startup. Even then there is a problem as many of the chinese monitors on ebay will display 1920x1080 and specify that as their default resolution but their physical resolution is usually smaller than that.

Another resolution that would be good to support is: 1280x1024. This would pixel-perfectly fit the Amiga Workbench.

That isn't actually a good resolution for the Amiga as it will crop any overscanned output to 640x256. Also one other potential problem which happens with the BBC micro is that each different resolution has a slightly different horizontal pixel shift so if 640x256 fits, 320x256 might be shifted a pixel or two left or right losing those pixels. I mostly use 1600x1200 4:3 LCD monitors as they produce excellent results with all supported computers. The HP LP2065 1600x1200 monitor will also lock to 50Hz on it's DVI input so has the correct refresh rate.

There are potentially many ways around the menu issue: e.g. There is a serial port on the Pi which is normally used to output debug info but the input could be hooked up to the Amiga's serial output listening for a specific set of command strings which would simulate the button presses. Perhaps a latch could be mapped into an address with the bits simulating button presses or something similar using an I2C latch. The above would require a driver of some sort but a driverless approach would be to loop the keyboard connector through the board so that a special combination of kepresses could be detected to simulate the buttons. The main issue with this option would be the actual detection as the Pi is 100% involved in capture and doesn't have the time available to respond to interrupts. It might be possible that one of the Pi's on board peripherals can interpret the Amiga keyboard protocol or may be a little extra external hardware could help.

c0pperdragon commented 4 years ago

So, a fixed resolution, that's it.
By default I can provide a 1920x1080 at 50Hz setting. Users can easily modify this config.txt von the SD card, I guess. I will also do that for my machine to get a perfect integer scaling.

c0pperdragon commented 4 years ago

I would like to start designing a PCB for this internal mod. But I feel that this work belongs into the RGBtoHDMI project, alongside all the other kicad folders there. How could I best contribute to this project? The matter is a bit confused by the fact that your repo is a clone of hoglet67's which seems to be the single point of truth . So who is in charge of merging stuff together?

IanSB commented 4 years ago

I've let hoglet do the final merging and releasing so far as it was his project originally but he isn't doing any active development on it at the moment. I also have collaborator access to hoglet's RGBtoHDMI repository.

c0pperdragon commented 4 years ago

So if I actually issue a pull request, you could also merge this?

IanSB commented 4 years ago

Yes, I think so.

c0pperdragon commented 4 years ago

Now I have just managed to sequeeze all parts and traces into the form-factory I have in mind. But only by assuming that I have total freedom to use any GPIO pin of the Pi in any order I want. I need to feed in the 12 color signals + csync + pixel clock. +5V and GND pins are also connected to power the Pi from the adapter. But I am sure there are some very specific restrictions which will make my life much harder, I assume. Could you summarize what I need to apply here? Also I would like some information about the correct signal timings your software can handle. It would be very simple to implement any setup-time for the colors and csync in the range of 0ns to about 8ns.

IanSB commented 4 years ago

Could you summarize what I need to apply here?

Here are the current GPIO connections, the RGB values are sequential to minimise the amount of bit shifting to get them formatted for the 16 bit per pixel memory. If you have the space, you might want to bring the switch connections to a header with +3.3v & GND as you will probably need to connect them temporarily to do the initial timing setup for the default profile.

B0 = gpio2 B1 = gpio3 B2 = gpio4 B3 = gpio5 G0 = gpio6 G1 = gpio7 G2 = gpio8 G3 = gpio9 R0 = gpio10 R1 = gpio11 R2 = gpio12 R3 = gpio13

Sync = gpio23 Pixel Clock = gpio17

SW1 = gpio16 SW2 = gpio26 SW3 = gpio19

I would like some information about the correct signal timings your software can handle

I'm still investigating that as the timings are quite marginal. You may need to prototype this before getting a PCB made in case some specific signal conditioning is required.

IanSB commented 4 years ago

If you add a pull down resistor on to an appropriate GPIO I should be able to auto detect your board and configure the software for it. I'll have a look at which GPIOs can be used.

c0pperdragon commented 4 years ago

My envisioned adapter board is indeed a very narrow part and it is pretty difficuly to route the traces. Especially because all the color pins of the Denise are located at one end. So I need to mostly utilize GPIO pins from the same end of the board. With my planned relative orientation of adapter and the RPi, this means the end with GPIO pin 21. Would it be OK to use 16 consecutive GPIO pins (lets say 5 - 20) and connect the 12 color signals in some arbitrary way? By this the CPU would need a single shift to put all the data into the lower 16 bits of a register and write them to the buffer. Is the GPU then able to disentangle all the bits to create the correct HDMI output?

Sync at gpio23 and pixel clock at gpio17 are perfect already.

For a mode selector resistor, I have a bit of space left on one side of the board - so gpio 2, 3, or 4 would be most convenient.

By the way, I have renamed the repo so to better reflect what we are now trying to achieve...

c0pperdragon commented 4 years ago

Redesigned the board to reach the above mentioned GPIO pins. It would look something like this: deniseadapter

I am not sure about which IC socket to use then. Precision sockets may be of better quality (gold plated), but it is also harder to insert/remove the chip. With this 48pin Denise it could become a real trouble. So I will probably use the sockets with double-sided springs instead.

IanSB commented 4 years ago

Redesigned the board to reach the above mentioned GPIO pins.

Do you mean the pins I specified?

One problem I can see with this design: The HDMI port on the Pi will be opposite the large blue capacitor (see my photo above) and the HDMI cable might not fit.

It might work with a 90 Degree cable like this: https://www.ebay.co.uk/itm/90-Degree-Right-Angle-Mini-HDMI-Male-to-HDMI-Female-Adapter-Cable-connector-cord/232016100201 Or a flat cable like this: https://www.ebay.co.uk/itm/20cm-Up-Angle-Mini-HDMI-Male-to-HDMI-Male-FPC-Flat-Cable-For-Multicopter-Aerial/233196072598 (which might also make it easier to route the cable outside the case.)

c0pperdragon commented 4 years ago

No, actually I have first tried to do it with GPIO5 - GPIO20 to see if it will work at least with this relaxed restrictions. The actual assignment is: B0 - GPIO 19 B1 - GPIO 16 B2 - GPIO 20 B3 - GPIO 13 R0 - GPIO7 R1 - GPIO 5 R2 - GPIO 6 R3 - GPIO 12 G0 - GPIO 11 G1 - GPIO 9 G2 - GPIO 8 G3 - GPIO 10 CSYNC - GPIO 16 PCLK - GPIO17

The orientation of the board inside your computer is the same as on my picture. And the RPi will then extend further down. The adapter board and the RPi are not stacked above each other, but extend in opposite directions. I want do do this for better thermal management. At least the Denise chip already gets a bit warm by itself, and I assume the RPi will also generate some extra heat. In this configuration the HDMI cable should stay clear of other obstacles. CPU accelerator cards are a problem, but these normally come with their built-in HDMI solution, so no need to have both.

IanSB commented 4 years ago

And the RPi will then extend further down

OK, does that cause any issues with the expansion connector?

The GPIO order may be a problem. The ARM cpu can re-arrange any bit order to match the 16 bit screen memory but that will require a lot of bit masking and shifting which might cause timing issues, keeping each set of four GPIOs sequential so e.g R0-R3 = GPIO n - GPIO n+3 only needs 1 mask and shift for 4 bits.

I'll have to do some tests on that.

The pull down resistor should be on GPIO 18.

Also it would be preferable to have Csync on GPIO23

c0pperdragon commented 4 years ago

The RPi should ride pretty high because of the height of the adapter board itself and then I will use 8mm pin sockets to plug the Pi into. So I guess it should stay clear of the edge connector.

OK, I will actually give it a try to adhere to your orginal pin assignment for the color pins. Maybe I will need to make the board a tiny bit wider outside the GPIO connector to accomodate the additional traces...

CSYNC is alread on GPIO23 - I just mixed up gipo number and connector pin number in the previous comment.

Pull down on GPIO 18 is pretty easy.

c0pperdragon commented 4 years ago

Yes! I managed to route all the signals to your specified pins. And I did not even have to make the board any larger. This was really a tough call - had to route some signals through a different flip-flop but in the end it now all looks nice and tidy.

IanSB commented 4 years ago

I've tested the interlace mode and that works OK, you get weave lines on any motion / screen update as you can see from the boxes in the screen grab below but there is no flicker. It might be possible to do some advanced deinterlacing on a faster system like the Pi 4 one day. capture21

I've also implemented detection of the pull down resistor on GPIO 18 and that seems to work OK, switching the software into "simple" mode with no CPLD. I have tested this in a limited way by configuring for the amiga mode using the CPLD so it is setup correctly to output 12 bits + regenerated pixel clock, then pulling GPIO 18 low and resetting which doesn't reinit the CPLD but it still works. Note GPIO 18 is normally an output, it is briefly made an input during bootup to detect the resistor. It is weakly pulled high by an approx 50K internal pullup by default so the pulldown needs to be low enough to overcome that but as it is normally an output, you cannot ground it as that will cause damage.

I should have a beta build available in a few days if you want to do any tests.

c0pperdragon commented 4 years ago

That is really cool! I plan to set up a breadboard circuit to test the software before commiting to have PCBs made and ordering parts.

About the pulldown resistor: 3.3k should work with enough margin.

IanSB commented 4 years ago

I've also got it working with a 16 Mhz pixel clock by overclocking the core and sdram clocks on the Pi zero which means that Atari ST support is also possible. The 1000Mhz CPU clock isn't very overclockable but the core and sdram clocks which are around half that speed do seem to be, the core clock seems to improve GPIO access speeds and the sdram obviously improves the uncached memory speed which seem to be the main bottlenecks.

c0pperdragon commented 4 years ago

As the atari st has only 9 bit per pixel, maybe you can compactify the data a bit to reduce ram access. i don't know much of the hardware (i was more of an Amiga fanboy) but the schematic show that the individual RGB bits are as easily accessible as in the Amiga. on the other hand: 8 different voltage leves are maybe not that difficult to reliably detect. if you prefer a purely external solution.

IanSB commented 4 years ago

I've just built a beta test release (Beta4) which should have the necessary support for your board

https://github.com/IanSB/RGBtoHDMI/releases/tag/20ccba2

You will probably have to temporarily hook up switches to GPIOs 16,26 & 19 (Pins 36, 37 & 35) to change settings. The default "Amiga" profile has trailing edge sync detection. There is also an "Amiga Leading Edge" profile which you can select which uses leading edge detection instead. I have not been able to test that properly as the CPLD does leading/trailing selection and the simple mode option doesn't work properly with the CPLD. (Unzip the files onto a blank micro SD card.)

Note the software will hang up if there is no pixel clock (7.1Mhz) so you will need to have at least that connected before you can call up any menus.

Also note the CPLD suppresses the pixel clock for a couple of cycles after the active H sync edge to avoid glitches because the regenerated pixel clock isn't an exact multiple of the line length:

DS1Z_QuickPrint54 DS1Z_QuickPrint55

Your simple system shouldn't need to do that but it probably means some slight adjustment to h-offset picture centreing will be needed in the geometry menu.

c0pperdragon commented 4 years ago

I am closing this issue now as it is much too long. Answer follows on other issue...