0
\$\begingroup\$

EDIT: Found a solution. See the bottom of the post

I am wondering why my simulation of the following entity fails to run for more than one input. When I change the inputs, the system crashes, and I don't understand why.

I have the following entity:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity subtraction is
    port (
        signal clk : in std_logic;
        signal minuend : in std_logic_vector(15 downto 0);
        signal subtrahend : in std_logic_vector(15 downto 0);
        signal difference : out std_logic_vector(15 downto 0)
    );
end subtraction;

architecture datapath of subtraction is

type ROM_ARRAY is array (0 to 9) of std_logic_vector(3 downto 0);
constant ROM: ROM_ARRAY := 
    (
    x"0",
    x"1",
    x"2",
    x"3",
    x"4",
    x"5",
    x"6",
    x"7",
    x"8",
    x"9"
    );

constant thousand : unsigned(11 downto 0) := x"3E8";
constant hundred : unsigned(7 downto 0) := x"64";
constant ten : unsigned(3 downto 0) := x"A";

signal m, s, d: unsigned(14 downto 0);
signal d0, d1, d2, d3 : unsigned(14 downto 0);

begin

m <=    resize(unsigned(minuend(15 downto 12)) * thousand, m'length) + 
        resize(unsigned(minuend(11 downto 8)) * hundred, m'length) +
        resize(unsigned(minuend(7 downto 4)) * ten, m'length) +
        resize(unsigned(minuend(3 downto 0)), m'length);
        
s <=    resize(unsigned(subtrahend(15 downto 12)) * thousand, s'length) + 
        resize(unsigned(subtrahend(11 downto 8)) * hundred, s'length) +
        resize(unsigned(subtrahend(7 downto 4)) * ten, s'length) +
        resize(unsigned(subtrahend(3 downto 0)), s'length);

--d <=  m - s;

d0 <= ((m-s) mod 10); -- (m-s) can be replaced with d if uncommented above
d1 <= ((((m-s) mod 100) - ((m-s) mod 10)) / 10);
d2 <= ((((m-s) mod 1000) - ((m-s) mod 100) - ((m-s) mod 10)) / 100);
d3 <= ((((m-s) mod 10000) - ((m-s) mod 1000) - ((m-s) mod 100) - ((m-s) mod 10)) / 1000);

--difference <= ROM(to_integer(d3))&ROM(to_integer(d2))&ROM(to_integer(d1))&ROM(to_integer(d0));

process(clk)
begin
    difference <= ROM(to_integer(d3))&ROM(to_integer(d2))&ROM(to_integer(d1))&ROM(to_integer(d0));
end process;   

end datapath;

The code might look a bit complicated, but it's actually quite simple. I'm just taking a Binary Coded Decimal input (of up to 4 digits) and subtracting another BCD input of the same size from it, and then outputting the difference in BCD (which is what the vector array is for).

Some of this is just to practice using certain things I'm learning about VHDL. So please forgive my inelegance.

At any rate, the code works perfectly for one input. But then fails the moment I change the inputs. This is true whether I use a process to handle assignment of a the final value to difference, or if I use the purely combinational logic approach (which is currently commented out).

The testbed is simple, and shows that I'm just looking at the output for three sample inputs. Removing the difference assignment and the d0 through d3 assignments allows the code to continue through the testbed (and this is verifiable if you add internal testing-only signal to the testbench, or otherwise expose the values in m, s, and (optionally) d).

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity subtraction_tb is
--  Port ( );
end subtraction_tb;

architecture Behavioral of subtraction_tb is

signal clktb : std_logic;
signal mtb, stb, dtb : std_logic_vector(15 downto 0);
signal moutb, soutb,doutb : std_logic_vector(14 downto 0);
--signal m0tb, m1tb, m2tb, m3tb : std_logic_vector(14 downto 0);

begin

uut : entity work.subtraction
    port map (
    clk => clktb,
    minuend => mtb,
    subtrahend => stb,
    difference => dtb
    );

clock : process
begin
    clktb <= '1';
    wait for 5 ns;
    clktb <= '0';
    wait for 5 ns;
end process;

testing : process
begin
    mtb <= x"1430";
    stb <= x"0430";
    wait for 10 ns;
    mtb <= x"9450";
    stb <= x"9320";
    wait for 10 ns;
    mtb <= x"9000";
    stb <= x"8111";
    wait for 10 ns;
wait;
end process;

end Behavioral;

I am frankly confused why the "d" assignments create this mischief in my entity. In purely combinational terms, and in comparison to other projects I've done, everything looks above aboard.

Any insight would be greatly appreciated.

EDIT TO ADD ERROR DETAILS:

As a few people have asked for simulator and error details. The simulator is Xilinx Vivado, there is no error. Rather, the simulation runs, and displays output for the first entry. But it shows only the output for that entry. It's as if the testbed had only up to the first wait for 10 ns statement, and then stops, displaying nothing at all after 10 ns.

It does automatically go from the waveform back to the code, and an arrow appears pointing at the line where I assign the difference, however, that arrow, the console, and elaborate.log contain no message indicating what might be happening. It simply does this thing I've described.

If this isn't clear enough, I will find time to post the waveform later, to demonstrate what I mean.

Here are the waveforms:

Waveform

Arrow to line

More Additions:

I have been trying to account for the possibility that the simulator does not like the addition I'm doing, which could result in a value larger than the vector size I've tried. To that end, here is one example of an attempted solution which results in utter nonsense at the output.

Code:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity bdc_subtraction is
    port (
        --Internal Testing Signals
        signal mout, sout: out std_logic_vector(18 downto 0);
        signal dout : out std_logic_vector(18 downto 0);
        --signal m0t, m1t, m2t, m3t, s0t, s1t, s2t, s3t : out std_logic_vector(14 downto 0);
        signal m0t : out std_logic_vector(15 downto 0);
        signal m1t : out std_logic_vector(15 downto 0);
        signal m2t : out std_logic_vector(15 downto 0);
        signal m3t : out std_logic_vector(15 downto 0);
        signal s0t : out std_logic_vector(15 downto 0);
        signal s1t : out std_logic_vector(15 downto 0);
        signal s2t : out std_logic_vector(15 downto 0);
        signal s3t : out std_logic_vector(15 downto 0);
        --Inputs and outputs
        signal clk : in std_logic;
        signal minuend : in std_logic_vector(15 downto 0);
        signal subtrahend : in std_logic_vector(15 downto 0);
        signal difference : out std_logic_vector(15 downto 0)
    );
end bdc_subtraction;

architecture datapath of bdc_subtraction is

type ROM_ARRAY is array (0 to 9) of std_logic_vector(3 downto 0);
constant ROM: ROM_ARRAY := 
    (
    x"0",
    x"1",
    x"2",
    x"3",
    x"4",
    x"5",
    x"6",
    x"7",
    x"8",
    x"9"
    );

constant thousand : unsigned(11 downto 0) := x"3E8";
constant hundred : unsigned(7 downto 0) := x"64";
constant ten : unsigned(3 downto 0) := x"A";

signal m, s: unsigned(18 downto 0);

signal m0 : unsigned(15 downto 0);
signal m1 : unsigned(15 downto 0);
signal m2 : unsigned(15 downto 0);
signal m3 : unsigned(15 downto 0);

signal s0 : unsigned(15 downto 0);
signal s1 : unsigned(15 downto 0);
signal s2 : unsigned(15 downto 0);
signal s3 : unsigned(15 downto 0);

signal d0, d1, d2, d3 : unsigned(14 downto 0);
signal d : unsigned(18 downto 0) := (others => '0');

begin
--Testing Signals
mout <= std_logic_vector(m);
sout <= std_logic_vector(s);
dout <= std_logic_vector(d);
m0t <= std_logic_vector(m0);
m1t <= std_logic_vector(m1);
m2t <= std_logic_vector(m2);
m3t <= std_logic_vector(m3);
s0t <= std_logic_vector(s0);
s1t <= std_logic_vector(s1);
s2t <= std_logic_vector(s2);
s3t <= std_logic_vector(s3);

-- COMBINATIONAL LOGIC

m3 <=   resize(unsigned(minuend(15 downto 12)) * 1000, m3'length);
m2 <=   resize(unsigned(minuend(11 downto 8)) * 100, m2'length);
m1 <=   resize(unsigned(minuend(7 downto 4)) * 10, m1'length);
m0 <=   resize(unsigned(minuend(3 downto 0)), m0'length);

s3 <=   resize(unsigned(subtrahend(15 downto 12)) * 1000, s3'length);
s2 <=   resize(unsigned(subtrahend(11 downto 8)) * 100, s2'length);
s1 <=   resize(unsigned(subtrahend(7 downto 4)) * 10, s1'length);
s0 <=   resize(unsigned(subtrahend(3 downto 0)), s0'length);

m <=    resize(m3+m2+m1+m0, m'length);
s <=    resize(s3+s2+s1+s0, s'length);

d <=    (m-s);

--d0 <= (d mod 10);
--d1 <= (((d mod 100) - (d mod 10)) / 10);
--d2 <= (((d mod 1000) - (d mod 100) - (d mod 10)) / 100);
--d3 <= (((d mod 10000) - (d mod 1000) - (d mod 100) - (d mod 10)) / 1000);

--difference <= ROM(to_integer(d3))&ROM(to_integer(d2))&ROM(to_integer(d1))&ROM(to_integer(d0));


end datapath;

And here is the slightly modified testbench:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity bcd_subtraction_tb is
--  Port ( );
end bcd_subtraction_tb;

architecture Behavioral of bcd_subtraction_tb is

signal clktb : std_logic;
signal mtb, stb, dtb : std_logic_vector(15 downto 0);
signal moutb, soutb : std_logic_vector(18 downto 0);
signal doutb : std_logic_vector(18 downto 0);
signal m0tb : std_logic_vector(15 downto 0);
signal m1tb : std_logic_vector(15 downto 0);
signal m2tb : std_logic_vector(15 downto 0);
signal m3tb : std_logic_vector(15 downto 0);
signal s0tb : std_logic_vector(15 downto 0);
signal s1tb : std_logic_vector(15 downto 0);
signal s2tb : std_logic_vector(15 downto 0);
signal s3tb : std_logic_vector(15 downto 0);




begin

uut: entity work.bdc_subtraction
    port map (
    --Internal Testings
    mout => moutb,
    sout => soutb,
    dout => doutb,
    m0t => m0tb,
    m1t => m1tb,
    m2t => m2tb,
    m3t => m3tb,
    s0t => s0tb,
    s1t => s1tb,
    s2t => s2tb,
    s3t => s3tb,
    --Standards
    clk => clktb,
    minuend => mtb,
    subtrahend => stb,
    difference => dtb
    );

clock : process
begin
    clktb <= '1';
    wait for 5 ns;
    clktb <= '0';
    wait for 5 ns;
end process;

testing : process
begin
    mtb <= x"1430";
    stb <= x"0430";
    wait for 10 ns;
    mtb <= x"9450";
    stb <= x"9320";
    wait for 10 ns;
    mtb <= x"9000";
    stb <= x"8111";
    wait for 10 ns;
    --std.env.stop(0);
wait;
end process;

end Behavioral;

Whereas the earlier version gave the correct output for a single input, this version doesn't give the correct anything as an output, and instead provides rather interesting values for m2, m3, and s2 and s3. I'm not sure why, yet.

enter image description here

I also want to add, I've tried doing this as well (and modifying the other signals accordingly):

signal m0t : out std_logic_vector(3 downto 0);
signal m1t : out std_logic_vector(7 downto 0);
signal m2t : out std_logic_vector(11 downto 0);
signal m3t : out std_logic_vector(15 downto 0);
signal s0t : out std_logic_vector(3 downto 0);
signal s1t : out std_logic_vector(7 downto 0);
signal s2t : out std_logic_vector(11 downto 0);
signal s3t : out std_logic_vector(15 downto 0);

This is based on the idea that for two vectors, of n and m bits respectively, the product will be no greater in length than n+m bits. The results are exactly identify to using all 16-bit vectors, as in the code immediately above this segment.

I'm wondering if resize() behaves a bit differently than expected, or if the addition of vectors of varying sizes is causing some kind of overflow error somewhere. For instance, if X is greater than Y in bit length, X+Y could be greater than X, so perhaps a little concatonation might work as a fix.

I will try that, but any other thoughts remain welcome.

SOLUTION

There were two issues with this code. First, the addition used to assign values to "m" and "s" caused problems because vivado couldn't be sure of the vector sizes, since it had no way to know I had limited input to prevent illegal situations. After fixing that, I got rather bizarre outputs. To fix that, I simply had to use the constant thousand, hundred, and ten vectors which I had not previously been using. For some reason, it seems like Vivado was interpreting the lines like this one:

s3 <=   resize(unsigned(subtrahend(15 downto 12)) * 1000, s3'length);

as an unsigned vector times a vector, and not a natural number. So this was getting translated as * 8 rather than * 1000. On the other hand, the following works fine:

constant thousand : unsigned(11 downto 0) := x"3E8";
[...]
s3 <=   resize(unsigned(subtrahend(15 downto 12)) * thousand, s3'length);

I don't know why it does this, since 1000 was not in quotes to indicate a binary value or in hex. Numeric_std supports {unsigned * natural}, so my best guess is that the resize function is doing something unexpected. But I don't know for certain, because I haven't dug up the code for resize. If I have time to look into it, I'll see if I can nail this behavior down, and I will update this post.

\$\endgroup\$
9
  • \$\begingroup\$ Which simulator are you using? \$\endgroup\$ Mar 5, 2021 at 7:01
  • \$\begingroup\$ What is the actual error : by "the system crashes" do you mean a Windows blue screen or what? Post the actual error in the question. \$\endgroup\$
    – user16324
    Mar 5, 2021 at 13:29
  • \$\begingroup\$ If you remove the UUT, will it simulate the whole 30ns? \$\endgroup\$
    – po.pe
    Mar 5, 2021 at 14:40
  • \$\begingroup\$ I've added the waveforms and a description of the error. As stated in the post, vhdl.log, elaborate.log, the TCL console and messages console, and so on, contain no indication of what this arrow indicates. Mousing over the highlighted line just brings up "1000", which is the value assigned of difference given the initial two inputs. @po.pe I'm not sure what you mean? If I remove "uut:" and leave the entity port map, or if I remove the whole entity? \$\endgroup\$ Mar 5, 2021 at 14:41
  • \$\begingroup\$ Remove the whole entity \$\endgroup\$
    – po.pe
    Mar 5, 2021 at 14:45

1 Answer 1

0
\$\begingroup\$

Per the advice of some people here, I have reported the misinterpreting of 1000 as 8

SOLUTION (posting here so I can mark this problem as 'solved' tomorrow)

There were two issues with this code. First, the addition used to assign values to "m" and "s" caused problems because vivado couldn't be sure of the vector sizes, since it had no way to know I had limited input to prevent illegal situations. After fixing that, I got rather bizarre outputs. To fix that, I simply had to use the constant thousand, hundred, and ten vectors which I had not previously been using. For some reason, it seems like Vivado was interpreting the lines like this one:

s3 <=   resize(unsigned(subtrahend(15 downto 12)) * 1000, s3'length);

as an unsigned vector times a vector, and not a natural number. So this was getting translated as * 8 rather than * 1000. On the other hand, the following works fine:

constant thousand : unsigned(11 downto 0) := x"3E8";
[...]
s3 <=   resize(unsigned(subtrahend(15 downto 12)) * thousand, s3'length);

I don't know why it does this, since 1000 was not in quotes to indicate a binary value or in hex. Numeric_std supports {unsigned * natural}, so my best guess is that the resize function is doing something unexpected. But I don't know for certain, because I haven't dug up the code for resize. If I have time to look into it, I'll see if I can nail this behavior down, and I will update this post.

\$\endgroup\$
2
  • \$\begingroup\$ Maybe you can post this on Xilinx's forums, so they can fix it if it's a bug. \$\endgroup\$ Mar 7, 2021 at 13:11
  • \$\begingroup\$ That (misinterpreting 1000) has to be a Vivado bug. Report it. \$\endgroup\$
    – user16324
    Mar 8, 2021 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.