Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Spartan - 3A ADC and DAC interface not working

Status
Not open for further replies.

NJ176

Newbie level 5
Joined
Jul 18, 2017
Messages
9
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
283
Hello!

I am working on ADC and DAC interface for Spartan - 3A board and I have checked the output of the amplifier which works. But I am having issues with ADC and DAC. I am displaying the 8 MSBs of the ADC on the LEDs, but all the LEDs are always lit.

I had set the clock the frequency to 1.5MHz from 1MHz and finally 1KHz, yet there is no change in the output. The code is given below. Can someone help me figure out the problem?


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
 
entity ADC is
port    (   enable              : in std_logic;     --switch enable for start preamp and adc
            clk_div             : in std_logic;
            start_AD_CONV       : in std_logic;     --switch enable for start adc
            SPI_MISO                : in std_logic;
            AD_CONV             : out std_logic;    --SPI-bus for ADC
            ADC1                    : inout std_logic_vector(13 downto 0); --VINA
            ADC2                    : out std_logic_vector(13 downto 0); --VINB
            AMP_CS              : out std_logic;    --SPI-bus for preamp
            SPI_MOSI                : out std_logic;
            SPI_SCK             : out std_logic;
            DAC_CS              : out std_logic;
            AMP_SHDN                : out std_logic;    --preamp
            DAC_CLR             : out std_logic     --dac
        );
end ADC;
 
architecture Behavioral of ADC is
 
    type state_type is (amp_state, adc_state, dac_state);
    type amp_state_type is (IDLE, START, HI, LO);
    type adc_state_type is (IDLE_AD, START_AD,LO_AD,HI_AD,FINE_AD);
    type dac_state_type is (da_idle, da_send, da_sck1, da_loop, da_end);
    type state_type_clock is (clock_on, clock_off);
    signal state: state_type := amp_state;
    signal amp_state_reg: amp_state_type := IDLE;
    signal adc_state_reg: adc_state_type := IDLE_AD;
    signal dac_state_reg: dac_state_type := da_idle;
    signal state_clock : state_type_clock := clock_off;             -- Clock state set to clock_off
    signal sck_reg: std_logic:='0';
    signal da_data_reg: std_logic_vector (31 downto 0) := "00000000001100000000000000000000"; -- signal for the DAC
    signal da_bit_reg : natural range 0 to 32;
    signal ADC_COPY: std_logic_vector (13 downto 0) := (others => '0');         -- a copy of the ADC-value to use for the DAC
    signal clk_sample : STD_LOGIC := '0';                                                   -- Clock Sample is initalized to zero
    signal counter1 : integer range 0 to 34;                            -- Counter up to 34
    signal risingedge : std_logic := '1';                               -- For clock of rising edge
    signal count1:integer:=1;
    signal tmp : std_logic := '0';
    
 
begin
 
