Задача
Перевести двоичное число в три десятичных разряда без деления.
Тип схемы: Конечный автомат с составным сдвиговым регистром.
Модуль: bin2bcd. Среда: Quartus, Vivado, ISE или свободный GHDL для моделирования.
Теорию по языку смотрите в статье VHDL — язык описания аппаратуры,
по самим микросхемам — на странице ПЛИС.
Алгоритм
- Поместить входное число в младшую часть общего сдвигового регистра.
- Проверить каждый BCD-разряд: если он больше четырёх, прибавить тройку.
- Сдвинуть весь регистр целиком на один разряд влево.
- Повторить коррекцию и сдвиг по числу входных бит и забрать старшую часть как результат.
Код на VHDL
Код проверен компилятором GHDL: проходит анализ и элаборацию без ошибок и предупреждений. Скачать: v42.vhd
--=====================================================
-- Проект 42. Двоично-десятичный преобразователь (double dabble)
-- Восьмиразрядное двоичное число превращается
-- в три десятичных разряда без операции деления.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity bin2bcd is
port (
clk : in std_logic;
rst : in std_logic;
start : in std_logic;
bin_in : in std_logic_vector(7 downto 0);
bcd_out : out std_logic_vector(11 downto 0); -- сотни-десятки-единицы
done : out std_logic
);
end entity bin2bcd;
architecture rtl of bin2bcd is
type state_t is (IDLE, SHIFT, ADJUST, FINISH);
signal state : state_t := IDLE;
signal shreg : std_logic_vector(19 downto 0); -- 12 бит BCD + 8 бит числа
signal cnt : integer range 0 to 7 := 0;
begin
process (clk, rst)
variable d : std_logic_vector(19 downto 0);
begin
if rst = '1' then
state <= IDLE;
done <= '0';
elsif rising_edge(clk) then
done <= '0';
case state is
when IDLE =>
if start = '1' then
shreg <= (11 downto 0 => '0') & bin_in;
cnt <= 0;
state <= ADJUST;
end if;
when ADJUST => -- коррекция разрядов BCD по 3
d := shreg;
if unsigned(d(11 downto 8)) > 4 then
d(11 downto 8) := std_logic_vector(unsigned(d(11 downto 8)) + 3);
end if;
if unsigned(d(15 downto 12)) > 4 then
d(15 downto 12) := std_logic_vector(unsigned(d(15 downto 12)) + 3);
end if;
if unsigned(d(19 downto 16)) > 4 then
d(19 downto 16) := std_logic_vector(unsigned(d(19 downto 16)) + 3);
end if;
shreg <= d;
state <= SHIFT;
when SHIFT =>
shreg <= shreg(18 downto 0) & '0'; -- сдвиг всего слова влево
if cnt = 7 then
state <= FINISH;
else
cnt <= cnt + 1;
state <= ADJUST;
end if;
when FINISH =>
done <= '1';
state <= IDLE;
end case;
end if;
end process;
bcd_out <= shreg(19 downto 8);
end architecture rtl;
Пояснения
- Коррекция «если больше четырёх — прибавить три» компенсирует то, что при сдвиге BCD-разряд способен перейти за границу 9 без переноса в следующий десятичный разряд.
- Тот же результат получается циклом вычитания степеней десяти, но double dabble работает за фиксированное число тактов независимо от значения числа.
- Алгоритм легко расширяется на большую разрядность — добавляется лишь ширина сдвигового регистра и число BCD-разрядов для коррекции.
Что дальше
Следующие проекты — в разделе проекты на VHDL. Сравнить подход с языком Verilog можно в статье Verilog, а разобраться в устройстве самих кристаллов — в статье ПЛИС: FPGA и CPLD.