Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20778 Discussions

Filter & decimation implementation on AD7401

Altera_Forum
Honored Contributor II
3,465 Views

Hello everybody! 

 

We have chosen the AD7401 Sigma Delta modulator for our project, translated the Verilog code given by the datasheet into VHDL and are now confused about the results of the simulation. 

 

We have attached our translated VHDL code plus the datasheet of AD7401. 

 

1) We will give the master clock from the FPGA to the ADC from one of the I/O pins. The clocks don't seem to be synchronized though, there is a 5nsec latch delay. Should that be ok, or do we have to eliminate that delay somehow? 

 

2) We tried to simulate the data flow inputting only ones '1', but result is really not the expected one. (DATA samples should be at the highest level?!) Yes, but what about overflow? The given interface is permitting that, if such a case comes up. 

 

Below are some results of a random inputted bit stream and a only '1' bit stream. 

 

http://i252.photobucket.com/albums/hh3/unoturbomk2/samples.jpg  

 

http://i252.photobucket.com/albums/hh3/unoturbomk2/samples2.jpg  

 

*** Is the code correct? *** 

 

We'd appreciate any kind of help! 

Thanks in advance!
0 Kudos
27 Replies
Altera_Forum
Honored Contributor II
1,033 Views

The original Verilog code is an example of bad coding style, involving word_clk designed as a ripple clock, but it's basically working. 

 

You have confused the code by an adder, that hasn't been there before and apparently act's as a kind of random number generator in your simulation. 

if mdata1='1' then ip_data1 <= ip_data1 + '1'; else ip_data1 <= ip_data1 - '1'; 

The purpose of this combinational code is to assign 0/+1 respectively -1/+1 to ip_data1, not to sum up anything.
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

Can you explain to us how that is expressed in VHDL? 

 

ip_data1 <= 1; 

 

Because, for instance, in SBAA094 of ADS1202 it is done the way we did it. 

 

DELTA1 <= DELTA1 + 1; 

 

Where exactly is our code mistaken, despite the fact that it is bad coding style given by the datasheet itself. Shouldn't vector ip_data1 work as an adder?
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

ip_data1 <= conv_std_logic_vector(1,24);

0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

Ok, we tried to merge in some way the code of ADS1202 and AD7401, and we got really great results of the code which came out. 

 

The first figure is with feeding 0's and then random input, and the second one is with 1s first and then random values. 

 

http://i252.photobucket.com/albums/hh3/unoturbomk2/2-3.jpg  

 

http://i252.photobucket.com/albums/hh3/unoturbomk2/1-5.jpg  

 

As you can see the code responses to the simulation quite well, respecting that it actually takes 2 words/samples for the code to respond to sudden changes of input values, due to the 3rd order sinc filter. 

 

The code is attached. 

If someone could confirm that this is correct, we'd appreciate it! 

Thank you!
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

The results are correct, except for the unhandled arithmetic overflow with mdata = '1'. I suggest to consider it in the code. 

if CN5(24) = '1' then DATA(15 downto 0) <= (others => '1'); else DATA(15 downto 0) <= CN5(23 downto 8); end if;
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

FvM thank you we corrected that, although we had tried that out, but in a different way! 

I think that now we are done with this part! 

Thanks again!
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

In addition, I see a timing violation in my compilation, caused by the previously mentioned ripple clock. Implementing word_clk as a clock enable removes this problem.

0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

FvM, I tried to implement it as a clock_enable, but results were terrible! 

 

process(clk20,reset) begin if reset = '0' then clk_en <= '0'; elsif clk20'event and clk20 = '0' then cnt <= cnt + 1; -- cnt is a 8-bit vector if cnt(7) = '1' then clk_en <= NOT(clk_en); end if; end if; end process; 

 

After that I used clk_en in combination with the system clock directly in the processes where word_clk used to be. 

 

Could you please suggest to me some other way of implementing that?
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

A clock enable should be active for one clock cycle, e.g.: 

process(word_count) begin if word_count = x"ff" then word_clk <= '1'; else word_clk <= '0'; end if; end process; 

and 

if reset = '0' then -- elsif clk20'event and clk20 = '1' then if word_clk = '1' then DN0 <= CN2; -- end if; end if;
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

Ok FvM I did that and it works great! 

I also did the same thing in the last process, where the DATA assignment is done. 

I think now it works perfectly, most warnings eliminated, except the pin assignment ones! 

Thank you! 

 

process(clk20,reset) begin if reset = '0' then DATA <= (others => '0'); elsif clk20'event and clk20 = '1' then if word_clk = '1' then if CN5(24) = '1' then DATA(15 downto 0) <= (others => '1'); else DATA(15 downto 0) <= CN5(23 downto 8); end if; end if; end if; end process;
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

FvM, 

 

I was searching for a solution for the clock-output issue we have, and I ended up with PLLs. 

I read in Cyclone Handbook about how to handle PLL features in Cyclone FPGAs, but I unfortunately found out that the 100-pin EP1C3 device used in PLUTO-II board does not support external clock output. 

 

