# 数字逻辑实验期末速通攻略 2026.6

写在前面：本文档**并非** SystemVerilog 的教学文档，因此在阅读该文档前建议首先学习 SV 语言的基本语法规则（模块定义写法、变量类型、两种赋值、组合/时序块等）。本文档旨在提供一套通用的解题策略，使用这套思路理论上 90% 的代码都可以直接套用文档中的模板。如果你还没有自己的一套解题思路，那么相信这份文档能够帮助你快速上手 SV 程序设计。

## 可编程模块相关

### 管脚信息

| 信号名称 | 管脚 | 输入输出 | 说明 |
| :---: | :---: | :---: | :---: |
| CLK_EXT | J19 | 输入 | CLK |
| EXT0 | K18 | 输入 | IO0 |
| EXT1 | M21 | 输入/输出 | IO1 |
| EXT2 | N20 | 输入/输出 | IO2 |
| EXT3 | N22 | 输入/输出 | IO3 |
| EXT4 | P21 | 输入/输出 | IO4 |
| EXT5 | P22 | 输入/输出 | IO5 |
| EXT6 | T21 | 输入/输出 | IO6 |
| EXT7 | U21 | 输入/输出 | IO7 |
| EXT8 | R21 | 输入/输出 | IO8 |
| EXT9 | R22 | 输入/输出 | IO9 |
| EXT10 | P20 | 输入/输出 | IO10 |
| EXT11 | W21 | 输入/输出 | IO11 |
| EXT12 | W22 | 输入/输出 | IO12 |
| EXT13 | Y22 | 输入/输出 | IO13 |
| EXT14 | Y21 | 输入/输出 | IO14 |
| EXT15 | AB22 | 输入/输出 | IO15 |
| EXT16 | AA18 | 输入/输出 | IO16 |
| EXT17 | AB18 | 输入/输出 | IO17 |
| EXT18 | AA20 | 输入/输出 | IO18 |
| EXT19 | AB21 | 输入/输出 | IO19 |
| EXT20 | AA21 | 输入/输出 | IO20 |

### 其他相关信息

- 芯片型号：`xc7a35tfgg484-2`

## 代码相关

### 管脚绑定代码模板

```xdc
# CLK
set_property -dict {PACKAGE_PIN J19 IOSTANDARD LVCMOS33} [get_ports clk];     #CLK接插孔

# RST
set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports rst];     #IO0接插孔

# IO
set_property -dict {PACKAGE_PIN M21 IOSTANDARD LVCMOS33} [get_ports X];     #IO1接插孔
set_property -dict {PACKAGE_PIN N20 IOSTANDARD LVCMOS33} [get_ports X];     #IO2接插孔
set_property -dict {PACKAGE_PIN N22 IOSTANDARD LVCMOS33} [get_ports X];     #IO3接插孔
set_property -dict {PACKAGE_PIN P21 IOSTANDARD LVCMOS33} [get_ports X];     #IO4接插孔
set_property -dict {PACKAGE_PIN P22 IOSTANDARD LVCMOS33} [get_ports X];     #IO5接插孔

set_property -dict {PACKAGE_PIN T21 IOSTANDARD LVCMOS33} [get_ports X];     #IO6接插孔
set_property -dict {PACKAGE_PIN U21 IOSTANDARD LVCMOS33} [get_ports X];     #IO7接插孔
set_property -dict {PACKAGE_PIN R21 IOSTANDARD LVCMOS33} [get_ports X];     #IO8接插孔
set_property -dict {PACKAGE_PIN R22 IOSTANDARD LVCMOS33} [get_ports X];     #IO9接插孔
set_property -dict {PACKAGE_PIN P20 IOSTANDARD LVCMOS33} [get_ports X];     #IO10接插孔

set_property -dict {PACKAGE_PIN W21 IOSTANDARD LVCMOS33} [get_ports X];     #IO11接插孔
set_property -dict {PACKAGE_PIN W22 IOSTANDARD LVCMOS33} [get_ports X];     #IO12接插孔
set_property -dict {PACKAGE_PIN Y22 IOSTANDARD LVCMOS33} [get_ports X];     #IO13接插孔
set_property -dict {PACKAGE_PIN Y21 IOSTANDARD LVCMOS33} [get_ports X];     #IO14接插孔
set_property -dict {PACKAGE_PIN AB22 IOSTANDARD LVCMOS33} [get_ports X];     #IO15接插孔

set_property -dict {PACKAGE_PIN AA18 IOSTANDARD LVCMOS33} [get_ports X];     #IO16接插孔
set_property -dict {PACKAGE_PIN AB18 IOSTANDARD LVCMOS33} [get_ports X];     #IO17接插孔
set_property -dict {PACKAGE_PIN AA20 IOSTANDARD LVCMOS33} [get_ports X];     #IO18接插孔
set_property -dict {PACKAGE_PIN AB21 IOSTANDARD LVCMOS33} [get_ports X];     #IO19接插孔
set_property -dict {PACKAGE_PIN AA21 IOSTANDARD LVCMOS33} [get_ports X];     #IO20接插孔

# required if touch button used as manual clock source
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets CLK_IBUF]

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

```

