• 0
Miguel

Nexys_Video_Vivado2014_PmodDA4

Question

Hello, dear collegues!

I work with Nexys Video board. I use VHDL. Now I try to create project with PmodDA4. I have 4 variables which I obtained inside the project and I want to obtain it like 4 analog signals.

I have found an example, but when I tried to implement it for my board it does not work (code is bellow)...

If it is some example code for this PmodDA4, please send it... I could not find it for this board.

 

 

 

-- The four left-most switches (SW15-SW12) define the command, i.e. 0011
-- The four switches after (SW11-SW8) define the address, i.e. 1111
-- The right-most switch (SW0) defines the regime: working, fast (0) or "human", slow (1) 

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_unsigned.all;

entity pgnd is
     port(
         btnc : in STD_LOGIC;
         sysclk : in STD_LOGIC;
         sw : in STD_LOGIC_VECTOR(7 downto 0);
         led : out STD_LOGIC_VECTOR(3 downto 0);
         jb : out STD_LOGIC_VECTOR(3 downto 0)
         );
end pgnd;

architecture pgnd of pgnd is
signal count: STD_LOGIC_VECTOR(27 downto 0) := X"0000000";
signal count2: STD_LOGIC_VECTOR(11 downto 0) := X"001";
signal word_count: STD_LOGIC_VECTOR(5 downto 0) := "000000";
signal sclk0, pout: STD_LOGIC;
signal sync0 : STD_LOGIC := '0';

-- signal data: STD_LOGIC_VECTOR(11 downto 0) := X"000";
signal pdata: STD_LOGIC_VECTOR(31 downto 0) := X"00000000";
begin
    counterp: process(sysclk, btnc)
    begin
        if btnc = '1' then
            count <= X"0000000";
        elsif rising_edge(sysclk) then
            count <= count + 1;
        end if;             
    end process;
    
    sclkp: process(sysclk, count, btnc)
    begin
       if btnc = '1' then
           sclk0 <= '0';
       elsif rising_edge(sysclk) then
           if sw(0) = '1' then     -- Use the same freq for both LEDs and sync
               sclk0 <= count(25); -- Divide 100 MHz / 2^25 => "human" freq
           else
               sclk0 <= count(5);  -- Divide 100 MHz / 32 = 3.125 MHz
           end if;
       end if;
    end process;
    
    -- Word bits counter: 40 = 32 bits sync + 8 void bits
    word_countp: process(sclk0, btnc)
    begin
       if btnc = '1' then
           word_count <= "101000";  -- #  40
       elsif rising_edge(sclk0) then
           if word_count = "101000" then
               word_count <= "000000";
           else
               word_count <= word_count + 1;
           end if;
       end if;
    end process;
    
    -- Sync signal
    signal_syncp: process(sclk0, word_count, btnc)
    begin
        if btnc = '1' then
            sync0 <= '1';
        elsif rising_edge(sclk0) then
           if word_count = "000000" then
               sync0 <= '0';
           elsif word_count = "100000" then
               sync0 <= '1';
           end if;
        end if;
    end process;
    
    signal_shiftp: process(sclk0, word_count, sw, count2, btnc)
    begin
        if btnc = '1' then
            pdata <= X"0" & sw(7 downto 0) & count2 & X"00";
        elsif rising_edge(sclk0) then
           if word_count = "101000" then
               pdata <= X"0" & sw(7 downto 0) & count2 & X"00";
           else
               pdata <= pdata(30 downto 0) & pdata(31); 
           end if;
        end if;
    end process;    
    
    -- Sawtooth data modulation
    data_countp: process(sclk0, word_count, btnc)
    begin
       if btnc = '1' then
           count2 <= X"001";
       elsif rising_edge(sclk0) and word_count = "101000" then
           count2 <= count2 + 1;
       end if;
    end process;
    
    -- Data transmission process
    spip: process(sclk0, word_count, btnc)
    begin
       if btnc = '1' then
           pout <= '0';
       elsif rising_edge(sclk0) then
           -- Send the signal
           pout <= pdata(31);
       end if;
    end process;
    
    JB(0) <= sync0;  -- SYNC
    JB(1) <= pout;  -- DOUT
    JB(2) <= pout;  -- Just duplicate DOUT
    JB(3) <= sclk0;  -- SCLK
        
    led(0) <= sclk0 when sw(0) = '1' else  -- show the real SCLK
              count(24);                   -- or indicate device is working on higher freq
    led(1) <= sclk0 when sw(0) = '1' else
              not count(24);
    -- Couple the other leds only if the frequency is "human"
    led(2) <= pout when sw(0) = '1' else '0';  -- DOUT
    led(3) <= sync0 when sw(0) = '1' else '0';  -- SYNC
