当前位置: 首页 > news >正文

个人学习笔记12

  • 最终版test_macro.svh

  • `ifndef TEST_MACRO_SVH `define TEST_MACRO_SVH // ============================================================ // Color Definition // ============================================================ `define COLOR_RESET "\033[0m" `define COLOR_BOLD_BLUE "\033[1;34m" `define COLOR_BOLD_GREEN "\033[1;32m" `define COLOR_BOLD_RED "\033[1;31m" `define COLOR_BOLD_YELLOW "\033[1;33m" `define COLOR_BOLD_CYAN "\033[1;36m" // ============================================================ // Global Counter (需要在TB里定义) // int total_pass, total_fail; // ============================================================ // ============================================================ // Test Suite // ============================================================ `define TEST_SUITE_BEGIN(name) \ $display("\n"); \ $display(`COLOR_BOLD_CYAN "==========================================" `COLOR_RESET); \ $display(`COLOR_BOLD_CYAN "Test Suite: %s" `COLOR_RESET, name); \ $display(`COLOR_BOLD_CYAN "==========================================" `COLOR_RESET); // ============================================================ // Test Case (支持 name + id) // 👉 会定义局部变量 __test_name / __test_id // ============================================================ `define TEST_CASE_BEGIN(name, id) \ string __test_name = name; \ int __test_id = id; \ $display("\n"); \ $display(`COLOR_BOLD_BLUE "[TEST CASE %0d] %s" `COLOR_RESET, __test_id, __test_name); // ============================================================ // Step // ============================================================ `define TEST_STEP(msg) \ $display(`COLOR_BOLD_YELLOW "[STEP][TC%0d][%0t] %s" `COLOR_RESET, \ __test_id, $time, msg); // ============================================================ // PASS / FAIL // ============================================================ `define TEST_PASS(msg) \ begin \ total_pass++; \ $display(`COLOR_BOLD_GREEN "[PASS][TC%0d][%0t] %s" `COLOR_RESET, \ __test_id, $time, msg); \ end `define TEST_FAIL(msg) \ begin \ total_fail++; \ $display(`COLOR_BOLD_RED "[FAIL][TC%0d][%0t] %s" `COLOR_RESET, \ __test_id, $time, msg); \ end // ============================================================ // CHECK (最常用) // ============================================================ `define TEST_CHECK(cond, msg) \ if (cond) begin \ `TEST_PASS(msg) \ end else begin \ `TEST_FAIL(msg) \ end // ============================================================ // Summary // ============================================================ `define TEST_SUMMARY \ $display("\n"); \ $display(`COLOR_BOLD_CYAN "============== TEST SUMMARY ==============" `COLOR_RESET); \ $display(`COLOR_BOLD_GREEN "TOTAL PASS = %0d" `COLOR_RESET, total_pass); \ $display(`COLOR_BOLD_RED "TOTAL FAIL = %0d" `COLOR_RESET, total_fail); \ $display(`COLOR_BOLD_CYAN "==========================================" `COLOR_RESET); // ============================================================ // Optional: Abort on Fail // ============================================================ `define TEST_CHECK_FATAL(cond, msg) \ if (!(cond)) begin \ `TEST_FAIL(msg) \ $fatal; \ end else begin \ `TEST_PASS(msg) \ end `endif

    ✅ Testbench 里正确用法(必须这样写)

  • `include "test_macro.svh" module tb; int total_pass = 0; int total_fail = 0; initial begin `TEST_SUITE_BEGIN("LPDDR6 Initialization") // Test Case 1 `TEST_CASE_BEGIN("Power-up Sequence", 1) `TEST_STEP("Check reset low") reset_n = 0; `TEST_CHECK(reset_n == 0, "RESET_n is LOW") `TEST_STEP("Release reset") reset_n = 1; `TEST_CHECK(reset_n == 1, "RESET_n is HIGH") // Test Case 2 `TEST_CASE_BEGIN("Command Test", 2) `TEST_STEP("Send NOP") cs = 0; `TEST_CHECK(cs == 0, "CS is LOW") // Summary `TEST_SUMMARY #10us; $finish; end endmodule

    给你一版可以直接放进 testbench 使用的 LPDDR6 上电初始化 task,同时支持:

    单通道:lpddr6_powerup_seq(1)
    双通道:lpddr6_powerup_seq(2)

  • 这张图对应 JEDEC 的Voltage Ramp and Device Initialization / Power Ramp and Initialization Sequence,关键参数包括tINIT1=200ustINIT2=10nstINIT3=4mstINIT4=5nCKtINIT5=2ustZQLAT=max(30ns,4nCK);LPDDR6 每个 sub-channel 有独立的CK/CS/CA/WCK/DQ信号。

