FPGA毕业实习学习记录

该文介绍了Verilog的基本语法,通过3-8译码器、流水灯、按键控制LED灯和呼吸灯等项目展示了Verilog在数字电路设计中的应用。此外,还讲解了数码管静态显示,包括位选和段选信号的控制,以及如何实现静态显示不同数字和字母的方法。
摘要由CSDN通过智能技术生成

一、verilog语法基础

1.基本语法
  • Verilog程序由模块(module)组成,每个模块描述了电路中的一个功能单元。我们将从最简单的模块开始学习,逐渐深入了解Verilog的不同特性。
  • 在Verilog中,语句以分号(;)结尾,模块由关键字module开始,后面跟着模块的名称和用括号括起来的端口列表。例如,下面是一个简单的Verilog模块的示例:
module AndGate(input a, b, output y);
    assign y = a & b;  // 逻辑与门
endmodule
  • 在上面的示例中,模块名为AndGate,有两个输入端口a和b,一个输出端口y。使用assign关键字,我将y连接到了a和b的逻辑与操作(&)的结果。
  • 在Verilog中,我也可以使用always块来描述组合逻辑和时序逻辑。组合逻辑是根据当前输入立即计算得出的逻辑,而时序逻辑则依赖于时钟信号和存储元件。下面是一个使用always块描述的时序逻辑的示例:
module Counter(input clk, output reg [3:0] count);
    always @(posedge clk) begin
        if (reset) begin
            count <= 0;  // 复位
        end else begin
            count <= count + 1;  // 计数
        end
    end
endmodule
  • 在上面的示例中,模块名为Counter,有一个时钟输入端口clk和一个四位的寄存器型输出端口count。通过always块和posedge关键字,我定义了在时钟上升沿触发时执行的行为。当reset信号为1时,计数器复位为0;否则,计数器每次递增1。
  • Verilog还提供了一些用于组合逻辑操作的内置运算符,如逻辑与(&)、逻辑或(|)、逻辑非(!)和异或(^)等。此外,我还可以使用if语句、for循环等结构来实现更复杂的逻辑。
2.实现简单项目

3-8译码器

module decoder(	//3-8译码器
	input wire[2:0] a,//三位输入
	
	output reg[7:0] b//八位输出
);

always @(*) begin//对寄存器类型进行赋值
	case(a)
		3'b000: b = 8'b1111_1110;
		3'b001: b = 8'b1111_1101;
		3'b010: b = 8'b1111_1011;
		3'b011: b = 8'b1111_0111;
		3'b100: b = 8'b1110_1111;
		3'b101: b = 8'b1101_1111;
		3'b110: b = 8'b1011_1111;
		3'b111: b = 8'b0111_1111;
		default:b = 8'b0000_0000;
	endcase
end
endmodule

二、LED灯

1.流水灯

流水灯的效果。

模块描述
  • 输入信号分别为时钟信号和复位信号,设计一个计时器。每0.2s改变四个led的状态,同一时刻下只能有一只led亮,其余的led灭。最后通过移位寄存器输出信号给四个led灯。
模块设计
module led (
    input       wire        clk     ,
    input       wire        rst_n   ,

    output      reg [3:0]   led     
);
parameter MAX_NUM = 26'd49_999_999;
reg [25:0]  cnt     ;
reg [1:0 ]  state   ;//保存四个状态

//1s计数器并亮灭led灯
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt <= 26'd0;
    end
    else if(cnt == MAX_NUM)begin
        cnt <= 26'd0;
    end
    else begin
        cnt <= cnt + 1'd1;
    end
end
//状态的切换
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        state <= 2'd0;
    end
    else if(cnt == MAX_NUM)begin
        state <= state + 1'd1;
    end
    else begin
        state <= state;
    end
end
//根据状态对led赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        led <= 4'b0000;
    end
    else begin
        case (state)
            2'd0    : led <= 4'b0001;
            2'd1    : led <= 4'b0010;
            2'd2    : led <= 4'b0100;
            2'd3    : led <= 4'b1000;
            default : led <= 4'b0000;
        endcase
    end
