1.项目需求
这个项目的任务是实现一个二位十进制数的计算器。基于FPGA 硬件描述语言 Verilog HDL,系统设计由计算部分、显示部分,输入部分三个部分组成,显示部分由八个七段译码管组成,分别来显示输入数字,输入部分采用4*4矩阵键盘,由0-9一共十个数字按键,加减乘除四个运算符按键,一个等号按键组成的。通过外部的按键可以完成加、减、乘、除四种功能运算,其结构简单,易于实现。
2.需求分析
实现一个基本的计算器需要以下几种功能(我们会在部分3中阐述实现的方式)
- 键盘输入
- 数字和运算符的存储
- 运算处理
- 结果显示
3.实现的方式
怎么通过fpga底板以及板载资源实现部分2中的需求呢?
- 键盘输入
- 通过FPGA编程驱动矩阵键盘电路,获取矩阵键盘键入的信息,然后通过编码将键盘输出的信息译码成对应的键值信息
- 某时刻,FPGA控制4根行线分别为ROW1=0、ROW2=1、ROW3=1、ROW4=1时,
- 在这一时刻只有K1、K2、K3、K4按键被按下,才会导致4根列线输出COL1=0、COL2=0、COL3=0、COL4=0,否则COL1=1、COL2=1、COL3=1、COL4=1,反之当FPGA检测到列线(COL1、COL2、COL3、COL4)中有低电平信号时,对应的K1、K2、K3、K4按键应该是被按下了。 按照扫描的方式,一共分为4个时刻,分别对应4根行线中的一根拉低,4个时刻依次循环,这样就完成了矩阵按键的全部扫描检测,我们在程序中以这4个时刻对应状态机的4个状态。我们将矩阵键盘的扫描周期分为4个时刻,对应4个状态,使得状态机在4个状态上循环跳转,最终通过扫描的方式获取矩阵键盘的操作状态。
- 按键抖动的不稳定时间在10ms以内,所以对同一个按键采样的周期大于10ms,这里同样取20ms时间。20ms时间对应4个状态,每5分钟进行一次状态转换。所以我们在状态机之前先增加分频模块,得到200Hz的分频时钟,然后状态机按照200Hz分频时钟的节拍做状态跳转和键盘采样。
- 我们还需要一个pulse采样矩阵键盘是否按下
- 数码管显示
- 共阴8段数码管的信号端低电平有效,而共阳端接高电平有效。当共阳端接高电平时只要在各个位段上加上相应的低电平信号就可以使相应的位段发光。比如:要使a段发光,则在a段信号端加上低电平即可。共阴极的数码管则相反。我们采用74HC595进行数码管的驱动
- 计算模块实现
- 计算模块是连接按键模块和数码管显示模块的桥梁。通过按键模块的输入,计算器进行计算,并且输出到数码管模块
- 在计算器中运用了一个状态机,让计算模块在输入数据和运算之间运转
- 并且计算器模块为了适配数码管显示,还需要做一个二进制转bcd的模块
4.功能框图
- 矩阵键盘功能框图
2.数码管显示功能框图
3.计算模块框图
5.代码和说明
顶层模块
module caculator(
input clk, //系统时钟 12MHz
input rst_n, //系统复位 低电平有效
input [3:0] col,
output wire [3:0] row,
output wire seg_rck, //74HC595的RCK管脚
output wire seg_sck, //74HC595的SCK管脚
output wire seg_din //74HC595的SER管脚
);
wire [15:0] key_out;
wire [15:0] key_pulse;
wire [15:0] seg_data;
wire [3:0] fuhao;
reg key_pulse_cal; //key是否按下
reg [3:0] key_data; //按下的键0-15
reg [3:0] dat_1; //SEG1 显示的数据
reg [3:0] dat_2; //SEG2
reg [3:0] dat_3; //SEG3
reg [3:0] dat_4; //SEG4
reg [3:0] dat_5; //SEG5
reg [3:0] dat_6; //SEG6
reg [3:0] dat_7; //SEG7
reg [3:0] dat_8; //SEG8
//转码
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
key_data <= 0;
key_pulse_cal <= 0;
end
else
begin
case(key_pulse)
16'b0000_0000_0000_0001 : begin key_data <= 4'h7; key_pulse_cal <= 1'b1; end
16'b0000_0000_0000_0010 : begin key_data <= 4'h8; key_pulse_cal <= 1'b1; end
16'b0000_0000_0000_0100 : begin key_data <= 4'h9; key_pulse_cal <= 1'b1; end
16'b0000_0000_0000_1000 : begin key_data <= 4'hA; key_pulse_cal <= 1'b1; end
16'b0000_0000_0001_0000 : begin key_data <= 4'h4; key_pulse_cal <= 1'b1; end
16'b0000_0000_0010_0000 : begin key_data <= 4'h5; key_pulse_cal <= 1'b1; end
16'b0000_0000_0100_0000 : begin key_data <= 4'h6; key_pulse_cal <= 1'b1; end
16'b0000_0000_1000_0000 : begin key_data <= 4'hB; key_pulse_cal <= 1'b1; end
16'b0000_0001_0000_0000 : begin key_data <= 4'h3; key_pulse_cal <= 1'b1; end
16'b0000_0010_0000_0000 : begin key_data <= 4'h2; key_pulse_cal <= 1'b1; end
16'b0000_0100_0000_0000 : begin key_data <= 4'h1; key_pulse_cal <= 1'b1; end
16'b0000_1000_0000_0000 : begin key_data <= 4'hC; key_pulse_cal <= 1'b1; end
16'b0001_0000_0000_0000 : begin key_data <= 4'h0; key_pulse_cal <= 1'b1; end
16'b0010_0000_0000_0000 : begin key_data <= 4'hD; key_pulse_cal <= 1'b1; end
16'b0100_0000_0000_0000 : begin key_data <= 4'hE; key_pulse_cal <= 1'b1; end
16'b1000_0000_0000_0000 : begin key_data <= 4'hF; key_pulse_cal <= 1'b1; end
default : key_pulse_cal <= 1'b0;
endcase
end
end
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
dat_5 <= 4'b0;
dat_6 <= 4'b0;
dat_7 <= 4'b0;
dat_8 <= 4'b0;
end
else
begin
dat_4 <= seg_data[19:16];
dat_5 <= seg_data[15:12];
dat_6 <= seg_data[11:8];
dat_7 <= seg_data[7:4];
dat_8 <= seg_data[3:0];
end
end
cal cal(
.clk(clk), //系统时钟 12MHz
.rst_n(rst_n), //系统复位
.key_pulse(key_pulse_cal), //key是否按下
.key_data(key_data), //按下的键倿0-15
.seg_data(seg_data), //用来让数码管显示
.fuhao(fuhao)
);
array_keyboard array_keyboard
(
.clk(clk),
.rst_n(rst_n),
.col(col),
.row(row),
.key_out(key_out),
.key_pulse(key_pulse)
);
segment_scan segment_scan(
.clk(clk), //系统时钟 12MHz
.rst_n(rst_n), //系统复位 低有效
.dat_1( fuhao), //SEG1
.dat_2(), //SEG2
.dat_3(), //SEG3
.dat_4(), //SEG4
.dat_5(seg_data[16:12]), //SEG5
.dat_6(seg_data[11:8]), //SEG6
.dat_7(seg_data[7:4]), //SEG7
.dat_8(seg_data[3:0]), //SEG8
.dat_en(8'b1000_1111), //数码管数据位显示使能,[MSB~LSB]=[SEG1~SEG8]
.dot_en(8'b0000_0000 ), //数码管小数点位显示使能,[MSB~LSB]=[SEG1~SEG8]
.seg_rck(seg_rck), //74HC595的RCK管脚
.seg_sck(seg_sck), //74HC595的SCK管脚
.seg_din(seg_din) //74HC595的SER管脚
);
endmodule
顶层模块例化了矩阵键盘,数码管和计算模块。并进行了矩阵模块采样到的键值的解码,顶层模块的输入有时钟和复位,矩阵键盘的列为输入,行为输出。还有输出到数码管的信号。
矩阵键盘模块
module array_keyboard #
(
parameter CNT_200HZ = 60000
// parameter CNT_200HZ = 60 //仿真
)
(
input clk,
input rst_n,
input [3:0] col,
output reg [3:0] row,
output reg [15:0] key_out,
output [15:0] key_pulse
);
localparam STATE0 = 2'b00;
localparam STATE1 = 2'b01;
localparam STATE2 = 2'b10;
localparam STATE3 = 2'b11;
/*
因使用4x4矩阵按键,通过扫描方法实现,所以这里使用状态机实现,共分为4种状态
在其中的某一状态时间里,对应的4个按键相当于独立按键,可按独立按键的周期采样法采样
周期采样时每隔20ms采样一次,对应这里状态机每隔20ms循环一次,每个状态对应5ms时间
对矩阵按键实现原理不明白的,请去了解矩阵按键实现原理
*/
//计数器计数分频实现5ms周期信号clk_200hz,系统时钟12MHz
reg [15:0] cnt;
reg clk_200hz;
always@(posedge clk or negedge rst_n) begin //复位时计数器cnt清零,clk_200hz信号起始电平为低电平
if(!rst_n) begin
cnt <= 16'd0;
clk_200hz <= 1'b0;
end else begin
if(cnt >= ((CNT_200HZ>>1) - 1)) begin //数字逻辑中右移1位相当于除2
cnt <= 16'd0;
clk_200hz <= ~clk_200hz; //clk_200hz信号取反
end else begin
cnt <= cnt + 1'b1;
clk_200hz <= clk_200hz;
end
end
end
reg [1:0] c_state;
//状态机根据clk_200hz信号在4个状态间循环,每个状态对矩阵按键的行接口单行有效
always@(posedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
c_state <= STATE0;
row <= 4'b1110;
end else begin
case(c_state)
//状态c_state跳转及对应状态下矩阵按键的row输出
STATE0: begin c_state <= STATE1; row <= 4'b1101; end
STATE1: begin c_state <= STATE2; row <= 4'b1011; end
STATE2: begin c_state <= STATE3; row <= 4'b0111; end
STATE3: begin c_state <= STATE0; row <= 4'b1110; end
default:begin c_state <= STATE0; row <= 4'b1110; end
endcase
end
end
reg [15:0] key,key_r;
//因为每个状态中单行有效,通过对列接口的电平状态采样得到对应4个按键的状态,依次循环
always@(negedge clk_200hz or negedge rst_n) begin
if(!rst_n) begin
key_out <= 16'hffff; key_r <= 16'hffff; key <= 16'hffff;
end else begin
case(c_state)
//采集当前状态的列数据赋值给对应的寄存器位
//对键盘采样数据进行判定,连续两次采样低电平判定为按键按下
STATE0: begin key_out[ 3: 0] <= key_r[ 3: 0]|key[ 3: 0]; key_r[ 3: 0] <= key[ 3: 0]; key[ 3: 0] <= col; end
STATE1: begin key_out[ 7: 4] <= key_r[ 7: 4]|key[ 7: 4]; key_r[ 7: 4] <= key[ 7: 4]; key[ 7: 4] <= col; end
STATE2: begin key_out[11: 8] <= key_r[11: 8]|key[11: 8]; key_r[11: 8] <= key[11: 8]; key[11: 8] <= col; end
STATE3: begin key_out[15:12] <= key_r[15:12]|key[15:12]; key_r[15:12] <= key[15:12]; key[15:12] <= col; end
default:begin key_out <= 16'hffff; key_r <= 16'hffff; key <= 16'hffff; end
endcase
end
end
reg [15:0] key_out_r;
//Register low_sw_r, lock low_sw to next clk
always @ ( posedge clk or negedge rst_n )
if (!rst_n) key_out_r <= 16'hffff;
else key_out_r <= key_out; //将前一刻的值延迟锁存
//wire [15:0] key_pulse;
//Detect the negedge of low_sw, generate pulse
assign key_pulse= key_out_r & ( ~key_out); //通过前后两个时刻的值判断
endmodule
矩阵键盘模块用了小脚丫官方的历程代码,采用行列扫描矩阵键盘,并且这个状态机实现了采样和消抖,最后还通过两个锁存键值的寄存器,输出了key_pulse,监测是否按下矩阵键盘某个键,方便使用。
数码管模块
//模块2:数码管
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'h40; // -
seg[11] = 7'h00; // 设计成不亮作为初始状态
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
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
数码管显示模块同样使用的小脚丫官方历程,值得注意的是,在本次计算器的实现中为了功能需要,将数码管的字库进行了修改,将seg[10]改为了负号,将seg[11]改为了全0,即不显示任何数值。底板上使用的是共阴极数码管。底板上使用了74HC595芯片进行串行转并行。
计算模块
//模块3:计算器
module cal(
input clk, //系统时钟 12MHz
input rst_n, //系统复位
input key_pulse, //key是否按下
input [3:0] key_data, //按下的键值0-15
output reg [15:0] seg_data, //用来让数码管显示
output reg [3:0] fuhao //在数码管的第五位
);
reg [2:0] state;
reg [7:0] data1; //操作数1
reg [7:0] data2; //操作数2
reg [3:0] op; //运算符号
reg [15:0] result; //计算结果9999 14位
parameter IN_DATA1 = 3'h0;
parameter IN_DATA2 = 3'h1;
parameter CALCULATE= 3'h2;
parameter DECODE = 3'h3;
parameter IN_DATA3 = 3'h4;
reg turn = 1'b0; //是否需要转bcd的标志位
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= IN_DATA1;
op <= 4'h0;
data1 <= 8'h0;
data2 <= 8'h0;
result<= 16'h0;
seg_data <= 16'hBBBB;
turn <= 1'b0;
fuhao <= 4'HB;
end
else
begin
case(state)
IN_DATA1 :
if(key_pulse == 1)
begin
if(key_data < 4'd10)
begin
data1 <= data1*10 +key_data;
seg_data <= {seg_data[11:0],key_data};
state <= IN_DATA1;
turn <= 1'b0;
end
else if(key_data < 4'd14 || key_data == 4'd15)//运算符按下
begin
op <= key_data;
state <= IN_DATA2;
seg_data <= 16'hBBBB;
end
else
state <= IN_DATA1;
end
else
state <= IN_DATA1;
IN_DATA2 :
if(key_pulse)
begin
if(key_data < 4'd10)
begin
data2 <= data2*10 +key_data;
seg_data <= {seg_data[11:0],key_data};
//seg_data <= seg_data*10+key_data
state <= IN_DATA2;
end
else if(key_data == 4'd14)
state <= CALCULATE;
else
state <= IN_DATA2;
end
else
state <= IN_DATA2;
CALCULATE :
begin
case(op)
4'hA : begin result <= data1 / data2; turn <= 1'b1; end
4'hB : begin result <= data1 * data2; turn <= 1'b1; end
4'hC : begin
if(data1<data2)
begin
result <= data2 - data1;
turn <= 1'b1;
fuhao <= 4'd10;
end
else
begin
result <= data1 - data2;
turn <= 1'b1;
end
end
4'hF : begin result <= data1 + data2; turn <= 1'b1; end
endcase
state <= DECODE;
end
DECODE :
begin
if(turn)
begin
seg_data[3:0] <= (bcd_segdata[3:0]||bcd_segdata[7:4]||bcd_segdata[11:8]||bcd_segdata[15:12])?bcd_segdata[3:0]:4'hB;
seg_data[7:4] <= (bcd_segdata[7:4]||bcd_segdata[11:8]||bcd_segdata[15:12])?bcd_segdata[7:4]:4'hB;
seg_data[11:8] <= (bcd_segdata[11:8]||bcd_segdata[15:12])?bcd_segdata[11:8]:4'hB;
seg_data[15:12] <= bcd_segdata[15:12]?bcd_segdata[15:12]:4'hB;
end
/*用/再取模会出问题,用[3:0] = 【3:0】也会出现13+3=10的问题*/
/*所以需要再写一个module 二进制转为bcd*/
else
seg_data <= seg_data;
state <= IN_DATA3;
end
IN_DATA3 :
if(key_pulse == 1)
begin
if(key_data < 4'd10)
begin
data1 <= data1*10 + key_data; //按下多少数都可以
seg_data <= {seg_data[11:0],key_data};
data2 <= 0;
state <= IN_DATA1;
end
else if(key_data < 4'd14) //连续运算
begin
data1 <= result;
data2 <= 0;
seg_data <= 0;
op <= key_data;
state <= IN_DATA2;
end
else
state <= IN_DATA3;
end
else
state <= IN_DATA3;
default :
state <= IN_DATA1;
endcase
end
end
wire [19:0] bcd_segdata;
reg [35:0] data;
assign bcd_segdata = data[35:16];
always@(result) begin
data = {20'd0,result};
repeat(16) begin // 16位二进制码需要循环16次
if(data[19:16]>=5)
data[19:16] = data[19:16] + 3;
if(data[23:20]>=5)
data[23:20] = data[23:20] + 3;
if(data[27:24]>=5)
data[27:24] = data[27:24] + 3;
if(data[31:28]>=5)
data[31:28] = data[31:28] + 3;
if(data[35:32]>=5)
data[35:32] = data[35:32] + 3;
data = data << 1;
end
end
endmodule
在计算模块中,使用了一个状态机,初始显示设置为不亮,接收到矩阵键盘模块的key_pulse信号后判断是误操作,还是键入数据。指的一提的是在DECODE状态下,因为设置默认为不亮,然而若个位为0十位不为0,进行了一个或运算来更好的显示。计算模块实现了加减乘除等基本操作,并且实现了小数减大数得到一个负数,以及连续计算的功能。
6.仿真波形图
计算模块仿真图如下,实例为4-9=(-)5,其中负号由额外的reg(fuhao显示)fuhao为a时对应的数码管字库为-,为b时对应数码管不亮
我们可以看到在45.3us进行了data1的输入为4,170us进行了操作符的输入,c对应减号。300us进行操作数2的输入为9,435us按下等号,计算result,经过bcd转换后,为5,并且负号由B(对应字符库中不亮)转换为a(对应字符库中-号)。
这里再给出整体top的仿真波形,在图中有矩阵键盘是否按下的key_pulse信息和key_data按下的哪个键,有计算模块的data,result和fuhao,也有数码管显示模块的dat1,5,6,7,8
7.fpga的资源利用说明
Design Summary
Number of registers: 190 out of 4665 (4%)
PFU registers: 190 out of 4320 (4%)
PIO registers: 0 out of 345 (0%)
Number of SLICEs: 459 out of 2160 (21%)
SLICEs as Logic/ROM: 459 out of 2160 (21%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 170 out of 2160 (8%)
Number of LUT4s: 916 out of 4320 (21%)
Number used as logic LUTs: 576
Number used as distributed RAM: 0
Number used as ripple logic: 340
Number used as shift registers: 0
Number of PIO sites used: 13 + 4(JTAG) out of 115 (15%)
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
Net clk_200hz: 24 loads, 0 rising, 24 falling (Driver:
array_keyboard/clk_200hz_38 )
Net clk_c: 79 loads, 79 rising, 0 falling (Driver: PIO clk )
Net segment_scan/clk_40khz: 18 loads, 18 rising, 0 falling (Driver:
segment_scan/clk_40khz_68 )
Number of Clock Enables: 20
Net clk_c_enable_52: 5 loads, 5 LSLICEs
Net key_data_3__N_25: 2 loads, 2 LSLICEs
Net segment_scan/clk_40khz_enable_1: 1 loads, 1 LSLICEs
Net segment_scan/clk_40khz_enable_2: 1 loads, 1 LSLICEs
Net segment_scan/clk_40khz_enable_20: 7 loads, 7 LSLICEs
Net segment_scan/state_0: 4 loads, 4 LSLICEs
Net segment_scan/clk_40khz_enable_5: 1 loads, 1 LSLICEs
Net segment_scan/state_1: 2 loads, 2 LSLICEs
Net segment_scan/clk_40khz_enable_21: 1 loads, 1 LSLICEs
Net cal/clk_c_enable_48: 13 loads, 13 LSLICEs
Net cal/clk_c_enable_33: 16 loads, 16 LSLICEs
Net cal/clk_c_enable_3: 1 loads, 1 LSLICEs
Net cal/clk_c_enable_17: 5 loads, 5 LSLICEs
Net cal/clk_c_enable_7: 1 loads, 1 LSLICEs
Net cal/clk_c_enable_55: 2 loads, 2 LSLICEs
Net cal/clk_c_enable_62: 5 loads, 5 LSLICEs
Net array_keyboard/clk_200hz_N_537_enable_48: 6 loads, 6 LSLICEs
Net array_keyboard/clk_200hz_N_537_enable_37: 6 loads, 6 LSLICEs
Net array_keyboard/clk_200hz_N_537_enable_41: 6 loads, 6 LSLICEs
Net array_keyboard/clk_200hz_N_537_enable_45: 6 loads, 6 LSLICEs
Number of LSRs: 8
Net clk_200hz_N_539: 9 loads, 9 LSLICEs
Net segment_scan/n5166: 2 loads, 2 LSLICEs
Net segment_scan/n5150: 4 loads, 4 LSLICEs
Net segment_scan/n5156: 1 loads, 1 LSLICEs
Net segment_scan/n5149: 3 loads, 3 LSLICEs
Net segment_scan/cnt_9__N_688: 6 loads, 6 LSLICEs
Net cal/n2627: 5 loads, 5 LSLICEs
Net array_keyboard/n5167: 1 loads, 1 LSLICEs
Number of nets driven by tri-state buffers: 0
Top 10 highest fanout non-clock nets:
Net cal/data2_0: 43 loads
Net cal/n434: 35 loads
Net key_data_3: 34 loads
Net cal/data2_2: 30 loads
Net cal/data2_3: 28 loads
Net cal/data2_4: 28 loads
Net cal/n9437: 28 loads
Net cal/op_0: 27 loads
Net segment_scan/cnt_main_0: 27 loads
Net cal/data2_1: 26 loads