### 常用代码模板

#### 状态机模板（重要！）

```systemverilog

// 以移位寄存器方式实现的状态机模板
parameter S0 = 3'b000,S1 = 3'b001,S2 = 3'b011,S3 = 3'b111,S4 = 3'b110;

reg [2:0] state, next_state;

always_comb begin
        case(state)
            S0:  next_state = S1;
            S1:  next_state = S2;
            S2:  next_state = S3;
            S3:  next_state = S4;
            S4:  next_state = S4;
            default: next_state = S0;
        endcase
    end
```

#### 分频脉冲生成（重要！）

```systemverilog
parameter CLK_FREQ = 1_000_000; // 时钟频率（默认 1MHz）
parameter PULSE_PERIOD = CLK_FREQ * 1; // 脉冲周期（默认 1s）

reg [31:0] counter; // 计数器
reg pulse; // 脉冲信号

always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
        counter <= 0;
        pulse <= 0;
    end else begin
        if (counter == PULSE_PERIOD - 1) begin
            counter <= 0; // 重置计数器
            pulse <= 1; // 翻转脉冲信号
        end else begin
            counter <= counter + 1; // 计数器加一
            pulse <= 0; // 保持脉冲信号为低电平
        end
    end
end

// 后续在时序逻辑块中使用 pulse 信号即可实现间隔固定时间执行某操作的功能
```

#### 开关同步化

```systemverilog

wire switch; // 开关信号(input)
reg switch_sync; // 同步化后的开关信号

always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
        switch_sync <= 0; // 同步化后的开关信号（rst 同步复位）
    end else begin
        switch_sync <= switch; // 同步化处理
    end
end

// 后续在时序逻辑块中使用 switch_sync 信号即可避免亚稳态问题
```

#### 按键同步边沿检测（重要！）

```systemverilog

wire key; // 按键信号(input)
reg key_sync1, key_sync2; // 同步化后的按键信号

always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
        key_sync1 <= 0; // 同步化后的按键信号（rst 同步复位）
        key_sync2 <= 0; // 同步化后的按键信号（rst 同步复位）
    end else begin
        key_sync1 <= key; // 同步化处理
        key_sync2 <= key_sync1; // 再次同步化处理
    end
end

wire key_posedge = key_sync1 & ~key_sync2; // 上升沿检测
wire key_negedge = ~key_sync1 & key_sync2; // 下降沿检测

// 后续在时序逻辑块中使用 key_posedge 或 key_negedge 信号即可实现按键事件的边沿触发功能
```

#### 分频时钟生成（占空比 50%）

```systemverilog

parameter CLK_FREQ = 1_000_000; // 时钟频率（默认 1MHz）
parameter DIV_CLK_HALF_PERIOD = CLK_FREQ * 1; // 分频时钟半周期（默认全周期 2s）

reg [31:0] counter; // 计数器
reg div_clk; // 分频时钟信号

always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
        counter <= 0;
        div_clk <= 0;
    end else begin
        if (counter == DIV_CLK_HALF_PERIOD - 1) begin
            counter <= 0;
            div_clk <= ~div_clk;
        end else begin
            counter <= counter + 1;
        end
    end
end

// 后续在时序逻辑块中使用 div_clk 信号即可实现分频时钟的功能
```

#### 基本时序模块模板

```systemverilog
always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
        // 同步复位逻辑
    end else begin
        // 时序逻辑
    end
end
```

### 一些组合逻辑模块（较不重要）

#### 7段数码管解码器（4位二进制 -> 7段数码管输出）

