Thursday, 30 July 2015

Fleshing out the MAXI09

My last post was full of text, and therefore not very approachable. This post will hopefully explain some of my thoughts in a clearer way, with the diagrams and schematics (hopefully) doing most of the talking.

First up here is the current, work in progress, system overview:


(I tried looking for some nice online tools for making these kinds of diagrams, but in the end settled with using the schematic capture program from the gEDA suite, the same one I use for my schematics. All the online tools I found sucked in one way or another.)

Common busses, notably the data bus, run around the edge.

The MuDdy FPGA is the system core, and glues the whole computer together. It generates the physical address (seen by the RAM and EEPROM) and the various chip selects, one for each device. Only key select lines are shown. Likewise most read and write lines are hidden in this diagram. This FPGA is also connected to the HALT line on the CPU, so it can stop it whilst performing DMA operations. MuDdy also receives bus status information from the CPU. A buzzer is also attached, and will be used, as before, principally to indicate boot progress and boot errors.

DISCo is pure peripheral, though it also has the job of routing interrupts. Again, only some interrupt lines are shown. DISCo is also connected to a DAC, and through the DAC to a 3.5mm mono jack. It is also connected to the IDE header.

Moving along, the SPI controller is the 65SPI/B, a variant on the regular 65SPI used previously. It has some advantages, the main one (for me) is that it can route interrupts. This means the DS3234 (PDF) interrupt output, very useful for generating a precise system tick, does not need to be attached to MuDdy, and instead the 65SPI/B can pick up the interrupt and forward it along by generating an interrupt on its own interrupt line. In addition to this, it can also generate its own interrupts at the end of a transferred byte, etc. Also attached to the SPI master is an ADC. This is used for a new addition to the MAXI09 plan: an analogue joystick. Writing pong should be reasonably easy, and is much nicer to play with an analogue joystick.

As a side project I will build my own joystick out of an old X Box controller stick and some push buttons. It won't be pretty but it should work well. It will connect to the computer using a six pin mini-DIN. This is the same connector as a PS/2 keyboard or mouse, and has the advantage over a DB9 because it is smaller, saving precious board space.

A VIA will be used for a single 9 pin Atari joystick port. It will handle the six digital joystick inputs, plus two buttons from the analogue joystick. The other half of the VIA will be useable for general IO, but won't be bought out into an external connector.

Video will be provided by a V9958, equipped with 128+64KB of RAM. Unlike the old IO board, RGB video will be amplified by an AD8073 (PDF) IC, which should provide a better quality picture. Composite and S-VIDEO will be provided by appropriate connectors, driven from an AD724 (PDF).

The keyboard and controller will be similar to the one used previously, expect it will be attached to a UART channel instead of a VIA port. This will allow bidirectional communications between the MPU and the keyboard controller, allowing things like key repeat rate to be set. The keyboard MCU will also have an RGB LED attached, that will be used to show keyboard and system health. For instance, if the MPU does not acknowledge scan code bytes in a reasonable time, it will enter an error mode and flash the LED. For fun, it will be possible to change the colour of the LED from the MPU. This LED will be attached using a flying lead and a header, such that it will be visible outside the computer's case, when one is eventually made. In this way it will double up as the systems power indicator.

Below is a draft schematic for the keyboard controller portion:


I have chosen to continue using the PLCC44 version of the ATMega8 on the MAXI09, even though a version in DIP28 has just enough IO pins now that the UART is used instead of a full 8 bit port with handshake lines to talk to the MPU. But I have chosen to stick with the PLCC44 footprint with four 8 bit ports since it will be easier to program as it won't require port accesses to be spliced together when scanning the keyboard.

This takes us on to the UART. Or rather QUART, since this computer will have four UART channels. Perhaps it is excessive, but they could all be used one day. As mentioned, one will be used for the keyboard. A further two will be presented at EIA-232 signal levels, but to save board space RJ45 connectors will be used instead of DB9s. Unlike in the previous board hardware handshaking lines, RTS and CTS will be available. An eight line driver, in the form of the MAX238 (PDF), will be used as a level shifter for the two externally presented UART channels. The final channel will be headered out on a simple 3 pin header and will be useable via a USB FTDI lead.