Could you come up with any other (perhaps tricky) solution about how we could drive the ADC from the FPGA, providing it the external 20MHz clock we feed to the board from the dedicated clock input pin? 

 

I think we can output the clock from any I/O of the board to the ADC without PLL, but there will be a sync issue of the system clock and the clock feeding into the ADC, am I right? 

 

Thank you for your help!
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

I won't expect any clock output issues with your rather slow clock. What do you mean?

0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

I mean that the filter/decimation operations will be working with system clock, but data will arrive from the ADC on every falling edge of the clock we output from the FPGA, which has a ~5nsec delay. Could we maybe pass the clock through some delay before outputting it to the ADC so that the 2 clocks are identical? (Tried some NOTs, but unfortunately..) 

 

Something else, what about initialization at system-boot? Do the signals and I/Os need something like an internal reset at the start?
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

If you specify the timing correctly, Quartus timing analysis will care for it. 5 ns output delay is only part of the involved delay, the ADC has most likely more delay. The problem is common to interfacing external devices. 

 

If you don't need correct measrements from the start, you don't need a reset with your design, I think. You have to check, if it has illegal states, that may cause a dead-lock.
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

What do you mean by "specifying the timing correctly"? 

 

I understand that the delay will be obviously more than 5ns, and that this is a common problem in interfacing external devices such as an ADC in our case. But unfortunately I really got confused with this, again... 

 

Regarding the datasheet, it is mentioned that data is valid during the rising edge of MCLKIN, and max data access time is 25ns (after rising edge). At the point of introducing the recommended filter+decimation code, it is mentioned that data is read on the falling edge. Also, at the interface of the code, mdata is not driven by the system clock, but by itself (sensitivity list, only when mdata changes value ?!). In our code we drive mdata with clk20 at the rising edge. 

 

Outputting the 20 MHz clock towards the ADC, concerning 5ns++ (???) delay, and doing the operations of interfacing/filter/decimation with system clock, how can we be sure that we will achieve correct data receiving - synchronization? 

 

ads_clk <= clk20; 

 

I'm really confused, sorry. 

 

 

 

I agree that it is not necessary in our application to get correct values/samples from "system-boot" - in worst case scenario there will be only 3-4 distorted samples. 

 

I closed every 'IF' block in our code with "else... NULL" operations, so this should be enough to avoid dead-lock. Also, we are going to implement a asynchronous hard-reset pushbutton. 

 

Just because of curiosity, would it be difficult to implement an initialization? 

 

Thank you so much!
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

If a design involves a risk of deadlock condition, they are not avoided by adding functionless code, e.g. "others" in a case construct. The compiler is smart enough to remove it. A deadlock can e.g. occur with illegal states of a state machine, it can be triggered by an incorrect reset or a timing violation of the system clock or an asynchronous input signal. But your design has no stae machines, only binary counters without a risk of deadlock. 

 

Regarding timing of ads_clock and mdata in your design. I see mainly two options: 

- specify exact timing constraints for the external signals with Time Quest timing analyzer 

- perform a hand calculation of the expectable mdata timing related to your system clock (clk20) and check if it can be safely received. If not, invert the output clock. 

 

Other options are e.g.to generate phase shifted ads_clock through a PLL and send it through general I/O pins. But I think, it's not reasonable for a slow 20 MHz clock.
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

FvM thank you very much, I'll check out these issues.

0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

Sir, Hello! 

I used the code you write to debug to the actual circuit not simulation, and found it cannot produce word_clk clock signal, so I have written in verilog a divider circuit, to produce mclk and word_clk signal, and made &#8203;&#8203;some changes to your program (Delete word_clk produce circuit ), and then debug, I find the result of the conversion are all 0 (DATA [15 .. 0]).After all, it cannot work in actual circuit.I don't know why,Could you tell me?Have you ever used the AD7401 to apply a actual project such as a sampling circuit of voltage? 

The following is a modified program of you:  

 

library IEEE; 

use IEEE.std_logic_1164.all; 

use IEEE.std_logic_unsigned.all; 

 

 

entity AD7401decimationfilter is 

port(reset, mdata, clk20,word_clk: in std_logic; 

DATA : out std_logic_vector(15 downto 0); 

ads_clk : out std_logic); 

end AD7401decimationfilter; 

 

 

architecture RTL of AD7401decimationfilter is 

signal CN2_D2,DN1_D,DN2_D,DN1,DN2,DN3 : std_logic_vector(24 downto 0); 

signal CN1, CN2 : std_logic_vector(24 downto 0); 

signal delta : std_logic_vector(24 downto 0); 

--signal word_count : std_logic_vector(7 downto 0); 

--signal word_clk : std_logic; 

 

 

begin 

 

ads_clk <= clk20; 

 

p11:process(clk20, reset) 

begin 

if reset = '0' then 

delta <= (others => '0'); 

elsif clk20'event and clk20 = '1' then 

-- if mdata = '1' then 

