A simple XADC usage in VHDL

New: I put two tutorials on youtube about XADC and an FIR filter:


https://www.youtube.com/watch?v=Tz9c8cNTlxs
https://www.youtube.com/watch?v=yS5MsFkwzyU&feature=youtu.be

A very important caution about the pins and the AUX inputs.

You cannot connect any pins of JXAD or JA to any VAUX of XADC. You need to respect the following constraints:


      VAUX4(P,N) @ address 14h to JA(7,3) (connector JA10 and JA4)
      VAUX5(P,N) @ address 15h to JA(4,0) (connector JA7 and JA1)
      VAUX12(P,N) @ address 1Ch to JA(6,2) (connector JA9 and JA3)
      VAUX13(P,N) @ address 1Dh to JA(5,1) (connector JA8 and JA2)
      
      VAUX6(P,N) @ address 16h to JXADC(0,4) (connector JXADC1 and JXADC7)
      VAUX7(P,N) @ address 17h to JXADC(2,6) (connector JXADC3 and JXADC9)
      VAUX14(P,N) @ address 1Eh to JXADC(1,5) (connector JXADC2 and JXADC8)
      VAUX15(P,N) @ address 1Fh to JXADC(3,7) (connector JXADC4 and JXADC10)

    

Simplest configuration: single channel continous mode

Here is a few steps to use the XADC of the board BASYS 3. Every thing is based on the document :"7 Series FPGAs and Zynq-7000 SoC XADC Dual 12-Bit 1 MSPS Analog-to-Digital Converter" document.

Step 1- Create a project with Vivado (I used the version 2018.2). When creating the project, do not indicate that you are using the basys board, but just select the right FPGA and do the connections by hand: XC7A35T-236C

Step 2: add the IP XADC and configure it as below:

That is all. Generate the IP and then in Vivado (in the small window of "sources") develop "inst_xadc:xadc_wiz_0" to see the vhdl file "xadc_wiz_0.vhd". Open it and copy the whole entity. We need it to instantiate the xadc in our main program.

Step 3: add a VHDL file with ports as:

        entity myADC_singChan is
        Port ( clk : in STD_LOGIC;
               sw : in std_logic;
               led : out STD_LOGIC_VECTOR (15 downto 0);
               JA : in STD_LOGIC_VECTOR (7 downto 0));
        end myADC_singChan; 
    
Here, even the input signals are analog, we still use std_logic. Don't worry, the tool ignores the type and will use the analog type.
The leds are used to monitor the analog signal. Just the 12 MSB will be significant.

Step 4: in the VHDL file instantiate the XADC as

      inst_xadc : xadc_wiz_0 
      port map
      (
        daddr_in        => daddr_in,
        den_in          => eoc_out,
        di_in           => "0000000000000000",
        dwe_in          => '0',
        do_out          => do_out,
        drdy_out        => open,
        dclk_in         => clk,
        reset_in        => sw,
        vauxp5          => anal_p,
        vauxn5          => anal_n,
        busy_out        => open,
        channel_out     => channel_out,
        eoc_out         => eoc_out,
        eos_out         => open,
        alarm_out       => open,
        vp_in           => '0',
        vn_in           => '0');
    

and add some VHDL lines as:

      daddr_in <= "00" & channel_out;
      anal_p <= JA(4);
      anal_n <= JA(0);
      led <= do_out;
    

You need to decalre all the signals as:

      signal channel_out : std_logic_vector(4 downto 0);
      signal daddr_in  : std_logic_vector(6 downto 0);
      signal eoc_out : std_logic;
      signal do_out  : std_logic_vector(15 downto 0);  
      signal anal_p, anal_n : std_logic; 
    
Now every thing is good, we are going to connect the pins.

Step 5: add the constraints to your project. Find the file Basys3_master.xdc digilent site and uncomment the lines related to clk, leds, and JA. I used also a switch for reset. You can use the SW(0) as :

      set_property PACKAGE_PIN V17 [get_ports sw]
      set_property IOSTANDARD LVCMOS33 [get_ports sw]
    

Step 6: Just test it. On the Basys 3 board, connect the JA(1) to the ground, and JA(7) to your signal. The voltage value will be displayed on the leds (the input voltage is unipolar between 0 and 1v).

Single channel, event sampling

Here we need to add a trig signal at the sampling frequency. For example:
      -- for a sampling freq of 100 kHz, with a clk freq of 100 MHz
      process(clk)
      begin
          if clk'event and clk='1' then
              count <= count + 1;
              convst <= '0';
              if count = 999 then
                  count <= 0;
                  convst <= '1';
              end if;
          end if;
      end process;
    
You need just to select "event mode" in XADC wizard and connect the input "convst_in" to your trig signal (your "convst" above) when you instantiate xadc_wiz_0.
You can use the "eoc" signal as a clock_enable to register the "do_out" signal. The delay between the convst and eoc is about 102 clock cycles.

Dual chanel simultaneous mode

Here the XADC wizard should be configured as :
DRP, Event mode, simultaneous Selection, convst in; and every thing disabled in ADC Setup tab and Alarms tab ; in channel sequancer tab check just Vaux4 and vaux12. And that is it !
You instantiate then the xadc and make the connections.
      your_instance_xadc : xadc_wiz_0
      PORT MAP (
        di_in => "0000000000000000",
        daddr_in => daddr,
        den_in => den,
        dwe_in => '0',
        drdy_out => drdy,
        do_out => data,
        dclk_in => clk,
        reset_in => sw, 
        convst_in => conv_en,
        vp_in => '0',
        vn_in => '0',
        vauxp4 => anal1_P,
        vauxn4 => anal1_N,
        vauxp12 => anal2_P,
        vauxn12 => anal2_N,
        channel_out => channel_add,
        eoc_out => eoc,
        alarm_out => open,
        eos_out => eos,
        busy_out => open
      );
    
I used a state machine to read the two sampled data:
      process(clk)
      begin
          if clk'event and clk='1' then
              case state is
              when idle =>  -- waiting for eoc
                  den <= '0';
                  daddr <= "0010100"; -- Vaux4
                  if eoc = '1' then
                      state <= read1; -- read the first data
                      den <= '1';
                  end if;
              when read1 =>
                  den <= '0';
                  if drdy = '1' then
                      data1 <= data;
                      daddr <= "0011100"; -- vaux12
                      state <= tempo;
                  end if;
                  if eoc = '1' then  -- normally it never happens
                      state <= idle; -- but better to put it
                  end if;
              when tempo =>
                  den <= '1';
                  state <= read2;
              when read2 =>
                  if drdy = '1' then
                      data2 <= data;
                      state <= idle;
                  end if;
              end case;
          end if;
      end process;
    
Don't forget to create the conv_en signal, which gives the sampling frequency. The same process as before to create the convst. For analog connections, I used in the architecture the following lines:
      anal1_P <= JA(7);
      anal1_N <= JA(3);
      anal2_P <= JA(6);
      anal2_N <= JA(2);
    
In the entity I added
      JA : in STD_LOGIC_VECTOR (7 downto 0);
    
And at the end, I connected to the Basys 3 board, two unipolar inputs to the connectors JA10 and JA9, while JA4 and JA3 are to the ground.