FPGARelated.com
Forums

subtractor

Started by Olaf Petzold November 25, 2005
Hi,

if I synthese the followng code (substract using two's-complement and 
adder) using xst and then have a look to the RTL schematic, the co 
output is on ground and a 16bit adder is infered. The carry out (co) 
is the interesting signal for me. Did I wrote wrong code (TB not yet)? 
How to correct it? BTW, is there a way to 'tune' this entities 
especially to avoid such castings and conversations?

Thanks
Olaf

---8<---
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity adder is
    generic (
       WIDTH : natural := 16);
    port (
       a, b : in  std_logic_vector(WIDTH-1 downto 0);
       sum  : out std_logic_vector(WIDTH-1 downto 0);
       co   : out std_logic);
end entity adder;

architecture rtl of adder is
    signal tmp      : std_logic_vector(WIDTH downto 0);
    signal a_i, b_i : natural range 0 to 2**WIDTH;
begin
    a_i <= to_integer(unsigned(a));
    b_i <= to_integer(unsigned(b));
    tmp <= std_logic_vector(to_unsigned(a_i + b_i, tmp'length));
    sum <= tmp(WIDTH-1 downto 0);
    co  <= tmp(WIDTH);
end architecture rtl;


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity subtractor is
    generic (
       WIDTH : natural := 16);
    port (
       a, b : in  std_logic_vector(WIDTH-1 downto 0);
       diff : out std_logic_vector(WIDTH-1 downto 0);
       co   : out std_logic);
end entity subtractor;

architecture rtl of subtractor is
    -- two's-complement
    signal b_2c : std_logic_vector(WIDTH-1 downto 0);
begin
    b_2c <= std_logic_vector(to_unsigned(to_integer(unsigned(not b)) + 
1, b_2c'length));

    adder_i : entity work.adder
       generic map (
          WIDTH => WIDTH)
       port map (
          a   => a,
          b   => b_2c,
          sum => diff,
          co  => co);
end architecture rtl;
there seems to be some problems on this line:

> b_2c <= std_logic_vector(to_unsigned(to_integer(unsigned(not b)) + 1, > b_2c'length));
if b is zero, I got the warning: NUMERIC_STD.TO_UNSIGNED: vector truncated. Well, it's ok, since complement of 0x00 is 0xFF and +1 got overflow. The interesting part is, that the testbench with zero fails. Are there some thing to care about? Thanks Olaf
> architecture rtl of subtractor is > -- two's-complement > signal b_2c : std_logic_vector(WIDTH-1 downto 0); > begin > b_2c <= std_logic_vector(to_unsigned(to_integer(unsigned(not b)) + 1, > b_2c'length)); > > adder_i : entity work.adder > generic map ( > WIDTH => WIDTH) > port map ( > a => a, > b => b_2c, > sum => diff, > co => co); > end architecture rtl;
somethings is wrong here. The adder TB success. The TB for this entity fails: # ** Note: Verify Test #0: 27 - 9 = 18; co = '0' vs. got = '1' # ** Note: got wrong carry = '1' # ** Note: Verify Test #1: 39 - 0 = 39; co = '0' vs. got = '0' # ** Note: Verify Test #2: 0 - 0 = 0; co = '0' vs. got = '0' # ** Note: Verify Test #3: 255 - 0 = 255; co = '0' vs. got = '0' # ** Note: Verify Test #4: 10 - 20 = 246; co = '1' vs. got = '0' # ** Note: got wrong carry = '0' # ** Note: Verify Test #5: 0 - 255 = 1; co = '1' vs. got = '0' # ** Note: got wrong carry = '0' Does someone say what gone wrong? Only the carry flag fails. Thanks Olaf
> architecture rtl of subtractor is > -- two's-complement > signal b_2c : std_logic_vector(WIDTH-1 downto 0); > begin > b_2c <= std_logic_vector(to_unsigned(to_integer(unsigned(not b)) + 1, > b_2c'length));
a simplification doesn't help: constant ONE : unsigned(WIDTH-1 downto 0) := to_unsigned(1, WIDTH); -- two's-complement signal b2c : std_logic_vector(WIDTH-1 downto 0); ... b2c <= std_logic_vector(unsigned(not b) + ONE);
Olaf Petzold schrieb:
> Hi, > > if I synthese the followng code (substract using two's-complement and > adder) using xst and then have a look to the RTL schematic, the co > output is on ground and a 16bit adder is infered. The carry out (co) is > the interesting signal for me. Did I wrote wrong code (TB not yet)? How > to correct it? BTW, is there a way to 'tune' this entities especially to > avoid such castings and conversations? > > Thanks > Olaf >
Hi Olaf, think about the results numeric range. Can it be larger than the biggest number when you do a subtraction in a signed range (remember, you are doing two's complement!) because the result will be two's complement too. In fact there is no other result for co than '0'. Also you are not using a ci. (which is not really important for a subtractor) a more compact code would look like this: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity simple_sub is Port ( a : in std_logic_vector(15 downto 0); b : in std_logic_vector(15 downto 0); diff : out std_logic_vector(15 downto 0); co : out std_logic); end simple_sub; architecture Behavioral of simple_sub is signal total_sum : std_logic_vector(16 downto 0); begin total_sum <= a-b; diff <= total_sum (15 downto 0); co <= total_sum(16); end Behavioral; Your synthesis result: Number of Slices: 17 My synthesis result : Number of Slices: 8 Because I get a single subtractor while you are infering two adders. Write a testbench and compare both. I'm using the std_logic libs because it's in the xilinx template, try for yourself how it can be done with std_numeric. (Please no discussion about which lib to prefer here. There are enough postings about this topic.) The important point: Don't re-invent the wheel, when you have a wheelfactory in the neighbourhood. The synthesis tools today know how to subtract, just let them do their job :-) have a nice synthesis Eilert
> # ** Note: Verify Test #4: 10 - 20 = 246; co = '1' vs. got = '0' > # ** Note: got wrong carry = '0'
8 bit only (for convenience) 10 = 8 + 2 = 00001010 20 = 16 + 4= 00010100 inverted: 11101011 add one : 11101100 Do the addition : 00001010 + 11101100 = 11110110 No carry needed! -1*2^7+1*2^6+1*2^5+1*2^4+1*2^2+1*2^1 = -128+64+32+16+4+2 = -10 calculation correct! So what's your TB expecting??? Probably you are searching for the (here) ninth bit of the result which should be '1' if the result is negative. But don't confuse this with a carry signal! That's two totally different things!
thank you Eilert. Unfortunally it doesn't compile, imo it's the same 
as you wrote:

# ** Error: D:/electronic/projects/la/la/source/vhdl/sub.vhd(33): 
Length of expected is 17; length of actual is 16.
# ** Error: D:/electronic/projects/la/la/source/vhdl/sub.vhd(40): 
Length of expected is 17; length of actual is 16.

The same error I get on using unsigned of ieee.numeric_std. Here the 
complete file:

---8<---
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity sub is
end entity sub;

architecture behave of sub is
    constant BIT_WIDTH     : natural   := 16;
    constant PERIOD        : time      := 10 ns;
    signal   lower_bound   : std_logic_vector(BIT_WIDTH-1 downto 0);
    signal   upper_bound   : std_logic_vector(BIT_WIDTH-1 downto 0);
    signal   x             : std_logic_vector(BIT_WIDTH-1 downto 0);
    signal   out_of_limits : std_logic;
    signal   within_limits : std_logic;
    signal   le, ge        : std_logic;
    signal   tb_clk        : std_logic := '0';
    signal   tb_done       : boolean   := false;
begin
    tb_clk_proc : process is
    begin
       tb_clk <= '0';
       if tb_done then wait; end if;     -- stops clock
       wait for PERIOD/2;                -- clock low
       tb_clk <= '1';
       wait for PERIOD/2;                -- clock high
    end process tb_clk_proc;

    le_proc : process (upper_bound, x) is
       variable diff : std_logic_vector(BIT_WIDTH downto 0);
    begin
       diff := upper_bound - x; -- LINE 33
       le   <= diff(BIT_WIDTH);
    end process le_proc;

    ge_proc : process (lower_bound, x) is
       variable diff : std_logic_vector(BIT_WIDTH downto 0);
    begin
       diff := x - lower_bound; -- LINE 40
       ge   <= diff(BIT_WIDTH);
    end process ge_proc;

    within_limits <= le and ge;
    out_of_limits <= le xor ge;

    tb : process is
    begin
       lower_bound <= conv_std_logic_vector(27, BIT_WIDTH);
       x           <= conv_std_logic_vector(0, BIT_WIDTH);
       upper_bound <= conv_std_logic_vector(129, BIT_WIDTH);
       wait until rising_edge(tb_clk);
       -- report "within_limits = " & std_logic'image(within_limits);
       -- report "out_of_limits = " & std_logic'image(out_of_limits);
       assert (out_of_limits = '1') and (within_limits = '0')
          report "test failed" severity error;
       tb_done <= true;
    end process tb;
end architecture behave;

Thanks
Olaf
Olaf Petzold wrote:
> > # ** Error: D:/electronic/projects/la/la/source/vhdl/sub.vhd(33): > Length of expected is 17; length of actual is 16. > # ** Error: D:/electronic/projects/la/la/source/vhdl/sub.vhd(40): > Length of expected is 17; length of actual is 16. > > > architecture behave of sub is > constant BIT_WIDTH : natural := 16;
OK, so BIT_WIDTH is 16.
> constant PERIOD : time := 10 ns; > signal lower_bound : std_logic_vector(BIT_WIDTH-1 downto 0); > signal upper_bound : std_logic_vector(BIT_WIDTH-1 downto 0);
lower_bound and upper_bound are (15 downto 0), or 16 bits wide.
> signal x : std_logic_vector(BIT_WIDTH-1 downto 0);
x is also (15 downto 0), 16 bits wide.
> le_proc : process (upper_bound, x) is > variable diff : std_logic_vector(BIT_WIDTH downto 0);
diff is (16 downto 0) or 17 bits wide.
> begin > diff := upper_bound - x; -- LINE 33
Here's the problem, and the error message is quite explicit. upper_bound and x are both 16 bits wide, and you're assigning them to a 17-bit vector.
> ge_proc : process (lower_bound, x) is > variable diff : std_logic_vector(BIT_WIDTH downto 0); > begin > diff := x - lower_bound; -- LINE 40
Same here: x and lower_bound are 16-bit values assigned to a 17-bit value. Can't do that. You simply need to extend x, upper_bound and lower_bound to be the same width as your result. In other words, something like (using numeric_std and appropriate types, either unsigned or signed): diff := resize(x, diff'length) - resize(lower_bound, diff'length); oughta work. -a
Hi Olaf,
shame on me.
I have synthesized, but not simulated my source.

A simple change of my code like this:

total_sum <= ('0' & a)-('0' & b);

would do the trick and give you a 17 bit result. Wether you call the MSB 
  carry out or not depends on you, but it's no longer ignored.

Of course the use of the resize function, like Andy Peters showed us, is 
to prefer, for its a more general solution.

diff := resize(x, diff'length) - resize(lower_bound, diff'length);

So,
  have a nice synthesis (after simulation)  :-)
     Eilert
On Fri, 25 Nov 2005 21:16:17 +0100, Olaf Petzold <olaf@mdcc-fun.net>
wrote:

>Hi, > >if I synthese the followng code (substract using two's-complement and >adder) using xst and then have a look to the RTL schematic, the co >output is on ground and a 16bit adder is infered. The carry out (co) >is the interesting signal for me. Did I wrote wrong code (TB not yet)? >How to correct it? BTW, is there a way to 'tune' this entities >especially to avoid such castings and conversations?
Yes there is: first note that numeric_std "signed" type is conveniently defined to be 2's complement, so it's a better choice than "unsigned" for your application; then note that if ports a, b, diff were signed, then diff <= b - a; would do the job. The only reasons go into more detailed manipulations are for exercise, OR if you need some other arithmetic, i.e. 1's complement or sign+magnitude. Then you could wrap that up in a "numeric_std" lookalike library, and let it support (e.g.) S+M subtraction as easily as "b-a"... - Brian