delta <= delta + 1;--mdata; 

--end if; 

end if; 

end process p11; 

 

p12:process(reset, clk20) 

begin 

if reset = '0' then 

CN1 <= (others => '0'); 

CN2 <= (others => '0'); 

elsif clk20'event and clk20 = '1' then 

CN1 <= CN1 + delta ; 

CN2 <= CN2 + CN1; 

end if; 

end process p12; 

 

--DECIMATION STAGE (MCLKIN/WORD_CLK) 

--process(clk20,reset) 

--begin 

-- if reset ='0' then 

-- word_count <= "00000000";--(others => '0');  

-- elsif clk20'event and clk20 = '0' then 

-- if word_count="11111111" then  

-- word_count<="00000001"; 

-- else word_count <= word_count + 1; -- word_count is an 8 bit reg for decimation 2^8= 256 

-- end if;  

-- end if; 

--end process; 

 

--process(word_count,reset) 

--begin 

--if reset = '0' then 

-- word_clk <= '0'; 

-- oe<='0'; 

--else 

-- word_clk <= word_count(7);  

-- oe<=word_count(7); 

--end if; 

--end process; 

 

p13:process(reset, word_clk) 

begin 

if reset = '0' then 

CN2_D2 <= (others => '0'); 

DN1_D <= (others => '0'); 

DN2_D <= (others => '0'); 

DN1 <= (others => '0'); 

DN2 <= (others => '0'); 

DN3 <= (others => '0'); 

elsif word_clk'event and word_clk = '1' then 

DN1 <= CN2 - CN2_D2; 

DN2 <= DN1 - DN1_D; 

DN3 <= DN2 - DN2_D; 

 

CN2_D2 <= CN2; 

DN1_D <= DN1; 

DN2_D <= DN2; 

end if; 

end process p13; 

 

--p14:process(word_clk,reset) 

--begin 

-- if reset = '0' then 

-- DATA <= (others => '0'); 

-- elsif word_clk'event and word_clk = '1' then 

-- DATA(15 downto 0) <= DN3(23 downto 8); -- extracting the Most Significant Bits MSB  

-- end if; 

--end process p14; 

process(clk20,reset) 

begin 

if reset = '0' then 

DATA <= (others => '0'); 

elsif clk20'event and clk20 = '1' then 

if word_clk = '1' then 

if DN3(24) = '1' then 

DATA(15 downto 0) <= (others => '1'); 

else  

DATA(15 downto 0) <= DN3(23 downto 8); 

end if; 

end if; 

end if; 

end process; 

 

end RTL; 

 

 

 

*i wrote it myself sub-frequency signal procedure as follows: 

 

module dividefrequency(sysclk,reset,mclk1,mclk2,word_clk); 

input sysclk; 

input reset; 

output mclk1; 

output mclk2; 

output word_clk; 

 

wire sysclk; 

wire reset; 

reg mclk1; 

wire mclk2; 

reg word_clk; 

 

reg [31:0] div_count; 

reg [7:0] word_count; 

integer location; 

integer info_file; 

//wire [7:0] dec_rate; 

//parameter rate=256;//280wen,256(12M);//70->1/2 i2c//116->1.4*16; 

//parameter h_rate=64; 

//assign dec_rate = 256 - rate;//ini value 

 

assign mclk2=mclk1; 

/* 

initial 

begin 

 

end 

*/ 

/*divide sysclk 50MHZ to 6.25MHZ*/ 

always @(posedge reset or negedge sysclk) 

if(reset) 

begin 

div_count<=0; 

end 

else 

begin 

div_count<=div_count+1; 

if(div_count==8) 

div_count<=1'b1;  

end 

always @(div_count,mclk1)//mclk  

begin 

if(div_count<=4) mclk1<=1; 

else if(div_count<=8) mclk1<=0; 

end 

 

always @ (negedge mclk1 or posedge reset)  

begin 

if (reset) word_count <= 0;  

else if (word_count == 8'hFF) word_count <=1;// dec_rate+1; 

else word_count <= word_count + 1; 

end 

/* 

always @ (word_count)  

word_clk <= word_count[8]; 

*/ 

always @ (word_count)  

begin 

if(word_count<=64)//64) 

word_clk<=1; 

else if(word_count<=8'hff)//255) 

word_clk<=0; 

end 

endmodule  

sysclk is 50mhz,which is divided to 6.25mhz as mclk and 24.41khz as word_clk.
0 Kudos
Altera_Forum
Honored Contributor II
1,033 Views

Hi, 

 

sorry but currently I have no time to look into your issue. I have attached our filter block, which had worked for us (in a real implementation). It contains the part of code that you need. 

 

Please disregard the last section tagged as "RAM Writing Process" and its associated signals, and adjust "DATA Assignment Process" to your needs. 

 

Hope it helps! 

 

BR, 

David
0 Kudos
Altera_Forum
Honored Contributor II
942 Views

Thank you first. @dfuschelberger

0 Kudos
Reply