Driving a YM2151 FM synth chip


alex

Recommended Posts

As great as the famous C64 SID chip is, it is not the only way to produce great music and sound effects. There is another category of sound chips based on frequency modulation (FM) synthesis.

A very popular choice, mainly in Japanese arcades and consoles as well as some keyboard synthesizers / pianos, is the Yamaha YM2151. This is in fact one of several chips in the family of OPL, OPN, OPM and OPS chips. These acronyms indicate how many oscillators per channel the chip contains but they all fall in the wider family of FM synthesis chips.

The YM2151 (and others in its category) do not output analog sound, instead they output digital data over a serial channel. This typically goes to a separate D/A chip like a YM3012 stereo DAC or a YM3014 mono DAC. The YM2151 and its companion DACs are available for purchase on ebay for just $3 each so the enthusiast can experiment with them without considerable cost.

The fact that the chip does not have an integrated D/A and requires additional external components might be considered a drag by some, but in this particular instance, for someone like me, the fact that the chip outputs digital data is actually a bonus since that output can be fed back into an FPGA (without going through a D/A and A/D conversion) and further processed numerically.

To be honest, the idea for this mini project came to me after browsing though various schematics of arcade games. I came across a high res scan of a schema for the arcade version of Double Dragon

This was one of those rare schemas that are clean and legible but also just real easy to follow (despite it being over 20 pages), it's just a pleasure to look at and it just makes sense. The signals that cross pages are also very easy to follow because the designer labeled them not just with the page they connect to but the coordinates on that page as well. This is one of these games that have a separate sound board, complete with its own CPU. The interface from the game board to the sound board is via a simple 8 bit port with a single strobe (write) signal. I had the feeling I could just implement most of this sound board on a FPGA and then simply drive the external YM2151 chip with the signals produced by this sound board implementation.

Wiring all the external chips on a prototyping board is not really that hard, below is the basic schema that needs to be followed. This schema is a composite after cutting/pasting parts from different pages of the original schema to illustrate just the YM2151 connections to the D/A and audio amps.

post-29560-0-10551000-1386127523_thumb.p

So in order to implement the sound board on a FPGA I need to figure out how the main game communicates with the sound board. Focusing on the main game CPU is the wrong approach here though, this is like taking the long way to get somewhere, the right thing to do is look at the sound board CPU and see how it needs to be "tickled". Here is the schema with irrelevant bits taken out.

post-29560-0-70864800-1386127520_thumb.p

The data latch IC17 is written to by pulsing the signal line *3806 low then high. This clocks in the value from the DB data bus into the IC17 latch. In parallel however, the signal *3806 also clocks the flip-flop IC39. Because the D input is tied high, the pulse on its clock line cause the D input to be latched to the Q output (not shown) and its complement /Q at pin 9, meaning /Q goes low whenever the clock (line *3806) rises. This cause the active low /IRQ line to the 6809 CPU IC49 to go low triggering an interrupt.

Disassembling the sound ROM we see that the CPU vector table contains:

ROM:FFF0                 fdb start               ; RSVDROM:FFF2                 fdb start               ; SWI1ROM:FFF4                 fdb start               ; SWI2ROM:FFF6                 fdb vec_FIRQ            ; FIRQROM:FFF8                 fdb vec_IRQ             ; IRQROM:FFFA                 fdb start               ; SWIROM:FFFC                 fdb start               ; NMIROM:FFFE                 fdb start               ; RST
So following the IRQ vector we get to:

ROM:880D vec_IRQ:                                ; DATA XREF: ROM:FFF8oROM:880D                 lda     $1000ROM:8810                 cmpa    byte_2          ; if same as currently playing melodyROM:8812                 beq     locret_881A     ; exitROM:8814                 ldb     byte_0ROM:8816                 bne     loc_881BROM:8818ROM:8818 loc_8818:                               ; CODE XREF: ROM:881DjROM:8818                                         ; ROM:8823jROM:8818                 sta     byte_A          ; store index of melody to playROM:881AROM:881A locret_881A:                            ; CODE XREF: ROM:8812jROM:881A                 rti
Simple stuff so far. When the IRQ vector is called it reads address $1000 and ignoring some of the bits in the middle, it stores that value to byte_A (RAM address $000A) and exits the interrupt handler via the RTI instruction. The reason we ignore some of the code in the middle is because I've already analysed it for you and I can tell you it's not relevant for our discussion.

