ProjectXG Revisitied

[This is a post I made to AmiBay back in 2011.  The domain on which I hosted the images and audio snippets is about to expire, so I’ve rescued the files and moved them here – so I may as well archive the post itself too, while I’m at it!]

Anyone remember CU Amiga Magazine’s ProjectXG feature many years ago? They published a parts list and instructions in the magazine for interfacing a Yamaha DB50XG WaveBlaster MIDI daughterboard, intended for use with a PC soundcard, to an Amiga.

Continue reading

A Clockport for the Chameleon64’s Minimig?

Christian Vogelgsang, who a couple of years ago collected together the git repository of Tobias Gubener’s Minimig_tc64 project and ported the OSD menu code to GCC – thus sparking my interesting and subsequent foray into the world of FPGA development – has been hard at work once again!  This time he’s succeeded in adding support for the Chameleon’s clockport to the Minimig core, which means that in theory a good proportion of clockport peripherals should now be usable!

So far it’s only been tested with a Silver Surfer card – it will be most interesting to see which cards work without any further changes, and which prove to be troublesome.

Links to source, binary and details of a small hardware hack required can be found on Chris’s blog – but before downloading and trying them take note that

THIS MUST NOT BE USED WHILE THE CARTRIDGE IS PLUGGED INTO A C64.

The IO mux code has been stripped right down to bare bones to give usable clockport performance, and the synchronization with C64 bus cycles is no longer present, so a connected C64 will definitely crash, and may even be damaged.

With that warning in mind, do check out his work here: http://lallafa.de/blog/2013/09/a-clockport-for-chameleon64s-minimig/

The next generation of Retro fans?

My nieces visited today – and spent a very happy hour playing two of my old favourite games – Pang and Lotus II. Both games went down extremely well with the girls despite being released about 10 years before they were born! While I still find a lot of fun in classic games, it’s encouraging to see that they’re enjoyable even to the iPad generation.

I was rather amused, however, that they were playing such ancient games on brand new, 2013 hardware: For Pang we used Majsta’s prototype Vampire 500 board, while we ran Lotus II on one of Till Harbaum’s prototype MIST boards!

Well that was nerve-wracking!

My Vampire500 board came with “some assembly required” – the FPGA, SDRAM and level translators were all in place, but installing the non-essential stuff – the micro SD slot and PS/2 socket – was left as an exercise for the user!

I want to press the Micro SD slot into service so that I can load alternative Kickstart ROMs, because my old A500 board has kickstart 1.3, and I need a more up-to-date ROM to test Zorro III autoconfig, and to run certain test software.

As it happens I haven’t done any surface mount work before, so I was a bit nervous about attacking an almost-one-of-a-kind board with a soldering iron – but after butchering the control board from a dead hard-drive and convincing myself that I *am* capable of soldering on that scale, I had a go at mounting the micro SD socket.  I was relieved to find that it has locating pegs that fit holes in the PCB, so at least I didn’t have to worry about alignment.

MicroSD

Well it *looks* OK – now to see if it actually works!

 

It’s Aliiiiive!

After spending a satisfying few hours tracking down a stubborn bug which turned out to be a mere bad solder joint, my old A500 motherboard is now able to boot using the Vampire500 board in place of the CPU!

FirstBoot

Unfortunately, since that means I can now play some old favourites on the board (in the name of testing, of course!) there probably won’t be much more progress today!

Pang

From the “What, are you nuts?” department!

I have an Amiga with a ZPU processor!

So what possessed me to create such a perverse combination? Quite simply that it synthesizes in about a third of the time that a TG68-based equivalent would, which speeds up the development cycle significantly while I’m bug hunting.

(I will admit there’s an element of “because I can” there, too!)

ZPUChipRAMTest

This not-very-exciting screenshot shows a simple program running on the ZPU, and testing the reliability of reads and writes to Chip RAM through the Vampire500.  The program simply sets up a 2-colour high-res screen, clears the framebuffer, and then repeatedly adds 1 to each longword within the framebuffer.  Any glitches in either reading or writing would thus be cumulative, so if I can leave the program running for a while and if the pattern on screen remains in nice neat columns (which it does), I can be sure that no glitches have occurred. (Or that any errors are perfectly consistent!)

Of course, a ZPU-based Amiga is of no practical use whatsoever, besides assisting in debugging the Vampire500 – but I find myself idly wondering how practical it would be to use a more modern CPU – such as, perhaps, the OR1200 – and then emulate the 68000 in firmware?

Anyhow, pipe-dreams aside, the next task will be to take a checksum of the Kickstart ROM, since ROM reads are the only kind I haven’t yet tested.

A New Distraction!

I have another new toy to distract me from all the things I should be doing!

Vampire500_InSitu

This is one of Majsta’s hand-assembled prototypes of his FPGA accelerator, connected to a spare A500 motherboard that I just happened to have lying around.  (I don’t currently have an A600 to play with, which is why Majsta sent me the less-mature A500 variant of the project).  While I’ve been trying to help with the SDRAM problems his project’s run into, there’s only so much I can do without access to the hardware.  Well, now I have that access!

The A500 variant of the project’s not yet as complete as the A600 version, and can’t yet boot from the TG68 processor, so now begins the fun of testing the various kinds of accesses to the A500 motherboard, and ironing out the glitches.

Because the whole project takes some time to build, while I’m testing and diagnosing I’m using a simple state machine to simulate the CPU, which looks like this:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;

