VHDL: CLA subtractor Module cascade - vhdl

Good day,
I have implemented a 8 bit CLA add/subtract module and it works great. The code is below: I have strung 2 of these modules below to create a 16 bit adder/subtractor. This 16 bit version works great for addition, the carry is generated by 1 8 bit adder for the lower bits, which is then passed onto the next adder to be handled for the upper bits.
The problem is with subtraction. It doesn't work, even on paper. Let me give you an example. Lets say I want to do 350 - 50
300: 00000001 00101100
50: 00000000 00110010
Hence, if i let one adder handle the lower bits, and another handle the upper bits, it simply would not work. Here is why:
300: 00000001 00101100
50 in 2's compliment: 11111111 11001110
250 is supposed to be 00000000 11111010
1st adder: Generates correct value after addition 11111010. That's cool. Now the second adder, is a problem. It will do (1's compliment + 1) which will give 00000000 (with carry). It was supposed to be (11111111), but because of the + 1 in the algo which is a general implementation, it screws up the final answer.
By right, since Adder 0 has no carry, Adder 1 should not do the + 1. How might I be able to implement this in the logic for the general purpose 8 bit sub/adder? Is this the correct thinking and have i covered all possible fallouts/edge cases?
entity CLA_ADD_SUB is
generic (N : integer := 8);
Port ( A : in STD_LOGIC_VECTOR (N-1 downto 0);
B : in STD_LOGIC_VECTOR (N-1 downto 0);
Binv : in STD_LOGIC;
C_in: in STD_LOGIC;
S : out STD_LOGIC_VECTOR (N-1 downto 0);
TEST : out STD_LOGIC_VECTOR (N-1 downto 0);
C_out : out STD_LOGIC
);
end CLA_ADD_SUB;
architecture CLA_ADD_SUB_ARCH of CLA_ADD_SUB is
SIGNAL h_sum : STD_LOGIC_VECTOR(N-1 DOWNTO 0);
SIGNAL carry_generate : STD_LOGIC_VECTOR(N-1 DOWNTO 0);
SIGNAL carry_propagate : STD_LOGIC_VECTOR(N-1 DOWNTO 0);
SIGNAL carry_in_internal : STD_LOGIC_VECTOR(N-1 DOWNTO 1);
SIGNAL B_mod : STD_LOGIC_VECTOR(N-1 DOWNTO 0) := B;
SIGNAL C_in_mod: STD_LOGIC := C_in;
signal S_wider : std_logic_vector(N downto 0);
begin
WITH Binv SELECT
B_mod <= B WHEN '0',
not B WHEN '1',
B WHEN OTHERS;
WITH Binv SELECT
C_in_mod <= C_in WHEN '0',
not C_in WHEN '1',
C_in WHEN OTHERS;
-- Sum, P and G
h_sum <= A XOR B_mod;
carry_generate <= A AND B_mod;
carry_propagate <= A OR B_mod;
PROCESS (carry_generate,carry_propagate,carry_in_internal,C_in_mod)
BEGIN
carry_in_internal(1) <= carry_generate(0) OR (carry_propagate(0) AND C_in_mod);
inst: FOR i IN 1 TO (N-2) LOOP
carry_in_internal(i+1) <= carry_generate(i) OR (carry_propagate(i) AND carry_in_internal(i));
END LOOP;
C_out <= carry_generate(N-1) OR (carry_propagate(N-1) AND carry_in_internal(N-1));
END PROCESS;
S(0) <= h_sum(0) XOR C_in_mod;
S(N-1 DOWNTO 1) <= h_sum(N-1 DOWNTO 1) XOR carry_in_internal(N-1 DOWNTO 1);
end CLA_ADD_SUB_ARCH;

The problem is, that you did an error calculating the 2's complement of 50:
In your equation you use:
50 in 2's compliment: 11111111 11001101 <--- WRONG.
which is just 50 with all bits inverted. two's complement however is build by first inverting the bits, then add one to it.
The correct representation of -50 in 16 bit integers is:
50 in 2's compliment: 11111111 11001110
If we now do the math of 300 - 50 we'll get:
00000001 00101100 | 300
+ 11111111 11001110 | -50 in 2's complement form
-------------------
00000000 11111010 | 250 (expected result)
For your VHDL implementation that means that you can implement subtraction by re-using the addition part. To do so build the two's complement in two steps:
If subtraction-mode:
negate all bits of B prior to addition
force the carry-in of the low 8 bit adder to high.
The second step will add the one to the negated B which then is a correct two's complement number. Now you add your numbers as usual and get a functional subtracter.

