소규모 숫자의 ASCII 코드 변환
1. 십진 정수를 BCD 코드(8421 방식)로 변환
BCD(Binary-Coded Decimal)는 각 십진 자릿수를 4비트 이진수로 표현하는 인코딩 방식이다. 예를 들어, 십진수 5는 0101로 표현된다.
| 십진수 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| BCD (4비트) | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 |
2. BCD 코드를 ASCII 코드로 변환
숫자 문자의 ASCII 코드는 16진수로 0x30부터 시작한다. 따라서 BCD 값에 0x30을 더하면 해당 숫자의 ASCII 코드가 된다.
| BCD | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 |
| ASCII (16진수) | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
| ASCII (10진수) | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
대규모 정수의 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