User Tools

Site Tools


tonegen_design

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
tonegen_design [2022/03/14 23:46]
dpisuperadmin [Minimal Tone Generator with Volume Control]
tonegen_design [2022/03/20 03:51] (current)
dpisuperadmin
Line 3: Line 3:
  
 ==== Phase Accumulators ==== ==== Phase Accumulators ====
-{{ ::phase_acc.png?200|}}A common digital technique to generate very specific frequencies is to use what is called //phase accumulation// The idea of a phase accumulator is to add a fraction of a cycle to the phase on each clock edge such that the accumulated phase reaches one (one full cycle) at the right time.+{{ wiki:phase_acc.png?200|}}A common digital technique to generate very specific frequencies is to use what is called //phase accumulation// The idea of a phase accumulator is to add a fraction of a cycle to the phase on each clock edge such that the accumulated phase reaches one (one full cycle) at the right time.
  
 Let's use an example that is specific to this design.  Let's say our audio update rate is 100 KHz.  This gives use 10 microseconds per audio sample to the output DAC.  The note C0 is 16.35 Hertz.  In one sample a C0 note will change its phase by Let's use an example that is specific to this design.  Let's say our audio update rate is 100 KHz.  This gives use 10 microseconds per audio sample to the output DAC.  The note C0 is 16.35 Hertz.  In one sample a C0 note will change its phase by
Line 11: Line 11:
 The FPGA never sees the note C0 or the frequency.  All it sees is the phase offset per sample and this is set by a look-up table in the host. Feeding the MSB of the phase accumulator to an output pin is a minimal tone generator. The FPGA never sees the note C0 or the frequency.  All it sees is the phase offset per sample and this is set by a look-up table in the host. Feeding the MSB of the phase accumulator to an output pin is a minimal tone generator.
  
-==== Minimal Tone Generator with Volume Control ==== +==== Minimal Tone Generator with Volume Control ==== 
-{{ ::audiotaper.png?200|}} A full synthesizer, as described below, may take more that its fair share of FPGA logic.  This is fine if the goal of the project is a synthesizer but is not fine if the user just wants something that beeps.  This section describes something that beeps but includes a volume control.+A full synthesizer may take more that its fair share of FPGA logic.  This is fine if the goal of the project is a synthesizer but is not fine if the user wants something that just beeps.  This section describes something that beeps but includes a volume control.
  
-Square waves are easy to generate and have a pleasing sound since they have lots of higher harmonics.  A more difficult question for a simple tone generator is how to control the volume.  Human hearing perceives audio volume on a logarithmic scale.  Electronics manufactures use what is called a 'audio taper' for potentiometers in audio applications.  We want our gain control to follow the same curve.+The API for a simple tone generator might specify the musical note to play, the volume in the range of 0 to 100, and the number of milliseconds to play the note.  We might also want to play a file of notes where each line the the file has the note, volume, and duration.  For example: 
 +    dpset tonegen note b4 30 200 
 +    dpset tonegen melody mymelody.txt
  
-There are two ways we can control the output volume of square wave.  The first is called 'pulse density modulation' The idea that we turn off the output briefly during the high part of the square wave.  Be briefly we mean fast enough that an RC low pass filter can easily average the fast pulses into DC level during the high time of the square wave.  In our case, we have two simple approaches to pulse density modulation.  One is to have, say, a 4 bit counter run continuously at a high rate and to turn off the output if the count is less than a target (volume) value.  The output volume is N/16 where N is 4 bit value set by the host.  This give linear volume relative to N.+{{ wiki:audiotaper.png?200|}} Square waves are easy to generate and have pleasing sound since they have lots of higher harmonics.  A more difficult question for a simple tone generator is how to control the volume.  Human hearing perceives audio volume on logarithmic scale.  Electronics manufactures use what is called 'audio taper' for potentiometers in audio applications. The diagram to the right shows an audio taper and we want our gain control to follow the same curve.
  
