Application Note.

Binary to BCD conversion.

 

This application note is intended to aid those designers that require a technique to convert a binary number of variable length (that is, 4, 8, 16 or 24 bits) into an equivalent representation as BCD digits. Please note that the theory utilized for the implementation of this VHDL algorithm was obtained from one of the application notes found in the Xilinx home page in the application notes section. The application note number is XAPP092 and is title "Serial Code Conversion Between BCD and Binary".

 

The procedure is a lot easier to understand if it is first tried with small number such as a 4-bit or an 8-bit binary number. Another word of advice; the designer must make sure that the binary number that will undergo the conversion be a positive or an unsigned number; that is, its most significant bit must be a ‘0’ (zero). I have yet to verify that the algorithm works for negative numbers, but it surely works for positive numbers.

 

Parallel to Serial Conversion

As described in the Xilinx application note, the input data must be in serial format and it must be fed into the Binary-to-BCD converter starting by the most significant bit. Therefore, if the original binary data is available in parallel form, the first step in the process consists in serializing the data. I found out that the easiest and cheapest (in terms of the number of Logic Cells used) way of doing a parallel-to-serial conversion is by using a Finite State Machine. The number of states of the FSM is proportional to the number of bits in the binary number. Each bit, starting by the most significant bit, will be shifted out on one of the edges of the clock signal. The choice of rising or falling edge is left to the designers discretion (I chose to use the rising edge).

The output port of this parallel-to-serial converter must be interfaced directly to the input port of the binary-to-BCD converter. The customized parallel-to-serial entity must also provide a flag to indicate to the binary-to-BCD entity when the last bit of the data has been sent.

 

Remember that in VHDL all processes happen in parallel, even though they appear to be coded as sequentially. This is an useful feature to keep in mind because both conversions (parallel-to-serial and binary-to-BCD) must take place at the same time.

 

Binary to BCD Conversion

In the Xilinx application note one can read that the binary-to-BCD conversion takes place in a modified shift register that doubles its contents as each data bit is receive, thereby, leaving a "empty" location where the next bit of the data will be placed. I chose to implement this so-called modified shift register with another Finite State Machine. This FSM should have a fixed number of states independent of the number of bits in the binary data. My design has only three states and these include the IDLE and the FINISHED states.

As you all know, a BCD digit is made up of four bits and it can only represent the numbers ‘0’ to ‘9’ ("0000" to "1001" in binary). Therefore, depending on the number of BCD digits that are required, the serial data appended to a STD_LOGIC_VECTOR whose length is directly proportional to the number of BCD digits needed. For example, a 16-bit binary number will be represented in 5 BCD digits; consequently, the length of the STD_LOGIC_VECTOR is 20 bits. In general the length of the vector is equal to the number of BCD digits required multiplied by 4 bit per digit.

I already mentioned that the objective of the FSM is to shift the contents of the "modified register" as each incoming data bit is received. However, in parallel, the designer must have another process for each BCD digit in which he/she checks if the contents of each digit is equal to or greater that 5 before the contents of the "modified register" are doubled (shifted right by one position). This will ensure that each digit remains a true BCD number. These processes do the following manipulation on each set of four bits:

If the number is equal to ‘5’, it will be converted to a ‘0’ and a 1 will be shifted to the next higher digit. A ‘6’ is converted into a ‘2’, a ‘7’ into a ‘4’, an ‘8’ into a ‘6’ and a ‘9’ into an ‘8’. For each case, a ‘1’ must be shifted into the next higher digit.

 

 

 

COPYRIGHT NOTE

I am enclosing a copy of my VHDL code so that anyone that wishes to use is free to do so, and to modify it for their particular application. The only conditions imposed is that credit be given to the designer of this Binary to BCD converter implementation, whenever this code is used. ( Don’t take this comment too seriously though ).

 

 

Author : Marco Castellon

Course: EE552

April 09, 1998

SAMPLE CODE

LIBRARY ieee;

USE ieee.std_logic_1164.ALL;

 

ENTITY ptserial IS

GENERIC( datawidth : POSITIVE := 16);

PORT(

clock : IN STD_LOGIC;

reset : IN STD_LOGIC;

start : IN STD_LOGIC;

ackSerialEnd : IN STD_LOGIC;

parallelData : IN STD_LOGIC_VECTOR(datawidth-1 downto 0);

serialout : BUFFER STD_LOGIC;

serialEnd : BUFFER STD_LOGIC

);