entity DummyCPU is
    generic(
        SR_Read : integer:= 0;         --0=>user,   1=>privileged,      2=>switchable with CPU(0)
        VBR_Stackframe : integer:= 0;  --0=>no,     1=>yes/extended,    2=>switchable with CPU(0)
        extAddr_Mode : integer:= 0;    --0=>no,     1=>yes,    2=>switchable with CPU(1)
        MUL_Mode : integer := 0;       --0=>16Bit,  1=>32Bit,  2=>switchable with CPU(1),  3=>no MUL,  
        DIV_Mode : integer := 0;       --0=>16Bit,  1=>32Bit,  2=>switchable with CPU(1),  3=>no DIV,  
        BitField : integer := 0           --0=>no,     1=>yes,    2=>switchable with CPU(1)  
        );
   port(clk                   : in std_logic;
        nReset                 : in std_logic;            --low active
        clkena_in             : in std_logic:='1';
        data_in              : in std_logic_vector(15 downto 0);
        IPL                      : in std_logic_vector(2 downto 0):="111";
        IPL_autovector       : in std_logic:='0';
        CPU                 : in std_logic_vector(1 downto 0):="00";  -- 00->68000  01->68010  11->68020(only some parts - yet)
        addr                   : buffer std_logic_vector(31 downto 0);
        data_write            : out std_logic_vector(15 downto 0);
        nWr                      : out std_logic;
        nUDS, nLDS              : out std_logic;
        busstate                : out std_logic_vector(1 downto 0);    -- 00-> fetch code 10->read data 11->write data 01->no memaccess
        nResetOut              : out std_logic;
        FC                  : out std_logic_vector(2 downto 0);
-- for debug        
        skipFetch              : out std_logic;
        regin                  : buffer std_logic_vector(31 downto 0)
        );
end DummyCPU;

architecture rtl of DummyCPU is

type states is (write1,write2,write3,write4,write5,write6,read1,read2);
signal state : states := read1;
signal counter : unsigned(15 downto 0);
signal temp : std_logic_vector(15 downto 0);

begin

process(clk)
begin
    nResetOut<=nReset;
    if nReset='0' then
        state<=read1;
    elsif rising_edge(clk) then

        case state is

            when write1 =>
                data_write<=temp; -- std_logic_vector(counter);
                addr<=X"00DFF180";
                busstate<="11";    -- Write data
                state<=write2;
                nWR<='0';
                nUDS<='0';
                nLDS<='0';

            when write2 =>
                if clkena_in='1' then
                    state<=write3;
                end if;

            when write3 =>
                addr<=X"00BFE201";
                data_write<=X"0003"; -- Set OVL and LED as output
                nWR<='0';
                nUDS<='1';
                nLDS<='0';    -- Byte write to odd address.
                state<=write4;

            when write4 =>
                if clkena_in='1' then
                    state<=write5;
                end if;

            when write5 =>
                addr<=X"00BFE001";
                data_write<=X"FFFF";
                data_write(1)<=temp(6);    -- Echo mouse button status to keyboard LED.
                nWR<='0';
                nUDS<='1';
                nLDS<='0';    -- Byte write to odd address.
                state<=write6;

            when write6 =>
                if clkena_in='1' then
                    state<=read1;
                end if;

                when read1 =>
                addr<=X"00BFE001";
                busstate<="10";    -- Read data
                state<=read2;
                nWR<='1';
                nUDS<='1';    -- Byte read from odd address.
                nLDS<='0';

            when read2 =>
                temp<=data_in;
                if clkena_in='1' then
                    state<=write1;
                end if;

        end case;
    end if;
end process;

end architecture;

This state machine reads the value of CIA-A PRA and writes the value read to the COLOR0 register, so the colour of the screen changes in response to the mouse or joystick buttons. It also writes the status of the left mouse button to the LED bit, so the keyboard LED lights in response to the mouse button.

Next I shall test reads and writes to Chip RAM and reads from the Kickstart ROM for reliability.

Caching and Bus Snooping

When I first added the Turbo ChipRAM feature to the Minimig core, there were suprisingly few unpleasant side-effects. However, one side-effect did become apparent when I added the Two-way CPU cache.

On a real Amiga, it’s possible for the CPU’s bus and the chipset’s bus to operate independently, so if Fast RAM is available, the CPU can conduct a FastRAM operation at the same time as the chipset is reading or writing from Chip RAM. On the TG68-based Minimig variants, there’s only one type of RAM available – SDRAM, and no matter how they’re handled, Chip RAM and Fast RAM accesses ultimately all end up in the same RAM chip. Chip RAM accesses are coordinated by the Minimig’s chipset emulation, while Fast RAM accesses use a different port in the SDRAM controller and bypass the Minimig chipset emulation entirely, which is much faster. This is the key to my “Turbo ChipRAM” option – which basically allows Chip RAM to be accessed through the SDRAM controller’s Fast RAM port.

The problem is that the Fast RAM port is cached, so if the chipset writes to a piece of Chip RAM which happens to be in the cache, the cached data becomes stale, and next time the CPU reads that address it received the stale data, not the newly-written data.

The solution to this is to perform Bus Snooping. The CPU cache needs to monitor the SDRAM controller’s Chip RAM port, watching for writes, and any time it sees a write to an address that’s in cache, it must either update or flush the cached data.

I’ve taken the easier but slower option here, simply marking the data as invalid, forcing it to be re-read from SDRAM next time the CPU wants to access it. There’s scope to improve performance by updating the cached data and leaving it marked as valid. The cost for this would simply be the extra logic elements needed to store the written data temporarily and write it to the cache.

Source for the Two-way cache with Bus Snooping can be found here – and I’ll try and release a new version of the Chamemleon core in the near future with this change included.