I made some good progress tonight on getting the various peripherals working with the Xula.
Although I could try and hook up the entire FPGABeeCore and hope for the best there are too many places where things could go wrong. So I've been working on making sure each individual piece of the puzzle works before putting it all together - yesterday was VGA and a status panel display, today is keyboard, CPU and RAM.
Hooking up the keyboard was fairly straight forward. I used the PS2 Stick-It that I ordered with the Xula board and since I've not had any luck with my keyboard at 3.3V I went straight for cutting the track and installing a jumper to switch to 5.5V (as explained in the Stick-It board manual).
Next I brought in the PS2Interface and KeyboardPort components from FPGABee and connected the output scan code to the bottom 2 digits of the hex status display. A bit of work to map the PMod to XuLA channels to FPGA pins and a fresh build and... it didn't work. Pressing keys on the keyboard showed nothing.
But then as I was staring at the VHDL code trying to figure out the problem I happened to glance back at other the monitor and noticed something had been displayed. Now it worked.
I can't really explain this except to say I remember this keyboard worked on the Nexys-3 for a short period at 3.3V and sometimes not. It's like it takes a few second to charge up - weird, but it's now working flawlessly as best I can tell.
Anyway, here it is after pressing up arrow key: 0x75 is the scan code, the two lit up leds indicate key release (as opposed to press) and extended key code.
Next I added an instance of the T80 core, wired port 00 to the 8 leds on the status panel and wrote a tiny assembly language program to cycle the LEDs. Since I had no RAM yet, I had to be careful not to push, call, or otherwise reference memory.
It worked first go. Nice!
Although I had no RAM, I obviously needed some way to get the program code - I knocked together a little Python script to take the binary from the z80asm assembler and spit out a VHDL ROM component - see here.
The obvious next step was RAM. The Microbee Premium 128K that FPGABee emulates has more RAM than will fit on the Spartan chip on the Xula, so I needed to figure out how to talk to the Xula's SDRAM. Conveniently Xess provide an easy to use SD RAM controller core that handles all the nasty details. I simply wrapped it in a small component that handles the cross-domain clocking between the Z80 (3.375Mhz) and SD RAM core (100Mhz). see here
Although the Xula board has 16Mb RAM, only 8Mb is accessible since both of the bank select signals to the SD RAM component are wired together - giving just 2 banks instead of 4. Also since RAM has 16-bit words but no upper/lower byte select signals I decided the easiest solution was to just use the lower byte of each word - leaving the upper byte unused. This reduces the available RAM by half again - to 4Mb. Luckily I only need about 256K.
One of the debugging techniques I've used many times on the Spartan 6 is to simply light up a series of LEDs to indicate when each state in a state machine is hit.
debug_leds: out std_logic_vector(7 downto 0); -- output port process (reset, clock) if reset='1' then debug_leds <= (others=>'0'); state <= state_init; elsif rising_edge(clock) then case state is when state_init => debug_leds(0) <= '1'; when state_1 => debug_leds(1) <= '1'; end case; end if; end process;
In trying to get the RAM working, I tried this technique and started seeing some very weird results - states that should have been impossible to reach were getting lit up. In the end I noticed a warning the build log about some of the debug_leds bits being optimized away and assuming a value of 1 (ie: lit up).
I solved it by using an explicit register, rather than using the debug_led output ports directly:
debug_leds: out std_logic_vector(7 downto 0); -- output port signal debug_leds_r : std_logic_vector(7 downto 0); -- internal register debug_leds <= debug_leds_r; -- registered output process (reset, clock) if reset='1' then debug_leds_r <= (others=>'0'); state <= state_init; elsif rising_edge(clock) then case state is when state_init => debug_leds_r(0) <= '1'; -- use register when state_1 => debug_leds_r(1) <= '1'; -- use register end case; end if; end process;
With that little side track sorted, I soon found the issues with the RAM and now had a Z80 with RAM. Since I've never used this kind of RAM before I also wrote a simple ASM program to test reading/writing to RAM - no problems found.
Pretty happy with the progress so far. I'm still not sure the entire FPGABee implementation will fit on the smaller Xula board. After adding the T80 core I'm up to just over 50% usage - I'm hopeful but not sure.
Next up is to try FPGABee's SD Card controller.