Division with ieee.numeric_std

Discussion in 'VHDL' started by Gabriel Schuster, Sep 21, 2006.

1. Gabriel SchusterGuest

Hi everybody,

I've got a little problem with the division module (part of a
microcontroller alu) in my current project: I found a nice code sample
in the www implementing a similar module.
Originally ieee.std_logic_arith was used to create it, which I strictly
avoid in my project so I rewrote the divider module using
ieee.numeric_std... but unfortunately it doesn't work so far.

I think there's something wrong in the assignments / conversion between
std_logic_vector and unsigned. I'd appreciate any help, because I'm used
to work with std_logic_arith and I think I'll have more of these
conversion problems in the near future.
How is this conversion done the right way???

Here are the sources of the two divider modules. I also got two

Thanks
Gabriel

=====================WORKING=========================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity divider is

generic (DWIDTH : integer := 8);

port (dvdnd_i : in std_logic_vector(DWIDTH-1 downto 0); -- Dividend
dvsor_i : in std_logic_vector(DWIDTH-1 downto 0); -- Divisor
qutnt_o : out std_logic_vector(DWIDTH-1 downto 0); --
Quotient
rmndr_o : out std_logic_vector(DWIDTH-1 downto 0);
ov_o : out std_logic); -- Remainder

end divider;

architecture rtl of divider is
begin -- rtl

ov_o <= '1' when dvsor_i = "00000000" else '0';

-- purpose: Divide dvdnd_i through dvsor_i and deliver the result to
qutnt_o
-- and the remainder to rmndr_o.
-- type : combinational
-- inputs : dvdnd_i, dvsor_i
-- outputs: qutnt_o, rmndr_o
p_divide: process (dvdnd_i, dvsor_i)

variable v_actl_dvdnd : unsigned(DWIDTH-1 downto 0);
variable v_dffrnc : unsigned(DWIDTH-1 downto 0);
variable v_qutnt : unsigned(DWIDTH-1 downto 0);

begin -- process p_divide

v_actl_dvdnd := unsigned(dvdnd_i);

for i in DWIDTH-1 downto 0 loop
-- If the divisor can be subtracted from this part of the
dividend, then
-- the corresponding bit of the quotient has to be 1, otherwise 0.
if conv_std_logic_vector(v_actl_dvdnd(DWIDTH-1 downto i),DWIDTH) >=
dvsor_i then
-- Divisor can be subtracted
v_qutnt(i) := '1';
v_dffrnc := conv_unsigned(v_actl_dvdnd(DWIDTH-1 downto i),DWIDTH)
- unsigned(dvsor_i);
-- As long as this is not the last step of calculation, shift the
-- intermediate result.
if i /= 0 then
v_actl_dvdnd(DWIDTH-1 downto i) := v_dffrnc(DWIDTH-1-i downto 0);
v_actl_dvdnd(i-1) := dvdnd_i(i-1);
end if;
else
-- Divisor is greater than this part of the dividend.
v_qutnt(i) := '0';
v_dffrnc := conv_unsigned(v_actl_dvdnd(DWIDTH-1 downto i),DWIDTH);
end if;
end loop; -- i

rmndr_o <= std_logic_vector(v_dffrnc);
qutnt_o <= std_logic_vector(v_qutnt);

end process p_divide;

end rtl;

=============================NOT WORKING=============================
-- remark:
-- this is the numeric_std version
-- it's possible to simulate the other one with ghdl using --ieee=synopsis
-- but several people suggested to switch to the "real" ieee-stuff
--

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

entity alu_div is
port (
op1_i : in std_logic_vector(7 downto 0); -- dividend
op2_i : in std_logic_vector(7 downto 0); -- divisor
resa_o : out std_logic_vector(7 downto 0); -- result
resb_o : out std_logic_vector(7 downto 0); -- remainder
ov_o : out std_logic
);
end alu_div;

architecture rtl of alu_div is
begin

ov_o <= '1' when (op2_i="00000000") else '0';

