verilog中等难度设计实践与ALU设计

news/2025/2/23 17:49:05

Verilog中等难度部分设计实践(含ALU算术逻辑单元的设计)

verilog的中等部分根据Deepseek给出的大纲理应包括时序逻辑verilog设计实践和组合逻辑verilog组合设计实践两个部分。

时序逻辑verilog设计和实践

在 Verilog 中,时序逻辑通常通过 always 块实现,并依赖时钟信号和复位信号。

关键点如下:

  • 敏感列表:必须包含时钟(如 posedge clk)和可能的复位信号。
  • 非阻塞赋值(<=:时序逻辑中使用 <= 避免竞争条件,确保并行更新。
  • 复位策略:
    • 同步复位:复位信号仅在时钟边沿生效。
    • 异步复位:复位信号立即生效(如 posedge rst)。

时序逻辑的设计要点如下:

时序逻辑设计要点

(1) 时钟域(Clock Domain)
  • 所有时序逻辑需明确归属某个时钟域。
  • 避免跨时钟域直接传递信号(需同步器处理)。
(2) 复位策略
  • 同步复位:更安全,但依赖时钟。
  • 异步复位:立即生效,但可能产生毛刺。
(3) 时序约束
  • 需在综合工具中指定时钟频率、建立时间(Setup Time)和保持时间(Hold Time)。
  • 使用 # 延迟在仿真中模拟时序行为(仅用于验证)。

时序逻辑的设计实践与验证

寄存器的设计与验证

下面的时序逻辑实践中实现了一个数字寄存器的设计和验证:

首先是register.v中的代码:

// 文件名: register.v
module register(
    input clk,
    input rst,
    input [7:0] d,
    output reg [7:0] q
);
always @(posedge clk or posedge rst)begin
    if (rst)begin
        q<=8'h00;
    end else begin
        q<=d;
    end
end
endmodule

其次是tb_register.v中的代码:

// 文件名: tb_register.v
`timescale 1ns/1ns  // 定义仿真时间单位

module tb_register;

// 定义信号
reg clk;
reg rst;
reg [7:0] d;
wire [7:0] q;

// 实例化被测模块
register u_register (
    .clk(clk),
    .rst(rst),
    .d(d),
    .q(q)
);

// 生成时钟信号(周期10ns)
initial begin
    clk = 0;
    forever #5 clk = ~clk;  // 每5ns翻转一次,形成50MHz时钟
end

// 生成测试激励
initial begin
    // 初始化信号
    rst = 0;
    d = 8'h00;

    // 测试步骤1:复位测试
    rst = 1;     // 复位有效
    #10;         // 等待10ns(1个时钟周期)
    rst = 0;     // 复位释放
    d = 8'hAA;   // 输入数据变化

    // 测试步骤2:正常数据传输
    #10;         // 等待时钟上升沿
    d = 8'h55;   // 输入新数据

    // 测试步骤3:继续观察
    #10;
    d = 8'hFF;

    // 仿真结束
    #20 $finish;
end

// 监控信号变化
initial begin
    $monitor("Time=%t: rst=%b, d=0x%h, q=0x%h", $time, rst, d, q);
    // 生成波形文件(用于Verdi/ModelSim等工具)
    $dumpfile("wave.vcd");
    $dumpvars(0, tb_register);
end

endmodule

验证的结果如下:

在这里插入图片描述

实现的寄存器原理是,在时钟的上升沿的时候,假如rst信号为1则使得q=8’h00(一个十六进制等于四位的二进制),否则为q=d.q是输出量,d是输入量。在实际测试所得的波形图中也确实满足。在WSL+iverilog测试环境中的命令如下:

iverilog -o register.out register.v tb_register.v
vvp register.out
gtkwave wave.vcd

计数器的设计与验证

实现的具体代码和test_bench代码分别如下:

// 文件名: counter.v
module counter(
    input clk,
    input rst_n,
    input en,
    output reg [3:0] count
);
always @(posedge clk or negedge rst_n)begin
    if (!rst_n)begin
        count<=4'b0000;
    end else if (en )begin
        count<=count+1;
    end

end
endmodule
/*
实现的功能是设计了一个计数器,假如rst_n为0,那么count为0,否则如果en为1,那么count会一直加1,直到count为15,然后count会重新从0开始计数。

*/
// 文件名: tb_counter.v
`timescale 1ns/1ns

module tb_counter;

// 定义信号
reg clk;
reg rst_n;
reg en;
wire [3:0] count;

// 实例化被测模块
counter u_counter (
    .clk(clk),
    .rst_n(rst_n),
    .en(en),
    .count(count)
);

// 生成时钟信号(周期20ns)
initial begin
    clk = 0;
    forever #10 clk = ~clk; // 50MHz时钟
end

// 生成测试激励
initial begin
    // 初始化信号
    rst_n = 1;
    en = 0;

    // 测试步骤1:复位测试
    rst_n = 0;      // 复位有效(低电平)
    #20;
    rst_n = 1;      // 复位释放
    en = 1;         // 使能计数

    // 测试步骤2:正常计数(观察从0到15)
    #320;           // 等待16个时钟周期(16*20ns=320ns)

    // 测试步骤3:关闭使能,观察是否停止计数
    en = 0;
    #40;

    // 测试步骤4:重新开启使能
    en = 1;
    #40;

    // 仿真结束
    #20 $finish;
end

// 监控信号变化
initial begin
    $monitor("Time=%t: rst_n=%b, en=%b, count=%d", $time, rst_n, en, count);
    // 生成波形文件
    $dumpfile("wave.vcd");
    $dumpvars(0, tb_counter);
end

endmodule

实测发现这是比hspice好用很多的硬件解释语言,假如用hspice来写一个计数器不知道要写多少个晶体管。当然了二者 是不同层级的设计工具。

验证结果如下所示:

在这里插入图片描述

编译与仿真和波形观察命令如下:

iverilog -o counter.out counter.v tb_counter.v
vvp counter.out
gtkwave wave.vcd

组合逻辑设计与实践

Verilog中的组合逻辑是实现数字电路的基础,其核心特点是输出仅由当前输入决定,无记忆功能。verilog组合逻辑实现的方法有assign 连续赋值实现和always逻辑块实现。多说无益,下面是几个例子。

四选一多路选择器的设计和验证

module mux_4to1 (
    input [3:0] data_in,  // 4位输入信号:data_in[3], data_in[2], ..., data_in[0]
    input [1:0] sel,      // 2位选择信号
    output reg out        // 输出信号
);

// 组合逻辑:根据 sel 选择对应的输入位
always @(*) begin
    case (sel)//根据sel作case分支选择
        2'b00: out = data_in[0];  // sel=00 时选择第0位
        2'b01: out = data_in[1];  // sel=01 时选择第1位
        2'b10: out = data_in[2];  // sel=10 时选择第2位
        2'b11: out = data_in[3];  // sel=11 时选择第3位
        default: out = 1'bx;      // 处理未定义的 sel 值(避免锁存器)
    endcase
end

endmodule
//命名为mux_4to1.v
`timescale 1ns/1ns  // 定义仿真时间单位

module tb_mux_4to1();

// 定义测试信号
reg [3:0] data_in;  // 输入数据
reg [1:0] sel;      // 选择信号
wire out;           // 输出信号

// 实例化被测模块
mux_4to1 u_mux (
    .data_in(data_in),
    .sel(sel),
    .out(out)
);

// 生成测试激励
initial begin
    // 初始化信号
    data_in = 4'b0000;
    sel = 2'b00;
    
    // 遍历所有可能的 sel 值,并测试不同输入
    // 测试用例 1:data_in = 4'b1010
    data_in = 4'b1010;
    sel = 2'b00;  #10;  // 等待10ns,预期 out = data_in[0] = 0
    sel = 2'b01;  #10;  // 预期 out = data_in[1] = 1
    sel = 2'b10;  #10;  // 预期 out = data_in[2] = 0
    sel = 2'b11;  #10;  // 预期 out = data_in[3] = 1

    // 测试用例 2:data_in = 4'b0101
    data_in = 4'b0101;
    sel = 2'b00;  #10;  // 预期 out = 1
    sel = 2'b01;  #10;  // 预期 out = 0
    sel = 2'b10;  #10;  // 预期 out = 1
    sel = 2'b11;  #10;  // 预期 out = 0

    // 结束仿真
    $display("Simulation completed!");
    $finish;
end

// 实时打印信号变化(可选)
initial begin
    $monitor("Time = %t | sel = %b | data_in = %b | out = %b", 
             $time, sel, data_in, out);
end

endmodule
//命名为tb_mux_4to1.v

仿真所得结果如下,验证成功多路4选1功能,根据输入的sel值输出选择后的值。

Time = 0 | sel = 00 | data_in = 1010 | out = 0
Time = 10 | sel = 01 | data_in = 1010 | out = 1
Time = 20 | sel = 10 | data_in = 1010 | out = 0
Time = 30 | sel = 11 | data_in = 1010 | out = 1
Time = 40 | sel = 00 | data_in = 0101 | out = 1
Time = 50 | sel = 01 | data_in = 0101 | out = 0
Time = 60 | sel = 10 | data_in = 0101 | out = 1
Time = 70 | sel = 11 | data_in = 0101 | out = 0
Simulation completed!

编译仿真命令如下:

iverilog -o mux.out mux_4to1.v tb_mux_4to1.v
vvp mux.out

ALU模块的设计与验证

首先需要知道什么是ALU模块,ALU模块(Arithmetic Logic Unit,算术逻辑单元)是计算机CPU的核心部件之一,负责执行所有算术运算(如加减乘除)和逻辑运算(如与、或、非、移位等)。它是CPU的“计算大脑”,直接影响处理器的性能。

从0到1构建计算机(3/12)--组合逻辑芯片:逻辑门、加法器、ALU_设计做cpu的逻辑芯片-CSDN博客

ALU的核心功能

运算类型典型操作示例
算术运算加法、减法、乘法、增量等A + BA - 1
逻辑运算与、或、非、异或、移位等A & BA << 1
比较运算相等判断、大小比较A == BA > B

ALU的组成结构

  1. 算术单元
    • 加法器、减法器、乘法器等
    • 支持带进位(Carry)的运算
  2. 逻辑单元
    • 与门(AND)、或门(OR)、异或门(XOR)等
    • 移位器(左移/右移)
  3. 标志寄存器
    • Zero(零标志):运算结果是否为0
    • Carry(进位标志):加法溢出或减法借位
    • Overflow(溢出标志):有符号运算溢出
    • Sign(符号标志):结果的最高位(正负)

ALU的工作流程

  1. 输入操作数:从寄存器或内存中读取数据(如 AB)。
  2. 接收控制信号:由控制单元(Control Unit)指定操作类型(如 ADDAND)。
  3. 执行运算:根据操作码计算结果。
  4. 输出结果与标志位:将结果写回寄存器/内存,并更新标志位。

CPU内部组成及原理 - 走看看

在CPU架构中可见算术逻辑单元处于核心位置,或者可以说算术逻辑单元本身就是计算机的核心构件。

ALU的verilog代码和ALU_tb的verilog代码如下所示:

module ALU (
    input [3:0] A,          // 操作数A
    input [3:0] B,          // 操作数B
    input [2:0] opcode,     // 操作码(定义见下方)
    input cin,              // 进位输入(用于加减法)
    output reg [3:0] F,     // 结果输出
    output reg cout,        // 进位/借位标志
    output zero,            // 零标志(F=0时置1)
    output overflow,        // 溢出标志(有符号运算溢出)
    output sign             // 符号位(F的最高位)
);

// 操作码定义
parameter 
    OP_ADD = 3'b000,    // A + B + cin
    OP_SUB = 3'b001,    // A - B - cin
    OP_AND = 3'b010,    // A & B
    OP_OR  = 3'b011,    // A | B
    OP_XOR = 3'b100,    // A ^ B
    OP_NOT = 3'b101,    // ~A
    OP_SHL = 3'b110,    // 逻辑左移(A << 1,最低位补0)
    OP_SHR = 3'b111;    // 逻辑右移(A >> 1,最高位补0)

// 中间信号
wire [4:0] add_result;  // 加法器结果(包含进位)
wire [4:0] sub_result;  // 减法器结果(包含借位)

// 零标志(组合逻辑)
assign zero = (F == 4'b0000);
// 符号位(直接取最高位)
assign sign = F[3];
// 溢出标志(有符号加减法溢出判断)
assign overflow = (opcode == OP_ADD) ? (A[3] & B[3] & ~F[3]) | (~A[3] & ~B[3] & F[3]) :
                  (opcode == OP_SUB) ? (A[3] & ~B[3] & ~F[3]) | (~A[3] & B[3] & F[3]) : 1'b0;

// 加法器(A + B + cin)
assign add_result = A + B + cin;
// 减法器(A - B - cin,转换为补码加法)
assign sub_result = A - B - cin;

// ALU核心逻辑
always @(*) begin
    case (opcode)//根据设定的opcode来执行指令,opcode包括与,或等逻辑运算和加减等算术运算
        OP_ADD: begin
            F = add_result[3:0];
            cout = add_result[4];
        end
        OP_SUB: begin
            F = sub_result[3:0];
            cout = sub_result[4]; // 借位标志(实际为~cout)
        end
        OP_AND: {cout, F} = {1'b0, A & B};
        OP_OR:  {cout, F} = {1'b0, A | B};
        OP_XOR: {cout, F} = {1'b0, A ^ B};
        OP_NOT: {cout, F} = {1'b0, ~A};
        OP_SHL: {cout, F} = {A[3], A[2:0], 1'b0}; // 左移,cout为移出的最高位
        OP_SHR: {cout, F} = {A[0], 1'b0, A[3:1]}; // 右移,cout为移出的最低位
        default: {cout, F} = {1'b0, 4'b0000};
    endcase
end

endmodule

module ALU_tb;

// 输入信号
reg [3:0] A;
reg [3:0] B;
reg [2:0] opcode;
reg cin;
// 输出信号
wire [3:0] F;
wire cout, zero, overflow, sign;
    parameter 
        OP_ADD = 3'b000,
        OP_SUB = 3'b001,
        OP_AND = 3'b010,
        OP_OR  = 3'b011,
        OP_XOR = 3'b100,
        OP_NOT = 3'b101,
        OP_SHL = 3'b110,
        OP_SHR = 3'b111;

// 实例化ALU
ALU uut (
    .A(A),
    .B(B),
    .opcode(opcode),
    .cin(cin),
    .F(F),
    .cout(cout),
    .zero(zero),
    .overflow(overflow),
    .sign(sign)
);

// 测试用例
initial begin
    // 初始化输入
    A = 4'b0000;
    B = 4'b0000;
    opcode = 3'b000;
    cin = 0;

    // 测试加法:5 + 3 + 0 = 8(无进位)
    #10 A = 4'b0101; B = 4'b0011; opcode = OP_ADD; cin = 0;
    #10 $display("ADD: 5 + 3 = %d, Cout=%b, OF=%b", F, cout, overflow);

    // 测试带进位加法:5 + 3 + 1 = 9(无溢出)
    #10 cin = 1;
    #10 $display("ADD+C: 5 + 3 +1 = %d, Cout=%b", F, cout);

    // 测试减法:8 - 3 - 0 = 5
    #10 A = 4'b1000; B = 4'b0011; opcode = OP_SUB; cin = 0;
    #10 $display("SUB: 8 -3 = %d, Cout=%b", F, cout);

    // 测试溢出:7 + 7 = 14(有符号溢出)
    #10 A = 4'b0111; B = 4'b0111; opcode = OP_ADD; cin = 0;
    #10 $display("ADD Overflow: F=%b, OF=%b", F, overflow);

    // 测试逻辑操作:A=1010, B=1100
    #10 A = 4'b1010; B = 4'b1100;
    #10 opcode = OP_AND; #10 $display("AND: %b & %b = %b", A, B, F);
    #10 opcode = OP_OR;  #10 $display("OR : %b | %b = %b", A, B, F);
    #10 opcode = OP_XOR; #10 $display("XOR: %b ^ %b = %b", A, B, F);
    #10 opcode = OP_NOT; #10 $display("NOT: ~%b = %b", A, F);

    // 测试移位
    #10 A = 4'b1011;
    #10 opcode = OP_SHL; #10 $display("SHL: 1011 << 1 = %b, Cout=%b", F, cout);
    #10 opcode = OP_SHR; #10 $display("SHR: 1011 >> 1 = %b, Cout=%b", F, cout);

    // 结束仿真
    #10 $finish;
end

// 注意:OP_ADD等参数需在测试平台中重新声明或通过`include引入
// 此处简化处理,实际需补充参数定义

endmodule

验证结果如下:

ADD: 5 + 3 = 8, Cout=0, OF=1
ADD+C: 5 + 3 +1 = 9, Cout=0
SUB: 8 -3 = 5, Cout=0
ADD Overflow: F=1110, OF=1
AND: 1010 & 1100 = 1000
OR : 1010 | 1100 = 1110
XOR: 1010 ^ 1100 = 0110
NOT: ~1010 = 0101
SHL: 1011 << 1 = 0110, Cout=1
SHR: 1011 >> 1 = 0101, Cout=1

编译和仿真命令如下所示:

iverilog -o ALU.out ALU.v ALU_tb.v
vvp ALU.out

我其实本人是比较好奇现代数字系统设计的流程的,经过查询得知在verilog代码写好后并综合后是有专门的工具可以将其映射到物理版图构造并制作的。

大体流程如下:

1. 逻辑综合(Synthesis)

  • 工具作用

    :综合工具(如Synopsys Design Compiler)将Verilog代码转换为门级网表(Gate-level Netlist)。

    • 例如,assign F = A + B 可能被转换为4位行波进位加法器(Ripple Carry Adder)的AND/OR门组合。
  • 优化目标:根据约束(速度、面积、功耗)优化电路结构。


2. 布局布线(Place & Route)

  • 物理实现

    :将门级网表映射到具体的硬件单元(如FPGA的查找表LUT或ASIC的标准单元库)。

    • FPGA:配置可编程逻辑块(CLB)和连线资源。
    • ASIC:生成掩膜版图,定义晶体管位置和金属层连线。

3. 硬件生成

  • FPGA:通过比特流(Bitstream)文件配置芯片。
  • ASIC:经过光刻、蚀刻等半导体工艺制造。

http://www.niftyadmin.cn/n/5863635.html

相关文章

计算机考研复试上机07

14、数据结构 1)二叉树 1.常用操作 struct TreeNode{int data;TreeNode *leftChild;TreeNode *rightChild; }; //前序遍历 void PreOrder(TreeNode *root){if(root == NULL) return;visit(root->data);PreOrder(root->leftChild);PreOrder(root->rightChild);ret…