Here is the preliminary schematic for the UART section:


The QUART used is a Philips/NXP SC16C654 (PDF) in PLCC68. I have not used this part before but it appears fairly similar the the XR88C681 (PDF), used in my previous micro. Slightly irritatingly, this UART does not have a timer. Therefore the main system timer will have to come from another source. Luckily there are still several others to choose from.

The main reason this schematic is a draft is because I have not figured out which pinout to use for the RJ45s. Unlike with DB9s, there is no standard pinout. Each equipment vendor (Cisco, etc) have their own standards. Therefore I will base the pinout on the RJ45 to DB9 leads I already have access to, but I have yet to determine the pinout they use. (I do appreciate that I am using the wrong terms here. The DB9 used on serial ports is correctly referred to as the DE9; the RJ45 used on Ethernet (and some serial console ports) is actually an 8P8C connector. I choose to use the terms that most people know these things as, even if they are wrong.)

This pretty much finishes up the current thinking behind the hardware. There's a few details related to the PCB which I have also decided.

As I mentioned in my previous post it will be a four layer board. Vcc and ground will be on internal layers, with the outer layers used for signals. This should hopefully simplify the laying out process, at the expense of a more costly board. Power will be presented on a 5V barrel connector instead of my usual USB-B socket. This will mean an ordinary wall-wart type PSU could be used. Alternatively, USB-A to barrel leads are available. The one disadvantage with this idea is that it's possible someone might plug a higher Voltage barrel plug into the computer by accident, blowing up components. This generally wouldn't happen with a USB socket since all USB devices are rated at 5V. This makes it a bit of dilemma, but I think the advantages of a barrel connector outweigh the disadvantages.

I have yet to figure out how, precisely, to utilise a single configuration flash to store the design for two FPGAs. The data sheets (PDF) explain how to wire up the parts to achieve this; what is not clear is how to construct the "program file" which needs to be loaded into the Flash such that it will then program multiple FPGAs at startup. This is quite frustrating, since I really don't want to use a configuration flash with each FPGA. If I cannot figure it out soon I will have to consult the experts on the Altera forums.

I have also yet to settle on a choice of DAC, nor am I entirely satisfied that I wouldn't be better off attaching one to the MPU buses instead of the DISCo FPGA. This would save at least 8 pins on DISCo and still let me play with 8 bit PCM via DMA. The only thing not possible would be wave table generation from the FPGA. A possible choice of device that can be attached to an MPU bus is the TLC7226 (PDF), a four output DAC with an MPU compatible write/select pin. One advantage of a multi output DAC is it would allow the computer to generate stereo sound. Of course this IC could also be used with DISCo, though more then 8 pins would be required.

I am slightly concerned that I am under using the generally rather useful 6522 VIA by relegating it to only being used for reading joysticks. The VIA can do much more, as my previous usage of it to interface to a keyboard controller shows. One idea that would be great to do, since it would add to the retro feel, is to add a parallel printer port to my computer. This would require a full VIA all to itself though. But I'm considering it. It would allow me to attach the computer to an old Centronics printer, which could be fun. It mostly comes down to the size of the PCB. At a minimum I will add a header for the unused half of the VIA, so at least it will be usable for breadboard-type experiments. Another idea is to include a second pair of analogue and digital joysticks. This would only require the addition of a second two channel ADC, using the other half of the VIA for the digital inputs for both joysticks, but of course more board space, especially for the DB9, would be needed.

My next task will be to prototype up the RGB to composite circuit. I will need to dust off the IO board with the V9958 for this. I already have a couple of AD725s (PDF), and some suitable SOIC breadboard adapters. I need to buy the amplifier ICs (AD8073 (PDF)) though, and will verify their function when I have them.

So, MAXI09 is coming along well, though that multiple FPGA mystery needs solving. Once the last few decisions have been made I will start to finalise the circuit before, at long last, I can start on the PCB design...

Saturday, 18 July 2015

Outline of the MAXI09

