最近在研究FPGA,涉及到一个将二进制数转换为BCD码显示出来的工作,整理出一套同时支持整数和小数的由二进制转换为十进制的算法。这套算法是可综合并且实测有效的。
这个电路的设计思路来源于两方面。一方面是人工进行进制转换的方法:将每一位二进制数所代表的十进制数加起来;另一方面是汇编语言中的BCD加法调整指令DAA的算法:若加法的结果大于9,则标记进位并且将结果再加上6。
电路的原理大致总结如下:
- 将二进制数分为几个部分(例如每1位一组,或者4位一组),然后将这几个部分手动转换成BCD码。比如这样:A(5) = 1 -> 16, = 0 -> 0或者A(7 downto 4) = 1 -> x”16″, = 2 -> x”32″等等。
- 将几个部分的二进制数转换为的BCD码用BCD加法加起来。
根据纸上谈兵的分析,如果每一位分为一组的话,则输入数据需要经过大量的加法器才能到达输出,延时应该会比较显著。如果很多位分为一组的话,那么你需要做很多的工作。
以下是我做的8位二进制整数+4位二进制小数转BCD码的例子。另外你可以直接跳到最后一段代码,查看关键算法之BCD加法算法。
首先展示整数部分的转换。Whole为输入的二进制整数(unsigned类型),WholeBcd为输出的BCD码(std_logic_vector类型)。这里采用的是4位一组。
process (Whole)
variable result: unsigned(11 downto 0);
begin
result := to_unsigned(0, 12);
if (Whole(3 downto 0) >= 10) then
result := result + Whole(3 downto 0) + 6;
else
result := result + Whole(3 downto 0);
end if;
case Whole(7 downto 4) is
when "0000" => result := result;
when "0001" => result := BcdAdd(result, "000000010110");
when "0010" => result := BcdAdd(result, "000000110010");
when "0011" => result := BcdAdd(result, "000001001000");
when "0100" => result := BcdAdd(result, "000001100100");
when "0101" => result := BcdAdd(result, "000010000000");
when "0110" => result := BcdAdd(result, "000010010110");
when "0111" => result := BcdAdd(result, "000100010010");
when "1000" => result := BcdAdd(result, "000100101000");
when "1001" => result := BcdAdd(result, "000101000100");
when "1010" => result := BcdAdd(result, "000101100000");
when "1011" => result := BcdAdd(result, "000101110110");
when "1100" => result := BcdAdd(result, "000110010010");
when "1101" => result := BcdAdd(result, "001000001000");
when "1110" => result := BcdAdd(result, "001000100100");
when "1111" => result := BcdAdd(result, "001001000000");
when others => result := result;
end case;
WholeBcd <= std_logic_vector(result);
end process;
小数部分的转换采用的是每一位单独转换(因为总共也没多少位)之后加起来。
process (Fraction)
variable result: unsigned(15 downto 0);
begin
result := to_unsigned(0, 16);
if (Fraction(0) = '1') then
result := result + x"0625";
end if;
if (Fraction(1) = '1') then
result := BcdAdd(result, x"1250");
end if;
if (Fraction(2) = '1') then
result := BcdAdd(result, x"2500");
end if;
if (Fraction(3) = '1') then
result := BcdAdd(result, x"5000");
end if;
FractionBcd <= std_logic_vector(result);
end process;
最后是这个谜一般的BCD加法函数。
function Max(L, R: integer) return integer is
begin
if (L > R) then
return L;
else
return R;
end if;
end function;
function BcdAdd(L, R: unsigned) return unsigned is
variable size: integer := Max(L'length, R'length);
variable i: integer;
variable result: unsigned(size - 1 downto 0);
variable l1, r1: unsigned(size - 1 downto 0);
begin
if (L'length > R'length) then
size := L'length;
else
size := R'length;
end if;
result := (others => '0');
l1 := resize(L, size);
r1 := resize(R, size);
i := 0;
while (i + 4 < size) loop if (result(i + 4 downto i) + l1(i + 3 downto i) + r1(i + 3 downto i) >= 10) then
result(i + 4 downto i) :=
result(i + 4 downto i) + l1(i + 3 downto i) + r1(i + 3 downto i) + 6;
else
result(i + 4 downto i) :=
result(i + 4 downto i) + l1(i + 3 downto i) + r1(i + 3 downto i);
end if;
i := i + 4;
end loop;
if ('0' & result(size - 1 downto i) + l1(size - 1 downto i) + r1(size - 1 downto i) >= 10) then
result(size - 1 downto i) :=
result(size - 1 downto i) + l1(size - 1 downto i) + r1(size - 1 downto i) + 6;
else
result(size - 1 downto i) :=
result(size - 1 downto i) + l1(size - 1 downto i) + r1(size - 1 downto i);
end if;
return result;
end function;
最后加一句声明:这样弄出来虽然结果比较正确,但是用Lattice Diamond综合的过程中出来不少no driver的警告,并不清楚是为什么……
留言
有想法?请给我们留言!您的留言不会直接显示在网站内。