上一篇文章中,介绍了一个基本的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 << blk_size_pow2; // Convert ns_size_in_blks = ByteSwap64({identify_buffer.GetData(8 >> 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,请随意浏览示例:它们应该看起来很熟悉。
审核编辑:郭婷
全部0条评论
快来发表一下你的评论吧 !