So why does the IRQ handler read address $1000? Well if we lay out the address lines as A15, A14, ... A1, A0 then $1000 is binary 0001 0000 0000 0000 on the corresponding address lines. So the top 5 address lines A15,14,13,12,11 are 00010 when reading from address $1000.

Looking at the schema we see these lines are connected to address decoder IC79 (pin 5 on that IC is not labeled in this cut down schema but tracing it on the real schematic shows it going to A15 on the CPU). IC79 is a 3-to-8 decoder meaning the 3 bit binary signal presented to its inputs 1, 2 and 3 is decoded so the corresponding output 0 through 7 goes low. Pins 5 and 6 are active low chip enables (meaning they must be low for the chip to do its job). So when the CPU places address $1000 on the address bus, the chip sees 00010 on its pins 5,4,3,2,1, meaning the two enables are enabled (low) and the remaining binary data 010 represents decimal number 2, so output 2 goes low. This output goes to our familiar flip-flop active low clear input. When the clear input is activated by a low signal the flip-flop clears its state, so the Q output (not shown) goes low (cleared) and its complement /Q goes high.

So the operation is now clear, whenever the *3806 signal goes low then high, the DB is latched into IC17 (which goes to the CPU data lines through another buffer to avoid contentions with other stuff on the data bus) and an IRQ is triggered. The CPU stops whatever it is doing and services the IRQ by executing the IRQ routine which clears the IRQ signal by reading address $1000 and storing the DB data from the IC17 latch into the CPU internal memory at address $000A.

This is pretty cool because I can now go to MAME and set a breakpoint at the sound CPU address $880D and every time an IRQ occurs, I can see what value was being written. Using this trick I can see that the following values cause the following things to happen.

$FF clears/stops any melody sound currently playing$FE clears/stops any sound effect currently playing$01 through roughly $30 play different effects or melodies on the YM2151$80 through ?? play different sound effects but not through YM2151.
The sound effects triggered with values > $80 do not use the YM2151 but instead play these effects through separate DAC chips IC80, IC81 and associated circuitry consisting of discrete programmable counters and digital comparators (the relevant schema for these is not shown anywhere in this post). There are in fact two identical circuit duplicated so two effects can be played simultaneously. I won't show an analysis of the circuitry for the effects but roughly it works like this.

Each channel has a 64KB ROM with sound effects stored inside. The CPU writes to the programmable counters to preset them with the starting address in the ROM and also to the digital comparators with the end address in ROM of the sound effect. The counters start counting up from the programmed address until the end address is reached at which point the programmable comparators issue a reset to stop the sound playing and the counters stop counting.

For example the CPU can preset $1234 into the counters and $2345 into the comparators and the ROM would be presented with addresses $1234, $1235, $1236, ... up to $2345 on it's address bus then the address would freeze there. The ROM output is connected to the DAC chip causing the value from the ROM data bus to be turned to analog sound into the speakers. The address counters are clocked with a presumably low clock which it seems is generated by the DAC chips, probably a few KHz.

More to follow...

EDIT: Any code for this project will be available here. So far I have added there the full schematic of the sound board, all three pages into one image for convenience. My development hardware will be a Papilio Plus with an Arcade Mega Shield (plus any external chips of course).

This post has been promoted to an article

Link to comment
Share on other sites

So far I've created a VHDL test jig to enable me to drive the not yet implemented sound board. The test jig re-uses components I've used before in other projects, such as a PS2 keyboard controller, a VGA controller to generate a video signals and the hexy module from the C64 project to display characters. These put together allow me to enter a hex value on the keyboard and display it on the VGA screen. When I hit enter, it latches the entered value to the sound board module's input and generates a write pulse on signal *3806 in the schematic. The simulation of this was straightforward, if you check out my code repository, the file "keypress.txt" contains a set of key presses to simulate key presses ESC, RET, down arrow, RET, 8, 0, RET. The meaning of these is simple, escape just resets the value to $FF, return, latches it, then down arrow decrements the value to $FE and return latches it finally $80 is entered and the last return latches it as well. So the sound board is being sent values $FF, $FE and $80. The first two as you recall were found to clear and stop all music and sound effects, while value $80 should play a sound effect via the A/D chips (not the YM2151).

 