end pgnd;

Share this post


Link to post
Share on other sites

13 answers to this question

  • 1

@Miguel

It's typically better practice to derive your clock along with your data logic, rather than using the clock to time your data. Another fix would have been to just invert the clock at the output.

Thanks.

Arthur

Share this post


Link to post
Share on other sites
  • 0

@Miguel,

Gosh, where to start?

  1. If you don't simulate things, via building a test bench, etc., you'll find yourself in what I call "FPGA Hell": Your code won't work, and you'll have no idea why.  I'm trying to slowly work through a series of blog posts regarding this specific problem you are struggling with, and how to avoid it, although the discussion is far from complete (yet).
  2. You are using logic to generate a clock, that you are then looking for the positive edge of.  This is not a good idea, and a common newbie mistake.  Much better to create a strobe (on for one system clock only) that you use in your logic, and then with it keep everything at your system clock.
  3. That asynchronous reset (btnc) is unreliable.  Indeed, it is so unreliable that Xilinx warns that radio waves can trigger it.  You might wish to (instead) synchronize it so everything is on the positive edge of the clock.
  4. The next problem is that you are looking for a button press and ... you haven't synchronized the button press.  (See metastability)  Any time you accept an asynchronous input, you want to clock that input twice before using it, as in:
reg	r_btnc, r_reset_stb;
always @(posedge i_clk)
begin
	r_btnc <= btnc;
	r_reset_stb <= r_btnc;
end

This will also synchronize the btnc input (and rename it r_reset_stb ... a little more legible for what you are doing with it), so that you can now test it on any positive edge of a clock.

While this isn't a complete answer, I hope it gets you going at least a step further.  Write back if you get any closer, or get stuck again after making those changes,

Dan

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

@Miguel,

In addition to the points that @D@n brings up, it appears that you are not setting the internal reference register of the AD5628 part. I would recommend reviewing the DA4's reference manual. In particular the note that: "Users should note that the Pmod DA4 by default attempts to use an external reference voltage. However, as there is no external reference voltage provided on the Pmod, users must write to the register to program the chip to use its internal reference voltage of 1.25V. The data stream required to change this is provided below ...". The short version of this is that you will need to send the 32 bits x8001 once on startup of your project, before doing any conversions.

Running the following - overly simple - simulation testbench (written in verilog), makes me think that your SPI controller is largely working, though this does nothing to test for D@n's concerns about the btnc reset or clock generation.