end
endmodule
仿真文件
`timescale 1ns/1ns  //单位精度
module led_tb ();
    
    reg         clk     ;   //50MHz时钟信号
    reg         rst_n   ;   //复位信号

    wire [3:0]  led     ;
    parameter MAX_NUM = 4'd9;
    always #10 clk = ~clk;

    initial begin
        clk     = 1'b0;
        rst_n   = 1'b0;//开始复位
        #10;
        rst_n   = 1'b1;//结束复位
        #1000;
        $stop;//停止仿真
    end

    led #(.MAX_NUM (MAX_NUM))u_led(
        .clk    (clk),
        .rst_n  (rst_n),

        .led    (led)
    );

endmodule
2.按键控制led灯
模块描述
  • 利用按键控制led的变化,使用开发板上的四个按键控制四个LED灯,按下不同的按键时,四个LED灯显示不同效果。当没有按键按下时,四个led灯全灭;按下key0时,自右向左的流水灯;按下key1时,自左向右的流水灯;按下key2时,四个led灯同时闪烁;
    按下key3时,四个led灯全亮。
模块设计
module key_led (
    input   wire        clk     ,
    input   wire        rst_n   ,
    input   wire [3:0]  key     ,

    output  reg  [3:0]  led
);
    
parameter MAX_NUM = 24'd9_999_999;
reg [1:0] state;//保存当前状态
reg [23:0] cnt;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt <= 24'd0;
    end
    else if(cnt == MAX_NUM)begin
        cnt <= 24'd0;
    end
    else begin
        cnt <= cnt + 1'd1;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        state <= 2'd0;
    end
    else if(cnt == MAX_NUM)begin
        state <= state + 2'd1;
    end
    else begin
        state <= state;
    end
end

//结合按键判断led的赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        led <= 4'b0000;
    end
    else if(key[0] == 0)begin
        case (state)
            2'd0    : led <= 4'b0001;
            2'd1    : led <= 4'b0010;
            2'd2    : led <= 4'b0100;
            2'd3    : led <= 4'b1000;
            default : led <= 4'b0000;
        endcase
    end
    else if(key[1] == 0)begin
        case (state)
            2'd0    : led <= 4'b1000;
            2'd1    : led <= 4'b0100;
            2'd2    : led <= 4'b0010;
            2'd3    : led <= 4'b0001;
            default : led <= 4'b0000;
        endcase
    end
    else if(key[2] == 0)begin
        case (state)
            2'd0    : led <= 4'b1111;
            2'd1    : led <= 4'b0000;
            2'd2    : led <= 4'b1111;
            2'd3    : led <= 4'b0000;
            default : led <= 4'b0000;
        endcase
    end
    else if(key[3] == 0)begin
        led <= 4'b1111;
    end
end
endmodule
仿真文件
`timescale 1ns/1ns //单位精度
module key_led_tb ();

    reg clk;
    reg rst_n;
    reg [3:0] key;

    wire [3:0] led;

    parameter MAX_NUM   = 9 ;
    parameter CYCLE     = 20;

    always #(CYCLE/2) clk = ~clk;

    initial begin
        clk     = 1'b0;
        rst_n   = 1'b0;//开始复位
        #(CYCLE);
        rst_n   = 1'b1;//结束复位
        key = 4'b1111;//没有按下按键
        #(4*(MAX_NUM + 1)*CYCLE);
        key = 4'b1110;//按下第一个按键
        #(4*(MAX_NUM + 1)*CYCLE);
        key = 4'b1101;//按下第二个按键
        #(4*(MAX_NUM + 1)*CYCLE);
        key = 4'b1011;//按下第三个按键
        #(4*(MAX_NUM + 1)*CYCLE);
        key = 4'b0111;//按下第四个按键
        #(4*(MAX_NUM + 1)*CYCLE);
        $stop;
    end

    key_led #(.MAX_NUM(MAX_NUM)) u_key_led (
        .clk    (clk)   ,
        .rst_n  (rst_n) ,
        .key    (key)   ,  

        .led    (led)
    );

endmodule
3.呼吸灯

呼吸灯是指灯光在微电脑的控制之下完成由亮到暗的逐渐变化,感觉好像是人在呼吸。其广泛应用于手机之上,并成为各大品牌新款手机的卖点之一,起到一个通知提醒的作用。

