Verilog的几个实验常见代码及分析

  1. 四位加法器的实现(全加器和半加器)
  • 半加器(Half Adder)
    半加器是数字电路中用于两个单个位二进制数相加的电路,产生一个和位(sum)和一个进位位(carry)。
    半加器Verilog代码
    verilog

    module half_adder (
        input wire a,      // 输入位 a
        input wire b,      // 输入位 b
        output wire sum,   // 和
        output wire carry  // 进位
    );
        assign sum = a ^ b;     // 异或操作生成和
        assign carry = a & b;   // 与操作生成进位
    endmodule


二、全加器(Full Adder)
全加器是数字电路中用于三个单个位二进制数相加的电路,产生一个和位(sum)和一个进位位(carry)。

全加器Verilog代码
verilog
复制代码
module full_adder (
    input wire a,      // 输入位 a
    input wire b,      // 输入位 b
    input wire cin,    // 输入进位
    output wire sum,   // 和
    output wire carry  // 进位
);
    assign sum = a ^ b ^ cin;             // 异或操作生成和
    assign carry = (a & b) | (b & cin) | (a & cin);  // 与和或操作生成进位
endmodule


三、四位全加器(4-bit Ripple Carry Adder)
四位全加器是由多个全加器串联组成的,用于对两个四位二进制数进行相加。

四位全加器Verilog代码
verilog

module four_bit_adder (
    input wire [3:0] a,    // 4位输入 a
    input wire [3:0] b,    // 4位输入 b
    input wire cin,        // 输入进位
    output wire [3:0] sum, // 4位和
    output wire carry      // 输出进位
);
    wire c1, c2, c3; // 内部进位信号

    // 实例化全加器模块
    full_adder fa0 (
        .a(a[0]),
        .b(b[0]),
        .cin(cin),
        .sum(sum[0]),
        .carry(c1)
    );
    full_adder fa1 (
        .a(a[1]),
        .b(b[1]),
        .cin(c1),
        .sum(sum[1]),
        .carry(c2)
    );
    full_adder fa2 (
        .a(a[2]),
        .b(b[2]),
        .cin(c2),
        .sum(sum[2]),
        .carry(c3)
    );
    full_adder fa3 (
        .a(a[3]),
        .b(b[3]),
        .cin(c3),
        .sum(sum[3]),
        .carry(carry)
    );
endmodule

代码解释
半加器:

输入:a 和 b
输出:sum(和)和 carry(进位)
异或操作 (^) 用于生成和,& 操作用于生成进位。
全加器:

输入:a、b 和 cin(输入进位)
输出:sum(和)和 carry(进位)
异或操作 (^) 用于生成和,组合的与和或操作 (& 和 |) 用于生成进位。
四位全加器:

输入:两个四位二进制数 a 和 b,以及一个输入进位 cin
输出:四位和 sum 和输出进位 carry
使用4个全加器模块实例化,实现串联进位逻辑。
内部进位信号 c1、c2 和 c3 用于连接各个位的进位。

  1. Verilog编程实现奇数分频器和偶数分频器
  • 奇数分频器
    奇数分频器的实现通常需要使用计数器和触发器来生成所需的分频信号。假设我们需要实现一个3分频器,即将输入时钟频率除以3。

