[SystemVerilog] Clocking

SystemVerilog Clocking用法详解

SystemVerilog 的 clocking 块(Clocking Block)是一种专门用于定义信号时序行为的构造,主要用于验证环境(如 UVM)中,以精确控制信号的采样和驱动时序。clocking 块通过将信号与特定时钟关联,简化了测试环境中对时序敏感信号的处理,减少了手动时序管理的复杂性。本文将详细介绍 SystemVerilog 中 clocking 块的各种用法,包括基本定义、输入输出信号、时序控制、接口中的使用、以及在验证中的应用,并提供示例代码和最佳实践。

1. Clocking 块概述

clocking 块是 SystemVerilog 中用于封装信号与时钟关系的构造,通常定义在 interface 或模块中。它的主要功能是:

  • 信号采样与驱动:定义信号相对于时钟的采样和驱动时序。
  • 时序抽象:屏蔽底层时序细节,简化测试用例开发。
  • 验证支持:在验证环境中(如 UVM)提供标准化的时序控制接口。
  • 避免竞争:通过明确的采样和驱动时间点,减少仿真中的竞争与冒险。

主要用途

  • 在验证环境中精确控制信号的采样和驱动。
  • 简化测试环境中对 DUT(待测设计)的时序交互。
  • interface 结合,提供模块化的时序接口。

基本语法

clocking clocking_name @(clock_event);
  input input_signal;
  output output_signal;
endclocking
  • clocking_name:时钟块的名称。
  • clock_event:触发时钟块的时钟事件(如 @(posedge clk))。
  • input_signal:输入信号,定义采样时序。
  • output_signal:输出信号,定义驱动时序。

2. 基本 Clocking 块定义与使用

clocking 块通过指定信号的采样和驱动时序,简化了验证代码的编写。

示例:基本 Clocking 块

module top;
  logic clk = 0;
  logic [7:0] data;
  logic valid;

  // 定义 clocking 块
  clocking cb @(posedge clk);
    input data;
    input valid;
  endclocking

  // 测试逻辑
  initial begin
    forever #5 clk = ~clk;
  end

  initial begin
    @(cb); // 等待 clocking 块的时钟边沿
    if (cb.valid)
      $display("Sampled data: %h", cb.data);
  end
endmodule

说明

  • clocking cb 定义了一个时钟块,基于 clk 的上升沿。
  • input datainput valid 指定信号在时钟上升沿前采样。
  • 测试逻辑通过 cb.datacb.valid 访问同步信号。

优点

  • 信号采样自动与时钟边沿对齐,避免手动时序控制。
  • 提高了代码的可读性和可维护性。

3. 输入与输出信号的时序控制

clocking 块支持通过 inputoutput 关键字定义信号的采样和驱动时序,并可以通过延迟指定具体的时序偏移。

3.1 输入信号采样

input 信号在时钟边沿前采样,默认采样时间点为时钟边沿前的非阻塞赋值(NBA)区域。可以通过 input #delay 指定采样延迟。

3.2 输出信号驱动

output 信号在时钟边沿后驱动,默认驱动时间点为时钟边沿后的非阻塞赋值区域。可以通过 output #delay 指定驱动延迟。

示例:带时序延迟的 Clocking 块

interface simple_bus (input logic clk);
  logic [7:0] data;
  logic valid;
  logic ready;

  clocking cb @(posedge clk);
    input #1ns data, valid; // 采样延迟 1ns
    output #2ns ready;     // 驱动延迟 2ns
  endclocking
endinterface

module receiver (simple_bus bus);
  always @(bus.cb) begin
    bus.cb.ready <= 1; // 在时钟边沿后 2ns 驱动 ready
    if (bus.cb.valid)
      $display("Received data: %h", bus.cb.data);
  end
endmodule

module top;
  logic clk = 0;
  always #5 clk = ~clk;

  simple_bus bus_inst(.clk(clk));
  receiver u_receiver (.bus(bus_inst));

  initial begin
    bus_inst.data = 8'hA5;
    bus_inst.valid = 1;
    #20 $finish;
  end
endmodule

说明

  • input #1ns 表示 datavalid 在时钟上升沿前 1ns 采样。
  • output #2ns 表示 ready 在时钟上升沿后 2ns 驱动。
  • 模块通过 bus.cb 访问同步信号。

注意

  • 延迟值必须为非负,且在仿真中有效(综合通常忽略)。
  • 延迟值应根据 DUT 的时序要求设置。

4. Clocking 块与 Interface 的结合

clocking 块通常定义在 interface 中,与信号和 modport 结合,提供模块化的时序接口。

示例:接口中的 Clocking 块

interface simple_bus (input logic clk);
  logic [7:0] data;
  logic valid;
  logic ready;

  clocking cb @(posedge clk);
    input data, valid;
    output ready;
  endclocking

  modport slave (
    input data, valid,
    output ready
  );
