BCD to Binary Conversion on an FPGA

Binary Coded Decimal format is a binary encoding of decimal numbers that represents each decimal digit by a fixed binary number. For example, 42 is represented in BCD format by the binary representations of 4 and 2, as shown above. The BCD format is common in electronic systems where numeric digits are displayed, as well as in systems where the rounding and conversion errors introduced by binary floating point representation and arithmetic are undesirable.

We will focus on designing a conversion circuit that converts a BCD formatted number to to a binary formatted number. I chose to detail this direction of conversion as binary to BCD conversion circuits are easily be found by a quick web search.

We will consider two algorithms to perform the conversion, the first being a direct arithmetic approach, and the second an iterative algorithm using a finite state machine with data path (FSMD).

We will be designing for the Basys 2 FPGA board which has 8 input switches. We can use the 8 input switches to encode 2 BCD numbers of 4 bits each. We will therefore concern ourselves with designing a circuit to convert a 2 digit BCD number to a 7 bit binary representation (27 = 128 > 99, the largest 2 digit BCD number we can input).

The first algorithm will simply take the “tens” BCD digit, multiply it by 10, and add the “ones” digit to it. For the conversion of BCD 42, 4 is multiplied by 10, and 2 is added to it, giving us 101010, which is the binary representation of 42.

module bcd2bin_direct
   (
    input wire [3:0] bcd1, bcd0,
    output wire [6:0] bin
   );

   assign bin = (bcd1 * 4'b1010) + {3'b0, bcd0};

endmodule

 

The inputs are 4-bit bcd1 and bcd0, with the output being the converted 7-bit binary representation, bin. We simply multiply bcd1 by 10, add to it a 7-bit adjusted bcd0, and assign it to the output.

To test the circuit, we can route the binary output to a binary to hexadecimal 7-segment multiplexing circuit, which was detailed in my “Stopwatch with the Basys 2 FPGA” blog.