This is all so far being set up for simulation in isim, this is why I have created a text file with key presses in it. Running the simulation (and fixing minor bugs on the way) I can see the sound board module (remember this VHDL module is still currently empty) is being driven as expected. Next it's time to finally put something in the sound module. At a minimum I brought in the 6809 CPU I used in Robotron, a small 2KB RAM, the sound CPU's 32KB program ROMs and I implement the address decoding and data bus multiplexing to allow this to run. Finally it's time for another simulation, remember these pictures are from  the successful end result simulations, several more were done until all bugs were cleared.

 

post-29560-0-98034900-1386236453.png

 

Over here we see the top two traces representing the keyboard data as explained earlier. Values are entered on the db bus, the wr_n is the strobe signal going low, data is latched on the rising edge generating an IRQ as explained in the previous post above. The IRQs can be seen in the mpu_irq trace as brief rising pulses (this 6809 VHDL implementation uses positive logic, unlike the real 6809 CPU which uses active low interrupts). It is clear that for each value entered a new IRQ is generated. Zooming into the region where the final value $80 is entered we can see:

 

post-29560-0-90063300-1386236452_thumb.p

 

As soon as wr_n rises (note it is asynchronous to the CPU clock hclk), on the next rising CPU clock the IRQ line is asserted (goes high). The CPU was executing code at address $83a1, $83a2, etc but then starts accessing RAM memory at $6fc down to $6f2. This is where it pushes registers onto the stack prior to jumping to the ISR (interrupt service routine). Next we see it fetching the ISR vector from address $fff8 and $fff9, then jumps to it at address $880d. This matches perfectly with the disassembly we saw in my previous post. The first instruction is fetched and executed and as soon at memory location $1000 is addressed, it clears the IRQ latch as explained before. Zooming further into the simulation, we can see what happens after the value $80 was sent to the sound CPU. For this part you'll have to download and refer to the full sound board schematic from my source code repository as it's a little too large to post here.

 

post-29560-0-62809300-1386236454.png

 

The address decoding signals s3800w through s3807w begin to be triggered in the following order (unlike the schematic, I'm using positive logic here, so active high instead of active low). s3806w is triggered first, then s3804w, s3802w and finally s3800w. This makes sense because as per the schematic, the effect of this is as follows:

 

s3806w IC75 flip-flop clear input is asserted causing AD1RST signal to go high. This is the reset signal to IC80 A/D converter.

s3804w preset the counters IC44,IC28 with the start address in ROM where the sample is located

s3802w presets address comparator latch IC15 with the address in ROM where the sound should stop playing.

s3800w finally IC75 flip-flop set input is asserted causing the AD1RST reset signal to be deasserted so the A/D can start converting values.

 

Things are definitely falling into place. At this point I feel I'm very close to being able to play sound samples through the two DACs. I know I promised to hook up a YM2151 chip to this, but I feel a slight detour is in order here. Having this play some sound samples would be a good test that the sound module behaves as expected, not just in simulation but on real hardware as well. Sorry, I just can't help myself.

 

Link to comment
Share on other sites

Aha ha ha (or should I say, bah humbug!), somebody smack me! This entire time I've been assuming the two chips IC80 and IC81 were just plain old A/D converters. I did see they only received a 4 bit data bus from the ROMs but I imagined this might just be enough to play some PCM sound effects. Upon looking up the datasheets for these MSM5205 chips, it turns out they are ADPCM decoders which according to Wikipedia ADPCM is (or is closely related to) G.726 speech codec. This means the data in the ROMs has been compressed to 4 bit ADPCM data and the MSM5205 chips receive this 4 bit data and decompress it into 12 bit internally before feeding it to a 10 bit internal A/D converter. Hey, this is from the data sheet, I'm not making this stuff up. 12 bit data, 10 bit A/D converter! Who am I to argue.

 

This is all starting to make sense now, the ADPCM decoders receive the clock on M8H, which comes from the horizontal video counter line and it carries a 375Khz clock signal. From the datasheet of MSM5205 they recommend the clock to be 384Khz or 768Khz and it can be divided internally by a programmable value of 48, 64, 96 to obtain 8Khz, 6Khz or 4Khz respectively (or 16K, 12K, 8K when the clock is 768K). This is effectively the sampling rate at which the sound will be played and this sampling clock is output on pin 14 /VCK which goes back and clocks the counters that generate the ROM addresses. Makes perfect sense now. Given that they drive the MSM5205 with 375Khz, and they select a divisor of 48 through pins 1, 2 being tied to VCC and GND respectively, they end up with a sampling frequency of 7812.5Hz, close enough to 8KHz I guess.

 

Googling for ADPCM, the first result being Wikipedia of course, the second search result link goes straight to a Dialogic PDF on the ADPCM algorithm and inside the PDF they mention the VOX file format. I immediately recognized this because my go to sound processing software, Goldwave, has that as one of the many formats it supports. In addition, Goldwave is good for mucking around with sounds because it is able to load an arbitrary chunk of binary data as a selectable 8, 12 or 16 bits, signed or unsigned, little or big endian, mono or stereo, selectable sampling rate. Provided your chunk of binary data is a PCM waveform, you end up with a nice sound, otherwise, just screetching noise. According to MAME's source code file ddragon.c ROMs 21j-6 and 21j-7 hold the ADPCM data (had I consulted this first, I'd not had made the erroneous assumption the MSM5205 chips were just plain DACs) so loading each of these file in Goldwave as VOX format, gives indeed the sound effects that can be heard in the game. That's a good start and a confirmation the ADPCM algorithm that MAME uses can be used as reference to build a VHDL implementation of the MSM5205 chips, because to be honest, the algorithm description in the Dialogic's PDF leaves a lot to be desired, as far as giving enough detail to write some code is concerned.

 

