0
\$\begingroup\$

I'm making an ADC (in VHDL) for Spartan-3AN. Unfortunately I have to program my FPGA (program FPGA only) a random time before the programmable gain amplifier (Spartan 3AN User Guide page 73) works corectly... And when I program the flash, it never works correctly... I put my code at the end of this subject so you can look at it but my question is :

Does someone know the kind of problems that can make me have to program random times my FPGA to get the right code on it?

Here's my code :

library     IEEE;
use         IEEE.STD_LOGIC_1164.ALL;
entity adc_dac is
Port (  CLK_50MHZ   :   in      STD_LOGIC;
            SPI_MOSI    :   out     STD_LOGIC;                                                                                      
            AMP_CS      :   out     STD_LOGIC :='1';
            SPI_SCK         :   out     STD_LOGIC :='0';
            AMP_SHDN    :   out     STD_LOGIC :='0';
            AD_CONV         :   out     STD_LOGIC;
            ADC_OUT         :   in      STD_LOGIC;
            led             :   out     STD_LOGIC_vector (7 downto 0)
        );
end adc_dac;

architecture Behavioral of adc_dac is

type state_type is (    pga_load, 
                        idle, sendBitPGA, clockHighPGA,
                        catchADC, setADC
                        );
signal state : state_type:=pga_load;
-- Extra counter
signal cnt  : integer range 0 to 34 := 8;
signal cnt_SCK  : integer range 0 to 1 := 0;

-- Programmable Gain Amplifier
signal pga_data : std_logic_vector (7 downto 0) := "00010001";
signal counter_10MHZ: integer range 0 to 10 := 0;

-- Auxiliary signals ADC
signal Data : std_logic_vector (13 downto 0);
signal ADC_path: std_logic:='0';
signal output : std_logic_vector (13 downto 0) := "10000000000000";

begin

--**********************************
--**        STATE MACHINE         **
--**********************************

process (CLK_50MHZ)
begin
if (rising_edge(CLK_50MHZ)) then                                          
     case state is

                when idle =>                                     
                    if      ADC_path = '1' then
                            ADC_path <= '0'; 
                            AD_CONV <= '0';
                            cnt <= 34;
                            state <= setADC;                         
                    else
                            SPI_SCK <= '0';
                            AD_CONV <= '1';
                            ADC_path <= '1';
                            state <= idle;
                    end if;  

    --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    --^^       PGA interface          ^^
    --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^    

                when pga_load =>
                     SPI_SCK <= '0';
                     cnt <= 8;
                     AMP_SHDN <= '0';
                     pga_data <= "00010001";
                     AMP_CS <= '0'; 
                     state <= sendBitPGA;

                when sendBitPGA =>
                        if (counter_10MHZ = 5) then
                            if (cnt_SCK = 0) then
                                SPI_SCK <= '1';
                                cnt_SCK <= 1;
                                SPI_MOSI <= pga_data(7);
                                cnt <= cnt-1;   
                                state <= clockHighPGA;
                            elsif(cnt_SCK = 1) then
                                SPI_SCK <= '0';
                                cnt_SCK <= 0;
                            end if;
                            if (cnt = 0) then
                                AMP_CS <= '1';
                                state <= idle;
                            end if;
                    counter_10MHZ <= 0;
                    else
                        counter_10MHZ <= counter_10MHZ + 1;
                        state <= sendBitPGA;
                    end if;


              when clockHighPGA =>
                    pga_data <= pga_data(6 downto 0) & '0';
                    state <= sendBitPGA;        

    --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    --^^        ADC interface         ^^
    --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                
              when setADC =>
                    if (cnt = 0) then
                        AD_CONV <= '1';
                        output <= Data (13 downto 0);
                        led <= output (13 downto 6);
                        ADC_path <= '1';
                        state <= idle;
                    else
                        SPI_SCK <= '1'; 
                        cnt <= cnt-1;
                        state <= catchADC;
                    end if;

              when catchADC =>
                        SPI_SCK <= '0';
                        if (cnt >= 18 and cnt <= 31) then
                            Data <= Data(12 downto 0) & ADC_OUT ;   
                        end if; 
                        state <= setADC;
                end case;
     end if;
   end process;
end Behavioral;

Thanks in advance !