p_divide: process (op1_i,op2_i)
variable v_result : unsigned(7 downto 0);
variable v_remainder : unsigned(7 downto 0);
variable v_tmp : unsigned(7 downto 0);
begin
v_tmp := unsigned(op1_i);
for i in 7 downto 0 loop
if (std_logic_vector(v_tmp(7 downto i)) >= op2_i) then
v_result(i) := '1';
v_remainder := unsigned(v_tmp(7 downto i)) - unsigned(op2_i);
if (i /= 0) then
v_tmp(7 downto i) := v_remainder(7-i downto 0);
v_tmp(i-1) := op1_i(i-1);
end if;
else
v_result(i) := '0';
v_remainder := "00000000";
v_remainder(7-i downto 0) := unsigned(v_tmp(7 downto i));
--v_remainder := to_unsigned(to_integer(v_tmp(7 downto i)),8);
end if;
end loop; -- i

resa_o <= std_logic_vector(v_result);
resb_o <= std_logic_vector(v_remainder);

end process p_divide;

end rtl;

Gabriel Schuster, Sep 21, 2006

2. Gabriel SchusterGuest

tb_divider.vhdl

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
library std;
use std.textio.all;

entity tb_divider is
generic (DWIDTH : integer := 8);
end tb_divider;

architecture behave of tb_divider is

