1
0
mirror of https://github.com/mist-devel/mist-board.git synced 2026-02-06 16:14:42 +00:00
Files
mist-devel.mist-board/tutorials/soc/readme.md
2018-09-10 10:52:19 +02:00

768 lines
44 KiB
Markdown

What is this?
-------------
This part of the tutorial was presented in [issue 26 '15 of the c't magazine](http://www.heise.de/ct/ausgabe/2015-26-Mit-FPGAs-Retro-Chips-implementieren-Teil-3-3010854.html).
Each lesson presented here explains a single aspect of the MiST
board. The lessons are not VHDL or Verilog tutorials. Instead the
lessons include all files required to build demo setups for the MiST
board. They come with compiled and synthesised binaries so you can
test run them before getting into the details.
Each lesson focuses on one aspect like the VGA output, then SDRAM, the
SD card etc etc. Most lessons use a VHDL Z80 CPU and implement a small
but fairly complete system on a chip (SoC) which can be used to
experiment with the lessons. But it can also be used as a basis for
bigger systems.
The intended audience of this are people who already have some basic
VHDL or Verilog knowledge and know how to use a tool like Quartus and
who want to learn how to use the various peripherals on the MiST
board.
Unless otherwise stated all code included comes without restrictions
and you can re-use it in any way be it for closed source or open
source projects. Most included third party code (T80, YM2149, PS2)
comes under GPL or simimlar license and may e.g. not be used closed
source projects or the like. Please have a closer look at the files
you intend to re-use for your project
[Lesson 1](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson1): A VGA controller
--------------------------
![Lesson 1: VGA checkerboard](lesson1/lesson1.png)
A 160x100 pixel VGA controller based on the 640x400@70Hz VGA mode. A
simple b/w checkerboard is being displayed. The Video clock of 25.175
MHz is generated from the 27 MHz system clock using a PLL.
The VGA controller mainly consists of the two counters, one to count
the pixels per line (h_cnt) and one counting lines (v_cnt). Both
counters are used to generate the horizontal and vertical sync signals
and to determine the time where pixels are to be displayed. The
horizontal counter is directly updated by the pixel clock. The
vertical counter is updated once per line only.
The VGA controller has six output bits per color. On the MiST board
these are fed into a resistor ladder (r2r) which is used as a digital
analog converter to generate the analog video signals. Six bits are
sufficient for 2^6=64 shades per color resulting in a total of 262144
colors in total. Using PWM techniques more colors are
possible. E.g. the Amiga AGA core does that. This demo however just
displays a black'n white checkerboard.
Links:
- https://eewiki.net/pages/viewpage.action?pageId=15925278
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson1/soc.rbf) renamed to `core.rbf`
[Lesson 2](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson2): Video memory and embedded ROM
---------------------------------------
![Lesson 2: VGA image](lesson2/lesson2.png)
The VGA controller is now being equipped with 16000 bytes of embedded
FPGA RAM as video memory (VMEM/VRAM). The resulting video controller
can display 256 colors in RGB 332 format (3 bits red, 3 bits green, 2
bits blue). A demo image is placed into an embedded ROM and copied
into screen memory at start up.
The graphics needs to be in 160x100 pixels in RGB332 format. The
img2hex.sh shell script uses the Linux tool "avconv" (on older distros
it may be named "ffmpeg") to generate a matching raw image from a
160x100 pixel PNG image. The resulting raw image is exactly 16000
bytes in size. img2hex.sh then calls the srec_cat to convert this into
intel hex format.
The ROM has been generated using Quartus' Megafunction wizard. It allows
to specify a intel hex file as the data source for the ROM.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson2/soc.rbf) renamed to `core.rbf`
[Lesson 3](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson3): Z80 CPU and RAM
-------------------------
![Lesson 3: CPU](lesson3/lesson3.png)
The T80 Z80 CPU core is being added. 4 kilobytes of RAM are added for
the CPU as well as 4 kilobytes of ROM. ROM and VRAM share the same
memory region as ROM is read only and (our) VRAM is write only. On
most systems video memory can be read and written which is quite
useful when altering video contents. In our SoC we implement the video
memory write only which is not the usual way to do it. But a platform
like an FPGA allows us to do this and if it turns out to be a bad idea
we can easily change this. But being able to map VRAM and ROM to the
same address space makes efficient use of the 64k address space the
Z80 CPU offers.
The CPU is clocked at 4 Mhz which is additionally to the VGA clock
generated by the existing PLL.
The ROM contents are compiled from a C source using the SDCC compiler
(http://sdcc.sourceforge.net). SDCC generates a intel hex file which
is directly included into the ROM by Quartus' Megafunction wizard like
the image data in lesson 2.
All memory is decoded only partially which means that the 4k ROM at
address 0x0000 is mirrored 7 times in the lower 32k memory area
(A15=0). The ROM shows up at addresses 0x0000-0x0fff, 0x1000-0x1fff,
0x2000-0x2fff, 0x3000-0x3fff, 0x4000-0x4fff, 0x5000-0x5fff,
0x6000-0x6fff and 0x7000-0x7fff. The 16000 bytes video of video memory
is mapped twice and can be written at address 0x0000-0x3ef7 and
0x4000-0x7e7f. Finally the 4K RAM is mapped to the upper half of the
address space (A15=1) and can be read and written at 0x8000-0x8fff,
0x9000-0x9fff, 0xa000-0xafff, 0xb000-0xbfff, 0xc000-0xcfff,
0xd000-0xdfff, 0xe000-0xefff and 0xf000-0xffff. The SDCC compiler by
default uses 0xffff for the stack going downwards and it used the
memory region from 0x8000 for global variables. The aforementioned
mirroring allows to use the default SDCC memory layout with only 4k
RAM. We can do this as long as we don't need the address space for
other purposes.
The test program is a simple graphics demo. It doesn't use any global
variables but uses the stack for local variables. Thus the running
demo shows that ROM as well as RAM are working as well as the video
memory, of course.
Links:
- https://de.wikipedia.org/wiki/Zilog_Z80
- http://sdcc.sourceforge.net/
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson3/soc.rbf) renamed to `core.rbf`
[Lesson 4](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson4): SDRAM
---------------
So far we've been using FPGA internal embedded RAM. This is very easy
to implement and use and incredibly fast. Unfortunately there's only a
little more than 70 Kilobytes of embedded memory available inside the
MiST's FPGA. Therefore the MiST comes with additional 32MBytes
SDR-SDRAM.
SDR-SDRAM is a more modern memory type than the DRAM that was used in
the homecomputer age. But it's also significantly older than the
latest DDR4/5-SDRAM memories todays computers use. The latest memory
types can be very fast under certain conditions but are very complex
to control. The usage of SDR-SDRAM was a useful tradeoff between speed
and ease of use. Furthermore modern RAMs don't match the retro
requirements very good.
The MiST comes with a 133MHz 16 bit wide SDR-SDRAM. This means that
the RAM can be clocked at up to 133 Mhz and that it transfers 16 bits
(two bytes) at once. A SDR-SDRAM uses a synchronous protocol to access
its contents unlike DRAM which was asynchronous. In the first access
stage (RAS cycle) a part of the desired address information is sent
into the SDRAM. After a certain pause the second half of the address
information (CAS cycle) is sent to the SDRAM and finally after another
pause the data itself can be read or written. The lengths of these
pauses depend on certain SDRAM parameters and on the clock that's
actually being used to access the SDRAM.
On the MiST a typical single SDRAM transfer requires 8 clock
cycles. Thus the SDRAM is typically clocked at 8 times the CPU clock
so the SDRAM can perform a full access cycle during one CPU
cycle. Since the CPU is clocked at 4 Mhz in our SoC the SDRAM is
clocked at 32Mhz. The 32Mhz are again generated by our PLL and the
4Mhz are now derived from the 32Mhz by dividing it by 8.
There is a counter "q" inside the SDRAM controller [`sdram.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson4/sdram.v) which
permanently counts from 0 to 7. This counter synchronizes itself to the
CPU clock to make sure the counter always starts with 0 at the begin
of a CPU cycle. When running at 32Mhz and with one full memory
transfer every 8 cycles the resulting total access time is 250ns. This
was a typical RAM access rate in the age of home computers. The SDRAM
supports a clock of 133Mhz and thus access times of ~60ns are
possible. Special burst access modes of the SDRAM can be used to read
more than 16 bits in one access cycle. But these must be consecutive
memory contents and require a CPU to have caches to increase the
system performance. Retro CPUs usually don't have that. The limit for
single random accesses is ~60ns (this is actually still the same with
modern DDR RAM).
The SDRAM has a 16 bit data bus but our SoC is a 8 bit system. We thus
simply ignore one half of the data bus. As a result only 16 of the 32
MBytes can be addressed. This is still much more than the Z80 can
easily handle. The Z80 has a 16 bit address space giving a total of 64
kBytes directly accessible memory. The SoC is currently using the
upper half of this for RAM. Thus only 32 kBytes of the SDRAM is
actually being used. It would be possible to implement banking or the
like to give the Z80 access to more memory. But this requires special
support in the software which we'd like to avoid.
SDRAMs need to be initialized before they are fully operational. The
[`sdram.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson4/sdram.v) contains a simple logic to do this once the PLL reports that
it's generating stable clocks via its locked signal. The CPU is being
kept in reset a little longer to make sure the SDRAM is ready once the
CPU starts running.
The SDRAM timing is quite critical. One result of this is that a
second 32Mhz clock is generated by the PLL with a small offset (phase
shift) of -2.5ns. This clock is fed into the SDRAM. This makes sure
that the FPGA and the SDRAM aren't changing signals at exactly the
same time. Instead one of them changes signals on one clock and the
other component sees stable signals when it's own slightly shifted
clock changes.
Further stability is added by the soc.sdc constraints file. This tells
Quartus about timing critical signals. Quartus will then make sure
these signals need to be connected in a way that they have a minimum
delay.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson4/soc.rbf) renamed to `core.rbf`
[Lesson 5](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5): OSD and User_IO
-------------------------
![Lesson 5: OSD](lesson5/lesson5.png)
The MiST comes with a separate ARM microcontroller (IO
controller). The main purpose of this controller is to load the FPGA
config from SD card at power on and to configure the FPGA with
it. During run time the ARM controller is idle.
So far our core didn't receive and user input. Actually the FPGA isn't
connected to any input device. All the joystick ports, USB ports and
the buttons are connected to the ARM IO controller and it can do all
the complex tasks like e.g. doing all the USB handling which retro
machines cannot do as USB didn't exist at that time.
The IO controller and the FPGA are connected by a SPI connection. The
files [`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/user_io.v) and [`osd.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/osd.v) implement exactly the type of SPI client the
IO controller expects to use. You typically don't have to care much
about these two files. You just include them into your projects and
let them do their job. [`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/user_io.v) receives all kinds of events related
to user interaction. It thus always knows if the user pressed a key on
the keyboard, moved the mouse or used a joystick. The file [`osd.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/osd.v) can
intercept VGA signals and include a small image (on screen display,
OSD) into the video stream. The contents of this small image are
received from the IO controller. Also the IO controller can show and
hide the on screen display. Both files ([`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/user_io.v) and [`osd.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/osd.v)) together
can provide the well know OSD that you can open in most cores via F12.
The [`osd.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/osd.v) needs to be integrated into the video data path. VGA signals
from out VGA controller are thus not connected to the MiSTs VGA
outputs anymore. Instead they are connected to the inout signals of
[`osd.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/osd.v). The outputs of [`osd.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/osd.v) are in turn connected to the MiSTs VGA
outputs.
The file [`soc.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/soc.v) contains a small config string including the cores name
("Z80_SOC") and information about entries the core would like to have
displayed in the on screen display. The IO controller reads this in
order to control the contents of the OSD. In the [`soc.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/soc.v) this is a
option entry named "Scanlines" which can be switched "On" or "Off" and
a toggle signal named "Reset" which is activated for a few
milliseconds whenever the user selects this in the OSD.
The state of both OSD entries is returned into the core through status
byte signals of [`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson5/user_io.v). Bit 0 of this byte has a fixed meaning and
goes high whenever the IO controller wants to restart the core
(e.g. when it is rebooting itself or when it just uploaded a new
core). The other bits of the status byte are controlled by the IO
controller depending on the state of the OSD. In out SoC status[1]
represents the state of the "Scanlines" option in the OSD and
status[2] indicates whether the user selected "Reset" from the OSD.
The status[1] signal is fed into the vga controller to enable or
disable the scanlines effect.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson5/soc.rbf) renamed to `core.rbf`
[Lesson 6](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson6): ROM upload, IRQs
--------------------------
![Lesson 6: ROM Upload](lesson6/lesson6.png)
Storing ROM contents inside the Core also consumes a lot of internal
FPGA memory. It also requires the whole core to be rebuilt for every
change of the ROM contents.
The MiSTs IO controller provides a simple helper mechanism for
this. Whenever it uploads a new core it will read the cores config
string incl. the cores name. It will then add ".rom" to this name and
check whether it finds a file with that name on SD card. In case of
the Z80 SoC it searches for a file named [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson6/z80_soc.rom). If it finds
one it sends it via SPI to the FPGA. On FPGA side the file [`data_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson6/data_io.v)
takes care of this. It activates a signal named "downloading" and
delivers all bytes received from the IO controller one by one. It also
generates a write signal and an address for this which can both be
directly connected to a RAM.
To save even more FPGA internal memory we now also use SDRAM for the
ROM area. Thus [`data_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson6/data_io.v) writes into SDRAM. While the download is
progressing the CPU is kept in reset and it's being disconnected from
the SDRAM. Once the download is complete the [`data_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson6/data_io.v) is disconnected
from the SDRAM and the CPU is re-connected and its reset is being
released. The CPU will then start executing the ROM contents which
have now been placed in SDRAM. Now 64 kBytes of SDRAM are used in
total. The first 32kBytes are used as ROM and the the second 32kBytes
as RAM together making use of the entire 64k address space of the Z80.
In order to boot successfully the file z80_soc.rom must from now on be
placed on the SD card.
To make the software a little more interesting as well this version
implements a vsync interrupt. An interrupt is an external signal that
causes the CPU to stop whatever it's doing and to execute a certain
function. The Z80 CPU in interrupt mode 1 will read a pointer from
address $38 whenever it notices that its interrupt pin is being driven
low. Subsequently it will execute the code stored at that position
until the function returns causing it to continue whatever it did
before. Unfortunately there is no easy way to tell the SDCC compiler
to set the pointer at address $38. Instead its default startup code
will always install an empty interrupt handler there. The file
irqvec.s is thus needed to trick the SDCC compiler into setting the
interrupt vector without modifying the SDCC's startup code. This is by
no means an elegant solution but it works. I am sure better solutions
for this exist ...
In the interrupt handler the 16 pixels in the center of the screen are
drawn using changing colors.
The OSD also allows to manually select ROM files from SD card. Many
game console cores allow this. The [`data_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson6/data_io.v) generates an index signal
which indicates whether the ROM download is the initial download the
SoC also uses (index = 0) or if the user triggered the download via
OSD (index = OSD line which was used to start the download). The SoC
does not use this feature (yet). The ZX01/ZX81 core uses the same
feature to upload cassette tape images which are then replayed through
the audio circuitry internally after upload.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson6/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson6/z80_soc.rom)
[Lesson 7a](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7a): SD card
------------------
![Lesson 7: SD card](lesson7a/lesson7.png)
Many retro systems use mass storage devices like floppies and tapes.
These aren't available on the MiST. Instead the MiST comes with an SD
card. SD card interfaces for many retro systems exist like e.g.
the "divmmc" device for the ZX spectrum or the SD2IEC for the C64.
On the MiST the SD card is connected to the IO controller and not to
the FPGA. To cope with this fact the file [`sd_card.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7a/sd_card.v) implements a SD
card inside the FPGA. [`sd_card.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7a/sd_card.v) behaves itself like an SD card in SPI
mode and sends and receive SD card contents from the real SD card
connected to the IO controller. It only implements a subset of the SD
card commands but all systems tested so far only use this subset.
The file [`sd_card.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7a/sd_card.v) has several connections to [`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7a/user_io.v) to send
and receive data to and from the IO controller. The four signals `sd_cs`,
`sd_sck`, `sd_sdi` and `sd_sdo` can be used by the rest of the core to access the
FPAG internal fake sd_card just like a real sd card.
In order to make any use of the SD cards contents the core needs to
implement a file system driver. This is a piece of software that
understands how data is stored on an SD card and e.g. reads files and
directories. The petit fat file system (pffs,
http://elm-chan.org/fsw/ff/00index_p.html) is such a file system
driver software. In order to use it we have to include all the source
files into our firmware and call the required functions from our main
routine in [`boot_rom.c`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7a/boot_rom.c). Also we need to implement a software component
that allows pffs to read sectors from the SD card. The pffs web page
provides a sample archive which contains a generic SD card driver in
mmcbbp.c. We've done minimal modifications to this to allow it to
access a few GPIO pins inside the FPGA. These are mapped into the Z80
IO memory area.
As a result the [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7a/z80_soc.rom) is able to access the SD card and list it's contents.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7a/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7a/z80_soc.rom)
- other files to show up in the directory listing
[Lesson 7b](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7b): Hardware SPI
-----------------------
The previous SD card integration used "bit banging" to let the Z80
control the SPI connection to the SD card. This works but is really
REALLY slow as the Z80 CPU needs to execute several instructions for
every single bit it receives as it needs to control every single
change of the clock and data signals going to the SD card. This is
easy to implement in hardware as it just needs two output bits for
sd_cs, sd_sck and sd_sdi and an input bit for sd_sdo and it's easy to
use in software as only minimal changes over the generic code example
is required. But it's too slow to be useful.
Since we can easily implement support hardware in the FPGA it's
possible to implement a hardware SPI master peripheral that can then
be used by the Z80. [`spi.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7b/spi.v) implements this. The file [`mmc.c`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7b/mmc.c) also needs
to be modified to make use of the new hardware. Sending a byte to the
SD card now requires only one Z80 instruction. As a result accessing
the SD card got significantly faster.
An additional sector buffer inside [`mmc.c`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7b/mmc.c) reduces the number of SD card
accesses and further increases speed. The resulting setup has a
sufficient performance to be useful.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7b/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7b/z80_soc.rom)
- other files to show up in the directory listing
[Lesson 7c](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson7c): Simple block IO
-----------------------
The fake SD card implementation used in the previous two lessons is
useful for cores that already come with full SD card support or for
cores which are supposed to be ported from the MIST to boards that
have an SD card directly connected to the FPGA.
If this is not the case and if you only want to get simple and direct
access to the SD cards contents and don't plan to run your core on
anything else than the MIST board then it's possible to bypass all the
complexity of SD card handling and just request sector data from the
IO controller.
This lesson implements such a simple interface. All SD card and SPI
handling is removed from the core as well as from the code running on
the Z80. Thus the files `sd_card.v` and `spi.v` have been removed from
the core and the file `mmc.c` has been removed from the Z80 source
code. Instead the new files `block_io.v` and `block_io.c` now provide
the same functionality in a much simpler way and without going through
the fake SD card.
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7c/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson7c/z80_soc.rom)
- other files to show up in the directory listing
[Lesson 8](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson8): Audio
---------------
![Lesson 8: Audio](lesson8/lesson8.png)
The MiST has two single bit audio channels. This can be used to output
some simple square wave by just switching the outputs on and off at the
desired frequency. A better sound can be achieved by using PWM
techniques like sigma delta conversion. This is what most retro cores
for the MiST use to implement audio output.
This lesson adds an existing [YM2149](http://www.ym2149.com/ym2149.pdf) audio chip implementation to our
SoC. This is the same audio chip the Atari ST uses as well as some
versions of the ZX spectrum or some arcade machines used. The ym2149
is controlled via two registers which are mapped to the Z80's IO space
at address 0x10 and 0x11.
Sounds for the YM2149 can be stored in `*.ym` files. These are direct
records of Atari ST sounds and simply store the whole YM2149 register
set at a 50 hz rate. Unfortunately most of these files use a format
that is rather inconvenient for streaming as whole file is compressed
and the bytes inside the files have been reordered to achieve better
compression. For our simple SoC setup we need the files uncompressed
and in linear order. Any YM file from the internet can be uncompressed
using the LHA program. Additionally the tool [`ym_deint.c`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson8/ym_deint.c) included
with this lesson can undo any byte reordering. A ready decoded file
[`song.ym`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson8/song.ym) is also included with the lesson.
The 8 bit output of the ym2149 is fed into a sigma delta converter in
order to be fed into the single bit output of the MiST.
With the SD card implementation from the last lessons the song.ym file
can be read from the card and replayed as it is being read. This way
even those files can be replayed that would be too large to be stored
completely in the Z80's memory.
The replay routine itself is placed inside the Z80 interrupt handler
allowing for seamless playback even while SD card accesses take
place. In the previous lessons the interrupt was connected to the VGAs
vsync signal resulting in 60 interrupts per second. This doesn't match
the intended 50Hz playback rate of YM files. Therefore this lesson
connects the Z80's interrupt input to a counter which generates a 50Hz
signal from the 4 Mhz CPU clock by dividing it by 160000.
Links:
- http://www.ym2149.com/ym2149.pdf
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson8/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson8/z80_soc.rom)
- [`song.ym`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson8/song.ym)
[Lesson 9](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson9): Keyboard & Mouse
--------------------------
![Lesson 9: Keyboard and mouse](lesson9/lesson9.png)
The MiST uses USB to connect mice and keyboards. USB is a rather
complex protocol and requires a lot of communication and message
parsing to detect even a single key press on a keyboard. The focus of
the MiST board lies on the implementation of retro machines of the
homecomputer age. At that time USB didn't even exist. That's why the
MiST board tries to hide all the complexity of USB from the FPGA
developer. All USB related communication is handled by the ARM
controller which in turn simply reports keyboard and mouse events to
the core via the SPI connection.
Many of the 8 bit cores available for the MiST were ported from the
FPGA development boards on which they were initially developed. These
boards typically bring a PS2 keyboard and/or mouse
connector. Therefore most existing FPGA projects expect to directly
connect to a PS2 mouse or PS2 keyboard. To ease porting of such
projects the MiST boards [`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson9/user_io.v) implements two PS2 interfaces of
which one behaves like a mouse and one behaves like a keyboard.
This lesson uses a PS2 protocol decoder taken from Mike Sterlings ZX
Spectrum core which was developed to be used on the Terasic DE2. This
decoder re-assembles the [PS2 bitstream](http://computer-engineering.org/ps2protocol/) into bytes. Those bytes are then
parsed either by a [PS2 mouse parser](http://www.computer-engineering.org/ps2mouse/) or by a [PS2 keyboard parser](http://computer-engineering.org/ps2keyboard/).
Two keys (SPACE and 'C') are decoded and detected in hardware in
[`keyboard.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson9/keyboard.v). The resulting two bits are made available to the Z80 CPU
via a IO port.
The mouse movement is decoded in [`mouse.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson9/mouse.v). The movement is accumulated
in two other IO registers and made available to the Z80 CPU. Whenever
the CPU reads these registers the hardware counters are cleared to
restart accumulating new movement information.
The Z80 CPU simply takes this information and uses it to update two
variables keeping track of the mouse position. This position is used
to draw to the video memory whenever a mouse button is pressed.
To allow the user to see where the mouse currently is requires a mouse
cursor to be drawn. This is where our write-only video memory causes
trouble as moving a mouse cursor on screen requires to read the pixels
"under" the mouse cursor in order to be able to restore them if the
mouse is moved to another spot. Our VGA controller simply doesn't
allow this. To circumvent this the VGA controller has been updated to
implement a hardware cursor. This curser is drawn by the VGA
controller itself. A further advantage of this is that the Z80 CPU
doesn't have to do any complex painting as it would with a software
cursor. This is very similar to the hardware sprites the C64
supported. This way of offloading CPU intense tasks into the hardware
can reduce the CPU load significantly.
Links:
- [PS2 protocol basics](http://computer-engineering.org/ps2protocol/)
- [PS2 mouse protocol](http://www.computer-engineering.org/ps2mouse/)
- [PS2 keyboard protocol](http://www.computer-engineering.org/ps2keyboard/)
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson9/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson9/z80_soc.rom)
[Lesson 10](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson10): Ethernet
--------------------------
![Lesson 10: Ethernet](lesson10/lesson10.png)
Only few devices of the homecomputer era came with some kind of network interface included. But for many
existed add-ons like the [TFE for the C64](http://dunkels.com/adam/tfe/), the
[Etherncx for the Atari ST](http://hardware.atari.org/ether/) and some Amigas could use PCMCIA ethernet cards.
The MiST is able use certain USB-to-ethernet adaptors based on certain
[Asix chips](http://www.asix.com.tw/products.php?op=pItemdetail&PItemID=86;71;101&PLine=71)
to connect to an ethernet. One such adapter is the [D-Link
DUB-E100](http://www.dlink.com/de/de/support/product/dub-e100-high-speed-usb-2-fast-ethernet-adapter). Similar
to the mouse and the keyboard the IO controller again hides all the
USB complexity from the core. The core just needs to implement a
simple interface to the IO controller to be able to send and receive
network packets. The IO controllers firmware will care for the rest.
The conection to the IO controller is again handled inside
[`user_io.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson10/core/user_io.v). Since
only few cores make used of ethernet most `user_io.v` files don't
include the necessary parts. You have to make sure to start with a
`user_io.v` including ethernet features if you intend to implement a
network interface. `user_io.v` provides an ethernet related
communication channel to the IO controller. But it e.g. doesn't buffer
any packets. This has to be implemented depending on the demands of
the core. For this tutorial the Z80 just needed a simple custom
network interface which has been implemented in
[`eth.v`](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson10/core/eth.v).
The file `eth.v` interfaces between the Z80 CPU and the connection to
the IO controller provided by `user_io.v`. Send and receive buffers
inside `eth.v` make sure that ethernet packets can easily be exchanged
without data loss between the IO controller and the SoCs Z80 CPU. If
you intend to implement ethernet for a retro machine you might want to
implement `eth.v` in a way that it resembles one of the original
interfaces so that the original driver software can be used. E.g. the
Atari ST core implements an [ethernec compatible
interface](http://hardware.atari.org/ether/) and thus doesn't need any
new drivers but used the original drivers written for the ethernec.
Since there is no existing software for our little Z80 SoC we have to
implement our own network stack and drivers. For full network
connectivity this requires a full blown TCP/IP stack. Such kind of
software is rather complex and is close to the limits of what a small
homecomputer of the 80ies can handle. For this totorial we choose
[Adam Dunkels UIP stack](https://github.com/adamdunkels/uip) to
provide the necessary network stack. It turned out that this stack has
some issues with the SDCC compiler we are using. I ironed out the most
critical problems until i had it somewhat working as a web
server. When running the
[`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson10/boot_rom/z80_soc.rom)
you'll be presented with the MAC address of your Ethernet USB adapter
and the preset IP addresses are displayed on a VGA screen connected to
the MIST. If the message `Wait for ETH` doesn't disappear then the
MIST doesn't recognize your USB ethernet dongle or you don't have one
connected at all. Once you see the IP configuration being displayed
you should be able to `ping` the MIST under IP `192.168.0.2` from a PC
and you should be able to access UIPs embedded webserver from a PC
under `http://192.168.0.2`.
Please be aware that the whole setup is not a high performance network
device and the transmission is neither fast nor very reliable. Also
some of the demo web pages of the included UIP web server don't work
currectly. This is not a problem of the MIST itself but of the
incompatibilities of the SDCC compiler and the UIP stack together with
the limited performance of an 8 bit Z80 system. With a total of
30kBytes code this very basic network setup is already close to the
32kBytes code limit of our small SoC.
To reduce the load on the target system a little bit the IO controller
does some network filtering. It will only forward packets which are
directly addressed to its MAC address or which are ARP broadcasts. All
other (broadcast/multicast) traffic is not forwarded to the core to
reduce the load a little bit and to protect it from all
those modern protocols causing lots of background noise in a typcial
home network of today. This may affect fancy auto conf network setups
trying to run on the MIST. If you encounter problems with this
filtering, then please get in touch with us and we will find a way to
solve the problem. But ordinary IP traffic is supposed to work.
Links:
- [UIP 1.0 on github](https://github.com/adamdunkels/uip)
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson10/core/soc.rbf) renamed to `core.rbf`
- [`z80_soc.rom`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson10/boot_rom/z80_soc.rom)
[Lesson 11](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson11): 15kHz TV video
--------------------------
![Lesson 11: TV](lesson11/lesson11.png)
All tutorials so far have used the VGA output to generate a [VGA
compatible video
signal](https://en.wikipedia.org/wiki/Video_Graphics_Array). But one
of the big advantages of an FPGA is it's ability to generate all kinds
of signals. The VGA output is thus not limited to VGA signals. Its
three analog outputs could actually even be used to generate three
audio channels or to transmit an RF signal.
But a more obvious usage is the generation of TV signals. Even today
many TVs accept standard definition (SD) TV signals in form of a [RGB component video signal](https://en.wikipedia.org/wiki/Component_video#RGB_analog_component_video)
via e.g. their [SCART](https://en.wikipedia.org/wiki/SCART)
inputs. Many machines of the homecomputer era generated such signals
so it makes sense to generate these with the MIST as well for the
perfect retro game experience using a TV as the display.
The main timing difference between TV and VGA signals lies in the line
frequency and the number of lines displayed per image. While VGA
screens display at least 31000 lines per second (31 kHz) a TV signal only
consists of 15625 lines per second (15 kHz). Furthermore the [European PAL TV
signal](https://en.wikipedia.org/wiki/PAL) only draws 50 frames per second (50 Hz) while VGA expects a signal to carry at least 56 frames per second (56 Hz). [American NTSC TVs](https://en.wikipedia.org/wiki/NTSC) draw 60 frames per second (60 Hz).
Thus a SCART TV input usually doesn't cope with VGA signals and vice
versa. The easiest method to make a TV signal somehow VGA compliant is
to draw every line twice. This is called scan doubling and makes a
60 Hz NTSC TV signal compatible with most VGA screens. Scan doubling a 50 Hz
PAL signal results in a VGA signal with 50Hz frame refresh rate. Many
VGA screens cope with this but not all. Especially TVs often don't
accept these not-100%-VGA signals on their VGA inputs. Most cores
contain such scan doublers since the machines they implement were meant to be connected to TVs and thus generate TV video signals while the
primary use case of the MIST board is with VGA screens.
It is possible to connect the VGA output of the MIST with a TVs RGBS
input like e.g. its SCART connector. Since the TV expects TV signals
on this input the MIST must in turn be programmed to generate such
signals. Furthermore TVs expect a composite synchronization signal
where VGA screens expect seperate horizontal and vertical sync
signals. And SCART finally needs another static "high" signal to
indicate that a RGBS signal is being received. The solution to this
was introduced with the first
[Minimig](https://en.wikipedia.org/wiki/Minimig). The so called
[Minimig SCART
cables](https://github.com/mist-devel/mist-board/wiki/ScartCable)
connect the VGAs hsync output with the SCARTs composite sync and use
the VGAs vertical sync output as the RGBS switch signal.
In order to make use of such a cable a MIST core has to disable its
scandoublers and it must output a composite sync signal on the VGAs
hsync output and a static high voltage on the VGAs vsync output. Using
the OSD to switch between these two modes would be rather useless
since with the wrong setting the OSD is invisible. The MISTs firmware
thus accepts a [`scandoubler_disable` option in the `mist.ini`
file](https://github.com/mist-devel/mist-board/wiki/DocIni#scandoubler_disable)
to globally force cores supporting this feature into disabling their
scan doublers.
This tutorial includes a retro video unit
[video.v](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson11/video.v). This
can either output a PAL like video signal or an NTSC like video
signal. The mode can be changed via the OSD. The resulting video
signal is then converted into a VGA signal by
[scandoubler.v](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson11/scandoubler.v). The
scan doubler can be bypassed via the [`scandoubler_disable` option in
the `mist.ini`
file](https://github.com/mist-devel/mist-board/wiki/DocIni#scandoubler_disable)
which is reported to the core via the apropriate output of
[user_io.v](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson11/user_io.v).
The resulting core will generate a 50 or 60 Hz VGA signal without a
`mist.ini` file disabling the scan doubler. If the
`disable_scandoubler` option is set to 1 then the core will output a
TV compatible PAL (50 Hz) or NTSC (60 Hz) signal for direct connection
to a TVs RGBS input. Many old CRTs also support this.
Links:
- [MIST Scart cable](https://github.com/mist-devel/mist-board/wiki/ScartCable)
- [Disabling scandoubler in MIST .ini file](https://github.com/mist-devel/mist-board/wiki/DocIni#scandoubler_disable)
- Buy a cable [from Lotharek](https://lotharek.pl/productdetail.php?id=171) or [Dragonbox](https://www.dragonbox.de/de/413-mist-scart-kabel-2m-kabel.html)
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson11/soc.rbf) renamed to `core.rbf`
[Lesson 12](https://github.com/mist-devel/mist-board/tree/master/tutorials/soc/lesson12): YPbPr component video
-------------------------------
SCART is a nice way to send a true "retro" video signal to a TV.
Unfortunately SCART or any other kind of RGBs input isn't available
on most modern TVs anymore.
What many TVs still have is a so-called YPbPr component input. This
type of input uses three connections Y, Pb and Pr. Y carries a
geyscale signal together with a composite sync signal. Thus only
connecting the Y signal may already result in a grayscale image
on many TVs. The other two signal Pb and Pr carry color difference
signals. Together with the Y signal they allow the TV to reconstruct
the color.
For YPbPr a special adaptor is need for the MIST as described in the
[MIST wiki](https://github.com/mist-devel/mist-board/wiki/YPbPr_Cable).
Many TVs support various common video modes on their component inputs.
It is thus often possible to use the YPbPr with or without
scandoubler. With scandoubler enabled the resulting 720p modes may
even use scanline effects.
In order to make use of such a cable a MIST core has to output the
color differnce signals instead of RGB and it must output a composite
sync signal on the VGAs hsync output and a static high voltage on the
VGAs vsync output. Using the OSD to switch between these two modes
would be rather useless since with the wrong setting the OSD is
invisible. The MISTs firmware thus accepts a [`ypbpr` option in the
`mist.ini`](https://github.com/mist-devel/mist-board/wiki/DocIni#ypbpr)
to globally force cores supporting this video mode. This option can be
combined with the [`scandoubler_disable`
option](https://github.com/mist-devel/mist-board/wiki/DocIni#scandoubler_disable)
This makes a YPbPr connection a very nice choice when connecting the
MIST to many modern TVs.
Links:
- [MIST YPbPr adapter](https://github.com/mist-devel/mist-board/wiki/YPbPr_Cable)
- [Enabling YPbPr in the MIST .ini file](https://github.com/mist-devel/mist-board/wiki/DocIni#ypbpr)
Files required on SD card:
- [`soc.rbf`](https://github.com/mist-devel/mist-board/raw/master/tutorials/soc/lesson12/soc.rbf) renamed to `core.rbf`