-The other PDM method we can use is to output one pulse every N clock cycles.  This gives a 1/N kind of response Both linear and reciprocal approaches have the same minimum (1and maximum (1/16) attenuation and differ only in the intermediate values.+In this section the term 'gain' mean Vout/Vin where Vin the output voltage of the FPGAGain in this section is always between 0 and 1and a lower gain means a lower volume.
  
 +{{ wiki:linear-x.png?200}} {{ wiki:one-over-x.png?200}} There are two ways we can control the output volume of a square wave.  The first is called 'pulse density modulation' The idea is that we turn off the output briefly during the high part of the square wave.  By briefly we mean fast enough that an RC low pass filter can easily average the fast pulses into a DC level during the high time of the square wave.  In our case, we have two simple approaches to pulse density modulation.  One is to have, say, a 4 bit counter run continuously at a high rate and to turn off the output if the count is less than a target (volume) value.  The output volume is N/16 where N is a 4 bit value set by the host.  This give a linear volume relative to N.
  
 +The other PDM method we can use is to output one pulse every N clock cycles.  This gives a 1/N kind of response.  Both linear and reciprocal approaches have about the same minimum and maximum attenuation and differ mostly in the intermediate values.
  
  
 +The other approach to volume control is to build a DAC out of resistors connected to the four FPGA pins dedicated to the peripheral.  Many DACs use what is called an 'R-2R' resistor network of give a linear response to the input values.  (See https://en.wikipedia.org/wiki/Resistor_ladder#R-2R)  We specifically do **not** want a linear response.  We want one in which the higher values are much greater than their linear equivalents. Shown below is a circuit that does this.  In it we swap the normal linear R-2R network resistors to get a 2R-R resistor network.  The gain of the circuit is clearly non linear.
  
 +{{wiki:r2-r.png?300}} {{wiki:r2-rgain.png?200}}
  
-==== Wave Tables ==== +The minimum output of the 2R-R circuit is 0.013 and the maximum value is 0.9935, a ratio of low to high of about 76.  While we have lost a great deal of resolution, we have gone from 4 bits of dynamic range for the linear R-2R network to over 6 bits of dynamic range using 2R-R network.
-{{ ::wavetable.png?300|}}A wave table captures a waveform by dividing it into slices and recording the output value for each slice.  If the address decoder has N bits then the length of the table is 2^N.  A wave table is usually driven by the N high bits of a phase accumulator.+
  
-The number of bits in each word determine the audio fidelity of the output.  The values in the table are often the sign and the log of the desired value Human hearing is such that perceived volume follows a logarithmic scaleand using log value makes subsequent gain and modulations easier since multiplication in linear scale is just addition in a log scale.  You can get the best fidelity from the least number of bits by making the waveform span the full range of bits.  The log function can be log-base-2, natural logarithm, or one of the standard u-law or a-law functions.+{{ :wiki:tonegengain.png?200|}} What if we combined the linear pulse density modulation with the non-linear DAC?  We could PDM control each of the FPGA output pins with a separate 4 bit counter.  Doing this gives 64K possible gain settings. Since some combinations give the same gain there are only 14000 unique gain settings.  The minimum (one-sixteenth of the LSB) has a gain of 0.000817 and the maximum (all bits high) has a gain of 0.9314giving us dynamic range (log2(max/min)) of about 10 bits.  This is not too bad considering we have 4 bit DAC
  
-There are often more than one wave table and while each may have a defaultall can be set from the host.  The API is to be determined but might appear something like: +Our design is now ready for a Verilog Wishbone implementation.  We should expect it to have  
-    dpset tonegen table_id 3      # select the table to download +24 bit phase accumulator, 
-    dpset tonegen table_addr 0    # point to first address in table +a 24 bit phase offset set by the host, 
-    dpset tonegen table_val 00 12 34 56 78 9a 87 56 78 88 99 aa bb cc dd ee +four 4 bit PDM counters, 
-    dpset tonegen table_val ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee +four 4 bit gain settings set by the host, and 
-    (... for all 2^N values ...)+an 8 bit duration counter (to count milliseconds),
  
-==== Gain Blocks ==== +(The diagrams in this section were generated using Octave.  The sources are attached to this section in a wiki comment.  Edit the page or contact the author to get the sources for the diagrams.)
-{{ ::gain.png?150|}}  As mentioned above, gain is just addition when using log amplitude values.  You have a few choices when dealing with the issue of overflow.  One approach is to increase the sample size at each addition.  This is shown in the diagram at the right.  This approach maintains the greatest fidelity through the system at the expense of consuming more FPGA fabric.  Another approach is to use //saturating arithmetic// Saturating addition prevent overflow by clipping the output to a maximum value.  For example, in 8 bits the value of ''FF+1'' would not be zero but remain at ''FF''.+
  
-Another consideration is the meaning of gain.  If the values in the wave tables represent the smallest signal then gain means amplification.  It the value represent the loudest samples possible then gain means attenuation and the gain block is actually subtraction. 
  
-    + 
 +/* This block comment has the Octave source code to generate the above tables. 
 +% Generate a log plot that shows a typical audio taper 
 + x = 1:1:100;% Generate 100 points on an log curve and map the gain 
 +% to setting in pwm - nonlinear DAC scheme.  This gives 
 +% the actual table to use in the API driver modules. 
 +% Manually add {0,0,0,0} and {15,15,15,15}. 
 + 
 +% Get the target gains 
 +x = 1:1:100; 
 +out = exp((5 .* x) ./ 100) ./ 150; 
 +target_idx = 1; 
 +% loop past all possible gains recording the first to pass out(target_idx) 
 +idx = 0; 
 + for i3 = 0:15; 
 +  for i2 = 0:15; 
 +   for i1 = 0:15; 
 +    for i0 = 0:15; 
 +     idx = idx +1; 
 +     outval = ((i3 .* .73203) + (i2 .* .19608) + (i1 .* 0.05229) + (i0 .* 0.01307)) ./ 16; 
 +     if outval > out(target_idx), 
 +         %printf("%d %f {%d, %d, %d, %d},\n", target_idx, outval, i3, i2, i1, i0); 
 +         printf("{%d, %d, %d, %d},\n", i3, i2, i1, i0); 
 +         target_idx = target_idx + 1; 
 +     end 
 +    end 
 +   end 
 +  end 
 + end 
 + 
 + out = exp((5 .* x) ./ 100) ./ 150; 
 + plot(x, out); 
 + title("Audio Taper", 'fontsize', 24); 
 + xlabel("Gain Setting", 'fontsize', 18); 
 + ylabel("Gain", 'fontsize', 18); 
 + set([gca; findall(gca, 'Type','line')], 'linewidth', 4); 
 + grid on; 
 + print -dpng '/tmp/audiotaper.png'; 
 + 
 +% Show a plot of linear gain 
 +x=0:1:15; 
 +out = x ./ 16; 
 +plot(x, out); 
 +title("Linear Attenuation",'fontsize', 24); 
 +xlabel("Gain Setting", 'fontsize', 18); 
 +ylabel("Gain", 'fontsize', 18); 
 +set([gca; findall(gca, 'Type','line')], 'linewidth', 4); 
 +grid on; 
 +print -dpng '/tmp/linear-x.png'; 
 + 
 +% Show a plot of a reciprocal gain function 
 +x = 0:1:15; 
 +out = 1 ./ ( 16 .- x); 
 +plot(x, out); 
 +title("1/x Attenuation",'fontsize', 24); 
 +xlabel("Gain Setting", 'fontsize', 18); 
 +ylabel("Gain", 'fontsize', 18); 
 +set([gca; findall(gca, 'Type','line')], 'linewidth', 4); 
 +grid on; 
 +print -dpng '/tmp/one-over-x.png'; 
 + 
 +% Show the output for PDM with the rate set to one-third 
 +for x=1:1:32; 
 +  if mod(x,3) == 0, out(x) = 1; 
 +  else out(x) = 0; 
 +  end 
 +end 
 +subplot(4,1,1), bar(out); 
 +axis off 
 + 
 +% Generate the possible unique gain settings for four bit 2R-R network  
 +% where each bit has a four bit PWM control 
 +idx = 0; 
 + for i3 = 0:15; 
 +  for i2 = 0:15; 
 +   for i1 = 0:15; 
 +    for i0 = 0:15; 
 +     idx = idx +1; 
 +     outval =(i3 .* .73203) + (i2 .* .19608) + (i1 .* 0.05229) + (i0 .* 0.01307); 
 +     %outval =(i3 .* .5) + (i2 .* .25) + (i1 .* 0.125) + (i0 .* 0.0625); 
 +     out(idx, :) = [ outval / 16 , i3, i2, i1, i0 ]; 
 +    end 
 +   end 
 +  end 
 + end 
 + sortout = sort(out); 
 + uniqout = unique(sortout); 
 + length(uniqout) 
 + length(out) 
 + uniqout(2) 
 + uniqout(length(uniqout)) 
 +% Generate a plot of the gain of a four bit 2R-R DAC circuit. 
 +for i3 = 0:1 
 +  for i2 = 0:1 
 +    for i1 = 0:1 
 +      for i0 = 0:1 
 +        idx = (8 .* i3) + (4 .* i2) + (2 .* i1) + (1 .* i0) + 1 
 +        out(idx) = (i3 .* .73203) + (i2 .* 0.19608) + (i1 .* 0.05229) + (i0 .* 0.01307) 
 +      end 
 +    end 
 +  end 
 +end 
 +plot(out); 
 +title("2R-R Response",'fontsize', 24); 
 +xlabel("Input Code", 'fontsize', 18); 
 +ylabel("Vout", 'fontsize', 18); 
 +set([gca; findall(gca, 'Type','line')], 'linewidth', 4); 
 +grid on; 
 +print -dpng '/tmp/r2-2.png'; 
 + 
 +*/ 
  
tonegen_design.1647301608.txt.gz · Last modified: 2022/03/14 23:46 by dpisuperadmin