2

I am trying to write a very simple module with two integer inputs and one select input. When select is 0 output should be sum of inputs and when select is 1 output should be difference of them. I will verify the module with a simple test bench using GHDL. The module doesn't have to be synthesizable.

My first attempt is as follows.

entity alu is
  port (
    a   : in  integer;                  -- first input
    b   : in  integer;                  -- second input
    y   : out integer;                  -- result
    sel : in  bit);                     -- if sel = 0 y = a+b,  sel = 1 y = a-b

end entity alu;

architecture behav of alu is

begin  -- architecture behav

  -- purpose: calculate the output
  -- type   : combinational
  -- inputs : a, b, y
  -- outputs: y
  compute: process (sel, a, b) is
  begin  -- process compute
    if sel='0' then
      y <= a + b;
    else
      y <= a - b;
    end if;
  end process compute;

end architecture behav;

The problem is GHDL gives overflow error because as far as I understand sum of two integers can't fit into another integer.

How can I define a type which has range enough to keep results? My first try is as follows. However in that case I should define '+' and '-' operators for new type.

type big_int is range 2 * integer'low  to 2 * integer'high;

Since the required range is wider than integer, I can't use subtype definition. If I were able to define a subtype, I could use '+' and '-' operators defined for integers without redefining them.

EDIT 1:

For those who wonder test bench and exact error, here is the test bench which is semi automatically generated using EMACS vhdl-mode.

library ieee;
use ieee.std_logic_1164.all;

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

entity alu_tb is

end entity alu_tb;

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

architecture test of alu_tb is

  -- component ports
  signal a   : integer;
  signal b   : integer;
  signal y   : integer;
  signal sel : bit;

  -- clock
  signal Clk : std_logic := '1';

begin  -- architecture test

  -- component instantiation
  DUT: entity work.alu
    port map (
      a   => a,
      b   => b,
      y   => y,
      sel => sel);

  -- clock generation
  Clk <= not Clk after 10 ns;

  -- waveform generation
  WaveGen_Proc: process
  begin
    a <= 24;
    b <= 46;
    sel <= '0';
    wait for 20 ns;

    sel <= '1';
    wait for 20 ns;

    wait;

  end process WaveGen_Proc;



end architecture test;

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

configuration alu_tb_test_cfg of alu_tb is
  for test
  end for;
end alu_tb_test_cfg;

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

Here is the exact error from GHDL:

C:\GHDL\bin\ghdl.exe:error: overflow detected
  from: process work.alu(behav).compute at q9_alu.vhd:21
C:\GHDL\bin\ghdl.exe:error: simulation failed

Line 21 corresponds to

y <= a + b;

in source file.

EDIT 2:

About my GHDL:

ghdl -v

GHDL 0.35 (tarball) [Dunoon edition]
 Compiled with GNAT Version: GPL 2017 (20170515-63)
 mcode code generator
Written by Tristan Gingold.

Copyright (C) 2003 - 2015 Tristan Gingold.
GHDL is free software, covered by the GNU General Public License.  There is
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
ayazar
  • 145
  • 1
  • 11
  • 1
    You are likely having issue with default values for the actuals of a and b that aren't constrained or initialized. Those would have an initial value of integer'left. If sel also does not have an initial value it will be 'U' and you will subtract b from a and underflow. You can constrain your integers and expand the sum/diffs to match a wider range for y, you can make your if statement 'U' proof (elsif or to_01) or you can use a numeric_std type (signed) which will rollover/rollunder (integer has no bits, it has a value range). Show a and b actual signal declarations. –  Jan 19 '18 at 08:01
  • Use ranged subtypes of integer. For example if your inputs are ranged 0 to 100 your output is bounded by 0 and 200. –  Jan 19 '18 at 12:08
  • Why not use `numeric_std` and `unsigned`/`signed`? See this answer: https://stackoverflow.com/questions/13442822/how-to-represent-integer-greater-than-integerhigh – DLnd Jan 19 '18 at 12:18
  • You need to give the exact error message and the testbench. VHDL is entirely rational about this (like most languages) - the operator you get is the operator you ask for, and the language doesn't care about what you consider to be overflow. Verilog, OTOH, will resize operators to try to second-guess your intent. – EML Jan 21 '18 at 13:01
  • @DLnd I just wondered whether a solution in VHDL exists or not. Those type may be correct but I am trying to learn VHDL language itself. – ayazar Jan 24 '18 at 05:47
  • @EML Please check the original message. I added test bench and exact error message under **EDIT 1**. – ayazar Jan 24 '18 at 05:48
  • 1
    @user1155120 I doubt that sel which is a bit can take 'U' value. My goal is just understand VHDL language without using IEEE types. – ayazar Jan 24 '18 at 05:50
  • @Alper I don't believe there's the solution you wish for inside VHDL, unless by implementing your own, larger type and casting operators to/from integer. Note that using 'just' VHDL, without IEEE libaries might be considered an exercise in futility for most people, since they are universally used for synthesis and non-synthesis tasks. It's like wanting to use C without using the standard library - sure, you can do it, but there's a high risk it will teach you bad coding style, and let you miss out on knowledge of powerful standard tools. – DLnd Jan 24 '18 at 07:26
  • Re: sel is type bit. Oops! –  Jan 24 '18 at 16:12
  • @DLnd Thanks for your advice. – ayazar Jan 25 '18 at 05:42