提效10倍:基于Paimon+Dolphin湖仓一体新架构在阿里妈妈品牌业务探索实践

1. 业务背景 阿里妈妈品牌广告数据包括投放引擎、下发、曝光、点击等日志&#xff0c;面向运筹调控、算法特征、分析报表、诊断监控等应用场景&#xff0c;进行了品牌数仓能力建设。随着业务发展&#xff0c;基于Lambda架构的数仓开发模式&#xff0c;缺陷日益突出&#xff1a;…

Windows 上编译 mebedtls 的鸿蒙库

mebedtls 地址&#xff1a;https://github.com/Mbed-TLS/mbedtls 准备工作&#xff1a; clone mebedtls 仓库到本地(tag: mbedtls-2.26.0)鸿蒙工具链(SDK version: v5.0.5) 编译文件修改&#xff1a; 对 CMakeLists.txt 进行修改&#xff0c;主要是关闭了以下几个选项 ENABLE_P…

Spring Boot Validation 接口校验:从零到掌握

在开发 Web 应用时&#xff0c;数据校验是不可忽视的一部分。无论是注册用户信息、提交表单数据&#xff0c;还是处理业务逻辑&#xff0c;数据的有效性和完整性都需要得到保证。Spring Boot 提供了强大的验证功能&#xff0c;基于 Hibernate Validator 框架&#xff0c;通过注…

