目录
实验要求
顶层模块名:run_led 路径lab1/run_led/src/run_led.v
CLK时钟为50MHz
端口信号定义如下:
信号名 | 输入/输出 | 含义 | 位宽 |
clk | input | 时钟 | 1 |
rst_n | input | 复位信号 | 1 |
sw | input | 拨码开关(模式控制) | 2 |
led | output | 流水灯 | 8 |
流水灯的显示要求(要求使用按键输入切换模式,即SW[1:0])
模式1(sw[1:0]=2’b00):从左至右循环点亮,间隔100us(0.1ms)。
10000000(复位状态)-----01000000-----00100000-----00010000-----00001000-----00000100-----00000010-----00000001-----10000000
模式2(sw[1:0]=2’b01):从右至左循环点亮,间隔100us(0.1ms)。
00000001(复位状态)
-----00000010-----00000100-----00001000-----00010000-----00100000-----01000000-----10000000-----00000001
模式3(sw[1:0]=2’b10):从左至右顺次点亮,之后再逐步熄灭,间隔100us(0.1ms)。
00000000(复位状态)-----10000000-----11000000-----11100000-----11110000-----11111000-----11111100-----11111110-----11111111-----01111111-----00111111-----00011111-----00001111-----00000111-----00000011-----00000001-----00000000
模式4(sw[1:0]=2’b11):从右至左顺次点亮,之后再逐步熄灭,间隔100us(0.1ms)。
00000000(复位状态)-----00000001-----00000011-----00000111-----00001111-----00011111-----00111111-----01111111-----11111111-----11111110-----11111100-----11111000-----11110000-----11100000-----11000000-----10000000-----00000000
理论基础
①基于状态机的流水灯写法
1.什么是状态机?
状态机一般指有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。
2.状态机有哪些类型?
- 若输出只和状态有关而与输入无关,则称为moore状态机 摩尔状态机
- 输出不仅和状态有关而且和输入有关系,则称为mealy状态机 米利状态机
3.状态机的写法有哪些?
分别为一段式、二段式和三段式
一段式写法:
只有一个always块,把所有的逻辑(输入、输出、状态)都在一个always块的时序逻辑中实现。
优点:写法简洁
缺点:这种写法看起来很简洁,但不利于维护,如果状态复杂一些容易出错。
二段式写法:
有两个always块,把时序逻辑和组合逻辑分隔开来。时序逻辑里进行当前状态和下一状态的切换,组合逻辑实现各个输入、输出以及状态判断。
优点:这种写法不仅便于阅读、理解、维护,而且利于综合器优化代码,利于用户添加合适的时序约束条件,利于布局布线器实现设计。
缺点:在两段式描述中,当前状态的输出用组合逻辑实现,可能存在竞争和冒险,产生毛刺。
三段式写法:
有三个always块,
一个采用同步时序的方式描述状态转移
一个采用组合逻辑的方式判断状态转移条件、描述状态转移规律
第三个always使用同步时序的方式描述每个状态的输出。
优点:代码容易维护,时序逻辑的输出解决了两段式组合逻辑的毛刺问题,
缺点:从资源消耗的角度上看,三段式的资源消耗多一些
②基于移位寄存器的流水灯写法
移位寄存器SRL可用于存储数据,实现串并转换
根据数据移动方向可分为左移寄存器,右移寄存器
左移是向数据高位移动,右移是向数据低位移动。
实验代码
①基于状态机的流水灯写法(三段式)
变量声明、输入和输出定义
module run_led(
input clk,
input rst_n,
input [1:0]sw,
output reg[7:0]led
);
localparam state_led0 = 0;//四种模式:模式一和二,有8种状态;模式三和四,有16种状态
localparam state_led1 = 1;
localparam state_led2 = 2;
localparam state_led3 = 3;
localparam state_led4 = 4;
localparam state_led5 = 5;
localparam state_led6 = 6;
localparam state_led7 = 7;
localparam state_led8 = 8;
localparam state_led9 = 9;
localparam state_led10 = 10;
localparam state_led11 = 11;
localparam state_led12 = 12;
localparam state_led13 = 13;
localparam state_led14 = 14;
localparam state_led15 = 15;
parameter T = 50000000; //period 时钟周期50MHz=20ns 50000000*20ns=1s
reg [3:0] cstate=0; //current state 现态
reg [3:0] nstate=0; //next state 次态
reg [26:0] count = 0; //counter number pay attention scale 计数器变量
reg [1:0] flag; //根据按键sw的数值,用来切换模式12和34的标志位
计数器块
always @(posedge clk or negedge rst_n)begin //counter 20ns count+1 计数器
flag<=sw; //sw标志位状态刷新
if(!rst_n) //复位 初始化
count<= 1'b0; //reset clear
else if(count === T-1)
count<= 1'b0; //maxmin clear
else
count<= count + 1'b1; //+1
end
采用同步时序的方式描述状态转移 :现态和次态
//first current-->next <= cstate state 时序逻辑
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cstate <= state_led0;
else
cstate <= nstate;
end
采用组合逻辑的方式判断状态转移条件、描述状态转移规律
//second cstate--> nstate<=state 组合逻辑
always@(*)begin //zusheshi
if(!rst_n) //复位,初始化
begin
nstate=state_led0;
end
else
case(cstate)
state_led0: begin
if(count == T - 1) //一个指定周期后进入下一个状态
nstate = state_led1;
else
nstate = state_led0;
end
state_led1: begin
if(count == T - 1)
nstate = state_led2;
else
nstate = state_led1;
end
state_led2: begin
if(count == T - 1)
nstate = state_led3;
else
nstate = state_led2;
end
state_led3: begin
if(count == T - 1)
nstate = state_led4;
else
nstate = state_led3;
end
state_led4: begin
if(count == T - 1)
nstate = state_led5;
else
nstate = state_led4;
end
state_led5: begin
if(count == T - 1)
nstate = state_led6;
else
nstate = state_led5;
end
state_led6: begin
if(count == T - 1)
nstate = state_led7;
else
nstate = state_led6;
end
state_led7: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led8;
else
nstate = state_led0;
else
nstate = state_led7;
end
state_led8: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led9;
else
nstate = state_led0;
else
nstate = state_led8;
end
state_led9: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11) //模式三和模式四
nstate = state_led10;
else
nstate = state_led0;
else
nstate = state_led9;
end
state_led10: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led11;
else
nstate = state_led0;
else
nstate = state_led10;
end
state_led11: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led12;
else
nstate = state_led0;
else
nstate = state_led11;
end
state_led12: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led13;
else
nstate = state_led0;
else
nstate = state_led12;
end
state_led13: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led14;
else
nstate = state_led0;
else
nstate = state_led13;
end
state_led14: begin
if(count == T - 1)
if(flag==2'b10 | flag==2'b11)
nstate = state_led15;
else
nstate = state_led0;
else
nstate = state_led14;
end
state_led15: begin
if(count == T - 1)
nstate = state_led0;
else
nstate = state_led15;
end
default: ;
endcase
end
使用同步时序的方式描述每个状态的输出
//thrith-----state-->output 根据实际状态输出
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
led <= 8'b00000000;
else
case(sw)
2'b00:
case(cstate)
state_led0: led <= 8'b10000000;
state_led1: led <= 8'b01000000;
state_led2: led <= 8'b00100000;
state_led3: led <= 8'b00010000;
state_led4: led <= 8'b00001000;
state_led5: led <= 8'b00000100;
state_led6: led <= 8'b00000010;
state_led7: led <= 8'b00000001;
default : ;
endcase
2'b01:
case(cstate)
state_led0: led <= 8'b00000001;
state_led1: led <= 8'b00000010;
state_led2: led <= 8'b00000100;
state_led3: led <= 8'b00001000;
state_led4: led <= 8'b00010000;
state_led5: led <= 8'b00100000;
state_led6: led <= 8'b01000000;
state_led7: led <= 8'b10000000;
default : ;
endcase
2'b10:
case(cstate)
state_led0: led <= 8'b00000000;
state_led1: led <= 8'b10000000;
state_led2: led <= 8'b11000000;
state_led3: led <= 8'b11100000;
state_led4: led <= 8'b11110000;
state_led5: led <= 8'b11111000;
state_led6: led <= 8'b11111100;
state_led7: led <= 8'b11111110;
state_led8: led <= 8'b11111111;
state_led9: led <= 8'b01111111;
state_led10: led <= 8'b00111111;
state_led11: led <= 8'b00011111;
state_led12: led <= 8'b00001111;
state_led13: led <= 8'b00000111;
state_led14: led <= 8'b00000011;
state_led15: led <= 8'b00000001;
default : ;
endcase
2'b11:
case(cstate)
state_led0: led <= 8'b00000000;
state_led1: led <= 8'b00000001;
state_led2: led <= 8'b00000011;
state_led3: led <= 8'b00000111;
state_led4: led <= 8'b00001111;
state_led5: led <= 8'b00011111;
state_led6: led <= 8'b00111111;
state_led7: led <= 8'b01111111;
state_led8: led <= 8'b11111111;
state_led9: led <= 8'b11111110;
state_led10: led <= 8'b11111100;
state_led11: led <= 8'b11111000;
state_led12: led <= 8'b11110000;
state_led13: led <= 8'b11100000;
state_led14: led <= 8'b11000000;
state_led15: led <= 8'b10000000;
default : ;
endcase
default : ;
endcase
end
endmodule
②基于移位寄存器的流水灯写法代码
变量声明、输入和输出定义
module run_led(
input clk,
input rst_n,
input [1:0] sw,
output logic[7:0] led
); //输入和输出定义
parameter T = 5_000; //计数周期 50MHz=20ns--> 50000000*20ns=1s 5000 0.1ms
reg [26:0] count = 0; //计数器计数变量
reg [7:0] Right_Shift; //模式一循环右移变量
reg [7:0] Left_Shift; //模式二循环左移变量
reg [7:0] Right_Shift1; //模式三循环右移变量 +置位
reg [7:0] Left_Shift1; //模式四循环左移变量 +置位
reg n1,n2; //set and reset 0/1 模式三和四中的循环移动中的置位变量
计数器块
always @(posedge clk or negedge rst_n)begin //counter 20ns count+1 计数器
if(!rst_n)
count<= 1'b0; //复位 初始化
else if(count === T-1)
count<= 1'b0; //达到指定周期后 清零
else
count<= count + 1'b1; //每个时钟周期+1
end
模式一和模式二
always@(posedge clk or negedge rst_n)begin //xunhuan right shift 模式一循环右移
if(!rst_n)begin
Right_Shift<=8'b10000000; //复位 初始化
end
else if(count === T-1)begin //达到周期后,最低位移向最高位(最高位赋值为最低位的值),同理
Right_Shift[7]<= Right_Shift[0];
Right_Shift[6:0]<= Right_Shift[7:1];
end
end
always@(posedge clk or negedge rst_n)begin //xunhuan left shift 模式二循环左移
if(!rst_n)begin
Left_Shift<=8'b00000001; //复位 初始化
end
else if(count === T-1)begin //达到周期后,最高位移向最低位(最低位赋值为最高位的值),同理
Left_Shift[0]<= Left_Shift[7];
Left_Shift[7:1]<= Left_Shift[6:0];
end
end
模式三和模式四
always@(posedge clk or negedge rst_n)begin //specail right shift 模式三循环右移加置位
if(!rst_n)begin
n1=1;
Right_Shift1<=8'b0; //复位 初始化
end
else if(count === T-1)begin
case(Right_Shift1) //全0和全1为该模式下的转折点
8'b11111111: n1=0; //全1时,在循环右移的情况下,置位为0时能够满足模式三的要求
8'b00000000: n1=1; //全0时,在循环右移的情况下,置位为1时能够满足模式三的要求
default : ;
endcase
Right_Shift1[7]<= n1; //同理模式一
Right_Shift1[6:0]<= Right_Shift1[7:1];
end
end
always@(posedge clk or negedge rst_n)begin //specail left shift 模式四循环左移加置位
if(!rst_n)begin
n2=1;
Left_Shift1<=8'b0; //复位 初始化
end
else if(count === T-1)begin
case(Left_Shift1) //全0和全1为该模式下的转折点
8'b11111111: n2=0; //全1时,在循环右移的情况下,置位为0时能够满足模式三的要求
8'b00000000: n2=1; //全0时,在循环右移的情况下,置位为1时能够满足模式三的要求
default : ;
endcase
Left_Shift1[0]<= n2; //同理模式二
Left_Shift1[7:1]<= Left_Shift1[6:0];
end
end
main
always@(posedge clk or negedge rst_n)begin //main 主菜单 按键切换四种模式
if(!rst_n)
led <= 8'b00000000; //复位 初始化
else
case(sw)
2'b00 :led<=Right_Shift ; //模式一
2'b01 :led<=Left_Shift ; //模式二
2'b10 :led<=Right_Shift1 ; //模式三
2'b11 :led<=Left_Shift1 ; //模式四
default : ;
endcase
end
endmodule
tb.sv——Modelsim测试文件
`timescale 1ns/1ns
`define clk_p 20.0
module tb;
logic clk,rst_n;
logic [7:0] led;
logic [1:0] sw;
initial begin
clk=1;
forever begin
#(`clk_p/2.0) clk=~clk;
end
end
run_led run_led_u0(
.clk(clk),
.rst_n(rst_n),
.sw(sw),
.led(led)
);
initial begin
rst_n=0;sw=2'b00;
#(1000000);
rst_n=1;
#(15*50000*`clk_p);
rst_n=0;sw=2'b01;
#(1000000);
rst_n=1;
#(15*50000*`clk_p);
rst_n=0;sw=2'b10;
#(1000000);
rst_n=1;
#(19*50000*`clk_p);
rst_n=0;sw=2'b11;
#(1000000);
rst_n=1;
#(19*50000*`clk_p);
$stop;
end
endmodule
实验结果(modelsim仿真结果)
时钟周期20ns 计数器5000次 100000ns=0.1ms
四种模式波形图如图所示
参考文献:
【FPGA入门二】状态机+LED流水灯_module diandeng( input clk, input rst_n, output re_机智的橙子的博客-CSDN博客