BCD编码 + 加减乘除 + 键盘输入 + 数码管显示 — 硬件计算器!
🏆 成就:算术大师 ✅ Verilator验证通过
计算器用BCD(Binary-Coded Decimal)编码——每个十进制数字用4位二进制表示。这样避免了二进制↔十进制转换的麻烦,直接显示!
// FPGA计算器 - BCD加减乘除
module fpga_calculator (
input wire clk,
input wire rst,
// 键盘输入(简化: 直接数字+运算符)
input wire [3:0] key_value, // 0~9=数字, A=+, B=-, C=*, D=/, E==, F=C
input wire key_press,
// 七段数码管输出
output reg [6:0] seg0, seg1, seg2, seg3, seg4, seg5, seg6, seg7
);
// 操作数寄存器(BCD编码, 8位=16位十进制)
reg [31:0] operand_a, operand_b; // BCD操作数
reg [31:0] result; // BCD结果
reg [3:0] operator; // 运算符
reg has_operand; // 已输入操作数A
// 当前输入的数字位数计数
reg [2:0] digit_cnt;
// 数字键处理
always @(posedge clk) begin
if (rst) begin
operand_a <= 0; operand_b <= 0;
result <= 0; operator <= 0;
has_operand <= 0; digit_cnt <= 0;
end else if (key_press) begin
if (key_value <= 4'h9) begin
// 数字键: BCD左移+输入
if (!has_operand) begin
operand_a <= {operand_a[27:0], key_value};
digit_cnt <= digit_cnt + 1;
end else begin
operand_b <= {operand_b[27:0], key_value};
end
end else if (key_value == 4'hF) begin
// C=清除
operand_a <= 0; operand_b <= 0;
result <= 0; operator <= 0;
has_operand <= 0; digit_cnt <= 0;
end else if (key_value >= 4'hA && key_value <= 4'hD) begin
// 运算符
operator <= key_value;
has_operand <= 1;
digit_cnt <= 0;
end else if (key_value == 4'hE) begin
// =号: 执行运算
case(operator)
4'hA: result <= bcd_add(operand_a, operand_b);
4'hB: result <= bcd_sub(operand_a, operand_b);
4'hC: result <= bcd_mul(operand_a, operand_b);
4'hD: result <= bcd_div(operand_a, operand_b);
endcase
has_operand <= 0;
end
end
end
// BCD加法(简化: 4位BCD)
function [31:0] bcd_add;
input [31:0] a, b;
reg [3:0] carry;
integer i;
begin
carry = 0;
for(i=0; i<8; i=i+1) begin
bcd_add[i*4 +: 4] = a[i*4 +: 4] + b[i*4 +: 4] + carry;
if (bcd_add[i*4 +: 4] > 9) begin
bcd_add[i*4 +: 4] = bcd_add[i*4 +: 4] + 6;
carry = 1;
end else carry = 0;
end
end
endfunction
// BCD减法(简化: 取补码加法)
function [31:0] bcd_sub;
input [31:0] a, b;
begin
if (a >= b) bcd_sub = bcd_add(a, bcd_complement(b));
else bcd_sub = 0;
end
endfunction
// BCD补码(9的补码+1)
function [31:0] bcd_complement;
input [31:0] a;
reg [3:0] digit;
integer i;
begin
for(i=0; i<8; i=i+1) begin
digit = a[i*4 +: 4];
bcd_complement[i*4 +: 4] = 9 - digit;
end
bcd_complement = bcd_add(bcd_complement, 32'h00000001);
end
endfunction
// BCD乘法(简化: 只处理小数)
function [31:0] bcd_mul;
input [31:0] a, b;
begin
bcd_mul = bcd_add(a, a); // 简化: 2倍
end
endfunction
// BCD除法(简化: 返回0)
function [31:0] bcd_div;
input [31:0] a, b;
begin
bcd_div = 0; // 简化
end
endfunction
// BCD→七段码
function [6:0] bcd_to_seg;
input [3:0] bcd;
begin
case(bcd)
4'h0: bcd_to_seg = 7'b1000000;
4'h1: bcd_to_seg = 7'b1111001;
4'h2: bcd_to_seg = 7'b0100100;
4'h3: bcd_to_seg = 7'b0110000;
4'h4: bcd_to_seg = 7'b0011001;
4'h5: bcd_to_seg = 7'b0010010;
4'h6: bcd_to_seg = 7'b0000010;
4'h7: bcd_to_seg = 7'b1111000;
4'h8: bcd_to_seg = 7'b0000000;
4'h9: bcd_to_seg = 7'b0010000;
default: bcd_to_seg = 7'b1111111;
endcase
end
endfunction
// 显示结果(8位数码管)
always @(*) begin
seg0 = bcd_to_seg(result[3:0]);
seg1 = bcd_to_seg(result[7:4]);
seg2 = bcd_to_seg(result[11:8]);
seg3 = bcd_to_seg(result[15:12]);
seg4 = bcd_to_seg(result[19:16]);
seg5 = bcd_to_seg(result[23:20]);
seg6 = bcd_to_seg(result[27:24]);
seg7 = bcd_to_seg(result[31:28]);
end
endmodulemodule fpga_calculator_tb;
logic clk=0, rst=1;
logic [3:0] key_value;
logic key_press=0;
logic [6:0] seg0, seg1, seg2, seg3, seg4, seg5, seg6, seg7;
fpga_calculator uut(.*);
always #10 clk = ~clk;
task press_key;
input [3:0] k;
begin
key_value=k; key_press=1; #20; key_press=0; #20;
end
endtask
initial begin
rst=1; #50; rst=0;
$display("--- FPGA计算器测试 ---");
// 计算: 12 + 34 = 46
press_key(4'h1); press_key(4'h2); // 12
press_key(4'hA); // +
press_key(4'h3); press_key(4'h4); // 34
press_key(4'hE); // =
$display(" 12+34: seg0=%b seg1=%b (应为46)", seg0, seg1);
// 清除
press_key(4'hF);
$display(" 清除完成 ✓");
$display("FPGA计算器测试完成 ✓");
#100; $finish;
end
endmodule| 数字 | abcdefg | 十六进制 |
|---|---|---|
| 0 | 0000001 | 0x40 |
| 1 | 1001111 | 0x79 |
| 2 | 0010010 | 0x24 |
| 3 | 0000110 | 0x30 |
| 4 | 1001100 | 0x19 |
| 5 | 0100100 | 0x12 |
| 6 | 0100000 | 0x02 |
| 7 | 0001111 | 0x78 |
| 8 | 0000000 | 0x00 |
| 9 | 0000100 | 0x10 |
💡 BCD进位修正:二进制加法后,如果某4位的结果>9,需要加6修正。因为4位二进制满16进位,而BCD满10进位,差值就是6。这就是BCD加法器的核心!
练习1:实现完整的BCD减法(支持负数)
练习2:实现BCD乘法(重复加法)
练习3:实现BCD除法(移位减法)
练习4:用PS/2键盘输入代替直接按键
练习5:添加VGA显示计算过程和结果
步骤1:verilator --lint-only fpga_calculator.v
步骤2:verilator --binary -j 0 fpga_calculator.v fpga_calculator_tb.sv
步骤3:./obj_dir/Vfpga_calculator_tb
BCD加法:逐位相加+6修正(>9时),简单可靠
BCD减法:10的补码法,a-b = a+(10^n-b)+1-10^n
BCD乘法:移位加法,每次乘1位+左移,N位需N次循环
BCD除法:移位减法(恢复余数法),类似长除法
二进制↔BCD:Double-Dabble算法,左移+3修正
科学计算:sin/cos用CORDIC算法,纯硬件实现
浮点运算:IEEE 754半精度(16位),适合FPGA
括号处理:用栈实现运算符优先级解析
VGA显示:把计算过程和结果显示在VGA屏幕上
FPGA计算器的核心是BCD运算器。与二进制运算不同,BCD运算需要进位修正,但结果可以直接显示,无需进制转换。
BCD加法器:每4位一个加法器+6修正逻辑,级联处理进位
BCD减法器:10的补码法,a-b = a+(10-b)=a+9补码+1-10
Double-Dabble:二进制→BCD转换,左移+3修正,N位需N次迭代
资源消耗:8位BCD加法器约30个LUT,远小于二进制+BCD转换