alex

Isim is driving me crazy

15 posts in this topic

I'm trying to simulate a DRAM design using the free Webpack ISE Simulator, basically used the MIG core generator to get a MCB core, downloaded the Verilog model of the DRAM chip from Micron, added some glue logic to tie it all together and when I try to simulate it, I get "This is a limited version of the ISE Simulator. The current design has exceeded the design size limit for this version and the performance of the simulation will be derated."

 

The simulation still runs, if you can call it that, but at about 1000th the normal speed. It literally takes 2 minutes to simulate 1uS, at this rate to get past the MCB core initialization, I estimate I'd have to run the simulator for a few days. I searched online for a solutions and I came across the FAQ at Xilinx which states "ISE Webpack - Performance Deration when exceeding 50,000 lines of HDL code". Great, that is fine, except I have counted all the lines in every single source file I have for this design, not just my code, but also the Verilog DDR model I downloaded, the config files to go with it and every single text source file that the wizard created for the memory core and everything together, including comments barely reaches 16000 lines. Yes the memory core is quite fat once you count the wrapper files generated for it by the MIG but even so I'm way below the 50K lines.

 

This has to be some kind of bug, has anyone come across this before? 

Share this post


Link to post
Share on other sites

I thought I'd re-use this old post of mine since isim is driving me crazy again. Perhaps it's because it's late and I'm tired, perhaps I'm slowly going mad or perhaps it's a bug in isim this time, but I've been staring at the code below and I can't explain it. Hoping one of you guys can clear things up, so run this piece of code in isim and see for yourselves. Perhaps this can be part of the puzzle that Hamster started a while back, though I have no prizes for you or even a correct answer.

library ieee;  use ieee.std_logic_1164.all;  use ieee.std_logic_unsigned.all;  use ieee.numeric_std.all;entity test_tb isend test_tb;architecture behavior of test_tb is   signal output   : std_logic := '0';  signal counter  : std_logic_vector(4 downto 0) := (others => '0');  constant period : time := 20 ns;begin  process  begin    wait for period;    counter <= counter + 1;  end process;  process(counter(1), counter(4))  begin    if (counter(4) = '0') then      output <= '0';    elsif rising_edge(counter(1)) then      output <= (not counter(3)) and counter(2);    end if;  end process;end;

When I simulate the code above I get the result in the top picture, even though the expected result should be as in the bottom picture where the output toggles high. It's as if the statement " if (counter(4) = '0' " always evaluates to true, since taking that out makes the rest work as expected, but clearly counter(4) is not always zero. You can think of counter(1) as the clock and counter(4) as the reset in the process. If however I increase the counter definition by 1 bit and make it std_logic_vector(5 downto 0) I get the expected result, even though the additional top bit is not used in the design and should not affect it. WTF isim! Please explain.

 

 post-29560-0-77501000-1364371981.jpg

Share this post


Link to post
Share on other sites

Hi Alex - I had a read and just threw my hands up - I hate async resets (even though it is really sync). Since the gauntlet has been thrown down I will fire up ISIM....

Share this post


Link to post
Share on other sites

Hi Alex - very odd.

 

A working theory here. The scheduling of changes in ISIM reflect the order that the bits are updated by the addition? I can't really see what is going on - have you tried single-stepping it?

Share this post


Link to post
Share on other sites

I should have said I'm using version 14.3 of the tools. would be interesting if someone tried it on a different version as well.

This is not just an idle problem to pass the time. It is actually a real world problem I came across while simulation the galaxian game recently. It appears in the simulator it is not producing the hsync signal. If I increase the h counter by one bit it then produces hsync but since the counter now counts twice as long the hsync timing is off. I'm pretty sure this used to work in older versions of the tools.

Share this post


Link to post
Share on other sites

Alex - I just ran your code in ISIM 13.3 and I get the same result: output is always 0.

 

When I single step through the 2nd process, here's what I observe:

1. The process is being triggered as expected when either counter(1) or counter(4) changes

2. Oddly, the elsif rising_edge(counter(1)) then conditional test is NEVER reached when counter(4) = '1'.

   That is, the process is suspended immediately after the failed if (counter(4) = '0') then conditional test???

 

I simulated your testbench using ModelSim PE 10.1 and the simulation works as you expected, as shown below.

 