This blog post is concerned with planning and, unlike pretty much all my posts to date, is not concerned with things I’ve actually done.

My new computer is coming together nicely, at least in my mind and in some rough plans. Here is a list of what I hope will be the main hardware features:
All of this will be laid out on a 4 layer PCB, with the entire computer put in a case (some day) with the keyboard. The expansion headers will be organised so I can either use them to attach to breadboards, or attach daughterboards.

This is obviously very ambitious. Especially around the idea that this will be a self-contained computer, not just a PCB that sits on the desk. There are loads of things that can go wrong. But I’m quietly confident I can get most of it done. My biggest concern is with the size of the PCB and achieving a decent layout. And also the cost of the main PCB, which is likely to be anything upto £200. The second concern is with my VHDL skills. I suspect I will be challenged, especially by the IDE controller. But you don’t learn unless it is difficult, so that is alright.

The computer has a name: the MAXI09.

I’ll now discuss each of the two FPGAs. Of course I will be using the EPF10K10 in PLCC84, FLEX10Ks (PDF). Each one has 59 useable IO pins. And I’ll need every single pin on both of them.

MuDdy - MMU and DMAC

Summary

MuDdy is a combined MMU and DMAC. It generates chip selects for all memory and peripheral ICs. It also generates the global \READ and \WRITE signals, which are gaited from R\/W and the E clock. Finally, it generates the RESET signal, which is (annoyingly) required by the UART.

Pins
  • 8 data bus (D7-D0)
  • 12 page address (A11-A0)
  • 4 virtual address (VA3-VA0)
  • 8 physical address (PA7-PA0)
  • 2 clocks (E, Q)
  • 2 resets (\RESET, RESET)
  • 3 readwrite (R/\W, \READ, \WRITE)
  • 1 halt (\HALT)
  • 2 bus activity (BA, BS)
  • 1 interrupt (\INT)
  • 14 chip selects (various)
Total: 59

MMU

The purpose of a MMU, for my 8 bit computer at least, is twofold. First of all it allows a multitasking environment (I am reluctant to say Operating System) to prevent different tasks from seeing each others memory. This is fundamentally why modern Operating Systems present a reliable experience to the user; crashing tasks cannot generally destroy the system as a whole. Secondly, it will allow my computer to utilise more then 64KB of RAM, in a similar, but more effective, way to the banking used previously.

The system will use 4KB pages, for 12 bits of addressing within a page. Since the MPU has 16 bits of address, this means the top 4 bits will be used to map a virtual (MPU) page to a physical (Memory, IO) page.

The system will have 512KB of RAM and 32KB of EEPROM. This means (at least) 20 bits of physical address will be required.  20 bits is useful, since it means 20 bits less the 12 bits for the within the page address yields 8 bits for the physical page number. ie. there will be 256 4KB pages.

16 registers will hold the physical addresses which the 4 bits of virtual address are mapped to.  There is only the active internal mapping held in the MMU.  On a context switch, the 16 registers will be updated to hold the new context's mappings.  This could be done by DMA (see below).

FUTURE: It might be useful to hold the address of the various tasks' mapping tables in registers. That way the MMU itself could switch mapping with a single register write, which would trigger a DMA action to fetch the tables.  The alternative would be to hold all the tasks mappings in registers, and again a task switch could be achieved with a single write.  In any case, such optimisations will be done later.

When the CPU address bus is valid (determined from the clocks), the 4 PA bits will be decoded to find out which of the 16 registers should be used to generate the 8 bits of physical address.  External address decoding (ie. the generation of peripheral chip selects) will be via a combination of physical address and page address:
  • PA7-PA0
  • 0x00 to 0x7f - RAM (128 x 4KB = 512KB)
  • 0xf8 to 0xff - EEPROM (8 x 4KB = 32KB)
  • (others) - IO
Because ROM needs to always be mapped to virtual address 0xfff0-0xffff, for the interrupt and reset vectors, virtual page 0xf will be special-cased in the mapping to map to physical page 0xff, the top 4KB of EEPROM. Critical OS routines to perform remappings will also be held in this page.

