FPGA开发
led_run
module led_run(clk,rst_n,led);
input clk,rst_n;
output reg[3:0] led;
reg [1:0] state;
reg [25:0] cnt;
parameter T=5000_0000;//常量
always @ (posedge clk,negedge rst_n)//时钟上升沿 复位下降沿(复位低电平有效 那就是下降沿触发)
begin
if(!rst_n)
begin
state<=0;//<=非阻塞赋值用在时序逻辑电路里 //=阻塞赋值在组合逻辑电路中使用
cnt<=0;
led<=4'b1111;
end
else
case(state)
0 : begin
if(cnt<T-1)
begin
cnt<=cnt+1;
state<=0;
led<=4'b0111;
end
else
begin
cnt<=0;
state<=1;
led<=4'b1011;
end
end
1 : begin
if(cnt<T-1)
begin
cnt<=cnt+1;
state<=1;
led<=4'b1011;
end
else
begin
cnt<=0;
state<=2;
led<=4'b1101;
end
end
2 : begin
if(cnt<T-1)
begin
cnt<=cnt+1;
state<=2;
led<=4'b1101;
end
else
begin
cnt<=0;
state<=3;
led<=4'b1110;
end
end
3 : begin
if(cnt<T-1)
begin
cnt<=cnt+1;
state<=3;
led<=4'b1110;
end
else
begin
cnt<=0;
state<=0;
led<=4'b0111;
end
end
default : state<=0;
endcase
end
endmodule
led_run_state
Verilog HDL(硬件描述语言) 编写的 三段式状态机,用于控制 4 个 LED 灯 按照特定的顺序循环点亮(类似跑马灯效果)。
代码功能分析
1. 模块定义
module led_run_state3(clk, rst_n, led);
clk:时钟信号输入(用于同步状态切换)。rst_n:复位信号(低电平有效,0时复位)。led:输出 4 位寄存器,控制 4 个 LED 灯的亮灭(1灭,0亮)。
2. 状态机设计(三段式)
(1) 状态定义
reg [1:0] c_state, n_state; // 当前状态(c_state)和下一状态(n_state)
parameter T = 5000_0000; // 计时周期(控制LED切换速度)
c_state:当前状态(2 位,可表示 4 种状态0~3)。n_state:下一状态(由组合逻辑计算)。T:计数器阈值(5000_0000个时钟周期后切换状态)。
(2) 组合逻辑计算下一状态(第一段)
always @ (*) begin
if (!rst_n) n_state = 0; // 复位时回到状态0
else
case (c_state)
0: if (cnt == T-1) n_state = 1; else n_state = 0;
1: if (cnt == T-1) n_state = 2; else n_state = 1;
2: if (cnt == T-1) n_state = 3; else n_state = 2;
3: if (cnt == T-1) n_state = 0; else n_state = 3;
default: n_state = 0;
endcase
end
- 根据
c_state和计数器cnt决定n_state:- 如果
cnt计满T-1,则切换到下一状态(0→1→2→3→0循环)。 - 否则保持当前状态。
- 如果
(3) 时序逻辑更新当前状态(第二段)
always @ (posedge clk, negedge rst_n) begin
if (!rst_n) c_state <= 0; // 复位时状态归零
else c_state <= n_state; // 否则更新状态
end
- 在时钟上升沿或复位时更新
c_state。
3. 计数器(第三段)
always @ (posedge clk, negedge rst_n) begin
if (!rst_n) cnt <= 0;
else if (cnt < T-1) cnt <= cnt + 1;
else cnt <= 0;
end
- 每个时钟周期
cnt自增 1,直到T-1后归零,用于控制状态切换的时间间隔。
4. LED 输出逻辑
always @ (*) begin
if (!rst_n) led = 4'b1111; // 复位时全灭
else
case (c_state)
0: led = 4'b0111; // 第1个LED亮(低电平有效)
1: led = 4'b1011; // 第2个LED亮
2: led = 4'b1101; // 第3个LED亮
3: led = 4'b1110; // 第4个LED亮
default: led = 4'b1111;
endcase
end
- 根据
c_state控制 LED 的亮灭:0→0111(第1个亮)1→1011(第2个亮)2→1101(第3个亮)3→1110(第4个亮)- 复位时
1111(全灭)。
硬件行为
-
复位时(
rst_n=0):led = 4'b1111(所有 LED 熄灭)。c_state = 0,cnt = 0。
-
正常运行(
rst_n=1):- 每
5000_0000个时钟周期,状态切换一次(0→1→2→3→0)。 - LED 依次点亮,形成 “流水灯” 效果。
- 每
改进建议
n_state逻辑优化:- 可以用
n_state = (cnt == T-1) ? (c_state + 1) % 4 : c_state;简化代码。
- 可以用
led输出时序:- 建议用
always @ (posedge clk)避免组合逻辑的竞争冒险。
- 建议用
- 参数化
T:- 可通过模块参数传递
T,方便调整 LED 切换速度。
- 可通过模块参数传递
总结
这是一个典型的 “三段式状态机” 实现,通过 状态切换 + 计时器 控制 LED 循环点亮,适用于 FPGA 或 ASIC 设计中的硬件控制场景。
完整代码
module led_run_state3(clk,rst_n,led);//三段式状态机
input clk,rst_n;
output reg[3:0] led;
reg [25:0] cnt;
parameter T=5000_0000;//常量
reg [1:0] c_state,n_state;//三段式状态机
//用组合逻辑写
always @ (*)
begin
if(!rst_n)
n_state=0;
else
case(c_state)
0 : begin
if(cnt==T-1)
n_state=1;//记够跳
else
n_state=0;//否则不跳
end
1 : begin
if(cnt==T-1)
n_state=2;//记够跳
else
n_state=1;//否则不跳
end
2 : begin
if(cnt==T-1)
n_state=3;//记够跳
else
n_state=2;//否则不跳
end
3 : begin
if(cnt==T-1)
n_state=4;//记够跳
else
n_state=3;//否则不跳
end
default:n_state=0;
endcase
end
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
c_state<=0;
else
c_state<=n_state;
end
//计数器
always @ (posedge clk, negedge rst_n)
begin
if(!rst_n)
cnt<=0;
else
if(cnt<T-1)
cnt<=cnt+1;
else
cnt<=0;
end
always @ (*)
begin
if(!rst_n)
led=4'b1111;
else
case(c_state)
0 : led=4'b0111;
1 : led=4'b1011;
2 : led=4'b1101;
3 : led=4'b1110;
default : led=4'b1111;
endcase
end
endmodule
测试代码
`timescale 1ns/1ps
module led_run_tb;
reg clk,rst_n;
wire [3:0] led;
defparam dut.T=50;
led_run_state3 dut(
.clk(clk),
.rst_n(rst_n),
.led(led)
);
//写激励
initial begin
clk=0; rst_n=0;
#200.1 rst_n=1;
#20000 $stop;
end
always #10 clk=~clk;
endmodule
按键抖动
完整代码
module key_filter(clk,rst_n,key_in,key_out);
input clk,rst_n;
input key_in;
output reg key_out;
reg [18:0] cnt;
parameter T = 50_0000;
reg state;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)//复位
begin
state<=0;
cnt<=0;
key_out<=1;//没按下
end
else
case(state)
0 : begin
if(key_in==0)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
end
1 : begin
if(key_in==1)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
end
default:state<=0;
endcase
end
endmodule
测试代码
`timescale 1ns/1ps
module key_filter_tb;
reg clk,rst_n;
reg key_in;
wire key_out;
defparam dut.T=50;
key_filter dut(
.clk(clk),
.rst_n(rst_n),
.key_in(key_in),
.key_out(key_out)
);
initial begin
clk=0; rst_n=0;
key_in=1;
#200.1 rst_n=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#2000 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#2000 $stop;
end
always #10 clk =~clk;
endmodule
按键抖动(下午)
完整代码
module key_filter(clk,rst_n,key_in,nege_flag,pose_flag);
input clk,rst_n;
input key_in;
output nege_flag,pose_flag;
reg [18:0] cnt;
parameter T = 50_0000;
reg state;
reg key_out;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)//复位
begin
state<=0;
cnt<=0;
key_out<=1;//没按下
end
else
case(state)
0 : begin
if(key_in==0)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
end
1 : begin
if(key_in==1)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
end
default:state<=0;
endcase
end
reg key_buff1,key_buff2;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
key_buff1<=0;
key_buff2<=0;
end
else
begin
key_buff1<=key_out;
key_buff2<=key_buff1;
end
end
assign nege_flag=(~key_buff1)&&(key_buff2);
assign pose_flag=(~key_buff2)&&key_buff1;
endmodule
测试代码
`timescale 1ns/1ps
module key_filter_tb;
reg clk,rst_n;
reg key_in;
wire pose_flag,nege_flag;
defparam dut.T=50;
key_filter dut(
.clk(clk),
.rst_n(rst_n),
.key_in(key_in),
.pose_flag(pose_flag),
.nege_flag(nege_flag)
);
initial begin
clk=0; rst_n=0;
key_in=1;
#200.1 rst_n=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#2000 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#678 key_in=0;
#488 key_in=1;
#2000 $stop;
end
always #10 clk =~clk;
endmodule
实现
第一步:按键消抖(防抖)
- 问题:按键按下时会有抖动(比如10ms内
0-1-0-1乱跳),直接读会误判多次按下。 - 解决:
- 检测到按键状态变化(比如
1→0)时,先不立刻响应,而是:- 开始数数(计数器
cnt从0加到T-1,比如数满10ms)。 - 如果这期间按键状态一直稳定,才确认“真的按下了”。
- 开始数数(计数器
- 代码里用
state=0和state=1区分“等待按下”和“等待松开”两种状态。
- 检测到按键状态变化(比如
第二步:边沿检测(抓瞬间)
- 问题:消抖后的信号
key_out只告诉你当前是按下还是松开,但我们需要 按下/松开的瞬间。 - 解决:
- 用两个寄存器
key_buff1和key_buff2存key_out的历史值:key_buff1= 当前值key_buff2= 上一次的值
- 按下瞬间(
1→0):
nege_flag = (~key_buff1) && key_buff2(当前是0,上次是1)。 - 松开瞬间(
0→1):
pose_flag = key_buff1 && (~key_buff2)(当前是1,上次是0)。
- 用两个寄存器
7段数码管
完整代码
module seg7(clk,rst_n,/*data_in*/,sel,seg);
input clk,rst_n;
//input [23:0] data_in;
output reg[2:0] sel;
output reg[7:0] seg;
parameter data_in = 24'h789abc;
//分频器
reg [14:0] cnt;
reg clk_1ms;
parameter T=25000;//1毫秒/2微秒=50000 占空比50%
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
cnt<=0;
clk_1ms<=0;
end
else
if(cnt<T-1)
cnt<=cnt+1;
else
begin
cnt<=0;
clk_1ms<=~clk_1ms;
end
end
reg [3:0] temp;
reg [2:0] state;
always @ (posedge clk_1ms,negedge rst_n)
begin
if(!rst_n)
begin
sel<=3'b000;
state<=0;
temp<=0;
end
else
case(state)
0 : begin
sel<=3'b000;
temp<=data_in[23:20];
state<=1;
end
1 : begin
sel<=3'b001;
temp<=data_in[19:16];
state<=2;
end
2 : begin
sel<=3'b010;
temp<=data_in[15:12];
state<=3;
end
3 : begin
sel<=3'b011;
temp<=data_in[11:8];
state<=4;
end
4 : begin
sel<=3'b100;
temp<=data_in[7:4];
state<=5;
end
5 : begin
sel<=3'b101;
temp<=data_in[3:0];
state<=0;
end
default : state<=0;
endcase
end
always @ (*)
begin
if(!rst_n)
seg=8'b1111_1111;
else
case(temp)
0 : seg = 8'b1100_0000; // 显示数字 0
1 : seg = 8'b1111_1001; // 显示数字 1
2 : seg = 8'b1010_0100; // 显示数字 2
3 : seg = 8'b1011_0000; // 显示数字 3
4 : seg = 8'b1001_1001; // 显示数字 4
5 : seg = 8'b1001_0010; // 显示数字 5
6 : seg = 8'b1000_0010; // 显示数字 6
7 : seg = 8'b1111_1000; // 显示数字 7
8 : seg = 8'b1000_0000; // 显示数字 8
9 : seg = 8'b1001_0000; // 显示数字 9
// 字母 A-F
10 : seg = 8'b1000_1000; // A
11 : seg = 8'b1000_0011; // b
12 : seg = 8'b1100_0110; // C
13 : seg = 8'b1010_0001; // d
14 : seg = 8'b1000_0110; // E
15 : seg = 8'b1000_1110; // F
default : seg = 8'b1111_1111; // 全灭
endcase
end
endmodule
测试代码
计数器
完整代码
module seg7(clk,rst_n,data_in,sel,seg);
input clk,rst_n;
input [23:0] data_in;
output reg[2:0] sel;
output reg[7:0] seg;
//分频器
reg [14:0] cnt;
reg clk_1ms;
parameter T=25000;//1毫秒/2微秒=50000 占空比50%
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
cnt<=0;
clk_1ms<=0;
end
else
if(cnt<T-1)
cnt<=cnt+1;
else
begin
cnt<=0;
clk_1ms<=~clk_1ms;
end
end
reg [3:0] temp;
reg [2:0] state;
always @ (posedge clk_1ms,negedge rst_n)
begin
if(!rst_n)
begin
sel<=3'b000;
state<=0;
temp<=0;
end
else
case(state)
0 : begin
sel<=3'b000;
temp<=data_in[23:20];
state<=1;
end
1 : begin
sel<=3'b001;
temp<=data_in[19:16];
state<=2;
end
2 : begin
sel<=3'b010;
temp<=data_in[15:12];
state<=3;
end
3 : begin
sel<=3'b011;
temp<=data_in[11:8];
state<=4;
end
4 : begin
sel<=3'b100;
temp<=data_in[7:4];
state<=5;
end
5 : begin
sel<=3'b101;
temp<=data_in[3:0];
state<=0;
end
default : state<=0;
endcase
end
always @ (*)
begin
if(!rst_n)
seg=8'b1111_1111;
else
case(temp)
0 : seg = 8'b1100_0000; // 显示数字 0
1 : seg = 8'b1111_1001; // 显示数字 1
2 : seg = 8'b1010_0100; // 显示数字 2
3 : seg = 8'b1011_0000; // 显示数字 3
4 : seg = 8'b1001_1001; // 显示数字 4
5 : seg = 8'b1001_0010; // 显示数字 5
6 : seg = 8'b1000_0010; // 显示数字 6
7 : seg = 8'b1111_1000; // 显示数字 7
8 : seg = 8'b1000_0000; // 显示数字 8
9 : seg = 8'b1001_0000; // 显示数字 9
// 字母 A-F
10 : seg = 8'b1000_1000; // A
11 : seg = 8'b1000_0011; // b
12 : seg = 8'b1100_0110; // C
13 : seg = 8'b1010_0001; // d
14 : seg = 8'b1000_0110; // E
15 : seg = 8'b1000_1110; // F
default : seg = 8'b1111_1111; // 全灭
endcase
end
endmodule
module key_filter(clk,rst_n,key_in,nege_flag,pose_flag);
input clk,rst_n;
input key_in;
output nege_flag,pose_flag;
reg [18:0] cnt;
parameter T = 50_0000;
reg state;
reg key_out;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)//复位
begin
state<=0;
cnt<=0;
key_out<=1;//没按下
end
else
case(state)
0 : begin
if(key_in==0)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
end
1 : begin
if(key_in==1)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
end
default:state<=0;
endcase
end
reg key_buff1,key_buff2;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
key_buff1<=0;
key_buff2<=0;
end
else
begin
key_buff1<=key_out;
key_buff2<=key_buff1;
end
end
assign nege_flag=(~key_buff1)&&(key_buff2);
assign pose_flag=(~key_buff2)&&key_buff1;
endmodule
module key_num(clk,rst_n,add_flag,sub_flag,data);
input clk,rst_n;
input add_flag,sub_flag;
output reg[3:0] data;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
data <=0;
else
if(add_flag)
if(data<15)
data<=data+1;
else
data<=data;
else
if(sub_flag)
if(data>0)
data<=data-1;
else
data<=data;
else
data<=data;
end
endmodule
module key_num_top(clk,rst_n,key_add,key_sub,sel,seg);
input clk,rst_n;
input key_add,key_sub;
output [2:0] sel;
output [7:0] seg;
key_filter k1(
.clk(clk),
.rst_n(rst_n),
.key_in(key_add),
.nege_flag(add_flag),
.pose_flag()
);
key_filter k2(
.clk(clk),
.rst_n(rst_n),
.key_in(key_sub),
.nege_flag(sub_flag),
.pose_flag()
);
wire [3:0] data;
key_num k3(
.clk(clk),
.rst_n(rst_n),
.add_flag(add_flag),
.sub_flag(sub_flag),
.data(data)
);
seg7 s1(
.clk(clk),
.rst_n(rst_n),
.data_in({20'd0,data}),
.sel(sel),
.seg(seg)
);
endmodule
BCD码
完整代码
//module move(data_in,data_out);
// input [19:0] data_in;
// output [19:0] data_out;
// wire [19:0] d_reg;
//
// adjust a1(
// .d_in(data_in[19:16]),
// .d_out(d_reg[19:16])
// );
//
// adjust a2(
// .d_in(data_in[15:12]),
// .d_out(d_reg[15:12])
// );
//
// adjust a3(
// .d_in(data_in[11:8]),
// .d_out(d_reg[11:8])
// );
//
// assign d_reg[7:0]=data_in[7:0];
//
// assign data_out=d_reg<<1;
//
//endmodule
module move #(parameter BIN_WIDTH=10,
parameter BCD_WIDTH=16)(data_in,data_out);
input [BIN_WIDTH+BCD_WIDTH-1:0] data_in;
output [BIN_WIDTH+BCD_WIDTH-1:0] data_out;
wire [BIN_WIDTH+BCD_WIDTH-1:0] d_reg;
genvar i;
generate
for(i=0;i<BCD_WIDTH/4;i=i+1)
begin : adjust_inst
adjust a1(.d_in(data_in[BIN_WIDTH+BCD_WIDTH-1-i*4:BIN_WIDTH+BCD_WIDTH-4-i*4]),
.d_out(d_reg[BIN_WIDTH+BCD_WIDTH-1-i*4:BIN_WIDTH+BCD_WIDTH-4-i*4]));
end
endgenerate
assign d_reg[BIN_WIDTH-1:0]=data_in[BIN_WIDTH-1:0];
assign data_out =d_reg<<1;
endmodule
//module bin_bcd_v2 (bin,bcd);
// input [7:0] bin;
// output [11:0] bcd;
//
// wire [19:0] temp[8:0];
//
// assign temp[0]={12'd0,bin};
//
// move m1(.data_in(temp[0]),.data_out(temp[1]));
// move m2(.data_in(temp[1]),.data_out(temp[2]));
// move m3(.data_in(temp[2]),.data_out(temp[3]));
// move m4(.data_in(temp[3]),.data_out(temp[4]));
// move m5(.data_in(temp[4]),.data_out(temp[5]));
// move m6(.data_in(temp[5]),.data_out(temp[6]));
// move m7(.data_in(temp[6]),.data_out(temp[7]));
// move m8(.data_in(temp[7]),.data_out(temp[8]));
//
// assign bcd=temp[8][19:8];
//
//endmodule
//顶层
module bin_bcd_v2 # (parameter BIN_WIDTH=10,
parameter BCD_WIDTH=16)(bin,bcd);
input [BIN_WIDTH-1:0] bin;
output [BCD_WIDTH-1:0] bcd;
wire [BIN_WIDTH+BCD_WIDTH-1:0] temp [BIN_WIDTH:0];
assign temp[0]={{BCD_WIDTH{1'b0}},bin};
genvar i;
generate
for(i=0;i<BIN_WIDTH;i=i+1)
begin:move_inst
move #(.BIN_WIDTH(BIN_WIDTH),
.BCD_WIDTH(BCD_WIDTH)) m1(.data_in(temp[i]),.data_out(temp[i+1]));
end
endgenerate
assign bcd=temp[BIN_WIDTH][BIN_WIDTH+BCD_WIDTH-1:BIN_WIDTH];
endmodule
module adjust (d_in,d_out);
input [3:0] d_in;
output [3:0] d_out;
assign d_out=(d_in>4)?d_in+3:d_in;
endmodule
//bcd版本一
module bin_bcd(bin,bcd);
input [7:0] bin;
output [11:0] bcd;
assign bcd[11:8]=bin/100;
assign bcd[7:4]=(bin/10)%10;
assign bcd[3:0]=bin%10;
endmodule
测试代码
`timescale 1ns/1ps
module bin_bcd_tb;
reg [9:0] bin;
wire [15:0] bcd;
bin_bcd_v2 dut(
.bin(bin),
.bcd(bcd)
);
initial begin
bin=0;
#20
repeat(10)
begin
bin=($random)%1024;
#20;
end
#20;
end
endmodule
BCD码的Keynum计数+第四个小数点点亮
完整代码
//module move(data_in,data_out);
// input [19:0] data_in;
// output [19:0] data_out;
// wire [19:0] d_reg;
//
// adjust a1(
// .d_in(data_in[19:16]),
// .d_out(d_reg[19:16])
// );
//
// adjust a2(
// .d_in(data_in[15:12]),
// .d_out(d_reg[15:12])
// );
//
// adjust a3(
// .d_in(data_in[11:8]),
// .d_out(d_reg[11:8])
// );
//
// assign d_reg[7:0]=data_in[7:0];
//
// assign data_out=d_reg<<1;
//
//endmodule
module move #(parameter BIN_WIDTH=10,
parameter BCD_WIDTH=16)(data_in,data_out);
input [BIN_WIDTH+BCD_WIDTH-1:0] data_in;
output [BIN_WIDTH+BCD_WIDTH-1:0] data_out;
wire [BIN_WIDTH+BCD_WIDTH-1:0] d_reg;
genvar i;
generate
for(i=0;i<BCD_WIDTH/4;i=i+1)
begin : adjust_inst
adjust a1(.d_in(data_in[BIN_WIDTH+BCD_WIDTH-1-i*4:BIN_WIDTH+BCD_WIDTH-4-i*4]),
.d_out(d_reg[BIN_WIDTH+BCD_WIDTH-1-i*4:BIN_WIDTH+BCD_WIDTH-4-i*4]));
end
endgenerate
assign d_reg[BIN_WIDTH-1:0]=data_in[BIN_WIDTH-1:0];
assign data_out =d_reg<<1;
endmodule
//module bin_bcd_v2 (bin,bcd);
// input [7:0] bin;
// output [11:0] bcd;
//
// wire [19:0] temp[8:0];
//
// assign temp[0]={12'd0,bin};
//
// move m1(.data_in(temp[0]),.data_out(temp[1]));
// move m2(.data_in(temp[1]),.data_out(temp[2]));
// move m3(.data_in(temp[2]),.data_out(temp[3]));
// move m4(.data_in(temp[3]),.data_out(temp[4]));
// move m5(.data_in(temp[4]),.data_out(temp[5]));
// move m6(.data_in(temp[5]),.data_out(temp[6]));
// move m7(.data_in(temp[6]),.data_out(temp[7]));
// move m8(.data_in(temp[7]),.data_out(temp[8]));
//
// assign bcd=temp[8][19:8];
//
//endmodule
module bin_bcd_v2 # (parameter BIN_WIDTH=10,
parameter BCD_WIDTH=16)(bin,bcd);
input [BIN_WIDTH-1:0] bin;
output [BCD_WIDTH-1:0] bcd;
wire [BIN_WIDTH+BCD_WIDTH-1:0] temp [BIN_WIDTH:0];
assign temp[0]={{BCD_WIDTH{1'b0}},bin};
genvar i;
generate
for(i=0;i<BIN_WIDTH;i=i+1)
begin:move_inst
move #(.BIN_WIDTH(BIN_WIDTH),
.BCD_WIDTH(BCD_WIDTH)) m1(.data_in(temp[i]),.data_out(temp[i+1]));
end
endgenerate
assign bcd=temp[BIN_WIDTH][BIN_WIDTH+BCD_WIDTH-1:BIN_WIDTH];
endmodule
module adjust (d_in,d_out);
input [3:0] d_in;
output [3:0] d_out;
assign d_out=(d_in>4)?d_in+3:d_in;
endmodule
module seg7(clk,rst_n,data_in,sel,seg,dp);
input clk,rst_n;
input [23:0] data_in;
input [5:0] dp;
output reg[2:0] sel;
output reg[7:0] seg;
//分频器
reg [14:0] cnt;
reg clk_1ms;
parameter T=25000;//1毫秒/2微秒=50000 占空比50%
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
cnt<=0;
clk_1ms<=0;
end
else
if(cnt<T-1)
cnt<=cnt+1;
else
begin
cnt<=0;
clk_1ms<=~clk_1ms;
end
end
reg [3:0] temp;
reg [2:0] state;
always @ (posedge clk_1ms,negedge rst_n)
begin
if(!rst_n)
begin
sel<=3'b000;
state<=0;
temp<=0;
end
else
case(state)
0 : begin
sel<=3'b000;
temp<=data_in[23:20];
state<=1;
end
1 : begin
sel<=3'b001;
temp<=data_in[19:16];
state<=2;
end
2 : begin
sel<=3'b010;
temp<=data_in[15:12];
state<=3;
end
3 : begin
sel<=3'b011;
temp<=data_in[11:8];
state<=4;
end
4 : begin
sel<=3'b100;
temp<=data_in[7:4];
state<=5;
end
5 : begin
sel<=3'b101;
temp<=data_in[3:0];
state<=0;
end
default : state<=0;
endcase
end
always @ (*)
begin
if(!rst_n)
seg[6:0]=7'b111_1111;
else
case(temp)
0 : seg[6:0] = 7'b100_0000; // 显示数字 0
1 : seg[6:0] = 7'b111_1001; // 显示数字 1
2 : seg[6:0] = 7'b010_0100; // 显示数字 2
3 : seg[6:0] = 7'b011_0000; // 显示数字 3
4 : seg[6:0] = 7'b001_1001; // 显示数字 4
5 : seg[6:0] = 7'b001_0010; // 显示数字 5
6 : seg[6:0] = 7'b000_0010; // 显示数字 6
7 : seg[6:0] = 7'b111_1000; // 显示数字 7
8 : seg[6:0] = 7'b000_0000; // 显示数字 8
9 : seg[6:0] = 7'b001_0000; // 显示数字 9
// 字母 A-F
10 : seg[6:0] = 7'b000_1000; // A
11 : seg[6:0] = 7'b000_0011; // b
12 : seg[6:0] = 7'b100_0110; // C
13 : seg[6:0] = 7'b010_0001; // d
14 : seg[6:0] = 7'b000_0110; // E
15 : seg[6:0] = 7'b000_1110; // F
default : seg[6:0] = 7'b111_1111; // 全灭
endcase
end
always @ (*)
begin
if(!rst_n)
seg[7]=1'b1;
else
case(sel)
0 : seg[7]=dp[5];
1 : seg[7]=dp[4];
2 : seg[7]=dp[3];
3 : seg[7]=dp[2];
4 : seg[7]=dp[1];
6 : seg[7]=dp[0];
default : seg[7]=1'b1;
endcase
end
endmodule
module key_filter(clk,rst_n,key_in,nege_flag,pose_flag);
input clk,rst_n;
input key_in;
output nege_flag,pose_flag;
reg [18:0] cnt;
parameter T = 50_0000;
reg state;
reg key_out;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)//复位
begin
state<=0;
cnt<=0;
key_out<=1;//没按下
end
else
case(state)
0 : begin
if(key_in==0)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
end
1 : begin
if(key_in==1)
if(cnt<T-1)
begin
cnt<=cnt+1;
key_out<=0;
state<=1;
end
else
begin
cnt<=0;
key_out<=1;
state<=0;
end
else
begin
cnt<=0;
key_out<=0;
state<=1;
end
end
default:state<=0;
endcase
end
reg key_buff1,key_buff2;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
key_buff1<=0;
key_buff2<=0;
end
else
begin
key_buff1<=key_out;
key_buff2<=key_buff1;
end
end
assign nege_flag=(~key_buff1)&&(key_buff2);
assign pose_flag=(~key_buff2)&&key_buff1;
endmodule
module key_num(clk,rst_n,add_flag,sub_flag,data);
input clk,rst_n;
input add_flag,sub_flag;
output reg[3:0] data;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
data <=0;
else
if(add_flag)
if(data<15)
data<=data+1;
else
data<=data;
else
if(sub_flag)
if(data>0)
data<=data-1;
else
data<=data;
else
data<=data;
end
endmodule
module key_num_top(clk,rst_n,key_add,key_sub,sel,seg);
input clk,rst_n;
input key_add,key_sub;
output [2:0] sel;
output [7:0] seg;
key_filter k1(
.clk(clk),
.rst_n(rst_n),
.key_in(key_add),
.nege_flag(add_flag),
.pose_flag()
);
key_filter k2(
.clk(clk),
.rst_n(rst_n),
.key_in(key_sub),
.nege_flag(sub_flag),
.pose_flag()
);
wire [3:0] data;
wire [7:0] data_bcd;
key_num k3(
.clk(clk),
.rst_n(rst_n),
.add_flag(add_flag),
.sub_flag(sub_flag),
.data(data)
);
seg7 s1(
.clk(clk),
.rst_n(rst_n),
.data_in({16'd0,data_bcd}),
.sel(sel),
.seg(seg),
.dp(6'b111011)
);
bin_bcd_v2 # (.BIN_WIDTH(4),
.BCD_WIDTH(8)) b1(.bin(data),.bcd(data_bcd));
endmodule
呼吸灯
原理:逐渐点亮2s,逐渐熄灭2s。使用PWM脉冲宽度调制:将两秒时间分为1000份,每份2毫秒,两毫米再分成一千份,2微秒,两微秒直接使用系统时钟进行计数,最终将两毫秒的计数与两秒的计数进行判断,如果两豪秒计数器小于两秒计数器,就让led点亮。
一个灯的电亮和熄灭
module led_breath (clk,rst_n,led);
input clk,rst_n;
output led;
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms,cnt_2s;
parameter T2us=100;
parameter T2ms=1000;
parameter T2s=1000;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt_2us<=0;
else
if(cnt_2us<T2us-1)
cnt_2us<=cnt_2us+1;
else
cnt_2us<=0;
end
wire flag_2us;
assign flag_2us=(cnt_2us==T2us-1)?1'b1:1'b0;
//2ms计数器
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt_2ms<=0;
else
if(flag_2us)
if(cnt_2ms<T2ms-1)
cnt_2ms<=cnt_2ms+1;
else
cnt_2ms<=0;
else
cnt_2ms<=cnt_2ms;
end
wire flag_2ms;
assign flag_2ms=(cnt_2ms==T2ms-1&&flag_2us)?1'b1:1'b0;
//2s计数器
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt_2s<=0;
else
if(flag_2ms)
if(cnt_2s<T2s-1)
cnt_2s<=cnt_2s+1;
else
cnt_2s<=0;
else
cnt_2s<=cnt_2s;
end
wire flag_2s;
assign flag_2s=(cnt_2s==T2s-1&&flag_2ms)?1'b1:1'b0;
reg led_change;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
led_change<=0;
else
if(flag_2s)
led_change<=~led_change;
else
led_change<=led_change;
end
wire led_signal;
assign led_signal = (cnt_2ms<cnt_2s)?1'b0:1'b1;
assign led=(led_change==0)?led_signal:(~led_signal);
endmodule
四个灯循环点亮
module led_breath_top(clk,rst_n,led);
input clk,rst_n;
output [3:0] led;
wire [3:0] en;
led_ctrl l1(
.clk(clk),
.rst_n(rst_n),
.en(en)
);
led_breath l2(
.clk(clk),
.rst_n(en[3]),
.led(led[3])
);
led_breath l3(
.clk(clk),
.rst_n(en[2]),
.led(led[2])
);
led_breath l4(
.clk(clk),
.rst_n(en[1]),
.led(led[1])
);
led_breath l5(
.clk(clk),
.rst_n(en[0]),
.led(led[0])
);
endmodule
module led_ctrl(clk,rst_n,en);
input clk,rst_n;
output reg[3:0] en;
reg[28:0] cnt;
parameter T1=5000_0000;
parameter T2=10000_0000;
parameter T3=15000_0000;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt<=0;
else
if(cnt<T3-1)
cnt<=cnt+1;
else
cnt<=cnt;
end
always @ (*)
begin
if(!rst_n)
en<=4'b0000;
else
if(cnt>=T3-1)
en<=4'b1111;
else
if(cnt>=T2-1)
en<=4'b1110;
else
if(cnt>=T1-1)
en<=4'b1100;
else
en<=4'b1000;
end
endmodule
module led_breath (clk,rst_n,led);
input clk,rst_n;
output led;
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms,cnt_2s;
parameter T2us=100;
parameter T2ms=1000;
parameter T2s=1000;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt_2us<=0;
else
if(cnt_2us<T2us-1)
cnt_2us<=cnt_2us+1;
else
cnt_2us<=0;
end
wire flag_2us;
assign flag_2us=(cnt_2us==T2us-1)?1'b1:1'b0;
//2ms计数器
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt_2ms<=0;
else
if(flag_2us)
if(cnt_2ms<T2ms-1)
cnt_2ms<=cnt_2ms+1;
else
cnt_2ms<=0;
else
cnt_2ms<=cnt_2ms;
end
wire flag_2ms;
assign flag_2ms=(cnt_2ms==T2ms-1&&flag_2us)?1'b1:1'b0;
//2s计数器
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
cnt_2s<=0;
else
if(flag_2ms)
if(cnt_2s<T2s-1)
cnt_2s<=cnt_2s+1;
else
cnt_2s<=0;
else
cnt_2s<=cnt_2s;
end
wire flag_2s;
assign flag_2s=(cnt_2s==T2s-1&&flag_2ms)?1'b1:1'b0;
reg led_change;
always @ (posedge clk,negedge rst_n)
begin
if(!rst_n)
led_change<=0;
else
if(flag_2s)
led_change<=~led_change;
else
led_change<=led_change;
end
wire led_signal;
assign led_signal = (cnt_2ms<cnt_2s)?1'b0:1'b1;
assign led=(led_change==0)?led_signal:(~led_signal);
endmodule
自动售货机
模块功能
输入信号
clk:时钟信号(同步状态转换)rst_n:低电平复位信号(复位状态机和输出)half:投入0.5元(脉冲信号)one:投入1元(脉冲信号)
输出信号
goods:出货信号(高电平有效)change:找零信号(高电平有效)
核心逻辑
- 通过 6个状态 跟踪累计金额,假设商品价格为 2.5元(从状态转移推断)。
- 达到目标金额后:
- 状态5:出货不找零(累计刚好2.5元)。
- 状态6:出货并找零(累计超过2.5元)。
状态机详解
| 状态 | 累计金额 | 可能的下一状态转移 | 行为 |
|---|---|---|---|
| 0 | 0元 | 投0.5元→1;投1元→2 | 初始化 |
| 1 | 0.5元 | 投0.5元→2;投1元→3 | 累计中 |
| 2 | 1元 | 投0.5元→3;投1元→4 | 累计中 |
| 3 | 1.5元 | 投0.5元→4;投1元→6 | 累计中 |
| 4 | 2元 | 投0.5元→5;投1元→6 | 接近目标金额 |
| 5 | 2.5元 | 返回状态0,goods=1 |
出货不找零 |
| 6 | 3元 | 返回状态0,goods=1,change=1 |
出货并找零0.5元 |
完整代码
module auto_sell (clk,rst_n,half,one,change,goods);
input clk,rst_n;
input half,one;
output reg change;
output reg goods;
reg[3:0] state;
always @(posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
state<=0;
change<=0;
goods<=0;
end
else
case(state)
0: begin
if(half)
begin
state<=1;
goods<=0;
change<=0;
end
else
if(one)
begin
state<=2;
goods<=0;
change<=0;
end
else
begin
state<=0;
goods<=0;
change<=0;
end
end
1: begin
if(half)
begin
state<=2;
end
else
if(one)
begin
state<=3;
end
else
begin
state<=1;
end
end
2: begin
if(half)
begin
state<=3;
end
else
if(one)
begin
state<=4;
end
else
begin
state<=2;
end
end
3: begin
if(half)
begin
state<=4;
end
else
if(one)
begin
state<=6;
end
else
begin
state<=3;
end
end
4: begin
if(half)
begin
state<=5;
end
else
if(one)
begin
state<=6;
end
else
begin
state<=4;
end
end
5: begin
state<=0;
goods<=1;
change<=0;
end
6: begin
state<=0;
goods<=1;
change<=1;
end
default :state<=0;
endcase
end
endmodule
测试代码
`timescale 1ns/1ps
module auto_sell_tb;
reg clk, rst_n;
reg half, one;
wire change;
wire goods;
auto_sell dut(
.clk(clk),
.rst_n(rst_n),
.half(half),
.one(one),
.change(change),
.goods(goods)
);
initial begin
clk = 0; rst_n = 0;
one = 0; half = 0;
#200.1 rst_n = 1;
#200 half = 1;
#20 half = 0;
#50 one = 1;
#20 one = 0;
#100 half = 1;
#20 half = 0;
#100 one = 1;
#20 one = 0;
#5000 $stop;
end
always #10 clk = ~clk;
endmodule

浙公网安备 33010602011771号