I'm new to FPGA. I'm trying to replicate PIC16Fxxx core as an exersize
(any real programmer should write at least one OS and compiler :)
I'm trying to synthesize a simple ALU. I'm using VHDL and XST (WebPack).
Target is SpartanIIE. It sortof works but is rather inefficient.
At first I tried a big case statement for all ALU operations.
XST happily infers lots of built-in macros (one for each ALU op)
and a huge output mux. For example it produces 6 carry-chain adders
(one for each ADD, SUB, INC, DEC and another two to get the
half-carry bit for ADD/SUB) where I would think one is enough.
I've narrowed the problem down to a simple adder/subtractor:
if add='1' then
Y <= A + B;
else
Y <= A - B;
end if;
This works fine, produces a single 8-bit adder/subtractor. 4 slices in total.
But this does not give me carry/borrow bit.
if add='1' then
Y <= ('0' & A) + ('0' & B);
else
Y <= ('0' & A) - ('0' & B);
end if;
produces 8bit adder with carry-out, a separate 9bit subtractor and
a 9bit 2x1 mux. 9 slices. I tried different variations of the above
with the same results.
Finally I have come up with the following code.
It uses the fact that A-B = A +(-B) = A + ((not B) + 1).
variable tmp: integer;
variable cin: std_logic;
if op = '1' then
tmp := conv_integer(B);
cin := '0';
else
tmp := conv_integer(not B);
cin := '1';
end if;
Y <= conv_std_logic_vector(conv_integer(A) + tmp + conv_integer(cin),9);
This infers 1 "9bit adder carry in" and 8 2x1 muxes and takes only 4 slices.
Much better. One small detail: if I declare cin as integer instead
of converting it from std_logic at the last step, I'm back to 9 slices.
Now the questions.
* Am I on the right track?
* I'm trying to describe purely combinatorial logic here. The output
is supposed to be the same fixed boolean function of inputs no matter
how it is described. Why such big variations (more than 2 times the area)?
Is this a problem with the tool or they all like that?
* Should I be tweaking XST settings instead? Is there a magic setting
like "Do what I mean not what I say" :)
* Xilinx lib has "8bit adder carry out" but it doesn't seem to have
"8bit subtractor borrow out". Is this right?
* How do I get the half-carry bit out of the 8bit adder? I guess I can
instantiate/infer two separate 4bit adders. Is there a better way?
* What's the story with IEEE.std_logic.SIGNED vs .UNSIGNED? I heard that
they are are mutually exclusive and math operations produce different
results depending on which one is in use. Webpack automatically inserts
IEEE.STD_LOGIC_UNSIGNED.ALL at the beginning of every VHDL source it
creates. Should I always use UNSIGNED?
* Is there a decent on-line reference for all those IEEE.* libraries?
I've found several good VHDL tutorials but none of them covers
std_logic in details.
Thanks,
Dmitri
Believe me, I have been there and I couldn't find a nice way to do what I wanted.
Which was a add/sub with carry-in and carry-out.
I spend a day trying all sorts of way to express that but only ended up with half
solutions.
Then I just instanciated LUT, MUXCY and XORCY using a generate loop.
That took me 5 min to code which it's way faster than trying to foul the tools.
I have found out that if I have to go around the synthesize tool by changing my
code,
it's way faster to just instanciated what I want.
That also gives me the possibility to RLOC the components and do even more stuff
using the MULT_AND
and set/reset on the DFFs.
Göran
> Hi,
>
Hi Dimitri,
> ...
> Now the questions.
>
> * Am I on the right track?
As you know what you want you could express it directly:
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
...
signal IN1, IN2: UNSIGNED(8 downto 0);
signal CIN: NATURAL range 0 to 1;
...
IN1 <= '0' & A;
IN2 <= '0' & B when ADD = '1' else
'1' & not B;
CIN <= 0 when ADD = '1' else
1;
Y <= STD_LOGIC_VECTOR(IN1 + IN2 + CIN);
>
> * I'm trying to describe purely combinatorial logic here. The
> output is supposed to be the same fixed boolean function of inputs
> no matter how it is described. Why such big variations (more than
> 2 times the area)? Is this a problem with the tool or they all
> like that?
They're all like that.
> * What's the story with IEEE.std_logic.SIGNED vs .UNSIGNED? I
> heard that they are are mutually exclusive and math operations
> produce different results depending on which one is in use.
> Webpack automatically inserts IEEE.STD_LOGIC_UNSIGNED.ALL at the
> beginning of every VHDL source it creates. Should I always use
> UNSIGNED?
use IEEE.NUMERIC_STD instead of all these non-standard packages.
Regards,
--
Renaud Pacalet, ENST / COMELEC, 46 rue Barrault 75634 Paris Cedex 13
Tel. : 01 45 81 78 08 | Fax : 01 45 80 40 36 | Mel : pac...@enst.fr
I would suggest you to work a bit level. Create a bit cell able to
perform all the operation, not only add and subtract. The bit cell
will have two inputs A and B for operands, one input Cin for carry-in,
two inputs S0 and S1 to select the operation, one output for result
and one for Cout (carry out). Basically, you need a 4 inputs mux with
two selection bits. The inputs of the mux will be
A – used for NOPs and other instruction which don’t
change the Accumulator
A or B – used for OR instruction and set bit instruction
A and B – used for AND instructions and clear bit instructions
A xor B used for XOR, ADD and SUB (if you apply an inverted operand
at the input B and set Cy to 1 if no borrow).
I didn’t enter in details (you should use also Generate and
Propagate signals for speed, you should think how two make rotate and
shift, bit operations and test bit operations), I just want to suggest
to use a structural description rather than a behavioral one if you
want to optimize the design.
Regards,
Adrian
Goran speaks the the truth here. This is why a large part of our internal library is
structurally instantiated.
The trouble with an add/sub with carry-in is that for subtraction you need to do A +
!B +1, which means the +1 has to go into the carry chain as the carry in, so you'll
have trouble making that work in 1 level of logic unless you do the +1 in other logic
either before or after the add/sub stage.
The trick to getting the synth to produce what you want most of the time is to code
it up with a structure exactly mimicking the slice, which is to say code it so that
you do any muxing etc in front of the add:
B_inv<= B when sub='1' else not B;
cin <= 1 when sub='1' else 0;
d<= A + Binv + cin;
If need be, you can put syn_keeps on the inputs of that logic to keep the synth
honest.
Goran Bilski wrote:
--
--Ray Andraka, P.E.
President, the Andraka Consulting Group, Inc.
401/884-7930 Fax 401/884-7950
email r...@andraka.com
http://www.andraka.com
"They that give up essential liberty to obtain a little
temporary safety deserve neither liberty nor safety."
-Benjamin Franklin, 1759
This is the add/sub part of the ALU from the risc5x core on opencores, where
you can also get the package and the generic vhdl / simulation model.
Output is A + B, A -B or A.
The trick is to force a one on the carry in when doing a subtract.
Logic usage : 1 slice for every 2 bits.
Some people argue (rightly so) that this level of code is unreadable. True,
it is, but you can build up a library of these things which have been
simulated to death (I have simulation models of LUT4, MUXCY etc) and then
just use them when you need them.
hope this helps,
Mike.
--
-- Risc5x
-- www.OpenCores.Org - November 2001
--
--
-- This library is free software; you can distribute it and/or modify it
-- under the terms of the GNU Lesser General Public License as published
-- by the Free Software Foundation; either version 2.1 of the License, or
-- (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful, but
-- WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-- See the GNU Lesser General Public License for more details.
--
-- A RISC CPU core.
--
-- (c) Mike Johnson 2001. All Rights Reserved.
-- mikej@<NOSPAM>opencores.org for support or any other issues.
--
-- Revision list
--
-- version 1.0 initial opencores release
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
--
-- op <= A +/- B or A
--
entity ADD_SUB is
generic (
WIDTH : in natural := 8
);
port (
A : in std_logic_vector(WIDTH-1 downto 0);
B : in std_logic_vector(WIDTH-1 downto 0);
ADD_OR_SUB : in std_logic; -- high for DOUT <= A +/- B, low for DOUT
<= A
DO_SUB : in std_logic; -- high for DOUT <= A - B, low for DOUT
<= A + B
CARRY_OUT : out std_logic_vector(WIDTH-1 downto 0);
DOUT : out std_logic_vector(WIDTH-1 downto 0)
);
end;
use work.pkg_xilinx_prims.all;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
architecture VIRTEX of ADD_SUB is
signal lut_op : std_logic_vector(WIDTH-1 downto 0);
signal mult_and_op : std_logic_vector(WIDTH-1 downto 0);
signal carry : std_logic_vector(WIDTH downto 0);
signal op_int : std_logic_vector(WIDTH-1 downto 0);
function loc(i : integer) return integer is
begin
return (((WIDTH+1)/2)-1) - i/2;
end loc;
begin
carry(0) <= DO_SUB;
INST : for i in 0 to WIDTH-1 generate
attribute RLOC of u_lut : label is "R" & integer'image(loc(i)) &
"C0.S1";
attribute RLOC of u_1 : label is "R" & integer'image(loc(i)) &
"C0.S1";
attribute RLOC of u_2 : label is "R" & integer'image(loc(i)) &
"C0.S1";
attribute RLOC of u_3 : label is "R" & integer'image(loc(i)) &
"C0.S1";
attribute INIT of u_lut : label is "C66C";
begin
u_lut : LUT4
--pragma translate_off
generic map (
INIT => str2slv(u_lut'INIT)
)
--pragma translate_on
port map (
I0 => ADD_OR_SUB,
I1 => A(i),
I2 => B(i),
I3 => DO_SUB,
O => lut_op(i)
);
u_1 : MULT_AND
port map (
I0 => ADD_OR_SUB,
I1 => A(i),
LO => mult_and_op(i)
);
u_2 : MUXCY
port map (
DI => mult_and_op(i),
CI => carry(i),
S => lut_op(i),
O => carry(i+1)
);
u_3 : XORCY
port map (
LI => lut_op(i),
CI => carry(i),
O => op_int(i)
);
end generate;
CARRY_OUT <= carry(WIDTH downto 1);
DOUT <= op_int;
end Virtex;
<SNIP>
Dmitri
dmi...@mailandnews.com (Dmitri Katchalov) wrote in message news:<3db7c986.02072...@posting.google.com>...
> I'm trying to synthesize a simple ALU.
Yes, but this will be somewhat compilier dependent.
> * I'm trying to describe purely combinatorial logic here. The output
> is supposed to be the same fixed boolean function of inputs no matter
> how it is described. Why such big variations (more than 2 times the area)?
> Is this a problem with the tool or they all like that?
As you said, "any real programmer should write at least one OS and
compiler", try writing code to or even just figuring out how to
translate this stuff into hardware. Not so easy. Compliers are simple
in comparison.
> * Should I be tweaking XST settings instead? Is there a magic setting
> like "Do what I mean not what I say" :)
No, issues with carry and the like are not easy since different chips
deal with them differently. So the compiler needs to be able to map to
different architectures.
> * Xilinx lib has "8bit adder carry out" but it doesn't seem to have
> "8bit subtractor borrow out". Is this right?
Don't know, but as you found, an adder and a subtractor are the same
thing with inverters on one input and the carries.
> * How do I get the half-carry bit out of the 8bit adder? I guess I can
> instantiate/infer two separate 4bit adders. Is there a better way?
The last time I tried to get a carry out of the middle of a carry chain,
I found that the Xilinx architecture does not support that without
breaking the carry chain. So it will need to be done with two 4 bit
adders, as you say.
> * What's the story with IEEE.std_logic.SIGNED vs .UNSIGNED? I heard that
> they are are mutually exclusive and math operations produce different
> results depending on which one is in use. Webpack automatically inserts
> IEEE.STD_LOGIC_UNSIGNED.ALL at the beginning of every VHDL source it
> creates. Should I always use UNSIGNED?
Both of these libraries are NOT IEEE standards. They are Synopsis
proprietary IIRC. So avoid using them and use the "numeric_std" library
instead.
use IEEE.NUMERIC_STD.all;
> * Is there a decent on-line reference for all those IEEE.* libraries?
> I've found several good VHDL tutorials but none of them covers
> std_logic in details.
If you find one, let us all know. Type conversion is the only thing I
have trouble with in VHDL. I recently worked with some Verilog people
and could not convince them that VHDL was even viable because of all the
issues created by strong typing. Verilog is much like C and lets you do
anything you want, no matter how stupid or wrong. But then in a year of
coding, I only made two mistakes from that and it was the same mistake
twice! Sometimes I am a little slow to learn :)
--
Rick "rickman" Collins
rick.c...@XYarius.com
Ignore the reply address. To email me use the above address with the XY
removed.
Arius - A Signal Processing Solutions Company
Specializing in DSP and FPGA design URL http://www.arius.com
4 King Ave 301-682-7772 Voice
Frederick, MD 21701-3110 301-682-7666 FAX
rickman wrote:
> A couple of comments for points that were not fully addressed.
>
>
I think there is an adder/subtractor in the coregen, if you insist on using a
generated core.
>
> > * Xilinx lib has "8bit adder carry out" but it doesn't seem to have
> > "8bit subtractor borrow out". Is this right?
>
> Don't know, but as you found, an adder and a subtractor are the same
> thing with inverters on one input and the carries.
>
> > * How do I get the half-carry bit out of the 8bit adder? I guess I can
> > instantiate/infer two separate 4bit adders. Is there a better way?
It can be done, but it takes a little mind-bending. Basically, you need to turn
your 8 bit adder into a 9 bit one with bit 4 being a dummy so that you can pull out
the carry out through the bit. It takes a bit of caressing the tools to make them
infer it.
Ray Andraka <r...@andraka.com> writes:
> It can be done, but it takes a little mind-bending. Basically, you
> need to turn your 8 bit adder into a 9 bit one with bit 4 being a
> dummy so that you can pull out the carry out through the bit. It
> takes a bit of caressing the tools to make them infer it.
Is there any advantage to doing that rather than two four-bit adders?
For instance, with two four-bit adders, does the synthesizer not
recognize that it can continue the carry chain between them? Or
does the FPGA not allow you to tap the carry from intermediate stages
of the chain?
Eric Smith wrote:
--
Using your suggestions I've managed to implement PIC-style
ADD/SUB/INC/DEC with carry and half-carry out in just 4 slices, see code below.
I'm not sure about the polarity of the borrow bit though.
Synthesis infers 2 5-bit adders, later optimised into 4-bit
adders with carry in/out. P&R places them in one column one immediately
on top of another (in otherwise empty FPGA). I don't have suffucient
knowledge to tell from all those the reports whether the carry chain
is broken or continues over. It does seem to continue over.
Here is the code, comments appreciated.
Regards,
Dmitri
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity alu_adder is
Port ( A,B: in std_logic_vector(7 downto 0);
op: in std_logic_vector(1 downto 0);
Y: out std_logic_vector(7 downto 0);
carry_out: out std_logic;
dc_out: out std_logic );
constant ADD : std_logic_vector(1 downto 0) := "00";
constant SUB : std_logic_vector(1 downto 0) := "01";
constant DEC : std_logic_vector(1 downto 0) := "10";
constant INC : std_logic_vector(1 downto 0) := "11";
end entity alu_adder;
architecture Behavioral of alu_adder is
begin
process( A, B, op )
variable tmp: std_logic_vector(7 downto 0);
variable lo_nibble, hi_nibble: unsigned(5 downto 0);
variable cin: std_logic;
begin
case op is
when INC => tmp := (others => '0'); cin := '1';
when DEC => tmp := (others => '1'); cin := '0';
when SUB => tmp := not B; cin := '1';
when ADD => tmp := B; cin := '0';
when others => tmp := (others => '-'); cin := '-';
end case;
lo_nibble := unsigned('0' & A(3 downto 0) & cin ) +
unsigned('0' & tmp(3 downto 0) & cin );
hi_nibble := unsigned('0' & A(7 downto 4) & lo_nibble(5) ) +
unsigned('0' & tmp(7 downto 4) & lo_nibble(5) );
Y <= std_logic_vector( hi_nibble(4 downto 1) & lo_nibble(4 downto 1));
dc_out <= lo_nibble(5);
carry_out <= hi_nibble(5);
end process;
end architecture Behavioral;
Ray Andraka <r...@andraka.com> wrote in message news:<3D43FE74...@andraka.com>...
Dmitri Katchalov wrote:
--
Mikej
"Ray Andraka" <r...@andraka.com> wrote in message
news:3D4541C8...@andraka.com...