自写代码来理解 get_global_id 和 get_global_size

<2022-01-24 周一>

《OpenCL编程指南》第三章

自写代码来理解get_global_idget_global_size

使用本书第三章中关于输入信号卷积的代码来进行理解,见随书代码“src/Chapter_3/OpenCLConvolution”,附代码如下:

//
// Book:      OpenCL(R) Programming Guide
// Authors:   Aaftab Munshi, Benedict Gaster, Timothy Mattson, James Fung, Dan Ginsburg
// ISBN-10:   0-321-74964-2
// ISBN-13:   978-0-321-74964-2
// Publisher: Addison-Wesley Professional
// URLs:      http://safari.informit.com/9780132488006/
//            http://www.openclprogrammingguide.com
//

// Convolution.cpp
//
//    This is a simple example that demonstrates OpenCL platform, device, and context
//    use.

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <iomanip>

#ifdef __APPLE__
#include <OpenCL/cl.h>
#else
#include <CL/cl.h>
#endif

#if !defined(CL_CALLBACK)
#define CL_CALLBACK
#endif

// Constants
const unsigned int inputSignalWidth = 8;
const unsigned int inputSignalHeight = 8;

cl_uint inputSignal[inputSignalWidth][inputSignalHeight] =
{
  {3, 1, 1, 4, 8, 2, 1, 3},
  {4, 2, 1, 1, 2, 1, 2, 3},
  {4, 4, 4, 4, 3, 2, 2, 2},
  {9, 8, 3, 8, 9, 0, 0, 0},
  {9, 3, 3, 9, 0, 0, 0, 0},
  {0, 9, 0, 8, 0, 0, 0, 0},
  {3, 0, 8, 8, 9, 4, 4, 4},
  {5, 9, 8, 1, 8, 1, 1, 1}
};

const unsigned int outputSignalWidth = 6;
const unsigned int outputSignalHeight = 6;

cl_uint outputSignal[outputSignalWidth][outputSignalHeight];

const unsigned int maskWidth = 3;
const unsigned int maskHeight = 3;

cl_uint mask[maskWidth][maskHeight] =
{
  {1, 1, 1}, {1, 0, 1}, {1, 1, 1},
};

///
// Function to check and handle OpenCL errors
inline void
checkErr(cl_int err, const char * name)
{
  if (err != CL_SUCCESS) {
    std::cerr << "ERROR: " << name << " (" << err << ")" << std::endl;
    exit(EXIT_FAILURE);
  }
}

void CL_CALLBACK contextCallback(
  const char * errInfo,
  const void * private_info,
  size_t cb,
  void * user_data)
{
  std::cout << "Error occured during context use: " << errInfo << std::endl;
  // should really perform any clearup and so on at this point
  // but for simplicitly just exit.
  exit(1);
}

