verilog语法入门

目录

一、概述

二、代码结构

1. 模块定义:

2. 参数定义:

3. 内部信号定义:

4.always语句

5. 实例化模块(底层模块调用):

6. 结束模块

三、功能实现

1、流速控制

2、亮度调节

完整代码 


DE2-115学习过程中verilog语法入门

以流水灯实验代码为例。

这个流水灯实现了以下功能:

流速控制(使用拨码开关控制),亮度控制(按键控制),8个LED依次点亮。

流速控制

点亮LED很简单,定义8个位分别对应8个LED,初始为【0000 0000】然后依次点亮

【0000 0001】,【0000 0010】·······设置好切换时间。

学习中,表述不完整,always语句,阻塞式非阻塞式,asign语句,逻辑符号等,穿插着提太乱了,所以具体语句语法还需另外学习。欢迎交流

一、概述

首先,Verilog是硬件描述语言,通过描述硬件,生成电路。

Verilog - hdl  语法相对宽松,但相对来说不适合大型项目

Verilog有设计语言(30%)和测试语言(70%)。

代码经综合器解释后转化成电路,这个综合器就是 quartus , vivado等开发工具。

二、代码结构

一份完整的代码框架如下:

`````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````

module 模块名

【端口信号声明】

input,output,inout

【参数声明】

reg [0:1]  a,

wier []      b

```````````````````````````````````````````````````````【功能描述】````````````````````````````````````````````````````````

内部信号声明

assign语句

always语句

底层模块调用

`

`

endmodule