Well, I tried to post the image but got a popup that said "You are not allowed to use that image extension on this community." I pasted the screen clip from Windows clipboard. How do I post images in a reply as you did?

Share this post


Link to post
Share on other sites

OK, so I made a few code mods and ran some tests. What I found is that the rising_edge() function in ISIM doesn't appear to work correctly when the argument is a bit in a standard_logic_vector (or maybe for any vector, as I didn't try bit_vector).

 

I added four new processes, each slight modifications to the original to see which would detect the rising edge of counter(1). I also added a boolean signal, counter_1_re_x, that replicates the counter(1) trigger expressions in each of the 5 processes. The activity of these boolean signals gives a direct indicator of whether the process where the corresponding expressions are used will be triggered when desired. Here's what I found.

 

Original

'output' is always 0, which is the original problem. Signal 'counter_1_re_orig' only toggles when counter(4) is low??? Thus the output is consistent with this behavior.

 

 

Parentheses

This test just wrapped 'counter(1)' in parentheses inside of the rising_edge() function call. The thought was to have the bit fully 'evaluated' before the function call was made. It was really just a stab in the dark, since this is such an odd problem.

 

'output_parens' is always 0, which was expected. The boolean signal, 'counter_1_re_parens' has the same behavior as 'counter_1_re_orig'.

 

 

Alias

This test aliased 'counter(1)' to 'counter_1_alias'. 'counter_1_alias' replaces 'counter(1) in the process sensitivity list and in the rising_edge('counter_1_alias') function call. As with the parentheses, the thought was to have the bit fully 'evaluated' before the function all was made. Another stab in the dark, since this is such an odd problem.

 

'output_alias' is always 0. The boolean signal, 'counter_1_re_alias' has slightly different behavior from 'counter_1_re_orig', which is odd. It's hard to describe, so you'll have to run the sim until I figure out how to add images in a post.

 

 

Assign

This test assigned 'counter(1)' to 'counter_1_assign', 'counter_1_alias' replaces 'counter(1) in the process sensitivity list and in the rising_edge('counter_1_alias') function call. My suspicion was that form some reason the rising_edge() function has a bug when the passed parameter is part of a vector.

 

'output_assign' is correct!!! The boolean signal, counter_1_re_assign' <= rising_edge(counter_1_assign), changes on every edge of 'counter(1)' as expected. It is true when counter(1) is high and false when it is low.

 

 

Classic

This test uses the classic elsif counter(1)'event and counter(1)='1' then in the process instead of using the rising_edge() function call.

 

'output_classic' is correct!!! The boolean signal, counter_1_re_assign' <= rising_edge(counter_1_assign), has the same behavior as the Assign test described previously.

 

 

There appears to be something amiss with the rising_edge() function in the Xilinx library or the way in which ISIM evaluates the function. In either case it seems to be quite a serious error.

 

I ran the simulation using ModelSim 10.1 and all 5 test cases have the same (and correct!) output values. I grudgingly pay the annual maintenance cost for ModelSim each year because I rely heavily on simulations for my customer's projects. ModelSim is the standard for HDL simulators and it delivers for me. I realize that for many on this forum ModelSim isn't an economically viable option. I do know that Xilinx' internal IP development team uses ModelSim to validate their designs, not ISIM - and that says a lot.

 

If you will be using ISIM to validate your design, it appears you'll have to modify the code to change all rising_edge() and falling_edge() function instances to foo'event and foo = '1' (or '0'). That shouldn't be too difficult and isn't a bad price to pay for a free mixed mode simulator.

 

Another option would be to use an open source simulator such as GHDL. I don't have any experience with this tool, but I've read good things about it.

 

 

