verilog测试平台设计与verilog的synthesis

verilog测试平台的设计与verilog的synthesis

verilog模块代码在写好后,还需要进行调试和仿真,这有些类似于hspice中的测试文件,也要包括声明,激励定义,结果处理。Testbench(测试平台)是数字电路设计验证中的核心工具,用于模拟和验证硬件设计(DUT, Design Under Test)的功能正确性。其核心原理是通过仿真环境生成输入激励、监测输出响应,并与预期结果对比。以下是编写原理的详细说明:

一个典型的 Testbench 包含以下模块:

  • DUT 实例化:将被测设计连接到 Testbench。
  • 激励生成模块:模拟输入信号(如时钟、复位、数据等)。
  • 监控模块:实时捕获 DUT 的输出信号。
  • 自动检查模块:对比输出结果与预期值(Golden Model)。

DUT实例化

DUT就是design under test,DUT模块在被设计好后需要在testbench中进行实例化。DUT示例化的示例代码如下:

verilog 复制代码
module counter (//模块定义
  input clk,       // 输入端口:时钟
  input reset,     // 输入端口:复位
  input enable,    // 输入端口:使能
  output [3:0] count  // 输出端口:计数值
);
module testbench;
  reg tb_clk;      // 测试平台生成的时钟信号(reg类型,驱动DUT输入)
  reg tb_reset;    // 测试平台生成的复位信号
  reg tb_enable;
  wire [3:0] tb_count;  // 接收DUT的输出(wire类型)
endmodule
counter dut (//实例化,通常是模块端口名在前而测试平台的信号名在后
  .clk(tb_clk),     // DUT的clk端口 <------ 测试平台的tb_clk信号
  .reset(tb_reset), 
  .enable(tb_enable),
  .count(tb_count)  
);

二者之间的映射关系具体如下所示:

激励生成

在Verilog测试平台设计中,激励生成对于测试模块编写的正确性至关重要。这里主要介绍三个部分的激励生成:

时钟与复位:基础激励,需严格同步。

数据激励:支持确定性和随机生成。

同步控制 :使用 @(edge)#delay 控制时序。

verilog 复制代码
// 示例:生成50MHz时钟(周期20ns)
reg clk;
initial begin
  clk = 0;
  forever #10 clk = ~clk;  // 每10ns翻转一次,使用forever来无限次的循环,#10是延迟10ns
end
//示例:复位信号的生成
reg reset;
initial begin
  reset = 1;     // 初始复位有效
  #100 reset = 0; // 100ns后释放复位
end
//示例:确定性输入数据的激励生成
reg [7:0] data;
initial begin//开始Initial逻辑块
  data = 8'h00;  // 初始值
  #20 data = 8'hFF;//经典的8位二进制,二位16进制
  #20 data = 8'h55;
  #20 data = 8'hAA;
end

这里顺带补充一下在verilog测试平台开头往往要去规定测试时间步和测试精度的,规定的具体格式如下所示,一般来说,测试时间步长是1ns,测试精度是1ps.

verilog 复制代码
`timescale 1ns / 1ps   // 时间单位为1ns,精度为1ps
`timescale <时间单位> / <时间精度>

监控模块

在Verilog中,监控语句 用于实时跟踪信号变化、输出调试信息或验证设计行为,是测试平台(Testbench)调试和验证的关键工具。在Verilog仿真中,initial逻辑块是测试平台(Testbench)设计的核心组成部分,用于定义仿真开始时的初始化和激励生成逻辑,重要的仿真代码都是写在initial逻辑块中的,所有initial逻辑块在仿真开始的时候立刻并行执行。以下是Verilog中常用监控语句的详细说明及其应用场景:

verilog 复制代码
$display(format_string, arg1, arg2, ...);  // 自动换行,dsiplay和write是直接输出相关信息的,类似于printf函数
$write(format_string, arg1, arg2, ...);    // 不换行
initial begin//initial逻辑块
  $display("仿真开始,时间:%t", $time);  // 显示当前仿真时间
  #10;
  $write("信号A=%b, ", a);              // 不换行输出
  $display("信号B=%h", b);              // 换行输出
end

monitor语句持续监控指定信号变化,当任何被监控信号发生变化时,自动触发输出。而且重要的是全局只有一个monitor语句会生效,其余的都会被最后出现的覆盖掉。

verilog 复制代码
initial begin
    $monitor("时间=%t, a=%b, b=%h", $time, a, b); // 监控a和b的变化,当 a 或 b 的值变化时,自动打印当前时间和信号值。

end

相关的格式化字符与其对应的含义如下:

符号 说明
%b 二进制格式
%d 十进制格式
%h 十六进制格式
%t 时间格式
%s 字符串格式

strobe语句会在当前时间步结束时 输出信号值,确保获取稳定的信号状态。触发时机时间步结束前(所有非阻塞赋值完成后)。示例代码如下:

verilog 复制代码
always @(posedge clk) begin
  $strobe("时钟上升沿时刻:a=%d, b=%d", a, b); // 在时钟边沿后输出稳定值
end

自动检查部分

在Verilog测试平台中,自动检查(Automatic Checking) 是验证设计正确性的核心机制。其核心目标是无需手动查看波形,通过代码自动比对DUT输出与预期结果,并实时报告错误。以下是自动检查的设计原理、实现方法和最佳实践。下面的代码中是进行直接自动检查的示例:

verilog 复制代码
always @(posedge clk) begin
  if (!reset && enable) begin
    // 检查计数器是否递增
      if (count !== expected_count) begin//假如count与expected_count的值不相同则报错
      $error("错误:预期值=%h,实际值=%h", expected_count, count);//报错
      $finish; // 可选:终止仿真
    end
    expected_count <= expected_count + 1; // 更新预期值
  end
end

实践与分析:

一个简单的CPU的构造如下所示

在verilog中实现一个CPU步骤较为复杂, 但是若简单实现CPU的若干功能则较为简答, 下面的verilog代码实现了一个最为简单的CPU的设计和验证:

verilog 复制代码
module SimpleCPU (
    input clk,
    input reset,
    output [7:0] data_out
);
    reg [7:0] PC, ACC, IR;
    reg [7:0] ROM [0:15];//Read only memory 
    reg [7:0] RAM [0:15];//random access memory
    parameter FETCH = 0, EXECUTE = 1;//两个状态,存与取
    reg state;

    // 初始化ROM(指令)
    initial begin
        ROM[0] = 8'b0000_0001; // LOAD 1 +地址
        ROM[1] = 8'b0001_0010; // ADD 2 +地址
        ROM[2] = 8'b0010_0011; // STORE 3 +地址
        ROM[3] = 8'b1111_1111; // HALT
    end

    // 初始化RAM(数据)
    initial begin
        RAM[1] = 8'h05; // 地址1存5
        RAM[2] = 8'h03; // 地址2存3
    end

    // 主状态机
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            PC <= 0;
            ACC <= 0;
            IR <= 0;
            state <= FETCH;
            // 复位时初始化RAM(可选)
            for (integer  i=0; i<16; i++) RAM[i] <= 8'h00;
            RAM[1] <= 8'h05;
            RAM[2] <= 8'h03;
        end else begin
            case (state)
                FETCH: begin
                    IR <= ROM[PC];
                    PC <= PC + 1;
                    state <= EXECUTE;
                end
                EXECUTE: begin
                    case (IR[7:4])
                        4'b0000: ACC <= RAM[IR[3:0]];  // LOAD
                        4'b0001: ACC <= ACC + RAM[IR[3:0]]; // ADD
                        4'b0010: RAM[IR[3:0]] <= ACC;  // STORE
                        4'b1111: state <= EXECUTE;     // HALT:冻结状态机,进入循环执行状态
                        default: ;
                    endcase
                    state <= FETCH;
                end
            endcase
        end
    end

    assign data_out = ACC;//输出信号
endmodule
//SimpleCpu.v
verilog 复制代码
module SimpleCPU_tb;

// 信号定义
reg clk;
reg reset;
wire [7:0] data_out;

// 实例化CPU
SimpleCPU uut (
    .clk(clk),
    .reset(reset),
    .data_out(data_out)
);

// 生成时钟(周期=10ns)
initial begin
    clk = 1;
    forever #5 clk = ~clk;//这里的代码是永远每隔五ns翻转一次时钟信号
end

// 测试流程
initial begin
    reset = 1;  // 复位
    #10 reset = 0;//10ns后reset键设为0

    // 观察执行过程
    #80; // 等待8个时钟周期(LOAD -> ADD -> STORE -> HALT),每二个时钟周期会执行一次命令

    // 打印最终结果
    $display("ACC = %h", data_out);//打印输出ACC累加器寄存器的值
    $display("RAM[3] = %h", uut.RAM[3]);//打印输出RAM固定位置的值
    $stop;
end

endmodule
//应该保存为SimpleCpu_tb.v

仿真测试结果如下:

CPU的基础理论其实是比较简单的,实现的无非是存,取,运算三大指令。

Synthesis综合初探

