Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
17 years ago

Programmable watchdog in FPGA

Hi all..I am trying to implment a programmable (by microcontoller) watchdog using FPGA..I am using the Altera Megafunction lpm_counter for this purpose. However, my watchdog is generating a lot of warmings ('barking') some of them I don't understand how to fix...

Description: I am using the 50MHz onboard oscillator (DE2) as my clock input and put it through a PLL to generate a 10MHz clock (the lowest one can get from these buildin PLL)..then I am using counter1 to further divide this down to approximately 32787Hz (10,000,000/305) which should give me about 2 second if I am counting down from 65535 to 0 (16bit)...so here is my code...

The warming I am getting is:

Warning: Found combinational loop of 2 nodes

Warning: Node "inst1|COUNTDOWN_COUNTER|auto_generated|latch_signal[9]~995|combout"

Warning: Node "inst1|COUNTDOWN_COUNTER|auto_generated|latch_signal[9]~995|dataa"

and there are many of this kind...I am attaching the whole project zipped file to this post as well...any recommendataions are welcomed!

--------------------------------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;

LIBRARY lpm;

USE lpm.all;

entity WDT is

Generic

(

add_WDT: std_logic_vector (15 downto 0) := X"0044"

);

port

(

reset : in std_logic; -- Hardware reset line from ColdFire

timeout : out std_logic := '0';

datain : in std_logic_vector (15 downto 0);

dataout : out std_logic_vector (15 downto 0);

address : in std_logic_vector (15 downto 0);

CS : in std_logic;

RW : in std_logic;

TA : out std_logic := '1';

oe : out std_logic;

q_out : out std_logic_vector (15 downto 0);

clkin : in std_logic -- 10MHz input clock

);

end entity WDT;

architecture sys of WDT is

signal counter1: integer range 304 downto 0;

signal WDT: std_logic_vector (15 downto 0);

signal Timer: std_logic_vector (15 downto 0);

signal stop, flag, flag2, load: std_logic;

signal intclk: std_logic;

type STATE_TYPE is (S0, S1, S2, S3, S4, S5); -- Delay

signal SC, NS: STATE_TYPE;

component lpm_counter

generic

(

lpm_direction : STRING;

lpm_port_updown : STRING;

lpm_type : STRING;

lpm_width : NATURAL

);

PORT

(

aload : IN STD_LOGIC;

cnt_en : IN STD_LOGIC;

clock : IN STD_LOGIC ;

q : OUT STD_LOGIC_VECTOR (15 DOWNTO 0);

data : IN STD_LOGIC_VECTOR (15 DOWNTO 0)

);

END COMPONENT;

begin

clock_divider: process -- this process will divide the 10MHz clock downto approximately 32787Hz

begin -- therefore, 65536 cycles corresponding to about 2 seconds.

wait until clkin'event and clkin='1';

counter1 <= counter1 + 1;

if counter1 = 304 then

intclk <= intclk xor '1';

counter1 <= 0;

end if;

end process;

SYNC_PROC: process -- These following processes take care of ColdFire writes/reads from the watchdog

begin -- to set and read the countdown time

wait until clkin'event and clkin = '1';

SC <= NS; -- move to next state

end process;

STATE_PROC: process (SC)

begin

case SC is

when S0 => -- S0 is generally a wait state

if CS = '0' and RW = '1' then -- ColdFire reads

NS <= S1;

elsif CS = '0' and RW = '0' then -- ColdFire writes

NS <= S3;

else

NS <= S0;

end if;

-- ColdFire reads (S1-S2)

when S1 =>

if (address = add_WDT ) then

NS <= S2;

else

NS <= S5;

end if;

when S2 =>

NS <= S5;

-- ColdFire writese (S3-S6)

when S3 =>

if (address = add_WDT) then

NS <= S4;

else

NS <= S5;

end if;

when S4 =>

NS <= S5;

when S5 =>

if CS = '1' then

NS <= S0;

else

NS <= S5;

end if;

end case;

end process;

READ_PROC: process

begin

wait until clkin'event and clkin='1';

case SC is

when S1=> -- ColdFire reads

TA <= '1';

oe <= '1';

when S2=>

dataout <= WDT;

TA <= '0';

oe <= '1';

when S3=> -- ColdFire writes

TA<= '1';

oe <= '0';

when S4=>

WDT <= datain;

TA <= '0';

oe <= '0';

when others =>

TA <= '1';

oe <= '0';

end case;

end process;

L0AD_PROC: process

begin

wait until clkin'event and clkin='1';

flag <= reset;

end process;

LOAD_PROC2: process

begin

wait until clkin'event and clkin='0';

flag2 <= flag;

end process;

load <= flag xor flag2; -- Create pulses when reset change states

COUNTDOWN_COUNTER : lpm_counter

generic map

(

lpm_direction => "DOWN",

lpm_port_updown => "PORT_UNUSED",

lpm_type => "LPM_COUNTER",

lpm_width => 16

)

PORT MAP (

aload => load, -- Use the previously created pulse as asynload signal to the counter.

clock => intclk,

q => Timer,

cnt_en => stop,

data => WDT

);

q_out <= timer;

stop <= '0' when timer = X"0000" else '1'; -- If counter reaches 0, stop the count down and

timeout <= not (stop); -- rise the timeout flag

end architecture;