///
//	main() for Convoloution example
//
int main(int argc, char** argv)
{
  cl_int errNum;
  cl_uint numPlatforms;
  cl_uint numDevices;
  cl_platform_id * platformIDs;
  cl_device_id * deviceIDs;
  cl_context context = NULL;
  cl_command_queue queue;
  cl_program program;
  cl_kernel kernel;
  cl_mem inputSignalBuffer;
  cl_mem outputSignalBuffer;
  cl_mem maskBuffer;

  // First, select an OpenCL platform to run on.
  errNum = clGetPlatformIDs(0, NULL, &numPlatforms);
  checkErr(
    (errNum != CL_SUCCESS) ? errNum : (numPlatforms <= 0 ? -1 : CL_SUCCESS),
    "clGetPlatformIDs");

  platformIDs = (cl_platform_id *)alloca(
    sizeof(cl_platform_id) * numPlatforms);

  errNum = clGetPlatformIDs(numPlatforms, platformIDs, NULL);
  checkErr(
    (errNum != CL_SUCCESS) ? errNum : (numPlatforms <= 0 ? -1 : CL_SUCCESS),
    "clGetPlatformIDs");

  // Iterate through the list of platforms until we find one that supports
  // a CPU device, otherwise fail with an error.
  deviceIDs = NULL;
  cl_uint i;
  for (i = 0; i < numPlatforms; i++)
  {
    errNum = clGetDeviceIDs(
      platformIDs[i],
      CL_DEVICE_TYPE_CPU,
      0,
      NULL,
      &numDevices);
    if (errNum != CL_SUCCESS && errNum != CL_DEVICE_NOT_FOUND)
    {
      checkErr(errNum, "clGetDeviceIDs");
    }
    else if (numDevices > 0)
    {
      deviceIDs = (cl_device_id *)alloca(sizeof(cl_device_id) * numDevices);
      errNum = clGetDeviceIDs(
        platformIDs[i],
        CL_DEVICE_TYPE_CPU,
        numDevices,
        &deviceIDs[0],
        NULL);
      checkErr(errNum, "clGetDeviceIDs");
      break;
    }
  }

  // Check to see if we found at least one CPU device, otherwise return
  if (deviceIDs == NULL) {
    std::cout << "No CPU device found" << std::endl;
    exit(-1);
  }

  // Next, create an OpenCL context on the selected platform.
  cl_context_properties contextProperties[] =
  {
      CL_CONTEXT_PLATFORM,
      (cl_context_properties)platformIDs[i],
      0
  };
  context = clCreateContext(
    contextProperties,
    numDevices,
    deviceIDs,
    &contextCallback,
    NULL,
    &errNum);
  checkErr(errNum, "clCreateContext");

  std::ifstream srcFile("Convolution.cl");
  checkErr(srcFile.is_open() ? CL_SUCCESS : -1, "reading Convolution.cl");

  std::string srcProg(
    std::istreambuf_iterator<char>(srcFile),
    (std::istreambuf_iterator<char>()));

  const char * src = srcProg.c_str();
  size_t length = srcProg.length();

  // Create program from source
  program = clCreateProgramWithSource(
    context,
    1,
    &src,
    &length,
    &errNum);
  checkErr(errNum, "clCreateProgramWithSource");

  // Build program
  errNum = clBuildProgram(
    program,
    numDevices,
    deviceIDs,
    NULL,
    NULL,
    NULL);
  if (errNum != CL_SUCCESS)
  {
    // Determine the reason for the error
    char buildLog[16384];
    clGetProgramBuildInfo(
      program,
      deviceIDs[0],
      CL_PROGRAM_BUILD_LOG,
      sizeof(buildLog),
      buildLog,
      NULL);

    std::cerr << "Error in kernel: " << std::endl;
    std::cerr << buildLog;
    checkErr(errNum, "clBuildProgram");
  }

  // Create kernel object
  kernel = clCreateKernel(
    program,
    "convolve",
    &errNum);
  checkErr(errNum, "clCreateKernel");

  // Now allocate buffers
  inputSignalBuffer = clCreateBuffer(
    context,
    CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
    sizeof(cl_uint) * inputSignalHeight * inputSignalWidth,
    static_cast<void *>(inputSignal),
    &errNum);
  checkErr(errNum, "clCreateBuffer(inputSignal)");

  maskBuffer = clCreateBuffer(
    context,
    CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
    sizeof(cl_uint) * maskHeight * maskWidth,
    static_cast<void *>(mask),
    &errNum);
  checkErr(errNum, "clCreateBuffer(mask)");

  outputSignalBuffer = clCreateBuffer(
    context,
    CL_MEM_WRITE_ONLY,
    sizeof(cl_uint) * outputSignalHeight * outputSignalWidth,
    NULL,
    &errNum);
  checkErr(errNum, "clCreateBuffer(outputSignal)");

  // Pick the first device and create command queue.
  queue = clCreateCommandQueue(
    context,
    deviceIDs[0],
    0,
    &errNum);
  checkErr(errNum, "clCreateCommandQueue");

  errNum = clSetKernelArg(kernel, 0, sizeof(cl_mem), &inputSignalBuffer);
  errNum |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &maskBuffer);
  errNum |= clSetKernelArg(kernel, 2, sizeof(cl_mem), &outputSignalBuffer);
  errNum |= clSetKernelArg(kernel, 3, sizeof(cl_uint), &inputSignalWidth);
  errNum |= clSetKernelArg(kernel, 4, sizeof(cl_uint), &maskWidth);
  checkErr(errNum, "clSetKernelArg");

  const size_t globalWorkSize[1] = { outputSignalWidth * outputSignalHeight };
  const size_t localWorkSize[1] = { 1 };

  // Queue the kernel up for execution across the array
  errNum = clEnqueueNDRangeKernel(
    queue,
    kernel,
    1,
    NULL,
    globalWorkSize,
    localWorkSize,
    0,
    NULL,
    NULL);
  checkErr(errNum, "clEnqueueNDRangeKernel");

  errNum = clEnqueueReadBuffer(
    queue,
    outputSignalBuffer,
    CL_TRUE,
    0,
    sizeof(cl_uint) * outputSignalHeight * outputSignalHeight,
    outputSignal,
    0,
    NULL,
    NULL);
  checkErr(errNum, "clEnqueueReadBuffer");

  // Output the result buffer
  for (int y = 0; y < outputSignalHeight; y++)
  {
    for (int x = 0; x < outputSignalWidth; x++)
    {
      std::cout << std::setw(2) << outputSignal[x][y] << " ";
    }
    std::cout << std::endl;
  }

  std::cout << std::endl << "Executed program succesfully." << std::endl;

  return 0;
}
//
// Book:      OpenCL(R) Programming Guide
// Authors:   Aaftab Munshi, Benedict Gaster, Timothy Mattson, James Fung, Dan Ginsburg
// ISBN-10:   0-321-74964-2
// ISBN-13:   978-0-321-74964-2
// Publisher: Addison-Wesley Professional
// URLs:      http://safari.informit.com/9780132488006/
//            http://www.openclprogrammingguide.com
//

