Verilog를 이용한 십진 양수와 ASCII 코드 상호 변환

소규모 숫자의 ASCII 코드 변환

1. 십진 정수를 BCD 코드(8421 방식)로 변환

BCD(Binary-Coded Decimal)는 각 십진 자릿수를 4비트 이진수로 표현하는 인코딩 방식이다. 예를 들어, 십진수 5는 0101로 표현된다.
십진수0123456789
BCD (4비트)0000000100100011010001010110011110001001

2. BCD 코드를 ASCII 코드로 변환

숫자 문자의 ASCII 코드는 16진수로 0x30부터 시작한다. 따라서 BCD 값에 0x30을 더하면 해당 숫자의 ASCII 코드가 된다.
BCD0000000100100011010001010110011110001001
ASCII (16진수)30313233343536373839
ASCII (10진수)48495051525354555657

대규모 정수의 ASCII 변환 구현

1. 수치의 자릿수 분리 (비교 기반 방법)

큰 숫자의 각 자릿수를 추출할 때 나눗셈을 피하고 비교 연산을 사용하면 FPGA에서 리소스 효율이 높아진다. 다음은 16비트 입력값을 만, 천, 백, 십, 일의 자리로 분해하는 로직의 개요이다.

2. Verilog: 십진수 → ASCII

module decimal_to_ascii_16bit (
    input               clk,
    input               reset,
    input               data_valid,
    input       [15:0]  input_value,
    output reg          ready,
    output wire [39:0]  ascii_output
);

// 자릿수 저장용 레지스터
reg [15:0] remaining;
reg [3:0]  digit_10k, digit_1k, digit_100, digit_10, digit_1;

// 유효 신호 딜레이
reg [2:0] valid_delay;

always @(posedge clk) begin
    if (reset) begin
        valid_delay <= 3'b0;
    end else begin
        valid_delay <= {valid_delay[1:0], data_valid};
    end
end

// 만의 자리 추출
always @(posedge clk) begin
    if (reset) begin
        digit_10k <= 4'd0;
        remaining <= 16'd0;
    end else begin
        casez (input_value)
            16'hFFFF: digit_10k <= 4'd6; remaining <= input_value - 60000;
            >= 50000: digit_10k <= 4'd5; remaining <= input_value - 50000;
            >= 40000: digit_10k <= 4'd4; remaining <= input_value - 40000;
            >= 30000: digit_10k <= 4'd3; remaining <= input_value - 30000;
            >= 20000: digit_10k <= 4'd2; remaining <= input_value - 20000;
            >= 10000: digit_10k <= 4'd1; remaining <= input_value - 10000;
            default:  digit_10k <= 4'd0; remaining <= input_value;
        endcase
    end
end

// 천의 자리 추출
always @(posedge clk) begin
    if (reset) begin
        digit_1k <= 4'd0;
    end else begin
        if      (remaining >= 9000) digit_1k <= 4'd9;
        else if (remaining >= 8000) digit_1k <= 4'd8;
        else if (remaining >= 7000) digit_1k <= 4'd7;
        else if (remaining >= 6000) digit_1k <= 4'd6;
        else if (remaining >= 5000) digit_1k <= 4'd5;
        else if (remaining >= 4000) digit_1k <= 4'd4;
        else if (remaining >= 3000) digit_1k <= 4'd3;
        else if (remaining >= 2000) digit_1k <= 4'd2;
        else if (remaining >= 1000) digit_1k <= 4'd1;
        else                        digit_1k <= 4'd0;
    end
end

// 백의 자리 추출
always @(posedge clk) begin
    if (reset) begin
        digit_100 <= 4'd0;
    end else begin
        if      (remaining % 1000 >= 900) digit_100 <= 4'd9;
        else if (remaining % 1000 >= 800) digit_100 <= 4'd8;
        else if (remaining % 1000 >= 700) digit_100 <= 4'd7;
        else if (remaining % 1000 >= 600) digit_100 <= 4'd6;
        else if (remaining % 1000 >= 500) digit_100 <= 4'd5;
        else if (remaining % 1000 >= 400) digit_100 <= 4'd4;
        else if (remaining % 1000 >= 300) digit_100 <= 4'd3;
        else if (remaining % 1000 >= 200) digit_100 <= 4'd2;
        else if (remaining % 1000 >= 100) digit_100 <= 4'd1;
        else                              digit_100 <= 4'd0;
    end
end

// 십의 자리 추출
always @(posedge clk) begin
    if (reset) begin
        digit_10 <= 4'd0;
    end else begin
        if      ((remaining % 100) >= 90) digit_10 <= 4'd9;
        else if ((remaining % 100) >= 80) digit_10 <= 4'd8;
        else if ((remaining % 100) >= 70) digit_10 <= 4'd7;
        else if ((remaining % 100) >= 60) digit_10 <= 4'd6;
        else if ((remaining % 100) >= 50) digit_10 <= 4'd5;
        else if ((remaining % 100) >= 40) digit_10 <= 4'd4;
        else if ((remaining % 100) >= 30) digit_10 <= 4'd3;
        else if ((remaining % 100) >= 20) digit_10 <= 4'd2;
        else if ((remaining % 100) >= 10) digit_10 <= 4'd1;
        else                             digit_10 <= 4'd0;
    end