module bcd2bin_direct_test

	(
	  input wire clk, 
	  input [7:0] bcd,
	  output wire [3:0] an,
	  output wire [7:0] sseg
	 );
	 
	 wire [6:0] bin;
	 
	 bcd2bin_direct bcd2bin_direct_unit (.bcd1(bcd[7:4]), .bcd0(bcd[3:0]), .bin(bin));
	 
         displayMuxBasys disp_unit (.clk(clk), .hex3(4'b0000), .hex2(4'b0000), .hex1({1'b0, bin[6:4]}), .hex0(bin[3:0]), .dp_in(4'b1111), .an(an), .sseg(sseg));

endmodule 

 

While not a complete testbench, below are a couple of the tested outputs.

From the input switches we can see that BCD 0100 0010, or 42, gives us 2A in hex, which is decimal 42.

Testing the upper limit, BCD 1001 1001, or 99, gives an output of hex 63, which is decimal 99.


Next we will consider an iterative algorithm that will also convert our 2 BCD digits to a 7-bit binary representation. We will have two 4-bit registers for bcd1 and bcd0, as well as a 7-bit binary answer register, bin.

The algorithm is as follows:

  1. Right shift bcd1, with the LSB shifting to the MSB of bcd0.
  2. Right shift bcd0, with the LSB shifting to the MSB of bin.
  3. If bcd0 is now > 4, subtract 3
  4. repeat steps 1-3, 7 times.

Lets illustrate this with the example of converting BCD 42, 0100 0010, to binary 42, 0101010.

To implement this algorithm we will use a FSM with data path to control the overall operation of the circuit. When in the idle state and a start signal is asserted, the state will change to operate, in which the FSM will iterate through the 7 bits like the algorithm shown above. Once the algorithm completes, the state will change to done, which will assert a done_tick, and then go back to idle.

Lets look at the code implementation in chunks.

module bcd2bin
   (
    input wire clk, reset,
    input wire start,
    input wire [3:0] bcd1, bcd0,
    output reg ready, done_tick,
    output wire [6:0] bin
   );

   // symbolic state declaration
   localparam [1:0]
      idle = 2'b00,
      op   = 2'b01,
      done = 2'b10;

   // signal declaration
   reg [1:0] state_reg, state_next;
   reg [6:0] bin_reg, bin_next;
   reg [3:0] n_reg, n_next;
   reg [3:0] bcd1_reg, bcd0_reg;
   reg [3:0] bcd1_next, bcd0_next;

 

We define our symbolic states for the FSM operation to be idle, op for operation, and done.  The internal signals are declared for registers and their next state logic, for the state (state), converted binary value (bin), iteration variable (n), and BCD inputs (bcd1 & bcd0).

// FSMD state & data registers
   always @(posedge clk, posedge reset)
      if (reset)
         begin
            state_reg <= idle;
            bin_reg <= 0;
            n_reg <= 0;
            bcd1_reg <= 0;
            bcd0_reg <= 0;
         end
      else
         begin
            state_reg <= state_next;
            bin_reg <= bin_next;
            n_reg <= n_next;
            bcd1_reg <= bcd1_next;
            bcd0_reg <= bcd0_next;
         end

 

Above we implement the state and data registers, resetting them when reset is asserted, and assigning their next-state logic values at each positive clock edge.

   // FSMD next-state logic
   always @*
   begin
      // defaults
      state_next = state_reg;
      ready = 1'b0;
      done_tick = 1'b0;
      bin_next = bin_reg;
      bcd0_next = bcd0;   // route in bcd1 input
      bcd1_next = bcd1;   // route in bcd0 input
      n_next = n_reg;
		
      case (state_reg)
         idle:
            begin
               ready = 1'b1;
               if (start)
                  begin
                     state_next = op;
                     n_next = 4'b0111;      // iterate 7 times
                  end
            end		
         op:
            begin
               bin_next = {bcd0_reg[0], bin_reg[6:1]};   // right shift in lowest bit from bcd0_reg
	       bcd1_next = {1'b0, bcd1_reg[3:1]};        // right shift in 0 to bcd1	
	       // right shift in bcd1[0] into bcd0, if bcd0 > 4, subtract 3
               bcd0_next = ({bcd1_reg[0], bcd0_reg[3:1]} > 4) ? ({bcd1_reg[0], bcd0_reg[3:1]} - 4'b0011) : {bcd1_reg[0], bcd0_reg[3:1]};   
					
               n_next = n_reg - 1;   // decrement n
               if (n_next==0)
                   state_next = done;	 
            end		
         done:
            begin
               done_tick = 1'b1;   
               state_next = idle;  
            end		
         default: state_next = idle;
      endcase
   end  

   // assign output
   assign bin = bin_reg;
	
endmodule

 

The FSMD next-state logic determines what happens in each state and how a state transition occurs. Assignments before the case statement are defaults that are only changed if overwritten in the case statements. For example, done_tick is normally 0, unless the FSM is in the done state, in which it is set to 1.

In the idle state, the ready line is asserted, and if the start input is asserted, the next state will transition to op, and n_reg is set to 7, the number of iterations.

In the op state the iterative algorithm is achieved by right shifting in the LSB of bcd0 to bin, right shifting in 0 to bcd1, right shifting in the LSB of bcd1 into bcd0, checking if bcd0 is greater than 4, and if so subtracting 3. This happens 7 times until n_reg = 0, in which the state transitions to done.

In the done state, a done_tick line is asserted, letting a possible dependent system know the operation has completed, then the state transitions back to idle.

The final converted binary value in bin_reg is assigned to the output line, bin.

Since the FSMD iterative algorithm requires a finite time to compute, our test circuit has a master FSM that will idle until the start line is asserted, perform a conversion, receive a done tick, and go back to idling.

module bcd2bin_test

	(
	  input wire clk, reset, 
	  input wire start,
	  input [7:0] bcd,
	  output wire [3:0] an,
	  output wire [7:0] sseg
	 );
	 
	 // symbolic state declaration
         localparam  
              idle  = 1'b0,
               b2b  = 1'b1;   // bcd to binary conversion

    // internal signal declarations 
    wire [6:0] bin;
    reg b2b_start;
    wire b2b_done_tick;
    reg state_reg, state_next;
	 
    // instantiation of bcd to binary circuit, routing the bcd input in, and binary output out 
    bcd2bin bcd2bin_unit (.clk(clk), .reset(reset), .start(b2b_start), .bcd1(bcd[7:4]), .bcd0(bcd[3:0]), .ready(), .done_tick(b2b_done_tick), .bin(bin));
	 
    // instantiation of multiplexing 7-segment circuit, routing in binary output of converstion circuit to lower two displays
    displayMuxBasys disp_unit (.clk(clk), .hex3(4'b0000), .hex2(4'b0000), .hex1({1'b0, bin[6:4]}), .hex0(bin[3:0]), .dp_in(4'b1111), .an(an), .sseg(sseg));
 
    // state register
    always @(posedge clk, posedge reset)
     if (reset)
        state_reg <= idle;
     else
        state_reg <= state_next;
		  
    // FSM control
    always @*
    begin
      state_next = state_reg;
      b2b_start = 1'b0;
		
      case (state_reg)
         idle:
            if (start)
               begin
                  b2b_start = 1'b1;  // assert start line to bcd2bin circuit
                  state_next = b2b;  
	       end
         b2b:
            if (b2b_done_tick)       // once bcd2bin conversion done, go back to idle
               state_next = idle;
      endcase
     end
endmodule 

 

We can see that unlike before where the result was always routed to the output display, we now have to enable the start line to compute the conversion. The intermediate state showing “88” occurs only when the button is pressed and the master FSM rapidly cycles through its states, until it is disengaged. This could be remedied by instead implementing a positive edge detection circuit for the start button input signal.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s