====== 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