1. 项目描述
本次参加硬禾学堂举办的2024寒假一起练的活动,选择的任务是基于小脚丫 FPGA 套件 STEP BaseBoard 实现一个简易的计算器。
2. 硬件资源
STEP BaseBoard V4.0 是第4代小脚丫FPGA扩展底板,可以用于全系列小脚丫核心板的功能扩展,采用100mm*161.8mm的黄金比例尺寸,板子集成了存储器、温湿度传感器、接近式传感器、矩阵键盘、旋转编码器、HDMI接口、RGBLCD液晶屏、8个7位数码管、蜂鸣器模块、UART通信模块、ADC模块、DAC模块和WIFI通信模块,配合小脚丫FPGA板能够完成多种实验,是数字逻辑、微机原理、可编程逻辑语言以及EDA设计工具等课程完美的实验平台。
本次任务中,使用到了下列外设资源:
- 4×4矩阵键盘
- 8位7段数码管
矩阵键盘用于输入数字和运算符,数码管用于显示输入的数字和计算结果。
3. 需求分析
- 基于小脚丫 FPGA 套件 STEP BaseBoard V4.0 实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,4×4键盘按键分配如下图所示:
- 运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。
- 可以在 WebIDE环境 下进行 Verilog 代码编程、综合、仿真、生成 JED 代码并下载到FPGA中进行验证,也可以使用 Lattice 官方提供的 Diamond 工具,在本项目中我使用的是 Diamond 工具。
根据上述要求,项目可以分成四个步骤完成:
- 实现键盘输入的检测和解码;
- 实现8位数码管的显示;
- 加减乘除的计算逻辑;
- 整合这些模块,实现交互逻辑控制。
4. 功能框图
4.1 模块分解
4.2 信号连接
5. 项目开发
5.1 使用 Diamond 工具新建工程
注意选择 Diamond 比较新的版本,我在项目初期,安装了 Diamond v3.10 版本,发现没有集成免费授权可以使用的 Modelsim 工具,后来重新安装了 Diamond v3.13 才解决该问题。 Modelsim 可以仿真模块,所以如果不想单独安装 Modelsim 的话,最好选择最新的 Diamond 版本。
- 新建项目
- 选择芯片
- 选择综合工具
- 添加文件
5.2 键盘扫描模块
原理
矩阵键盘扫描是通过将键盘的按键布局排列成一个矩阵,然后使用行和列的交叉扫描来检测按键是否被按下的原理。一般来说,矩阵键盘由若干列和行组成,每个按键位于行和列的交叉点上。
在扫描过程中,控制器会依次选中每一行,并检查该行上的各列,以确定哪些按键被按下。这种扫描方法可以有效地减少所需的输入/输出引脚数量,从而节省硬件资源。
扫描的过程如下:
- 所有的列被设置为高电平,所有的行被设置为输入模式。
- 逐行扫描,将当前行设置为低电平,同时检测各列的输入状态。
- 如果有按键被按下,对应的列会变为低电平,因此可以通过检测低电平的列来确定哪些按键被按下。
本次项目中,step baseboard 上有一个 4x4 的矩阵键盘,四根行线作为输入,信号由FPGA输出,四根列线作为输出,默认输出高电平,也连接到FPGA。FPGA在四根行线依次输出低电平,每5ms切换到下一根行线。
模块代码
module array_keyboard # (
parameter CNT_200HZ = 60000
)
(
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;
/**
* Because a 4x4 matrix keyboard is used and implemented by scanning method, a state machine is used here to implement it, divided into 4 states in total.
* During a certain state's time, the corresponding 4 keys act as independent keys, which can be sampled using a cycle sampling method for each key independently.
* When using the period sampling method, samples are taken every 20ms, corresponding to the state machine cycling every 20ms, with each state corresponding to a 5ms time.
*/
// Counter counting frequency to generate a 5ms clock signal 'clk_200hz' from the system clock at 12MHz
reg [15:0] cnt;
reg clk_200hz;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 16'd0;
clk_200hz <= 1'b0;
end else begin
if (cnt >= ((CNT_200HZ >> 1) - 1)) begin
cnt <= 16'd0;
clk_200hz <= ~clk_200hz;
end else begin
cnt <= cnt + 1'b1;
clk_200hz <= clk_200hz;
end
end
end
reg [1:0] c_state;
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)
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;
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;
always @ (posedge clk or negedge rst_n)
if (!rst_n)
key_out_r <= 16'hffff;
else
key_out_r <= key_out;
assign key_pulse = key_out_r & (~key_out);
endmodule
仿真波形
仿真时将 CNT_200HZ
调整为10,这样出结果快一些:
5.3 键盘脉冲解码模块
原理
键盘扫描模块根据按键的不同,输出不同的脉冲数据,总共由16种,分别为:
在本项目中,因为没有实现小数的计算,我将按键 '.' 屏蔽掉了。
“加减乘除d等于” 分别解码为 0x10, 0x11, 0x12, 0x13, 0x14
key pulse | 描述 | 解码数据 |
---|---|---|
16'h0001 | 按键 '7' | 8'h07 |
16'h0002 | 按键 '8' | 8'h08 |
16'h0004 | 按键 '9' | 8'h09 |
16'h0008 | 按键 '/' | 8'h13 |
16'h0010 | 按键 '4' | 8'h04 |
16'h0020 | 按键 '5' | 8'h05 |
16'h0040 | 按键 '6' | 8'h06 |
16'h0080 | 按键 '*' | 8'h12 |
16'h0100 | 按键 '1' | 8'h01 |
16'h0200 | 按键 '2' | 8'h02 |
16'h0400 | 按键 '3' | 8'h03 |
16'h0800 | 按键 '-' | 8'h11 |
16'h1000 | 按键 '0' | 8'h00 |
16'h2000 | 按键 '.' | 屏蔽掉了 |
16'h4000 | 按键 '=' | 8'h14 |
16'h8000 | 按键 '+' | 8'h10 |
模块代码
module key_decode (
input clk,
input rst_n,
input [15:0] key_pulse, // Debounced action pulse signal of the key
output reg rdy,
output reg [ 7:0] key_data // High 4 bits represent the tens place, low 4 bits represent the ones place
);
// Key_pulse transfer to key_data
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_data <= 8'h00;
rdy <= 1'b0;
end else begin
case (key_pulse) // The pulse width of key_pulse is equal to the period of clk_in
16'h0001: begin key_data <= 8'h07; rdy <= 1'b1; end // '7'
16'h0002: begin key_data <= 8'h08; rdy <= 1'b1; end // '8'
16'h0004: begin key_data <= 8'h09; rdy <= 1'b1; end // '9'
16'h0008: begin key_data <= 8'h13; rdy <= 1'b1; end // '/'
16'h0010: begin key_data <= 8'h04; rdy <= 1'b1; end // '4'
16'h0020: begin key_data <= 8'h05; rdy <= 1'b1; end // '5'
16'h0040: begin key_data <= 8'h06; rdy <= 1'b1; end // '6'
16'h0080: begin key_data <= 8'h12; rdy <= 1'b1; end // '*'
16'h0100: begin key_data <= 8'h01; rdy <= 1'b1; end // '1'
16'h0200: begin key_data <= 8'h02; rdy <= 1'b1; end // '2'
16'h0400: begin key_data <= 8'h03; rdy <= 1'b1; end // '3'
16'h0800: begin key_data <= 8'h11; rdy <= 1'b1; end // '-'
16'h1000: begin key_data <= 8'h00; rdy <= 1'b1; end // '0'
16'h2000: begin key_data <= key_data; rdy <= 1'b0; end // '.', keep the last number
16'h4000: begin key_data <= 8'h14; rdy <= 1'b1; end // '='
16'h8000: begin key_data <= 8'h10; rdy <= 1'b1; end // '+'
default : begin key_data <= key_data; rdy <= 1'b0; end // keep the last number when no key is pressed
endcase
end
end
endmodule
仿真波形
5.4 基于74HC595的8位数码管模块
原理
74HC595是一个8位移位寄存器,可以通过串行输入并行输出的方式来扩展微控制器的输出端口数量,在本项目中,用了两个 74HC595,一个用于控制数码管的段数,另一个用于数码管的选择。
工作原理如下:
- 数据输入:将要显示的数据经过处理后,以串行方式输入到74HC595芯片的串行输入引脚(SER)。
- 时钟信号:通过触发时钟信号(SH_CP),数据会被顺序地移入移位寄存器中。每个时钟脉冲,数据向左移动一位。
- 锁存信号:在所有数据都移入移位寄存器后,触发锁存信号(ST_CP),此时数据被并行输出到8个输出引脚上。
- 连接到数码管:将其中一个74HC595的8个输出引脚连接到7段数码管对应的数码管段上,另外一个74HC595的8个输出引脚连接到对应数码管的位选信号,以便选择要显示的数码管。
- 重复步骤:通过不断重复上述操作,可以实现多个数码管的显示。
模块代码
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
仿真波形
5.5 计算模块
状态机
状态 | 条件 | 动作 |
---|---|---|
STATE0 | 收到数字 | 保存数字为NUMBER_A,收到多个数字则依次乘10进位,状态不变 |
STATE0 | 收到等于号 | 记录等于号,状态不变 |
STATE0 | 收到算数运算符 | 跳转到 STATE1 |
STATE1 | 收到数字 | 保存数字为NUMBER_B,收到多个数字则依次乘10进位 |
STATE1 | 收到等于号 | 进行计算,结果为NUMBER_C,NUMBER_A和NUMBER_B 清零,跳转到 STATE0 |
STATE1 | 收到算数运算符 | 进行计算,结果为NUMBER_C 并且赋值给 NUMBER_A,NUMBER_B 清零,保持状态不变 |
运算
- 两个数的加法,减法,乘法可以直接通过算数运算符进行计算;
- 两个数的除法,需要实现一个加法器,直接用
/
的结果不正确;
模块逻辑代码
部分代码如下:
module calculate (
input wire clk,
input wire rst_n,
input wire decode_rdy,
input wire [ 7:0] decode_data,
output reg [ 7:0] seg_en,
output [31:0] seg_data
);
// divide calculation
reg start;
reg [31:0] a;
reg [31:0] b;
wire [31:0] c;
wire [31:0] R;
wire done;
wire err;
divide_v1 u5 (
.clk (clk ),
.reset (rst_n ),
.start (start ),
.A (a ),
.B (b ),
.D (c ),
.R (R ),
.ok (done ), // =1 when ready to get the result
.err (err )
);
reg [ 1:0] state, div_state;
reg [ 1:0] operator, cnt;
reg [31:0] num_a, num_b, num_c;
reg [31:0] data_bin;
// calculation
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 2'b00;
div_state<= 2'b00;
operator <= 2'b00;
num_a <= 32'd0;
num_b <= 32'd0;
num_c <= 32'd0;
start <= 1'b0;
data_bin <= 32'd0;
cnt <= 2'd0;
end else begin
case (state)
2'b00: begin
if (decode_rdy == 1'b1) begin
if (decode_data >= 8'h00 && decode_data <= 8'h09) begin // '0' ~ '9'
num_a = num_a * 10 + decode_data;
data_bin <= num_a;
end else if (decode_data == 8'h10) begin // '+'
state <= 2'b01;
div_state<= 2'b00;
cnt <= 2'd0;
operator <= 2'b00;
end else if (decode_data == 8'h11) begin // '-'
state <= 2'b01;
div_state<= 2'b00;
cnt <= 2'd0;
operator <= 2'b01;
end else if (decode_data == 8'h12) begin // '*'
state <= 2'b01;
div_state<= 2'b00;
cnt <= 2'd0;
operator <= 2'b10;
end else if (decode_data == 8'h13) begin // '/'
state <= 2'b01;
div_state<= 2'b00;
cnt <= 2'd0;
operator <= 2'b11;
end
end
end
2'b01
....
....
除法模块代码
在除法模块中,采用了一个简单的条件判断来执行除法运算。当接收到start
信号时,状态机会根据除法算法逐步计算商和余数,直到完成整个除法过程。在每个时钟周期中,状态机会检查当前的除法结果,并更新下一步的计算。最终,在除法完成后,输出结果到D
和R
端口,并设置ok
信号为1以通知外部可以获取结果。这个模块仅支持32位无符号整数的除法运算,且不处理除数为0的情况,因此在使用时需确保输入数据的正确性。
另外在本项目中,余数部分没有做处理,不能除尽时,只显示商。
module divide_v1 (
input clk,
input reset,
input start,
input [31:0] A,
input [31:0] B,
output [31:0] D,
output [31:0] R,
output ok, // =1 when ready to get the result
output err
);
reg active; // True if the divider is running
reg [ 4:0] cycle; // Number of cycles to go
reg [31:0] result; // Begin with A, end with D
reg [31:0] denom; // B
reg [31:0] work; // Running R
// Calculate the current digit
wire [32:0] sub = { work[30:0], result[31] } - denom;
assign err = !B;
// Send the results to our master
assign D = result;
assign R = work;
assign ok = ~active;
// The state machine
always @(posedge clk or negedge reset) begin
if (~reset) begin
active <= 0;
cycle <= 0;
result <= 0;
denom <= 0;
work <= 0;
end
else if(start) begin
if (active) begin
// Run an iteration of the divide.
if (sub[32] == 0) begin
work <= sub[31:0];
result <= {result[30:0], 1'b1};
end
else begin
work <= {work[30:0], result[31]};
result <= {result[30:0], 1'b0};
end
if (cycle == 0) begin
active <= 0;
end
cycle <= cycle - 5'd1;
end
else begin
// Set up for an unsigned divide.
cycle <= 5'd31;
result <= A;
denom <= B;
work <= 32'b0;
active <= 1;
end
end
end
endmodule
仿真波形
5.6 BIN 转 BCD 码模块
BCD 编码
当需要将数字显示在数码管上时,BCD(Binary-Coded Decimal)编码。BCD编码是一种用二进制数形式来表示十进制数的方式,每个十进制数都用4个位来进行表示。例如,十进制数57会被表示为0101 0111
。
在控制数码管时,使用BCD编码可以简化数字到显示的转换过程。
参考step fpga官方的代码。
5.7 顶层模块
说明
- 顶层模块中实例化了上述键盘扫描模块,键盘脉冲解码模块,数码管模块,计算模块。
- BIN转BCD模块和除法模块在运算模块中实例化。
模块代码
module simple_calculator (
input wire clk,
input wire rst_n,
input wire [ 3:0] col,
output wire [ 3:0] row,
output seg_rck, // RCK pin of 74HC595
output seg_sck, // SCK pin of 74HC595
output seg_din // SER pin of 74HC595
);
// For array_keyboard and key_decode
wire [15:0] key_out;
wire [15:0] key_pulse;
wire decode_rdy;
wire [ 7:0] decode_data;
// For segment_scan display
wire [ 7:0] seg_en;
wire [31:0] seg_data;
// Array_KeyBoard
array_keyboard array_keyboard_inst (
.clk (clk ),
.rst_n (rst_n ),
.col (col ),
.row (row ),
.key_out (key_out ),
.key_pulse (key_pulse )
);
// key_decode
key_decode key_decode_inst (
.clk (clk ),
.rst_n (rst_n ),
.key_pulse (key_pulse ),
.rdy (decode_rdy ),
.key_data (decode_data)
);
// Segment_scan display module
segment_scan segment_scan_inst (
.clk (clk ), // System clock 12MHz
.rst_n (rst_n ), // System reset, active low
.dat_1 (seg_data[31:28]), // Input data for SEG1 display
.dat_2 (seg_data[27:24]), // Input data for SEG2 display
.dat_3 (seg_data[23:20]), // Input data for SEG3 display
.dat_4 (seg_data[19:16]), // Input data for SEG4 display
.dat_5 (seg_data[15:12]), // Input data for SEG5 display
.dat_6 (seg_data[11: 8]), // Input data for SEG6 display
.dat_7 (seg_data[ 7: 4]), // Input data for SEG7 display
.dat_8 (seg_data[ 3: 0]), // Input data for SEG8 display
.dat_en (seg_en ), // Enable for displaying digit data, [MSB~LSB]=[SEG1~SEG8]
.dot_en (8'b0000_0000 ), // Enable for displaying decimal points, [MSB~LSB]=[SEG1~SEG8]
.seg_rck (seg_rck ), // RCK pin of 74HC595
.seg_sck (seg_sck ), // SCK pin of 74HC595
.seg_din (seg_din ) // SER pin of 74HC595
);
// Calculation module
calculate calulation_inst (
.clk (clk ),
.rst_n (rst_n ),
.decode_rdy (decode_rdy ),
.decode_data (decode_data ),
.seg_en (seg_en ),
.seg_data (seg_data )
);
endmodule
6. 管脚分配
7. FPGA的资源利用说明
资源使用如下:
Design Summary:
Number of registers: 439 out of 4635 (9%)
PFU registers: 439 out of 4320 (10%)
PIO registers: 0 out of 315 (0%)
Number of SLICEs: 1301 out of 2160 (60%)
SLICEs as Logic/ROM: 1301 out of 2160 (60%)
SLICEs as RAM: 0 out of 1620 (0%)
SLICEs as Carry: 387 out of 2160 (18%)
Number of LUT4s: 2601 out of 4320 (60%)
Number used as logic LUTs: 1827
Number used as distributed RAM: 0
Number used as ripple logic: 774
Number used as shift registers: 0
Number of PIO sites used: 13 + 4(JTAG) out of 105 (16%)
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_c: 241 loads, 241 rising, 0 falling (Driver: PIO clk )
Net clk_200hz: 24 loads, 0 rising, 24 falling (Driver: array_keyboard_inst/clk_200hz_38 )
Net segment_scan_inst/clk_40khz: 18 loads, 18 rising, 0 falling (Driver: segment_scan_inst/clk_40khz_68 )
Number of Clock Enables: 25
Net clk_c_enable_244: 6 loads, 6 LSLICEs
Net calulation_inst/clk_c_enable_259: 17 loads, 17 LSLICEs
Net calulation_inst/clk_c_enable_4: 1 loads, 1 LSLICEs
Net calulation_inst/clk_c_enable_64: 32 loads, 32 LSLICEs
Net calulation_inst/clk_c_enable_65: 1 loads, 1 LSLICEs
Net calulation_inst/clk_c_enable_113: 25 loads, 25 LSLICEs
Net calulation_inst/state_0: 2 loads, 2 LSLICEs
Net calulation_inst/clk_c_enable_223: 32 loads, 32 LSLICEs
Net calulation_inst/start: 9 loads, 9 LSLICEs
Net calulation_inst/clk_c_enable_127: 7 loads, 7 LSLICEs
Net calulation_inst/clk_c_enable_257: 1 loads, 1 LSLICEs
Net calulation_inst/clk_c_enable_236: 26 loads, 26 LSLICEs
Net calulation_inst/u5/clk_c_enable_192: 16 loads, 16 LSLICEs
Net array_keyboard_inst/clk_200hz_N_24_enable_36: 6 loads, 6 LSLICEs
Net array_keyboard_inst/clk_200hz_N_24_enable_40: 6 loads, 6 LSLICEs
Net array_keyboard_inst/clk_200hz_N_24_enable_44: 6 loads, 6 LSLICEs
Net array_keyboard_inst/clk_200hz_N_24_enable_48: 6 loads, 6 LSLICEs
Net key_decode_inst/clk_c_enable_33: 4 loads, 4 LSLICEs
Net segment_scan_inst/state_0: 3 loads, 3 LSLICEs
Net segment_scan_inst/clk_40khz_enable_20: 8 loads, 8 LSLICEs
Net segment_scan_inst/clk_40khz_enable_2: 1 loads, 1 LSLICEs
Net segment_scan_inst/clk_40khz_enable_3: 1 loads, 1 LSLICEs
Net segment_scan_inst/clk_40khz_enable_26: 1 loads, 1 LSLICEs
Net segment_scan_inst/state_1: 2 loads, 2 LSLICEs
Net segment_scan_inst/clk_40khz_enable_27: 1 loads, 1 LSLICEs
Number of LSRs: 8
Net clk_200hz_N_26: 9 loads, 9 LSLICEs
Net calulation_inst/n15241: 17 loads, 17 LSLICEs
Net calulation_inst/u5/n6193: 16 loads, 16 LSLICEs
Net array_keyboard_inst/n15271: 2 loads, 2 LSLICEs
Net segment_scan_inst/cnt_9__N_204: 6 loads, 6 LSLICEs
Net segment_scan_inst/n67600: 2 loads, 2 LSLICEs
Net segment_scan_inst/n62659: 3 loads, 3 LSLICEs
Net segment_scan_inst/n63684: 1 loads, 1 LSLICEs
Number of nets driven by tri-state buffers: 0
Top 10 highest fanout non-clock nets:
Net rst_n_c: 154 loads
Net calulation_inst/n68747: 113 loads
Net n909: 82 loads
Net calulation_inst/n5910: 67 loads
Net n68736: 66 loads
Net calulation_inst/n67563: 63 loads
Net calulation_inst/num_a_0: 53 loads
Net calulation_inst/active: 45 loads
Net calulation_inst/num_a_1: 45 loads
Net calulation_inst/num_b_0: 45 loads
8. 功能展示
见视频。