Within the IO page, the 12 bits of page address will be split so 4 bits are used for the peripheral IC, leaving 8 bits for register selects.  This provides room for 16 peripheral ICs on the board, which should be ample.  Note that peripheral 0x0 will be the MuDdy device itself.

FUTURE: Write protecting a page is also trivial to do via this mechanism. An extra byte per page could be used to hold settings, such as write protected.  Since the 6809 has a synchronous bus, it is not possible to have a faulting mechanism, though basic things like status bits that indicates a page has been written to, read etc, could have some utility.

DMAC

The purpose of a DMA Controller is to allow data transfer to happen within the computer without utilising the MPU. This is useful any time block transfers are required, for example:
  1. Reading and writing disk blocks
  2. Reading and writing data blocks to the serial port (eg. XMODEM)
  3. Writing to a sound device
  4. General memory copying or initialising
The DMA Controller will allow for the transfer of chunks up to 64KB in length. The source and destination addresses will be arbitrary, allowing memory to memory, IO to memory and memory to IO transfers. The DMA Controller will operate by mastering the CPU busses by asserting \HALT. There will be two channels: a bursting channel in which the CPU is halted for the duration of the transfer and a “slow” channel where the transfer happens at a particular rate set by a period counter. The purpose of the rate limited transfer is principally to support driving the audio output in DISCo. Because audio transfers should not be paused while transferring data with the bursting channel, the slow channel will take priority over the bursting channel.

FUTURE: It may be more practical to make both channels the same, and achieve bursting transfers by setting the rate to 0, or similar.

For each channel the following registers will be needed:
  • Source address (20 bits)
  • Destination address (20 bits)
  • Flags (8 bits)
  • Length (16 bits)
Both addresses are physical addresses. The flags byte will be used to hold transfer properties:
  • Increment source
  • Increment destination
  • Clear destination
  • Negate source
  • Generate interrupt at transfer end
  • Active transfer
Optional incrementing makes the DMAC useful for IO port transfers. As well as source to destination transfers, it will also be possible to zero the destination. This will make it possible to zero memory extremely rapidly, since no read will be involved. It doesn’t have much utility, but it will also be possible to negate the source before it is written to the destination.

FUTURE: Some DMACs can do transfers backwards, ie. decrementing the source and/or destination. This does not seem to be very useful to me, though it would be trivial to implement.

Interrupt generation will allow the next transfer to be initiated inside an interrupt handler, etc. The transfer will initiate when this register is written to, assuming the active transfer bit is set.

The slow channel has an additional register:
  • Transfer period (16 bits)
A 16 bit counter, with a 2Mhz clock, will allow transfers rated down to about 30 bytes a second. A 8KHz audio transfer will yield a byte transfer every 250 clock ticks, which should allow a useful amount of MPU work to be done whilst playing audio data.

Implementation of the DMA transfer will follow the very rough prototype created with the XC95108. When a transfer is initiated \HALT is asserted. The MPU will then cease operation, but not immediately. A few cycles later BA and BS will be set to high, indicating the busses are tristate. At this point the DMAC can claim the bus and perform the transfer. At the end of the transfer the \HALT line will be returned to the inactive state.

Actually determining if a transfer should take place will involve examining both channels and first determining if the slow channel has a byte to transfer. This will be done by decrementing the rate counter and if it is zero setting up a transfer. Otherwise, if the bursting channel is marked as active, that channel will be made active. I must admit the details around this area a little lacking at the moment.

DISCo - Disk, Interrupt and Sound Controller

Summary

DISCo is responsible for 3 things - the IDE interface, interrupt routing and PCM sound output.

Pins
  • 8 databus (D7-D0)
  • 6 register selects (A5-A0)
  • 2 readwrite (\READ, \WRITE)
  • 1 chip select (\CS)
  • 16 ide data (IDED15-IDED0)
  • 3 ide address (IDEA2-IDEA0)
  • 2 ide readwrite (\IDEREAD, \IDEWRITE)
  • 1 ide chip select (\IDECS)
  • 1 reset (\RESET)
  • 2 cpu interrupt pins (\IRQ, \FIRQ)
  • 8 digital sound (SOUND7-SOUND0)
  • 1 clock (E)
  • 8 interrupt sources (various)