// Convolution.cl
//
//    This is a simple kernel performing convolution.

__kernel void convolve(
	const __global  uint * const input,
    __constant uint * const mask,
    __global  uint * const output,
    const int inputWidth,
    const int maskWidth)
{
    const int x = get_global_id(0);
    const int y = get_global_id(1);

    uint sum = 0;
    for (int r = 0; r < maskWidth; r++)
    {
        const int idxIntmp = (y + r) * inputWidth + x;

        for (int c = 0; c < maskWidth; c++)
        {
            sum += mask[(r * maskWidth) + c] * input[idxIntmp + c];
        }
    }

    output[y * get_global_size(0) + x] = sum;
}

注:代码中clGetDeviceIDs()函数使用CL_DEVICE_TYPE_CPU,虽然编译和执行都没有问题,但是在vs2017上单步调试时clCreateContext()这个函数会出错,返回错误-2,这是CL_DEVICE_NOT_AVAILABLE错误:

CL_DEVICE_NOT_AVAILABLE if a device in devices is currently not available even though the device was returned by clGetDeviceIDs.

具体原因不深究了,将CL_DEVICE_TYPE_CPU都改为CL_DEVICE_TYPE_GPU就一切正常了。代码输出:

22 27 19 35 41 12
21 35 10 26 48 24
27 35 29 16 31 26
25 31 33  6 34 48
22 31 39 22  9 37
16 27 43 31  0 38

Executed program succesfully.

我想用c++代码来模拟kernel函数,实现同样的效果,因此我有如下的c++代码:

#include <stdio.h>

int input[8][8] = {
  {3, 1, 1, 4, 8, 2, 1, 3},
  {4, 2, 1, 1, 2, 1, 2, 3},
  {4, 4, 4, 4, 3, 2, 2, 2},
  {9, 8, 3, 8, 9, 0, 0, 0},
  {9, 3, 3, 9, 0, 0, 0, 0},
  {0, 9, 0, 8, 0, 0, 0, 0},
  {3, 0, 8, 8, 9, 4, 4, 4},
  {5, 9, 8, 1, 8, 1, 1, 1}
};

int mask[3][3] = {
  {1, 1, 1},
  {1, 0, 1},
  {1, 1, 1},
};

int output[6][6] = { 0 };

void convolve()
{
  for (int y = 0; y < 6; ++y) {
    for (int x = 0; x < 6; ++x) {
      int sum = 0;
      for (int r = 0; r < 3; r++) {
        const int idxIntmp = (y + r) * 8 + x;

        for (int c = 0; c < 3; c++)
          sum += *((int*)mask + (r * 3) + c) * *((int*)input + idxIntmp + c);
      }
      *((int*)output + y * 6 + x) = sum;
    }
  }
}

int main()
{
  convolve();

  for (int y = 0; y < 6; ++y) {
    for (int x = 0; x < 6; ++x)
      printf("%2d ", output[x][y]);
    printf("\n");
  }
}

对比一下,发现结果不一致:

// 书中的
22 27 19 35 41 12
21 35 10 26 48 24
27 35 29 16 31 26
25 31 33  6 34 48
22 31 39 22  9 37
16 27 43 31  0 38

// 第一次
22 35 39 41 26 42
21 31 43 48 48 43
27 31 35 31 37 42
25 27 26 34 38 30
22 19 16  9 17 23
16 10  6  0 12 11