// ============================================================ // LPDDR6 Power-up Sequence Task // Support single channel / dual channel // ============================================================ localparam int LPDDR6_MAX_CH = 2; localparam int CA_W = 4; localparam int DQ_W = 12; localparam time tCK = 5000ps; // Example boot CK period: 200MHz localparam time tINIT1 = 200us; localparam time tINIT2 = 10ns; localparam time tINIT3 = 4ms; localparam time tINIT4 = 5 * tCK; localparam time tINIT5 = 2us; localparam time tZQLAT = 30ns; // max(30ns, 4nCK), example uses 30ns // ------------------------------------------------------------ // LPDDR6 TB driving signals // ch0/ch1 represent sub-channel 0 / sub-channel 1 // ------------------------------------------------------------ logic [LPDDR6_MAX_CH-1:0] ck_t; logic [LPDDR6_MAX_CH-1:0] ck_c; logic [LPDDR6_MAX_CH-1:0] wck_t; logic [LPDDR6_MAX_CH-1:0] wck_c; logic reset_n; // RESET_n is usually device-level logic [LPDDR6_MAX_CH-1:0] cs; logic [LPDDR6_MAX_CH-1:0][CA_W-1:0] ca; // DQ is bidirectional, so use tri + output enable tri [LPDDR6_MAX_CH-1:0][DQ_W-1:0] dq; logic [LPDDR6_MAX_CH-1:0] dq_oe; logic [LPDDR6_MAX_CH-1:0][DQ_W-1:0] dq_drv; genvar gi; generate for (gi = 0; gi < LPDDR6_MAX_CH; gi++) begin : GEN_DQ_ASSIGN assign dq[gi] = dq_oe[gi] ? dq_drv[gi] : 'z; end endgenerate
// ============================================================ // CK generation // CK_t / CK_c are differential and complementary // ============================================================ task automatic start_ck(input int ch_id); fork forever begin #(tCK/2); ck_t[ch_id] = ~ck_t[ch_id]; ck_c[ch_id] = ~ck_c[ch_id]; end join_none endtask // ============================================================ // WCK generation // LPDDR6 WCK:CK = 2:1 // So WCK period = tCK / 2 // ============================================================ task automatic start_wck(input int ch_id); fork forever begin #(tCK/4); wck_t[ch_id] = ~wck_t[ch_id]; wck_c[ch_id] = ~wck_c[ch_id]; end join_none endtask
// ============================================================ // Drive command to one channel // NOTE: // ca_value is placeholder now. // Later you should replace it with real LPDDR6 command encoding. // ============================================================ task automatic lpddr6_drive_cmd_one_ch( input int ch_id, input string cmd_name, input logic [CA_W-1:0] ca_value, input int nck_cycles ); `TEST_STEP($sformatf("CH%0d send command: %s", ch_id, cmd_name)) @(posedge ck_t[ch_id]); cs[ch_id] <= 1'b1; ca[ch_id] <= ca_value; repeat (nck_cycles) @(posedge ck_t[ch_id]); cs[ch_id] <= 1'b0; ca[ch_id] <= '0; endtask
// ============================================================ // Drive command to active channels // active_ch_num = 1: only CH0 // active_ch_num = 2: CH0 + CH1 // ============================================================ task automatic lpddr6_drive_cmd_active_ch( input int active_ch_num, input string cmd_name, input logic [CA_W-1:0] ca_value, input int nck_cycles ); if (active_ch_num == 1) begin lpddr6_drive_cmd_one_ch(0, cmd_name, ca_value, nck_cycles); end else if (active_ch_num == 2) begin fork lpddr6_drive_cmd_one_ch(0, cmd_name, ca_value, nck_cycles); lpddr6_drive_cmd_one_ch(1, cmd_name, ca_value, nck_cycles); join end else begin `TEST_FAIL("active_ch_num must be 1 or 2") end endtask
  • 核心:上电初始化 task

  • // ============================================================ // LPDDR6 Voltage Ramp and Device Initialization Sequence // // active_ch_num: // 1 -> single channel mode, only CH0 is driven // 2 -> dual channel mode, CH0 and CH1 are driven // ============================================================ task automatic lpddr6_powerup_seq(input int active_ch_num); int ch; `TEST_CASE_BEGIN("LPDDR6 Voltage Ramp and Device Initialization", 1) if (!(active_ch_num inside {1, 2})) begin `TEST_FAIL("active_ch_num must be 1 or 2") return; end // -------------------------------------------------------- // Phase Ta: before / during power ramp // Required: // RESET_n LOW // CS LOW // CA valid low level // CK/WCK stable complementary or inactive // DQ High-Z // -------------------------------------------------------- `TEST_STEP("Ta: initialize all driven signals before power ramp") reset_n <= 1'b0; for (ch = 0; ch < LPDDR6_MAX_CH; ch++) begin ck_t[ch] <= 1'b0; ck_c[ch] <= 1'b1; wck_t[ch] <= 1'b0; wck_c[ch] <= 1'b1; cs[ch] <= 1'b0; ca[ch] <= '0; dq_oe[ch] <= 1'b0; dq_drv[ch] <= '0; end `TEST_CHECK(reset_n == 1'b0, "RESET_n is LOW during power ramp") `TEST_CHECK(cs[0] == 1'b0, "CH0 CS is LOW during power ramp") if (active_ch_num == 2) begin `TEST_CHECK(cs[1] == 1'b0, "CH1 CS is LOW during power ramp") end // -------------------------------------------------------- // Ta -> Tb: power ramp // In digital TB, supplies are usually abstracted by delay. // If your DUT has supply-good signals, drive them here. // -------------------------------------------------------- `TEST_STEP("Ta to Tb: simulate power ramp until supplies are stable") // Example delay. Real max tINIT0 is 20ms. // You can replace this with your own supply-good handshake. #(1ms); `TEST_STEP("Tb: supplies are considered stable") // -------------------------------------------------------- // Tb -> Tc: keep RESET_n LOW for tINIT1 // -------------------------------------------------------- `TEST_STEP("Tb to Tc: keep RESET_n LOW for tINIT1") #(tINIT1); // -------------------------------------------------------- // Before RESET_n deassertion: // CS must already be LOW for at least tINIT2 // -------------------------------------------------------- `TEST_STEP("Wait tINIT2: CS LOW before RESET_n HIGH") #(tINIT2); // -------------------------------------------------------- // Tc: deassert RESET_n // CK_t / CK_c need to toggle or be valid complementary // almost at the same time // -------------------------------------------------------- `TEST_STEP("Tc: deassert RESET_n and start CK") reset_n <= 1'b1; if (active_ch_num >= 1) begin start_ck(0); end if (active_ch_num == 2) begin start_ck(1); end #1ps; `TEST_CHECK(reset_n == 1'b1, "RESET_n is deasserted HIGH") // -------------------------------------------------------- // Tc -> Td: CS must remain LOW for tINIT3 // -------------------------------------------------------- `TEST_STEP("Tc to Td: keep CS LOW for tINIT3 after RESET_n HIGH") #(tINIT3); `TEST_CHECK(cs[0] == 1'b0, "CH0 CS remains LOW during tINIT3") if (active_ch_num == 2) begin `TEST_CHECK(cs[1] == 1'b0, "CH1 CS remains LOW during tINIT3") end // -------------------------------------------------------- // Td -> Te: CK stable for at least tINIT4 = 5nCK // -------------------------------------------------------- `TEST_STEP("Td to Te: wait tINIT4, stable CK before first CS toggle") repeat (5) @(posedge ck_t[0]); // -------------------------------------------------------- // Te: first CS toggle with NOP // -------------------------------------------------------- `TEST_STEP("Te: issue first NOP command") lpddr6_drive_cmd_active_ch( active_ch_num, "NOP", 4'b0000, 2 ); // -------------------------------------------------------- // Te -> Tf: wait tINIT5 before first MRW/MRR // -------------------------------------------------------- `TEST_STEP("Te to Tf: wait tINIT5 before first MRW/MRR") #(tINIT5); // -------------------------------------------------------- // Tf: issue initial MRW / MRR // These CA values are placeholders. // Replace with real command truth table encoding later. // -------------------------------------------------------- `TEST_STEP("Tf: issue initial MRW/MRR commands") lpddr6_drive_cmd_active_ch( active_ch_num, "MRW_INIT", 4'b0001, 2 ); lpddr6_drive_cmd_active_ch( active_ch_num, "MRR_CHECK", 4'b0010, 2 ); // -------------------------------------------------------- // ZQ Latch command // Initial ZQ calibration is automatically started after RESET_n high. // Controller should issue ZQ Cal Latch command later. // -------------------------------------------------------- `TEST_STEP("Issue MPC ZQ Calibration Latch") lpddr6_drive_cmd_active_ch( active_ch_num, "MPC_ZQ_CAL_LATCH", 4'b0011, 2 ); `TEST_STEP("Wait tZQLAT after ZQ Calibration Latch") #(tZQLAT); // -------------------------------------------------------- // After ZQ Latch: // Optional training may start. // Start WCK before WCK/DQ related training. // -------------------------------------------------------- `TEST_STEP("Start WCK for later training") if (active_ch_num >= 1) begin start_wck(0); end if (active_ch_num == 2) begin start_wck(1); end // -------------------------------------------------------- // Simplified optional training sequence // -------------------------------------------------------- `TEST_STEP("Optional Training: Command Bus Training") lpddr6_drive_cmd_active_ch(active_ch_num, "ENTER_CBT", 4'b0100, 2); lpddr6_drive_cmd_active_ch(active_ch_num, "CBT_PATTERN", 4'b0101, 8); lpddr6_drive_cmd_active_ch(active_ch_num, "EXIT_CBT", 4'b0110, 2); `TEST_STEP("Optional Training: WCK2CK Leveling") lpddr6_drive_cmd_active_ch(active_ch_num, "ENTER_WCK2CK_LEVELING", 4'b0111, 2); lpddr6_drive_cmd_active_ch(active_ch_num, "WCK2CK_LEVELING", 4'b1000, 8); lpddr6_drive_cmd_active_ch(active_ch_num, "EXIT_WCK2CK_LEVELING", 4'b1001, 2); `TEST_STEP("Optional Training: DQ Training") lpddr6_drive_cmd_active_ch(active_ch_num, "DQ_TRAINING", 4'b1010, 8); `TEST_PASS("LPDDR6 power-up initialization sequence finished") endtask

    在 testbench 里这样调用

  • initial begin `TEST_SUITE_BEGIN("LPDDR6 Initialization Test") // 单通道,只驱动 CH0 lpddr6_powerup_seq(1); // 或者双通道,同时驱动 CH0 + CH1 // lpddr6_powerup_seq(2); `TEST_SUMMARY #10us; $finish; end