endinterface

module receiver (simple_bus.slave bus);
  always @(bus.cb) begin
    bus.cb.ready <= 1;
    if (bus.cb.valid)
      $display("Received data: %h", bus.cb.data);
  end
endmodule

module top;
  logic clk = 0;
  always #5 clk = ~clk;

  simple_bus bus_inst(.clk(clk));
  receiver u_receiver (.bus(bus_inst));

  initial begin
    bus_inst.data = 8'hA5;
    bus_inst.valid = 1;
    #20 $finish;
  end
endmodule

说明

  • clocking cb 定义在 interface 中,与接口信号关联。
  • receiver 模块通过 bus.cb 访问同步信号。
  • modport slave 定义了信号方向,增强接口的模块化。

优点

  • 将时序控制与信号封装结合,简化验证代码。
  • 支持模块化的 DUT 和测试环境连接。

5. 多时钟 Clocking 块

clocking 块支持定义多个时钟块,用于处理多时钟域的信号。

示例:多时钟 Clocking 块

interface multi_clock_bus (input logic clk1, clk2);
  logic [7:0] data;
  logic valid;

  clocking cb1 @(posedge clk1);
    input data, valid;
  endclocking

  clocking cb2 @(posedge clk2);
    input data, valid;
  endclocking
endinterface

module monitor (multi_clock_bus bus);
  always @(bus.cb1) begin
    if (bus.cb1.valid)
      $display("clk1 domain: data = %h", bus.cb1.data);
  end

  always @(bus.cb2) begin
    if (bus.cb2.valid)
      $display("clk2 domain: data = %h", bus.cb2.data);
  end
endmodule

module top;
  logic clk1 = 0, clk2 = 0;
  always #5 clk1 = ~clk1;
  always #7 clk2 = ~clk2;

  multi_clock_bus bus_inst(.clk1(clk1), .clk2(clk2));
  monitor u_monitor (.bus(bus_inst));

  initial begin
    bus_inst.data = 8'hA5;
    bus_inst.valid = 1;
    #50 $finish;
  end
endmodule

说明

  • cb1cb2 分别基于 clk1clk2 定义时钟块。
  • monitor 模块在不同时钟域中采样信号。
  • 支持多时钟域验证。

注意

  • 确保时钟信号正确连接到接口。
  • 在多时钟域中,注意信号的跨时钟域处理。

6. Clocking 块在验证中的应用

clocking 块在验证环境(如 UVM)中广泛使用,用于连接 DUT 和测试环境,提供标准化的时序接口。

示例:UVM 验证中的 Clocking 块

interface simple_bus (input logic clk);
  logic [7:0] data;
  logic valid;
  logic ready;

  clocking cb @(posedge clk);
    input data, valid;
    output ready;
  endclocking
endinterface

module dut (simple_bus bus);
  always @(posedge bus.clk) begin
    if (bus.valid && bus.ready)
      $display("DUT received: %h", bus.data);
  end
endmodule