Total: 59

IDE interface

The bulk of DISCo will be concerned with interfacing to the IDE drive in the system. Unlike in the previous IDE interface, the one in this computer will be “intelligent”. The data and address busses on the IDE drive will not be attached to the MPU directly; instead they will be connected to DIsCo. The host MPU will issue commands to DISCo, which will then communicate with the IDE device. Part of the motivation for this functionality is so that the details of the 16 bit data connection can be hidden. As far as the MPU (and more importantly the DMA Controller) is concerned, the IDE will present a 8 bit data path. Thus the DMA Controller will be able to DMA blocks from the disk transparently, without caring about the fact that the IDE presents data 16 bits wide.

Again, this functionality is not yet very well defined. I do not want to duplicate the disk controller logic in the FPGA. But at the same time it would be nice to hide some disk-level details from the MPU code, implementing only the parts of the IDE specification as I need. In summary, if I was only to be concerned with the 16 bit to 8 bit problem (presenting the 16 bit transfers at the same 8 bit IO port) then I could probably simplify this interface quite a bit and pass IDEA2-IDEA0 through from the MPU as I did in my previous CPLD-based computer. But this does not seem like an interesting solution.

Interrupt routing

This is essentially the same functionality as implanted in the CPLD. The following registers will be required:
  • IRQ mask
  • FIRQ mask
  • Interrupt status
To save a couple of pins, \NMI at the MPU will simply be connected to a button, making it not possible to mask this out at the interrupt controller. Two pins is a large price to pay for the fairly useless ability to mask this interrupt.

PCM sound output

In its initial implementation, this will simply be an output latch connected to an external 8 bit DAC. This will allow me to experiment with PCM sound output. Since this will be drivable from the DMAC, it will be possible to generate sound output from memory containing PCM sound data without the MPU doing all the work. It might even be possible to DMA disk blocks directly into the sound output.

FUTURE: Digital sound synthesis is something I’m also interested in looking into. It might be possible to generate sound waveforms in the FPGA itself, instead of reading them from memory.

In my next post I will present a proper block diagram, as well as (hopefully) the first draft of the schematic. I'll also touch on some of my plans for the system software…

Tuesday, 7 July 2015

Experiments with a 6809 and an FPGA

After getting the FPGA test rig up and running, the next stage was to attach it to the 6809 SBC. This was critical if I am to incorporate one into the core of my next computer.

To start with I utilised the 7 segment displays as an output latch. Since I had 16 bits of display (four seven segments) I needed the following connections:
  • 8 data bus lines
  • 1 address bus line
  • 1 read line
  • 1 write line
  • 1 chip select line
  • 1 E clock line
The E clock replaced the oscillator. It was used to drive the multiplexor for the display.

Read and write allow the latch value to be both read and written. While being able to read the value displayed back is not essential to a working output latch, it would prove that the FPGA could be used in other applications where reading a register is required.

For the Chip Select, I simply borrowed an existing line that was used by the (now removed) IO board. The FPGA was at 0x90XX, meaning that, if desired, I had 8 bits of address to use as individual register selects. Since I was implementing a 16 bit output latch, I needed only one address line, to indicate wether the high or low byte was selected.

Obviously the eight bits of data bus are needed to convey a byte to or from the FPGA.

You can see in the following picture the output latch being exercised:


The latch has 0xabcd written to it, which you can just read on the seven segments. The latch is then read back, or rather 0x9000 to 0x900f is read back. On the screen you can just about read the displayed value, which shows 0xabcd across the 16 bytes. The initial write call makes use of a nice feature of my monitor: the ability to write in 16 bit chunks. Of course this boils down to two 8 bit writes, but it is the MPU which handles this, it is not two distinct write instructions as it would be with some other 8 bit MPUs.

VHDL code for the FPGA will be given later, since I expanded on this design but left the output latch intact.

