内容介绍
内容介绍
项目要求
本项目要求使用小脚丫FPGA套件STEP BaseBoard V4.0,实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制。
- 运算数和计算结果通过八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。(注:由于板子较宽,拍摄时会导致八位数码管很难和LCD显示屏保持同框,所以这里仅使用前四位数码管进行显示。当然也可以修改代码为后四位显示,这里暂时不做修改。)
- 使用TFTLCD来做显示,自行设计显示界面,代替数码管显示输入的参数、运算符以及计算后的结果。
板卡介绍
STEP BaseBoard V4.0是小脚丫FPGA系列的第四代扩展底板,为各类小脚丫核心板提供功能扩展支持。
- 功能扩展板:提供了多种功能模块,包括存储器、传感器、接口等。
- 黄金比例尺寸:采用了100mm*161.8mm的黄金比例尺寸设计。
- 广泛应用:适用于数字逻辑、微机原理、可编程逻辑语言等多种教学和实验场景。
- 丰富资源:板载资源包括存储器、传感器、键盘、编码器、接口等,满足各种实验需求。
实现思路
实现一个两位十进制加、减、乘、除计算器,需要设计一个适合的算法,利用FPGA的逻辑资源和板载的功能模块来完成计算器的逻辑运算和界面显示。
实现框图
实现方法
- 设计按键事件:利用板载的矩阵键盘输入操作数和运算符。
always @(posedge clk or negedge rst_n)
begin
if (!rst_n)
begin
dat <= 32'hffffffff;
dot_en <= 8'h00;
calc <= 3'b0;
first_dat <= 32'hffffffff;
end
else
begin
if(key_falling_edge[0])
new_data = 4'd7;
else if(key_falling_edge[1])
new_data = 4'd8;
else if(key_falling_edge[2])
new_data = 4'd9;
else if(key_falling_edge[4])
new_data = 4'd4;
else if(key_falling_edge[5])
new_data = 4'd5;
else if(key_falling_edge[6])
new_data = 4'd6;
else if(key_falling_edge[8])
new_data = 4'd3;
else if(key_falling_edge[9])
new_data = 4'd2;
else if(key_falling_edge[10])
new_data = 4'd1;
else if(key_falling_edge[12])
new_data = 4'd0;
else if (key_falling_edge[13])
begin
dot_en[0] <= ~dot_en[0];
end
else if (key_falling_edge[3])
begin
calc <= 3'd1;
end
else if (key_falling_edge[7])
begin
calc <= 3'd2;
end
else if (key_falling_edge[11])
begin
calc <= 3'd3;
end
else if (key_falling_edge[15])
begin
calc <= 3'd4;
end
else if (key_falling_edge[14])
begin
if (calc != 0)
begin
dat <= {20'hfffff, result_bcd_out};
calc <= 0;
end
end
if (key_falling_edge[3] || key_falling_edge[7] || key_falling_edge[11] || key_falling_edge[15])
begin
if (dat != 32'hffffffff)
begin
first_dat <= dat;
dat <= 32'hffffffff;
dot_en <= 8'h00;
end
end
if(key_falling_edge[0] || key_falling_edge[1] || key_falling_edge[2] || key_falling_edge[4] || key_falling_edge[5] || key_falling_edge[6] || key_falling_edge[8] || key_falling_edge[9] || key_falling_edge[10] || key_falling_edge[12])
begin
dat <= {dat[27:0], new_data};
dot_en <= {dot_en[6:0], 1'b0};
end
if (key_falling_edge)
refresh <= 1;
else
refresh<=0;
end
end
通过检查按键的跳变沿,判断是否按下了相应的按键。通过映射按键为 0~9 的数字和运算符,将结果暂存起来。
- 计算结果实现:当按下了等于号,计算结果,并把结果转换为BCD码。
assign result = calc == 1 ? first_dat_value / dat_value : (calc == 2 ? dat_value * first_dat_value : (calc == 3 ? (first_dat_value - dat_value) : (dat_value + first_dat_value)));
wire [11:0] result_bcd;
wire [11:0] result_bcd_out;
assign result_bcd_out = {(result_bcd[11:8] ==0) ? 4'hf: result_bcd[11:8], (result_bcd[11:4] ==0) ? 4'hf: result_bcd[7:4], result_bcd[3:0]};
这里,使用了小脚丫FPGA提供的 bcd2bin 模块进行BCD码的转换。
bin2bcd bin2bcd_i (
.bitcode(result[7:0]),
.bcdcode(result_bcd)
);
/*-------------------------------------*/
// Module name : bin2bcd
// Author : STEP
// Description : 8位二进制数转BCD码
// Web : www.stepfpga.com
/*-------------------------------------*/
module bin2bcd (
input [7:0] bitcode,
output [11:0] bcdcode
);
reg [19:0] data;
assign bcdcode = data[19:8];
always@(bitcode) begin
data = {12'd0,bitcode};
repeat(8) begin //二进制码总共8位,所以循环位数是8
if(data[11:8]>=5)
data[11:8] = data[11:8] + 3;
if(data[15:12]>=5)
data[15:12] = data[15:12] + 3;
if(data[19:16]>=5)
data[19:16] = data[19:16] + 3;
data = data << 1;
end
end
endmodule
- 数码管输出设计:根据结果,输出到数码管上。由于需要去除起始位上的零,这里对 BCD 码进行处理,如果等于 0b1111,则将对应数码管的输出关闭。(注:由于板子较宽,拍摄时会导致八位数码管很难和LCD显示屏保持同框,所以这里仅使用前四位数码管进行显示。当然也可以修改代码为后四位显示,这里暂时不做修改。)
assign out_dat = (calc == 0) ? dat : (dat == 32'hffffffff ? first_dat : dat);
wire [7:0] dat_en;
assign dat_en = { out_dat[31:28] != 4'b1111 , out_dat[27:24] != 4'b1111 , out_dat[23:20] != 4'b1111 , out_dat[19:16] != 4'b1111 , out_dat[15:12] != 4'b1111 , out_dat[11:8] != 4'b1111 , out_dat[7:4] != 4'b1111 , out_dat[3:0] != 4'b1111 };
segment_scan segment_scan_i (
.clk(clk),
.rst_n(rst_n),
.dat_1(out_dat[15:12]),
.dat_2(out_dat[11:8]),
.dat_3(out_dat[7:4]),
.dat_4(out_dat[3:0]),
.dat_5(4'd0),
.dat_6(4'd0),
.dat_7(4'd0),
.dat_8(4'd0),
.dat_en({dat_en[3:0], 4'd0}),
.dot_en({dot_en[3:0], 4'd0}),
.seg_rck(seg_rck),
.seg_sck(seg_sck),
.seg_din(seg_din)
);
数码管驱动使用小脚丫FPGA提供的驱动。
// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: segment_scan
//
// Author: Step
//
// Description: Display with Segment tube
//
// Web: www.stepfpga.com
//
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date: |Changes Made:
// V1.0 |2015/11/11 |Initial ver
// --------------------------------------------------------------------
module segment_scan(
input clk, //系统时钟 12MHz
input rst_n, //系统复位 低有效
input [3:0] dat_1, //SEG1 显示的数据输入
input [3:0] dat_2, //SEG2 显示的数据输入
input [3:0] dat_3, //SEG3 显示的数据输入
input [3:0] dat_4, //SEG4 显示的数据输入
input [3:0] dat_5, //SEG5 显示的数据输入
input [3:0] dat_6, //SEG6 显示的数据输入
input [3:0] dat_7, //SEG7 显示的数据输入
input [3:0] dat_8, //SEG8 显示的数据输入
input [7:0] dat_en, //数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
input [7:0] dot_en, //数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
output reg seg_rck, //74HC595的RCK管脚
output reg seg_sck, //74HC595的SCK管脚
output reg seg_din //74HC595的SER管脚
);
localparam CNT_40KHz = 300; //分频系数
localparam IDLE = 3'd0;
localparam MAIN = 3'd1;
localparam WRITE = 3'd2;
localparam LOW = 1'b0;
localparam HIGH = 1'b1;
//创建数码管的字库,字库数据依段码顺序有关
//这里字库数据[MSB~LSB]={G,F,E,D,C,B,A}
reg[6:0] seg [15:0];
always @(negedge rst_n)
begin
seg[0] = 7'h3f; // 0
seg[1] = 7'h06; // 1
seg[2] = 7'h5b; // 2
seg[3] = 7'h4f; // 3
seg[4] = 7'h66; // 4
seg[5] = 7'h6d; // 5
seg[6] = 7'h7d; // 6
seg[7] = 7'h07; // 7
seg[8] = 7'h7f; // 8
seg[9] = 7'h6f; // 9
seg[10] = 7'h77; // A
seg[11] = 7'h7c; // b
seg[12] = 7'h39; // C
seg[13] = 7'h5e; // d
seg[14] = 7'h79; // E
seg[15] = 7'h71; // F
end
//计数器对系统时钟信号进行计数
reg [9:0] cnt = 1'b0;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 1'b0;
else if(cnt>=(CNT_40KHz-1))
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
end
//根据计数器计数的周期产生分频的脉冲信号
reg clk_40khz = 1'b0;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_40khz <= 1'b0;
else if(cnt<(CNT_40KHz>>1))
clk_40khz <= 1'b0;
else
clk_40khz <= 1'b1;
end
//使用状态机完成数码管的扫描和74HC595时序的实现
reg [15:0] data;
reg [2:0] cnt_main;
reg [5:0] cnt_write;
reg [2:0] state = IDLE;
always@(posedge clk_40khz or negedge rst_n)
begin
if(!rst_n)
begin //复位状态下,各寄存器置初值
state <= IDLE;
cnt_main <= 3'd0;
cnt_write <= 6'd0;
seg_din <= 1'b0;
seg_sck <= LOW;
seg_rck <= LOW;
end
else
begin
case(state)
IDLE:
begin //IDLE作为第一个状态,相当于软复位
state <= MAIN;
cnt_main <= 3'd0;
cnt_write <= 6'd0;
seg_din <= 1'b0;
seg_sck <= LOW;
seg_rck <= LOW;
end
MAIN:
begin
cnt_main <= cnt_main + 1'b1;
state <= WRITE; //在配置完发给74HC595的数据同时跳转至WRITE状态,完成串行时序
case(cnt_main)
//对8位数码管逐位扫描
//data [15:8]为段选, [7:0]为位选
3'd0:
data <= {{dot_en[7],seg[dat_1]},dat_en[7]?8'hfe:8'hff};
3'd1:
data <= {{dot_en[6],seg[dat_2]},dat_en[6]?8'hfd:8'hff};
3'd2:
data <= {{dot_en[5],seg[dat_3]},dat_en[5]?8'hfb:8'hff};
3'd3:
data <= {{dot_en[4],seg[dat_4]},dat_en[4]?8'hf7:8'hff};
3'd4:
data <= {{dot_en[3],seg[dat_5]},dat_en[3]?8'hef:8'hff};
3'd5:
data <= {{dot_en[2],seg[dat_6]},dat_en[2]?8'hdf:8'hff};
3'd6:
data <= {{dot_en[1],seg[dat_7]},dat_en[1]?8'hbf:8'hff};
3'd7:
data <= {{dot_en[0],seg[dat_8]},dat_en[0]?8'h7f:8'hff};
default:
data <= {8'h00,8'hff};
endcase
end
WRITE:
begin
if(cnt_write >= 6'd33)
cnt_write <= 1'b0;
else
cnt_write <= cnt_write + 1'b1;
case(cnt_write)
//74HC595是串行转并行的芯片,3路输入可产生8路输出,而且可以级联使用
//74HC595的时序实现,参考74HC595的芯片手册
6'd0:
begin
seg_sck <= LOW;
seg_din <= data[15];
end //SCK下降沿时SER更新数据
6'd1:
begin
seg_sck <= HIGH;
end //SCK上升沿时SER数据稳定
6'd2:
begin
seg_sck <= LOW;
seg_din <= data[14];
end
6'd3:
begin
seg_sck <= HIGH;
end
6'd4:
begin
seg_sck <= LOW;
seg_din <= data[13];
end
6'd5:
begin
seg_sck <= HIGH;
end
6'd6:
begin
seg_sck <= LOW;
seg_din <= data[12];
end
6'd7:
begin
seg_sck <= HIGH;
end
6'd8:
begin
seg_sck <= LOW;
seg_din <= data[11];
end
6'd9:
begin
seg_sck <= HIGH;
end
6'd10:
begin
seg_sck <= LOW;
seg_din <= data[10];
end
6'd11:
begin
seg_sck <= HIGH;
end
6'd12:
begin
seg_sck <= LOW;
seg_din <= data[9];
end
6'd13:
begin
seg_sck <= HIGH;
end
6'd14:
begin
seg_sck <= LOW;
seg_din <= data[8];
end
6'd15:
begin
seg_sck <= HIGH;
end
6'd16:
begin
seg_sck <= LOW;
seg_din <= data[7];
end
6'd17:
begin
seg_sck <= HIGH;
end
6'd18:
begin
seg_sck <= LOW;
seg_din <= data[6];
end
6'd19:
begin
seg_sck <= HIGH;
end
6'd20:
begin
seg_sck <= LOW;
seg_din <= data[5];
end
6'd21:
begin
seg_sck <= HIGH;
end
6'd22:
begin
seg_sck <= LOW;
seg_din <= data[4];
end
6'd23:
begin
seg_sck <= HIGH;
end
6'd24:
begin
seg_sck <= LOW;
seg_din <= data[3];
end
6'd25:
begin
seg_sck <= HIGH;
end
6'd26:
begin
seg_sck <= LOW;
seg_din <= data[2];
end
6'd27:
begin
seg_sck <= HIGH;
end
6'd28:
begin
seg_sck <= LOW;
seg_din <= data[1];
end
6'd29:
begin
seg_sck <= HIGH;
end
6'd30:
begin
seg_sck <= LOW;
seg_din <= data[0];
end
6'd31:
begin
seg_sck <= HIGH;
end
6'd32:
begin
seg_rck <= HIGH;
end //当16位数据传送完成后RCK拉高,输出生效
6'd33:
begin
seg_rck <= LOW;
state <= MAIN;
end
default:
;
endcase
end
default:
state <= IDLE;
endcase
end
end
endmodule
- 界面设计:通过LCD液晶屏显示输入的操作数和运算符,以及计算结果。这里设计为当任意按键按下时,就进行屏幕刷新。
if (key_falling_edge)
refresh <= 1;
else
refresh<=0;
屏幕内容会从RAM中进行读取,这里前 45 行会使用数字和运算符进行显示,后 46~319 行会显示电子森林的 LOGO。
`timescale 1 ns / 100 ps
module pic_ram (address, q, num,calc);
input wire [8:0] address;
output wire [239:0] q;
input wire [11:0] num ;
input wire [2:0] calc ;
wire [25:0] q_1, q_2, q_3, q_4;
number_ram number_ram_inst_0
(
.address(address),
.num(num[3:0]),
.q(q_1)
);
number_ram number_ram_inst_1
(
.address(address),
.num(num[7:4]),
.q(q_2)
);
// number_ram number_ram_inst_2
// (
// .address(address),
// .num(num[11:8]),
// .q(q_3)
// );
assign q_3 = 0;
calc_ram calc_ram_inst_0
(
.address(address),
.calc(calc),
.q(q_4)
);
assign q = (address <= 44) ? {135'd0, q_4, 26'd0, q_1, q_2, q_3} : 240'h0;
- 使用 WebIDE 进行综合。https://www.stepfpga.com/project/9735
仿真结果
FPGA的资源利用说明
Design Summary:
Number of registers: 476 out of 4635 (10%)
PFU registers: 476 out of 4320 (11%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 2106 out of 2160 (98%)
SLICEs as Logic/ROM: 2106 out of 2160 (98%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 1068 out of 2160 (49%)
Number of LUT4s: 4204 out of 4320 (97%)
Number used as logic LUTs: 2068
Number used as distributed RAM: 0
Number used as ripple logic: 2136
Number used as shift registers: 0
Number of PIO sites used: 19 + 4(JTAG) out of 105 (22%)
Number of block RAMs: 0 out of 10 (0%)
Number of GSRs: 1 out of 1 (100%)
EFB used : No
JTAG used : No
Readback used : No
Oscillator used : No
Startup used : No
POR : On
Bandgap : On
Number of Power Controller: 0 out of 1 (0%)
Number of Dynamic Bank Controller (BCINRD): 0 out of 6 (0%)
Number of Dynamic Bank Controller (BCLVDSO): 0 out of 1 (0%)
Number of DCCA: 0 out of 8 (0%)
Number of DCMA: 0 out of 2 (0%)
Number of PLLs: 0 out of 2 (0%)
Number of DQSDLLs: 0 out of 2 (0%)
Number of CLKDIVC: 0 out of 4 (0%)
Number of ECLKSYNCA: 0 out of 4 (0%)
Number of ECLKBRIDGECS: 0 out of 2 (0%)
Notes:-
1. Total number of LUT4s = (Number of logic LUT4s) + 2*(Number of distributed RAMs) + 2*(Number of ripple logic)
2. Number of logic LUT4s does not include count of distributed RAM and ripple logic.
Number of clocks: 3
实物展示
- 输入的数字和运算符同步显示在LCD屏和数码管上。(注:由于板子较宽,拍摄时会导致八位数码管很难和LCD显示屏保持同框,所以这里仅使用前四位数码管进行显示。当然也可以修改代码为后四位显示,这里暂时不做修改。)
- 结果可以同步展示。
遇到问题
- 资源限制:在设计逻辑电路时需要考虑FPGA资源的限制,尽量优化设计以节约资源的使用。
- 接口兼容:保证各功能模块之间的接口兼容性,确保数据传输的稳定性和准确性。
总结感想
通过本次项目,我成功实现了一个功能完善的两位十进制加、减、乘、除计算器。感谢“硬禾学堂”对本项目的赞助支持,他们的支持为我提供了实验所需的资源和条件,使得本次实验取得了圆满成功。在未来的实验中,我将继续利用小脚丫FPGA套件开展更多有意义的项目,拓展我的技术能力和实践经验。
软硬件
附件下载
implement.jed
archive.zip
团队介绍
中国计量大学
团队成员
cjmf
评论
0 / 100
查看更多
猜你喜欢
2024寒假练-用小脚丫FPGA套件STEP BaseBoard V4.0完成两位十进制加、减、乘、除计算器该项目使用了小脚丫FPGA套件STEP BaseBoard V4.0,实现了两位十进制加、减、乘、除计算器的设计,它的主要功能为:计算器。
kkkmmmhhh
276
2024寒假练 - 基于小脚丫FPGA套件STEP BaseBoard V4.0制作的两位十进制加、减、乘、除计算器该项目使用了WebIDE平台、Verilog HDL语言,实现了计算器的设计,它的主要功能为:实现两位十进制加、减、乘、除。
Glass
701
2024寒假练 - 基于小脚丫FPGA套件STEP BaseBoard V4.0制作的两位十进制加、减、乘、除计算器硬禾学堂-2024寒假一起练项目-平台2-任务1-两位十进制加、减、乘、除计算器
Lee
581