end

// 일의 자리 계산
always @(posedge clk) begin
    if (reset) begin
        digit_1 <= 4'd0;
    end else begin
        digit_1 <= remaining % 10;
    end
end

// 최종 ASCII 출력 생성 (각 숫자 앞에 '3' 추가)
assign ascii_output = {
    4'h3, digit_10k,
    4'h3, digit_1k,
    4'h3, digit_100,
    4'h3, digit_10,
    4'h3, digit_1
};

assign ready = valid_delay[2];

endmodule

ASCII 코드를 십진수로 변환

1. 자릿수 조합을 통한 수치 복원

각 자릿수를 10의 거듭제곱과 곱하여 전체 값을 재구성한다: - 만의 자리: ×10000 - 천의 자리: ×1000 - 백의 자리: ×100 - 십의 자리: ×10 - 일의 자리: ×1 승수는 비트 시프트와 덧셈으로 구현 가능하다: - 100 = 64 + 32 + 4 = 2⁶ + 2⁵ + 2² - 1000 ≈ 1024 − 32 + 8 = 2¹⁰ − 2⁵ + 2³

2. Verilog: ASCII → 십진수

module ascii_to_decimal_16bit (
    input               clk,
    input               reset,
    input               data_valid,
    input       [39:0]  ascii_in,
    output reg          ready_out,
    output reg  [15:0]  result
);

// 각 자릿수 추출 (상위 4비트 무시, 하위 4비트 사용)
wire [3:0] d10k = ascii_in[39:36] & 4'hF;
wire [3:0] d1k  = ascii_in[31:28] & 4'hF;
wire [3:0] d100 = ascii_in[23:20] & 4'hF;
wire [3:0] d10  = ascii_in[15:12] & 4'hF;
wire [3:0] d1   = ascii_in[7:4]   & 4'hF;

// 중간 결과 저장 레지스터
reg [15:0] partial_10k, partial_1k, partial_100, partial_10, partial_1;
reg [2:0] valid_pipe;

always @(posedge clk) begin
    if (reset) begin
        valid_pipe <= 3'b0;
    end else begin
        valid_pipe <= {valid_pipe[1:0], data_valid};
    end
end

// 각 자릿수에 승수 적용
always @(posedge clk) begin
    if (reset) begin
        partial_10k <= 16'd0;
    end else begin
        // 10000 = 2^13 + 2^11 - 2^8 + 2^4
        partial_10k <= {d10k, 13'd0} + {d10k, 11'd0} - {d10k, 8'd0} + {d10k, 4'd0};
    end
end

always @(posedge clk) begin
    if (reset) begin
        partial_1k <= 16'd0;
    end else begin
        // 1000 = 2^10 - 2^5 + 2^3
        partial_1k <= {d1k, 10'd0} - {d1k, 5'd0} + {d1k, 3'd0};
    end
end

always @(posedge clk) begin
    if (reset) begin
        partial_100 <= 16'd0;
    end else begin
        // 100 = 2^6 + 2^5 + 2^2
        partial_100 <= {d100, 6'd0} + {d100, 5'd0} + {d100, 2'd0};
    end
end

always @(posedge clk) begin
    if (reset) begin
        partial_10 <= 16'd0;
    end else begin
        // 10 = 2^3 + 2^1
        partial_10 <= {d10, 3'd0} + {d10, 1'd0};
    end
end

always @(posedge clk) begin
    if (reset) begin
        partial_1 <= 16'd0;
    end else begin
        partial_1 <= d1;
    end
end

// 최종 합산
always @(posedge clk) begin
    if (reset) begin
        result <= 16'd0;
    end else begin
        result <= partial_10k + partial_1k + partial_100 + partial_10 + partial_1;
    end
end

assign ready_out = valid_pipe[2];

endmodule

기능 검증을 위한 시뮬레이션 환경

module tb_conversion();

    reg clk;
    reg rst;

    initial begin
        rst = 1;
        #300;
        rst = 0;
    end

    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    reg         valid_in;
    reg [15:0]  input_data;

    wire        ready_ascii;
    wire [39:0] ascii_stream;

    wire        ready_dec;
    wire [15:0] final_value;

    always @(posedge clk) begin
        if (rst) begin
            valid_in   <= 1'b0;
            input_data <= 16'd0;
        end else begin
            valid_in   <= 1'b1;
            input_data <= input_data + 1;
        end
    end

    decimal_to_ascii_16bit encoder (
        .clk(clk),
        .reset(rst),
        .data_valid(valid_in),
        .input_value(input_data),
        .ready(ready_ascii),
        .ascii_output(ascii_stream)
    );

    ascii_to_decimal_16bit decoder (
        .clk(clk),
        .reset(rst),
        .data_valid(ready_ascii),
        .ascii_in(ascii_stream),
        .ready_out(ready_dec),
        .result(final_value)
    );

endmodule

태그: Verilog FPGA BCD ASCII 디지털 논리 설계

6월 15일 03:12에 게시됨