END ptserial;

 

ARCHITECTURE behavior OF ptserial IS

TYPE STATE_TYPE IS ( IDLE, SHIFT0, SHIFT1, SHIFT2, SHIFT3,

SHIFT4, SHIFT5, SHIFT6, SHIFT7, SHIFT8,

SHIFT9, SHIFT10, SHIFT11, SHIFT12, SHIFT13,

SHIFT14, SHIFT15, FINISHED );

SIGNAL state : STATE_TYPE;

 

BEGIN

state_logic : PROCESS( reset, clock ) IS

BEGIN

 

IF reset = '1' THEN

state <= IDLE;

 

ELSIF rising_edge(clock) THEN

CASE state IS

WHEN IDLE =>

IF start = '1' THEN

state <= SHIFT0;

ELSE

state <= IDLE;

END IF;

 

WHEN SHIFT0 =>

state <= SHIFT1;

 

WHEN SHIFT1 =>

state <= SHIFT2;

 

WHEN SHIFT2 =>

state <= SHIFT3;

.

.

.

WHEN SHIFT14 =>

state <= SHIFT15;

 

WHEN SHIFT15 =>

state <= FINISHED;

 

WHEN FINISHED =>

IF ackSerialEnd = '1' THEN

state <= IDLE;

ELSE

state <= FINISHED;

END IF;

 

WHEN OTHERS =>

state <= IDLE;

 

END CASE;

END IF;

END PROCESS state_logic;

 

 

output_logic:

WITH state SELECT

serialout <= parallelData(15) WHEN SHIFT0,

parallelData(14) WHEN SHIFT1,

parallelData(13) WHEN SHIFT2,

.

.

.

parallelData(1) WHEN SHIFT14,

parallelData(0) WHEN SHIFT15,

'0' WHEN OTHERS;

WITH state SELECT

serialEnd <= '1' WHEN SHIFT15 | FINISHED,

'0' WHEN OTHERS;

 

END behavior;

 

Binary to BCD converter Entity.

 

LIBRARY ieee;

USE ieee.std_logic_1164.ALL;

 

ENTITY bcdfsm IS

PORT(

clock : IN STD_LOGIC;

reset : IN STD_LOGIC;

start : IN STD_LOGIC;

ackEOC : IN STD_LOGIC;

lastbit : IN STD_LOGIC;

serialData : IN STD_LOGIC;

BCDdigit1 : BUFFER STD_LOGIC_VECTOR(7 downto 0);

BCDdigit2 : BUFFER STD_LOGIC_VECTOR(7 downto 0);

BCDdigit3 : BUFFER STD_LOGIC_VECTOR(7 downto 0);

BCDdigit4 : BUFFER STD_LOGIC_VECTOR(7 downto 0);

BCDdigit5 : BUFFER STD_LOGIC_VECTOR(7 downto 0);

EndofConvert : OUT STD_LOGIC

);

END bcdfsm;

 

ARCHITECTURE behavior OF bcdfsm IS

TYPE CONV_STATE_TYPE IS ( IDLE, SHIFT, FINISHED );

SIGNAL present_state : CONV_STATE_TYPE;

SIGNAL shiftedData : STD_LOGIC_VECTOR(19 downto 0);

SIGNAL load_digit1 : STD_LOGIC_VECTOR(3 downto 0);

SIGNAL load_digit2 : STD_LOGIC_VECTOR(3 downto 0);

SIGNAL load_digit3 : STD_LOGIC_VECTOR(3 downto 0);

SIGNAL load_digit4 : STD_LOGIC_VECTOR(3 downto 0);

CONSTANT ascii_prefix : STD_LOGIC_VECTOR(3 downto 0) := "0011";

 

BEGIN

state_logic : PROCESS( reset, clock ) IS

BEGIN

 

IF reset = '1' THEN

present_state <= IDLE;

shiftedData <= X"00000";

 

ELSIF rising_edge(clock) THEN

CASE present_state IS

WHEN IDLE =>

IF start = '1' THEN

present_state <= SHIFT;

shiftedData <= X"00000";

ELSE

present_state <= IDLE;

shiftedData <= X"00000";

END IF;

 

WHEN SHIFT =>

IF lastbit = '1' THEN

present_state <= FINISHED;

shiftedData <= shiftedData(18 downto 16) & load_digit4 & load_digit3 & load_digit2 & load_digit1 & serialData;

