Möchte man eine Zahl auf einem Display ausgeben, muß die erst mal in ihre Zehnerstellen umgewandelt werden. Das BCD-Format bietet sich hier an. Man könnte jetzt z.B. durch Subtraktionen oder Divisionen die einzelnen Stellen extrahieren.
Wesentlich eleganter und schneller ist aber ein BCD-Schieberegister. Der Vektor wird von rechts in die BCD-Zahlen hineingeschoben und beim Überlauf die entsprechende BCD-Stelle mit einer Addition +6 korrigiert. Dieses Verfahren heißt Shift-Add-6, es hat aber den Nachteil, dass bei einem Übertrag evtl die nachfolgende Stelle abenfalls manipuliert (incrementiert) werden muß. Hier als Beispiel die Umwandlung der Zahl 99dez = 01100011bin in eine zweistellige BCD-Zahl:
Z E
---- ---- 01100011 = 99dez
---- ---0 1100011
---- --01 100011
---- -011 00011
---- 0110 0011
---0 1100 011 E>9 -->
---1 0010 011 E = E+6 = 12+6 = 18 = 1 0010 --> Z=1, E=0010
--10 0100 11
-100 1001 1
1001 0011 Überlauf, weil E nach Shift E>9 -->
1001 1001 E = E+6 = 3+6 = 9 = 1001 --> E=1001 -> 9 9
Ein findiger Kopf hat daraus das Shift-Add-3 Verfahren gemacht, bei dem zuerst kontrolliert wird, ob beim Schieben ein Überlauf entstehen könnte (Wert>=5), und dann vor dem Schieben 3 aufaddiert werden. Hier kann innerhalb der BCD-Stelle kein Überlauf anfallen, die nachfolgende Stelle muß also nicht beachtet werden.
Man sieht die Verwandtschaft der Verfahren: einmal wird nach dem Schieben 6 addiert, das andere Mal vor dem Schieben eine 3 (die ja dann durch das Schieben mit 2 multipliziert wird).
Z E
---- ---- 01100011 = 99dez
---- ---0 1100011
---- --01 100011
---- -011 00011
---- 0110 0011 E>4 --> E = E+3 = 6+3 = 9 = 1001
---- 1001 0011
---1 0010 011
--10 0100 11
-100 1001 1 E>4 --> E = E+3 = 9+3 = 12 = 1100
-100 1100 1
1001 1001 -> 9 9
Dieses Shift-Add-3 Verfahren habe ich in folgende VHDL-Beschreibung umgesetzt:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity Vector2BCD is
Generic ( stellen : natural := 4; -- Anzahl BCD-Stellen
breite : natural := 16); -- Breite des Eingangsvektors
Port ( clk : in std_logic;
vector : in std_logic_vector (breite-1 downto 0);
start : in std_logic;
busy : out std_logic;
overflow : out std_logic;
bcd : out std_logic_vector (stellen*4-1 downto 0));
end Vector2BCD;
architecture Behavioral of Vector2BCD is -- Beispiel: Stellen=3, Breite=8
type states is (idle,calc); -- 2 1111 1111 11
signal state : states := idle; -- 0 9876 5432 1098 76543210
signal sr : unsigned (stellen*4+breite downto 0); -- O BCDh & BCDz & BCDe & VVVVVVVV
signal bitcnt : integer range 0 to breite;
signal ovl : std_logic;
begin
process begin
wait until rising_edge(clk);
busy <= '0';
case state is
when idle =>
sr <= (others=>'0');
sr(breite-1 downto 0) <= unsigned(vector);
bitcnt <= breite;
if (start='1') then
busy <= '1';
ovl <= '0';
state<=calc;
end if;
when calc =>
if (bitcnt/=0) then
bitcnt <= bitcnt-1; -- bearbeitete Bits mitzählen
busy <= '1';
sr <= sr(stellen*4+breite-1 downto 0)&'0'; -- einmnal komplett durchschieben
for i in 0 to stellen-1 loop
if (sr(i*4+breite+3 downto i*4+breite)>4) then -- falls nötig: BCD-Übertrag + schieben
sr(i*4+breite+4 downto i*4+breite+1) <= sr(i*4+breite+3 downto i*4+breite)+3;
end if;
end loop;
if (sr(sr'high)='1') then ovl <= '1'; end if;
else
state <= idle;
bcd <= std_logic_vector(sr(stellen*4+breite-1 downto breite));
if(ovl='1' or sr(sr'high)='1') then overflow <= '1';
else overflow <= '0';
end if;
end if;
end case;
end process;
end Behavioral;
Ein Ausschnitt aus der Testbench:
:
clk <= not clk after 5 ns;
tb : PROCESS
BEGIN
vector <= std_logic_vector(to_unsigned(8,16));
wait for 25 ns;
start <= '1';
wait for 25 ns;
start <= '0';
wait until falling_edge(busy);
wait for 25 ns;
vector <= std_logic_vector(to_unsigned(33,16));
wait for 25 ns;
start <= '1';
wait for 25 ns;
start <= '0';
wait until falling_edge(busy);
wait for 25 ns;
:
Hier das Ergebnis mit 4 Stellen und einem 16-bit Eingangsvektor:
Der vector ist die Engangs-Binärzahl (dargestellt als unsigned), bcd ist die BCD-Zahl (dargestellt als Hex-Zahl). Bei Zahlen ab 10000 wird das Overflow-Flag gesetzt und solange gehalten wie die zugehörige BCD-Zahl.
Wer das Ausgangsregister weglässt, spart sich noch ein paar Flipflops ein:
:
:
else
state <= idle;
if(ovl='1' or sr(sr'high)='1') then overflow <= '1';
else overflow <= '0';
end if;
end if;
end case;
end process;
bcd <= std_logic_vector(sr(stellen*4+breite-1 downto breite));
end Behavioral;