====== Array-Divider ======
===== Parameters =====
A short interface specification:
* The input widths of the model are parameterizable (@BITBR).
* The inputs and outputs will have the same bit width.
* The algorithm is implemented asynchronous without flipflops.
{{:synthesizeable_vhdl-model-library:lib_array_divider.svg?nolink&300|Array-Divider block diagram}}
===== Interface =====
dvd : Input port for the dividend
dvs : Input port for the divisor
quot : Output port for the quotient
rest : Output port for the remainder (2's complement)
===== Model =====
-- ############################################################################
-- # Project : VHDL-Modellbibliothek #
-- # #
-- # Filename : array_divider.vhd #
-- # #
-- # Schaltung : Array-Dividierer #
-- # #
-- # Modell : array_divider #
-- # #
-- # Designer : Wolfgang Sehr #
-- # Abteilung : Lehrstuhl fuer Rechnergestuetzten Schaltungsentwurf #
-- # Datum : 20.01.1995 #
-- ############################################################################
-- ############################################################################
-- # IEEE PACKAGES #
-- ############################################################################
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
-- ############################################################################
ENTITY array_divider IS
GENERIC(X :INTEGER := @BITBR); -- X := Operandenbreite
-- symmetrisches Array X * X
PORT(dvd : IN UNSIGNED ((X-1) DOWNTO 0);
dvs : IN UNSIGNED ((X-1) DOWNTO 0);
quot : OUT UNSIGNED ((X-1) DOWNTO 0);
rest : OUT UNSIGNED ((X-1) DOWNTO 0));
-- dvd : Eingangsport fuer Dividenden
--
-- dvs : Eingangsport fuer Divisor
--
-- quot : Ausgangsport fuer Quotienten
--
-- rest : Ausgangsport fuer Rest
--
-- -> Darstellung im 2'er Komplement
END array_divider;
ARCHITECTURE verhalten OF array_divider IS
BEGIN
PROCESS(dvd, dvs)
CONSTANT eins : STD_LOGIC := '1';
VARIABLE dvd_sgn : SIGNED ((X-1) DOWNTO 0);
VARIABLE dvs_sgn : SIGNED ((X-1) DOWNTO 0);
VARIABLE ztld : SIGNED ((2*X-2) DOWNTO 0);
VARIABLE tlr : SIGNED ((X-1) DOWNTO 0);
VARIABLE k_ztld : UNSIGNED ((ztld'LENGTH-1) DOWNTO 0);
VARIABLE k_tlr : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE vz : UNSIGNED ( 1 DOWNTO 0);
VARIABLE a : UNSIGNED ((ztld'LENGTH-1) DOWNTO 0);
VARIABLE a_ein : UNSIGNED ((ztld'LENGTH-1) DOWNTO 0);
VARIABLE b : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE k_b : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE q : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE k_q : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE r : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE k_r : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE sum : UNSIGNED ( tlr'LENGTH DOWNTO 0);
VARIABLE n_divisable : UNSIGNED ((tlr'LENGTH-1) DOWNTO 0);
VARIABLE n_div_detect : STD_LOGIC;
VARIABLE b_add : UNSIGNED ((tlr'LENGTH) DOWNTO 0);
VARIABLE zwischenrest : UNSIGNED ((X*X-1) DOWNTO 0);
-- k_ztld : komplementierter Dividend aufbereitet fuer den
-- : Divisionsalgorithmus
--
-- k_tlr : komplementierter Divisor
--
-- vz : 0 -> positives Vorzeichen
-- : 1 -> negatives Vorzeichen
-- : vz(1) Vorzeichen des Dividenden
-- : vz(0) Vorzeichen des Divisors
--
-- a : Variable fuer den aktuellen Dividenden
-- : waehrend der Ausfuehrumg des Divisions-
-- : algorithmus
--
-- a_ein : Variable fuer den Dividenden, der anfangs in
-- : Divisionsalgorithmus uebernommen wird
--
-- b : Variable fuer den Divisor (immer positiv)
--
-- k_b : komplemtierter Divisor (immer negativ)
--
-- q : Variable fuer den Quotienten
--
-- k_q : komplementierter Quotient
--
-- r : ganzzahliger Rest der Division
--
-- k_r : komplementierter ganzzahliger Rest der
-- : Division
--
-- sum : Ergebnis der Addition/Subtraktion in den
-- : einzelnen Stufen
--
-- n_divisable : ist in der Stufe i Null, ab der der Dividend
-- : nicht mehr weiter durch den Divisor
-- : teilbar ist
--
-- n_div_detect : ist 1, wenn der Dividend vor der letzten Zeile
-- : nicht mehr durch den Divisor teilbar ist
--
-- b_add : Zwischenspeichervariable fuer Addition
--
-- zwischenrest : Stellt den aktuellen Zwischenrest dar, der
-- : sich aus dem Ergebnis der Addition/Subtraktion
-- : und den noch nicht behandelten Stellen des
-- : Dividenden zusammensetzt.
BEGIN
-- ####################################################################
-- Eingangssignale bzw. komplementierte Eingangssignale werden den
-- internen Variablen des PROCESS zugewiesen
-- ####################################################################
dvd_sgn := CONV_SIGNED(dvd, dvd_sgn'LENGTH);
dvs_sgn := CONV_SIGNED(dvs, dvs_sgn'LENGTH);
ztld := CONV_SIGNED(dvd_sgn, ztld'LENGTH);
tlr := CONV_SIGNED(dvs_sgn, tlr'LENGTH);
FOR i IN 0 TO (ztld'LENGTH-1) LOOP
k_ztld(i) := NOT(ztld(i));
END LOOP;
k_ztld := k_ztld + eins;
FOR i IN 0 TO (tlr'LENGTH-1) LOOP
k_tlr(i) := NOT(tlr(i));
END LOOP;
k_tlr := k_tlr + eins;
IF (dvd(dvd'LENGTH-1) = '1') THEN
vz(1) := '1';
ELSE
vz(1) := '0';
END IF;
IF (dvs(dvs'LENGTH-1) = '1') THEN
vz(0) := '1';
ELSE
vz(0) := '0';
END IF;
CASE vz IS
WHEN "00" => a := CONV_UNSIGNED(ztld,a'LENGTH);
a_ein := CONV_UNSIGNED(ztld,a'LENGTH);
b := CONV_UNSIGNED(tlr, b'LENGTH);
WHEN "01" => a := CONV_UNSIGNED(ztld, a'LENGTH);
a_ein := CONV_UNSIGNED(ztld, a'LENGTH);
b := CONV_UNSIGNED(k_tlr, b'LENGTH);
WHEN "10" => a := CONV_UNSIGNED(k_ztld,a'LENGTH);
a_ein := CONV_UNSIGNED(k_ztld,a'LENGTH);
b := CONV_UNSIGNED(tlr, b'LENGTH);
WHEN "11" => a := CONV_UNSIGNED(k_ztld,a'LENGTH);
a_ein := CONV_UNSIGNED(k_ztld,a'LENGTH);
b := CONV_UNSIGNED(k_tlr, b'LENGTH);
WHEN OTHERS => a := CONV_UNSIGNED(0, a'LENGTH);
a_ein := CONV_UNSIGNED(0, a'LENGTH);
b := CONV_UNSIGNED(0, b'LENGTH);
END CASE;
-- ####################################################################
-- ####################################################################
-- Divisionsalgorithmus
-- ####################################################################
sum(tlr'LENGTH) := '1';
zwischenrest := CONV_UNSIGNED(0, zwischenrest'LENGTH);
FOR n IN 0 TO (tlr'LENGTH-1) LOOP
k_b(n) := NOT(b(n));
END LOOP;
k_b := k_b + eins;
FOR i IN 0 TO (tlr'LENGTH-1) LOOP
--------------------------------------------------------------------
-- Addition oder Subtraktion des Divisors in Abhaengigkeit des
-- Uebertrags der zuvor erfolgten Rechenoperation
--------------------------------------------------------------------
IF sum(tlr'LENGTH) = '1' THEN
b_add := CONV_UNSIGNED(k_b, tlr'LENGTH+1);
ELSIF sum(tlr'LENGTH) ='0' THEN
b_add := CONV_UNSIGNED(b, tlr'LENGTH+1);
END IF;
sum := CONV_UNSIGNED(a(ztld'LENGTH-1-i DOWNTO
ztld'LENGTH-tlr'LENGTH-i), tlr'LENGTH+1)
+ b_add;
-------------------------------------------------------------------
-------------------------------------------------------------------
-- sichern des Zwischenrestes mit angehaengtem Dividendenrest
-------------------------------------------------------------------
FOR k IN 0 TO (X-1-1-i) LOOP
zwischenrest(i*X+k) := a_ein(k);
END LOOP;
FOR l IN 0 TO i LOOP
zwischenrest(i*X+(X-1-i+l)) := sum(l);
END LOOP;
-------------------------------------------------------------------
-------------------------------------------------------------------
-- Umlagern der Ergebnisse
-------------------------------------------------------------------
FOR m IN 1 TO (tlr'LENGTH-1) LOOP
a(ztld'LENGTH-m-1-i) := sum(tlr'LENGTH-m-1);
END LOOP;
q(tlr'LENGTH-1-i) := sum(tlr'LENGTH); -- XOR b(tlr'LENGTH-1);
-- Uebertrag bei der Addition stellt
-- Quotientenstelle dar
-------------------------------------------------------------------
END LOOP;
-- ####################################################################
-- Restkorrektur
-- ####################################################################
r := CONV_UNSIGNED('0', tlr'LENGTH);
n_div_detect := '0';
-- --------------------------------------------------------------------
-- Ueberpruefung der Teilbarkeit
-- --------------------------------------------------------------------
FOR i IN 0 TO (tlr'LENGTH-1) LOOP
IF (i < tlr'LENGTH-1) THEN
IF (q(tlr'LENGTH-1-i) = '1') THEN
n_divisable(i) := '0';
FOR l IN i+1 TO (tlr'LENGTH-1) LOOP
n_divisable(i) := n_divisable(i) OR (q(tlr'LENGTH-1-l));
END LOOP;
ELSE
n_divisable(i) := '1';
END IF;
END IF;
END LOOP;
-- --------------------------------------------------------------------
-- --------------------------------------------------------------------
-- Zuweisung des entsprechenden Restes
-- --------------------------------------------------------------------
IF (q = CONV_UNSIGNED(0, tlr'LENGTH)) THEN
r := CONV_UNSIGNED(a_ein((tlr'LENGTH-1) DOWNTO 0), tlr'LENGTH);
ELSE
FOR i IN 1 TO (tlr'LENGTH-1) LOOP
IF (i < tlr'LENGTH-1) THEN
IF (n_divisable(i) = '0') THEN
r := CONV_UNSIGNED(zwischenrest(i*X+X-1 DOWNTO i*X),
tlr'LENGTH);
n_div_detect := '1';
END IF;
ELSE
IF (n_div_detect = '0') THEN
r := CONV_UNSIGNED(zwischenrest(i*X+X-1 DOWNTO i*X),
tlr'LENGTH);
END IF;
END IF;
END LOOP;
END IF;
-- ####################################################################
-- ####################################################################
-- interne Variablen werden Ausgangssignalen zugewiesen
-- ####################################################################
FOR i IN 0 TO (q'LENGTH-1) LOOP
k_q(i) := NOT(q(i));
END LOOP;
k_q := k_q + CONV_UNSIGNED(1, q'LENGTH);
FOR i IN 0 TO (r'LENGTH-1) LOOP
k_r(i) := NOT(r(i));
END LOOP;
k_r := k_r + eins;
CASE vz IS
WHEN "00" => quot <= q;
rest <= r;
WHEN "01" => quot <= k_q;
rest <= r;
WHEN "10" => quot <= k_q;
rest <= k_r;
WHEN "11" => quot <= q;
rest <= k_r;
WHEN OTHERS => quot <= CONV_UNSIGNED(0, quot'LENGTH);
rest <= CONV_UNSIGNED(0, rest'LENGTH);
END CASE;
-- ####################################################################
END PROCESS;
END verhalten;
CONFIGURATION CFG_array_divider OF array_divider IS
FOR verhalten
END FOR;
END CFG_array_divider;
===== Testbench =====
-- ############################################################################
-- # Project : VHDL-Modellbibliothek #
-- # #
-- # Filename : tb_array_divider.vhd #
-- # #
-- # Schaltung : Array-Dividierer Testbench #
-- # #
-- # Modell : tb_array_divider #
-- # #
-- # Designer : Wolfgang Sehr; ueberarbeitet von Stefan Schmechtig #
-- # Abteilung : Lehrstul fuer rechnergestuetzten Schaltungsentwurf #
-- # Datum : 20.01.1995 #
-- ############################################################################
-- ############################################################################
-- # IEEE PACKAGES #
-- ############################################################################
Library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
USE IEEE.math_real.all; -- wird fuer die Erzeugung von Zufallszahlen
-- benoetigt
-- ############################################################################
ENTITY TB_array_divider IS
GENERIC(X :INTEGER := @BITBR); -- symmetrisches Array X * X
END TB_array_divider;
ARCHITECTURE verhalten OF TB_array_divider IS
SIGNAL tb_dvd : UNSIGNED ((X-1) DOWNTO 0);
SIGNAL tb_dvs : UNSIGNED ((X-1) DOWNTO 0);
SIGNAL tb_quot : UNSIGNED ((X-1) DOWNTO 0);
SIGNAL tb_rest : UNSIGNED ((X-1) DOWNTO 0);
SIGNAL fehler_num : STD_LOGIC;
SIGNAL fehler_bereich : STD_LOGIC;
SIGNAL fehler_insg : STD_LOGIC;
-- tb_dvd : Dividend, der von der Testbench stimuliert wird
-- :
-- tb_dvs : Divisor, der von der Testbench stimuliert wird
-- :
-- tb_quot : Quotient, den die Testbench vom UUT empfaengt
-- :
-- tb_rest : Rest den die Testbench vom UUT empfaengt
-- :
-- fehler_num : ist nur '1', wenn Division durch Null auftritt
-- :
-- fehler_bereich : ist nur '1', wenn ein von der Testbench
-- : ausgegebener Stimuli den Darstellungsbereich
-- : der Zahl in der gewawehlten Bitbreite uebersteigt
-- :
-- fehler_insg : ist '1', wenn ein Fehler auftritt der auf
-- : UUT zurueckzufuehren ist
COMPONENT array_divider
PORT(dvd : IN UNSIGNED ((X-1) DOWNTO 0);
dvs : IN UNSIGNED ((X-1) DOWNTO 0);
quot : OUT UNSIGNED ((X-1) DOWNTO 0);
rest : OUT UNSIGNED ((X-1) DOWNTO 0));
END COMPONENT;
BEGIN
UUT: array_divider -- einzige Schaltung
PORT MAP(tb_dvd,
tb_dvs,
tb_quot,
tb_rest);
stim: PROCESS
VARIABLE dividend_int : INTEGER;
VARIABLE divisor_int : INTEGER;
VARIABLE tb_dividend_var : SIGNED((X-1) DOWNTO 0);
VARIABLE tb_divisor_var : SIGNED((X-1) DOWNTO 0);
-- dividend_int : Dividend als Integerzahl dargestellt
-- divisor_int : Divisor als Integerzahl dargestellt
-- tb_dividend_var : Dividend im SIGNED-Format dargestellt
-- tb_divisor_var : Divisor im SIGNED-Format dargestellt
BEGIN
dividend_int := RAND;
divisor_int := RAND;
tb_dividend_var := CONV_SIGNED(dividend_int,
tb_dividend_var'LENGTH);
tb_divisor_var := CONV_SIGNED(divisor_int,
tb_divisor_var'LENGTH);
tb_dvd <= CONV_UNSIGNED(tb_dividend_var, tb_dvd'LENGTH);
tb_dvs <= CONV_UNSIGNED(tb_divisor_var, tb_dvs'LENGTH);
WAIT FOR 0.1 ns;
tb_dividend_var := CONV_SIGNED(tb_dvd,
tb_dividend_var'LENGTH);
tb_divisor_var := CONV_SIGNED(tb_dvs,
tb_divisor_var'LENGTH);
dividend_int := CONV_INTEGER(tb_dividend_var);
divisor_int := CONV_INTEGER(tb_divisor_var);
WAIT FOR 10 ns;
LOOP
-- ------------------------------------------------------------
-- Bestimmmung des Dividenden
-- ------------------------------------------------------------
dividend_int := RAND;
tb_dividend_var := CONV_SIGNED(dividend_int,
tb_dividend_var'LENGTH);
tb_dvd <= CONV_UNSIGNED(tb_dividend_var, tb_dvd'LENGTH);
WAIT FOR 0.1 ns;
tb_dividend_var := CONV_SIGNED(tb_dvd, tb_dividend_var'LENGTH);
dividend_int := CONV_INTEGER(tb_dividend_var);
WAIT FOR 7.5 ns;
-- ------------------------------------------------------------
-- ------------------------------------------------------------
-- Bestimmung des Divisors
-- ------------------------------------------------------------
divisor_int := RAND;
tb_divisor_var := CONV_SIGNED(divisor_int,
tb_divisor_var'LENGTH);
tb_dvs <= CONV_UNSIGNED(tb_divisor_var, tb_dvs'LENGTH);
WAIT FOR 0.1 ns;
tb_divisor_var := CONV_SIGNED(tb_dvs, tb_divisor_var'LENGTH);
divisor_int := CONV_INTEGER(tb_divisor_var);
WAIT FOR 12.3 ns;
-- ------------------------------------------------------------
END LOOP;
END PROCESS;
contr: Process(tb_quot, tb_rest)
VARIABLE quot_sgn : SIGNED(tb_quot'LENGTH-1 DOWNTO 0);
VARIABLE rest_sgn : SIGNED(tb_rest'LENGTH-1 DOWNTO 0);
VARIABLE quot_int : INTEGER;
VARIABLE rest_int : INTEGER;
VARIABLE quot_chk : INTEGER;
VARIABLE rest_chk : INTEGER;
VARIABLE dividend_chk : INTEGER;
VARIABLE divisor_chk : INTEGER;
VARIABLE dividend_chk_sgn : SIGNED((X-1) DOWNTO 0);
VARIABLE divisor_chk_sgn : SIGNED((X-1) DOWNTO 0);
VARIABLE unzul_bitkomb : UNSIGNED ((X-1) DOWNTO 0);
-- quot_sgn : Quotient vom UUT empfangen im SIGNED-Format
-- :
-- rest_sgn : Rest vom UUT empfangen im SIGNED-Format
-- :
-- quot_int : Quotient vom UUT empfangen im Integer-Format
-- :
-- rest_sgn : Rest vom UUT empfangen im Integer-Format
-- :
-- quot_chk : Quotient innerhalb der Testbench berechnet
-- : im Integer-Format
-- :
-- rest_chk : Rest innerhalb der Testbench berechnet
-- : im Integer-Format
-- :
-- dividend_chk : Dividend wie er als Stimuli vorliegt,
-- : aber im Integer-Format
-- :
-- divisor_chk : Divisor wie er als Stimuli vorliegt,
-- : aber im Integer-Format
-- :
-- divisor_chk_sgn : Divisor sie er als Stimuli vorliegt,
-- : aber im SIGNED-Format
-- :
-- dividend_chk_sgn : Dividend wie er als Stimuli vorliegt,
-- : aber im SIGNED-Format
-- unzul_bitkomb : In dieser Variable wird die entsprechende
-- : unzulaessige Bitkombination abgelegt die
-- : eine Bereichsueberschreitung im jeweiligen
-- : Zahlenformat darstellt.
-- : (naemlich: 100.....000)
BEGIN
-- -----------------------------------------------------------------
-- Abfragen der am UUT anliegenden Signale
-- -----------------------------------------------------------------
dividend_chk_sgn := CONV_SIGNED(tb_dvd, dividend_chk_sgn'LENGTH);
divisor_chk_sgn := CONV_SIGNED(tb_dvs, divisor_chk_sgn'LENGTH);
dividend_chk := CONV_INTEGER(dividend_chk_sgn);
divisor_chk := CONV_INTEGER(divisor_chk_sgn);
FOR i IN 0 TO (tb_quot'LENGTH-1) LOOP
quot_sgn(i) := tb_quot(i);
END LOOP;
quot_int := CONV_INTEGER(quot_sgn);
FOR i IN 0 TO (tb_rest'LENGTH-1) LOOP
rest_sgn(i) := tb_rest(i);
END LOOP;
rest_int := CONV_INTEGER(rest_sgn);
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
-- Berechnung der Ausgangssignale, die am UUT anliegen muessen,
-- lediglich aufgrund der Eingangssignale
-- "Vergleichsmodell"
-- -----------------------------------------------------------------
IF ( divisor_chk /= 0) THEN
quot_chk := dividend_chk / divisor_chk;
rest_chk := dividend_chk - (divisor_chk*quot_chk);
END IF;
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
-- Ueberpruefung, ob die Zahlenwerte, die im UUT berechnet wurden,
-- mit denen die hier in der Testbench berechnet wurden ueberein-
-- stimmen bzw., ob andere Fehler auftreten
-- -----------------------------------------------------------------
unzul_bitkomb := CONV_UNSIGNED(0, tb_dvd'LENGTH);
unzul_bitkomb(tb_dvd'LENGTH-1) := '1';
IF (tb_dvd = unzul_bitkomb) THEN
fehler_num <= '0';
fehler_bereich <= '1';
fehler_insg <= '0';
ELSIF (divisor_chk = 0) THEN
fehler_num <= '1';
fehler_bereich <= '0';
fehler_insg <= '0';
ELSE
IF (quot_chk /= quot_int) OR (rest_chk /= rest_int) THEN
fehler_num <= '0';
fehler_bereich <= '0';
fehler_insg <= '1';
ELSE
fehler_num <= '0';
fehler_bereich <= '0';
fehler_insg <= '0';
END IF;
END IF;
-- -----------------------------------------------------------------
END PROCESS;
END verhalten;
CONFIGURATION CFG_TB_array_divider OF TB_array_divider IS
FOR verhalten
FOR UUT: array_divider
USE CONFIGURATION WORK.CFG_array_divider;
END FOR;
END FOR;
END CFG_TB_array_divider;
===== Synopsys VSS Trace =====
tr tb_dvd
tr tb_dvs
tr tb_quot
tr tb_rest
tr fehler_num
tr fehler_bereich
tr fehler_insg
cd stim
tr dividend_int
tr divisor_int
cd contr
tr quot_int
tr rest_int