上一篇文章中,介绍了一个基本的nvme vip测试用例,包括一些基本的设置,发送命令和接收完成。在这里,我们将再看一些nvme命令,涉及vip的一些特性和功能。
在这里,您可以了解有关适用于 nvme 和 pcie 的 synopsys vc 验证 ip 的更多信息。
贵宾的(提醒)视图
我们上次简要介绍了这一点。这次我们将更深入地介绍,因此我们将继续参考此图:
nvme vip 提供了一组功能来帮助测试。其中包括随机化、功能窥探、简化的 prp 和数据缓冲区处理、内存屏蔽和内置记分板。我们将依次通过另一个示例来查看其中的每一个。
继续我们的测试用例...
继上一篇文章中的“琐碎测试用例”之后(同样,我们没有显示一些任务参数或检查错误),让我们再看几个命令来启动我们的 nvme 测试用例。
提醒一下:以脚本一词开头的任务是 nvme 命令。其他(不以脚本开头)是 vip 状态/控制/配置任务。
// we will assume that the pcie stack is setup and running
bit [63:0] base_addr = 32’h0001_0000; // ctlr bar base addr
dword_t num_q_entries, ctlr_id;
// tell the host where the controller has its base address
allocatecontrollerid(base_addr, ctlr_id, status);
num_q_entries = 2;
// create the admin completion and submission queues
scriptcreateadmincplq(ctlr_id, num_q_entries, status);
scriptcreateadminsubq(ctlr_id, num_q_entries, status);
// send an “identify controller” command
data_buf_t #(dword_t) identify_buffer; // identify data
identify_buffer = new(1024);
scriptidentify(ctlr_id, 0, identify_buffer, 0, status);
我们以调用标识控制器结束了最后一个示例。现在,继续在这一点上,我们读取字节 519:516 以获取有效命名空间 id 的数量。我们通过 setnumnamespaces() 调用将其传递给主机 vip。请注意,我们必须对识别控制器缓冲区中返回的(小端序)数据进行字节交换。
int num_ns, nsid, blk_size_pow2, blk_size_in_bytes;
bit [63:0] ns_size_in_blks;
feature_identifier_t feature_id;
nvme_feature_t set_feature;
// we’ll grab the number of valid namespaces (nn) from the
// identify buffer. note index converted from bytes to dword.
num_ns = byteswap32(identify_buffer[516 >> 2]); // bytes 519:516
// tell the vip how many active nsids the controller has
setnumnamespaces(ctlr_id, num_ns, status);
接下来,我们读取其中一个命名空间(命名空间 id=1)的信息。请注意,我们在这里“作弊”了一点,因为我们应该遍历所有有效的命名空间。对于这个例子,我们只假设我们只有 nsid=1。尽管标识调用不采用 prp 列表,但其主机内存缓冲区可以具有偏移量。如果需要,请选择参数“use_offset=1”。实际偏移通过约束 min/max_prp_dword_offset_var 随机化。
// now send an “identify namespace” command for nsid=1
nsid = 1;
use_offset = 1; // randomize buffer offset
scriptidentify(ctlr_id, nsid, identify_buffer,
use_offset, status);
// pull information from format[0]
blk_size_pow2 = byteswap32(identify_buffer.getdata(128 >> 2)));
blk_size_pow2 = (blk_size_pow2 >> 16) & 32’hff; // dword[23:16]
blk_size_in_bytes = 1 2),
identify_buffer.getdata(12 >> 2)});
// before we create queues, we need to configure the num queues
// on the controller.
feature_id = feature_num_queues;
set_feature = new(feature_id);
一旦识别命名空间返回,我们现在有了块大小和命名空间大小。我们使用设置功能设置请求的队列数量。通过 vip 的功能侦听,这将(透明地)将 vip 设置为当前支持的提交和完成队列数量(用于以后的检查和错误注入支持)。
接下来的步骤设置命名空间的格式(使用标识命名空间数据结构中的格式 0)。然后,我们更新命名空间信息的 vip 视图。vip 需要此命名空间信息来保留每个命名空间的记分板。
set_features.setnumcplq(2); // request number of sub &
set_features.setnumsubq(3); // cpl queues
// call set features command to set the queues on the ctlr
scriptsetfeatures(ctlr_id, set_features, …, status);
// note that set features number of queues command need not
// return the same amount of queues that were requested. we can
// check by examining set_features.getnumcplq() and
// getnumsubq(), but in this case we’ll just trust it…
// format the namespace
sec_erase = 0; // don’t use secure erase
pi_md_settings = 0; // don’t use prot info or metadata
format_number = 0; // from identify ns data structure
scriptformatnvm(ctlr_id, nsid, sec_erase, pi_md_settings,
format_number, …, status);
// tell the vip about this ns
setnamespaceinfo(ctlr_id, nsid, blk_size_in_bytes,
ns_size_in_blks, md_bytes_per_blk,
pi_md_settings, 0, status);
接下来,我们创建一对 i/o 队列。由于提交队列需要与其一起传递其配套完成队列,因此我们首先创建完成队列。请注意,队列创建例程采用参数重叠群。如果设置了重叠群,则队列将放置在连续内存中,否则将为该队列创建 prp 列表。除了创建实际队列之外,vip 还会在队列周围创建围栏,以验证对队列的内存访问。从控制器(例如)从完成队列读取的尝试将被标记为无效的访问尝试。实际队列 id 是随机的(在法律和用户可配置的约束范围内)。
// create the i/o queues
num_q_entries = 10;
contig = 1; // contiguous queue
scriptcreateiocplq(ctlr_id, num_q_entries,
contig, …, cplq_id, …, status);
contig = 0; // prp-based queue
scriptcreateiosubq(ctlr_id, num_q_entries,
contig, cplq_id …, subq_id, …, status);
一旦我们创建了 i/o 队列,我们就可以开始执行 i/o.使用 scriptwrite() 和 scriptread() 调用,我们将数据发送到控制器并立即检索相同的数据。数据的底层数据结构(在主机内存中)由 vip 自动构建。请注意 use_offset 参数(与我们的队列创建任务一样)来控制我们是否生成 prp 和 prp 列表偏移量(分别由 min/max_prp_dword_offset_var 和 min/max_prp_list_dword_offset 控制)。由于我们内置了记分板,我们不必比较从写入的数据读取的数据,vip 正在根据其卷影副本检查返回的数据,该卷影副本正在跟踪成功向控制器写入的 vip。
// do our i/o write then read with a random lba/length
data_buf_t #(dword_t) wbuf, rbuf; // write/read data buffers
num_blks = randbetween(1, ns_size_in_blks);
lba = randbetween(0, ns_size_in_blks – num_blks);
num_dwords = (blk_size_in_bytes / 4) * num_blks;
wbuf = new(num_dwords);
for (int idx = 0 ; idx < num_dwords ; idx++) // fill the buffer
wbuf.setdata(idx, { 16’hdada, idx[15:0] } );
scriptwrite(ctlr_id, subq_id, lba, nsid, wbuf, num_blks,
use_offset, …, status);
// we’ll read the same lba since we know it’s been written
scriptread(ctlr_id, subq_id, lba, nsid, rbuf, num_blks,
use_offset, …, status);
// do what you’d like with the rbuf (that’s the data we just read).
大功告成!
希望这能让我们完成大部分基础知识。您应该对vip的操作有很好的感觉。同样,其中许多任务都有更多的参数,允许更多的控制和错误注入,但我们的目标是在不处理更深奥的功能的情况下完成。如果您有vip手边的vip,请随意浏览示例:它们应该看起来很熟悉。
声光控延时开关原理与制作
SiliconLottery特挑一批i7-9700K处理器开卖 全核5.1GHz售价569.99美元
智能家居是否有安全性的威胁
通过不同的方法,试图打开人工智能「黑匣子」
LTspice:简单理想化的二极管
NVMe VIP架构:主机功能
智慧城市管理,监控指挥中心大屏幕只选海盛翔和激光无缝大屏
关于环境监测系统与5G通讯网络之间的联系
传感器系统的工作原理及如何使用进行生物计量的测量
【技术干货】如何掌握天线设计?
LTC1421应用电路
USB-C PD系统的保护挑战及集成解决方案
基于MAX7219芯片和BC7281控制芯片实现LED显示的两种方式对比分析
dcdc降压电路工作原理
DARPA正在开发终身学习机器、美陆军成立生物识别实验室
世界移动通信大会落下帷幕 ST最新物联网和智能驾驶解决方案纷纷亮相
怎样使用BC547晶体管制作一个简单的雨水报警电路
单身狗福利 首款伴侣机器人面世:能让人类心理和生理到达巅峰
微雪电子SD卡存储模块简介
恩智浦发布一套完整的新雷达传感器芯片组解决方案