Conversion principle

Binary to BCD code can use the Double_dabble algorithm, wikipedia has a detailed description, can be simply called “shift plus 3” algorithm.

8-bit binary number 243, shift plus 316-bit binary 65244, shift plus 3

Verilog implementation

Method 1: Pure combinatorial logic implementation

Implementation from Wikipedia. It can be seen that the binary bit width is W, so the BCD bit width only needs (W + (W – 4) / 3+1) bits. For example, if W=8, only 10 bits are needed, ranging from 0 to 255, and only two bits are needed for hundreds. BinTobcd. V file:

`timescale 1ns / 1ps
module binTobcd #( 
    parameter W = 18
)  // input width
( 
    //Inputs
    input [W - 1 : 0] bin ,   // binary
    //Outputs
    output reg [W + (W - 4) / 3: 0] bcd 
); // bcd {... ,thousands,hundreds,tens,ones}

integer i, j;

always @(bin) begin
    for (i = 0; i <= W + (W - 4) / 3; i = i + 1)
        bcd[i] = 0;     // initialize with zeros
    bcd[W - 1: 0] = bin;                                   // initialize with input vector
    for (i = 0; i <= W - 4; i = i + 1)                       // iterate on structure depth
        for (j = 0; j <= i / 3; j = j + 1)                     // iterate on structure width
            if (bcd[W - i + 4 * j - : 4] > 4)                      // if > 4
                bcd[W - i + 4 * j - : 4] = bcd[W - i + 4 * j - : 4] + 4 'd3; // add 3
end

endmodule
Copy the code

Method 2: Using the state machine to achieve variable bit width

From nandland. Binary_to_bcd.v, the actual measurement will consume more clock cycles.

/* INPUT_WIDTH = 8; DECIMAL_DIGITS = 3; It consumes 60 CLK */

module Binary_to_BCD #(
    parameter INPUT_WIDTH,
    parameter DECIMAL_DIGITS
)(
    //Inputs
    input i_Clock,
    input i_Rst_n,
    input [INPUT_WIDTH - 1 : 0] i_Binary,
    input i_Start,
    
    //Outputs
    output [DECIMAL_DIGITS * 4 - 1 : 0] o_BCD,
    output o_DV
);

localparam s_IDLE = 3'b000;
localparam s_SHIFT = 3'b001;
localparam s_CHECK_SHIFT_INDEX = 3'b010;
localparam s_ADD = 3'b011;
localparam s_CHECK_DIGIT_INDEX = 3'b100;
localparam s_BCD_DONE = 3'b101;

reg [2 : 0] r_SM_Main = s_IDLE;
reg [DECIMAL_DIGITS * 4 - 1 : 0] r_BCD = 0;
reg [INPUT_WIDTH - 1 : 0] r_Binary = 0;
reg [DECIMAL_DIGITS - 1 : 0] r_Digit_Index = 0;
reg [7 : 0] r_Loop_Count = 0;
reg r_DV = 1'b0;

wire [3 : 0] w_BCD_Digit = r_BCD[r_Digit_Index * 4 + : 4];

assign o_BCD = r_BCD;
assign o_DV = r_DV;

always @(posedge i_Clock) begin
    if(! i_Rst_n) begin
        r_BCD <= 'h0; r_Binary <= 'h0;
        r_DV <= 'h0; r_SM_Main <= 'h0;
        r_Loop_Count <= 'h0; r_Digit_Index <= 'h0;
    end
    else begin
        case (r_SM_Main)
            // Stay in this state until i_Start comes along
            s_IDLE : begin
                r_DV <= 1'b0;
                if (i_Start == 1'b1) begin
                    r_Binary <= i_Binary;
                    r_SM_Main <= s_SHIFT;
                    r_BCD <= 0;
                end
                else
                    r_SM_Main <= s_IDLE;
            end
            
            // Always shift the BCD Vector until we have shifted all bits through
            // Shift the most significant bit of r_Binary into r_BCD lowest bit.
            s_SHIFT : begin
                r_BCD <= r_BCD << 1;
                r_BCD[0] <= r_Binary[INPUT_WIDTH - 1];
                r_Binary <= r_Binary << 1;
                r_SM_Main <= s_CHECK_SHIFT_INDEX;
            end
            
            // Check if we are done with shifting in r_Binary vector
            s_CHECK_SHIFT_INDEX : begin
                if (r_Loop_Count == INPUT_WIDTH - 1) begin
                    r_Loop_Count <= 0;
                    r_SM_Main <= s_BCD_DONE;
                end
                else begin
                    r_Loop_Count <= r_Loop_Count + 1;
                    r_SM_Main <= s_ADD;
                end
            end
            
            // Break down each BCD Digit individually.? Check them one-by-one to
            // see if they are greater than 4.? If they are, increment by 3.
            // Put the result back into r_BCD Vector.?
            s_ADD : begin
                if (w_BCD_Digit > 4) begin
                    r_BCD[(r_Digit_Index * 4) + : 4] <= w_BCD_Digit + 3;
                end
                r_SM_Main <= s_CHECK_DIGIT_INDEX;
            end
            
            // Check if we are done incrementing all of the BCD Digits
            s_CHECK_DIGIT_INDEX : begin
                if (r_Digit_Index == DECIMAL_DIGITS - 1) begin
                    r_Digit_Index <= 0;
                    r_SM_Main <= s_SHIFT;
                end
                else begin
                    r_Digit_Index <= r_Digit_Index + 1;
                    r_SM_Main <= s_ADD;
                end
            end
            
            s_BCD_DONE : begin
                r_DV <= 1'b1;
                r_SM_Main <= s_IDLE;
            end
            default :
                r_SM_Main <= s_IDLE;
        endcase
    end
end // always @ (posedge i_Clock)?

endmodule // Binary_to_BCD
Copy the code

Simulation file, binary_to_bcd_tb.v:

`timescale 1ns/1ps