`timescale 1ns / 1ps

module sim;
    reg btnc = 1; // initialize to one, to reset the pgnd module on startup
    wire [3:0] led;
    reg [7:0] sw = 0; // initialize to zero, to use the faster clock
    wire [3:0] jb;
    reg sysclk = 1; // initialize one, so that positive edges will fall on integer-numbered timestamps 
    
    always #0.5 sysclk = ~sysclk; // toggle sysclk every half ns, to create a 1 ns clock period. the actual board runs slower, but this makes the simulation take less time to run
    initial #1 btnc = 0; // clear the reset
    
    pgnd uut ( // instantiate the unit under test 
        btnc,sysclk,sw,led,jb // connect the inputs and outputs of pgnd in the same order they appear in the VHDL file
    );
    initial #10000 $finish; // run the simulation for a couple of SPI periods, then finish
endmodule

 

Edit: when using a simulation in Vivado, additional signals can be added to the viewing window by right clicking on a Scope - in this case uut - and clicking "Add to Wave Window".

Thanks,

Arthur

Edited by artvvb
additional info about simulation

Share this post


Link to post
Share on other sites
  • 0

Hi @Miguel,

Unfortunately, I did not find any hdl code for the PmodDA4. Once you have got your SPI controller working you can use the MPIDE demos on the resource page Here as a reference for configuring and using the PmodDA4.  

cheers,

Jon

Share this post


Link to post
Share on other sites
  • 0

Dear collegues! Thank you for your answers!

 

I have found an example and rewrited it just a little bit (saw info from http://www.analog.com/media/en/technical-documentation/data-sheets/AD5628_5648_5668.pdf) , page 24.

 

But it does not work...I do not obtain analog signals on the output of the pmodda4.

 

pdata - is data for the output, it consists of: "0000" - first 4 do not care bits, 4 Command Bits + 4 Address Bits which are set by the 8 switches, data_0 for the output and 8 do not care bits. As I understood correctly, the 0 bit should be '1', because of the Internal REF register. But it does not work.

 

Please, if it is possible, could you help me to find the problem?

 

 

 

 

entity pmodda4_test is
port(
    sysclk      : in std_logic;                         -- 100MHz clock
    ja          : inout std_logic_vector(7 downto 0);   
    sw          : in std_logic_vector(7 downto 0);      
    );
end pmodad1_test;
 

 

architecture Behavioral of pmodda4_test is    
    signal data_0 : std_logic_vector(11 downto 0) := (others=>'0');
 

signal count: STD_LOGIC_VECTOR(27 downto 0) := X"0000000";
signal count2: STD_LOGIC_VECTOR(11 downto 0) := X"001";
signal word_count: STD_LOGIC_VECTOR(5 downto 0) := "000000";
signal sclk0 : STD_LOGIC;
signal pout : STD_LOGIC;
signal sync0 : STD_LOGIC := '0';
-- signal data: STD_LOGIC_VECTOR(11 downto 0) := X"000";
signal pdata: STD_LOGIC_VECTOR(31 downto 0) := X"00000000";
begin
 

 

 

counterp: process(sysclk)
begin
if rising_edge(sysclk) then
    count <= count + 1;
end if;
end process counterp;

sclkp: process(sysclk, count)
begin
elsif rising_edge(sysclk) then
    if btnl = '1' then     -- Use the same freq for both LEDs and sync
        sclk0 <= count(25); -- Divide 100 MHz / 2^25 => "human" freq
        else
        sclk0 <= count(5);  -- Divide 100 MHz / 32 = 3.125 MHz
    end if;
end if;
end process sclkp;

    -- Word bits counter: 40 = 32 bits sync + 8 void bits
word_countp: process(sclk0)
begin
if rising_edge(sclk0) then
    if word_count = "101000" then
       word_count <= "000000";
       else
       word_count <= word_count + 1;
    end if;
end if;
end process word_countp;

    -- Sync signal
signal_syncp: process(sclk0, word_count)
    begin

if rising_edge(sclk0) then
    if word_count = "000000" then
        sync0 <= '0';
        elsif word_count = "100000" then
        sync0 <= '1';
    end if;
end if;
end process signal_syncp;


signal_shiftp: process(sclk0, word_count, sw, count2)
begin
if rising_edge(sclk0) then
    if word_count = "101000" then
        pdata <= "0000" & sw(7) & sw(6) & sw(5) & sw(4) & sw(3) & sw(2) & sw(1) & sw(0) & data_0 & "00000000";
        else
        pdata <= pdata(30 downto 0) & pdata(31); 
    end if;
end if;
end process signal_shiftp;    
    
    -- Data transmission process
spip: process(sclk0, word_count)
    begin
if rising_edge(sclk0) then
           -- Send the signal
    if sync0 = '0' then
        pout <= pdata(31);
        else
        pout <= '0';
    end if;
end if;
end process spip;
 

 

    ja(0) <= sync0;  -- SYNC
    ja(1) <= pout;  -- DOUT
    ja(2) <= pout;  -- Just duplicate DOUT
    ja(3) <= sclk0;  -- SCLK
 

Share this post


Link to post
Share on other sites
  • 0

@Miguel,

Have you simulated your code?  Can you share a picture of what the simulated waveform looks like?

Dan

Share this post


Link to post
Share on other sites
  • 0

Dear Dan! Thank you for your quick answer!

I can not send you a picture from the simulator, because it sends me some errors, so I cant "switch it on". But I work with oscilloscope, so you can see all signalls what I send to the PmodDA4.

1 - data_0 <= (others =>'1'), 4 Command Bits + 4 Address Bits are '0', and the last one is pout (0) <= '1'.

 

IMG_3666.JPG

Share this post


Link to post
Share on other sites
  • 0

It works.

Code is bellow. I obtain only one analog signal (between GND and Pin A).

If somebody find some mistakes, please write about it!

Problem was: all things had to be at falling_edge of the sclk0.

 

library ieee;
use ieee.std_logic_1164.ALL;
use ieee.std_logic_unsigned.ALL;
use ieee.numeric_std.all;
 

entity pmodad1_test is
port(
    sysclk      : in std_logic;                         -- 100MHz clock
    ja          : inout std_logic_vector(7 downto 0);  
    sw          : in std_logic_vector(7 downto 0);   
    );
end pmodad1_test;

architecture Behavioral of pmodad1_test is    
    signal data_0 : std_logic_vector(11 downto 0) := (others=>'0');
--DAC_PROG
signal count: STD_LOGIC_VECTOR(27 downto 0) := X"0000000";
signal count2: STD_LOGIC_VECTOR(11 downto 0) := X"001";
signal word_count: STD_LOGIC_VECTOR(5 downto 0) := "000000";
signal sclk0 : STD_LOGIC;
signal pout : STD_LOGIC;
signal sync0 : STD_LOGIC := '0';
-- signal data: STD_LOGIC_VECTOR(11 downto 0) := X"000";
signal pdata: STD_LOGIC_VECTOR(31 downto 0) := X"00000000";
begin
counterp: process(sysclk)
begin
if rising_edge(sysclk) then
    count <= count + 1;
end if;
end process counterp;
--************************************************************************************************
sclkp: process(sysclk, count)
begin
if rising_edge(sysclk) then
    if btnl = '1' then     -- Use the same freq for both LEDs and sync
        sclk0 <= count(25); -- Divide 100 MHz / 2^25 => "human" freq
        else
        sclk0 <= count(5);  -- Divide 100 MHz / 32 = 3.125 MHz
    end if;
end if;
end process sclkp;
--************************************************************************************************
    -- Word bits counter: 40 = 32 bits sync + 8 void bits
word_countp: process(sclk0)
begin
if falling_edge(sclk0) then
    if word_count = "101000" then
       word_count <= "000000";
       else
       word_count <= word_count + 1;
    end if;
end if;
end process word_countp;
--************************************************************************************************
    -- Sync signal
signal_syncp: process(sclk0, word_count)
begin
if falling_edge(sclk0) then
    if word_count = "000000" then
        sync0 <= '0';
        elsif word_count = "100000" then
        sync0 <= '1';
    end if;
end if;
end process signal_syncp;
--************************************************************************************************
signal_shiftp: process(sclk0, word_count, sw, count2)
begin
if falling_edge(sclk0) then
    if word_count = "101000" then
        pdata <= "0000" & sw(7) & sw(6) & sw(5) & sw(4) & sw(3) & sw(2) & sw(1) & sw(0) & data_0 & "00000001";
        else
        pdata <= pdata(30 downto 0) & pdata(31); 
    end if;
end if;
end process signal_shiftp;    
--************************************************************************************************
    -- Data transmission process
spip: process(sclk0)
    begin
if falling_edge(sclk0) then
    if sync0 = '0' then
        pout <= pdata(31);           -- Send the signal
        else
        pout <= '0';
    end if;
end if;
end process spip;
--************************************************************************************************
ja(0) <= sync0;  -- SYNC
ja(1) <= pout;  -- DOUT
ja(2) <= pout;  -- Just duplicate DOUT
ja(3) <= sclk0;  -- SCLK
end Behavioral;

Share this post


Link to post
Share on other sites
  • 0

@Miguel,

Yes, exactly what @artvvb said: If you generate your clock with logic, then don't try to transition on the positive or negative edge of it.  You will tend to have no end of timing problems when you do that.

A better example of how to generate a SPI clock in the fashion I am discussing can be found here (in Verilog, there's also my own SPI decoder for the Pmod MIC3 here).  If you don't understand verilog, then just pay attention to the posedge clk statements.  You'll notice that all of the logic is based upon the positive edge of the system clock (100MHz), and the output clock is generated from that same logic.  The result of this is that the result of the digitizer is already synchronized with my 100Mhz system clock, and I don't need to have to figure out how to cross clock domains from the SPI clock to the system clock. 

Even better, the system clock never stops, whereas many SPI-type protocols want a clock that starts from a stopped condition--making transitioning on the positive (or negative) edge of that clock an ... uncertain endeavor.  Gosh, even the DA4's component data sheet, on Figure 2 of page 4, shows that the clock is stopped between reads.  This isn't an absolute, however, since other SPI devices can use the same bus, but something to be aware of.

Dan

 

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

The problem which I found is:

I always send              pdata <= "0000"  & "0000" & "0000" & DAC_1 & "00000001"; where  DAC_1 is std_logic_vector(11 downto 0),

I see analog signal from A chanel (Ground - GND, Positive - A) on the digital oscilloscope, but I see terrible result. But if I connect the second oscilloscope channel to the sclk - wave is really good.

 

Some times DAC does not work, so I have to send pdata <= "0000"  & "0100" & "0000" & DAC_1 & "00000001", then toutch by the second oscilloscope channel positive wire SYNC then pdata <= "0000"  & "0000" & "0000" & DAC_1 & "00000001" and it works.

What I do wrong? Should I send constant C bits always? and what sequence?

Edited by Miguel

Share this post


Link to post
Share on other sites
  • 0

@Miguel,

Transitioning on rising or falling edge of your own logically generated signal, such as your own clock signal, is inherently unreliable.  I'd recommend rewriting your work to remove all such logic before wondering why your design is ... unreliable.  :D

Dan

Share this post


Link to post
Share on other sites
  • 0

Posted (edited)

Hello, @D@n! Thank you VERY MUCH!!!!!! Problem with terrible signal is solved!!!!!(code is bellow) but Some times I have a problem with PmodDA4, I have to switch my sw (Comand bits), then it works normal... It is not very big problemfor me now, but it is better to solve it (in my opinion)

 

word_countp: process(sclk0)
begin
if rising_edge(sysclk) then

    if word_count < "10011100001111" then
        word_count <= word_count + 1;
        else
        word_count <=(others => '0');
    end if;
    
    if word_count < "11111001111" then 
        sync0 <= '1';
        else
        sync0 <= '0';
    end if;

    if  sync0 = '0' then
        if count_2 < 249 then
            count_2 <= count_2 + 1;
            pout <= pdata(31 - number);
            else
            count_2 <= 0;
            number <= number + 1;
        end if;
        else
        count_2 <= 0;
    end if;
    
    if number > 31 then
       number <= 0;
    end if;

      pdata <= "0000"  & sw(7) & sw(6) & sw(5) & sw(4) & "0000" & DAC_1 & "00000001";
end if;
end process word_countp;
 

Edited by Miguel
jpeyron likes this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now