```systemverilog
module Decoder(
    input wire [3:0] d,  // 4 位二进制
    output reg [6:0] seg // 7 段数码管输出
    );
    always_comb begin
        case (d)
            4'h0: seg = 7'b1111110;
            4'h1: seg = 7'b0110000;
            4'h2: seg = 7'b1101101;
            4'h3: seg = 7'b1111001;
            4'h4: seg = 7'b0110011;
            4'h5: seg = 7'b1011011;
            4'h6: seg = 7'b1011111;
            4'h7: seg = 7'b1110000;
            4'h8: seg = 7'b1111111;
            4'h9: seg = 7'b1110011;
            4'hA: seg = 7'b1110111;
            4'hB: seg = 7'b0011111;
            4'hC: seg = 7'b1001110;
            4'hD: seg = 7'b0111101;
            4'hE: seg = 7'b1001111;
            4'hF: seg = 7'b1000111;
            default: seg = 7'b0; // 默认为全灭（程序不应执行到这里）
        endcase
    end
endmodule
```

#### 7段数码管编码器（7段数码管输入 -> 4位二进制输出）

```systemverilog
module Encoder(
    input wire [6:0] seg, // 7 段数码管输入
    output reg [3:0] d    // 4 位二进制
    );
    
    always_comb begin
        case (seg)
            7'b1111110: d = 4'h0;
            7'b0110000: d = 4'h1;
            7'b1101101: d = 4'h2;
            7'b1111001: d = 4'h3;
            7'b0110011: d = 4'h4;
            7'b1011011: d = 4'h5;
            7'b1011111: d = 4'h6;
            7'b1110000: d = 4'h7;
            7'b1111111: d = 4'h8;
            7'b1110011: d = 4'h9;
            7'b1110111: d = 4'hA;
            7'b0011111: d = 4'hB;
            7'b1001110: d = 4'hC;
            7'b0111101: d = 4'hD;
            7'b1001111: d = 4'hE;
            7'b1000111: d = 4'hF;
            default: d = 4'hF; // 程序不应运行到这里
        endcase
    end
endmodule
```

#### 二进制转十进制模块

```systemverilog
// 将 4 位二进制输出 F_bin 转换为 2 位十进制
module bin_to_dec(
    input [3:0] F_bin,
    input Cout,
    output reg[3:0] F_high,
    output reg[3:0] F_low
    );
    
    always_comb begin
        if (~Cout) begin
            case (F_bin)
                4'd0: {F_high, F_low} = {4'd0, 4'd0};
                4'd1: {F_high, F_low} = {4'd0, 4'd1};
                4'd2: {F_high, F_low} = {4'd0, 4'd2};
                4'd3: {F_high, F_low} = {4'd0, 4'd3};
                4'd4: {F_high, F_low} = {4'd0, 4'd4};
                4'd5: {F_high, F_low} = {4'd0, 4'd5};
                4'd6: {F_high, F_low} = {4'd0, 4'd6};
                4'd7: {F_high, F_low} = {4'd0, 4'd7};
                4'd8: {F_high, F_low} = {4'd0, 4'd8};
                4'd9: {F_high, F_low} = {4'd0, 4'd9};
                4'd10: {F_high, F_low} = {4'd1, 4'd0};
                4'd11: {F_high, F_low} = {4'd1, 4'd1};
                4'd12: {F_high, F_low} = {4'd1, 4'd2};
                4'd13: {F_high, F_low} = {4'd1, 4'd3};
                4'd14: {F_high, F_low} = {4'd1, 4'd4};
                4'd15: {F_high, F_low} = {4'd1, 4'd5};
                default: {F_high, F_low} = {4'd0, 4'd0}; // 防止锁存（实际不应执行该语句）
            endcase
          end else begin
            case (F_bin)
                4'd0: {F_high, F_low} = {4'd1, 4'd6};
                4'd1: {F_high, F_low} = {4'd1, 4'd7};
                4'd2: {F_high, F_low} = {4'd1, 4'd8};
                4'd3: {F_high, F_low} = {4'd1, 4'd9};
                4'd4: {F_high, F_low} = {4'd2, 4'd0};
                4'd5: {F_high, F_low} = {4'd2, 4'd1};
                4'd6: {F_high, F_low} = {4'd2, 4'd2};
                4'd7: {F_high, F_low} = {4'd2, 4'd3};
                4'd8: {F_high, F_low} = {4'd2, 4'd4};
                4'd9: {F_high, F_low} = {4'd2, 4'd5};
                4'd10: {F_high, F_low} = {4'd2, 4'd6};
                4'd11: {F_high, F_low} = {4'd2, 4'd7};
                4'd12: {F_high, F_low} = {4'd2, 4'd8};
                4'd13: {F_high, F_low} = {4'd2, 4'd9};
                4'd14: {F_high, F_low} = {4'd3, 4'd0};
                4'd15: {F_high, F_low} = {4'd3, 4'd1};
                default: {F_high, F_low} = {4'd0, 4'd0}; // 防止锁存（实际不应执行该语句）
            endcase
        end
    end
    
endmodule
```