post-29560-0-21923100-1386284568.png

 

So I'm off to write an ADPCM decoder. Nice weekend project.

Link to comment
Share on other sites

So that was an interesting detour. What seemed complicated at first turned out to be not so hard after all. After reading the Dialogic PDF and the MSM5205 datasheet, I had an overview of how it all worked but to save time, I went straight to the MAME sound driver msm5205.c and quickly modified it to run standalone. I used it to run both 64KB Double Dragon ADPCM ROM files "21j-6" and "21j-7" through it and obtain a large PCM reference file containing all the sound effects the game can make. The goal would be to get my VHDL code to match this output bit for bit.
 
I initially started writing lazy VHDL, just using std_logic_vector types and quickly got into trouble when doing the signal processing requiring signed data, so I had to do the right thing and switch to signed types. I always hate this part because I have to figure out which libraries to include. You can't just include everything because there will be conflicting definition and you get errors like "Multiple declarations of signed included via multiple use clauses", but if you don't include the right libraries you end up with errors too like "cannot determine exact overloaded matching definition for operator" or "<signed> is not declared".
 
In the process of wrapping my head around vectors and signed math I came across this very useful picture explaining where we can just typecast and where we need to convert.
post-29560-0-40613200-1386385038.png
 
So examining how the MAME algorithm was coded, it seems to go through and calculate the lookup table values during initialization, but on a FPGA we can't afford this luxury. I took the suggestion in the Dialogic PDF page 6 "This equation can be implemented efficiently as a two-stage lookup table". The lookup tables are published on that same page as tables 1 and 2. Translating table 2, the step sizes, to VHDL gives us:
--  Dialogic ADPCM Algorithm, table 2type STEPVAL_ARRAY is array(0 to 48) of signed(11 downto 0);constant STEP_VAL_TABLE : STEPVAL_ARRAY := (  x"010",x"011",x"013",x"015",x"017",x"019",x"01C",x"01F",  x"022",x"025",x"029",x"02D",x"032",x"037",x"03C",x"042",  x"049",x"050",x"058",x"061",x"06B",x"076",x"082",x"08F",  x"09D",x"0AD",x"0BE",x"0D1",x"0E6",x"0FD",x"117",x"133",  x"151",x"173",x"198",x"1C1",x"1EE",x"220",x"256",x"292",  x"2D4",x"31C",x"36C",x"3C3",x"424",x"48E",x"502",x"583",  x"610");

Table 1, the adjustment factors, does not even warrant a lookup "table" as it only has 5 distinct cases so it just becomes:

case di(2 downto 0) is  when "111"  => if step < 41 then step <= step + 8; else step <= to_unsigned(48, 6); end if;  when "110"  => if step < 43 then step <= step + 6; else step <= to_unsigned(48, 6); end if;  when "101"  => if step < 45 then step <= step + 4; else step <= to_unsigned(48, 6); end if;  when "100"  => if step < 47 then step <= step + 2; else step <= to_unsigned(48, 6); end if;  when others => if step > 0  then step <= step - 1; else step <= to_unsigned( 0, 6); end if;end case;

