Sunday, 29 January 2017

Unexpected results when multiplying in VHDL


I'm trying to make a simple BCD --> binary conversion operation work in an ALU I'm coding. All the other operations work perfectly fine, just this last operation doesn't work for some reason.


I've tried numerous approaches and nothing seems to make sense.


My inputs are A and B which are both 8-bit vectors. The outputs are X and Y.


So the formula goes Y:X = BCD2BIN(B:A).


In my case, in my testbench I have B = 0x40 and A = 0x46, so the output I'm expecting is 0xFCE, which is 4046.



Just for those who are unaware, the simplest way to perform this is to divide each byte into 2 nibbles, and multiply by a constant and sum the terms.


So:


B:A = 0x4046 = (4 * 1000) + (0 * 100) + (4 * 10) * (6 * 1)


Signals I'm Using


B = 0x40  --These are the inputs of the ALU, so it isn't actually coded this way.
A = 0x46 --However these are the values.

signal sig1000 : STD_LOGIC_VECTOR(15 downto 0) := x"03E8"; --1000
signal sig100 : STD_LOGIC_VECTOR(15 downto 0) := x"0064"; --100
signal sig10 : STD_LOGIC_VECTOR(15 downto 0) := x"000A"; --10


signal dig1 : STD_LOGIC_VECTOR(3 downto 0);
signal dig2 : STD_LOGIC_VECTOR(3 downto 0);
signal dig3 : STD_LOGIC_VECTOR(3 downto 0);
signal dig4 : STD_LOGIC_VECTOR(3 downto 0);

After the BEGIN


dig1 <= B(7 downto 4);
dig2 <= B(3 downto 0);
dig3 <= A(7 downto 4);

dig4 <= A(3 downto 0);

Actual Operation


opF <= STD_LOGIC_VECTOR((unsigned(dig1) * unsigned(sig1000)) + (unsigned(dig2) * unsigned(sig100)) + (unsigned(dig3) * unsigned(sig10)) + (unsigned(dig4)));

And later the opF gets split into Y and X.


So even verifying the signals in simulation, they are correct, yet the output is not.


The sig10001, sig100 and sig10 are the correct values, and dig1 = 4, dig2 = 0, dig3 = 4 and dig4 = 6 with unsigned decimal radix.


Yet the result is 252


I've even tried this using constants, instead of vectors, with even less success.



For example