#### 一位全加器

```systemverilog
// 一位全加器
module onebit_fulladder(
    input  A, B, Cin,
    output F, Cout
    );
    
    assign F = A ^ B ^ Cin; // 结果
    assign Cout = (A & B) | (Cin & (A ^ B)); // 进位
    
endmodule
```


## 设计思路相关

- 模块的输入输出信号建议采用 `wire` 类型，确保其为单纯的组合逻辑信号。

- 如果输出信号涉及时序，建议增加 `reg` 类型的内部变量，用于处理时序逻辑。如：

```systemverilog
module example(
    output wire var
    );

    reg var_i; // 内部变量（reg 类型）用于处理时序逻辑
    reg next_var; // 下一状态变量

    assign var = combination_logic(var_i); // 输出信号通过组合逻辑计算得到

endmodule
```

- 对于**涉及时序的**的开关/按钮输入信号，应当进行同步化处理，防止亚稳态问题。不要使用 `always_ff @(posedge key)` 的方式捕捉输入信号，这会导致严重的时序混乱问题。

- 处理时序逻辑时，建议同时设置当前状态变量 `var` 和下一状态变量 `next_var` ，通常情况下 `next_var` 可以使用其他状态变量通过组合逻辑计算得到。

- 在一个时序/组合逻辑块中建议只处理一个变量或一组**更新/赋值逻辑完全相同**的变量，这样既便于后续调试，同时还可保证时序逻辑的正确性。

- 在时序逻辑块中建议全部使用非阻塞赋值 `<=` ，在组合逻辑块中建议全部使用阻塞赋值 `=`，以避免混用导致的时序问题。

- 处理一个状态变量的基本流程为：`同步化（如果为开关/按钮输入信号） -> 通过组合逻辑计算下一状态变量 -> 通过时序逻辑更新当前状态变量 -> 处理输出逻辑（如果为输出信号）`

- 即使在组合逻辑中，也建议对于 `rst` 单独开辟一个分支进行处理，以保证其位于最高优先级。

## 实战案例

### 2025第三节

```systemverilog
`timescale 1ns / 1ps

module main(
    input wire CLK,
    input wire RST,
    output wire [3:0] dis1,
    output wire [3:0] dis2,
    output wire [3:0] dis3,
    output wire led,
    input wire step
    );
    
// ------------------------------------------------------------------------
    
    parameter CLK_FREQ = 1_000_000; // 时钟频率（默认 1MHz）
    parameter PULSE_PERIOD = CLK_FREQ * 1; // 脉冲周期（默认 1s）
    
    reg [31:0] counter; // 计数器
    reg pulse; // 脉冲信号
    
    // 获取脉冲信号
    always_ff @(posedge CLK or posedge RST) begin
        if (RST) begin
            counter <= 0;
            pulse <= 0;
        end else begin
            if (counter == PULSE_PERIOD - 1) begin
                counter <= 0; // 重置计数器
                pulse <= 1; // 翻转脉冲信号
            end else begin
                counter <= counter + 1; // 计数器加一
                pulse <= 0; // 保持脉冲信号为低电平
            end
        end
    end

// ------------------------------------------------------------------------
    
    reg step_sync1, step_sync2; // 同步化后的按键信号
    
    // 获取 step 上升沿
    always_ff @(posedge CLK or posedge RST) begin
        if (RST) begin
            step_sync1 <= 0; // 同步化后的按键信号（RST 同步复位）
            step_sync2 <= 0; // 同步化后的按键信号（RST 同步复位）
        end else begin
            step_sync1 <= step; // 同步化处理
            step_sync2 <= step_sync1; // 再次同步化处理
        end
    end
    
    wire step_posedge = step_sync1 & ~step_sync2; // 上升沿检测
    reg [1:0] step_state, next_step_state;