Note the limiting put in place, if the new value after adding or subtracting tries to go outside the range 0..48, it is forced to the max or min value as appropriate. This is basically copied from the MAME code where they do this:

if (voice->step > 48) voice->step = 48; else if (voice->step < 0) voice->step = 0;

The final part of the algorithm is shown on page 5 of the Dialogic PDF and corresponds to this piece of code from MAME

voice->diff_lookup[step*16 + nib] = nbl2bit[nib][0] *  (stepval   * nbl2bit[nib][1] +   stepval/2 * nbl2bit[nib][2] +   stepval/4 * nbl2bit[nib][3] +   stepval/8);

So making that into VHDL we get:

-- table lookup only has positive valuesstepval  <= STEP_VAL_TABLE(to_integer(step));                                  -- value-- so we can afford to just shift in zeroes from the left without sign extensionstepval1 <=    "0" & stepval(11 downto 0) when di(2) = '1' else (others=>'0'); -- value/1stepval2 <=   "00" & stepval(11 downto 1) when di(1) = '1' else (others=>'0'); -- value/2stepval4 <=  "000" & stepval(11 downto 2) when di(0) = '1' else (others=>'0'); -- value/4stepval8 <= "0000" & stepval(11 downto 3);                                     -- value/8sum_val  <= ( (stepval1 + stepval2) + (stepval4 + stepval8) );sign_val <= sum_val when di(3) = '0' else -sum_val; -- di(3) determines if we return sum or -sum

This is basically all there is to it. Even though the MSM5205 datasheet states that the chip does not have internal overflow protection (unlike it's later revision, the MSM6585), I decided to implement this protection anyway (as per MAME code) so this is done like so:

-- hard limit math results to 12 bit valuesif (final_val + sign_val < -2048) then  final_val <= to_signed(-2048, 16); -- underflow, stay on max negative valueelsif (final_val + sign_val > 2047) then  final_val <= to_signed( 2047, 16); -- overflow,  stay on max positive valueelse  final_val <= final_val + sign_val;end if;

By writing a VHDL testbench that reads the two ADPCM game ROMS and passes them through this module then outputs all the values to another file, I was eventually able to compare that the output of this VHDL code matched the reference file obtained from the MAME C code 100%

 
So the MSM5205 chip is now implemented, more or less. I did not bother to figure out how it handles 3 bit ADPCM since in this application it is hard wired as 4 bit ADPCM. I imagine the lookup tables will be different (and smaller) and the addition of the step values will have one less sum to add as bit 0 of the ADPCM data does not exist.
 
Also unlike the real MSM5205 chip which outputs an analog value, I decided not to add the DAC internal to this module but just output the 12 bit audio data on a bus. This gives more flexibility by letting the user connect it to an external DAC, or further process this data, such as summing it with another audio channel.
 
Unfortunately for any of you playing along at home, you won't be able to fit the latest code into a standard Papilio as it stores the 128KB of ADPCM data in an external SRAM. By the time the 32K CPU ROM and 2K RAM is added to this, the total memory requirements are 162KB. My Papilio Plus has 1MB of SRAM so I'm good. The good news is that the external ADPCM ROMS are only accessed at 8Khz, so a DRAM controller like Hamster's could work just fine here, but I'll let someone who has a Papilio Pro deal with that :)
 
Link to comment
Share on other sites

Now that the ADPCM sound playback is working, no more mucking around, it's time to hook up the external chips and get some action. I realized that having the Arcade Megawing installed would get in the way of that as it takes up all the IO pins, heheh, duh! So with the last code commit, I've switched platforms from Papilio to Pipistrello. There's no reason I could not have continued with the Papilio except I do not have a PS2 keyboard wing but I have a PS2 PMOD for the Pipistrello. Also the Pipistrello has built in HDMI and audio out, for the ADPCM so there's less wings all around to install on it.

 

Nevermind, meet the victims players:

post-29560-0-56433600-1386540513.jpg

 

Clockwise from the bottom we have two level translators, the YM2151 FM synth, the YM3012 stereo DAC and a LM324 quad op-amp required by the DAC. The datasheet of the DAC has an example on page 5 using an op-amp with a slew rate of 4V/us whereas the LM324 only rates at about 0.4V/us. I'm not sure if this will cause any issues, but if it does I can move to a TL084 op-amp, however that requires dual power rails (positive and negative voltage supplies) which I'm trying to avoid, the LM324 is happy with just a positive power rail. Regardless, some sounds will be heard, no matter what!

 