u_ADDER_0: entity work.CLA_ADD_SUB(CLA_ADD_SUB_ARCH)
port map(
A => ADDER_0_A, -- Bits 0-7
B => ADDER_0_B,
Binv => TOADD_BINV,
C_in => ADDER_0_CARRY_IN,
S => ADDER_0_SUM,
TEST => ADDER_0_TEST,
C_out => ADDER_0_CARRY_OUT
);
u_ADDER_1: entity work.CLA_ADD_SUB(CLA_ADD_SUB_ARCH)
port map(
A => ADDER_1_A, --Bits 7 to 15
B => ADDER_1_B,
Binv => TOADD_BINV,
C_in => TOADD_BINV xor ADDER_0_CARRY_OUT,
S => ADDER_1_SUM,
TEST => ADDER_1_TEST,
C_out => ADDER_1_CARRY_OUT
);
Ah I get it, actually what I did was for all subsequent adders (for bits 7-15 etc.) I set the Carry for those to (Binv xor Adder_0_Carry). Hence, if the 1st adder has a carry due to the + 1 for the 2's compliment, it will propogate out of the carry, and since I xor it with Binv, I tell the second adder to not add a carry.
the idea is that if we are subtracting (doing 2's compliment), we should just do a (1's compliment) if the first adder had no carry
I cannot confirm this for all test cases. This is just a theory and so far it seems to work