模块描述

使用开发板上的四个led灯实现1s间隔的呼吸灯。

模块设计
module breath_led (
    input clk,
    input rst_n,

    output reg [3:0] led
);

parameter TIME_US = 6'd49;
parameter TIME_MS = 10'd999;
parameter TIME_S  = 10'd999;

reg [5:0] cnt_us;
reg [9:0] cnt_ms;
reg [9:0] cnt_s;
reg flag;

wire add_cnt_us;//微秒计数器开始计数的标志
wire end_cnt_us;//微秒计数器结束计数的标志

wire add_cnt_ms;//毫秒计数器开始计数的标志
wire end_cnt_ms;//毫秒计数器结束计数的标志

wire add_cnt_s;//秒计数器开始计数的标志
wire end_cnt_s;//秒计数器结束计数的标志

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_us <= 6'd0;
    end
    else if(add_cnt_us)begin
        if(end_cnt_us)begin
            cnt_us <= 6'd0;
        end
        else begin
            cnt_us <= cnt_us +1'd1;
        end
    end
    else begin
        cnt_us <= cnt_us;
    end
end
assign add_cnt_us = 1'b1;                           //开始计数的条件
assign end_cnt_us = add_cnt_us && cnt_us == TIME_US;//结束计数的条件

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_ms <= 10'd0;
    end 
    else if(add_cnt_ms)begin
        if(end_cnt_ms)begin
            cnt_ms <= 10'd0;
        end
        else begin
            cnt_ms <= cnt_ms +1'd1;
        end
    end
    else begin
        cnt_ms <= cnt_ms;
    end
end    
assign add_cnt_ms = end_cnt_us;
assign end_cnt_ms = add_cnt_ms && cnt_ms == TIME_MS;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt_s <= 10'd0;
    end 
    else if(add_cnt_s)begin
        if(end_cnt_s)begin
            cnt_s <= 10'd0;
        end
        else begin
            cnt_s <= cnt_s +1'd1;
        end
    end
    else begin
        cnt_s <= cnt_s;
    end
end    
assign add_cnt_s = end_cnt_ms;
assign end_cnt_s = add_cnt_s && cnt_s == TIME_S;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        flag <= 1'b0;
    end
    else if(end_cnt_s)begin
        flag <= ~flag;
    end
    else begin
        flag <= flag;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        led <= 4'b0000;
    end
    else if(!flag) begin
        led <= (cnt_s >cnt_ms)?4'b1111:4'b0000;
    end
    else if(flag)begin
        led <= (cnt_s > cnt_ms)?4'b0000:4'b1111;
    end
    else begin
        led <= led;
    end
end

