往 期:
- 【verilog实战】同步FIFO的设计与功能验证(附源码)
- 【Verilog实战】异步FIFO设计和功能验证(附源码)
- 【Verilog实战】UART通信协议,半双工通信方式(附源码)
- 【Verilog实战】SPI协议接口设计和功能验证(附源码)
- 【Verilog实战】AMBA 3 APB接口设计和功能验证(附源码)
- 【Verilog实战】AMBA AHB接口设计和功能验证(附源码)
- 【Verilog实战】AMBA AXI接口设计和功能验证(附源码)
- 【Verilog实战】UART2APB bridge 设计和功能验证(附源码)
- 【Verilog实战】AHB2APB bridge 设计和功能验证(附源码)
文章目录
一、Demand
设计一个地铁自助售票控制器,投币只能为1元、5元和10元;票价额为2—10元整数;具有找零功能。
二、Interface Description
Signal Name | Width | Direction | Description |
---|---|---|---|
clk | 1 | input | System clk signal |
reset | 1 | input | System reset signal |
sel_en | 1 | input | 触发选票程信号 |
fares | 4 | input | 票程总价 |
money1 | 1 | input | 投1块钱的脉冲信号 |
money5 | 1 | input | 投5块钱的脉冲信号 |
money10 | 1 | input | 投10块钱的脉冲信号 |
total | 4 | output | 已投入的总钱币数 |
ticket_out | 1 | output | 出票的脉冲信号 |
mout | 4 | output | 找钱的信号 |
三、FSM
地铁自助售票机非常适合使用状态机实现,这个模块采用一个状态机,分别为空闲状态(IDLE),选票程状态(SEL),投币状态(VOTE),出票状态(TICKET)和找零阶段(CHANGE)。其中在SEL状态,条件reset1是用户选错票程了,返回空闲状态。VOTE阶段,条件reset1是用户不想买票了,进行退钱,然后回到空闲状态。
四、Design and Functional Verification
(1)RTL
module subway
(
input clk_i ,
input reset ,
input sel_en ,
input [3:0] fares ,
input money1 ,
input money5 ,
input money10 ,
output reg [3:0] total ,
output reg ticket_out,
output reg [3:0] mout
);
parameter IDLE = 5'b00001;
parameter SEL = 5'b00010;
parameter VOTE = 5'b00100;
parameter TICKTE = 5'b01000;
parameter CHANGE = 5'b10000;
reg [4:0] cur_state;
reg [4:0] nxt_state;
reg [3:0] num1 ;
reg [1:0] num5 ;
reg num10;
reg [3:0] fares_buf;
always @ (reset or money1 or money5 or money10 or cur_state or nxt_state) begin
if (reset) begin
num1 = 5'b0;
num5 = 4'b0;
num10 = 1'b0;
end
else if (money1) begin
num1 = num1 + 1'b1;
end
else if(money5)begin
num5 = num5 + 1'b1;
end
else if(money10)begin
num10 = num10 + 1'b1;
end
else if(cur_state == IDLE && nxt_state == IDLE)begin
num1 = 5'b0;
num5 = 4'b0;
num10 = 1'b0;
end
else begin
num1 = num1;
num5 = num5;
num10 = num10;
end
end
//assign total = num1 + num5*5 + num10*10;
always @ (posedge clk_i) begin
if (reset) begin
cur_state <= IDLE;
end
else begin
cur_state <= nxt_state;
end
end
always @ (posedge clk_i) begin
if(reset)begin
nxt_state <= IDLE;
end
case(cur_state)
IDLE :if(sel_en && !reset)begin
nxt_state <= SEL;
end
SEL :begin
nxt_state <= VOTE;
end
VOTE :if((!reset) && (total < fares_buf) )begin
nxt_state <= VOTE;
end
else if((!reset) && (total >= fares_buf))begin
nxt_state <= TICKTE;
end
TICKTE:nxt_state <= CHANGE;
CHANGE:nxt_state <= IDLE;
//default:nxt_state = IDLE;
endcase
end
always @ (posedge clk_i) begin
if(reset)begin
fares_buf <= 4'b0;
total <= 4'b0;
ticket_out <= 1'b0;
mout <= 3'b0;
end
case(nxt_state)
IDLE :begin
fares_buf <= 4'b0;
total <= 4'b0;
ticket_out <= 1'b0;
mout <= 3'b0;
end
SEL :begin
fares_buf <= fares;
end
VOTE :begin
total <= num1 + num5*5 + num10*10;
end
TICKTE:begin
ticket_out <= 1'b1;
end
CHANGE:begin
mout <= total-fares_buf;
ticket_out <= 1'b0;
end
default:begin
total <= 4'b0;
ticket_out <= 1'b0;
mout <= 3'b0;
end
endcase
end
(2)Test Bench
module tb_subway;
reg clk_i;
reg reset;
reg sel_en;
reg [3:0] fares;
reg money1;
reg money5;
reg money10;
wire [3:0] total;
wire ticket_out;
wire mout;
initial begin
clk_i = 0;
reset = 1;
sel_en = 0;
fares = 4'b0;
money1 = 1'b0;
money5 = 1'b0;
money10 = 1'b0;
#15 reset = 0;
f2m1m1;
#130 f2m5;
#130 f2m10;
#130 f5m1m1m1m1m1;
#130 f5m5;
#130 f5m10;
#130 f10m5m5;
#130 f10m10;
end
task f2m1m1;
fares = 4'd2;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money1 = 1;
#10 money1 = 0;
#10 money1 = 1;
#10 money1 = 0;
endtask
task f2m5;
fares = 4'd2;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money5 = 1;
#10 money5 = 0;
endtask
task f2m10;
fares = 4'd2;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money10 = 1;
#10 money10 = 0;
endtask
task f5m1m1m1m1m1;
fares = 4'd5;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money1 = 1;
#10 money1 = 0;
#10 money1 = 1;
#10 money1 = 0;
#10 money1 = 1;
#10 money1 = 0;
#10 money1 = 1;
#10 money1 = 0;
#10 money1 = 1;
#10 money1 = 0;
endtask
task f5m5;
fares = 4'd5;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money5 = 1;
#10 money5 = 0;
endtask
task f5m10;
fares = 4'd5;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money10 = 1;
#10 money10 = 0;
endtask
task f10m5m5;
fares = 4'd10;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money5 = 1;
#10 money5 = 0;
#10 money5 = 1;
#10 money5 = 0;
endtask
task f10m10;
fares = 4'd10;
#30;
sel_en = 1;
#15 sel_en = 0;
#15 money10 = 1;
#10 money10 = 0;
endtask
initial begin
#2000 $finish;
end
always #10 clk_i = ~clk_i;
subway tb_subway(
.clk_i(clk_i),
.reset(reset),
.sel_en(sel_en),
.fares(fares),
.money1(money1),
.money5(money5),
.money10(money10),
.total(total),
.ticket_out(ticket_out),
.mout(mout)
);
initial begin
$fsdbDumpfile("subway.fsdb");
$fsdbDumpvars ;
$fsdbDumpMDA ;
end
endmodule
五、Result
结果:测试用例分别测试了
票价2元,投两次1元,找了0元;票价2元,投一次5元,找了3元;票价2元,投1次10元,找了8元;
票价5元,投5次1元,找了0元;票价5元,投一次5元,找了0元;票价5元,投一次10元,找了5元;
票价10元,投两次5元,找了0元;票价10元,投一次10元,找了0元。
结论:功能正常,但这个设计中,找钱功能是一次输出,这是不合要求的,起码是能找某几种货币,比如找钱3元,总不能找一张3元的,可以是找三个1元。还有就是没有考虑到用户可能投出1、5、10块面值的货币外的一个错误处理。
作者:xlinxdu
版权:本文是作者原创,版权归作者所有。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。