---
title: 基于 ZYNQ ECO开发板 跑马灯
url: "https://www.fpgalearn.com/9132.html"
kind: article
id: 9132
excerpt: always语句 在Verilog中，always语句是用来描述组合逻辑或时序逻辑的重要语句。它的声明格式如下： always @(<敏感信号列表>…
published_at: 2025-05-09
author: FPGA小白
tags: ECO开发板, fpga入门, FPGA工程
word_count: 3499
source: "https://www.fpgalearn.com"
---

# 基于 ZYNQ ECO开发板 跑马灯

> 标签：ECO开发板、fpga入门、FPGA工程
> always语句 在Verilog中，always语句是用来描述组合逻辑或时序逻辑的重要语句。它的声明格式如下： always @(<敏感信号列表>…

> always语句 在Verilog中，always语句是用来描述组合逻辑或时序逻辑的重要语句。它的声明格式如下： always @(<敏感信号列表>…

## always语句
在Verilog中，always语句是用来描述组合逻辑或时序逻辑的重要语句。它的声明格式如下：
always @(<敏感信号列表>) <语句块>
<敏感信号列表>中可以包含各种信号，如输入端口、寄存器或者其他内部信号，用于指定在这些信号发生变化时，<语句块>需要执行。
在always语句中，<语句块>可以是一系列的逻辑语句，用来描述所需的行为。这些语句可以是组合逻辑的赋值语句，或者是时序逻辑的if语句、case语句等。
然而，需要注意的是，always语句必须与时序控制结合使用，以确保其在仿真过程中能够正常工作。如果一个always语句没有明确的时序控制，可能会导致仿真死锁的情况。就像所给出的例子always i = ~i;，该语句没有指定敏感信号，因此在仿真过程中会发生死锁。
为了解决这个问题，我们应该使用时序控制结构，如posedge（上升沿触发）或negedge（下降沿触发）来指定敏感信号。例如，如果你想要一个always语句在时钟的上升沿触发时执行，你可以这样写：
always @(posedge clk) i <= ~i;
这样，always语句将在时钟的上升沿触发时执行，并将i的值取反并赋给i。
总结一下，为了确保always语句能够正常工作，在使用时必须结合适当的时序控制结构，以明确指定敏感信号。这样可以避免引发仿真死锁的情况。
## 赋值语句（阻塞赋值语句和非阻塞赋值语句）
在Verilog中，有两种不同的赋值语句：阻塞（blocking）赋值语句和非阻塞（non-blocking）赋值语句。
阻塞赋值语句使用等号（=）进行赋值操作，例如 b = a;。当执行阻塞赋值语句时，该语句将立即执行，并且在赋值完成之前不会执行下一条语句。这意味着阻塞赋值语句按照顺序逐个执行，是串行执行的。硬件电路中，阻塞赋值语句会导致电路的并行性受限，因为下一步操作必须等待当前赋值语句完成。因此，在可综合风格的模块中，建议使用阻塞赋值语句。
非阻塞赋值语句使用非阻塞赋值符（<=），例如 b <= a;。与阻塞赋值不同，非阻塞赋值语句在执行时不会立即执行赋值操作，而是在整个 always 块的末尾才会执行。这意味着非阻塞赋值语句的执行是并行的，多个非阻塞赋值语句可以同时执行。在硬件电路中，非阻塞赋值语句能够保持并行性，因为每个赋值语句的执行不会相互影响。因此，非阻塞赋值语句在时序逻辑中非常有用。
需要注意的是，在仿真过程中，阻塞赋值和非阻塞赋值的行为是不同的。阻塞赋值在执行时会立即赋值，而非阻塞赋值在执行时会将赋值操作推迟到下一个时间步。因此，在时序逻辑中使用非阻塞赋值可以更准确地模拟出电路的行为，而在组合逻辑中使用阻塞赋值则更直观和方便。
总结一下，阻塞赋值语句使用等号进行赋值，是串行执行的；非阻塞赋值语句使用非阻塞赋值符进行赋值，是并行执行的。在可综合风格的模块中，建议使用非阻塞赋值语句，而在组合逻辑中使用阻塞赋值语句更为方便。
## 位拼接运算符
位拼接运算符可以将多个信号的指定位拼接在一起，用于进行运算操作或者生成新的信号。位拼接运算符使用一对大括号（{}）来表示。
位拼接运算符的语法如下：
{信号1的某几位, 信号2的某几位, …, 信号n的某几位}
其中，每个信号后面的方括号（[]）用于指定要拼接的位的范围。例如，a[3:0]表示信号a的4个低位，w[3’b101]表示一个3位宽的信号，具体值为二进制101。
需要注意的是，在位拼接表达式中，所有被拼接的信号都必须明确指定要拼接的位数。也就是说，不允许直接拼接一个没有指明位数的信号。例如，{a, b[3:0], w, 3’b101}中，信号a、b、w都指定了要拼接的位数，而3’b101表示一个3位宽的二进制常数。
另外，还可以在位拼接表达式中使用具体的位值，如1’b1表示一个1位宽的常量1，1’b0表示一个1位宽的常量0。
总结一下，位拼接运算符可以将多个信号的指定位拼接在一起，生成新的信号。在位拼接表达式中，需要明确指定每个信号要拼接的位数，并且可以使用具体的位值。
## 跑马灯
```
`timescale 1ns / 1ps

module led_sy2(
input clk,
input rst_n,
output reg [3:0] led
);
//assign led = 4'b1100;
parameter LEDL = 0; //第一个灯
parameter s1 = 1;//第二个灯
parameter s2 = 2;//第三个灯
parameter s3 = 3;//第四个灯

parameter delay = 50_000_000;
reg [2:0]   c_state,n_state;
reg [31:0]  count ;
always @(posedge clk)
    if(!rst_n)
        c_state <= 0;
    else
        c_state <= n_state;

always @(*)
    if(!rst_n)
        n_state = LEDL;
    else begin
         case(c_state)
            LEDL: begin
                if(count == delay -1)
                    n_state = s1;
                else
                    n_state = LEDL;
            end
            s1: begin
                if(count == delay -1)
                    n_state = s2;
                else
                    n_state = s1;
            end
            s2: begin
                if(count == delay -1)
                    n_state = s3;
                else
                    n_state = s2;
            end
            s3: begin
                if(count == delay -1)
                    n_state = LEDL;
                else
                    n_state = s3;
            end
            endcase //case需要一个endcase来结束
     end

 always @(posedge clk)
    if(!rst_n)
        led <= 0;
    else    begin
        case(c_state)
            LEDL: begin
                led<=4'b0001;
            end
            s1: begin
                led<=4'b0010;
            end
            s2: begin
                led<=4'b0100;
            end
            s3: begin
                led<=4'b1000;
            end
        endcase
    end

 always @(posedge clk)
    if(!rst_n)
        count <= 0;
    else if(count == delay -1)
        count<=0;
    else
        count <= count + 1;
endmodule
```
这段代码是一个Verilog HDL的模块，名为led_sy2。下面对代码进行详细的解释：
timescale 1ns / 1ps：这是一个timescale指令，用于定义仿真中的时间单位。这里指定1纳秒为时间单位，1皮秒为时间精度。
module led_sy2(…)：定义了一个名为led_sy2的模块。该模块有三个输入信号：clk（时钟信号）、rst_n（异步复位信号），以及一个输出寄存器led（4位宽的LED灯）。
parameter：这里定义了四个参数，分别为LEDL、s1、s2、s3，分别表示四个不同状态的LED灯。
parameter delay = 50_000_000：定义了一个名为delay的参数，赋值为50000000，即延迟计数的值。该值约等于1秒（因为timescale中指定了1纳秒为时间单位）。
reg：定义了三个寄存器变量c_state（当前状态）、n_state（下一个状态）和count（计数器）。
always @(posedge clk)：这是一个时钟触发的过程块。当时钟的上升沿触发时，这个块中的代码将被执行。
if(!rst_n)：这是一个异步复位条件，当rst_n为0时，表示复位状态。
else：表示当复位条件不满足时的情况。
case(c_state)：这是一个case语句，根据c_state的值来执行相应的操作。
在case语句中，根据count的值来确定下一个状态n_state。如果count等于delay – 1（即延迟计数的最大值-1），则切换到下一个状态，否则保持当前状态。
always @(*)：这是一个组合逻辑过程块，当任何与其相关的信号发生变化时，这个块中的代码将被执行。
在always @(*)块中，根据当前状态c_state来分别设置输出led的值。根据不同的状态，将led赋值为不同的4位二进制数。
第三个always @(posedge clk)块用于控制计数器count的值。在每个时钟上升沿触发时，检查复位条件和计数器是否达到最大值，并根据情况进行设置。
总结：这段代码实现了一个状态机，用于控制4个LED灯的状态变化。通过计数器和延迟值来控制状态切换的时间，通过case语句来确定下一个状态，并根据当前状态设置输出LED的值。