// ------------------------------------------------------------------------

    parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b11, S3 = 2'b10; // step_state 状态
    
    // 处理 next_step_state 赋值
    always_comb begin
        case (step_state)
            S0: next_step_state = S1;
            S1: next_step_state = S2;
            S2: next_step_state = S3;
            S3: next_step_state = S3;
            default: next_step_state = S0;
        endcase
    end

// ------------------------------------------------------------------------
    
     // 处理 step_state 更新
     always_ff @(posedge CLK or posedge RST) begin
        if (RST) begin
            step_state <= S0;
        end else if (step_posedge) begin
            step_state <= next_step_state;
        end
     end
     
// ------------------------------------------------------------------------

    reg [7:0] lfsr; // 当前的lfsr随机数

    // 处理 next_lfsr 赋值
    reg [7:0] next_lfsr = {lfsr[6:0], lfsr[7] ^ lfsr[5] ^ lfsr[4] ^ lfsr[3]};
    
    // 处理 lfsr 更新
    always_ff @(posedge CLK or posedge RST) begin
         if (RST) begin
            // 当复位信号为 1 时，将LFSR重置
            lfsr <= 8'b1;
         end else if (pulse) begin
            // 计算新的LFSR值
            lfsr <= next_lfsr;
         end
     end
     
// ------------------------------------------------------------------------
     
     reg [3:0] dis1_i, dis2_i, dis3_i;
     reg [3:0] next_dis1, next_dis2, next_dis3;
     
     // 处理 next_dis 赋值
     always_comb begin
        case (step_state)
            S0: begin
                    next_dis1 = next_lfsr[7:6];
                    next_dis2 = next_lfsr[5:3];
                    next_dis3 = next_lfsr[2:0];
                end
            S1: begin
                    next_dis1 = dis1_i;
                    next_dis2 = next_lfsr[5:3];
                    next_dis3 = next_lfsr[2:0];
                end
            S2: begin
                    next_dis1 = dis1_i;
                    next_dis2 = dis2_i;
                    next_dis3 = next_lfsr[2:0];
                end
            S3: begin
                    next_dis1 = dis1_i;
                    next_dis2 = dis2_i;
                    next_dis3 = dis3_i;
                end
            default: begin
                    next_dis1 = next_lfsr[7:6];
                    next_dis2 = next_lfsr[5:3];
                    next_dis3 = next_lfsr[2:0];
                end
        endcase
     end
     
// ------------------------------------------------------------------------
     
     // 处理 dis_i 更新
     always_ff @(posedge CLK or posedge RST) begin
        if (RST) begin
            dis1_i <= 0;
            dis2_i <= 0;
            dis3_i <= 0;
        end else if (pulse) begin
            dis1_i <= next_dis1;
            dis2_i <= next_dis2;
            dis3_i <= next_dis3;
        end
     end
     
// ------------------------------------------------------------------------
     
     // 处理输出逻辑
     assign dis1 = RST ? 0 : dis1_i;
     assign dis2 = RST ? 0 : dis2_i;
     assign dis3 = RST ? 0 : dis3_i;
     
     wire [4:0] sum = dis1_i + dis2_i + dis3_i;
     
     assign led = RST ? 0 : (((step_state == S3) && (sum >= 10)) ? 1 : 0);
    