2 Answers2

2

With the testbench provided sel = '0' implicit intial value addition occurs in the first simulation cycle resulting in an error - ghdl is actually correct here.

The overflow error is caused by the implicit initial values for a and b which are integer'LEFT just as sel's value comes from bit'LEFT.

The cause of the error

IEEE Std 1076-2008, 5.2.3.1:

The same arithmetic operators are predefined for all integer types (see 9.2). It is an error if the execution of such an operation (in particular, an implicit conversion) cannot deliver the correct result (that is, if the value corresponding to the mathematical result is not a value of the integer type).

This is the same error as constrained range scalars violating their bounds. It's a run time error.

Where the implicit initial values come from

See 6.4.2.3 Signal declarations:

In the absence of an explicit default expression, an implicit default value is assumed for a signal of a scalar subtype or for each scalar subelement of a composite signal, each of which is itself a signal of a scalar subtype. The implicit default value for a signal of a scalar subtype T is defined to be that given by T'LEFT.

T'LEFT for integer is specified in package std.standard (16.3) for both type integer and type bit (both scalars). Type integer is declared with rising range and the largest magnitude negative number (integer'low) is the integer'LEFT value. (Also see 5.2.3.2 Predefined integer types).

The error isn't caught by all simulators

For an llvm back end version of ghdl-0.36dev

/usr/local/bin/ghdl -a alu_tb.vhdl
/usr/local/bin/ghdl -e alu_tb_test_cfg
/usr/local/bin/ghdl -r alu_tb_test_cfg --stop-time=70ns   --wave=alu_tb_test_cfg.ghw
./alu_tb_test_cfg:info: simulation stopped by --stop-time

And that gives us:

alu_tb_test_cfg.png