I chose those level translators based on TXB0108 chips because they are supposed to be smart and have auto direction sensing (they are bidirectional). Other level translator chips I had are unidirectional and would have complicated the wiring to the data bus of the YM2151 which is read/write.

 

A word of warning, I don't do pretty breadboard layouts, so there won't be any of this

 

 breadboard_final.jpg

 

but more likely plenty of this

 

perfboard_done.jpg

Link to comment
Share on other sites

I bought a bunch of these in all three types, M-M, M-F and F-F. You can use them to hook up just the parts of the megawing that you need, and they're handy for connecting breadboards and anything else. It's the same way I've been using wings on the Prime Sense board.

 

http://dx.com/p/single-port-female-to-female-jumper-wire-set-50-pack-20cm-length-55454

Link to comment
Share on other sites

Yeah you can get them from a number of places, all pretty much the same thing. I was skeptical at first but having been using the jumpers I've found they're incredibly useful. I also have an ancient set of precut solid core breadboard wires for tidy wiring on the breadboard itself.

 

I just ordered a bunch more of those jumpers yesterday. As you've also found they get used up fast. I tend to wire something up and then not want to unwire it to reuse the jumpers until I get around to building a proper cable.

Link to comment
Share on other sites

I bought loads of these http://www.ebay.com/itm/350882444179

also have wire and crimper, but not having luck crimping those bastards yet.

this crimper: http://www.adafruit.com/products/1213 it's supposed to be right size. (dumpont crimps sells cheap on ebay, like $9 for 500 male crimps, that should last a lifetime)

And after destroying to many wires using cheap wirestrippers I went for ths http://www.amazon.com/Knipex-1242195-Universal-Insulation-Stripper/dp/B000X4PSUA/ref=sr_1_3?s=power-hand-tools&ie=UTF8&qid=1386621680&sr=1-3&keywords=knipex

Link to comment
Share on other sites

I am always amazed that bi-dirctional logic level convertors actually work.How do they know which device is driving the line? why don't they latch up? I must read a datasheet one day.

 

Maybe it is it a bit like a thermos flask - which is smart enough to keep cold drink cold and keep hot drink hot...

Link to comment
Share on other sites

Well, after a whole week where I didn't seem to manage to find any time to progress this, I finally got a few hours on this lazy Sunday afternoon. First off, my f#@*! Rigol scope decided to fail of course now that it's outside its warranty. By fail I mean it still works fine in standalone mode but the USB connection to the PC isn't behaving. It is detected as the correct device every time when I plug in the USB cable but when I try to connect to it with the Rigol software it fails to connect about 99% of the time (not exagerating here) and the one time when it does finally connect, I can sometimes execute small commands like pressing a button through the virtual control panel but as soon as I try a more lengthy option like trying to get a screen shot from it the connection fails again.
 
This wasted a few hours I didn't have since I went though all the troubleshooting options, replaced USB cable, reinstalled the drivers, upgraded the firmware, tried a new PC, etc, with no luck. Since it was really pissing me off and wasting my time I bit the bullet and pulled out the USB cable and connected to the scope via RS232 (it's secondary port) which allowed me to control the scope and pull down screen shots from it. I suspect the USB xtal inside the Rigol might be out of spec and this causes the USB connection to fail on longer packets. I might have to tear it apart sometime and see what's going on with that POS.
 
Don't worry this isn't the only fail today, more coming right up. I wired up the chips on a breadboard as per the schematic, using level translators of course, double checked everything twice, applied power and breathed a sigh of relief when nothing went *pop*, checked all the voltages at the chip pins, all the chips were running at 5V  while the FPGA side was on 3.3V.
 
I next tried to play something and of course, no sound came out, conforming perfectly with Murphy's Laws and leading to another lengthy debugging session. This time it's actual chips on a board so no more luxurious Isim sessions, it's down to a 2 channel scope. I briefly considered pulling OLS into the project which would allow me to do multi-channel logic captures but that would just complicate things, I decided to at least do some basic debugging with just the 2 channel scope.
 
First off I scoped some signals and I could see fairly noisy waveforms. This is the 3.57MHz clock generated by the FPGA and sent to the YM2151 chip. Blue trace is the 3.3V FPGA side and yellow is the same waveform after being level upped by the converters to 5V.
 