14 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    Thanks Brad for your reply.

    It doesn't look like the multicycle statment in sdc file has any effects (don't know why)..However, if this clk_en is NOT set as global routing, I ended up glitches in my output (see timeout signal in the attached waveform). If clk_en is set as global routing. I got no problem..plus..If I look at the Fitter--->Control Signals..I don't see this clk_en listed as global signal..

    Any comments?

    --- Quote Start ---

    Yes, the clock enable is better. My other thread says that you can safely use a divided ripple clock by using global routing and having no synchronous cross-domain paths, but a fast PLL clock with a clock enable is still better (for example, the PLL might have less duty cycle distortion than a clock driven by a register). Instead of doing a divide by 305 to create a clock signal, create a clock enable signal that is asserted every 305th period of the fast clock. Note that the clock enable is asserted for just the 305th period--the assertion lasts for one fast-clock period. The registers using the clock enable are clocked by the fast clock. You can use a multicycle setup of 305 and multicycle hold of 305-1=304 for the paths that use the clock enable for both source and destination registers (see http://www.altera.com/support/examples/timequest/exm-tq-clock-enable.html).

    The primary reason to use globals is to minimize skew, which usually matters only for clock signals and does not matter for clock enables. Another reason to use globals is for very high fan-out signals to avoid using up lots of nonglobal routing resources; that's why high-fan-out reset signals typically use global routing.

    If there are no timing violations for clock enable paths and you do not run out of global resources, then it does not matter whether the clock enable uses global routing. The global buffer has a big delay that can make setup timing worse than it would be with nonglobal routing. If you have setup violations for clock enable paths, then set "Global Signal" to "Off" for the clock enable in the Assignment Editor or turn off automatic global control signals in the "More Fitter Settings" dialog box. Also, synthesis might take your single divide-by-305 clock enable from your RTL and make lots of different smaller clock enable signals that include additional logic from the RTL; if this happens, you'll see multiple clock enables in the "Control Signals" table in the Fitter report. If synthesis creates multiple clock enable signals without a very large fan-out on any individual signal, then there would be no reason to use a global routing resource for any of these just to cut down on the amount of nonglobal routing used.

    --- Quote End ---

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    It is normal for a timing simulation to have glitches on signals if any combinational logic is involved. (I would not expect a glitch on a signal that is driven directly by a register.) As long as the signal is used only synchronously (as is the case for a clock enable) and there are no setup or hold violations, the glitches don't matter.

    Are you saying that the clock enable signal is listed correctly as a clock enable in the "Control Signals" table but just not listed as a global? If you are saying it was not listed at all, then did you look only for the name you expected? The name might be different from what you expect. If you are using HDL, you need to code in a style that enables synthesis to recognize the signal in the HDL that you intended to be a clock enable. The Quartus handbook has a chapter on coding guidelines that shows the correct way to code the registers that use the clock enable. If you are saying the signal was identified by the Fitter as a clock enable but just wasn't listed as global when you expected it to be global, then there might be a problem with your "Global Signal" assignment if that's what you are using (verify that the Assignment Editor doesn't have a question mark by the signal name). In TimeQuest you can do something like "report_timing -setup -from [get_registers <name_of_register_driving_clock_enable>]" to see the clock enable paths. For one of these, look at the data arrival path. If you are using global routing, there will be a clock control block in the path.
  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    The lpm_counter was configured as aload, therefore, the clk_en wasn't fed directly into any register..therefore it doesn't show up on the global signal list...funny thing is that if I don't use this clk_en, but using it directly as a divided down clock to fed to this lmp_counter. I have timing issues (setup time/removal time violations)...now it's not used as a clock anymore (rather, it is used as a clock_en) and I am trouble free?

    --- Quote Start ---

    It is normal for a timing simulation to have glitches on signals if any combinational logic is involved. (I would not expect a glitch on a signal that is driven directly by a register.) As long as the signal is used only synchronously (as is the case for a clock enable) and there are no setup or hold violations, the glitches don't matter.

    Are you saying that the clock enable signal is listed correctly as a clock enable in the "Control Signals" table but just not listed as a global? If you are saying it was not listed at all, then did you look only for the name you expected? The name might be different from what you expect. If you are using HDL, you need to code in a style that enables synthesis to recognize the signal in the HDL that you intended to be a clock enable. The Quartus handbook has a chapter on coding guidelines that shows the correct way to code the registers that use the clock enable. If you are saying the signal was identified by the Fitter as a clock enable but just wasn't listed as global when you expected it to be global, then there might be a problem with your "Global Signal" assignment if that's what you are using (verify that the Assignment Editor doesn't have a question mark by the signal name). In TimeQuest you can do something like "report_timing -setup -from [get_registers <name_of_register_driving_clock_enable>]" to see the clock enable paths. For one of these, look at the data arrival path. If you are using global routing, there will be a clock control block in the path.

    --- Quote End ---

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    I'm not sure what you're doing, but I repeat my earlier recommendation to change the asynchronous load to a synchronous load if a synchronous load would be suitable for your design. Another possibility is to replace your lpm_counter instantiation with a counter inferred from RTL similar to what you did for your counter that does the divide by 305, but an instantiated lpm_counter with a clock enable port should be fine.

    If you really need an asynchronous load, maybe you can use an lpm_latch megafunction (or a latch coded in RTL following the recommended coding style for latches) to capture the asynchronous data and feed the latch output to the synchronous load of lpm_counter. The lpm_latch megafunction will have a proper latch implementation in Quartus unlike the combinational feedback loops you are getting with the lpm_counter asynchronous load. (I did report this lpm_counter design deficiency for asynchronous load to Altera.)

    If you still have problems with a synchronous load, try configuring the counter with the MegaWizard in case you did something wrong when you instantiated lpm_counter directly without using the MegaWizard.