At this point I was quite pleased with myself. I'd successfully incorporated an FPGA in my little 8 bit micro. This meant that it should be possible to use one in the core of a new micro design and implement my ideas for an MMU and a DMAC!

But before moving onto the design for such a micro, I thought I would extend the breadboard a bit more and implement a buzzer sound interface.

The CPLD in the main SBC has a sound interface; a single register sets a period value for the 1 bit tone. A zero in this period register stops the tone. But I thought I could do something better, since I have more resources available in the FPGA, and add another register which sets how long the tone should play for. That way the MPU wouldn't have to silence the sound if all that was needed was to play a tone for a short time. Further, if an interrupt could be generated at the end of the tone, it would be possible to play a stream of notes entirely under interrupt control, since the interrupt handler could itself start the next note playing.

Changes to the circuit to implement this are fairly trivial: another address line is required because the FPGA now needs four addresses, the buzzer needs to be attached to an output pin with a series 1k resistor to ground. An interrupt output with a 1k pull up completes the changes.

The addressees now used (the latch is still present):
  • 0 high byte of output latch
  • 1 low byte of output latch
  • 2 the period of the tone
  • 3 the length of the tone to play
Logically, it's also possible to use the same mechanism as when generating tones to deliberately not generate a tone, since no tone is emitted for a specific duration when the period is zero. This could be utilised too, for instance, queue a delayed interrupt. It is also, of course, similar to how timers are implemented.

The full VHDL for the output latch and sound generator will be on my github account soon, but by way of explanation here is the most interesting entity, the sound generator:

entity soundplayer is
port ( nSOUNDLENGTHSET : in STD_LOGIC;
SOUNDPERIODCLK : in STD_LOGIC;
SOUNDLENGTHCLK : in STD_LOGIC;
SOUNDPERIOD : in STD_LOGIC_VECTOR (7 downto 0);
SOUNDLENGTH : in STD_LOGIC_VECTOR (7 downto 0);
SOUNDER : out STD_LOGIC;
nSOUNDINT : out STD_LOGIC);
end soundplayer;