function printb (bv : in std_logic_vector) return string is
alias bv_norm : std_logic_vector(1 to bv'length) is bv;
variable result : string(1 to bv'length);
begin
for index in bv_norm'range loop
if bv_norm(index) = '0' then
result(index) := '0';
else
result(index) := '1';
end if;
end loop;
return result;
end printb;

component divider
port (dvdnd_i : in std_logic_vector(DWIDTH-1 downto 0); -- Dividend
dvsor_i : in std_logic_vector(DWIDTH-1 downto 0); -- Divisor
qutnt_o : out std_logic_vector(DWIDTH-1 downto 0); --
Quotient
rmndr_o : out std_logic_vector(DWIDTH-1 downto 0);
ov_o : out std_logic); -- Remainder
end component divider;

signal op1_i : std_logic_vector(7 downto 0) := "00000000";
signal op2_i : std_logic_vector(7 downto 0) := "00000000";
signal resa_o : std_logic_vector(7 downto 0);
signal resb_o : std_logic_vector(7 downto 0);
signal ov_o : std_logic;

begin

uut: divider
port map (
dvdnd_i => op1_i,
dvsor_i => op2_i,
qutnt_o => resa_o,
rmndr_o => resb_o,
ov_o => ov_o
);

p_testbench: process
variable l : line;
begin
for i in 0 to 255 loop
for j in 0 to 255 loop
op1_i <= conv_std_logic_vector(i,8);
op2_i <= conv_std_logic_vector(j,8);
wait for 10 ns;
write(l,string'("OP1 :"));
write(l,printb(op1_i));
write(l,string'(" / OP2 :"));
write(l,printb(op2_i));
writeline(output,l);
write(l,string'("RESA:"));
write(l,printb(resa_o));
write(l,string'(" / RESB:"));
write(l,printb(resb_o));
write(l,string'(" / OV:"));
if (ov_o = '1') then
write(l,string'("1"));
else
write(l,string'("0"));
end if;
writeline(output,l);
if (op2_i = "00000000") then
assert (ov_o = '1')
report "ERROR: DIV BY Zero - OV-Assignment"
severity failure;
else
assert (ov_o = '0')
report "ERROR: DIV - OV-Assignment"
severity failure;
assert (resa_o = conv_std_logic_vector(i/j,8))
report "ERROR: DIV - RESA-Assignment"
severity failure;
assert (resb_o = conv_std_logic_vector(i rem j,8))
report "ERROR: DIV - RESB-Assignment"
severity failure;
end if;
end loop;
end loop;
assert false
report "Simulation successfully finished."
severity note;
wait;
end process p_testbench;

end behave;

Gabriel Schuster, Sep 21, 2006

3. Gabriel SchusterGuest

tb_alu_div.vhdl

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

entity tb_alu_div is
end tb_alu_div;

architecture behave of tb_alu_div is

function printb (bv : in std_logic_vector) return string is
alias bv_norm : std_logic_vector(1 to bv'length) is bv;
variable result : string(1 to bv'length);
begin
for index in bv_norm'range loop
if bv_norm(index) = '0' then
result(index) := '0';
else
result(index) := '1';
end if;
end loop;
return result;
end printb;

component alu_div
port (
op1_i : in std_logic_vector(7 downto 0); -- dividend
op2_i : in std_logic_vector(7 downto 0); -- divisor
resa_o : out std_logic_vector(7 downto 0); -- result
resb_o : out std_logic_vector(7 downto 0); -- remainder
ov_o : out std_logic
);
end component alu_div;

signal op1_i : std_logic_vector(7 downto 0) := "00000000";
signal op2_i : std_logic_vector(7 downto 0) := "00000000";
signal resa_o : std_logic_vector(7 downto 0);
signal resb_o : std_logic_vector(7 downto 0);
signal ov_o : std_logic;

begin

uut: alu_div
port map (
op1_i => op1_i,
op2_i => op2_i,
resa_o => resa_o,
resb_o => resb_o,
ov_o => ov_o
);

p_testbench: process
variable l : line;
variable v_16 : unsigned(15 downto 0);
begin
v_16 := "0000000000000000";
for i in 0 to 65535 loop
op1_i <= std_logic_vector(v_16(15 downto 8));
op2_i <= std_logic_vector(v_16(7 downto 0));
wait for 10 ns;
write(l,string'("OP1 :"));
write(l,printb(op1_i));
write(l,string'(" / OP2 :"));
write(l,printb(op2_i));
writeline(output,l);
write(l,string'("RESA:"));
write(l,printb(resa_o));
write(l,string'(" / RESB:"));
write(l,printb(resb_o));
write(l,string'(" / OV:"));
if (ov_o = '1') then
write(l,string'("1"));
else
write(l,string'("0"));
end if;
writeline(output,l);
if (op2_i = "00000000") then
assert (ov_o = '1')
report "ERROR: DIV BY Zero - OV-Assignment"
severity failure;
else
assert (ov_o = '0')
report "ERROR: DIV - OV-Assignment"
severity failure;
assert (resa_o = std_logic_vector(unsigned(op1_i) /
unsigned(op2_i)))
report "ERROR: DIV - RESA-Assignment"
severity failure;
assert (resb_o = std_logic_vector(unsigned(op1_i) rem
unsigned(op2_i)))
report "ERROR: DIV - RESB-Assignment"
severity failure;
end if;
v_16 := v_16 + 1;
end loop;
assert false
report "Simulation successfully finished."
severity note;
wait;
end process p_testbench;

end behave;

Gabriel Schuster, Sep 21, 2006
4. AndyGuest

The following statement probably does not do what you want:

if (std_logic_vector(v_tmp(7 downto i)) >= op2_i) then

What you have is a comparison of two slvs, which iirc, is based on
length, not value(?).

You should convert op2_i to unsigned, and maybe have to resize() on
v_tmp, but you do not want to convert it to slv!

Be careful what you ask for, because the compiler will give you exactly
that!

Andy

Andy, Sep 21, 2006
5. Gabriel SchusterGuest

Thanks a lot!!!
I've changed it to

if (to_integer(v_tmp(7 downto i)) >= to_integer(unsigned(op2_i))) then

and it works perfectly!!!

Andy wrote:
> The following statement probably does not do what you want:
>
> if (std_logic_vector(v_tmp(7 downto i)) >= op2_i) then
>
> What you have is a comparison of two slvs, which iirc, is based on
> length, not value(?).
>
> You should convert op2_i to unsigned, and maybe have to resize() on
> v_tmp, but you do not want to convert it to slv!
>
> Be careful what you ask for, because the compiler will give you exactly
> that!
>
> Andy
>

Gabriel Schuster, Sep 22, 2006
6. Nicolas MatringeGuest

Gabriel Schuster a écrit :
> Thanks a lot!!!
> I've changed it to
>
> if (to_integer(v_tmp(7 downto i)) >= to_integer(unsigned(op2_i))) then

You can drop the conversions
if (v_tmp(7 downto i) >= unsigned(op2_i)) then

Nicolas

Nicolas Matringe, Sep 22, 2006