本文为明德扬原创及录用文章,转载请注明出处!
1.1 总体设计
1.1.1 概述
学习了明德扬至简设计法和明德扬设计规范,本人设计了一个基于FPGA的出租车计费系统。该系统由一个按键表示出租车上是否有乘客,再通过检测出租车的档位和轮胎的转速来获得乘客所需支付的总费用。在本案例的设计过程中,包含了按键定义和消抖、计数器、数码管显示等技术。经过逐步改进、调试等一系列操作之后,完成了此设计,下面将完整的设计记录与大家分享。
本工程使用VIVADO进行仿真,未进行上板。
1.1.2 设计目标
此设计可以完成出租车计费的功能。在按键按下后(乘客上车),开始按如下规则计费:
起步价5元,超过3KM以每公里2元计费,如果遇到红绿灯、堵车等需要停车等待时,则以每20分钟1元计费。
再按一下按键(乘客下车)则计费结束,算出乘客所需支付的总费用,并在数码管上显示。
1.1.3 系统结构框图
系统结构框图如下所示:
1.1.4 模块功能
由于按键信号是异步信号,所以对该将信号打两拍处理,将异步信号同步化;
实现10ms按键消抖功能,并输出按键信号key_flag,每按一次按键,key_flag取反。
其中key_flag=1表示有乘客,key_flag=0表示没有乘客。
通过按键信号key_flag有效,对轮胎转速进行每秒取样,进而获得乘客行驶总的路程和判断按时计费的使能信号en_0是否有效。
通过speed模块的总路程和按时计费有效使能信号来获得乘客所需支付的总费用,再取得总费用的个位、十位和百位数值并输出。
采用动态扫描3个数码管的方式显示fare模块中获得的三个数值即为乘客所需支付的总费用。
1.1.5 顶层信号
|
|
|
|
|
|
|
|
|
|
|
|
|
|
出租车轮胎转速信号。通过该信号,可以用来计算汽车行驶的距离。
|
|
|
|
|
|
|
|
|
段选信号,共8位。由低到高,分别表示数码管的a,b,c,d,e,f,g,点。当该比特为0时,表示点亮相应位置;为1时熄灭。
|
1.1.6 顶层代码
module taxi_fare(
clk ,
rst_n ,
d_w ,
rev ,
key_in ,
seg_sel ,
segment
);
input clk ;
input rst_n ;
input d_w ; //档位
input [15:0] rev ; //转速
input key_in ; //按键,表示乘客是否上车
output seg_sel ; //位选信号,在低电平是该位置数码管亮。
output segment ; //段选信号,共8位。由低到高,分别表示数码管的a,b,c,d,e,f,g,点。当该比特为0时,表示点亮相应位置;为1时熄灭。
wire [3-1:0] seg_sel ;
wire [8-1:0] segment ;
//中间量
wire key_flag ; //有乘客为1,无乘客为0
wire en_0 ; //按时间计费使能信号
wire [19:0] distance ; //行驶路程
wire [3:0] x_g ; //总费用的个位值
wire [3:0] x_s ; //总费用的十位值
wire [3:0] x_b ; //总费用的百位值
key_litter key_litter_0(
.clk (clk) ,
.rst_n (rst_n) ,
.key_in (key_in) ,
.key_flag (key_flag)
);
speed speed_0(
.clk (clk) ,
.rst_n (rst_n) ,
.d_w (d_w) ,
.rev (rev) ,
.key_flag (key_flag) ,
//输出信号
.en_0 (en_0) ,
.distance (distance)
);
fare fare_0(
.clk (clk) ,
.rst_n (rst_n) ,
.distance (distance) ,
.en_0 (en_0) ,
.key_flag (key_flag) ,
.x_g (x_g) ,
.x_s (x_s) ,
.x_b (x_b)
);
show show_0(
.clk (clk) ,
.rst_n (rst_n) ,
.din ({x_b,x_s,x_g}) ,
.din_vld (3'b111) ,
.disp_en (1) ,
.seg_sel (seg_sel) ,
.segment (segment)
);
endmodule
|
1.2 key模块设计
1.2.1 接口信号
1.2.2 设计思路
此模块通过一个按键来表示车上是否有乘客,复位时输出信号key_flag为0,当乘客上车时,司机按一下按键,key_flag由0变,1,表明乘客已上车,系统开始计费。到达目的地时,司机再按一下按键,key_flag由1变0,表明到达目的地乘客下车,系统结束计费。
独立式按键工作原理如上图所示,4条输入线接到FPGA的IO口上,当按键K1按下时,VCC通过电阻R1再通过按键K1最终进入GND形成一条通路,这条线路的全部电压都加在R1上,则引脚P14是低电平。当松开按键后,线路断开,就不会有电流通过,P14和VCC就应该是等电位,为高电平。我们可以通过P14这个IO口的高低电平状态来判断是否有按键按下。其它按键原理与K1一致,当然本实验只需要一个按键即可,任选一个按键都可以。
从图中可以看出,如果我们按下按键,那么按键就会接通并连接到低电平GND,如果我们没有按下,那么按键就会断开并接到VCC,因此按键为低电平有效。通常的按键所用开关为机械弹性开关,当机械触点断开或者闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而机械式按键在闭合及断开的瞬间均伴随有一连串的抖动,如果不进行处理,会使系统识别到抖动信号而进行不必要的反应,导致模块功能不正常,为了避免这种现象的产生,需要进行按键消抖的操作。
按键消抖主要分为硬件消抖和软件消抖。两个“与非”门构成一个RS触发器为常用的硬件消抖。软件方法消抖,即检测出键闭合后执行一个延时程序,抖动时间的长短由按键的机械特性决定,一般为5ms~20ms,让前沿抖动消失后再一次检测键的状态,如果仍保持闭合状态电平,则确认按下按键操作有效。当检测到按键释放后,也要给5ms~20ms的延时,待后沿抖动消失后才能转入该键的处理程序。经过按键消抖的行人优先按键,判断按键有效后,按键信号传递给控制系统,控制系统再进入相应的处理程序。如还不明白之处,见实验的PDF。
1.2.3 参考代码
使用明德扬的计数器模板,可以很快速很熟练地写出按键消抖模块。
每10ms扫描一次按键输入key_in,可以达到消抖的目的,再用寄存器缓存一下,按键为低电平有效;但本实验是需要按键按下松开这样一次完整的按按键操作后输出key_flag才发生变化,所以检测当检测到按键有上升沿变化时,代表该按键被按下松开,按键输出key_flag才发生变化。
本模块设计了一个状态机,用于按键的检测。该状态机共包括4个状态。
其各个状态的含义如下。
空闲状态(IDLE):表示按键没有被按下,检测到低电平进入下一状态。
延时确认状态(S1):开始10ms延时计数,若计数完成且依然为低电平则进入下一状态,若计数期间按键出现高电平说明为抖动回到初始状态。
检测释放状态(S2):表示按键按下未松开,检测到高电平进入下一状态。
延时确认状态(S3):开始10ms延时计数,若计数完成且依然为高电平视为有效松手行为进入初始状态再检测下一次按键按下,若计数期间按键出现低电平说明为抖动回到S2状态。
代码如下:
module key_litter(
clk ,
rst_n ,
key_in ,
key_flag
);
//消抖的状态
parameter IDLE = 4'b0000 ;
parameter S1 = 4'b0001 ;
parameter S2 = 4'b0010 ;
parameter S3 = 4'b0100 ;
parameter S4 = 4'b1000 ;
//输入信号定义
input clk ;
input rst_n ;
input key_in ;
//输出信号定义
output key_flag; //输出,表示是否有乘客
//输出信号reg定义
reg key_flag;
//中间信号定义
reg [19:0] cnt ;
reg key_in_1; //寄存器
reg key_in_2; //寄存器
reg [3:0] state_c ;
reg [3:0] state_n ;
wire idl2s1_start ;
wire s12s2_start ;
wire s12s2_end ;
wire s22s3_start ;
wire s32s4_start ;
wire s32s4_end ;
wire add_cnt ; //计数进行
wire end_cnt ; //计数清零
wire cnt_during ; //计数过程中
//打两拍
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_in_1 <= 0;
key_in_2 <= 0;
end
else begin
key_in_1 <= key_in;
key_in_2 <= key_in_1;
end
end
//消抖
//四段式状态机
//第一段:同步时序always模块,格式化描述次态寄存器迁移到现态寄存器(不需更改)
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑always模块,描述状态转移条件判断
always@(*)begin
case(state_c)
IDLE:begin
if(idl2s1_start)begin
state_n = S1;
end
else begin
state_n = state_c;
end
end
S1:begin
if(s12s2_start)begin
state_n = S2;
end
else if(s12s2_end)begin
state_n = IDLE ;
end
else begin
state_n = state_c;
end
end
S2:begin
if(s22s3_start)begin
state_n = S3;
end
else begin
state_n = state_c;
end
end
S3:begin
if(s32s4_start)begin
state_n = IDLE;
end
else if(s32s4_end)begin
state_n = S2;
end
else begin
state_n = state_c;
end
end
default:begin
state_n = IDLE;
end
endcase
end
//第三段:设计转移条件
assign idl2s1_start = key_in_2 == 0;
assign s12s2_start = key_in_2==0 && end_cnt == 1;
assign s12s2_end = key_in_2==1 && cnt_during ;
assign s22s3_start = key_in_2 == 1;
assign s32s4_start = key_in_2 == 1 && end_cnt == 1;
assign s32s4_end = key_in_2 == 0 && cnt_during ;
//第四段:同步时序always模块,格式化描述寄存器输出(可有多个输出)
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_flag <=1'b0 ; //初始化
end
else if(s32s4_start)begin
key_flag = ~key_flag;
end
else begin
key_flag <= key_flag;
end
end
//计数器,计数10ms
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
else
cnt <= 0;
end
assign add_cnt = state_c == S1 || state_c == S3;
assign end_cnt = add_cnt && cnt== /*仿真时使用以减少仿真时间5 - 1;*/ 500000-1;
assign cnt_during = add_cnt && end_cnt == 0;
endmodule
|
1.3 speed模块设计
1.3.1 接口信号
|
|
|
|
|
|
|
|
|
|
|
|
|
|
出租车轮胎转速信号。通过该信号,可以用来计算汽车行驶的距离。
|
|
|
|
|
|
表示出租车按时间计费信号,1位按时间计费,0则不按时间计费。等待红绿灯或堵车等需要停车过程中,使用时间计费;行驶过程中,使用距离计费。
|
|
|
|
1.3.2 设计思路
消抖后的按键信号输入到本模块中,同样使用明德扬至简设计法和计数器模板,可以快速写出计算总路程和获得按时计费信号en_0有效的代码。当key_flag为1有效且转速rev>=3r/s时,计数器开始计数,每计一秒钟对转速信号rev取样,获得每秒行驶路程并累加,当key_flag为0时,计数停止,累加也停止,此时获得的累加值即为总路程。当key_flag为1有效且rev<3r/s时,en_0拉高为1,表示此时需要按时计费。
1.3.3 参考代码
module speed(
clk ,
rst_n ,
d_w ,
rev ,
key_flag,
//输出信号
en_0 ,
distance
);
//参数定义
parameter DATA_W = 20;
//输入信号定义
input clk ;
input rst_n ;
input d_w ; //档位
input rev ; //转速
input key_flag; //有无乘客信号
//输入信号定义
wire [15:0] rev ;
//输出信号定义
output[DATA_W-1:0] distance ; //路程
output en_0 ; //按时计费信号
//输出信号reg定义
reg [DATA_W-1:0] distance ;
reg en_0 ;
//中间信号定义
reg [15:0] rev_1 ; //每秒取样转速信号
reg [25:0] cnt ; //一秒的计数器
wire high_en_0 ; //拉高按时计费信号的信号
wire add_cnt ;
wire end_cnt ;
//按时计费
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
en_0 <= 0;
end
else if(high_en_0) begin
en_0 <= 1;
end
else
en_0 <= 0;
end
assign high_en_0 = ( d_w == 0 && rev < 3 || rev < 3 ) && key_flag == 1;
//计数1秒
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
else if(key_flag == 0)
cnt <= 0;
end
assign add_cnt = rev >= 3 && key_flag == 1;
assign end_cnt = add_cnt && cnt == /*仿真时使用以节省仿真时间500-1;*/50000000-1 ;
//每秒取样rev
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
rev_1 <= 0;
end
else if(end_cnt) begin
rev_1 <= rev;
end
end
//distance
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
distance <= 0;
end
else if(end_cnt) begin
distance <=distance + rev_1/3;
end
end
endmodule
|
1.4 fare模块设计
1.4.1 接口信号
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
表示是否有乘客,有为1,没有为0,且在没有乘客时,所有信号归0
|
|
|
|
|
|
|
|
|
|
1.4.2 设计思路
从speed模块中得到乘客乘坐总路程distance和按时计费使能信号en_0,然后以5元起步价,超过3KM以每满1公里2元的计费方式计算出按路程计费的总费用,再通过en_0按20分钟1元的计费方式计算出按时间计费的总费用,再求和获得总费用,最后得到总费用得个位、十位、百位,分别是x_g、x_s、x_b。
1.4.3 参考代码
module fare(
clk ,
rst_n ,
distance,
en_0 ,
key_flag,
x_g ,
x_s ,
x_b
);
//参数定义
parameter DATA_W = 4;
//输入信号定义
input clk ;
input rst_n ;
input en_0 ; //按时计费
input [19:0] distance; //路程
input key_flag; //有无乘客
//输出信号定义
output[DATA_W-1:0] x_g ; //数码管显示个位
output[DATA_W-1:0] x_s ; //数码管显示十位
output[DATA_W-1:0] x_b ; //数码管显示百位
//输出信号reg定义
reg [DATA_W-1:0] x_g ;
reg [DATA_W-1:0] x_s ;
reg [DATA_W-1:0] x_b ;
//中间信号定义
reg [9:0] taxi_fare ; //总车费
reg [9:0] taxi_fare_1; //按路程车费
reg [9:0] taxi_fare_2; //按时间车费
reg [35:0] cnt ; //计数20分钟
reg key_flag1 ; //寄存器
reg key_flag2 ; //寄存器
reg key_flag_low ; //乘客下车信号
reg key_flag_low1 ; //乘客下车后隔一个时钟周期信号
reg [2:0] state ;
wire add_taxi_fare_1;
wire add_taxi_fare_2;
wire add_cnt ;
wire end_cnt ;
//获取key_flag下降沿,表示乘客下车
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_flag1 <= 0;
end
else begin
key_flag1 <= key_flag;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_flag2 <= 0;
end
else begin
key_flag2 <= key_flag1;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_flag_low <= 0;
end
else if(key_flag2==1&&key_flag1==0)begin
key_flag_low <= 1;
end
else begin
key_flag_low <= 0;
end
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_flag_low1 <= 0;
end
else
key_flag_low1 <= key_flag_low;
end
//按路程计费
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
taxi_fare_1 <= 0;
end
else if(add_taxi_fare_1)begin
if(distance<=3000)begin
taxi_fare_1 <= 5;
end
else if(distance>3000)begin
taxi_fare_1 <= 3'd5+ (distance-3000) / 500;//* 0.002;
end
end
end
assign add_taxi_fare_1 = key_flag_low && en_0 == 0 ;
//按时间计费
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
taxi_fare_2 <= 0;
end
else if(add_taxi_fare_2)begin
taxi_fare_2 <= taxi_fare_2 + 1;
end
end
assign add_taxi_fare_2 = end_cnt == 1;
//计数20分钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
end
assign add_cnt = en_0 == 1 && key_flag == 1;
assign end_cnt = add_cnt && cnt== /*仿真时使用以节省仿真时间500*1200-1;*/ 50000000*1200-1;
//获得总车费
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
taxi_fare <= 0;
end
else if(key_flag_low1==1)begin
taxi_fare <= taxi_fare_1+ taxi_fare_2;
end
end
//输出
always @(*)begin
if(rst_n == 1'b0)begin
x_g <= 0;
x_s <= 0;
x_b <= 0;
state <= 1;
end
else if(taxi_fare != 0)begin
case(state)
1: begin x_g <= taxi_fare%10;taxi_fare <= taxi_fare/10;state <= 2;end
2: begin x_s <= taxi_fare%10;taxi_fare <= taxi_fare/10;state <= 3;end
3: begin x_b <= taxi_fare%10;taxi_fare <= taxi_fare/10;state <= 1;end
endcase
end
end
endmodule
|
1.5 show模块设计
1.5.1 接口信号
|
|
|
|
|
|
|
|
|
|
|
打开数码管显示的使能信号。1表示打开显示,0表示不显示。
|
|
|
|
|
|
数码管显示数据刷新使能信号。当其值为1有效时,刷新要显示的数据值。
|
|
|
|
|
|
段选信号,共8位。由低到高,分别表示数码管的a,b,c,d,e,f,g,点。当该比特为0时,表示点亮相应位置;为1时熄灭。
|
1.5.2 设计思路
由于在fare模块中已经获得总费用的个位、十位和百位的值,所以在本模块只需要控制3个数码管对其数值进行显示即可。
本模块在设计过程中采用动态扫描3个数码管的方式进行显示,并且直接使用明德杨提供的数码管显示规范代码。动态扫描方式相比于使用3个独立的数码管显示会节约资源,硬件电路更简单,且数码管越多优势越明显。
1.5.3 参考代码
接口定义:
clk : 时钟信号,频率是50MHz
rst_n : 复位信号,在低电平时有效
seg_sel : 位选信号,在低电平是该位置数码管亮。
segment : 段选信号,共8位。由低到高,分别表示数码管的a,b,c,d,e,f,g,点。当该比特为0时,表示点亮相应位置;为1时熄灭。
module show(
rst_n ,
clk ,
disp_en ,
din ,
din_vld ,
seg_sel ,
segment
);
参数定义,明德扬规范要求,verilog内的用到的数字,都使用参数表示。
参数信号全部大写
parameter SEG_WID = 8;
parameter SEG_NUM = 3;
parameter COUNT_WID = 26;
parameter TIME_20US = 20'd1000;
parameter NUM_0 = 8'b1100_0000;
parameter NUM_1 = 8'b1111_1001;
parameter NUM_2 = 8'b1010_0100;
parameter NUM_3 = 8'b1011_0000;
parameter NUM_4 = 8'b1001_1001;
parameter NUM_5 = 8'b1001_0010;
parameter NUM_6 = 8'b1000_0010;
parameter NUM_7 = 8'b1111_1000;
parameter NUM_8 = 8'b1000_0000;
parameter NUM_9 = 8'b1001_0000;
parameter NUM_F = 8'b1011_1111;
parameter NUM_ERR = 8'b1000_0110;
input clk ;
input rst_n ;
input disp_en ;
input [SEG_NUM*4-1:0] din ;
input [SEG_NUM-1:0] din_vld ;
output [SEG_NUM-1:0] seg_sel ;
output [SEG_WID-1:0] segment ;
reg [SEG_NUM-1:0] seg_sel ;
reg [SEG_WID-1:0] segment ;
reg [COUNT_WID-1:0] cnt0 ;
wire add_cnt0 ;
wire end_cnt0 ;
reg [SEG_NUM-1:0] cnt1 ;
wire add_cnt1 ;
wire end_cnt1 ;
reg [4*SEG_NUM-1:0] din_ff0 ;
reg [ 4-1:0] seg_tmp ;
wire flag_20us ;
integer ii ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)
cnt0 <= 0;
else
cnt0 <= cnt0 + 1;
end
end
assign add_cnt0 = 1;
assign end_cnt0 = add_cnt0 && cnt0==TIME_20US-1 ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)
cnt1 <= 0;
else
cnt1 <= cnt1 + 1;
end
end
assign add_cnt1 = end_cnt0;
assign end_cnt1 = add_cnt1 && cnt1==SEG_NUM-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
seg_sel <= {SEG_NUM{1'b1}};
end
else if(disp_en)
seg_sel <= ~(1'b1 << cnt1);
else
seg_sel <= {SEG_NUM{1'b1}};
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_ff0 <= 0;
end
else begin
for(ii=0;ii<SEG_NUM;ii=ii+1)begin
if(din_vld[ii]==1'b1)begin
din_ff0[(ii+1)*4-1 -:4] <= din[(ii+1)*4-1 -:4];
end
else begin
din_ff0[(ii+1)*4-1 -:4] <= din_ff0[(ii+1)*4-1 -:4];
end
end
end
end
always @(*)begin
seg_tmp = din_ff0[(cnt1+1)*4-1 -:4];
end
always@(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
segment<=NUM_0;
end
else if(seg_tmp==0)begin
segment<=NUM_0;
end
else if(seg_tmp==1)begin
segment<=NUM_1;
end
else if(seg_tmp==2)begin
segment<=NUM_2;
end
else if(seg_tmp==3)begin
segment<=NUM_3;
end
else if(seg_tmp==4)begin
segment<=NUM_4;
end
else if(seg_tmp==5)begin
segment<=NUM_5;
end
else if(seg_tmp==6)begin
segment<=NUM_6;
end
else if(seg_tmp==7)begin
segment<=NUM_7;
end
else if(seg_tmp==8)begin
segment<=NUM_8;
end
else if(seg_tmp==9)begin
segment<=NUM_9;
end
else if(seg_tmp==4'hf)begin
segment<=NUM_F;
end
else begin
segment<=NUM_ERR;
end
end
endmodule
|
1.6 效果和总结
由于本系统涉及1秒、20分钟等时间节点,时间很长,所以在仿真时有一定的困难,为此我将系统中的时间节点全部乘上10^(-5),让所有状态提前到达,方便我们仿真验证。
由于乘上了10^(-5),所以原消抖10ms在仿真中为100ns;
系统中原1s取样转速rev在仿真中为10us;
原按时计费20分钟/元在仿真中为12ms/元;
在测试文件中转速rev>=3r/s的时间是50000000ns;
总路程distance=15/3*50000000/10000=25km;
由于在测试文件中按键消抖有一段时间且这一段时间在rev>=3r/s的时间内,所以系统实际对速度采样的次数会少一次,所以最终的总路程会少5m;
所以,按路程计费的总费用taxi_fare_1=5+(25 - 3)*2 – 0.005*2=48.99(元);
去除小数部分taxi_fare_1=48(元);
在测试文件中转速rev<3r/s的时间是24000000ns;
按时间计费的总费用taxi_fare_2=24000000/12000000=2(元);
所以总车费taxi_fare_1=taxi_fare_1+taxi_fare_2=50(元);
`define clk_period 20
module taxi_fare_tb(
);
reg clk ;
reg rst_n ;
reg d_w ;
reg [15:0] rev ;
reg key_in ;
wire [3-1:0] seg_sel ;
wire [8-1:0] segment ;
taxi_fare taxi_fare_0(
.clk (clk) ,
.rst_n (rst_n) ,
.d_w (d_w) ,
.rev (rev) ,
.key_in (key_in) ,
.seg_sel (seg_sel) ,
.segment (segment)
);
initial clk = 1;
always#(`clk_period/2) clk = ~clk;
initial begin
rst_n = 0;
#100;
rst_n = 1;
end
initial begin
d_w = 0;
#10000;
d_w = 1;
#200000000;
d_w = 0;
end
initial begin
rev = 0;
#10000;
rev = 15;
#50000000;
rev = 1;
#24000000;
rev = 0;
end
initial begin
key_in = 1;
#1000;
key_in = 0;
#10;
key_in = 1;
#10;
key_in = 0;
#10;
key_in = 1;
#10;
key_in = 0;
#9000;
key_in = 1;
#10;
key_in = 0;
#10;
key_in = 1;
#74000000;
key_in = 0;
#9000;
key_in = 1;
end
endmodule
|
由仿真结果可以看到seg_sel=110第1位数码管显示时segment=11000000、seg_sel=101第2位数码管显示时segment=10010010、seg_sel=011第3位数码管显示时segment=11000000,在数码管上分别对应数值为0、5、0,表示为50元。
仿真结果和理论结构都是50元,符合我们的预期,验证成功。
在这个设计中,使用明德杨的至简设计法,让我的思路非常清晰,逻辑非常严谨,虽然没有做到一遍成功,但在调试过程中我都比较快速的找到问题,并快速解决。对于学习FPGA的同学,我非常推荐使用明德杨至简设计法和明德杨模块进行学习和设计。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也欢迎大家在评论与我们进行讨论!