百思不得其解,确信我的理解是对的,get_global_id(0)get_global_id(1)的值都为6,如果将上述c++代码修改为:

void convolve()
{
  for (int y = 0; y < /*6*/8; ++y) {
    for (int x = 0; x < /*6*/8; ++x) {
      int sum = 0;
      for (int r = 0; r < 3; r++) {
        const int idxIntmp = (y + r) * 8 + x;

        for (int c = 0; c < 3; c++)
          sum += *((int*)mask + (r * 3) + c) * *((int*)input + idxIntmp + c);
      }
      *((int*)output + y * /*6*/8 + x) = sum;
    }
  }
}

输出结果就和书中的一致了:

// 书中的
22 27 19 35 41 12
21 35 10 26 48 24
27 35 29 16 31 26
25 31 33  6 34 48
22 31 39 22  9 37
16 27 43 31  0 38

// 第二次
22 27 19 35 41 12
21 35 10 26 48 24
27 35 29 16 31 26
25 31 33  6 34 48
22 31 39 22  9 37
16 27 43 31  0 38

虽然结果一致,但是此时output[6][6]变量的大小变成了output[8][8],说明已经溢出了,难道get_global_id(0)get_global_id(1)都是8,如果真是这样的话,那我之前的理解就全错了,这也太恐怖了吧!

经过尝试我发现:

funcvalue
get_global_id(0)0-35
get_global_id(1)0
get_global_size(0)36

原来书中代码是以一维数组传入的:

const size_t globalWorkSize[1] = { outputSignalWidth * outputSignalHeight };
const size_t localWorkSize[1] = { 1 };

// Queue the kernel up for execution across the array
errNum = clEnqueueNDRangeKernel(
  queue,
  kernel,
  1,
  NULL,
  globalWorkSize,
  localWorkSize,
  0,
  NULL,
  NULL);
checkErr(errNum, "clEnqueueNDRangeKernel");

所以,我将我的c++代码也进行了相应的修改:

void convolve()
{
  for (int y = 0; y < 1; ++y) {
    for (int x = 0; x < 36; ++x) {
      int sum = 0;
      for (int r = 0; r < 3; r++) {
        const int idxIntmp = (y + r) * 8 + x;

        for (int c = 0; c < 3; c++)
          sum += *((int*)mask + (r * 3) + c) * *((int*)input + idxIntmp + c);
      }
      *((int*)output + y * 0 + x) = sum;
    }
  }
}

这样结果就一致了:

// 书中的
22 27 19 35 41 12
21 35 10 26 48 24
27 35 29 16 31 26
25 31 33  6 34 48
22 31 39 22  9 37
16 27 43 31  0 38

// 第三次
22 27 19 35 41 12
21 35 10 26 48 24
27 35 29 16 31 26
25 31 33  6 34 48
22 31 39 22  9 37
16 27 43 31  0 38

或者我修改一下原书代码,以二维数组传入:

const size_t globalWorkSize[2] = { outputSignalWidth, outputSignalHeight };
const size_t localWorkSize[2] = { 1, 1 };

// Queue the kernel up for execution across the array
errNum = clEnqueueNDRangeKernel(
  queue,
  kernel,
  2,
  NULL,
  globalWorkSize,
  localWorkSize,
  0,
  NULL,
  NULL);
checkErr(errNum, "clEnqueueNDRangeKernel");

我的测试代码仍保持最初的模样,即:

void convolve()
{
  for (int y = 0; y < 6; ++y) {
    for (int x = 0; x < 6; ++x) {
      int sum = 0;
      for (int r = 0; r < 3; r++) {
        const int idxIntmp = (y + r) * 8 + x;

        for (int c = 0; c < 3; c++)
          sum += *((int*)mask + (r * 3) + c) * *((int*)input + idxIntmp + c);
      }
      *((int*)output + y * 6 + x) = sum;
    }
  }
}

这样的修改后的结果也一致,说明我的理解是正确的:

// 书中的
22 35 39 41 26 42
21 31 43 48 48 43
27 31 35 31 37 42
25 27 26 34 38 30
22 19 16  9 17 23
16 10  6  0 12 11

// 第一次
22 35 39 41 26 42
21 31 43 48 48 43
27 31 35 31 37 42
25 27 26 34 38 30
22 19 16  9 17 23
16 10  6  0 12 11