program testbench;
  import uvm_pkg::*;
  `include "uvm_macros.svh"

  logic clk = 0;
  always #5 clk = ~clk;

  simple_bus bus_if(.clk(clk));

  initial begin
    // 设置 UVM 接口
    uvm_config_db#(virtual simple_bus)::set(null, "*", "bus_if", bus_if);
    run_test();
  end
endprogram

说明

  • simple_bus 包含 clocking cb,为测试环境提供同步信号访问。
  • UVM 测试环境通过 uvm_config_db 获取虚拟接口。
  • dut 通过接口与测试环境交互。

优点

  • 简化了 UVM 驱动器和监视器的时序控制。
  • 提供标准化的信号访问接口。

7. Clocking 块的高级用法

7.1 默认时钟块

可以使用 default clocking 指定默认的时钟块,简化代码中的时序引用。

interface simple_bus (input logic clk);
  logic [7:0] data;
  logic valid;

  clocking cb @(posedge clk);
    input data, valid;
  endclocking

  default clocking cb; // 设置默认时钟块
endinterface

module monitor (simple_bus bus);
  always @(*) begin
    if (valid) // 直接引用信号,等效于 bus.cb.valid
      $display("Data: %h", data);
  end
endmodule

说明

  • default clocking cbcb 设置为默认时钟块。
  • 信号可以直接引用(如 valid),等效于 bus.cb.valid

注意

  • 默认时钟块在复杂接口中可能降低可读性,谨慎使用。

7.2 动态时序调整

clocking 块支持在仿真中动态调整时序(通过属性),但主要用于验证。

interface simple_bus (input logic clk);
  logic [7:0] data;
  logic valid;

  clocking cb @(posedge clk);
    input #1step data, valid; // 使用 1step 采样
  endclocking
endinterface

说明

  • #1step 表示在时钟边沿前的最小时间步长采样。
  • 适合需要精确时序控制的验证场景。

8. 注意事项与最佳实践

  1. 时序定义

    • 确保采样和驱动延迟与 DUT 的时序要求一致。
    • 避免过大的延迟值,以免影响仿真性能。
  2. 接口结合

    • clocking 块定义在 interface 中,与信号和 modport 结合。
    • 使用 modport 明确信号方向,增强模块化。
  3. 验证环境

    • 在 UVM 中,使用虚拟接口传递 clocking 块。
    • 结合 clocking 块简化驱动器和监视器的开发。
  4. 综合限制

    • clocking 块主要用于验证,不支持综合。
    • 确保 DUT 代码不依赖 clocking 块。
  5. 多时钟域

    • 为每个时钟域定义独立的 clocking 块。
    • 注意跨时钟域信号的同步。
  6. 代码可读性

    • clocking 块和信号提供清晰的命名。
    • 添加注释说明采样和驱动时序。
  7. 调试与验证

    • 使用仿真工具验证 clocking 块的时序行为。
    • 检查采样和驱动时间点是否符合预期。

9. 总结

SystemVerilog 的 clocking 块是一种强大的验证工具,用于定义信号的采样和驱动时序,简化测试环境的时序管理。通过基本定义、输入输出信号、时序控制、接口结合、多时钟支持等功能,clocking 块在 UVM 等验证环境中发挥了关键作用。特别是在复杂设计中,clocking 块与 interface 的结合提供了模块化的时序接口,显著提高了验证效率和代码质量。遵循最佳实践并根据具体场景选择合适的 clocking 用法,能够有效提升验证的可靠性和可维护性。

10. 设计工具推荐

  • SZ901
    SZ901 是一款基于XVC协议的FPGA网络下载器。
    • 最高支持53M
    • 支持4路JTAG独立使用
    • 支持端口合并
    • 支持国产FLASH烧写
    • 下载器无限扩展
    • 配备专属程序固化软件,一键烧写,能大大减小程序固化时间!
SystemVerilog的听课学习笔记,包括讲义截取、知识点记录、注意事项等细节的标注。 目录如下: 第一章 SV环境构建常识 1 1.1 数据类型 1 四、二值逻辑 4 定宽数组 9 foreach 13 动态数组 16 队列 19 关联数组 21 枚举类型 23 字符串 25 1.2 过程块和方法 27 initial和always 30 function逻辑电路 33 task时序电路 35 动态 静态变量 39 1.3 设计例化和连接 45 第二章 验证的方法 393 动态仿真 395 静态检查 397 虚拟模型 403 硬件加速 405 效能验证 408 性能验证 410 第三章 SV组件实现 99 3.1 接口 100 什么是interface 101 接口的优势 108 3.2 采样和数据驱动 112 竞争问题 113 接口中的时序块clocking 123 利于clocking的驱动 133 3.3 测试的开始和结束 136 仿真开始 139 program隐式结束 143 program显式结束 145 软件域program 147 3.4 调试方法 150 第四章 验证的计划 166 4.1 计划概述 166 4.2 计划的内容 173 4.3 计划的实现 185 4.4 计划的进程评估 194 第五章 验证的管理 277 6.1 验证的周期检查 277 6.2 管理三要素 291 6.3 验证的收敛 303 6.4 问题追踪 314 6.5 团队建设 321 6.6 验证的专业化 330 第六章 验证平台的结构 48 2.1 测试平台 49 2.2 硬件设计描述 55 MCDF接口描述 58 MCDF接口时序 62 MCDF寄存器描述 65 2.3 激励发生器 67 channel initiator 72 register initiator 73 2.4 监测器 74 2.5 比较器 81 2.6 验证结构 95 第七章 激励发生封装:类 209 5.1 概述 209 5.2 类的成员 233 5.3 类的继承 245 三种类型权限 protected/local/public 247 this super 253 成员覆盖 257 5.4 句柄的使用 263 5.5 包的使用 269 第八章 激励发生的随机化 340 7.1 随机约束和分布 340 权重分布 353 条件约束 355 7.2 约束块控制 358 7.3 随机函数 366 7.4 数组约束 373 7.5 随机控制 388 第九章 线程与通信 432 9.1 线程的使用 432 9.2 线程的控制 441 三个fork...join 443 等待衍生线程 451 停止线程disable 451 9.3 线程的通信 458 第十章 进程评估:覆盖率 495 10.1 覆盖率类型 495 10.2 功能覆盖策略 510 10.3 覆盖组 516 10.4 数据采样 524 10.5 覆盖选项 544 10.6 数据分析 550 第十一章 SV语言核心进阶 552 11.1 类型转换 552 11.2 虚方法 564 11.3 对象拷贝 575 11.4 回调函数 584 11.5 参数化的类 590 第十二章 UVM简介 392 8.2 UVM简介 414 8.3 UVM组件 420 8.4 UVM环境 425
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值