Part 6 - Sound
This article continues on from Part 5 - Video Revisited.
Now the video controller was sorted out and FPGABee was functional enough to run a game that I wrote back in the 80's, but there was no sound. In this article FPGABee gets a speaker.
Microbee Sound Interface
The Microbee's sound interface is extremely simple - bit 6 on I/O port 2 is wired to the speaker, so toggling that bit will also toggle the speaker. Toggle it at a suitable frequency and you get sound.
In a real Microbee, port 2 is actually connected to the PIO - a Programmable I/O chip that also handled tape I/O and other I/O responsibilities.
For the purposes of the speaker, FPGABee doesn't need the PIO so I'll be ignoring that for now.
Giving the Nexys 3 a Speaker
The first step in getting sound working was to figure out how to actually connect a speaker to the Nexys 3 board. Theoretically it might be possible for the Spartan chip to drive the speaker directly but I didn't want to risk it so I borrowed the circuitry from the Microbee and hooked it up through a transistor:
Since I don't have 10V handy, I picked up the 3.3V from the Nexys' PMod connector and adjusted R27 down to 15K. Although the speaker is currently connected to it's own PMod port, I decided to use PMod pin 3 since this would allow putting both the speaker and keyboard (which uses pins 2 and 4) on the one PMod connector later if necessary.
Here it is:
Implementing Port 2
The PIO port, port 2 needs to support both "in" and "out" operations, so I used a register to store it's state:
signal pio_port_b : STD_LOGIC_VECTOR(7 downto 0);
Connect the port write:
-- Write to port 2 (PIO port B) if (z80_addr(7 downto 0)=x"02" and port_wr = '1') then pio_port_b <= z80_dout; end if;
Connect the port read:
z80_din <= x"00" when (boot_scan = '1') else -- other assignments omitted pio_port_b when (port_rd='1' and (z80_addr(7 downto 0) = x"02")) else x"00";
And finally connect bit 6 to the correct pin on the Spartan chip:
speaker <= pio_port_b(6);
Here's the declaration in the UCF file that specifies which design ports go where. JB3 is pin 3 on the second PMod connector:
Net "speaker" LOC = L4 |IOSTANDARD = LVCMOS33; #Bank = 3, pin name = IO_L39P_M3LDQS, Sch name = JB3
Pretty simple and it worked first go. I heard the familiar Microworld Basic boot beep and running Robot Fire made the appropriate noises. It's a little quieter than a real Microbee, but that's a good thing.