-- 1.5MHz clock
     process(clk_div, enable) 
        begin
        if(enable='0') then 
        count1<=1;
        tmp<='0';
        elsif(clk_div'event and clk_div='1') then
        count1<=count1+1;
        if (count1= 25000) then                                 -- 1KHz frequency  --25 for 1MHz
        tmp <= NOT tmp;
        count1<= 1;
        end if;
        end if;
    end process;
    
    sclk_clock : process(clk_div)                                       -- Process for state of clock
        begin
            if(rising_edge(clk_div)) then                                   -- When rising edge, clock state is set to on
            case state_clock is
                when clock_on =>                                            -- When clock is on, set SPI_SCK = clk_sample
                SPI_SCK <= tmp;
                when clock_off =>                                           -- When clock is off, set SPI_SCK = 0
                SPI_SCK <= '0';
                end case;
            end if;
        end process;
 
--START PROCESS PREAMP - ADC - DAC
process(clk_div)
    variable count: integer range 0 to 16;  -- 14 bits for 1 ADC channel+2 for zero
    variable counter : integer range 0 to 34; -- 34 SPI_SCK clockchanges for an entire ADC-loop
    variable gaincount: integer range 0 to 7;   -- 8 bits preampsignal
    constant gain_temp: std_logic_vector (7 downto 0):= "00010001"; -- biggest range of preamp
begin   
    if clk_div'event and clk_div ='1' then
        case state is
        --START AMP CYCLE
            when amp_state =>
                DAC_CS <= '1';      --non active SPI-bus for DAC
                case amp_state_reg is
                    when IDLE =>
                        gaincount:=7;
                        if enable = '1' then
                            amp_state_reg <= START;
                        else
                            AMP_SHDN <= '1';
                            AMP_CS <= '1';
                            amp_state_reg <= IDLE;
                        end if;
                        
                    when START =>
                        AMP_SHDN <= '0';
                        state_clock <= clock_off;
                        SPI_MOSI <= '0';
                        counter := 0;
                        AMP_CS <= '0';
                        amp_state_reg <= HI;
                        
                    when HI =>
                        state_clock <= clock_on;
                        counter := counter+1;                -- The counter was set to 0    
                        SPI_MOSI <= gain_temp(gaincount); -- gaincount is set to be 7
                        amp_state_reg <= LO;
                        
                    when LO =>
                        if counter = 8 then                     -- When 8 bit transferred
                            AMP_SHDN <= '1';                        -- Shut the amp 
                            AMP_state_reg <= IDLE;              -- Go to IDLE state
                            SPI_MOSI <= '0';                        -- Set all the outgoing data to zero
                            AMP_CS <= '1';                          -- Basically, do the tasks assigned by the state IDLE
                            gaincount := 7;
                            state_clock <= clock_off;
                            state <= adc_state;                 -- When done communication, go to ADC
                        else
                            state_clock <= clock_off;           -- If counter less than 8 
                            gaincount := gaincount-1;           -- Decrease the gain count (from 7 to 6 and so on)
                            amp_state_reg <= HI;                    -- Go to HI to do the serial transfer
                        end if;
                        
                    when others =>                                  -- Any other situation - Go to IDLE
                        amp_state_reg <= IDLE;
                        
                end case;
                
            --START ADC CYCLE
            when adc_state =>
                DAC_CS <= '1';
                case adc_state_reg is
                    when IDLE_AD =>
                        counter := 0;
                        state_clock <= clock_off;
                        AD_CONV <= '0';
                        if enable = '0' then
                            state <= amp_state;
                        else
                            if start_AD_CONV = '1' then
                                AD_CONV <= '1';
                                adc_state_reg <= START_AD;
                            else
                                adc_state_reg <= IDLE_AD;
                            end if;
                        end if;
                        
                    when START_AD =>
                        state_clock <= clock_off;
                        AD_CONV <= '0' after 5 ns;
                        count := 16;
                        adc_state_reg <=LO_AD;
                        
                    when LO_AD =>
                        state_clock <= clock_off;
                        if count > 0 then                   -- Count is initialized to 16
                            count := count-1;
                        end if;
                        counter := counter+1;           -- Counter was set to 0
                        adc_state_reg <= HI_AD;
                        
                    when HI_AD =>
                        state_clock <= clock_on;
                        if counter <= 2 then
                            adc_state_reg <= LO_AD;
                        elsif counter > 2 and counter <= 16 then
                            ADC1(count) <= SPI_MISO;
                            adc_state_reg <= LO_AD;
                        elsif counter > 16 and counter <= 18 then
                            count:=14;
                            adc_state_reg <= LO_AD;
                        elsif counter > 18 and counter <=32 then
                            ADC2(count) <= SPI_MISO;
                            adc_state_reg <= LO_AD;
                        elsif counter = 33 then
                            adc_state_reg <= LO_AD;
                        else
                            adc_state_reg <= FINE_AD;
                        end if;
                        
                    when FINE_AD =>
                        counter := 0;
                        count := 16;
                        state_clock <= clock_off;
                        ADC_COPY <= ADC1;
                        AD_CONV <= '0';
                        adc_state_reg <= IDLE_AD;
                        state <= dac_state;
                        
                    when others =>
                        adc_state_reg <= IDLE_AD;
                        
                end case;
                
            --START DAC CYCLE
            when dac_state =>
                case dac_state_reg is
                    when da_idle =>
                        AMP_CS <= '1';
                        AD_CONV <= '0';
                        DAC_CS <= '0';
                        DAC_CLR <= '0';
                        state_clock <= clock_off;
                        da_bit_reg <= 32;
                        da_data_reg <= "00000000"&"0011"&"0000"&ADC_COPY(13 downto 2)&"0000";
                        dac_state_reg <= da_send;
                        
                    when da_send =>
                        DAC_CLR<='1';
                        SPI_MOSI <= da_data_reg(da_bit_reg);  -- da_bit_reg <= 32;
                        dac_state_reg <= da_sck1;
                        
                    when da_sck1 =>
                        state_clock <= clock_on;
                        da_bit_reg <= da_bit_reg-1;             -- Reduce the da_bit_reg by 1 every time the state
                        dac_state_reg <= da_loop;
                    
                    when da_loop =>
                        state_clock <= clock_off;
                        if da_bit_reg > 0 then
                            dac_state_reg <= da_send;
                        else
                            dac_state_reg <= da_end;
                        end if;
                        
                    when da_end =>
                        dac_cs <= '1';
                        state_clock <= clock_off;
                        dac_state_reg <= da_idle;
                        state <= amp_state;
                    
                    when others =>
                        dac_state_reg <= da_idle;
                        
                end case;
                
            when others =>
                state <= amp_state;
        end case;
    end if;
end process;
 
--SPI_SCK <= sck_reg;
end Behavioral;

 

Hi,

Let´s focus on the ADC first.
You need to provide a schematic of the circuit: supply, FPGA, ADC, amplifier, analog input
You need to provide a scope pictur of a complete data frame. From CS activate to CS inactivate of CS, SCK, MOSI, MISO (if used)

Klaus
 

Thank you for your reply!

For the supply, I am supplying from a function generator a voltage of around 0.5-0.8V for test. The Amplifier and the ADC are connected as shown in the datasheet, page 77: https://www.xilinx.com/support/documentation/boards_and_kits/ug330.pdf

I have checked the working of the amplifier by using an oscilloscope probe directly at the chip output.
The schematic and the timing for ADC is given on page 82 of the same datasheet.

Thanks in advance.
 

Hi,



you are using the starter kit? Not one build on oyur own?
Then the schematic should be OK.

I meant power supply. But this is on the starter kit and should be OK.

But the timing: do you use a ready to use IP, or you build it on your own?
If your own: Then the datasheet timing only tells how it should be, but it doesn´t tell your real timing. --> scope picture.

0.5..0.8V input signal.
For sure with this variable input .. then outbut will be variable, too. Not useful to debug with LEDs.
--> use a known, clean DC source

From the formula it seems that your input range is: 0.4V (-8192 LSB) ...1.65V (0 LSB) ... 2.9V (+8191 LSB).
(BTW: 0.5V..0.8V gives -7537 ... -5571, in binary: 10 xxxx xxxx xxxx, means only the upper two bits are stable.)

Klaus
 

Hello! Thanks for your reply, again!

Yes, I am using the starter kit with reference to the timings given in the datasheet.

I am using a DC supply with a constant DC output and I have checked multiply values by changing the values. However, the problem remains that all of the LEDs are either all lit together, or just the MSB is off and the rest all lit, whenever I toggle the start_AD_CONV switch.

Since the gain is -1, the voltages between 0.4-1.65V should be positive and voltage sin the range of 1.65-2.9V should be negative. However, for both the ranges I am getting a similar output wherein, either all the LEDs are lit or all are lit and only the MSB varies or that all are off when I toggle start_AD_CONV switch.

I don't quite get what you mean by this:
"But the timing: do you use a ready to use IP, or you build it on your own?
If your own: Then the datasheet timing only tells how it should be, but it doesn´t tell your real timing. --> scope picture."
 

Hi,

If you wrote the code on your own, then we need a scope picture.

Klaus

- - - Updated - - -

added

AD_CONV <= '0' after 5 ns;
I dont think it´s a good idea to use some fixed timing here.
I recommend to run a simple state machine with fixed SCK clock frequency. It runs:
0 (idle, SPI_CLK = 0),
1 (AD_Conv = 1, SPI_CLK=0), (ensures 4ns min and 3ns min timing)
2 to 3 (delay),
4 to 17 (ch 0 data)
18 to 19 (delay),
20 to 33 (ch 1 data)
34 to 36 (delay), (ensures 45ns timing)
then stops with 0
...and can be activated anew

mind: data input = on falling SCK edge.

Klaus
 

Line 181: AD_CONV <= '0' after 5 ns;
Don't think it's your problem, but that's not going to synthesize a delay.

You might also look at your use of variables. A quick scan through and I can't see any place where you need the new value that was assigned immediately. That is probably a habit you want to break. Again, that's not causing you a problem here.
 

DAC Code for Spartan 3A

Hello!

I am trying to interface the DAC of the Spartan-3A board with the FPGA. I have written a VHDL program for the same but it doesn't seem to be working. Can anyone please help me out?

I have used a logic analyzer to display the signals:

Channel 0 : DAC_CLR1
Channel 1 : DAC_CS1
Channel 2 : DAC_OUT1
Channel 3 : SPI_MOSI1
Channel 4 : SPI_SCK1

Can someone please tell me what I am doing wrong as I am not able to figure out. The signal DAC_CS amd DAC_CLR should vary once in the whole cycle, but they don't :/


Code VHDL - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
 
entity DAC is
    Port ( DAC_CS : inout  STD_LOGIC;
           SPI_MOSI : inout  STD_LOGIC;
           SPI_SCK : inout  STD_LOGIC;
              Clk : in STD_LOGIC;
              Enable : in STD_LOGIC;
              Reset : in STD_LOGIC;
           DAC_CLR : inout  STD_LOGIC;
              DAC_OUT : in STD_LOGIC;
              DAC_CS1 : out STD_LOGIC;
              SPI_MOSI1: out STD_LOGIC;
              SPI_SCK1: out STD_LOGIC;
              DAC_CLR1: out STD_LOGIC;
              DAC_OUT1: out STD_LOGIC;
              DEBUG : out STD_LOGIC_VECTOR (1 downto 0)
              );
end DAC;
 
architecture Behavioral of DAC is
 
type state_type_clock is (clock_on, clock_off);
signal state_clock : state_type_clock := clock_on;          -- Clock state set to clock_on
type dac_state_type is (da_idle, dac_send, da_end);
signal dac_state_reg: dac_state_type := da_idle;
signal da_data_reg: std_logic_vector (31 downto 0) := "00000000001100000000000000000000"; -- signal for the DAC
signal count : natural range 0 to 32;
signal ADC_COPY: std_logic_vector (13 downto 0) := "1000"&"0000"&"000000";          -- a copy of the ADC-value to use for the DAC
signal cnt: integer:=1;
signal tmp : std_logic := '0';
 
 
begin
 
DAC_CS1 <= DAC_CS;
DAC_CLR1 <= DAC_CLR;
DAC_OUT1 <= DAC_OUT;
SPI_SCK1 <= SPI_SCK;
SPI_MOSI1 <= SPI_MOSI;
 
 
process(clk, enable)
begin
    if(enable='0') then
    cnt<=1;
    tmp<='0';
    elsif(clk'event and clk='1') then
    cnt <=cnt+1;
    if (cnt = 25000) then                           -- 1 KHz Frequency
    tmp <= NOT tmp;
    cnt <= 1;
    end if;
    end if;
    SPI_SCK <= tmp;
end process;
 
 
process (tmp)
 
begin 
if tmp'event and tmp='1' then
 
            if (reset = '1') then 
            state_clock <= clock_off;
            DAC_CS <= '1';
            DAC_CLR <= '0';
            count <=32;
            da_data_reg <= "00000000"&"0011"&"0000"&"000000000000"&"0000";
            end if;
 
        case dac_state_reg is
                                
                when da_idle =>
                DEBUG <= "01";
                DAC_CS <= '0';
                DAC_CLR <= '1';
                state_clock <= clock_off;
                count <= 32;
                da_data_reg <= "00000000"&"0011"&"0000"&ADC_COPY(13 downto 2)&"0000";
                dac_state_reg <= dac_send;
                
                when dac_send =>
                DEBUG <= "10";
                    state_clock <= clock_on;
                        if count = 0 then
                        count <= 32;
                        else
                        if count >0 then 
                        count <= count - 1;
                        end if;
                    SPI_MOSI <= da_data_reg(count);
                        if count > 0 then
                                dac_state_reg <= dac_send;
                        else
                                dac_state_reg <= da_end;
                        end if;
                        end if;
                
                when da_end =>
                DEBUG <= "11";
                DAC_CS <= '1';
                state_clock <= clock_off;
                DAC_CLR <= '0';
                dac_state_reg <= da_idle;
                
                when others =>
                dac_state_reg <= da_idle;
                    
                        
                end case;
                end if;
                end process; 
end Behavioral;
 
 
[ATTACH=CONFIG]140729._xfImport[/ATTACH]

 

You should not write VHDL thinking like a software programmer...

This:
Code:
SPI_MOSI <= da_data_reg(count);
is how a software programmer thinks of a shift register. Hardware engineers think of VHDL (VHISIC Hardware Description Language) hardware shift registers as:
Code:
da_data_reg <= da_data_reg(30 downto 0) & "0"; -- MSB first

-- after the process assign the SPI_MOSI as the MSB
SPI_MOSI <= da_data_reg(31);
That creates a shift register instead of a register with a huge multiplexer on the output.

You also have an issue with the indexing as you count from 32 down to 0. There is no index #32 in the da_data_reg, what do you expect the synthesis to implement for that?

You should also run a simulations in modelsim or Vivado instead of "simulating" your design via synthesis-implementation-download-hook_up_logic_analyzer).

Also the reason you never get the DAC_CS going high is because it never reaches the state da_end, because you have logical issues with the following code.

Code:
                    state_clock <= clock_on;
                    if count = 0 then
                        count <= 31;
                    else
                        if count >0 then 
                            count <= count - 1;
                        end if;
                        SPI_MOSI <= da_data_reg(count);
                        if count > 0 then
                            dac_state_reg <= dac_send;
                        else
                            dac_state_reg <= da_end;
                        end if;
                    end if;
Once count is 0 it never reaches the else condition where you set the state.

- - - Updated - - -

This is a good reason to stick to only a single signal assignment in any given if statement. I'd rather have a bunch of separate if statements with repeated comparisons than have logical faults like this.

Pull the dac_state_reg if-else outside of the other if-else.

- - - Updated - - -

Maybe you should look at a more professionally done SPI master...or at least some code that has documentation and comments in the code to explain what is being done and why.
https://eewiki.net/pages/viewpage.action?pageId=4096096
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top