library ieee;  use ieee.std_logic_1164.all;  use ieee.std_logic_unsigned.all;entity test_tb isend test_tb;architecture behavior of test_tb is  signal output   : std_logic := '0';  signal counter  : std_logic_vector(4 downto 0) := (others => '0');  constant period : time := 20 ns;  -- add stuff to test rising_edge() function  signal output_parens  : std_logic := '0';  signal output_alias   : std_logic := '0';  signal output_assign  : std_logic := '0';  signal output_classic : std_logic := '0';  alias  counter_1_alias      : std_logic is counter(1);  signal counter_1_assign     : std_logic;  signal counter_1_re_orig    : boolean := false;  signal counter_1_re_alias   : boolean := false;  signal counter_1_re_parens  : boolean := false;  signal counter_1_re_classic : boolean := false;  signal counter_1_re_assign  : boolean := false;begin  process  begin    wait for period;    counter <= counter + 1;  end process;  process(counter(1), counter(4))  begin    if (counter(4) = '0') then      output <= '0';    elsif rising_edge(counter(1)) then      output <= (not counter(3)) and counter(2);    end if;  end process;  -- wrap counter(1) in parentheses to insure  -- it is evaluated and doesn't remain part of a vector  p_counter_1_re_parens:  process(counter(1), counter(4))  begin    if (counter(4) = '0') then      output_parens <= '0';    elsif rising_edge((counter(1))) then      output_parens <= (not counter(3)) and counter(2);    end if;  end process p_counter_1_re_parens;  -- use alias to counter(1) so that a part  -- of a vector isn't passed to rising_edge()  p_counter_1_re_alias:  process(counter_1_alias, counter(4))  begin    if (counter(4) = '0') then      output_alias <= '0';    elsif rising_edge(counter_1_alias) then      output_alias <= (not counter(3)) and counter(2);    end if;  end process p_counter_1_re_alias;  -- assign counter(1) to separate signal so  -- it is no longer a part of a vector  p_counter_1_re_assign:  process(counter_1_assign, counter(4))  begin    if (counter(4) = '0') then      output_assign <= '0';    elsif rising_edge(counter_1_assign) then      output_assign <= (not counter(3)) and counter(2);    end if;  end process p_counter_1_re_assign;  -- use the classic 'longhand' rising edge test  -- instead of the rising_edge() function  p_counter_1_re_classic:  process(counter(1), counter(4))  begin    if (counter(4) = '0') then      output_classic <= '0';    elsif counter(1)'event and counter(1)='1' then      output_classic <= (not counter(3)) and counter(2);    end if;  end process p_counter_1_re_classic;  -- test all of the rising edge methods  counter_1_assign      <= counter(1);   counter_1_re_orig     <= rising_edge(counter(1));  counter_1_re_parens   <= rising_edge((counter(1)));  counter_1_re_alias    <= rising_edge(counter_1_alias);  counter_1_re_assign   <= rising_edge(counter_1_assign);  counter_1_re_classic  <= true when counter(1)'event and counter(1)='1' else false;end;

Share this post


Link to post
Share on other sites

Hey Urbite, thank you very much for taking the time to look into this. I absolutely didn't expect this outcome, to be honest I sort of expected someone to come back to me and point out some really silly mistake I made.

 

I agree with you in that this is quite a serious issue for anyone doing simulation validation. I recall seeing somewhere online an article advising that rising_edge/falling_edge is preferable to the classic 'event driven clock evaluation. Since I don't pay for the Xilinx s/w as I use the free webpack edition I doubt I can submit a bug report easily, at most I can try opening a web case with them which will be treated as low priority since I'm not a paying customer.

Share this post


Link to post
Share on other sites

Looking at the implementation of rising_edge inside the Xilinx ieee library it appears to be defined as:

    CONSTANT cvt_to_x01 : logic_x01_table := (                         'X',  -- 'U'                         'X',  -- 'X'                         '0',  -- '0'                         '1',  -- '1'                         'X',  -- 'Z'                         'X',  -- 'W'                         '0',  -- 'L'                         '1',  -- 'H'                         'X'   -- '-'                        );      FUNCTION To_X01  ( s : std_ulogic ) RETURN  X01 IS    BEGIN        RETURN (cvt_to_x01(s));    END;    FUNCTION rising_edge  (SIGNAL s : std_ulogic) RETURN BOOLEAN IS    BEGIN        RETURN (s'EVENT AND (To_X01(s) = '1') AND (To_X01(s'LAST_VALUE) = '0'));    END;

The conversion to X01 does not seem to be the cause of the problem so for testing purposes it can be taken out so the function becomes (after adding a report for debugging):

function rising_edge (signal s : std_ulogic) return boolean isbegin  report "s'event=" & boolean'image(s'event) & " s=" & std_ulogic'image(s) & " s'last_value=" & std_ulogic'image(s'last_value);  return (s'event and (s = '1') and (s'last_value = '0') );end;

When run, we get:

Finished circuit initialization process.at 320 ns(2): Note: s'event=false s='0' s'last_value='0' (/test_tb/).at 360 ns(1): Note: s'event=true s='1' s'last_value='1' (/test_tb/).at 400 ns(1): Note: s'event=true s='0' s'last_value='1' (/test_tb/).at 440 ns(1): Note: s'event=true s='1' s'last_value='1' (/test_tb/).at 480 ns(1): Note: s'event=true s='0' s'last_value='1' (/test_tb/).at 520 ns(1): Note: s'event=true s='1' s'last_value='1' (/test_tb/).at 560 ns(1): Note: s'event=true s='0' s'last_value='1' (/test_tb/).at 600 ns(1): Note: s'event=true s='1' s'last_value='1' (/test_tb/).at 640 ns(1): Note: s'event=true s='0' s'last_value='1' (/test_tb/).at 960 ns(2): Note: s'event=false s='0' s'last_value='0' (/test_tb/).at 1 us(1): Note: s'event=true s='1' s'last_value='1' (/test_tb/).

So it's clear that for some reason the 'last_value attribute is not updated properly inside the function. Also note that for some reason the 'last_value attribute is only updated at the rising edge of counter(4) at 320ns, 960ns, etc, and of course counter(4) acts as a reset in this design. Also when increasing the counter SLV by one or more bits, the report output above shows that 'last_value is always zero which explains why the code then works as expected even though 'last_value still doesn't update properly inside the function.

 

If the rising_edge function is modified to only test for "return (s'event and (s = '1'));" then is works as expected always. Also the weird thing is that the code below works as expected too, so all we do here is move the guts of the function directly in the elsif conditional test. Adding a report in this process shows that 'last_value toggles properly as expected.

p_classic: process(counter(1), reset)begin  if (reset = '1') then    output_classic <= '0';  elsif counter(1)'event and ( counter(1) = '1' ) and ( counter(1)'last_value = '0' ) then    output_classic <= '1';  end if;end process;

So the lesson here at the end of the day is, for simulation, don't call rising_edge and falling_edge with a parameter that is an index into an SLV, even though the result should be a SL. Always call them with a parameter that is defined as an SL that has been assigned elsewhere accordingly.... or use the classic notation.

 

 

Share this post


Link to post
Share on other sites

Alex, I didn't expect my initial simulation of your code to yield such a strange result either. It's hard to imagine that a funciton expecting a scalar argument would handle a bit from a vector differently (and incorrectly!). Apparently a vector of length 1 isn't the same as a scalar signal???

OK, now on to the important stuff.

 

1. How can I include inline images in my post, as you did, without saving them externally and providing a url? Do I have to reach a minimum post count first?
2. What code format did you use to get decent colorization? As you can see, the colorization in my code snippet gets out of sync at times.

Share this post


Link to post
Share on other sites

To include pictures, hit the "more reply options" button (next to the "post" button) to bring up the advanced options, then at the bottom you have the "attach files" button now. Attach a jpg or png then when it shows at the bottom as a thumbnail, click the "add to post" at the far right of the thumbnail.

 

For code colorization, I did nothing, just paste the code text, highlight it and click the "code" button on the top toolbar, the colorization was automatic. EDIT: I see what you mean by out of sync, the reason for that is mismatched single and double quotes. There's nothing you can do about that short of adding another quote to cancel the effect but that makes the code syntax barf if someone was to cut paste the code from the forum into the Xilinx tools.

 

Share this post


Link to post
Share on other sites

Here's a blog post on the difference between the rising_edge() function and (foo'edge and foo='1'). Of course you've already found the root cause of the problem, but thought you might like this for reference. On thing that was pointed out in some followup comments is that rising_edge() will fail if the edge sensitive signal is created by a pullup or pulldown and open drain driver. You wouldn't be simulating this in an FPGA - long lines ant TBUFs are long gone from FPGAs. But if you were using ISIM to do a sim of some legacy board level logic, this might be an issue.

 

http://vhdlguru.blogspot.com/2010/04/difference-between-risingedgeclk-and.html

Share this post


Link to post
Share on other sites

i simulated a coed in ISIM and it was working fine, however the moment i do any kind of editing which can also be adding a comment, the code stops running and i get all the outputs as uninitialized. I am using 2 ip cores: fifo and a DPRAM. Could anybody please tell me the reason for it?

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