ELSE

present_state <= SHIFT;

shiftedData <= shiftedData(18 downto 16) & load_digit4 & load_digit3 & load_digit2 & load_digit1 & serialData;

END IF;

 

WHEN FINISHED =>

IF ackEOC = '1' THEN

present_state <= IDLE;

ELSE

present_state <= FINISHED;

END IF;

 

WHEN OTHERS =>

present_state <= IDLE;

 

END CASE;

END IF;

END PROCESS state_logic;

 

conv_digit1 : PROCESS IS

VARIABLE ValonetoLoad : STD_LOGIC_VECTOR(3 DOWNTO 0);

BEGIN

ValonetoLoad := shiftedData(3 DOWNTO 0);

 

CASE ValonetoLoad IS

WHEN "0101" => load_digit1 <= "1000";

WHEN "0110" => load_digit1 <= "1001";

WHEN "0111" => load_digit1 <= "1010";

WHEN "1000" => load_digit1 <= "1011";

WHEN "1001" => load_digit1 <= "1100";

WHEN OTHERS => load_digit1 <= shiftedData(3 downto 0);

END CASE;

 

END PROCESS conv_digit1;

conv_digit2 : PROCESS IS

VARIABLE ValtwotoLoad : STD_LOGIC_VECTOR(3 DOWNTO 0);

BEGIN

ValtwotoLoad := shiftedData(7 downto 4);

 

CASE ValtwotoLoad IS

WHEN "0101" => load_digit2 <= "1000";

WHEN "0110" => load_digit2 <= "1001";

WHEN "0111" => load_digit2 <= "1010";

WHEN "1000" => load_digit2 <= "1011";

WHEN "1001" => load_digit2 <= "1100";

WHEN OTHERS => load_digit2 <= shiftedData(7 downto 4);

END CASE;

 

END PROCESS conv_digit2;

 

conv_digit3 : PROCESS IS

VARIABLE ValthreetoLoad : STD_LOGIC_VECTOR(3 DOWNTO 0);

BEGIN

ValthreetoLoad := shiftedData(11 DOWNTO 8);

 

CASE ValthreetoLoad IS

WHEN "0101" => load_digit3 <= "1000";

WHEN "0110" => load_digit3 <= "1001";

WHEN "0111" => load_digit3 <= "1010";

WHEN "1000" => load_digit3 <= "1011";

WHEN "1001" => load_digit3 <= "1100";

WHEN OTHERS => load_digit3 <= shiftedData(11 downto 8);

END CASE;

 

END PROCESS conv_digit3;

 

conv_digit4 : PROCESS IS

VARIABLE ValfourtoLoad : STD_LOGIC_VECTOR(3 DOWNTO 0);

BEGIN

ValfourtoLoad := shiftedData(15 DOWNTO 12);

 

CASE ValfourtoLoad IS

WHEN "0101" => load_digit4 <= "1000";

WHEN "0110" => load_digit4 <= "1001";

WHEN "0111" => load_digit4 <= "1010";

WHEN "1000" => load_digit4 <= "1011";

WHEN "1001" => load_digit4 <= "1100";

WHEN OTHERS => load_digit4 <= shiftedData(15 downto 12);

END CASE;

 

END PROCESS conv_digit4;

 

WITH present_state SELECT

EndofConvert <= '1' WHEN FINISHED,

'0' WHEN OTHERS;

 

WITH present_state SELECT

BCDdigit1 <= ascii_prefix & shiftedData(3 downto 0) WHEN FINISHED,

ascii_prefix & BCDdigit1 WHEN OTHERS;

 

WITH present_state SELECT

BCDdigit2 <= ascii_prefix & shiftedData(7 downto 4) WHEN FINISHED,

ascii_prefix & BCDdigit2 WHEN OTHERS;

 

WITH present_state SELECT

BCDdigit3 <= ascii_prefix & shiftedData(11 downto 8) WHEN FINISHED,

ascii_prefix & BCDdigit3 WHEN OTHERS;

 

WITH present_state SELECT

BCDdigit4 <= ascii_prefix & shiftedData(15 downto 12) WHEN FINISHED,

ascii_prefix & BCDdigit4 WHEN OTHERS;

 

WITH present_state SELECT

BCDdigit5 <= ascii_prefix & shiftedData(19 downto 16) WHEN FINISHED,

ascii_prefix & BCDdigit5 WHEN OTHERS;

 

END behavior;