architecture behavioral of soundplayer is
-- Period (inverse frequency) of note
signal PERIODCOUNTER : STD_LOGIC_VECTOR (7 downto 0) := x"00";
-- Length of note
signal LENGTHCOUNTER : STD_LOGIC_VECTOR (7 downto 0) := x"00";
signal LOCALSOUNDER : STD_LOGIC := '0'; -- Toggling buzzer output
signal SOUNDPLAYING : STD_LOGIC := '0'; -- State indicating sound should play
begin
process (nSOUNDLENGTHSET, SOUNDLENGTHCLK, SOUNDPERIODCLK, SOUNDPERIOD)
begin
-- If we have just written to the sound length register, then clear the length counter
-- and set the playing state
if (nSOUNDLENGTHSET = '0') then
LENGTHCOUNTER <= x"00";
SOUNDPLAYING <= '1';
-- Otherwise, if the length clock ticked, then increment the length counter
elsif (SOUNDLENGTHCLK'Event and SOUNDLENGTHCLK = '0') then
LENGTHCOUNTER <= LENGTHCOUNTER + 1;
-- Also, if we have reached the end of the note, then stop playing
if (LENGTHCOUNTER = SOUNDLENGTH) then
SOUNDPLAYING <= '0';
end if;
end if;

-- If the period clock has ticked over, we need to increment the period counter
if (SOUNDPERIODCLK'Event and SOUNDPERIODCLK = '0') then
PERIODCOUNTER <= PERIODCOUNTER + 1;
-- If we are end of the period for this note frequency, then toggle the buzzer
if (PERIODCOUNTER = SOUNDPERIOD) then
LOCALSOUNDER <= not LOCALSOUNDER;
PERIODCOUNTER <= x"00";
end if;
end if;
end process;

-- Pass the buzzer state out only if we are playing a non-zero period node
SOUNDER <= LOCALSOUNDER when (SOUNDPLAYING = '1' and SOUNDPERIOD /= x"00") else '0';
-- And flag an interrupt only if we are not playing and the length is non zero
nSOUNDINT <= '0' when (SOUNDPLAYING = '0' and SOUNDLENGTH /= x"00") else '1';

end behavioral;

Two clocks, SOUNDPERIODCLK and SOUNDLENGTHCLK drive the two counters, PERIODCOUNTER and LENGTHCOUNTER. These clocks are of course derived from the global E clock. The PERIODCOUNTER will toggle the buzzer output when it reaches the value held in the SOUNDPERIOD register. This sets the frequency of the output. Similarly, the LENGTHCOUNTER controls wether the sound should be playing (SOUNDPLAYING is 1).

A difference to how this was implemented in the CPLD exists because the sound should only play once, when the SOUNDLENGTH register is written too. This is achieved by nLENGTHSET input. This is 0 only on the cycle that the register is updated. When this happens, the SOUNDPLAYING state signal is set to 1 and the LENGTHCOUNTER is reset. An interest quirk of this behaviour is that the LENGTHCOUNTER counts up regardless of wether a sound is being played, but it will always count from 0 when the sound is playing.

Interrupts are generated (nSOUNDINT becomes 0) when the SOUNDPLAYING signal turns 0 and a length is set. Otherwise the interrupt line is high. Thus to clear the interrupt, either another note should be played (setting SOUNDPLAYING to 1) or a zero note length should be set. The current design does not include niceties like a configuration register to enable or disable interrupts, but since the CPLD design includes interrupt routing facilities, this is not a problem.

Inside the MPU, operating the buzzer is nice and trivial. Here is the assembly code for the initialisation routine, and a simple interrupt handler:

buzzerinit:     clr BUZZERTONE          ; clear the buzzer frequency...
                clr BUZZERDURATION      ; and duration registers
                clr buzzerpointer       ; clear the "next note" pointer
                clr buzzerpointer+1

                ldx #buzzerhandler      ; get the handler address
                stx handlebuzzer        ; and save it in the vector table

                ; interrupt register
                lda IRQFILTER           ; get current value
                ora #IRQBUZZER          ; or in the buzzer irq line
                sta IRQFILTER           ; for the buzzer interrupt

                rts

buzzerhandler:  pshs a,b,x              ; save our registers!
                ldx buzzerpointer       ; load the current pointer
                lda ,x+                 ; get the period
                sta BUZZERTONE          ; save it in the register
                lda ,x+                 ; get the length
                sta BUZZERDURATION      ; save it in the register
                ; a zero here will cause no next note to play
                stx buzzerpointer       ; save the moved along pointer
buzzerhandlero: puls a,b,x              ; restore the registers
                rts

To set the sequence playing the buzzerpointer global needs to be set to the address of the first note, and that note played, by writing the period and length to the registers in the FPGA so that at the end of the first note, an interrupt will fire, causing the interrupt routine to be entered and the next note played. This will continue until a zero length value is "played".

If I was musically talented I would have written out a nice sequence of notes that play a tune. I would then be able to play the tune and, crucially, keep using the monitor whist the tune played, since the notes are played under interrupt control. Unfortunately I'm not even slightly musically talented. So instead of a nice video of this feature in action, you will have to settle for... a picture.

As a simple illustration of the sound generator in operation here is a grab of the logic analyser showing the buzzer output and the interrupt pin. I set this in motion by loading memory with the following period and length sequence:

01 01 02 01 04 01 08 01 10 01 20 01 00 00



If you look closely you will see a small glitch around the end of the forth note. It appears that the buzzer output is forced to zero when the interrupt line goes low. This requires some investigation but, in general, the VHDL is doing as intended.

I think I've pretty much reached the limits of what can be done with the old SBC board coupled to my FPGA development breadboard. The next thing I will start on will be the new FPGA-incorporating main board. And, indeed, I need to also decide what kind of computer board I want to produce. I have several main choices:
  • Repeat what was done before and have a main board and an IO daughter board
  • Some kind of backplane arrangement
  • A single board incorporating all the parts
But before this can start I need to flesh out the designs for the DMA controller and MMU, something I'll do in my next post...