Where the alu does not generate the error at run time (like EML's Questa/Modelsim).

Testing different ghdl versions demonstrates the mcode backend versions of ghdl perform the error checking while others do not.

We also see that determining exactly what was going on required the ability to duplicate the problem using the testbench.

How to deal with this?

Having some simulators that catch the error and some that don't is a portability issue.

There are a couple of things you could do:

  • Provide initial values for a and b that don't cause an overflow.

  • Don't allow an operation that will overflow to occur until operand values are presented that won't overflow. In this case you could have an initial value of '1' for sel. A negative number subtracted from itself is zero including when both a and b are integer'LEFT.

An easy fix here in the testbench would be to change the declaration of sel to include an initial value of '1' which would invoke subtraction:

  signal sel : bit := '1';  -- CHANGED, added initial value for subtraction

Making this change results in virtually the same waveform in the mcode version of ghdl as we see in the llvm version (a transition from '1' to '0' at time zero is the only difference you can see you).

Providing initial values for a and b would eliminate the bus signal change 'crosses' at time 0.

For pure behavioral models you could add another condition requiring a simulation cycle to have occurred before adding or subtracting. You could also use the other form of sensitivity list (using wait statements) and wait for 0 ns; to not proceed until there are valid values on a and b.

Clocked systems naturally block operations until the rising edge of a clock (which you can stop from occuring in the first simulation cycle).

What about declaring a bigger integer?

You can't declare a new integer type whose range bounds can't be expressed in values contained within range of the largest integer type. The only predefined integer type is type integer (5.2.3.2 Predefined integer types). This limitation is based on 5.2.3.1:

Integer literals are the literals of an anonymous predefined type that is called universal_integer in this standard. Other integer types have no literals. However, for each integer type there exists an implicit conversion that converts a value of type universal_integer into the corresponding value (if any) of the integer type (see 9.3.6).
...
The same arithmetic operators are predefined for all integer types (see 9.2). It is an error if the execution of such an operation (in particular, an implicit conversion) cannot deliver the correct result (that is, if the value corresponding to the mathematical result is not a value of the integer type).

It wouldn't be a cure either. What's to stop someone from performing operations with a new type and get bitten the same way?

  • I verified that setting initial value of `sel`in TB to '0' solves the problem and to '1' creates the same problem. But I didn't understand how simulator behaves completely. Is it failing before any assignment in `WaveGen_Proc`in TB done? According to book, BTW I am following 2nd edition, at initialization phase with initial values each process is activated. Therefore before TB process, ALU process adds two integer'low values and this causes an error. Just wondering, do all processes run once at initialization phase independent from their sensitivity list? How should I "imagine" simulator? – ayazar Jan 25 '18 at 06:35
  • I checked IEEE Std 1076-2008, section 5.2.3.2 and its neighbourhood couldn't be able to find a restriction on bigger integer. The only predefined integer type is INTEGER but how can we say that this is the upper bound from standard? At end of 5.2.3.1, it says "However, an implementation shall allow the declaration of any integer type whose range is wholly contained within the bounds –2147483647 and +2147483647 inclusive." but I think it doesn't imply an upper bound. – ayazar Jan 25 '18 at 10:08
  • Thank you for answers. I don't want to take your time more but I am not clear yet, this may be because I am very unfamiliar with details of VHDL and standard. Part 9.5 says that result shall lie within it range of the integer type with widest allowable integer. But does it mean that the predefined integer type (which is an integer type) has the widest range? Maybe predefined integer type given by implementation to us has narrower range than maximum allowable range? How can we make sure that widest integer range is same as range of THE integer? – ayazar Jan 26 '18 at 05:57
  • What about `big_int` type definition in my original post, isn't it legal? GHDL didn't give error with this definition. I could change type of ALU output port to `big_int`. The reported problem was undefined `+` and `-` operators not type definition. – ayazar Jan 26 '18 at 10:48
  • An email has been sent to the Original Poster (OP) responding to his comments. –  Jan 26 '18 at 23:01
0

EDIT: See how much easier it is with a MCVE? (Minimum Complete/Compilable Verifiable Example)

Now just add

 report "Sel " & bit'image(sel) & ": y is " & integer'image(y) severity Note;

after each Wait ...

ghdl (after adding the statement below after each wait(time) reports:

alu_tb.vhd:43:5:@20ns:(report note): Sel '0': y is 70
alu_tb.vhd:47:5:@40ns:(report note): Sel '1': y is -22

(and never terminates because the TB clock keeps running. Compile with --std=08 and call std.env.Stop instead of the plain Wait)

ghdl --version
GHDL 0.35-dev (2017-03-01-252-g90f4fd21-dirty) [Dunoon edition]
Compiled with GNAT Version: 6.3.0
GCC back-end code generator Written by Tristan Gingold.

I wonder where you got your ghdl and which version it is?

Go to github.com/ghdl/ghdl and get a newer one. (OK you were using 0.35, see below)

ghdl is one of the most closely conforming (to the VHDL standard) simulators around, and now supports a pretty good subset of VHDL-2008.

Original answer below; its info is generally useful but not the specific problem in the question.


EDIT again, now we have the tool version information.

GHDL can compile VHDL to 3 different code generation backends; gcc, llvm, and its own JIT code generator, mcode. Each has its own advantages and disadvantages.

GCC can produce highly optimised code, and can leverage the gcov tools for code coverage analysis.

LLVM (also used by the Clang compiler) also offers good code performance, and it's considerably lighter weight than GCC.

Mcode is a much simpler code generator, so it generates code fast, but the code itself is a little slower (in my experience, less than 2:1 so still pretty good). But it is tailor made for VHDL so it implements runtime checks more thoroughly than the others.

I've seen it catch real errors - overflows between Time calculations (in 64 bits) and Integer (in 32 bits) to setup a delay counter, that passed silently through every other tool I tried, generating incorrect counts (and delays much shorter than requested). Including synthesis tools. Imagine: "You have 30 seconds until detonation".

Of the tools I have been able to try, only ghdl (mcode) caught this error.

What happens in your testcase is that, because you use Integer, and don't explicitly initialise, the Integers are initialised to Integer'Left, or -2^^31. And adding 2 of these will overflow.

Initalising either of A or B (or both) to 0, your code runs with ghdl mcode version too.

  signal a   : integer := 0;

Don't take my word for this. Having tested the above fix, try this experiment.

a <= -2**31;
b <= 46;

Now the addition should work, but the subtraction should either hit an overflow error OR produce a large NEGATIVE number. Any other outcome is unacceptable.

ghdl (gcc) : FAILS this test : second value is POSITIVE

ghdl -r alu_tb
alu_tb.vhd:43:5:@20ns:(report note): Sel '0': y is -2147483602
alu_tb.vhd:47:5:@40ns:(report note): Sel '1': y is 2147483602

ghdl (mcode) : correctly reports an error

ghdl -r alu_tb
alu_tb.vhd:43:5:@20ns:(report note): Sel '0': y is -2147483602
ghdl:error: overflow detected
  from: process work.alu(behav).compute at alu.vhd:23
ghdl:error: simulation failed

I'd be curious to see the results from other simulators, e.g. an update to the other answer (currently deleted). It would be possible for some to go beyond what the standard requires and offer 64-bit integer, but I don't know of any.

IMO silent overflow generating wrong results is a bad thing, but that seems to be a minority opinion!


OK I'll expand on my comment :

Use ranged subtypes of integer. For example if your inputs are ranged 0 to 100 your output is bounded by 0 and 200.

For example,

subtype in_type is natural range 0 to 100;
subtype sum_type is natural range 0 to 200;

One characteristic of subtypes is that their default value, e.g. intypeleft, is the first value in the range, i.e. 0 above, rather than -2**31, the default value forInteger`.

Thus ranged subtypes explicitly avoid the problem user1155120 pointed out in his comment, which is the actual source of your overflow.

Then, an appropriately ranged type for the sum, as above, avoids the very possibility of overflow - correct by construction, avoiding a host of difficult to test cases.

But notice the word growth : you normally can't afford to propagate increasing word widths infinitely far through the design. But at least you can control how and when narrowing occurs, e.g. via explicit out-of-range checks, or saturating arithmetic, or truncation/rounding.

There is nothing wrong with Integer in synthesis, but ranged subtypes have the added advantage of allowing synthesis to generate the right size of hardware ( 7 bit inputs, 8 bit output here instead of possibly 32 bits).

So when would you want to use numeric_std.signed (or unsigned)? Three main reasons:

  1. You need access to the individual bits (std_logic signals )for some reason, e.g. shifts or logical operations
  2. You need to interchange with std_logic_vector signals, e.g. across a bus. Note that ranged naturals can be used as ports, e.g. to address an internal memory - but you normally need std_logic_vector at external ports so that individual bits map to individual pins.
  3. You need to overcome the range limitation on Integer and related types - generally still 32 bits. Unsigned can be declared to cover any required word width - 47 bits if you want.
  • My goal is just to explore VHDL "language itself". So in practice, I agree that using numeric_std may be better way to go. However I am reading a VHDL book (The Designer's Guide to VHDL). An exercise question asks for a module that takes two integer and sum/diff them. So I want to solve this problem without using numeric_std type. Defining inputs subtype of integer solves problem but it limits range of inputs narrower than integer. I don't want to narrow down input ranges instead try to enlarge output range. For this question, I am not interested in synthesis but I got your point. Thank you. – ayazar Jan 24 '18 at 05:56
  • My version `ghdl -v GHDL 0.35 (tarball) [Dunoon edition] Compiled with GNAT Version: GPL 2017 (20170515-63) mcode code generator Written by Tristan Gingold. Copyright (C) 2003 - 2015 Tristan Gingold. GHDL is free software, covered by the GNU General Public License. There is warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.` – ayazar Jan 25 '18 at 06:42
  • ah! and indeed the mcode backend is stricter, catching more overflows (correctly!) than even some professional simulators. David's initial comment is correct : the overflow is real, at T=0, before simulation starts. Editing answer... –  Jan 25 '18 at 11:15
  • Wrote the whole darn thing, posted it' and THEN saw Dave had scooped me. Oh well :-) –  Jan 25 '18 at 12:04