FPGA学习笔记——基于Verilog语言的两种流水灯LED

目录

实验要求

理论基础

①基于状态机的流水灯写法

②基于移位寄存器的流水灯写法

实验代码

①基于状态机的流水灯写法(三段式)

②基于移位寄存器的流水灯写法代码

实验结果(modelsim仿真结果)


实验要求

顶层模块名: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博客

Verilog基础之十一、移位寄存器实现 - 哔哩哔哩

什么是状态机?一篇文章就够了 - 知乎

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值