post-29560-0-76489300-1387087907.jpg
 
There's a fair amount of overshoot there and I tried my best to get rid of it by adding a termination resistor to the clock line, sprinkling decoupling caps to all the power lines, etc but nothing seemed to help. Also tried changing the constraints file:
NET "YM_CLK"         LOC = "D18"  | IOSTANDARD = LVTTL | DRIVE = 2 | SLEW = QUIETIO ;

I came across QUIETIO after some googling and I'm not entirely sure it has any effect, the only time it is mentioned is once in UG615 and only for IOBUF primitive. I also tried SLEW=SLOW without much luck. Anyway after a while I gave up trying to clean up signals and continued on. I got to the output of the YM2151 chip and as I mentioned in a previous post, the data exits the chip in digital form as a serial data stream. There is a clock of course, a serial data and two other signals that are used as channel selectors for channel 1 and channel 2 alternately. Below is one of the channel selectors in yellow and the serial data in blue. There is another channel selector and a clock signal not shown as I only have a 2 channel scope. This is its idle state after reset when no sound is output. The serial data format according to the datasheet is a 3 don't care bits a 10 bit mantissa and 3 bit exponent so 16 bits per channel which is shifted in LSbit to MSbit.

 

post-29560-0-67049700-1387087908.jpg
 
After instructing the chip to play a song, I could then see the serial output of the YM2151 starting to move data out, such as in the following snapshot where the blue waveform would continually change indicating data was being sent out.
 
post-29560-0-47805400-1387087909.jpg
 
However I could see no output from the YM3012 serial DAC at all. I checked that it was not being held in reset and that the clock, serial data and channel selectors were getting to it from the YM2151 and arriving at the correct pins, but the DAC simply was not outputting sound. I checked pin 15 which according to the datasheet is supposed to be a high precision voltage outputting 1/2 VDD but instead I was getting 1.8V when the expected voltage would have been 2.5V (1/2 of 5V supply). Checked the supply pin 1 to the chip and it indeed had 5V on it. I started to suspect the DAC was a dud, and I could see no signal output on pins 8 and 9 into the sample and hold caps.
 
At this point I considered my options and buying another YM3012 headed into the xmas holidays would not be a wise choice. It would probably take forever to get here. The other option is almost obvious, since the YM2151 outputs digital data, bring that back into the FPGA and decode the signals, convert the mantissa/exponent values back into unsigned data and feed them to the standard sigma-delta dacs we use all the time to make noise with our FPGAs.
 
After a little coding I had the basic YM3012 in VHDL and when trying to play a song now I can clearly hear the expected notes, however there is a lot of noise and the sound is very distorted. I suspect there are issues in my math and possibly there is overflow/underflow causing this huge amount of noise. On the plus side, this proves the YM2151 is working and the YM3012 chips is pushing up daisies.
 
It's now heading into the evening so it's time for me to have... lunch! Yes, time got away from me again.
Link to comment
Share on other sites

This is really great Alex, thank you for taking the time to share this project with all of us. :)

I will be posting this to the blog.

Jack.

Hey Jack, thanks for posting this on the front page. I just noticed however that you've made a typo and are calling the chip a YM2141 in both the title and content of the blog. I would recommend you have that changed to YM2151 in order to preserve everyone's sanity :)

You can link to the video as well since this project's now fairly complete. I'll tidy up the code and push the last update soon with the vhdl implementation of the YM3012 DAC. I just want to run it through some simulations to make absolutely sure I have the math right even thought the listening tests sound correct.

Link to comment
Share on other sites

Ahhh, ok, I just fixed the typo. It was weird to see YM2141 because I knew it was a YM2151. When I went to fix the typo I discovered that when I touch type my lazy index finger does not want to stretch all the way to the 5 key when my ring finger is still on the 1 key! So I was just touch typing too fast and my finger did not make it all the way to the 5!

 

Thanks for the heads up and keep up with the great work.

 

BTW, I started work on Friday with cleaning up the OLS core for our general use. I'm turning it into a schematic symbol for use with Papilio Schematic Library and working on a standalone and a wishbone version.

 

Jack.

Link to comment
Share on other sites

Can someone please take a look at this picture and let me know what kind of crack pipe these Japs were smoking when they wrote this.
 