endmodule
```

### 2026第一节

```systemverilog
`timescale 1ns / 1ps

module main(
    input wire clk,
    input wire rst,
    output wire [3:0] led,
    input wire key,
    input wire [3:0] sw,
    output wire crc,
    output wire [3:0] dis
    );

// ------------------------------------------------------------------------
    
    parameter CLK_FREQ = 1_000_000; // 时钟频率（默认 1MHz）
    parameter PULSE_PERIOD = 500_000; // 脉冲周期（0.5 s）
    
    reg [31:0] counter; // 计数器
    reg pulse; // 脉冲信号
    
    // 获取脉冲信号
    always_ff @(posedge clk or posedge rst) begin
        if (rst) begin
            counter <= 0;
            pulse <= 0;
        end else begin
            if (counter == PULSE_PERIOD - 1) begin
                counter <= 0; // 重置计数器
                pulse <= 1; // 翻转脉冲信号
            end else begin
                counter <= counter + 1; // 计数器加一
                pulse <= 0; // 保持脉冲信号为低电平
            end
        end
    end
    
// ------------------------------------------------------------------------
    
    reg key_sync1, key_sync2; // 同步化后的按键信号
    
    // 同步化按键信号
    always_ff @(posedge clk or posedge rst) begin
        if (rst) begin
            key_sync1 <= 0; // 同步化后的按键信号（rst 同步复位）
            key_sync2 <= 0; // 同步化后的按键信号（rst 同步复位）
        end else begin
            key_sync1 <= key; // 同步化处理
            key_sync2 <= key_sync1; // 再次同步化处理
        end
    end
    
    wire key_negedge = ~key_sync1 & key_sync2; // 下降沿检测

// ------------------------------------------------------------------------
    
    parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b11, S3 = 2'b10;
    reg [1:0] key_state, next_key_state;
    
    
    // 处理 next_key_state 赋值
    always_comb begin
        case (key_state)
            S0: next_key_state = S1;
            S1: next_key_state = S2;
            S2: next_key_state = S2;
            S3: next_key_state = S0;
            default: next_key_state = S0;
        endcase
    end
    
// ------------------------------------------------------------------------
    
    // 供功能3使用的脉冲信号
    reg [31:0] counter2; // 计数器
    reg pulse2; // 脉冲信号
   
    // 获取脉冲信号
    always_ff @(posedge clk or posedge rst) begin
        if (rst) begin
            counter2 <= 0;
            pulse2 <= 0;
        end else if (key_negedge) begin // 在 key 下降沿时重置计数器
            counter2 <= 0;
            pulse2 <= 0;
        end else begin
            if (counter2 == PULSE_PERIOD - 1) begin
                counter2 <= 0; // 重置计数器
                pulse2 <= 1; // 翻转脉冲信号
            end else begin
                counter2 <= counter2 + 1; // 计数器加一
                pulse2 <= 0; // 保持脉冲信号为低电平
            end
        end
    end
    
// ------------------------------------------------------------------------
    
    // 处理 key_state 更新
    always_ff @(posedge clk or posedge rst) begin
        if (rst) begin
            key_state <= S0;
        end else if (key_negedge && key_state == S0) begin
            key_state <= next_key_state;
        end else if (pulse2 && key_state == S1) begin
            key_state <= next_key_state;
        end
    end
    
// ------------------------------------------------------------------------
    
    reg [7:0] lfsr;
    reg [7:0] next_lfsr = {lfsr[6:0], lfsr[7] ^ lfsr[5] ^ lfsr[4] ^ lfsr[3]};

    // 处理 lfsr 更新
     always_ff @(posedge clk or posedge rst) begin
         if (rst) begin
         // 当复位信号为高时，将LFSR重置
             lfsr <= 8'b1;
         end else if (pulse) begin
         // 计算新的LFSR值
             lfsr <= next_lfsr;
         end
     end
     
// ------------------------------------------------------------------------
     
     reg [3:0] led_i;
     reg [3:0] next_led;
     
     // 处理 next_led 赋值
     always_comb begin
        case (key_state)
            S0: next_led = next_lfsr;
            S1: next_led = led_i;
            S2: next_led = led_i;
            S3: next_led = led_i;
            default: next_led = led_i;
        endcase
     end

// ------------------------------------------------------------------------
     
     // 处理 led_i 更新
     always_ff @(posedge clk or posedge rst) begin
        if (rst) begin
            led_i <= 0;
        end else if (pulse) begin
            led_i <= next_led;
        end
     end

// ------------------------------------------------------------------------
     
     reg [2:0] cnt = (sw[3] == led_i[3]) + (sw[2] == led_i[2]) + (sw[1] == led_i[1]) + (sw[0] == led_i[0]);
     
     reg [3:0] dis_i;
     reg [3:0] next_dis = 4 - cnt;
     
     // 处理 dis_i 更新
     always_ff @(posedge clk or posedge rst) begin
        if (rst) begin
            dis_i <= 0;
        end else begin
            dis_i <= next_dis;
        end
     end

// ------------------------------------------------------------------------
     
     // 处理 led, crc, dis 赋值
     assign led = rst ? 0 : ((key_state == S2) ? 0 : led_i);
     assign crc = rst ? 0 : ((key_state == S2 && sw == led_i) ? 1 : 0);
     assign dis = rst ? 0 : ((key_state == S2) ? dis_i : 0);
    
endmodule
```