PmodAD1

From Hamsterworks Wiki!

Jump to: navigation, search

This F{GA Project was completed in May 2015.

Somebody out on the Internet was having a problem with a project that used the Digilent PmodAD1, a dual 12-Bit ADC.

This is a cut down version of the code I wrote to read the ADCs and then display the eight most significant bits of both inputs on the LEDs.

I used a "Works with Arduino" Joystick as the test input.

PmodAD1.jpg

Contents

Source files

pmodad1_test.vhd

library ieee;
use ieee.std_logic_1164.ALL;
use ieee.std_logic_unsigned.ALL;
use ieee.numeric_std.all;
 
entity pmodad1_test is
    port(   clk      : in std_logic;   -- 100MHz clock
            ADC_CS   : out std_logic;  -- ADC chip select
            ADC_SCLK : out std_logic;  -- ADC serial clock
            ADC_D0   : in std_logic;   -- ADC Channel 0
            ADC_D1   : in std_logic;   -- ADC Channel 1
            led      : out std_logic_vector(15 downto 0)
            );
end pmodad1_test;
 
architecture Behavioral of pmodad1_test is    
    signal data_0 : std_logic_vector(11 downto 0) := (others=>'0');
    signal data_1 : std_logic_vector(11 downto 0) := (others=>'0');
    
    -----------------------------------------------------------------------------
    -- You can control the sampling frequency with the length of 
    -- sequncer_shift_reg and ce_sr.
    --
    -- F(sclk) =F(clk)/(2*(ce_sr'length+1))
    --
    -- Sampling freqency is F(sclk)/ (sequncer_shift_reg'length+1)
    --
    -- with 100MHz and ce_sr being four bits long SCLK is 10MHz.
    -- with sequncer_shift_reg of 19 bits, that gives a sample rate of 0.5MHz
    -----------------------------------------------------------------------------
    signal ce_sr               : std_logic_vector(3 downto 0) := (others=>'X');    
    signal sequncer_shift_reg  : std_logic_vector(18 downto 0) := (others=>'X');
 
    signal clock_state         : std_logic := 'X';
    signal clock_enable        : std_logic := 'X';
    signal din0_shift_reg      : std_logic_vector(15 downto 0) := (others=>'X');
    signal din1_shift_reg      : std_logic_vector(15 downto 0) := (others=>'X'); 
begin
    led <= std_logic_vector(data_1(11 downto 4)) & std_logic_vector(data_0(11 downto 4));
    -----------------------------------
    -- Generate the clock_enable signal
    -- For the rest of the design.
    --
    -- Change the length of ce_sr to 
    -- change the Serial clock speed
    -----------------------------------
    clock_divide : process(CLK)
        begin
             if rising_edge(CLK) then
                --------------------------------------
                -- Self-recovering in case of a glitch
                --------------------------------------
                if unsigned(ce_sr) = 0 then
                  ce_sr <= ce_sr(ce_sr'high-1 downto 0) & '1';
                  clock_enable <= '1';
                else
                  ce_sr <= ce_sr(ce_sr'high-1 downto 0) & '0';
                  clock_enable <= '0';
               end if;
            end if;
        end process clock_divide;
        
    main : process (CLK)
        begin
            if rising_edge(CLK) then
               if clock_enable = '1' then
                  if clock_state = '0' then
                     -- Things to do on the rising edge of the clock.
                     
                     -----------------------------------------------------------------
                     -- Capture the bits coming in from the ADC
                     -----------------------------------------------------------------
                     if sequncer_shift_reg(16) = '1' then
                        data_0 <= din0_shift_reg(11 downto 0);
                        data_1 <= din1_shift_reg(11 downto 0);
                     end if;
                     din0_shift_reg <= din0_shift_reg(din0_shift_reg'high-1 downto 0) & adc_d0;
                     din1_shift_reg <= din1_shift_reg(din1_shift_reg'high-1 downto 0) & adc_d1;
   

                     -----------------------------------------------------------------
                     -- And update the sequencing shift register
                     -- Self-recovering in case of a glitch
                     -----------------------------------------------------------------
                     if unsigned(sequncer_shift_reg) = 0 then
                        sequncer_shift_reg <= sequncer_shift_reg(sequncer_shift_reg'high-1 downto 0) & '1'; 
                     else
                        sequncer_shift_reg <= sequncer_shift_reg(sequncer_shift_reg'high-1 downto 0) & '0'; 
                     end if;
                     ----------------------------
                     -- Output rising clock edge
                     ----------------------------
                     adc_sclk    <= '1';
                     clock_state <= '1';
                  else
                     ----------------------------
                     -- Output falling clock edge
                     ----------------------------
                     adc_sclk    <= '0';
                     clock_state <= '0';
                  end if;
               end if;
 
               -----------------------------------------------------------------
               -- A special kludge to get CS to rise and fall while SCLK 
               -- is high on the ADC. This ensures setup and hold times are met.
               -----------------------------------------------------------------
               if ce_sr(ce_sr'length/2) = '1' and clock_state = '1' then
                  if sequncer_shift_reg(0) = '1' then
                     adc_cs <= '1';
                  elsif sequncer_shift_reg(1) = '1' then
                     adc_cs <= '0';
                  end if;
               end if;
            end if;
        end process main;
end Behavioral;

tb_pmodad1_test.vhd

This test bench includes a very rough model of the ADC, that increments the value sent by the ADC each time it is read.

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
use ieee.numeric_std.all;

ENTITY tb_pmodad1_test IS
END tb_pmodad1_test;
 
ARCHITECTURE behavior OF tb_pmodad1_test IS 
    COMPONENT pmodad1_test
    PORT(
         CLK : IN  std_logic;
         ADC_CS : OUT  std_logic;
         ADC_SCLK : OUT  std_logic;
         ADC_D0 : IN  std_logic;
         ADC_D1 : IN  std_logic;
         LED    : OUT std_logic_vector(15 downto 0)
        );
    END COMPONENT;
   --Inputs
   signal CLK : std_logic := '0';
   signal ADC_D0 : std_logic := '0';
   signal ADC_D1 : std_logic := '0';

 	--Outputs
   signal ADC_CS  : std_logic;
   signal ADC_SCLK : std_logic;
   signal led      : std_logic_vector(15 downto 0);

   -- For generating the test ADC signals
   signal tristate_sr : std_logic_vector(15 downto 0);
   signal adc_data    : std_logic_vector(15 downto 0);
   signal count :unsigned(11 downto 0) := (others => '0');
   -- Clock period definitions
   constant CLK_period : time := 10 ns;
   constant SCLK_period : time := 10 ns;
BEGIN
----------------------------------------
-- Cheap and nasty simulation of the ADC
----------------------------------------
   adc_d0       <= adc_data(adc_data'high) when tristate_sr(tristate_sr'high) = '1' and adc_cs ='0' else 'Z';
   adc_d1       <= adc_data(adc_data'high) when tristate_sr(tristate_sr'high) = '1' and adc_cs ='0' else 'Z'; 
 p: process(adc_sclk)
   begin
      if falling_edge(adc_sclk) then
         if adc_cs = '1' then
            adc_data <= "0000" & std_logic_vector(count);
            count <= count+1;
            tristate_sr <= (others => '1');
         else
            adc_data <= (others => 'X');
            adc_data    <= adc_data(adc_data'high-1 downto 0) & adc_data(adc_data'high) after 25 ns;
            tristate_sr <= tristate_sr(tristate_sr'high-1 downto 0) & '0' after 25 ns;
         end if; 
      end if;
   end process;
   
   uut: pmodad1_test PORT MAP (
          CLK      => CLK,
          adc_cs   => adc_cs,
          adc_sclk => adc_sclk,
          adc_d0   => adc_d0,
          adc_d1   => adc_d1,
          led      => led
        );

   -- Clock process definitions
   CLK_process :process
   begin
		CLK <= '0';
		wait for CLK_period/2;
		CLK <= '1';
		wait for CLK_period/2;
   end process;
END;

basys3.xdc

These are the pin constraints for my Basys3 board.

## Clock signal
set_property PACKAGE_PIN W5 [get_ports clk]							
	set_property IOSTANDARD LVCMOS33 [get_ports clk]
	create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]
 

## LEDs
set_property PACKAGE_PIN U16 [get_ports {led[0]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property PACKAGE_PIN E19 [get_ports {led[1]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property PACKAGE_PIN U19 [get_ports {led[2]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property PACKAGE_PIN V19 [get_ports {led[3]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property PACKAGE_PIN W18 [get_ports {led[4]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}]
set_property PACKAGE_PIN U15 [get_ports {led[5]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}]
set_property PACKAGE_PIN U14 [get_ports {led[6]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}]
set_property PACKAGE_PIN V14 [get_ports {led[7]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[7]}]
set_property PACKAGE_PIN V13 [get_ports {led[8]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[8]}]
set_property PACKAGE_PIN V3 [get_ports {led[9]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[9]}]
set_property PACKAGE_PIN W3 [get_ports {led[10]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[10]}]
set_property PACKAGE_PIN U3 [get_ports {led[11]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[11]}]
set_property PACKAGE_PIN P3 [get_ports {led[12]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[12]}]
set_property PACKAGE_PIN N3 [get_ports {led[13]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[13]}]
set_property PACKAGE_PIN P1 [get_ports {led[14]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[14]}]
set_property PACKAGE_PIN L1 [get_ports {led[15]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[15]}]
	
##Pmod Header JA
##Sch name = JA1
set_property PACKAGE_PIN J1 [get_ports {ADC_CS}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {ADC_CS}]
#Sch name = JA2
set_property PACKAGE_PIN L2 [get_ports {ADC_D0}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {ADC_D0}]
##Sch name = JA3
set_property PACKAGE_PIN J2 [get_ports {ADC_D1}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {ADC_D1}]
##Sch name = JA4
set_property PACKAGE_PIN G2 [get_ports {ADC_SCLK}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {ADC_SCLK}]

Personal tools