http://www.jsqmd.com/news/707409/

相关文章:

  • 快速入门Face3D.ai Pro:零代码生成专业级3D人脸模型
  • 基于Crypto APIs构建MCP服务器:AI与区块链交互的共享工具库实践
  • 2026年市面上符合摩洛哥标准防火卷帘门厂家排行 - 品牌排行榜
  • Pinpoint C Agent 实战指南:PHP/Python 微服务链路追踪部署与调优
  • Phi-3-mini-4k-instruct-gguf快速上手:Git版本控制下的模型项目协作管理
  • Phi-mini-MoE-instructGPU利用率提升:通过batch size与kv cache优化
  • AgenticHub:macOS原生AI工具资源管理器,高效管理MCP服务器与Agent技能
  • 别死记硬背!用“白兔的分身术”等5道蓝桥杯真题,带你掌握C/C++算法题的降维打击思维
  • 机器学习中五大核心离散概率分布详解与应用
  • VideoDownloadHelper视频下载助手:3分钟快速上手终极指南
  • AI 技术日报 - 2026-04-27
  • DeepWideResearch:AI研究中深度与广度双螺旋协作模式解析
  • 深入理解 async/await的原理
  • 构建个人神经科学知识库:基于Git与Markdown的“第二大脑”实践
  • 2026年收藏指南:三招让论文AI率直接砍半,毕业查重稳过,实测有效! - 降AI实验室
  • AI像素画创作:pixel-agents智能体框架原理与实践指南
  • aLEAKator混合域模拟技术:硬件安全验证新突破
  • 2222222222222222222
  • 别再只懂JWT三部分了:手把手教你用Node.js + Express实战JWT登录与权限控制
  • 初识MySQL,数据库相关概念,库操作,表操作
  • 2026年3月景观棚公司推荐,伸缩篷/膜结构车棚/景观棚/电动推拉棚/遮阳棚/停车棚/体育看台,景观棚定做厂家哪家好 - 品牌推荐师
  • 告别alert!用vConsole给你的Vue/React移动端项目做个‘移动版F12’调试面板
  • 机器人定位导航技术:多传感器融合与状态估计算法解析
  • Clang在Dev-C++中如何静态链接标准库
  • IDEA里Maven多模块项目显示多个Root?别慌,三步搞定项目结构混乱
  • JAVA基础之反射
  • H.266/VVC编解码技术解析与开源实现VVenC/VVdeC
  • STM32简介与选型
  • Java的java.lang.foreign优化模式
  • 英语阅读_choosing a career in your future