post-29560-0-04549800-1387185112_thumb.p
 
I've tossed and turned this every which way to make the math add up as per their explanation and in the end I ended up making some assumptions that ended up in giving me correct sounds but I can't for the life of me reconcile it with this datasheet math. If you have trouble with negative powers, that equation can be rewritten as:
 
Vout = 1/2Vdd + 1/4Vdd * (-1 + D9 + D8/2 + D7/4 + D6/8 + D5/16 + D4/32 + D3/64 + D2/128 + D1/256 + D0/512 + 1/1024) / 2N
 

Where D9 through D0 can be either 0 or 1 depending on if that bit is set or clear. We can then make the following observations:

  • N is the binary equivalent of the S2, S1, S0 bits (note the inversion sign over them!) and they tell us they can't all be zero, which because of the inversion it means that N can't be 7, so N can be 0,1,2,3,4,5,6
  • The terms in the equation use the bit value to either add or cancel out the corresponding bit term, so for example when D8=0 in D8/2, we end up with 0/2 so that term disappears, whereas when D8=1 we end up with 1/2, similarly when D1=0 we have 0/128 = 0 and when D1=1 we have 1/128
  • The bit D9 is the sign bit and in this case it is inverted, so then D9=0 we have a negative value because the -1 in front of it is added to D9 so -1+0=-1, whereas when D9=1 we have -1+1=0
 
So working with the above I can throw some bit values and end up with this sort of math for the part of the equation in parenthesis only
 
D9......D0
0000000000 = -0.9990234375          
0011111111 = -0.5009765625 
0111111111 = -0.0009765625 
1000000000 =  0.0009765625          
1011111111 =  0.4990234375 
1111111111 =  0.9990234375
 

So these values cover various mid and edge cases. We see that the parenthesis can't ever have the value of zero because of the term 1/1024 which can't be cancelled by any bit value, so that it will always have that as a minimum value and the maximum value will be 0.999 and all these values I mentioned can be positive or negative depending on D9.

 

So far so good, the spanner in the works is the last sentence. From there as I understand it, they are telling us that there is a 1/2Vdd potential as a center so the minimum deviation from that center is +/- 1/2Vdd*2-16 and the maximum deviation from that center is +/- 1/2Vdd
 
This in principle make sense because with the maximum deviation we would have Vout = 1/2Vdd +/- 1/2Vdd = 0 to Vdd range. What I cannot see is how they obtain these values from the original equation Vout = 1/2Vdd + 1/4Vdd(magical_value) where magical_value must reach to +/- 2
 
As we can see in that table above the range in parenthesis only stretches to just under +/- 1 and then it is multiplied with 2-N so really divided by 2N and there is no allowed value of N that would satisfy 2-N=2

The only way I can see everything fitting the description is if that 1/4 in the equation was a typo and they meant to write 1/2

Link to comment
Share on other sites

Yes granted, a signal that swings +/- 1/4Vdd on a 1/2Vdd center will have a peak to peak amplitude of 1/2Vdd, or in other words it swings from 1/4Vdd to 3/4Vdd. This could be one interpretation, but the same sentence mentions a minimum amplitude on 1/2Vdd*2-16 so the only way I can get close to that is by setting N=6, D9=1 and D8 to D0=0. This gives me this equation

 

Vout = 1/2Vdd + 1/4Vdd * (2-10) * 2-6  =>  Vout = 1/2Vdd + 1/4Vdd * 2-16

 

again the amplitude in the equation doesn't quite match the stated 1/2Vdd*2-16 in the text unless again the 1/4 was a typo and they meant 1/2 (or can you see any way to make sense of that, I might have missed?)

Link to comment
Share on other sites

Using the same thinking, the smallest peak-to-peak you can get is if N=6 and D changes from 0111111111 to 1000000000 which in is equivalent to Vout going from

Vout = 1/2Vdd + 1/4Vdd * (-1 + 2-1 + 2-2 + 2-3 + 2-4 + 2-5 + 2-6 + 2-7 + 2-8+ 2-9+ 2-10) * 2-6 =>  Vout = 1/2Vdd - 1/4Vdd * 2-16

to

Vout = 1/2Vdd + 1/4Vdd * (-1 + 1 + 2-10) * 2-6 =>  Vout = 1/2Vdd + 1/4Vdd * 2-16

or a peak-to-peak swing of 1/2Vdd*2-16

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.