This is supplemental to your own answer.
Hmm..if I set B_inv to 0, meaning I am doing addition, and I pass in two signed bits. say 50 and -250, it won't work, because the second adder's C_in is determined by Binv xor Adder_0_carry
I think your manual arithmetic is faulty:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_add_sub_gang is
constant n: natural := 8;
end entity;
architecture foo of tb_add_sub_gang is
signal a: std_logic_vector (2*n-1 downto 0);
signal b: std_logic_vector (2*n-1 downto 0);
signal s: std_logic_vector (2*n-1 downto 0);
signal test: std_logic_vector (2*n-1 downto 0);
signal binv: std_logic;
signal cout0: std_logic;
signal cin1: std_logic;
signal cout1: std_logic;
begin
cin1 <= cout0 xor binv;
uadder0:
entity work.cla_add_sub(cla_add_sub_arch)
port map(
a => a((2*n-1)/2 downto 0), -- bits 0-7
b => b((2*n-1)/2 downto 0),
binv => binv,
c_in => '0',
s => s((2*n-1)/2 downto 0),
test => s((2*n-1)/2 downto 0),
c_out => cout0
);
uadder1:
entity work.cla_add_sub(cla_add_sub_arch)
port map(
a => a(2*n-1 downto n), --bits 8 to 15
b => b(2*n-1 downto n),
binv => binv,
c_in => cin1,
s => s(2*n-1 downto n),
test => test(2*n-1 downto n),
c_out => cout1
);
STIMULUS:
process
begin
wait for 100 ns;
a <= std_logic_vector(to_unsigned(350,a'length));
b <= std_logic_vector(to_unsigned(50,b'length));
binv <= '1';
wait for 100 ns;
binv <= '0';
wait for 100 ns;
a <= std_logic_vector(to_unsigned(50,a'length));
b <= std_logic_vector(to_unsigned(250,b'length));
binv <= '1';
wait for 100 ns;
binv <= '0';
wait for 600 ns;
wait;
end process;
end architecture;
Gives:
(I update the waveform image for your signed subtraction/addition).
The binv selector in cla_add_sub is the equivalent of XOR between Binv and C_in. XORing Binv in again externally for the Most Significant n bit adder is gives you an even number number of XORs removing their effect, carrying in the carry out of the Least Significant adder.
The proper thing to do is actually to only invert B with Binv and use Binv to also invert the carry in of the LS adder. Because you never carry in to it other than for subtraction, hook it directly to Binv. This will fix everything.
in cla_add_sub comment out:
-- WITH Binv SELECT
-- C_in_mod <= C_in WHEN '0',
-- not C_in WHEN '1',
-- C_in WHEN OTHERS;
The declaration for C_in_mod:
-- SIGNAL C_in_mod: STD_LOGIC := C_in;
And use C_in directly:
PROCESS (carry_generate,carry_propagate,carry_in_internal,C_in)
BEGIN
carry_in_internal(1) <= carry_generate(0) OR (carry_propagate(0) AND C_in);
inst: FOR i IN 1 TO (N-2) LOOP
carry_in_internal(i+1) <= carry_generate(i) OR (carry_propagate(i) AND carry_in_internal(i));
END LOOP;
C_out <= carry_generate(N-1) OR (carry_propagate(N-1) AND carry_in_internal(N-1));
END PROCESS;
And for S(0):
S(0) <= h_sum(0) XOR C_in;
In the top level:
-- signal cin1: std_logic;
-- cin1 <= cout0 xor binv;
uadder0:
entity work.cla_add_sub(cla_add_sub_arch)
port map(
a => a((2*n-1)/2 downto 0), -- bits 0-7
b => b((2*n-1)/2 downto 0),
binv => binv,
c_in => binv,
s => s((2*n-1)/2 downto 0),
test => s((2*n-1)/2 downto 0),
c_out => cout0
);
uadder1:
entity work.cla_add_sub(cla_add_sub_arch)
port map(
a => a(2*n-1 downto n), --bits 8 to 15
b => b(2*n-1 downto n),
binv => binv,
c_in => cout,
s => s(2*n-1 downto n),
test => test(2*n-1 downto n),
c_out => cout1
);
And that gives the same results as shown above.
And notice this is in keeping with Nils answer.
If you only had an n bit adder/subtractor you'd use the method in your answer where carry out (LS) to carry in (MS) would be inverted again to chain two 8 bit operations together.
This instantiates two adders that happen to invert B, the 1's complement part of 2's Complement to subtract 'B'. You could add an input to cla_add_sub signify when it's the LS adder and only XOR Binv then. That way it could serve as either a stand alone n bit adder or one that can be daisy chained.

Related

N-bits adder/subtractor using ripple of full adders- problem with carryout

I am trying to create N-bits adder/subtractor using a ripple of full adders.
The input is N-bits A, N-bits B, and the result should be at length of 2N (it outputs ALU with 2 buses High and low of N-bits each, so I am trying to extend the signed bit).
The problem arises with carryout in subtraction. For example, when doing 3-2 (assume N=3 so that it's 011-010 and with two's compliment it's 011+110) I get 001 with carry 1. The problem is that this carry is garbage and can't be extended, but in other case it's necessary. For example, when trying do (-3)+(-3) (101+101, again N=3), then I get 010 with carry of 1. This time the carry really indicate the sign, so I would like to extend it.
Here is my code:
entity FullAdder is
Port (
A : in std_logic;
B : in std_logic;
Cin : in std_logic;
sum : out std_logic;
Cout : out std_logic
);
end FullAdder;
architecture gate of FullAdder is
begin
sum <= A xor B xor Cin ;
Cout <= (A and B) OR (Cin and A) OR (Cin and B) ;
end gate;
here is the N-bit Adder
entity NbitsAdder is
generic(N: integer := 8);
Port(
A : in std_logic_vector((N-1) downto 0);
B : in std_logic_vector((N-1) downto 0);
Cin: in std_logic;
SUM : out std_logic_vector((N-1) downto 0);
Cout : out std_logic
);
end NbitsAdder;
architecture NbitsAdderGate of NbitsAdder is
...
signal temp : std_logic_vector (N downto 0);
begin
temp (0) <= Cin;
arrrayOfFullAdders : for i in 0 to N-1 generate
adder_i: FullAdder port map ( A(i), B(i), temp(i), SUM(i), temp (i+1) );
end generate;
Cout <= temp(N); --which will be extend
end NbitsAdderGate;
And this is the ADDER or SUBTRACTOR
entity NbitsAddOrSub is
generic(N: integer := 8);
port(
A : in std_logic_vector ((N-1) downto 0);
B : in std_logic_vector ((N-1) downto 0);
addOrSub : in std_logic;
sumLo : out std_logic_vector ((N-1) downto 0);
sumHi : out std_logic_vector ((N-1) downto 0)
);
end NbitsAddOrSub;
architecture NbitsAddOrSubGate of NbitsAddOrSub is
signal tempB: std_logic_vector ( (N-1) downto 0);
signal CoutTemp: std_logic;
begin
loop1 : for i in 0 to N-1 generate
xor_i: xorGate port map ( B(i), addOrSub, tempB(i));
end generate;
theOperation : NbitsAdder generic map (N)
port map ( A => A, B => tempB, Cin => addOrSub, sum => sumLo, Cout => CoutTemp);
sumHi <= (N-1 downto 0 => CoutTemp); -- tring to extend the sign bit
end NbitsAddOrSubGate;
In signed addition the carry has no meaning. You get the sign bit from the MSB of the sum and not from the carry. In your second example there is an underflow because -3+-3 is smaller than 2^((N=3)-1), thus the result is incorrect.
To sign-extend the result you should first check the overflow/underflow conditions for signed addition. If no overflow/underflow has occured, you look at the MSB of the sum and extend that bit

How to implement a 4 bit ALU in VHDL using an opcode

So I have this lab assignment which carries on from a previous one where I implemented a single bit ALU in VHDL. The single bit ALU module takes an opcode and performs an operation based on this opcode e.g. addition or subtraction.
entity alu_slice is
Port ( a : in STD_LOGIC;
b : in STD_LOGIC;
s : out STD_LOGIC;
opcode : in STD_LOGIC_VECTOR (2 downto 0);
cin : in STD_LOGIC;
cout : out STD_LOGIC);
end alu_slice;
The opcode vector above is where the 3 bit opcode is placed at the time of simulation to tell the ALU what arithmetic operation to perform. The behavioural of the single bit ALU is given below:
architecture Behavioural of alu_slice is
architecture Behavioural of alu_slice is
begin
multiplex: process(a, b, opcode, cin) is begin
case opcode is
when "000" =>
--cin <= '0';
--s <= (a xor b) xor cin;
s <= (a xor b) xor cin;
cout <= (a and b) xor ((a xor b) and cin);
when "001" =>
s <= (a xor not b) xor cin;
cout <= (a and not b) xor ((a xor not b) and cin);
when "010" =>
s <= a and b;
when "011" =>
s <= a or b;
when "100" =>
s <= a xor b;
when "101" =>
s <= not a;
when "110" =>
--a <= cin;
cout <= a;
when "111" =>
s <= a xor cin;
cout <= a and cin;
when others =>
s <= (a xor b) xor cin;
cout <= (a and b) xor ((a xor b) and cin);
end case;
end process;
end behavioural;
I won't go into detail about what each of the opcodes are because that isn't part of the problem I'm having. I am supposed to use this alu slice as part of the architecture of a 4 bit ALU with four seperate instantiations of the alu_slice entity. The issue I am having is that if the code for the opcode is all inside the beavioural of the alu slice how is it supposed to be changed from the 4 bit module during simulation?
Here is the 4 bit alu entity I have made.
entity alu_pb is
Port ( a : in STD_LOGIC_VECTOR (3 downto 0);
b : in STD_LOGIC_VECTOR (3 downto 0);
s : out STD_LOGIC_VECTOR (3 downto 0);
--op: in STD_LOGIC_VECTOR (2 downto 0);
overflow : out STD_LOGIC;
compl_overflow : out STD_LOGIC;
zero : out STD_LOGIC);
end alu_pb;
This is using all of the signals I was told to use in my notes. I commented out the op vector because that is something I added trying to solve this problem. And so finally I have four ALU slices in the architecture of the project but I can't compile because I can't seem to figure out a way of updating the opcode logic vector in the alu slice component such that it is replicated across all of the separate slices performing the appropriate operation between each of the four bits.
Here is the rest of the code in the architecture:
architecture Behavioral of alu_pb is
component alu_slice is
Port(a: in STD_LOGIC;
b: in STD_LOGIC;
s: out STD_LOGIC;
opcode: in STD_LOGIC_VECTOR(2 down to 0);
cin: in STD_LOGIC;
cout: out STD_LOGIC);
end component;
signal carry: STD_LOGIC_VECTOR(3 down to 0);
signal op_carry: STD_LOGIC_VECTOR(2 down to 0);
signal sum_buffer: STD_LOGIC_VECTOR(3 down to 0);
signal carry_in: STD_LOGIC;
begin
slice_zero: alu_slice
port map(
--opcode(0) => op(0);
--opcode(1) => op(1);
--opcode(2) => op(2);
a => a(0),
b => b(0),
s => s(0),
--s => sum_buffer(0),
cin => carry_in,
cout=>carry(0)
);
slice_one: alu_slice
port map(
--op(0) => opcode(0),
--op(1) => opcode(1),
--op(2) => opcode(2),
a => a(1),
b => b(1),
s => s(1),
--s => sum_buffer(1)
cin => carry(0),
cout => carry(1)
);
slice_two: alu_slice
port map(
--op(0) => opcode(0),
--op(1) => opcode(1),
--op(2) => opcode(2),
a => a(2),
b => b(2),
s => s(2),
--s => sum_buffer(2)
cin => carry(1),
cout => carry(2)
);
slice_three: alu_slice
port map(
--op(0) => opcode(0),
--op(1) => opcode(1),
--op(2) => opcode(2),
a => a(3),
b => b(3),
s => s(3),
--s => sum_buffer(3),
cin => carry(2),
cout => carry(3)
);
--op_carry(0) <= op(0);
--op_carry(1) <= op(1);
--op_carry(2) <= op(2);
end Behavioral;
There is some other commented code at the end before 'end behavioral' but I omitted them because they are irrelevant to the actual problem and are to with the conditions whereby some of the ports of the alu_pb entity are set (compl_overflow, overflow, zero etc.)
If I try to simulate my model as is I get this error message:
"ERROR: [VRFC 10-704] formal opcode has no actual or default value [U:/alu/alu_pb.vhd:63]".
Hopefully one of you experienced VHDL guys will be able to help me track down a solution.
Thanks in advance,
Simon.

I can't understand why my waveform is coming out this way

I am very new to VHDL coding and I have been trying to debug my code for a 32-bit adder/subtractor. The N-bit adder/subtractor is composed multiple 1-bit adder/subtractor using a generate statement. I have been testing it for 6-bit inputs using simulation. The waveform is constantly incorrect and I have tried changing just about everything. Maybe, it is a problem with the delays and the generate statement not cycling through correctly. (I am just beginning to learn how to code in vhdl.)
My 1-bit adder/subtractor
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity addsub_1bit is
Port ( in_0 : in STD_LOGIC;
in_1 : in STD_LOGIC;
cin : in STD_LOGIC;
AddOrSub : in STD_LOGIC;
sum_sub : out STD_LOGIC;
cout_bout : out STD_LOGIC);
end addsub_1bit;
architecture data_flow_addsub_1bit of addsub_1bit is
begin
sum_sub <= (in_1 and (not in_0) and (not cin)) or ((not in_1) and in_0 and (not cin)) or ((not in_1) and (not in_0) and cin) or (in_1 and in_0 and cin) after 19 ns;
cout_bout <= (in_1 and in_0 and (not AddOrSub)) or ((not in_1)and in_1 and cin) or ((not in_1)and cin and AddOrSub) or (in_0 and cin) or (in_1 and cin and AddOrSub) after 19 ns;
end data_flow_addsub_1bit;
The N-bit adder/subtractor:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
ENTITY adder_sub32 is
GENERIC (BW : INTEGER :=32);
PORT ( a_32 : IN STD_LOGIC_VECTOR (BW -1 downto 0);
b_32 : IN STD_LOGIC_VECTOR (BW -1 downto 0);
cin : IN STD_LOGIC;
sub : IN STD_LOGIC;
sum_32 : out STD_LOGIC_VECTOR (BW -1 downto 0);
cout : INOUT STD_LOGIC ;
ov : OUT STD_LOGIC ); -- ov stands for overflow
END adder_sub32 ;
ARCHITECTURE adder_sub32_arch OF adder_sub32 IS
signal tmp : std_logic_vector (BW downto 0);
BEGIN
tmp(0) <= cin;
gen: for i IN 0 TO BW-1 GENERATE
as1: entity work.addsub_1bit
PORT MAP(
in_0 => a_32(i),
in_1 => b_32(i),
cin => tmp(i),
AddOrSub => sub,
sum_sub => sum_32(i),
cout_bout => tmp(i+1));
end GENERATE;
ov <= tmp(BW) after 95 ns;
END ARCHITECTURE;
My testbench:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
ENTITY adder_sub32_TB_SHan_53967364 IS
END adder_sub32_TB_SHan_53967364;
ARCHITECTURE behavior OF adder_sub32_TB_SHan_53967364 IS
-- Component Declaration for the Unit Under Test (UUT)
COMPONENT adder_sub32 IS
GENERIC (BW : INTEGER :=32);
PORT ( a_32 : IN STD_LOGIC_VECTOR (BW -1 downto 0);
b_32 : IN STD_LOGIC_VECTOR (BW -1 downto 0);
cin : IN STD_LOGIC ;
sub : IN STD_LOGIC ;
sum_32 : out STD_LOGIC_VECTOR (BW -1 downto 0);
cout : INOUT STD_LOGIC ;
ov : OUT STD_LOGIC ); -- ov stands for overflow
END COMPONENT;
signal a : std_logic_vector(5 downto 0); --:= (others => '0');
signal b : std_logic_vector(5 downto 0); --:= (others => '0');
signal cin : std_logic;
signal sub : std_logic;
signal cout : std_logic;
signal sum_32 : std_logic_vector(5 downto 0);
signal ov : std_logic;
BEGIN
test1: adder_sub32
GENERIC MAP (6)
PORT MAP (a_32 => a,b_32 => b,cin => cin,sub => sub,sum_32 => sum_32,cout => cout,ov => ov);
sub <= '0';
cin <= '0';
a <= "101010";
b <= "110101";
END;
The waveform I got:
The final sum is correct ("101010" + "110101" = "011111") in this case, but not in all cases.
EDIT2: Let's take a closer look, why the carry is not rippling as expected in your addition. The bits 0 (LSB) to 5 of the operands together, request that the carry-in is propagated from bit 0 to the carry-in of bit 6. Bits 6 of the operands generate a carry, which is carry-out of the adder. As the cin of bit 0 is '0', all intermediate carry-ins will be '0' too, but it should ripple through the carry-chain.
Now lets, take a look at the one-bit adder. You are adding two numbers, so that, AddOrSub is '0'. With this, the equation of cout_bout can be simplified to:
cout_bout <= (in_1 and in_0) or (in_0 and cin);
This equation is definitly wrong, because the carry-in is not propagated when in_1 = '1' and in_0 = '0'. Thus, some of the intermediate carries will be computed to '0' just after 19 ns without waiting for the rippling carry. The corresponding sum bit will be valid after 38 ns as shown in your waveform. The final value of the sum is not affected because this shortcuted carry is identical to the expected rippling carry. Please consider here, that all the 1-bit adder (generated by the generate statement) work concurrently.
To fix the equation, I recommend to write a testbench for the 1-bit adder. This testbench would have to check all possible 16 input combinations of in_0, in_1, cin, and AddOrSub.
Another testcase would be to add the above two operands with an cin of '1'.
(End of EDIT2.)
The ov is correct too in this case, but not in all cases.
EDIT: You mixed up the overflow ov with the carry-out cout. The overflow flag indicates an overflow in the signed number space. For the addition, the overflow flag is '1' if and only if:
the addition of two positive numbers results in a negative sum, or
the addition of two negative numbers results in a positive sum.
For subtraction it is the other way round.
Because this is a homework question, I will not solve it completely. But I will give a you a testcase where your current logic fails: if you add 1 ("000001") plus -1 ("111111"), then the sum must be zero, the overflow '0' and the carry-out '1'. (End of Edit.)
The cout is 'U' because you haven't connected it in adder_sub32. The carry-out is the top-most bit in your carry-chain, and thus:
cout <= tmp(BW);
And you should fix the direction of cout in adder_sub32. The carry-out is just an output of this component. So declare it as out instead of inout.

Subtractor Module VHDL generating wrong values

I have a code as such below that is designed to do subtraction and addition. Basically, when Binv is set, it should subtract, and Binv is 0, it should add. Unfortunately, it seems to be adding when Binv is set sometimes, and subtracting when it isn't set sometimes. Here is a snapshot of the simulation:
entity ADD_SUB is
Port ( A : in STD_LOGIC_VECTOR (31 downto 0);
B : in STD_LOGIC_VECTOR (31 downto 0);
Binv : in STD_LOGIC;
C_in: in STD_LOGIC;
S : out STD_LOGIC_VECTOR (31 downto 0);
TEST : out STD_LOGIC_VECTOR (31 downto 0);
C_out : out STD_LOGIC
);
end ADD_SUB;
architecture ADD_SUB_ARCH of ADD_SUB is
signal S_wider : std_logic_vector(32 downto 0);
begin
process (A,B,C_in,Binv)
begin
if Binv = '0' then
S_wider <= ( A(31) & A) + ( B(31) & B) + C_in;
elsif Binv = '1' then
S_wider <= (A(31)& A) + ('1'& not B) + '1';
else
S_wider <= std_logic_vector(to_signed(0,32));
end if;
S <= S_wider(31 downto 0);
C_out <= S_wider(32);
end process;
I am getting nonsensical results which make no sense. In the first case, you can see that I tried to do (50 - 30) (Binv is 1). I get 80 which is wrong. You can see however that it works on (30 - 50) where I get -20. Second problem is where I try to do (30 + (-50)), however it shows up as 20.
The results are completely off and I can't see where I am going wrong
Jim is absolutely correct.
There are a couple of points that may be worth making.
First, the + C_in or + not C_in implies two 32 bit adds, one of which gets optimized away during synthesis leaving just the carry in to the remaining add.
Second, you are really only manipulating B and C_in using Binv. Subtraction is the equivalent of adding the two's complement, for B the one's complement + X"00000001. Note that Jim inverts C_in with Binv which allows C_in to be used for daisy chain operations (e.g. a 64 bit add or subtract with a 32 bit ALU).
Both points are illustrated with the following code, which also only uses numeric_std.unsigned and and only needs the unsigned numeric_std."+":
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity add_sub is
port (
a: in std_logic_vector (31 downto 0);
b: in std_logic_vector (31 downto 0);
binv: in std_logic;
c_in: in std_logic;
s: out std_logic_vector (31 downto 0);
test: out std_logic_vector (31 downto 0);
c_out: out std_logic
);
end entity;
architecture foo of add_sub is
begin
UNLABELLED:
process (a,b,c_in,binv)
variable x,y,z: std_logic_vector (33 downto 0);
begin
x := a(31) & a & '1'; -- this '1' generates a true carry in to z(1)
-- z(0) is optimized away as unused it's carry
-- retained as carry in to the next MS bit.
if binv = '0' then
y := b(31) & b & c_in;
elsif binv = '1' then
y := not b(31) & not b & not c_in;
else
y := (others => 'X'); -- 'X' on binv is propagated from b onto y
end if;
z := std_logic_vector( unsigned(x) + unsigned(y)); -- only one add
c_out <= z(33);
s <= z(32 downto 1);
end process;
end architecture;
This above example connects C_in a bit more directly to the adder stage with the LS bits of A and B and gives:
(The image is can be clicked to open)
(Synthesis software is generally smart enough to do all this with using Jim's form modified to either add or subtract based on Binv and A and B extended to 33 bits without any direct bit or bitfield manipulation, our synthesis tools have had more than 25 years to get it right.)
The waveform was produced with the following test bench:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb_add_sub is
end entity;
architecture foo of tb_add_sub is
signal a: std_logic_vector (31 downto 0) := (others =>'0');
signal b: std_logic_vector (31 downto 0) := (others =>'0');
signal binv: std_logic := '0';
signal c_in: std_logic := '0';
signal s: std_logic_vector (31 downto 0);
signal test: std_logic_vector (31 downto 0);
signal c_out: std_logic;
begin
DUT:
entity work.add_sub
port map (
a => a,
b => b,
binv => binv,
c_in => c_in,
s => s,
test => test,
c_out => c_out
);
STIMULUS:
process
begin
wait for 100 ns;
a <= std_logic_vector(to_signed(50,a'length));
b <= std_logic_vector(to_signed(30,b'length));
wait for 100 ns;
binv <= '1';
wait for 100 ns;
binv <= '0';
a <= std_logic_vector(to_signed(30,a'length));
b <= std_logic_vector(to_signed(-50,b'length));
wait for 100 ns;
binv <= '1';
b <= std_logic_vector(to_signed(50,b'length));
wait for 600 ns;
wait;
end process;
end architecture;
enter code hereYour equation for subtraction is not correct. Like #neodelphi suggested, it should be:
A - B = A + (not B) + 1
However, this does not account for carry in and what to do with it. If I remember right, the borrow is subtracted:
A - B - C_in = A + (not B) + 1 - C_in = A + (not B) + (1 - C_in)
Now note that:
(1 - C_in) = not C_in
Now, to convert it to VHDL. If I overlook the fact that you are doing signed math with the package, std_logic_unsigned (Ahem), you could write (similar to #neodelphi):
S_wider <= (A(31)& A) + (not B(31) & not B) + not C_in ;
Note in the package std_logic_unsigned as well as numeric_std with VHDL-2008, there are no issues with adding with a std_ulogic.
My suggestion about types and packages is very simple. If you are doing math, use a math type like, signed (matching your math here) or unsigned (for other cases). I consider these part of the documentation.
Furthermore, using the appropriate type is important as the math packages allow you to add two array values that are different sizes. If you use the appropriate type, they do the appropriate extension replicate sign bit for signed or '0' fill for unsigned.
Hence, had you used type signed, then you could have used the first argument (A) to size the result and been lazy about B and written:
S_wider <= (A(31)& A) + not B + not C_in ;
BTW, testing for both '0' and '1' does not help the hardware in any way. My recommendation is to either be lazy (and safe) and write:
if Binv = '0' then
S_wider <= ( A(31) & A) + ( B(31) & B) + C_in;
else
S_wider <= (A(31)& A) + (not B(31) & not B) + not C_in;
end if;
Alternately be paranoid and vigilant and make the output is 'X' when the control input is an 'X'. However be sure to double check your "elsif" expression - get this wrong when it is more complex and it may be challenging to find the bug (meaning you better have test cases that cover all possible input values of the controls):
if Binv = '0' then
S_wider <= ( A(31) & A) + ( B(31) & B) + C_in;
elsif Binv = '1' then
S_wider <= (A(31)& A) + (not B(31) & not B) + not C_in;
else
S_wider <= (others => 'X') ; -- X in propagates as X out can help debug
end if;
An AddSub module has only one control input lets call it \bar{add}/sub. This means, if add_sub is zero perform an add operation, if its one perform a subtraction.
There is a solid relation between C_in and Binv. If you want to add Binv and C_in are zero, if you want to subtract both are one.
The equation for an adder is simply S := A + B + 0 for a subtracter it can be retrieved by some transformations:
S := A - B -- transform into an add operation
S := A + (- B) -- transform negative number using 2's complement
S := A + ( 2's complement of B) -- transform 2's complement into 1's complement
S := A + ((1's complement of B) + 1) -- transform 1's complement into bit wise not operation
S := A + ((bit wise not of B) + 1)
If you combine both equations you will get:
S := A + (B xor vector(add_sub)) + add_sub
So in VHDL this would be:
S_wider <= unsigned('0' & A) + unsigned('0' & (B xor (B'range => add_sub))) + unsigned((B'range => '0') & add_sub);
S <= S_wider(S'range);
C_out <= S_wider(S_width'high);
Synthesis is smart enough to find a 3:1 adder with a switchable constant input 3 to be an addsub-macro block. If you want to perform signed add/sub then exchange the conversion functions and sign-extension accordingly.

Making a 4-bit ALU from several 1-bit ALUs

I'm trying to combine several 1 bit ALUs into a 4 bit ALU. I am confused about how to actually do this in VHDL. Here is the code for the 1bit ALU that I am using:
component alu1 -- define the 1 bit alu component
port(a, b: std_logic_vector(1 downto 0);
m: in std_logic_vector(1 downto 0);
result: out std_logic_vector(1 downto 0));
end alu1;
architecture behv1 of alu1 is
begin
process(a, b, m)
begin
case m is
when "00" =>
result <= a + b;
when "01" =>
result <= a + (not b) + 1;
when "10" =>
result <= a and b;
when "11" =>
result <= a or b;
end case
end process
end behv1
I am assuming I define alu1 as a component of the larger entity alu4, but how can I tie them together?
Interesting you would even ask that question. VHDL synthesizers are quite capable of inferring any adder you like. You can just type what you need:
use ieee.numeric_std.all;
...
signal r : unsigned(3 downto 0);
signal a : unsigned(2 downto 0);
signal b : unsigned(2 downto 0);
signal c : unsigned(2 downto 0);
...
r <= a + b + c;
Then you can slice r to fit your needs:
result <= std_logic_vector(r(2 downto 0));
You can't (easily) string together these 1-bit ALUs into a functional multiple bit version. There is no way to handle the carry in/out needed for your add and subtract modes to work properly (the bitwise and & or should work OK, however).
Ignoring the carry issue for the moment, you would typically just setup a for generate loop and instantiate multiple copies of your bitwise logic, possibly special casing the first and/or last elements, ie:
MyLabel : for bitindex in 0 to 3 generate
begin
alu_x4 : entity work.alu1
port map (
a => input_a(bitindex),
b => input_b(bitindex),
m => mode,
result => result_x4(bitindex) );
end generate;

Resources