`````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````

在Verilog中,代码结构通常由以下几个主要部分组成:

1. 模块定义:


   每个Verilog代码文件通常以模块(module)开始和结束。模块是Verilog代码的基本构建块,可以包含输入、输出和内部信号的定义。

这里将模块命名water_led

定义了4个输入,1个输出

module water_led (
    input 	wire 						sys_clk,
    input 	wire 						sys_rst_n,
    input 	wire 						key1,
    input 			[1:0] 			speed_control,  // 速度控制输入
    output 	reg 	[7:0] 			led_out
);

2. 参数定义:


   使用parameter定义常量参数,通常用于配置模块的行为,比如设置波特率、时钟频率等。

这里是根据DE2-115内部50MHZ的时钟定义的

parameter CNT_1US_MAX = 6'd49;        // 1 us
parameter CNT_1MS_MAX = 10'd999;      // 1 ms

// 定义不同速度下的计数最大值
parameter CNT_MAX_SLOW = 25'd49_999_999;   // 1s
parameter CNT_MAX_MEDIUM = 25'd24_999_999; // 0.5s
parameter CNT_MAX_FAST = 25'd12_499_999;   // 0.25s

3. 内部信号定义:


   使用wire和reg定义内部信号。wire用于连接模块之间的信号,而reg用于存储值的信号。

wire 							key1_flag;		//底层模块调用
reg 							cnt_flag;
reg [9:0] 					tim;				//亮度控制
reg [24:0] 					cnt;
reg [5:0] 					cnt_1us;
reg [9:0] 					cnt_1ms;
reg [2:0] 					i;  				//0-7灯亮

reg [24:0] 					cnt_max;  // 用于存储当前速度下的计数最大值

4.always语句

这是控制流水灯流速的部分代码

这样理解:总是,当信号变化时执行以下语句,speed_contrlo已经给出定义,位宽为2的输入,当输入为00时,cnt_max = CNT_MAX_SLOW,以此类推

而这个输入由开发板上两个拨码开关控制。

// 根据速度控制输入选择计数最大值
always @(*) begin
    case (speed_control)
        2'b00: cnt_max = CNT_MAX_SLOW;
        2'b01: cnt_max = CNT_MAX_MEDIUM;
        2'b10: cnt_max = CNT_MAX_FAST;
        default: cnt_max = CNT_MAX_MEDIUM;  // 默认中等速度
    endcase
end

5. 实例化模块(底层模块调用):


        在一个模块中实例化其他模块,可以使用其他模块的功能。

        这里用按键消抖举例,顶层模块对底层模块调用也称端口映射,有两种方式,这里用最常见的命名法,格式如代码中,每个端口逗号隔开,末尾分号结束。                                                        内容为:(.底层端口名(外接信号名),)

// 按键滤波模块
key_filiter key_filiter_inst (
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
    .key_in(key1),
    .key_flag(key1_flag)
);

6. 结束模块


   每个模块都以endmodule结束。

仿真不过多提及,为了完整性,提一下仿真中会用到的语句:
   在仿真中,可以使用initial块来初始化信号或变量的值。

这些部分共同组成了Verilog代码的结构,能够描述数字电路的行为和功能。大致对代码框架有个理解,更多的用法和语法还需继续学习。

三、功能实现

1、流速控制

首先要知道灯是怎么点亮的,暂时不关注亮度调节的部分,可以看到,当 i 变化时,亮灯的状态发生改变,因此只需要改变 i 

always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)//复位
        led_out <= 8'b00000001;
    else if (cnt_1ms <= tim)//亮度调节
        led_out <= 8'b00000000;
    else//亮灯
        case (i)
            3'd0: led_out <= 8'b00000001;
            3'd1: led_out <= 8'b00000010;
            3'd2: led_out <= 8'b00000100;
            3'd3: led_out <= 8'b00001000;
            3'd4: led_out <= 8'b00010000;
            3'd5: led_out <= 8'b00100000;
            3'd6: led_out <= 8'b01000000;
            3'd7: led_out <= 8'b10000000;
            default: led_out <= 8'b00000000;
        endcase
end

那么请看代码内解释

首先,设计3种流速,那么对应至少需要一个2位寄存器或输入来控制【00】【01】【10】
这里使用输入控制
input 			[1:0] 			speed_control,  // 速度控制输入

流速就是相邻两个LED间点亮的间隔,根据芯片内50MHZ的时钟定义
// 定义不同速度下的计数最大值
parameter CNT_MAX_SLOW = 25'd49_999_999;   // 1s
parameter CNT_MAX_MEDIUM = 25'd24_999_999; // 0.5s
parameter CNT_MAX_FAST = 25'd12_499_999;   // 0.25s

使用always语句,对应speed_control不同输入,计时发生状态改变
// 根据速度控制输入选择计数最大值
always @(*) begin
    case (speed_control)
        2'b00: cnt_max = CNT_MAX_SLOW;
        2'b01: cnt_max = CNT_MAX_MEDIUM;
        2'b10: cnt_max = CNT_MAX_FAST;
        default: cnt_max = CNT_MAX_MEDIUM;  // 默认中等速度
    endcase
end

在接下来的代码中怎么实现呢,以 cnt_max = CNT_MAX_MEDIUM 为例

下面这段代码将根据不同的cnt_max计时,因此,cnt将计时到24_999_999为止。我们知道,当它记满时,只需一段 i+1的代码,就可以使LED依次点亮,接下来用两段代码实现

// 基于速度的计数逻辑
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt <= 25'd0;
    else if (cnt == cnt_max)
        cnt <= 25'd0;
    else
        cnt <= cnt + 25'd1;
end

计数标志位以及 i 自加1

注意观察代码,判断为当cnt == cnt_max - 1为真,cnt_flag赋值1

即cnt=24_999_998时执行cnt_flag赋值1

接着计满,i+1,此时来到24_999_999

然后亮灯(这部分属于个人理解,仅供参考)

// 计数满标志位
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt_flag <= 1'b0;
    else if (cnt == cnt_max - 1)
        cnt_flag <= 1'b1;
    else
        cnt_flag <= 1'b0;
end
// 每次计数满,i自加1,改变亮灯位置
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        i <= 3'd0;
    else if (cnt_flag && (i == 3'd7))
        i <= 3'd0;
    else if (cnt_flag)
        i <= i + 3'd1;
end

通过这些代码,将实现LED流速控制

2、亮度调节

所谓亮度调节,就是点亮时间的调节,类似PWM原理,大致可以理解为相同时间内能量的变化。1s输出1W,1s输出0.5W,亮度不同。有兴趣可以去探究。

定位到亮度调节代码

用tim的值控制点亮时间,tim最大值为d999,cnt_1ms的最大值也为d999,按下一次tim+300现象会比较明显。

由条件判断可知,当cnt_1ms <= tim为真,led_out <= 8'b00000000,即LED熄灭

利用视觉暂留效果,做到LED亮度调节。

// 亮度变量调整
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        tim <= 10'd0;
    else if (tim >= 10'd999)
        tim <= 10'd0;
    else if (key1_flag)
        tim <= tim + 10'd300;
// 输出控制
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        led_out <= 8'b00000001;
    else if (cnt_1ms <= tim)
        led_out <= 8'b00000000;
    else
        case (i)
            3'd0: led_out <= 8'b00000001;
            3'd1: led_out <= 8'b00000010;
            3'd2: led_out <= 8'b00000100;
            3'd3: led_out <= 8'b00001000;
            3'd4: led_out <= 8'b00010000;
            3'd5: led_out <= 8'b00100000;
            3'd6: led_out <= 8'b01000000;
            3'd7: led_out <= 8'b10000000;
            default: led_out <= 8'b00000000;
        endcase
end

完整代码 

module water_led (
    input 	wire 						sys_clk,
    input 	wire 						sys_rst_n,
    input 	wire 						key1,
    input 			[1:0] 			speed_control,  // 速度控制输入
    output 	reg 	[7:0] 			led_out
);

parameter CNT_1US_MAX = 6'd49;        // 1 us
parameter CNT_1MS_MAX = 10'd999;      // 1 ms

// 定义不同速度下的计数最大值
parameter CNT_MAX_SLOW = 25'd49_999_999;   // 1s
parameter CNT_MAX_MEDIUM = 25'd24_999_999; // 0.5s
parameter CNT_MAX_FAST = 25'd12_499_999;   // 0.25s

wire 							key1_flag;
reg 							cnt_flag;
reg [9:0] 					tim;
reg [24:0] 					cnt;
reg [5:0] 					cnt_1us;
reg [9:0] 					cnt_1ms;
reg [2:0] 					i;  

reg [24:0] 					cnt_max;  // 用于存储当前速度下的计数最大值

// 根据速度控制输入选择计数最大值
always @(*) begin
    case (speed_control)
        2'b00: cnt_max = CNT_MAX_SLOW;
        2'b01: cnt_max = CNT_MAX_MEDIUM;
        2'b10: cnt_max = CNT_MAX_FAST;
        default: cnt_max = CNT_MAX_MEDIUM;  // 默认中等速度
    endcase
end

// 基于速度的计数逻辑
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt <= 25'd0;
    else if (cnt == cnt_max)
        cnt <= 25'd0;
    else
        cnt <= cnt + 25'd1;
end

// 计数满标志位
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt_flag <= 1'b0;
    else if (cnt == cnt_max - 1)
        cnt_flag <= 1'b1;
    else
        cnt_flag <= 1'b0;
end

// 亮度变量调整
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        tim <= 10'd0;
    else if (tim >= 10'd999)
        tim <= 10'd0;
    else if (key1_flag)
        tim <= tim + 10'd300;
end

// cnt_1us
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt_1us <= 6'd0;
    else if (cnt_1us == CNT_1US_MAX)
        cnt_1us <= 6'd0;
    else
        cnt_1us <= cnt_1us + 6'd1;
end

// cnt_1ms
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        cnt_1ms <= 10'd0;
    else if ((cnt_1ms == CNT_1MS_MAX) && (cnt_1us == CNT_1US_MAX))
        cnt_1ms <= 10'd0;
    else if (cnt_1us == CNT_1US_MAX)
        cnt_1ms <= cnt_1ms + 10'd1;
end

// 每次计数满,i自加1,改变亮灯位置
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        i <= 3'd0;
    else if (cnt_flag && (i == 3'd7))
        i <= 3'd0;
    else if (cnt_flag)
        i <= i + 3'd1;
end

// 输出控制
always @(posedge sys_clk or negedge sys_rst_n) begin
    if (!sys_rst_n)
        led_out <= 8'b00000001;
    else if (cnt_1ms <= tim)
        led_out <= 8'b00000000;
    else
        case (i)
            3'd0: led_out <= 8'b00000001;
            3'd1: led_out <= 8'b00000010;
            3'd2: led_out <= 8'b00000100;
            3'd3: led_out <= 8'b00001000;
            3'd4: led_out <= 8'b00010000;
            3'd5: led_out <= 8'b00100000;
            3'd6: led_out <= 8'b01000000;
            3'd7: led_out <= 8'b10000000;
            default: led_out <= 8'b00000000;
        endcase
end

// 按键滤波模块
key_filiter key_filiter_inst (
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
    .key_in(key1),
    .key_flag(key1_flag)
);

endmodule

 按键消抖

//按键滤波器,按下按钮后即低电平输入,经过该模块输出一个系统时钟周期的高脉冲
module	key_filiter
(
	input		sys_clk     ,
	input		sys_rst_n   ,
	input		key_in      ,
	output	reg	key_flag
);

reg	[19:0]	cnt_20ms;
parameter	CNT_MAX=20'd999999;

always@(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		cnt_20ms<=20'd0;
	else	if(key_in)
		cnt_20ms<=20'd0;
	else	if(cnt_20ms==CNT_MAX)	//最大值保持
		cnt_20ms<=CNT_MAX;
	else
		cnt_20ms<=cnt_20ms+1'd1;
		
always@(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		key_flag<=1'b0;
	else	if(cnt_20ms==CNT_MAX-20'd1)
		key_flag<=1'b1;
	else
		key_flag<=1'b0;
		
endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值