This is a VHDL version of Amstrad CPC 6128 running on FPGA starter-kit NEXYS2 500k-gates from Digilent. A starter-kit is a board made for learning FPGA, so it is a standard FPGA development board.
Please refer to [mist-board CoreDocAmstrad] for the final user version, running on MiST-board.
Games running on it are : Tempest, Fruity Frank, Boulder Dash, Classic Invader, Xevious, Wiz'lair, Ghost'n'Goblins, Chase HQ, Xor, P47, Bruce Lee, Gryzor (romnation's one), Prince of Persia, Trail Blazer, Antiriad, Buggy Boy, Crazy Cars II, Donkey Kong, Axiens, Invaders, Arkanoid, Ikari Warriors, Barbarian, Barbarian II, Bubble Ghost, Super Ski, Action Fighter, Cauldron, Dan Dare I II and III, The Empire Strikes Back, Star Wars, Rick Dangerous I and II, Bomb Jack I and II, POP-UP, Mario Bros, Rock Raid, Rygar, Hyperbowl, Sapiens.
How to assemble it
You need:
- a "NEXYS2 500kgates" starter kit from Diligent [[1]] (1200kgates should be better for future version => in fact I choosen NEXYS4, it is same RAM inside) also in France : [[2]] or in Germany : [[3]]
- a "PMODSD" module for reading sdcard [[4]]
- an alimentation (cause they don't give it with starter kit) [[5]]
- optionally a DIGILENT USB JTAG (normally starter kit can be programmed directly by usb, but I don't have tested this way) [[6]]
- a 4GB SDCARD (no more), I have exactly a "SDHC 4GB class4 Verbatim"
- the binary of this project (candidate 002) : source+500k-gates binary [[7]] ; 1200k-gates binary [[8]]
- several ROM files: OS6128.ROM BASIC1-1.ROM AMSDOS.ROM (from JavaCPC[[9]]); MAXAM.ROM[[10]]
- one or more DSK files : TEMPEST.DSK[[11]] ARKANOID.DSK[[12]] FRUITY.DSK[[13]] BRUCELEE.DSK[[14]] CHASEHQ.DSK[[15]] WIZLAIR.DSK[[16]] XEVIOUS.DSK[[17]] BOULDER.DSK[[18]] CLASSIC_AXIENS.DSK[[19]] CLASSIC_INVADERS.DSK[[20]] GRYZOR.DSK[[21]] PRINCE.DSK[[22]] BUGGY.DSK[[23]] TRAIL.DSK[[24]] P47.DSK[[25]] SUPER_SKI.DSK[[26]] ACTION_FIGHTER.DSK[[27]]
A package that contains minimum set of ROM and DSK for filling simply sdcard [[28]] (OS6128, BASIC1-1, AMSDOS, MAXAM)
You have to:
- program FPGA with the binary file "amstrad_switch_z80_vga_sd.bit" of this project, for it I use Digilent Adept software and my USB JTAG cable
- format your 4GB SDCARD in FAT32 4096 byte allocation size
- copy ROM and DSK on SDCARD
- plug PMODSD on slot JC1 of starter kit, and set all 8 switches to 0
- plug VGA, and turn on starter kit
You can:
- plug a PS/2 keyboard, and type "cat"
- increment switch to select another disk at boot, if screen became RED, it's that binary value done by switches is too big, leds are doing a small animation when a disk is correctly loaded
- plug principal joystick on slot JB1 (Vcc 3.3v is common)
- plug another joystick on slot JA1 (Vcc 3.3v is common)
- plug a jack on slot JD1, one at upper GND plug, and second wire at next plug, just at left of it (if it was 3.3v Vcc, choose the right one instead)
Wires are plugs at upper part of pmod D.
From bottom to upper: [nothing: Vcc 3.3v] [blue: GND] [red: sound]
Sure you can connect a jack...
And two joysticks. On pmod B: bottom is (both) 3.3v, so joystick's common, next is (both) GND (not used), and others are joystick connections, to test manually :)
Video
Last news about this project
In March 2015, FPGAmstrad is stable on MiST-board, please refer to mist-board CoreDocAmstrad and mist-board amstrad bin core
In December 2014, FPGAmstrad is stable on NEXYS4, WIP, BOOTLOADER improved, VRAM improved (with BORDER colors !), RAM relaxed, no more timeout in keyboard key-press, Ghouls 'n' Ghosts, Macadam, a lot more to do. 14% of platform is used.
In November 2014, I bought MIST board, with two pro joysticks.
In September 2014, I bought NEXYS4, more powerfull than NEXYS2, with same external RAM, internal mini-sd, no PS/2 (it is a pmod option)... I have some patchs to make (MSB FAT32 offset). I would like to make a USB snifer also with it (usb to ethernet (wireshark))
Contact
I am Freemac, my IRL name is Renaud Hélias. I am a member of Tetalab [[29]].
You can contact me by mail to renaudhelias @t gmail d0t com, if you have questions about assembling this project.
Follow me on my google plus account!
Tests done
Great games that are running properly are listed at top of page.
On NEXYS4 version
Games that doesn't run are :
- moktar.dsk prehistorik.dsk: lag (key/joystick press lag)
- aigle_d_or.dsk: keyboard keys bad mapping evidence (pressing right arrow press also F3, then display "impossible" in this game in same time of moving, in fact it is not a joystick playable game) - this bug is located (some values appears double on keymap array)
- super_cauldron.dsk: strange disk sector size evidence (not implemented yet)
- ACPC_logon_system.dsk: text scrolling lag. This demo will be used for horizontal ink calibration (when I’ll buy a luxurious FPGA platform... I need in fact 224Ko of internal RAM to do it), and CRTC overcounts.
- gryzor.dsk: Pressing esc at start menu does activate music during game... no sound.
- atomic driver: does freeze (problem of interrupt cycle ?), it is a really simple dsk format.
Arkanoid.dsk stars use rupture address (changing address several time during display of one image), it is now supported on "candidate 001" version of FPGAmstrad
Gryzor and Prince of Percia use rupture ink/mode (changing ink and mode during display of one image), it is now supported on "candidate 002" version of FPGAmstrad
Crazycars2.dsk first car image use 32Ko of VRAM, it is now supported on "candidate 001" version of FPGAmstrad.
Ghouls'n'Ghost.dsk / Ecole.dsk does need RAM write when writing in ROM (RAM is beside ROM, hard to emulate with asynchronous SDRAM controler, MiST does use a hacked synchronous RAM done for that)
A lot of demos don't pass in NEXYS4 FPGAmstrad's version (I need to implement back the SDRAM hacked in MiST board FPGAmstrad version). CPCRULES demos is a cool ressource as it contains simple dsk formats.
Others bugs are about dsk format, in fact a dsk is uncompressed into RAM at startup, so all dsk access are direct:
- CP/M format: normally it runs, but in fact... it doesn't
- protected format: all format with strange sector size fail
- double side format: as you need to reboot in order to change disk, it's impossible to change disk while you are playing
- save: disks are writable, save is done in RAM, and it’s not persistent.
TODO :
- Spindizzy
- Asphalt (tested on alpha but not on last version)
- Ghostbusters, Ghostbusters 2, Rambo III, Sorcery, Commando, Yie Are Kung Fu 2, Kane (shoot X/Y)
On MiST version
Games that doesn't run are :
- commando.dsk: pixels that should be deleted are not deleted (only VRAM &C000-FFFF seems used)
- atomdriv.dsk: freeze (the music does freeze also).
- split ink demo.dsk: (from cpcrulez) : may help about ink raster calibration.
- superski.dsk: strange no up/down joystick on first menu, no up/down joystick during big jump. In slalom you can't stop. This game did run correctly before interrupt r52 counter reset implementation.
- buggyboy.dsk: I found one dsk running correctly and another not : problem on this last one is about turning left/right effect : the car continues straight.
- protext.rom: In edit mode, if I press continuously one key, it does write ten letter and freeze/crash. As a number is incremented in live at top right, I think also about an interrupt vector not long enough.
crazycar.dsk does use lowerVRAM=00 (&0000-3FFF and &C000-FFFF area for VRAM)
crazycar2.dsk does use lowerVRAM=10 (&8000-BFFF and &C000-FFFF area for VRAM) while overscan presentation, and lowerVRAM=00 (&0000-3FFF and &C000-FFFF area for VRAM) during play.
Game | Welcome | Title | Game |
---|---|---|---|
CrazyCar2 | 1011 | 0011 | |
One | 0111 | 0011 | |
Scarabus | 0111 | 0011 | |
Ace | 0010 | ||
Tintin | 0111 | 1011 | 0111 |
Devil's Crown | 0001 | 0011 | |
Mach 3 | 0111 | 0111 | 0110 |
Paranoia (1994) | 1011 | 0001 | |
World Class Rugby | 1011 | 0001 | 0001 |
XOR.dsk does use lowerVRAM=01 (&4000-7FFF and &C000-FFFF area for VRAM)
Effort done
Instruction timing
I tested instruction timing of T80 compare to instruction timing of JavaCPC emulator. I deduce synchronization of Z80 with CRTC on M1 signal by WAIT_n insertion in order to have a multiple of 4 Tstates per instruction. I deduce also one WAIT_n inserted during MEM_WR operation (yes I log testbench T80, I’m crazy)
I just made a test bench log of T80 (log of instruction's M1, and first M1 coming after knowing that I send a lot of NOP after my instruction), and compare it to a JavaCPC timing array. Some instructions was not tested (interrupt wait, and special timing (instructions with change timing)), but all others passed correctly.
Test of a real Zilog 80
Code name : Z80fx2bb, real Z80@2MHz (instead of 4MHz) on fx2bb extension card.
For it I plug all wires simply from 1 to 40. Some wires are cut, some are Vcc, others GND. Z80 output are directly connected, Z80 input are pull-up with red-red-red resistors (I like red), Z80 is powered 5v (pmod can give 5v using jumper). In fact z80 is so old component that powering it 5v do output 3.3v.
In fact the only difference between T80 of opencore and real Z80 is that T80 run on rising_edge, and Z80 run during low state. Test past with little modification of sequencer forcing it do nothing during low state of z80, resulting a downclock (memory is too overclocked with this sequencer modification), perhaps using buffer on address bus and data bus could solve this detail... but as it runs for me it is not a problem.
VRAM to VGA (aZRaEL_vram2vgaAmstradMiaow)
RAM and VGA does not use the same frequency. I add between them a magical VRAM having two clock entries and solving this problem automatically.
The magic RAM in FPGA, getting two clock entry, is not as magical as I thinked : it does solve clock equations using the clock manager (DCM) and BUFG components (saying phase is freedom between input and output). If you want a set of clock synchronized do not add a BUFG in one of its wires. If you don't care about synchronize of two clocks, just add it and then it will help to solve finer and greater the clock manager equations of DCM while compiling.
Alignment of HSYNC Interrupt
Interrupt are respected since version "candidate 001" of FPGAmstrad.
JavaCPC running norecess's "using-interrupts" code [[30]]
It could be interesting to test this asm code on next version of FPGAmstrad.
ram_palette
VRAM contains 800x300 amstrad pixels (VZoom x2), displayed 640x480.
RAM_palette contains the ink list and the mode for each line of VRAM.
Sniffing of a real Amstrad
I listen to some wires of my Amstrad CPC 6128 plus, but I can't access VSYNC/HSYNC output of CRTC, so I have to buy another model in order to do this test. In fact you can listen at clock of Amstrad and transmit it to FPGA DCM component, resulting a accelerated clock sequence, that's it, with FPGA DCM you can overclock output Amstrad clock signal in order to insert more operations, I use this tip for listening signals and save them inside starter kit asynchronous RAM (write, stop write, write, stop write... I’m a perfectionist paranoid...)
You can power Amstrad CPC using extension port, applying 5v. By doing it, power down button of Amstrad doesn’t run. Using this way you reach a common 5v power between starter-kit and Amstrad. I connected wires from extension port directly to FPGA, as they are used just for listening.
TODO : UpperROM policy
What does I read when a UpperROM is not physically here ?
- if no ROM do read RAM ? [actually what I do]
- if no ROM do read a fixed data value x"00", x"ZZ" ?
- if no ROM do read a default ROM ?
It shall be nice to do somes tests about that on original CPC.
DONE: Another disk selector
In first version of FPGAmstrad (NEXYS2) I used switches for disk selection. As final FPGA platform doesn't have any switches set, I have to add an BASIC instruction for it, something like "OUT &CAFE,disk_number" could be fine.
Since FPGAmstrad in NEXYS4, disk selection is done from keyboard, using "OUT &CAFE,disk_number" instruction. A reset key was added also. "PRINT INP(&CAFE)" does print current disk selected number.
TODO : A real disk drive
I think that great evolves for this project should be use of a real second disk drive.
TODO : A X/Y input
I want to work also on screen-pen entry, is there a manner to detect an analog X/Y as pen or gun ? YES : Markus Hohmann does it, he implements the lightgun on JavaCPC-GX4000 using mouse :)
http://cpcrulez.fr/hardware-pistolet-magnum_light_phaser_ACPC.htm
register 11,12 and 13 ?
TODO : A SCART output
In order to plug FPGAmstrad on TV, and help debugging. And also to test a simple scan-doubler.
DONE : More ROM and RAM
BaseRAM is 64KB, extension RAM is 512KB, FPGAmstrad has now 576KB of RAM.
-- "001" dsk_A(19:0) DSK_A -- "0000000" "xx" ROMBase L U0 U7 U1 <= finally only L is used -- "0000001" "xx" ROMBase 4 5 6 7 <= finally not used -- "0000010" "xx" RAMBase -- "0000011" "xx" RAMBank page 0 -- "0000111" "xx" RAMBank page 1 -- "0001111" "xx" RAMBank page 3 -- "0101111" "xx" RAMBank page 7 -- "100000000" ROMBank 0-255
ROM files does finish with file extension from .e00 to .eFF, LowerROM is a fixed name. So you can now use one LowerROM and 256 UpperROMs on FPGAmstrad.
TODO : Scanline
Scanline is an effect existing in CRTC used by Wolfenstrad demo.
It is also a simple visual effect to enable/disable, making games looking like old CRT monitors.
TODO : FAT32 fragmented files support
Actually, on final platform somes files are managed by OSD (on screen display), so you feel as if some files are deleted after a certain time (of hard reboot), but in fact files are still here : you just have to defrag de SDCARD.
To fix that, FAT32 not fragmented files need to be supported.
Agile method
This project results of a experiment applying Agile method. Finally this project has taken 5 months. The result is a standalone platform that can run several games of Amstrad. Normally, I had to dedicate 2 months on this project, but as result was so great, I continue to a standalone and better version.
This project was done for my father birthday, so sorry that I can't deliver it yet :^)
One day perhaps I'll write one book, or write a lot of wiki page by there, presenting step by step this adventure :)
But I want really to validate project before doing it. So it will stand a few I think.
Minimal Amstrad Architecture: Build your own Z80 Amstrad Computer
I explain here my first great experiment, having Amstrad saying hello :)
First schematic: Z80+RAM+ROM
Z80 can address from 0x0000 to 0xFFFF.
RAM is from 0x0000 to 0xFFFF.
You have lower and upper ROM, so starting at address 0x0000 you put OS464.ROM, and at address xC000 you put BASIC1-0.ROM.
- When Z80 do READ MEMORY, you read ROM
- When Z80 do WRITE MEMORY, you write RAM
- When Z80 do WRITE IO, you do nothing
- When Z80 do READ IO, you response it DATA=0x00
When you run this schematic on FPGA, RAM changes!
Second schematic RAM+VGA
With JavaCPC, when you snapshoot, and hex edit result file, you see RAM content starting at a certain address.
Do "paper 2", "cls" on JavaCPC, the screen became RED, and then save a snapshoot, you can see that last part (from 0xC000 to 0xFFFF) had change from a lot of 0x00 into a lot of 0xFF
So last part of RAM is used for video (it's shown on Quasar [[31]] and other legend websites...)
For making my VGA module, I take a look at UNIX "modeline" command that give us all timing for VGA signals, and it run :)
After having a VGA module displaying a RED screen (yeah!), I made it scanning last part of RAM (from 0xC000 to 0xFFFF), and I solved the puzzle.
RAM contain lines of 0xFF, each finishing by 0x00, but lines are not in great order
Third schematic Z80+ROM+RAM+VGA
Goal is: RAM empty at startup, VGA displays hello after run.
So you put the two last schematics together and tadam... got a problem.
The problem is that two components are accessing RAM in the same time: the Z80 and the VGA, so you had to make a sequencer. A sequencer is simply a counter fed by a clock: 00, 01, 10, 11. And you manage work task like this:
- 00 RAM WRITE start from Z80
- 01 RAM WRITE end from Z80
- 10 RAM READ start from VGA
- 11 RAM READ end from VGA
You plug sequencer(1) on z80 clock and not(sequencer(1)) on VGA...but another problem appears: VGA uses 25MHz speed for scanning RAM. So Z80 has to use same speed xD
To solve this problem you can use a special RAM done for this problem, a RAM that you can WRITE at a certain speed, and READ at another speed, this magic component is called ramb16_s16_s16. Note that they have no problem to write simultaneously on two RAM components, so that you can dump video RAM content using starter kit RAM, and you can display VGA using FPGA internal ramb16_s16_s16 RAM.
First and second schematic video
http://www.youtube.com/watch?v=9Y-RvYMxnbE
Here I do program FPGA with a serial port RAM filler (homemade), and then I do upload ROMs (sooo long, you can show several blue progress bars), and then I do program FPGA first schematic : Z80+RAM+ROM, executing it, and then I do program second schematic : VGA is plugged on FPGA platform and does display "Ready".
Just about the "RAM filler", filling a RAM with data is perhaps the more important thing* about using FPGA platforms, do just remember that a RAM does keep its content as long as voltage is entered on it. So you can program FPGA without erasing RAM content. So you can program three FPGA programs : one for filling RAM, one for running a program using RAM, one for dumping RAM. Here I did use a serial transmit, but in fact, in last versions of FPGAmstrad, I do use a SDCARD with a homemade bootloader (filling RAM from SDCARD content)
I did use also Mock components, in first version of FPGAmstrad, FPGA platforms was too small, so I could have FPGAmstrad only with cool sound and low graphics, or else FPGAmstrad with cool graphics and no sound. Both using 100% of this old FPGA platform. A Mock component is a fake component, an empty one, just telling back "I'm OK, please thrust me I do exist" to others components trying to communicate with me (it's a entity with same in out ports as original but using constant output values)
- * [[32]]
Third schematic video
https://www.youtube.com/watch?v=w_wifI-bRJc
Bootloader
After having a running Amstrad, I had to turn it into as standalone version. In fact before this step Amstrad ROM was put into RAM using serial port (#RAM_dump), it was slow, and Amstrad ROM was lost when I unplug electricity.
Bootloader FAT32 SDCARD is the only component playing with sdcard. Its tasks, all launched at boot, are:
- deploying ROM file on physical RAM
- deploying Nth DSK file on physical RAM, N being the binary number selected by 8 switches
Clock sequence
Original Gatearray of Amstrad is a sequencer (counter plugged with a clock), it manage synchronization between video card and z80 and memory access.
Historically there is a link between CU of CU/ALU, and... control bus and... how making your own sequencer. But I will say no more in order to not disturb these text part xD
Whatever, I made my own sequencer here in form of a bus of 4 wires called CLK4. CLK4 execute a simple repetitive sequence like 0001 0010 0011... CLK4(3), the last wire is directly connected to Z80 clock entry. Component not using explicit CLK4 as clock entry are generally using a not(CLK4(3)) entry, in order to do operation not as same time than z80.
Real Amstrad use buffer memory in front of each address and data access, and real z80 is clock low state active. Normally if you follow datasheet of z80 you know how to map memory following CU comportment. Or you do as Amstrad, saying that z80 CU sucks, I create my own sequencer, managing all my memories access, alternating CRTC work and z80 work with little synchronization, insert by the way more pixels that can support my small CRTC...
How to use a sequence in VHDL :
if rising_edge(master_clk) then if seq="00" then elsif seq="01" then elsif seq="10" then else end if end if
What not to do :
if rising_edge(seq[0]) then end if
Because that can auto-generate bad unwanted sub-clocks...
Clock sequence : version 2
In fact, it's better to create you clock sequencer wiring each CLK and not(CLK) directly from DCM, in this case you enter in time constraints norm, and then rules/checks are done on every _edge instruction. Choosing only one sort of _edge (rising or falling) seems better also. Using that way you just have more "bad compiling error" shown, helping you creating a better code.
Clock sequence using counter plugged with a clock was in fact out of law (but running fine in my first versions of FPGAmstrad as I'm a good blind developer), because output are not under clock constraint : just think about that a "not" component added just after a clock wire is out of -time constraints- law... destroying "time constraint" solver (the one telling you when your code is bad (can be better))
How to tickle JavaCPC
JavaCPC is developed in Java, and Java is so cool (Java is better computer language ever, and VHDL is better FPGA language ever =P)
I tickle JavaCPC in order to compare its components to my ones.
Emulator Architecture
while (¡stop_emulation) { executeCPU(cycles_to_execute); generateInterrupts(); emulateGraphics(); emulateSound(); emulateOtherSoftware(); timeSincronization(); } Figura 2. Basic Emulator Algorithm.
Extracted from the book [[33]],
Using this way, emulators reach a better running time. They don't need to implement the system-bus architecture[[34]] (CONTROL DATA ADDRESS) crossing Von Neumann architecture[[35]] (CU ALU MEM IO).
Component Architecture
Java is an object language, so having new, set, get, for each of its objects.
A Component Architecture in object language has a special cycle life :
- Build all components (new new new new new)
- Plug all components together (set set set set set)
- Run a main component.
Main component is Z80 on JavaCPC. In fact, JavaCPC's Z80 is already configured in order to run each instruction with a certain timing : a timing already synchronized with CRTC (each instruction takes 4 Tstates or 8 Tstates, Z80@4MHz CRTC@16bit@1MHz so drawing 8 colored pixels on mode 1 takes 4 Tstates)
My main component is #Clock_sequence
In real Amstrad, main component is GateArray.
The fact of choosing Z80 as main component just respects the emulation architecture.
Java debug mode
You can run JavaCPC on debug mode in Eclipse, and insert breakpoints.
It's useful for listening wires, and cut them. You can in live pause debug, cut a function and continue run.
Cut a wire, cut a function
Wire are done for sending message, a message in programming is a function call.
When we cut a input wire, we generally plug it to GND or Vcc.
For cutting a function, you have to insert a cut on it. A cut it's a return. You can insert a (very bad) forcing cut as:
if (1==1) return 0;
everywhere. So function is ended at this moment and next lines became death code. It exists quality code program for checking death code, because it's generally a bug of development, normally we put code in comment.
It is the way I used in order to induce JavaCPC, comparing it with my project.
USB joystick
Sniffing USB frames
USB uses two wires in order to transmit frames, green and white, each with two logical values: 0v and 5v.
Let's plug a joystick on PC, if you listen at its two wires, you can sniff a USB transmission. Finally you can save it for example on RAM.
These two wires can be traduced into one with four states: 00 01 10 11.
One of this states is sleep state, in fact it depends on USB mode you use.
USB mode: USB1 or USB2; low speed, full speed or high speed
For sampling, I speed up five times the saving speed on RAM. I succeed sampling an USB1 transmission: "Logitech dual action USB joystick", and an USB2: "Sony PS3 USB joystick". PS3 joystick is not stable enough with my FPGA, but Logitech joystick is correct.
pull up and pull down
If you respect USB protocol, you have to plug some pull-up and pull-down resistors and some capacitors. But as I am a bad electrician, I just simulate then in VHDL, they are important because they cause USB speed negotiations. You also have an electronic mechanism in order to detect presence of joystick plug, I don't care about it.
For reaching which wire you have to pull-up or pull-down, here the tips :
- For slave (ideal for sniffing) : just take your USB1 joystick without plug it, just supply it (+5v red, 0v black), and test while-black and green-black with voltmeter, if you have got 5v then put a VHDL pull-up, and if you have got 0v then put a VHDL pull-down.
- For master (ideal for creating a mini-host) : just take your PC USB1 port, and test while-black and green-black with voltmeter, if you have got 5v then put a VHDL pull-up, and if you have got 0v then put a VHDL pull-down. Normally you result two pull-down.
Synchronize, decode and check USB frames
One time sample is done, it is not readable. In fact USB frames are synchronized (they started with a certain synchronization pattern), encoded (NRZI), and checked (CRC). CRC type depends on frame length. Encoding is done for synchronization optimization.
Then using USB HID manual, you can understand type of frames, and author of them, and remark that the author alternate: USB master (PC) or USB slave (joystick)
You can use some "USB sniffer software" in order to understand more easily some frames contain, but they generally don't give all frame, and full frame.
great crc check example in perl - offered by www.usb.org
Build a minimum USB master frames state-machine
Let's just plug a USB joystick on FPGA, directly, permanently, thinking about minimum coding size : we can't implement full HID USB protocol on FPGA ^^'
Objective here is to build a minimum state-machine graph, having for transaction between state a "frame transmission". It is normal on USB protocol to have error of transmission, so you have also to put "error frame transmission" on the graph.
At stabilization, you finally switch between two states, one sending a certain frame that contains at different offset simply certain values of joystick button.
At start, some frames are employed for "next frame description", they can generally be ignored, as our USB architecture is fixed and minimal (one USB joystick, that's all)
go further with USB sniffer
A better way to snif USB could be generation of TCP/IP packets encapsuling USB packets, and to record them directly on PC from a RJ45 plug, using this way I could save more than 10 seconds of information transmission (RAM size is limited on FPGA platfoms)
Why NEXYS2 500kgates starter kit
Xilinx schematics
Xilinx webpack software permit drawing schematics as book schematics, My point of view is : "For programming a FPGA, you draw a schematic as old books and just press one button. Each component on this schematic can be edited, in a language called VHDL".
My source code is not Altera compatible because of schematics drawn, but webpack can export vhdl code from schematics if you want.
RAM dump
A starter kit that contains a RAM component, that you can dump separaly : you can change schematics without loosing RAM content ! - and so write a schematic for dump only ;)
While power is on you can:
- programming FPGA with a program/schematics done for filling RAM
- press reset button
- programming FPGA with a program/schematics done for using RAM
- press reset button
- programming FPGA with a program/schematics done for reading RAM
- press reset button
My own made program does it with poor serial port, so for dumping all RAM content it takes about 3 hours, and for dumping Amstrad RAM part it is about 15 minutes.
On [Diligent NEXYS2 official page], you can download a "Onboard Memory controller reference design" that contains explanation and VHDL source code about dumping on RAM/ROM of NEXYS2 directly from PC (usb port). I didn't tested this yet, but it is certainly a nicer approach :P
FPGA internal RAM size
It's to know that a FPGA chip contain 45Ko internal RAM (360Kb for NEXYS2 500k-gates, and 504Kb for 1200k-gates) so you can't insert a dsk inside. This internal RAM is already used in part by T80 (z80 from opencores), by the soundchip, and for special RAM ramb16_s16_s16 (RAM with two different speed one for writing another for reading, in fact two RAM with a common part) that I use for VGA mode.
VHDL components size
T80 (z80 processor) take 100kgates
Yamaha sound chip (from fpgaarcade) take 50kgates
InterruptGenerator + VGA mode take 50kgates
Bootloader (for standalone) take about 120kgates (FAT32 protocol, SPI protocol, DSK protocol)
Actually the project take about 99.9% of 500kgates. But I think that TV mode will take a lower size. A bigger size shall be great for Amstrad CPC Plus version, if JavaCPC evolve, and then if I evolve ;)
Source code
The project binary downloadable on #How_to_assemble_it section contains in fact source code and the binary file (.bit)
This is a simple zip of project folder.
The project was done using Xilinx webpack
It contains some direct drawn schematics, and VHDL components
Schematics
I explode the main schematic into a task by component, so the schematic is big.
Starter kit use only one RAM physical component for RAM ROM and DSK alignment, so I had to manage accesses (it is possible in fact because Z80 is a sequential processor)
My clock take 4 wires, in fact it exists a clock sequence #Clock_sequence (during 1 z80 tic, I do several things)
RAM is done for being dump, comparable to JavaCPC snapshoots.
Components
sound chip is ym2149 fpgaarcade one, patched.
PPI chip is 8255 CPCWiki one, patched.
PWM chip is PWM_DAC fpga4fun one. Warning, using PWM as it can result into a low quality sound, in fact if sound is stopped at 50% of voltage, it generates continious high-sound or ultrasound during this "silent" moment (cause it can't really make 2.5v with only two output 0v and 5v), so you can hear whistle. A solution is to add another clock entry, one clock is about executing PWM algorithm and another one is about pushing input digital sound data, that result a higher quality sound, that is what I've done in MiST-board version of FPGAmstrad.
In NEXYS2's version of FPAmstrad, PS/2 keyboard had a strange internal hardware timeout, it can be shown using game "Macadam" (pinball), I solved this problem since NEXYS4's version of FPGAmstrad (and then also in MiST version). Left shift is the true shift, right shift is mapped into another key, so in Macadam you can redifine keys... and play flipper using left and right shift keys :)
VGA display component use same parameters than unix modeline command
Big components as SPI on BOOTLOAD use state-machine led debug : an integer contain the state of state machine, and this integer is displayed on 8 leds so you know where you are, it's for that I add several crash state in order to understand why and where component crash. In MiST-board, this is displayed on the five 7-segment I added in OSD, I add also a input in order to select one or another state machine.
This project doesn’t use IBM ROM of NEXYS2. Be careful if playing one day with it, physical IBM ROM is plugged on bottom of physical RAM and can put death bit for some byte address. In fact my 8th byte is death; it's why I turn always my upper RAM address bit into 1. I think that I have written one time on a ghost part of ROM, and it was fatal...
And thanks
Certainly first thanks to Markus Hohmann, for having programmed a Java version of CPC, I love Java and VHDL, so this project comes from this Java Amstrad emulator.
Secondary Steve Ciarca, author of "Build your own Z80 computer" (1981), so nice book.
Then the author of the VHDL version of yamaha sound chip : fpgaarcade. And opencores for the Z80 (T80)...
And websites that give access to so much old Amstrad resources like :
- CPCWiki :) the Amstrad community
- Quasar Net fr lost legends
- Grimware lost schematics knowledge
- CPCRULEZ fr assembler and legends
- Genesis8 fr games news
- Amstrad TODAY fr a nice link list
And more :
- CPC GAMES REVIEWS large illustrated dsk image database
- JavaCPC emulator Markus Hohmann
- CPCMANIA plug Amstrad on TV
- Bellaminettes fr Artist drawer -nice girls- from ACBM magazine - Les puces informatiques - Sasfepu
Others tricks
If you aren't ready yet, here somes experiments (youtube) on real Amstrad :