opF <= STD_LOGIC_VECTOR((unsigned(dig1) * 10#1000#) + (unsigned(dig2) * 10#100#) + (unsigned(dig3) * 10#10#) + (unsigned(dig4)));

Full file, as some have requested.


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

entity ALU is
Port ( A : in STD_LOGIC_VECTOR (7 downto 0);

B : in STD_LOGIC_VECTOR (7 downto 0);
Cin : in STD_LOGIC;
OPCODE : in STD_LOGIC_VECTOR (3 downto 0);
Y : out STD_LOGIC_VECTOR (7 downto 0);
X : out STD_LOGIC_VECTOR (7 downto 0);
Z : out STD_LOGIC;
Cout : out STD_LOGIC;
V : out STD_LOGIC;
F_active : out STD_LOGIC;
X_bin_pal : out STD_LOGIC;

X_prime : out STD_LOGIC;
N : out STD_LOGIC);
end ALU;

architecture Behavioral of ALU is

signal resultSignal : STD_LOGIC_VECTOR(15 downto 0);

signal cin_vector : STD_LOGIC_VECTOR(0 downto 0);


signal op0 : STD_LOGIC_VECTOR(15 downto 0);
signal op1 : STD_LOGIC_VECTOR(15 downto 0);
signal op2 : STD_LOGIC_VECTOR(15 downto 0);
signal op3 : STD_LOGIC_VECTOR(15 downto 0);
signal op4 : STD_LOGIC_VECTOR(15 downto 0);
signal op5 : STD_LOGIC_VECTOR(15 downto 0);
signal op6 : STD_LOGIC_VECTOR(15 downto 0);
signal op7 : STD_LOGIC_VECTOR(15 downto 0);
signal op8 : STD_LOGIC_VECTOR(15 downto 0);
signal op9 : STD_LOGIC_VECTOR(15 downto 0);

signal opA : STD_LOGIC_VECTOR(15 downto 0);
signal opB : STD_LOGIC_VECTOR(15 downto 0);
signal opC : STD_LOGIC_VECTOR(15 downto 0);
signal opD : STD_LOGIC_VECTOR(15 downto 0);
signal opE : STD_LOGIC_VECTOR(15 downto 0);
signal opF : STD_LOGIC_VECTOR(15 downto 0);

signal dig1 : STD_LOGIC_VECTOR(3 downto 0);
signal dig2 : STD_LOGIC_VECTOR(3 downto 0);
signal dig3 : STD_LOGIC_VECTOR(3 downto 0);

signal dig4 : STD_LOGIC_VECTOR(3 downto 0);

signal Xsig : STD_LOGIC_VECTOR(7 downto 0) := x"00";
signal Xsig_reverse : STD_LOGIC_VECTOR(7 downto 0) := x"00";

signal Ysig : STD_LOGIC_VECTOR(7 downto 0) := x"00";

signal Zsig : STD_LOGIC := '0';
signal Coutsig : STD_LOGIC := '0';
signal Vsig : STD_LOGIC := '0';

signal Nsig : STD_LOGIC := '0';

signal sig1000 : STD_LOGIC_VECTOR(15 downto 0) := x"03E8";
signal sig100 : STD_LOGIC_VECTOR(15 downto 0) := x"0064";
signal sig10 : STD_LOGIC_VECTOR(15 downto 0) := x"000A";



begin


cin_vector(0) <= Cin;

dig1 <= B(7 downto 4);
dig2 <= B(3 downto 0);
dig3 <= A(7 downto 4);
dig4 <= A(3 downto 0);

op0 <= x"00" & (A AND B);
op1 <= x"00" & (A OR B);
op2 <= x"00" & (A XOR B);

op3 <= x"00" & (A XNOR B);
op4 <= STD_LOGIC_VECTOR(unsigned(x"00" & A) + unsigned(x"00" & B));
op5 <= x"00" & STD_LOGIC_VECTOR(signed(A) + signed(B));
op6 <= STD_LOGIC_VECTOR(unsigned(x"00" & A) + unsigned(x"00" & B) + unsigned(x"000" & "000" & cin_vector));
op7 <= STD_LOGIC_VECTOR(signed(A) * signed(B));
op8 <= STD_LOGIC_VECTOR(unsigned(A) * unsigned(B));
op9 <= STD_LOGIC_VECTOR(unsigned(x"00" & A) - unsigned(x"00" & B));
opA <= STD_LOGIC_VECTOR(ROTATE_LEFT(unsigned(A & x"00"), 1));
opB <= STD_LOGIC_VECTOR(ROTATE_LEFT(unsigned(Cin & A & "0000000"), 1));
opC <= STD_LOGIC_VECTOR(SHIFT_RIGHT(unsigned(A & x"00"), 1));

opD <= STD_LOGIC_VECTOR(SHIFT_RIGHT(signed(A & x"00"), 1));
opE <= STD_LOGIC_VECTOR(SHIFT_LEFT(unsigned(A & x"00"), 1));
opF <= STD_LOGIC_VECTOR((unsigned(dig1) * unsigned(sig1000)) + (unsigned(dig2) * unsigned(sig100)) + (unsigned(dig3) * unsigned(sig10)) + (unsigned(dig4)));

with OPCODE select resultSignal <=
op0 when x"0",
op1 when x"1",
op2 when x"2",
op3 when x"3",
op4 when x"4",

op5 when x"5",
op6 when x"6",
op7 when x"7",
op8 when x"8",
op9 when x"9",
opA when x"A",
opB when x"B",
opC when x"C",
opD when x"D",
opE when x"E",

opF when x"F";


Ysig <= resultSignal(15 downto 8) when OPCODE = "0111" or OPCODE = "1000" or OPCODE = "1111" else
x"00";

Y <= Ysig;

Xsig <= resultSignal(15 downto 8) when OPCODE = "1100" or OPCODE = "1101" or OPCODE = "1110" else
resultSignal(15 downto 9) & resultSignal(0) when OPCODE = "1010" else

resultSignal(14 downto 8) & resultSignal(0) when OPCODE = "1011" else
resultSignal(7 downto 0);

Xsig_reverse <= Xsig(0) & Xsig(1) & Xsig(2) & Xsig(3) & Xsig(4) & Xsig(5) & Xsig(6) & Xsig(7);

X <= Xsig;

Zsig <= '1' when resultSignal = x"0000" else
'0';


Z <= Zsig;

Coutsig <= resultSignal(8) when OPCODE = "0100" or OPCODE = "0110" or OPCODE = "1001" or OPCODE = "1110" else
resultSignal(15) when OPCODE = "1011" else
resultSignal(7) when OPCODE = "1100" or OPCODE = "1101" else
'1' when OPCODE = "1111" and Ysig /= x"00" else
'0';

Cout <= Coutsig;



Vsig <= '1' when OPCODE = "0101" and resultSignal(7) = '0' and A(7) = '1' and B(7) = '1' else
'1' when OPCODE = "0101" and resultSignal(7) = '1' and A(7) = '0' and B(7) = '0' else
'1' when OPCODE = "1110" and resultSignal(15) /= A(7) else
'0';

V <= Vsig;

F_active <= Zsig OR Coutsig OR Vsig;


X_bin_pal <= '1' when Xsig = Xsig_reverse else
'0';

X_prime <= '1' when Xsig = x"02" or Xsig = x"03" or Xsig = x"05" or Xsig = x"07" or Xsig = x"0B" or Xsig = x"0D" or Xsig = x"11" or Xsig = x"13" else
'1' when Xsig = x"17" or Xsig = x"1D" or Xsig = x"1F" or Xsig = x"25" or Xsig = x"29" or Xsig = x"2B" or Xsig = x"2F" or Xsig = x"35" else
'1' when Xsig = x"3B" or Xsig = x"3D" or Xsig = x"43" or Xsig = x"47" or Xsig = x"49" or Xsig = x"4F" or Xsig = x"53" or Xsig = x"59" else
'1' when Xsig = x"61" or Xsig = x"65" or Xsig = x"67" or Xsig = x"6B" or Xsig = x"6D" or Xsig = x"71" or Xsig = x"7F" or (Xsig = x"83" and Nsig = '0') else
'1' when (Xsig = x"89" or Xsig = x"8B" or Xsig = x"95" or Xsig = x"97" or Xsig = x"9D" or Xsig = x"A3" or Xsig = x"A7" or Xsig = x"AD") and Nsig = '0' else
'1' when (Xsig = x"B3" or Xsig = x"B5" or Xsig = x"BF" or Xsig = x"C1" or Xsig = x"C5" or Xsig = x"C7" or Xsig = x"D3" or Xsig = x"DF") and Nsig = '0' else
'1' when (Xsig = x"E3" or Xsig = x"E5" or Xsig = x"E9" or Xsig = x"EF" or Xsig = x"F1" or Xsig = x"FB") and Nsig = '0' else

'0';

Nsig <= '1' when OPCODE = "0101" and Xsig(7) = '1' else
'1' when OPCODE = "0111" and Ysig(7) = '1' else
'1' when OPCODE = "1101" and Xsig(7) = '1' else
'1' when OPCODE = "1110" and Xsig(7) = '1' else
'0';

N <= Nsig;




end Behavioral;

Answer



16 bits multiplied by 4 bits can become, at most, a 20 bits number, this is apparently the size implicitly created by VHDL for storing the intermediate result of the multiplication. When this implicit 20 bits number is assigned to a 16 bits array, the last 4 bits are lost. And that's why Brian Drummonds's VHDL simulator complained about bound array error.


Because your final result actually fits in a 16 bits number, the best workaround is to reduce the number of bits of sig1000, sig100, sig10 to a 12 bits array.


No comments:

Post a Comment

arduino - Can I use TI&#39;s cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...