现在就算真正理解了get_global_idget_global_size,最后一个疑问就是为什么以一组数组传入和以二维数组传入得到的结果不一样?

<think>我们参考了用户提供的引用内容,特别是引用[1]、[2]、[4]中关于`get_global_pool`的描述。用户想了解`get_global_pool`的使用方法作用。 根据引用[2][4]: - `uvm_event_pool`是`uvm_object_string_pool`的一个特例,而`uvm_object_string_pool`又继承自`uvm_pool`。 - `uvm_pool`是一个基础类,用于管理基于键(key)的动态关联数组(associative array)。 - `get_global_pool`是`uvm_pool`的一个静态方法,用于获取全局的pool实例(单例模式)。如果全局pool不存在,则先创建再返回。 具体到`uvm_event_pool`,它使用`get_global_pool`来获取一个全局的事件池(event pool),这样就可以在不同的组件中通过事件名(字符串)来共享同一个`uvm_event`对象。 引用[1][3]提到,使用`uvm_event_pool::get_global()`可以简化跨组件的事件共享,它实际上是封装了`get_global_pool().get(key)`的操作。 因此,我们可以总结如下: ### `get_global_pool` 详解 #### 1. 作用 `get_global_pool` 是一个静态方法,用于**获取全局唯一的对象池(object pool)**。该池是一个关联数组(associative array),以字符串(string)为键(key),存储特定类型的对象(如`uvm_event`)。它的主要作用是: - **提供全局访问点**:确保在整个测试环境中只有一个共享的池实例 - **对象复用**:通过键名获取已创建的对象,避免重复创建 - **跨组件共享**:解决不同组件间共享对象(如事件、屏障)的句柄传递问题[^2][^3] #### 2. 使用场景 主要用于需要全局共享对象的场景,例如: - **事件同步**:多个组件共享同一个 `uvm_event`(如 monitor 触发事件,scoreboard 等待事件)[^3] - **屏障同步**:多个进程同步执行阶段(如 `uvm_barrier`) - **自定义共享对象**:扩展用于管理任意需要全局访问的 UVM 对象 #### 3. 使用方法 以 `uvm_event_pool` 为例,说明全局池的使用步骤: ##### (1) 获取全局池实例 调用静态方法 `get_global_pool()` 获取全局池(首次调用时自动创建): ```systemverilog uvm_event_pool global_event_pool = uvm_event_pool::get_global_pool(); ``` ##### (2) 从池中获取对象 使用 `get(key)` 方法按名称获取对象(不存在时自动创建): ```systemverilog uvm_event ev = global_event_pool.get("reset_event"); ``` 等效简(推荐): ```systemverilog uvm_event ev = uvm_event_pool::get_global("reset_event"); // 内部调用 get_global_pool().get() ``` #### 4. 底层机制 - **单例模式**:通过静态变量 `m_global_pool` 确保全局唯一实例[^2] - **惰性初始化**:首次调用 `get_global_pool()` 时创建池对象 - **自动创建**:调用 `get(key)` 时,若键名不存在则自动创建新对象并加入池 #### 5. 代码示例 ##### 组件 A:触发事件 ```systemverilog class compA extends uvm_component; uvm_event reset_ev; function void build_phase(uvm_phase phase); reset_ev = uvm_event_pool::get_global("reset"); // 获取或创建全局事件 endfunction task run_phase(uvm_phase phase); #10ns; reset_ev.trigger(); // 触发全局事件 endtask endclass ``` ##### 组件 B:等待事件 ```systemverilog class compB extends uvm_component; uvm_event reset_ev; function void build_phase(uvm_phase phase); reset_ev = uvm_event_pool::get_global("reset"); // 获取同一个全局事件 endfunction task run_phase(uvm_phase phase); reset_ev.wait_trigger(); // 等待事件触发 `uvm_info("RESET", "Reset detected!", UVM_LOW) endtask endclass ``` #### 6. 关键特点 | 特性 | 说明 | |---------------------|----------------------------------------------------------------------| | **全局单例** | 整个仿真中只有一个全局池实例 | | **键值管理** | 通过字符串键名存取对象 | | **自动创建** | `get(key)` 在对象不存在时自动调用 `new` 创建 | | **线程安全** | UVM 内部保证多线程访问安全(基于 UVM 的进程调度机制) | > 注意:自定义扩展时需继承 `uvm_pool` 并实现类型参数化[^4]。 --- §§相关问题§§ 1. `uvm_event_pool` `uvm_barrier_pool` 有什么异同点? 2. 如何扩展 `uvm_pool` 实现自定义全局共享对象池? 3. 使用全局对象池时需要注意哪些线程同步问题? 4. `get_global()` `get_global_pool().get()` 的性能差异是什么?[^1][^3] </think>### `get_global_pool` 详解使用指南 `get_global_pool` 是 UVM(Universal Verification Methodology)中用于**管理全局共享对象池**的核心方法,主要用于实现跨组件的对象共享(如事件、屏障等)。下面从原理到实践详细说明: --- #### **1. 核心作用** - **全局单例池**: 通过 `get_global_pool()` 获取全局唯一的对象池实例(单例模式)。若池不存在,则自动创建[^2][^4]。 - **对象复用**: 通过字符串键名(`key`)存取对象,避免重复创建(如多个组件共享同一个 `uvm_event`)[^1][^3]。 - **跨组件共享**: 解决不同组件间传递对象句柄的难题(如 monitor scoreboard 共享事件)[^3]。 --- #### **2. 使用步骤** ##### (1) **获取全局池实例** ```systemverilog // 获取全局事件池(不存在则自动创建) uvm_event_pool ev_pool = uvm_event_pool::get_global_pool(); ``` ##### (2) **从池中获取对象** ```systemverilog // 获取键名为 "reset_event" 的 uvm_event(不存在则自动创建) uvm_event ev = ev_pool.get("reset_event"); ``` > **简化法**: > `uvm_event ev = uvm_event_pool::get_global("reset_event");` [^1] ##### (3) **操作共享对象** ```systemverilog // 在组件A中触发事件 ev.trigger(); ``` ```systemverilog // 在组件B中等待事件 ev.wait_trigger(); ``` --- #### **3. 底层原理** 1. **数据结构**: 基于关联数组 `pool[string]`,键名为字符串,值为泛型对象(如 `uvm_event`)[^2][^4]。 ```systemverilog protected T pool[KEY]; // KEY = string ``` 2. **单例实现**: 通过静态变量 `m_global_pool` 确保全局唯一实例[^2]: ```systemverilog static protected this_type m_global_pool; static function this_type get_global_pool(); if (m_global_pool==null) m_global_pool = new("global_pool"); return m_global_pool; endfunction ``` 3. **自动创建机制**: `get(key)` 会检查对象是否存在,不存在则调用 `new` 创建[^4]。 --- #### **4. 关键方法** | 方法 | 作用 | 示例 | |---------------------|----------------------------------------------------------------------|---------------------------------------| | `get_global_pool()` | 获取全局池 | `uvm_event_pool::get_global_pool()` | | `get(string key)` | 按键名获取对象(不存在则创建) | `ev_pool.get("timeout_event")` | | `add(string key, T)`| 添加对象到池(覆盖已存在键值) | `ev_pool.add("new_ev", my_event)` | | `delete(string key)`| 删除指定键名的对象 | `ev_pool.delete("obsolete_ev")` | | `exists(string key)`| 检查键名是否存在 | `if (ev_pool.exists("check_ev")) ...` | --- #### **5. 典型应用场景** 1. **全局事件同步** 多个组件监听同一事件(如复位结束事件): ```systemverilog // 组件A:触发复位结束 uvm_event reset_done = uvm_event_pool::get_global("reset_done"); reset_done.trigger(); // 组件B:等待复位结束 uvm_event reset_done = uvm_event_pool::get_global("reset_done"); reset_done.wait_trigger(); ``` 2. **跨组件屏障同步** 使用 `uvm_barrier_pool` 协调多个并行进程[^4]: ```systemverilog uvm_barrier br = uvm_barrier_pool::get_global("phase_sync"); br.wait_for(); // 等待所有进程到达同步点 ``` 3. **自定义共享对象池** 扩展 `uvm_pool` 管理任意对象: ```systemverilog typedef uvm_pool #(string, my_config) my_config_pool; my_config cfg = my_config_pool::get_global_pool().get("chip_cfg"); ``` --- #### **6. 注意事项** - **键名冲突**: 不同模块使用相同键名会访问同一对象,需规范命名(如 `"envA.reset"`)。 - **线程安全**: 多线程并发访问时需自行添加同步机制(如 `uvm_barrier`)。 - **作用域**: 全局池在仿真周期内持续存在,需手动调用 `delete()` 释放资源[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值