Second Order Sigma Delta DACs implemented in a FPGA.


Recommended Posts

I played around with a 1st and 2nd order 12-bit Sigma Delta DAC implemented on the FPGA.

I found the results quite interesting, as the change is pretty simple to implement and the change to the noise on the output spectrum is quite significant, with lowered 2nd harmonic and a much smother noise floor.

VHDL code is on GitHub at https://github.com/hamsternz/second_order_sigma_delta

first_order.jpg

second_order.jpg

Link to post
Share on other sites

@hamster,

Impressive!

Would you mind sharing the hardware you chose to use?  The clock speed of the delta-sigma converter?  It also looks like you biased your sine wave negative by a half a sample.  Out of curiosity, was this on purpose or just the result of the way you truncated floats to integers when generating your table?

I'm a bit surprised by the second harmonic you show in your pictures.  I had rather thought this technique would've  done better than that.  Do you have any suggestions as to what might have caused that harmonic?

Either way, good fun, and thanks for sharing!

Dan

Link to post
Share on other sites

BASYS3 + PMOD Breadboard + Analog Discovery 2.

It was just a hack, so the table was a quick formula in a spreadsheet, yes, I assume the + and - sides are both rounding towards zero causing some asymmetry, but with 11 significant bits that should be somewherere about -60dB at a guess.

Most of the noise is just the shoddy physical implementation. Flying wires on a breadboard, on PMODs, just the shielded wires on the AD2 and so on.  If I leave a wire hanging around  it will pick up most the noise too, maybe 6dB lower than on the channel that is under measurement.

This was just a quick experiment, just using the 100MHz clock rate. If I use a slower clock (e.g. update only every 8 cycles so 12.5MHz) the noise floor actually drops a lot..

Also using the AD2 on a different laptop to the one programming/powering the FPGA removes a lot of noise too. I assume that this is due to voltage drops and noise on the USB cables.

Plenty of room for experimentation and improvement.

Link to post
Share on other sites

@Dan, I managed to find the 'average' option rather than 'decimate' which has stopped high frequency noise showing up in the spectrums, giving a more reasonable noise floor.

 

 

first.png

second.png

Link to post
Share on other sites
Posted (edited)

Measurement technique is certainly critical to successful analysis of how your hardware is performing. So is the selection of test equipment.

I see that your most recent plots show a high DC component. As Sigma-Delta Modulators aren't DC accurate is appears that your are trying to do measurements using direct coupling. An AC coupled load would seem to me to be more appropriate. This  raises the topic of source and load impedance.

Edited by zygot
Link to post
Share on other sites

I glanced at your code.

The lines like this one bother me: new_val := dac1_accum + sample - 2048;

I wonder how Vivado implemented this. Usually, in my experience a better way to do this is to pipeline so that each '+' operation is performed on a separate pipeline version of the signal. From a coding viewpoint it appears to be straightforward. From an implementation viewpoint it looks like you have in implied latch, at least. Regardless, you are trying to perform 2 add or one add and 1 subtract in a clock cycle. Even for a quick prototype exercise this isn't a good idea. Sometimes our HDL code gets written by the parts of our brain wired for C. It's dangerous.

Link to post
Share on other sites
7 hours ago, zygot said:

I glanced at your code.

The lines like this one bother me: new_val := dac1_accum + sample - 2048;

I wonder how Vivado implemented this. Usually, in my experience a better way to do this is to pipeline so that each '+' operation is performed on a separate pipeline version of the signal. From a coding viewpoint it appears to be straightforward. From an implementation viewpoint it looks like you have in implied latch, at least. Regardless, you are trying to perform 2 add or one add and 1 subtract in a clock cycle. Even for a quick prototype exercise this isn't a good idea. Sometimes our HDL code gets written by the parts of our brain wired for C. It's dangerous.

I can't see the latching issues, but agree that if the +/-2048 was a separate signal, then the code could be simplified quite a bit... however, the optimizer should be doing that at the moment. "Premature optimization is the root of all evil" and so on.

One other finer point. As currently written +full scale value will generate a stream of all ones output but a -full scale won't generate all zero outputs, but a zero value gives a perfect 50:50 mix of ones and zeros.

Others might need it that -full scale gives all zeros, and +full scale gives all ones, but a zero value will give slightly more zeros than ones.

 

Link to post
Share on other sites
50 minutes ago, hamster said:

but a zero value gives a perfect 50:50 mix of ones and zeros.

Mull over your sine LUT. BTW, why did you replicate 2 cycles instead of just using 1 in your LUT?

 

Link to post
Share on other sites
26 minutes ago, zygot said:

Mull over your sine LUT. BTW, why did you replicate 2 cycles instead of just using 1 in your LUT?

 

Because I am quite happy to use a whole block of RAM rather than debugging indexing and sign-flipping code... :D It's actually one full cycle (half positive, half negative). It is jsut a column from a Google docs spreadsheet https://docs.google.com/spreadsheets/d/13srKHRNCD2dfbMglMvvCUESHR23kHWlzAJ24MJ_erzc/edit?usp=sharing

I could have also got away with just one quadrant, but then it would be more 'active' code on what I'm not interested in playing with

Link to post
Share on other sites
Posted (edited)

Hi,

for a DA converter hack, you could also have a look at the one I used here:

There was quite a bit of discussion about reconstruction filtering :). The shown measurements are taken using the on-board XADC, which acts as lowpass filter. I think the first two plot labels are wrong (plot 1, 2 show a sine wave. Plot 3, 4 show an 8-tone test signal)

It's not sigma-delta but open-loop PWM with creative dithering.
The idea is

- I generate a PWM signal simply by comparing the output value MSBs against a periodic ramp (e.g. 4 bit)
- for the lower bits, use pseudorandom numbers

It modulates most of the quantization error up to higher frequencies, where it can be filtered by analog means.

Sigma-Delta is more sophisticated (this one might be 8..10 bit equivalent for an audio signal). The main advantage of the PWM scheme is that it works rail-to-rail, where sigma-delta does not.

Quote

// pseudorandom generation with sufficiently long period

module pn24(i_clk, o_out);
   input wire         i_clk;
   output reg [23:0]     o_out = 24'h1;
   
   always @(posedge i_clk) begin
      o_out <= o_out >> 1;
      o_out[23] <= o_out[0];
      o_out[22] <= o_out[23] ^ o_out[0];
      o_out[21] <= o_out[22] ^ o_out[0];
      o_out[16] <= o_out[17] ^ o_out[0];
   end   
endmodule

// construct PWM reference with
// - large periodic component (quantization error is modulated up to the repetition frequency and harmonics)
// - small random component (dithering to break up systematic quantization error)

  wire [23:0]     dither2;
   pn24 iDither(.i_clk(CLKDAC), .o_out(dither2));
   reg [23:0]     dither = 24'd0;   
   reg [4:0]     dacCount = 5'd0;
   reg [4:0]     dacCountA = 5'd0;
   reg [4:0]     dacCountB = 5'd0;
   reg [17:0]     pwm = 18'd0;


   always @(posedge CLKDAC) begin
      dither <= dither2;      
      dacCount <= dacCount + 5'd1;      
      dacCountA <= dacCount;      
      dacCountB <= dacCount ^ 5'b01010; // break the linear ramp
      pwm <= {dacCountA[3:0], dither[13:0]};  
   end

Missing above: "outputBitReg <= myDacVal >= pwm;

Note that it's feedforward-only so it should be clocked fairly high e.g. 250 MHz on Artix.
The code allows also a 5-bit ramp but I got best results using 4 bits (if in doubt, experiment - depends on the lowpass).

Edited by xc6lx45
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now