\$\endgroup\$
  • 1
    \$\begingroup\$ What does it do when it "doesn't work properly"? Can you read the bitstream back off the FPGA flash to compare with your original and check if it's being written properly? \$\endgroup\$ – Nick Johnson Nov 17 '15 at 10:23
  • \$\begingroup\$ When it doesn't work properly, it put another PGA Gain that the one I wrote in my code (eg : I put 00000001 and the Gain set is 00000011) \$\endgroup\$ – Cabs Nov 17 '15 at 10:25
  • \$\begingroup\$ Can you connect a logic analyzer to the SPI pins for the PGA input so you can see what's being sent, and compare it to the datasheet? \$\endgroup\$ – Nick Johnson Nov 17 '15 at 10:27
  • \$\begingroup\$ I'll use the AMP_DOUT that echoes the gain set to the PGA and I'll tell you the results ! Thank you for the time you're spending for me ! \$\endgroup\$ – Cabs Nov 17 '15 at 13:23
  • 1
    \$\begingroup\$ You have several latches in your design, main reason for it is because you do not assign all your outputs in all branches of your case statement or if-then-else statements. One way to solve this is to set the signal to their defauly value, before start of the case statement, other is to make sure that all the signals are assigned under every section of your code. \$\endgroup\$ – FarhadA Nov 18 '15 at 20:16
2
\$\begingroup\$

Does it work OK in simulation? If not, fix that first. You don't want to be debugging both logic problems and the weirder-only-sometimes-happens-real-world problems at the same time.

Get a reset signal in there - it's much easier to debug in the real world when you can just prod a button and have it reset rather than having to reconfigure. Trust me... I've tried both ways :) And make sure you synchronise the reset signal to the clock before releasing it. That can be a source of hard to track down errors as your design gets bigger. See Figure 7 of Xilinx's document on resets - which is a good guide, and allows you to potentially reduce the logic required in your design by making use of the initalisation instead of reset - with the caveat that (as mentioned) I often find on-bench debug easier with a reset switch than with continual reconfiguration.

You will have to write some code to simulate your ADC. This is useful work to do, as it will ensure you actually understand the interface. Pay particular attention to the SPI clock edge on which the ADC captures and asserts data for example. And what order the bits come in on the SPI data lines.

Once the simulation works, check that

  • you IO pins are correctly allocated to the pins you think they should be
  • your clock (and other timing) constraints are correct. Tedious, but you do have to do it.
  • you actually met the timing constraints
  • you have synchronised the asynchronous inputs (like your ADC_out) to the clock before using them. Only use the synchronised version of the signal within your logic - like using the output of C1 in the example linked to.
  • your power supplies are good, and not sagging as the FPGA starts up (for example)
\$\endgroup\$
2
\$\begingroup\$

Firstly you need to make sure your clock constraints are set up right and your design passes timing. You can get all sorts of strange behaviours from a design that fails timing. Ideally you would set up IO timing constrains too though that's not vital in realitvely slow systems.

Secondly you need to check the xilnx documention on initial conditions in the version of the synthisis tool you are using. Some synthisis tools don't support the standard VHDL/verilog way of specifying them and either don't allow them to be set at all or require tool specific methods to set them. This is a legacy from ASIC design where initial conditions were not considered meaningful for synthisis.

Thirdly you set the SPI data line and the SCLK at the same time. This means that the transitions could arrive at the SPI device in either order. You really shouldn't touch both those lines on the same clock cycle. I suspect this is most likely your actual problem.

Fourthly have you checked the acceptable SCLK frequencies for your programable gain amplifier?

Finally you really need to get some kind of test equipment set up. Either external logic analysers or better still something inside the FPGA. I belive xilinx have a tool called chipscope for this but I don't know the details.

\$\endgroup\$
  • \$\begingroup\$ I'll look at all you said, it's quite hard to debug... Maybe I'll accept your answer as solution because you said everything I could do I guess ! \$\endgroup\$ – Cabs Nov 18 '15 at 15:28
  • \$\begingroup\$ I concur with the statement about initial conditions. Use a reset signal, in particular to put your FSM in a known state and also reset other signals. Assigning state := pga_load will be unreliable in anything except simulation. \$\endgroup\$ – bornruffians Nov 18 '15 at 15:54
  • \$\begingroup\$ Adding a reset signal is one option but then you have to generate that reset signal somehow. At least some FPGAs can set initial conditions but i'm an altera/verilog developer myself so I don't know the exact details on the other side of the fence. \$\endgroup\$ – Peter Green Nov 18 '15 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.