module Binary_to_BCD_tb;

reg clk;
reg rst_n;

reg start;
reg [7:0] bin;
wire [11:0] dec;
wire done;
wire [3:0] dec_b = dec[11:8];
wire [3:0] dec_s = dec[7:4];
wire [3:0] dec_g = dec[3:0];
wire [7:0] dec_real = dec_b * 100 + dec_s * 10 + dec_g;

reg [7:0] idx;
always # (10/2) clk <= ! clk; initial begin clk =0;
    rst_n = 0;
    start = 0;
    bin = 0;
    idx = 0;
    #50
    @(posedge clk);
    rst_n = 1;
    #50
    @(posedge clk);
    
    for(idx = 0; idx < 254; idx = idx + 1)
        trig_bcd_covert(idx);

    #2000;
    $stop();
    $display("stop");
end

task trig_bcd_covert(
    input [7:0] i_bin
);  
begin
    #80
    @(posedge clk);
    bin = i_bin;
    start = 1;
    @(posedge clk);
    start = 0;
    @(posedge done);
    if(dec_real == i_bin)
        $display("ok:%3d -> %1x%1x%1x", bin, dec_b, dec_s, dec_g);
    else 
        $display("err:%3d -> %1x%1x%1x", bin, dec_b, dec_s, dec_g);
end
endtask

Binary_to_BCD #(
    .INPUT_WIDTH(8),
    .DECIMAL_DIGITS(3)
)Binary_to_BCD_ut0(
    //Inputs
    .i_Clock(clk),
    .i_Rst_n(rst_n),
    .i_Binary(bin[7:0]),
    .i_Start(start),
    
    //Outputs
    .o_BCD(dec[11:0]),
    .o_DV(done)
);

endmodule
Copy the code

Method 3: Personal write state machine-based BCD code conversion

Only 8 binary to BCD is supported. The same operation is performed for other bit widths, bin_to_bCd.v

/* Binary to BCD code 243(11110011) = 0010 0100 0011 = 2/4/3 CLK = 9+8 */

