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:
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.
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.
UUT
, will it simulate the whole 30ns? \$\endgroup\$