11.Docker 之分布式仓库 Harbor

Docker 之分布式仓库 Harbor Docker 之分布式仓库 Harbor1. Harbor 组成2. 安装 Harbor Docker 之分布式仓库 Harbor Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器&#xff0c;由 VMware 开源&#xff0c;其通过添加一些企业必需的功能特性&#xff0c;例…

Spring Boot 集成 T-io 实现客户端服务器通信

Spring Boot 集成 T-io 实现客户端服务器通信 本文详细介绍如何在 Spring Boot 项目中集成 T-io 框架&#xff0c;实现客户端与服务器之间的通信&#xff0c;并支持组聊、群聊和私聊功能。通过本文&#xff0c;您能够全面了解 T-io core 的使用方法&#xff0c;以及如何正确启…

从零开始学 Rust:基本概念——变量、数据类型、函数、控制流

文章目录 Variables and MutabilityShadowing Data TypesScalar TypesCompound Types FunctionsFunction Parameters CommentsControl FlowRepetition with Loops Variables and Mutability fn main() {let mut x 5;println!("The value of x is: {}", x);x 6;pri…

蓝桥备赛(一)- C++入门(上)

一、工具安装 Dev-C安装&#xff1a;https://www.bilibili.com/video/BV1kC411G7CS 一般比赛会用到Dev-C, 但是Dev-C还是有自身的局限性 &#xff0c; 后续的博客学习中 &#xff0c; 必要的时候 &#xff0c; 会使用VS2022 &#xff0c; 下面是VS2022的安装和使用教程。 VS202…