2024寒假一起练 - 基于小脚丫 FPGA 套件 STEP BaseBoard 实现简易计算器
该项目使用了小脚丫 FPGA 套件 STEP BaseBoard,实现了简易计算器的设计,它的主要功能为:undefined。
标签
FPGA
topgear
更新2024-04-01
231

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设计工具等课程完美的实验平台。
image.png

本次任务中,使用到了下列外设资源:

  • 4×4矩阵键盘
  • 8位7段数码管

矩阵键盘用于输入数字和运算符,数码管用于显示输入的数字和计算结果。

3. 需求分析

  • 基于小脚丫 FPGA 套件 STEP BaseBoard V4.0 实现一个两位十进制数加、减、乘、除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,4×4键盘按键分配如下图所示:
    image.png
  • 运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。
  • 可以在 WebIDE环境 下进行 Verilog 代码编程、综合、仿真、生成 JED 代码并下载到FPGA中进行验证,也可以使用 Lattice 官方提供的 Diamond 工具,在本项目中我使用的是 Diamond 工具。

根据上述要求,项目可以分成四个步骤完成:

  • 实现键盘输入的检测和解码;
  • 实现8位数码管的显示;
  • 加减乘除的计算逻辑;
  • 整合这些模块,实现交互逻辑控制。

4. 功能框图

4.1 模块分解

image.png

4.2 信号连接

image.png

5. 项目开发

5.1 使用 Diamond 工具新建工程

注意选择 Diamond 比较新的版本,我在项目初期,安装了 Diamond v3.10 版本,发现没有集成免费授权可以使用的 Modelsim 工具,后来重新安装了 Diamond v3.13 才解决该问题。 Modelsim 可以仿真模块,所以如果不想单独安装 Modelsim 的话,最好选择最新的 Diamond 版本。

  • 新建项目
    image.pngimage.pngimage.png
  • 选择芯片
    image.png
  • 选择综合工具
    image.pngimage.png
  • 添加文件
    image.png

5.2 键盘扫描模块

原理

矩阵键盘扫描是通过将键盘的按键布局排列成一个矩阵,然后使用行和列的交叉扫描来检测按键是否被按下的原理。一般来说,矩阵键盘由若干列和行组成,每个按键位于行和列的交叉点上。

在扫描过程中,控制器会依次选中每一行,并检查该行上的各列,以确定哪些按键被按下。这种扫描方法可以有效地减少所需的输入/输出引脚数量,从而节省硬件资源。

扫描的过程如下:

  1. 所有的列被设置为高电平,所有的行被设置为输入模式。
  2. 逐行扫描,将当前行设置为低电平,同时检测各列的输入状态。
  3. 如果有按键被按下,对应的列会变为低电平,因此可以通过检测低电平的列来确定哪些按键被按下。

本次项目中,step baseboard 上有一个 4x4 的矩阵键盘,四根行线作为输入,信号由FPGA输出,四根列线作为输出,默认输出高电平,也连接到FPGA。FPGA在四根行线依次输出低电平,每5ms切换到下一根行线。
image.png

模块代码
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,这样出结果快一些:
image.png

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

仿真波形

image.png

5.4 基于74HC595的8位数码管模块

原理

74HC595是一个8位移位寄存器,可以通过串行输入并行输出的方式来扩展微控制器的输出端口数量,在本项目中,用了两个 74HC595,一个用于控制数码管的段数,另一个用于数码管的选择。
工作原理如下:

  1. 数据输入:将要显示的数据经过处理后,以串行方式输入到74HC595芯片的串行输入引脚(SER)。
  2. 时钟信号:通过触发时钟信号(SH_CP),数据会被顺序地移入移位寄存器中。每个时钟脉冲,数据向左移动一位。
  3. 锁存信号:在所有数据都移入移位寄存器后,触发锁存信号(ST_CP),此时数据被并行输出到8个输出引脚上。
  4. 连接到数码管:将其中一个74HC595的8个输出引脚连接到7段数码管对应的数码管段上,另外一个74HC595的8个输出引脚连接到对应数码管的位选信号,以便选择要显示的数码管。
  5. 重复步骤:通过不断重复上述操作,可以实现多个数码管的显示。
模块代码
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

仿真波形

image.png

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信号时,状态机会根据除法算法逐步计算商和余数,直到完成整个除法过程。在每个时钟周期中,状态机会检查当前的除法结果,并更新下一步的计算。最终,在除法完成后,输出结果到DR端口,并设置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
仿真波形

image.png

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. 管脚分配

image.png

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. 功能展示

见视频。

9. 参考资料链接

附件下载
simple_calculator.zip
Lattice Diamond 工程文件
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号