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开发板 的配套设计工具,我在大二的时候经常使用其烧录比特流并验证。

相关推荐
Bit流4 小时前
记录此刻:历时两月,初步实现基于FPGA的NVMe SSD固态硬盘存储控制器设计!
fpga开发·#fpga nvme ssd·# nvme ssd 读取·#fpga nvme协议实现
Florence2310 小时前
GPU和FPGA的区别
fpga开发
电棍2331 天前
verilog笔记
笔记·fpga开发
ZxsLoves1 天前
【【Systemverilog学习参考 简单的加法器验证-含覆盖率】】
学习·fpga开发
Ronin-Lotus2 天前
嵌入式硬件篇---数字电子技术中的触发器
嵌入式硬件·fpga开发·触发器·数字电子技术·上位机知识
ehiway2 天前
FPGA+GPU+CPU国产化人工智能平台
人工智能·fpga开发·硬件工程·国产化
蓑衣客VS索尼克2 天前
什么是逻辑分析仪?
arm开发·人工智能·fpga开发
Terasic友晶科技3 天前
第29篇 基于ARM A9处理器用C语言实现中断<五>
c语言·fpga开发·定时器中断
9527华安3 天前
FPGA实现GTY光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持
fpga开发·音视频·aurora·gty·usb3.0·ft601