User:Ashmanskas/p364/lab 12
From LaPET electronics
< User:Ashmanskas | p364
Contents |
Lab 12
Part 1: Build 6-bit R-2R DAC
- Start from the files you used in lab 9, so that you have the right pin assignments:
- http://positron.hep.upenn.edu/wja/p364/lab9/
- Don't forget to include the .ucf file in your design!
- Don't forget to choose the right FPGA: family=Coolrunner 2, device=XC2C64A, package=VQ44, speed=-7
- Implement a 6-bit counter and send the counter value to six output pins. For instance
/* * lab12.v * Skeletal program for Physics 364, Lab 12, fall 2010 */ `timescale 1ns / 1ps `default_nettype none module lab12 ( input wire pin01, pin02, pin03, pin04, input wire pin09, pin10, pin11, pin12, pin13, input wire pin14, pin15, pin16, pin17, pin18, output wire pin22, pin23, pin24, pin25, pin26, output wire pin27, pin28, pin29, pin30, pin31, output wire pin32, pin33, pin34, pin35, pin36, output wire pin37, pin38, pin39, pin40 ); wire clk = pin01; wire [5:0] count; dffe_Nbit #(6) count_ff (.clk(clk), .ena(1), .q(count), .d(count+1)); assign {pin40,pin39,pin38,pin37,pin36,pin35} = count; endmodule
- You will need the dffe_Nbit module:
module dffe_Nbit #(parameter N=1) ( input wire clk, input wire ena, input wire [N-1:0] d, output wire [N-1:0] q ); reg [N-1:0] qreg=0; always @ (posedge clk) begin if (ena) qreg <= d; end assign q = qreg; endmodule
- Here is lab9.ucf if you prefer to cut/paste it into a new project:
# lab9.ucf -- mapping Verilog names to FPGA pins NET "pin01" LOC="12" | IOSTANDARD=LVTTL ; NET "pin02" LOC="13" | IOSTANDARD=LVTTL ; NET "pin03" LOC="14" | IOSTANDARD=LVTTL ; NET "pin04" LOC="16" | IOSTANDARD=LVTTL ; NET "pin09" LOC="18" | IOSTANDARD=LVTTL ; NET "pin10" LOC="19" | IOSTANDARD=LVTTL ; NET "pin11" LOC="20" | IOSTANDARD=LVTTL ; NET "pin12" LOC="21" | IOSTANDARD=LVTTL ; NET "pin13" LOC="22" | IOSTANDARD=LVTTL ; NET "pin14" LOC="23" | IOSTANDARD=LVTTL ; NET "pin15" LOC="27" | IOSTANDARD=LVTTL ; NET "pin16" LOC="28" | IOSTANDARD=LVTTL ; NET "pin17" LOC="29" | IOSTANDARD=LVTTL ; NET "pin18" LOC="30" | IOSTANDARD=LVTTL ; NET "pin22" LOC="31" | IOSTANDARD=LVTTL ; NET "pin23" LOC="32" | IOSTANDARD=LVTTL ; NET "pin24" LOC="33" | IOSTANDARD=LVTTL ; NET "pin25" LOC="34" | IOSTANDARD=LVTTL ; NET "pin26" LOC="36" | IOSTANDARD=LVTTL ; NET "pin27" LOC="37" | IOSTANDARD=LVTTL ; NET "pin28" LOC="38" | IOSTANDARD=LVTTL ; NET "pin29" LOC="39" | IOSTANDARD=LVTTL ; NET "pin30" LOC="40" | IOSTANDARD=LVTTL ; NET "pin31" LOC="41" | IOSTANDARD=LVTTL ; NET "pin32" LOC="42" | IOSTANDARD=LVTTL ; NET "pin33" LOC="43" | IOSTANDARD=LVTTL ; NET "pin34" LOC="44" | IOSTANDARD=LVTTL ; NET "pin35" LOC="1" | IOSTANDARD=LVTTL ; NET "pin36" LOC="2" | IOSTANDARD=LVTTL ; NET "pin37" LOC="3" | IOSTANDARD=LVTTL ; NET "pin38" LOC="5" | IOSTANDARD=LVTTL ; NET "pin39" LOC="6" | IOSTANDARD=LVTTL ; NET "pin40" LOC="8" | IOSTANDARD=LVTTL ;
- Initially use the function generator to supply the clock. Remember to check with the oscilloscope that the clock is never below 0V or above +3.3V before connecting it to the FPGA. The clock frequency I have in mind for Part 2 is 68 kHz, so you might as well start there.
- Build the R-2R DAC whose schematic diagram is in the lecture notes, using the 6 counter outputs to drive the 6 inputs of the resistor ladder. Though it is not essential for this part, use a FET-input opamp instead of a 741, so that you have it on hand for possible use later.
- Once the DAC is working, you should see a 1 kHz sawtooth waveform at the opamp output.
- Now use a 555 timer IC ( http://en.wikipedia.org/wiki/555_timer_IC ) to provide the (approximately) 68 kHz clock, so that you can free up your function generator for more interesting uses. I think R1=1K, R2=10K, C=1nF should get you reasonably close to ~ 70 kHz. http://en.wikipedia.org/wiki/File:555_Astable_Diagram.svg
- If your 555 works with Vcc=3.3V, that's ideal. If it doesn't, then you'll need to power it from +5V and then use a voltage divider to keep its output between 0V and +3.3V. Remember to power the FPGA only from +3.3V!
- Check that your counter+DAC circuit still works with the 555 clock.
- A 555 timer IC turns out to be a handy thing to know how to use for all sorts of simple timing applications.
- Now modify your counter so that the DAC outputs a triangle wave instead of a sawtooth, as sketched on page 7 of the lecture notes. This involves changing your 6-bit counter to a 7-bit counter and using bit 6 to switch between counting up and counting down.
- If you reach this point by 6pm on Monday, then you will enjoy this final section of Part 1. Otherwise, skip it to save time.
- As sketched on page 8 of the lecture notes, replace the counter with a 10-bit "phase accumulator," which increments each clock tick by an amount that is programmed by six DIP switches (called 'freq[5:0]' below).
- Try varying the value of freq[3:0] with the switches and watch the triangle wave's frequency vary. If you like, try playing the result into the amplified speaker for some audible frequencies.
- I haven't tested the code snippet below yet, but this (or something very similar) ought to work:
module lab12_synthesizer ( input wire pin01, pin02, pin03, pin04, input wire pin09, pin10, pin11, pin12, pin13, input wire pin14, pin15, pin16, pin17, pin18, output wire pin22, pin23, pin24, pin25, pin26, output wire pin27, pin28, pin29, pin30, pin31, output wire pin32, pin33, pin34, pin35, pin36, output wire pin37, pin38, pin39, pin40 ); wire clk = pin01; wire [5:0] freq = {pin02,pin03,pin04,pin09,pin10,pin11}; wire [9:0] accum; dffe_Nbit #(10) accumff (.clk(clk), .ena(1), .q(accum), .d(accum+freq)); wire [6:0] count = accum[9:3]; wire dac[5:0] = count[6] ? (63-count[5:0]) : count[5:0]; assign {pin40,pin39,pin38,pin37,pin36,pin35} = dac; endmodule;
Part 2: Build 6-bit Wilkinson ADC, sampling at 1 kHz
- Starting from your existing DAC circuit, build the single-slope ADC sketched out on page 10 of the lecture notes.
- You will need a DG403 analog switch IC, which you used in Lab 7.
- You will also need a 311 comparator IC, which you used in Lab 4.
- Use a large enough capacitor that the ~ 150nA input current of the 311 will not affect the voltage held on the capacitor appreciably during the 1ms interval between samples.
- Arrange that the comparator's two output values are 0V and +3.3V and check that it outputs +3.3V when Vin>DAC and 0V when Vin<DAC.
- You will probably find this to be a good starting point for the ADC FPGA code. You have to match it up to the right I/O pins, etc.
/* * Implement single-slope ADC */ module adc ( input wire clk, // 68 kHz clock, for 1 kHz sample rate input wire cmp, // 1 if sample > DAC value, else 0 output wire [5:0] dac, // DAC used for comparing with sample output wire [5:0] adc, // ADC output value output wire conv_done, // indicate next ADC value is ready output wire sample_hold // closes FET switch for sample & hold ); // modulo-68 counter: ramp 0..63, then allow 4 extra ticks for processing wire [6:0] count; wire [6:0] count_next = (count==67) ? 0 : count+1; dffe_Nbit #(7) count_ff (.clk(clk), .ena(1), .d(count_next), .q(count)); // ramp the DAC output during the 0..63 phase of counter assign dac = count[6] ? 0 : count[5:0]; // new ADC value will be the largest DAC value for which sample>DAC wire [5:0] newadc; dffe_Nbit #(6) newadc_ff (.clk(clk), .ena(cmp), .d(dac), .q(newadc)); // when 0..63 ramp is complete, copy newadc into ADC output register dffe_Nbit #(6) adc_ff (.clk(clk), .ena(count==64), .d(newadc), .q(adc)); // when ADC output register has updated, strobe "conversion done" signal assign conv_done = (count==65); // before new ramp begins, tell sample & hold to get new sample assign sample_hold = (count==66); endmodule
- Check the ADC's output by sending it to six LEDs, and slowly varying Vin. Check the Vin range needed to generate the full range of ADC output values. (It should be 0V to +3.3V.)
- If you are doing really well on time, build a second DAC to display the ADC output, so that you can look at both the ADC input and the ADC output on the oscilloscope. If you are not doing really well on time, then just use an opamp follower (using the FET-input opamp you have on hand) to allow you to observe the voltage on the sample & hold capacitor without draining its charge. Then look at Vin and Vcapacitor on two oscilloscope channels.
- What you do in this section will depend on which fork in the road you took just above. The first scope input will be Vin. The second will be either the output of the second DAC or the output of the opamp that is looking at the S&H capacitor.
- Drive Vin from the function generator and vary the frequency of a sine wave. Observe that up until f(nyquist), the sampled waveform tracks the input waveform in frequency. Now notice that as you continue to increase the frequency of Vin, the sampled waveform actually decreases in frequency. This is aliasing.
Part 3: Re-program FPGA to make successive approximation ADC, sampling at ~ 7kHz
- Replace the linear-slope FPGA code with this:
/* * Implement successive-approximation ADC */ module adc ( input wire clk, // 68 kHz clock, for ~ 7 kHz sampling input wire cmp, // 1 if sample > DAC value, else 0 output wire [5:0] dac, // DAC used for comparing with sample output wire [5:0] adc, // ADC output value output wire conv_done, // indicate next ADC value is ready output wire sample_hold // closes FET switch for sample & hold ); // modulo-10 counter: ramp 0..5, then allow 4 extra ticks for processing wire [3:0] count; wire [3:0] count_next = (count==9) ? 0 : count+1; dffe_Nbit #(4) count_ff (.clk(clk), .ena(1), .d(count_next), .q(count)); // Update DAC bits: // on tick 0,1,2,3,4,5, bit 5,4,3,2,1,0 is set to 1; // on tick 1,2,3,4,5,6, bit 5,4,3,2,1,0 is set to comparison outcome // on tick 7, all bits are set to 0 for new cycle wire [5:0] dac_next; dffe_Nbit #(6) dac_ff (.clk(clk), .ena(1), .d(dac_next), .q(dac)); assign dac_next[5] = (count==0) ? 1 : (count==1) ? cmp : (count==7) ? 0 : dac[5]; assign dac_next[4] = (count==1) ? 1 : (count==2) ? cmp : (count==7) ? 0 : dac[4]; assign dac_next[3] = (count==2) ? 1 : (count==3) ? cmp : (count==7) ? 0 : dac[3]; assign dac_next[2] = (count==3) ? 1 : (count==4) ? cmp : (count==7) ? 0 : dac[2]; assign dac_next[1] = (count==4) ? 1 : (count==5) ? cmp : (count==7) ? 0 : dac[1]; assign dac_next[0] = (count==5) ? 1 : (count==6) ? cmp : (count==7) ? 0 : dac[0]; // ... dffe_Nbit #(6) adc_ff (.clk(clk), .ena(count==7), .d(dac), .q(adc)); // when ADC output register has updated, strobe "conversion done" signal assign conv_done = (count==8); // before new conversion begins, tell sample & hold to get new sample assign sample_hold = (count==9); endmodule
- By varying Vin slowly, check -- as you did in Part 2 -- that the ADC is in fact converting and check the range of ADC output values vs. the range of Vin values.
- By triggering the oscilloscope at the start of each ADC conversion cycle, watch the binary search performed by the DAC output as the ADC finds each successive bit.