endmodule
仿真文件
`timescale 1ns/1ns
module breath_led_tb ();

    reg clk;
    reg rst_n;

    wire [3:0] led;

    parameter CYCLE = 20;
    parameter TIME_US = 5;
    parameter TIME_MS = 10;
    parameter TIME_S = 10;

    always #(CYCLE/2) clk = ~clk;

    initial begin
        clk = 1'b0;
        rst_n = 1'b0;
        #(CYCLE);
        rst_n = 1'b1;
        #(2*(TIME_US + 1)*(TIME_MS + 1)*(TIME_S + 1)*CYCLE);
        $stop;
    end

breath_led #(
    .TIME_US(TIME_US),
    .TIME_MS(TIME_MS),
    .TIME_S(TIME_S)
) u_breath_led(
    .clk  (clk),
    .rst_n(rst_n),

    .led  (led)
);

endmodule

三、数码管

  • 数码管的显示,数码管分七段数码管和八段数码管。七段和八段的区别在于,是否包括小数点DP(Digital Point)。本实验中使用的是数码管是8段数码管,每段是由led组成。通过控制每段led的亮灭,来控制数码管显示不同的数字和字母。
静态显示
  • 在静态显示中,只考虑段选信号。在不同的时刻,各个位选信号保持不变,并根据真值表,选择要显示的数字或者字母。
1.模块描述

六个数码管同时间隔0.5s显示0-f。要求:使用一个顶层模块,调用计时器模块和数码管静态显示模块。

2.模块设计

time_count计数器模块

module time_count (     //计时0.5s,计满后输出高电平
    input clk,
    input rst_n,

    output reg flag
);

parameter MAX_NUM = 26'd24_999_999;

reg [25:0] cnt;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt <= 26'd0;
        flag <= 1'b0;
    end
    else if(cnt == MAX_NUM)begin
        cnt <= 26'd0;
        flag <= 1'b1;
    end
    else begin
        cnt <= cnt + 1'd1;
        flag <= 1'b0;
    end
end
    
endmodule

seg_led_static数码管驱动模块

module seg_led_static (
    input clk,
    input rst_n,
    input flag,             //计时0.5s后输入的高电平

    output reg [5:0] sel,   //六位位选信号
    output reg [7:0] seg    //八位段选信号
);

reg [3:0] num;  //保存当前数码管要显示的数字

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        sel <= 6'b111_111;
    end
    else begin
        sel <= 6'b000_000;
    end
end

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        num <= 4'h0;
    end
    else if(flag)begin
        num <= num +1'h1;
    end
    else begin
        num <= num;
    end
end
    
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        seg <= 8'b0;
    end
    else begin
        case (num)
            4'h0:    seg <= 8'b1100_0000;//匹配到后参考共阳极真值表
	        4'h1:    seg <= 8'b1111_1001;
	        4'h2:    seg <= 8'b1010_0100;
	        4'h3:    seg <= 8'b1011_0000;
	        4'h4:    seg <= 8'b1001_1001;
	        4'h5:    seg <= 8'b1001_0010;
	        4'h6:    seg <= 8'b1000_0010;
	        4'h7:    seg <= 8'b1111_1000;
	        4'h8:    seg <= 8'b1000_0000;
	        4'h9:    seg <= 8'b1001_0000;
	        4'ha:    seg <= 8'b1000_1000;
	        4'hb:    seg <= 8'b1000_0011;
	        4'hc:    seg <= 8'b1100_0110;
	        4'hd:    seg <= 8'b1010_0001;
	        4'he:    seg <= 8'b1000_0110;
	        4'hf:    seg <= 8'b1000_1110;
	      	default :seg <= 8'b1100_0000;
        endcase
    end
end
endmodule

顶层模块

module top_seg_led_static (
    input               clk     ,
    input               rst_n   ,

    output      [5:0]    sel     ,
    output      [7:0]    seg
);

parameter MAX_NUM = 26'd24_999_999;
wire flag_reg;

time_count #(.MAX_NUM(MAX_NUM)) u_time_count(
    .clk    (clk)   ,
    .rst_n  (rst_n) ,

    .flag   (flag_reg)
);

seg_led_static u_seg_led_static(
    .clk    (clk)       ,
    .rst_n  (rst_n)     ,
    .flag   (flag_reg)  ,

    .sel    (sel)       ,
    .seg    (seg)
);
    
endmodule
3.仿真文件
`timescale 1ns/1ns
module top_seg_led_static_tb();

reg   		 	clk    ;
reg  		 	rst_n  ;
wire    [5:0]	sel	   ;
wire 	[7:0]  	seg    ;
parameter CYCLE = 20;
parameter MAX_NUM = 9;
always #(CYCLE/2) clk = ~clk;//翻转时钟

initial begin
	clk   = 0		   ;//时钟初始为0
	rst_n = 0		   ;//复位初始为0
	#(CYCLE)		   ;//延迟20ns
	rst_n = 1		   ;//复位置1
	#(16*(MAX_NUM + 1)*CYCLE);//显示0-f时间
	$stop			   ;//停止
	
	
end 
top_seg_led_static#(.MAX_NUM (MAX_NUM))	u_top_seg_led_static(
.clk  	(clk)  ,//50MHz系统时钟
.rst_n	(rst_n),//系统复位信号(低有效)
.sel  	(sel)  ,//数码管位选
.seg	(seg)	//数码管段选
);
endmodule 

四、总结

此次FPGA学习主要是对基础知识的理解与掌握,学习了led灯的一些操作,也对数码管的静态显示有了了解,对verilog语言的学习也更加深刻,收获很多。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值