module bin_to_bcd(
    input clk,
    input rst_n,
    input start, //1 clk high pulse
    input [7:0] bin,
    
    output reg [11:0] bcd,  //[W + (W - 4) / 3: 0], w=8 -> 8+1=9:0=10
    output done     //need 17 clk time
);

localparam S0_IDLE    = 0;
localparam S1_SHIFT   = 1;
localparam S2_ADD     = 2;
localparam S3_FINISH  = 4;

reg [3:0] fsm;
reg [3:0] cnt_shift;        //max=8
reg [7:0] bin_buf;
reg [11:0] bcd_buf;

assign done = (fsm == S3_FINISH);

always @ (posedge clk) begin
    if(! rst_n) begin
        fsm <= S0_IDLE;
        cnt_shift <= 'h0; bcd_buf <= 'h0;
        bin_buf <= 'h0; bcd <= 'h0;
    end
    else begin
        case(fsm)
            S0_IDLE: begin
                if(start) begin
                    fsm <= S1_SHIFT;
                    bin_buf <= bin;
                    cnt_shift <= 'h0; bcd_buf <= 'h0;
                end
            end
            
            // Shift operation
            S1_SHIFT: begin
                if(cnt_shift ! =8) begin
                    cnt_shift <= cnt_shift + 1;
                    {bcd_buf, bin_buf} <= {bcd_buf, bin_buf} << 1;
                    fsm <= S2_ADD;
                end
                else 
                    fsm <= S3_FINISH;
            end
            
            // Add 3
            S2_ADD: begin
                if(cnt_shift ! =8) begin
                    fsm <= S1_SHIFT;
                    if(bcd_buf[11:8] > 4)
                        bcd_buf[11:8] <= bcd_buf[11:8] + 'd3; if(bcd_buf[7:4] > 4) bcd_buf[7:4] <= bcd_buf[7:4] + 'd3;
                    if(bcd_buf[3:0] > 4)
                        bcd_buf[3:0] <= bcd_buf[3:0] + 'd3; end else begin fsm <= S3_FINISH; bcd <= bcd_buf; End end // Finish S3_FINISH: begin FSM <= S0_IDLE; bin_buf <= 'h0;
                cnt_shift <= 'h0; bcd_buf <= 'h0;
            end
        endcase
    end
end
endmodule
Copy the code

Simulation file, bin_to_bcd_tb.v:

`timescale 1ns/1ps

module bin_to_bcd_tb;

reg clk;
reg rst_n;

reg start;
reg [7:0] bin;
wire [11:0] bcd;
wire done;
wire [3:0] bcd_b = bcd[11:8];
wire [3:0] bcd_s = bcd[7:4];
wire [3:0] bcd_g = bcd[3:0];
wire [7:0] bcd_real = bcd_b * 100 + bcd_s * 10 + bcd_g;

reg [7:0] idx;
always # (10/2) clk <= ! clk; initial begin clk =0;
    rst_n = 0;
    start = 0;
    bin = 0;
    idx = 0;
    #50
    @(posedge clk);
    rst_n = 1;
    #50
    @(posedge clk);
    
    for(idx = 0; idx < 254; idx = idx + 1)
        trig_bcd_covert(idx);
    #2000;
    $stop();
    $display("stop");
    
end

task trig_bcd_covert(
    input [7:0] i_bin
);  
begin
    #80
    @(posedge clk);
    bin = i_bin;
    start = 1;
    @(posedge clk);
    start = 0;
    @(posedge done);
    if(bcd_real == i_bin)
        $display("ok:%3d -> %1x%1x%1x", bin, bcd_b, bcd_s, bcd_g);
    else 
        $display("err:%3d -> %1x%1x%1x", bin, bcd_b, bcd_s, bcd_g);
end
endtask

bin_to_bcd bin_to_bcd_ut0(
    .clk(clk),
    .rst_n(rst_n),
    .start(start),
    .bin(bin[7:0]),
    
    .bcd(bcd[11:0]),
    .done(done)
);

endmodule

Copy the code