module odd_divider (

    input wire clk_in,    // 输入时钟

    input wire rst_n,     // 复位信号,低电平有效

    output reg clk_out    // 输出时钟

);

    reg [1:0] counter;    // 2位计数器

    always @(posedge clk_in or negedge rst_n) begin

        if (!rst_n)

            counter <= 2'b0;

        else

            counter <= counter + 1'b1;

    end

    always @(posedge clk_in or negedge rst_n) begin

        if (!rst_n)

            clk_out <= 1'b0;

        else if (counter == 2'b10)  // 当计数器值为2时,翻转输出时钟

            clk_out <= ~clk_out;

    end

endmodule

  • 偶数分频器
    偶数分频器的实现相对简单,可以通过一个计数器和简单的逻辑操作来实现。假设我们需要实现一个4分频器,即将输入时钟频率除以4。

module even_divider (

    input wire clk_in,    // 输入时钟

    input wire rst_n,     // 复位信号,低电平有效

    output reg clk_out    // 输出时钟

);

    reg [1:0] counter;    // 2位计数器

    always @(posedge clk_in or negedge rst_n) begin

        if (!rst_n)

            counter <= 2'b0;

        else if (counter == 2'b11)  // 当计数器值为3时,复位计数器

            counter <= 2'b0;

        else

            counter <= counter + 1'b1;

    end

    always @(posedge clk_in or negedge rst_n) begin

        if (!rst_n)

            clk_out <= 1'b0;

        else if (counter == 2'b11)  // 当计数器值为3时,翻转输出时钟

            clk_out <= ~clk_out;

    end

endmodule

代码解释

奇数分频器(3分频器):
输入信号:
clk_in:输入时钟信号
rst_n:复位信号,低电平有效
输出信号:
clk_out:输出分频时钟信号
实现原理:
使用一个2位计数器对输入时钟进行计数。
当计数器值达到2时,翻转输出时钟信号,并继续计数。

偶数分频器(4分频器):
输入信号:
clk_in:输入时钟信号
rst_n:复位信号,低电平有效
输出信号:
clk_out:输出分频时钟信号

实现原理:
使用一个2位计数器对输入时钟进行计数。
当计数器值达到3时,翻转输出时钟信号,并将计数器复位。
设计注意事项
复位信号:复位信号用于初始化计数器和输出时钟信号。在设计中,复位信号通常是低电平有效(Active Low)。
计数器位数:计数器的位数应根据分频系数确定。例如,3分频和4分频都可以使用2位计数器。如果分频系数更大,则需要更多位的计数器。
仿真验证:在综合和下载到硬件之前,建议使用仿真工具(如ModelSim)对分频器进行功能验证,确保设计的正确性。

  1. 移位寄存器

串行输入/串行输出(SISO)移位寄存器

module siso_shift_register (

    input wire clk,      // 时钟信号

    input wire rst_n,    // 复位信号,低电平有效

    input wire s_in,     // 串行输入数据

    output reg s_out     // 串行输出数据

);

    reg [3:0] shift_reg; // 4位移位寄存器

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n) begin

            shift_reg <= 4'b0;

            s_out <= 1'b0;

        end else begin

            shift_reg <= {shift_reg[2:0], s_in}; // 数据左移,s_in进入最低位

            s_out <= shift_reg[3];               // 最高位输出

        end

    end

endmodule

串行输入/并行输出(SIPO)移位寄存器

module sipo_shift_register (

    input wire clk,       // 时钟信号

    input wire rst_n,     // 复位信号,低电平有效

    input wire s_in,      // 串行输入数据

    output reg [3:0] p_out // 并行输出数据

);

    reg [3:0] shift_reg;  // 4位移位寄存器

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            shift_reg <= 4'b0;

        else

            shift_reg <= {shift_reg[2:0], s_in}; // 数据左移,s_in进入最低位

    end

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            p_out <= 4'b0;

        else

            p_out <= shift_reg; // 并行输出

    end

endmodule

并行输入/串行输出(PISO)移位寄存器

module piso_shift_register (

    input wire clk,        // 时钟信号

    input wire rst_n,      // 复位信号,低电平有效

    input wire load,       // 加载使能信号

    input wire [3:0] p_in, // 并行输入数据

    output reg s_out       // 串行输出数据

);

    reg [3:0] shift_reg;   // 4位移位寄存器

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            shift_reg <= 4'b0;

        else if (load)

            shift_reg <= p_in; // 并行加载数据

        else

            shift_reg <= shift_reg >> 1; // 数据右移

    end

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            s_out <= 1'b0;

        else

            s_out <= shift_reg[0]; // 最低位输出

    end

endmodule

并行输入/并行输出(PIPO)移位寄存器

module pipo_shift_register (

    input wire clk,        // 时钟信号

    input wire rst_n,      // 复位信号,低电平有效

    input wire load,       // 加载使能信号

    input wire [3:0] p_in, // 并行输入数据

    output reg [3:0] p_out // 并行输出数据

);

    reg [3:0] shift_reg;   // 4位移位寄存器

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            shift_reg <= 4'b0;

        else if (load)

            shift_reg <= p_in; // 并行加载数据

        else

            shift_reg <= shift_reg; // 保持当前数据

    end

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            p_out <= 4'b0;

        else

            p_out <= shift_reg; // 并行输出

    end

endmodule

  1. meanly型和moore型状态机

在 Moore 型状态机中,输出仅依赖于当前状态,因此状态转换需要考虑输入信号,并且状态机的输出在状态转换完成后更新。

module moore_fsm_11 (

    input wire clk,       // 时钟信号

    input wire rst_n,     // 复位信号,低电平有效

    input wire din,       // 数据输入

    output reg dout       // 数据输出

);

    typedef enum reg [1:0] {IDLE, S1, S2} state_t;

    state_t state, next_state;

    // 状态转移

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            state <= IDLE;

        else

            state <= next_state;

    end

    // 下一状态逻辑

    always @(*) begin

        case (state)

            IDLE: next_state = din ? S1 : IDLE;

            S1: next_state = din ? S2 : IDLE;

            S2: next_state = din ? S2 : IDLE;

            default: next_state = IDLE;

        endcase

    end

    // 输出逻辑

    always @(state) be

在 Mealy 型状态机中,输出依赖于当前状态和当前输入,因此状态转换和输出生成可以在同一时钟周期内完成

module mealy_fsm_11 (

    input wire clk,       // 时钟信号

    input wire rst_n,     // 复位信号,低电平有效

    input wire din,       // 数据输入

    output reg dout       // 数据输出

);

    typedef enum reg [1:0] {IDLE, S1} state_t;

    state_t state, next_state;

    // 状态转移

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            state <= IDLE;

        else

            state <= next_state;

    end

    // 下一状态逻辑

    always @(*) begin

        case (state)

            IDLE: next_state = din ? S1 : IDLE;

            S1: next_state = din ? S1 : IDLE;

            default: next_state = IDLE;

        endcase

    end

    // 输出逻辑

    always @(*) begin

        case (state)

            IDLE: dout = 1'b0;

            S1: dout = din ? 1'b1 : 1'b0;

            default: dout = 1'b0;

        endcase

    end

endmodule

Moore 型状态机:

输出仅依赖于当前状态,通常在状态转换完成后更新输出。
状态机实现相对简单,但有一个时钟周期的延迟。
Mealy 型状态机:

输出依赖于当前状态和当前输入,能够更快响应输入变化。
状态机实现稍复杂,但没有额外的时钟周期延迟。

Moore 型状态机
状态定义:

IDLE: 初始状态,没有检测到“1”。
S1: 检测到一个“1”。
S2: 检测到两个连续的“1”,输出为“1”。
状态转移:

在 IDLE 状态,如果输入 din 为“1”,则转移到 S1 状态,否则保持在 IDLE 状态。
在 S1 状态,如果输入 din 为“1”,则转移到 S2 状态,否则返回 IDLE 状态。
在 S2 状态,如果输入 din 为“1”,则保持在 S2 状态,否则返回 IDLE 状态。
输出逻辑:

输出 dout 在 S2 状态时为“1”,在其他状态时为“0”。
Mealy 型状态机
状态定义:

IDLE: 初始状态,没有检测到“1”。
S1: 检测到一个“1”。
状态转移:

在 IDLE 状态,如果输入 din 为“1”,则转移到 S1 状态,否则保持在 IDLE 状态。
在 S1 状态,如果输入 din 为“1”,则保持在 S1 状态,否则返回 IDLE 状态。
输出逻辑:

在 S1 状态时,如果输入 din 为“1”,则输出 dout 为“1”,否则为“0”。
在 IDLE 状态时,输出 dout 为“0

  1. 按键消抖以及毛刺的解决

利用Verilog实现按键的消抖;同时写出解决毛刺的方法,并举例说明。

按键消抖可以通过对按键信号进行采样和滤波来实现。常见的方法是使用计数器对稳定的按键状态进行确认。

module debounce (

    input wire clk,        // 时钟信号

    input wire rst_n,      // 复位信号,低电平有效

    input wire button_in,  // 按键输入

    output reg button_out  // 消抖后的按键输出

);

    reg [15:0] counter;    // 16位计数器

    reg button_sync_0, button_sync_1;  // 同步寄存器

    // 按键输入信号同步到时钟域

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n) begin

            button_sync_0 <= 0;

            button_sync_1 <= 0;

        end else begin

            button_sync_0 <= button_in;

            button_sync_1 <= button_sync_0;

        end

    end

    // 按键消抖逻辑

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n) begin

            counter <= 0;

            button_out <= 0;

        end else if (button_sync_1 == button_out) begin

            counter <= 0;

        end else begin

            counter <= counter + 1;

            if (counter == 16'hFFFF) begin

                counter <= 0;

                button_out <= button_sync_1;

            end

        end

    end

endmodule

毛刺问题可以通过滤波器或锁存器来解决,这里我们采用锁存器的方法。在毛刺出现时锁存器能保持前一个稳定的状态,从而过滤掉毛刺信号。

module glitch_filter (

    input wire clk,        // 时钟信号

    input wire rst_n,      // 复位信号,低电平有效

    input wire signal_in,  // 输入信号

    output reg signal_out  // 过滤毛刺后的输出信号

);

    reg signal_sync_0, signal_sync_1;  // 同步寄存器

    // 输入信号同步到时钟域

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n) begin

            signal_sync_0 <= 0;

            signal_sync_1 <= 0;

        end else begin

            signal_sync_0 <= signal_in;

            signal_sync_1 <= signal_sync_0;

        end

    end

    // 锁存器实现毛刺过滤

    always @(posedge clk or negedge rst_n) begin

        if (!rst_n)

            signal_out <= 0;

        else if (signal_sync_1 == signal_out)

            signal_out <= signal_out;

        else

            signal_out <= signal_sync_1;

    end

endmodule

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值