在Verilog中,综合(synthesis)是将Verilog代码转换成硬件电路的过程。这个过程是由电子设计自动化(EDA)工具完成的,通常称为综合工具。ASIC库(Application-Specific Integrated Circuit Library)是一组预先设计好的、可重用的硬件模块和元件的集合,它们用于在ASIC设计中实现特定的功能。这些库通常由半导体制造商或第三方EDA(电子设计自动化)工具提供商提供,并且与特定的工艺技术紧密相关。

以下是Verilog代码的综合过程的主要步骤:

  1. 设计输入

综合过程的第一步是提供设计输入,即编写好的Verilog代码。这个代码应该遵循一些编写规则,以确保可综合性,例如避免使用不可综合的语法(如initial块、$display等)。

  1. 解析和预处理

综合工具首先解析Verilog代码,检查语法错误,并将代码分解成更小的单元,如模块、实例、端口、信号等。预处理步骤还会处理宏定义和文件包含指令。

  1. 高层次综合(High-Level Synthesis, HLS)

在这个阶段,综合工具将Verilog代码中的行为描述(如always块)转换成更高层次的架构描述。这个过程可能包括算法优化、循环展开、资源共享等。

  1. 逻辑优化

综合工具会对设计进行逻辑优化,以减少资源使用和提高性能。这可能包括:

  • 删除未使用的信号和模块。
  • 合并逻辑门。
  • 简化逻辑表达式。
  • 优化时序路径。
  1. 映射到库

综合工具会将Verilog代码中的逻辑元素映射到FPGA或ASIC库中的具体单元,如查找表(LUTs)、触发器(FFs)、乘法器、RAM等。

  1. 约束和时序分析

在这个阶段,综合工具会考虑设计者提供的时序约束(如时钟周期、建立时间和保持时间等),并尝试满足这些约束。如果无法满足,工具会报告时序违规。

  1. 生成网表

综合过程的最终输出是一个网表(netlist),这是一个包含所有逻辑单元和它们之间连接关系的描述。网表是电路的图形表示,通常以特定格式(如EDIF、VHDL等)保存。

  1. 后续流程

综合完成后,如果是ASIC设计,网表将用于后续的布局和布线(layout and routing)流程;如果是FPGA设计,网表将输入到FPGA的布局和布线工具中,最终生成配置FPGA的比特流文件。

等。

  1. 约束和时序分析

在这个阶段,综合工具会考虑设计者提供的时序约束(如时钟周期、建立时间和保持时间等),并尝试满足这些约束。如果无法满足,工具会报告时序违规。

  1. 生成网表

综合过程的最终输出是一个网表(netlist),这是一个包含所有逻辑单元和它们之间连接关系的描述。网表是电路的图形表示,通常以特定格式(如EDIF、VHDL等)保存。

  1. 后续流程

综合完成后,如果是ASIC设计,网表将用于后续的布局和布线(layout and routing)流程;如果是FPGA设计,网表将输入到FPGA的布局和布线工具中,最终生成配置FPGA的比特流文件。

比较常用的synthesis工具就有vivado综合IDE,其是赛力斯公司旗下FPGA开发板 的配套设计工具,我在大二的时候经常使用其烧录比特流并验证。

相关推荐
芯门1 小时前
基于 Xilinx K7 FPGA 的全套万兆 10G GigE Vision 商业级传输方案
计算机视觉·fpga开发·万兆gige
ehiway1 小时前
FPGA在未来产业中的应用潜力与商业机会分析
fpga开发
GateWorld3 小时前
FPGA内部模块详解之第1篇 FPGA内部结构总览
fpga开发·fpga内部模块
爱吃汽的小橘3 小时前
驱动GPIO使用GPIO中断模式
fpga开发
普密斯科技3 小时前
精准把控每一处细节——FPGA焊点高度精准检测实施方案
人工智能·深度学习·数码相机·计算机视觉·fpga开发·测量
FPGA_小田老师6 小时前
Xilinx AXI UART Lite IP核:IP核深度解析
fpga开发·uart·串口通讯·axi转uart
GateWorld8 小时前
FPGA内部模块详解之二 FPGA的逻辑“心脏”——可编程逻辑块(PFU/CLB)深度解析
fpga开发·fpga内部结构
Saniffer_SH9 小时前
【高清视频】如何针对电动汽车进行通信可靠性测试、故障注入与功率分析?
服务器·驱动开发·测试工具·fpga开发·计算机外设·硬件架构·压力测试
博览鸿蒙10 小时前
基于FPGA技术的数字存储示波器设计探讨
fpga开发
Saniffer_SH10 小时前
【高清视频】企业级NVMe SSD (E3.S, U.2)和消费类M.2 SSD拆解分析
服务器·网络·数据库·驱动开发·测试工具·fpga开发·压力测试