news 2026/4/22 21:52:15

Vortex RTLSIM仿真环境简介(POCL)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vortex RTLSIM仿真环境简介(POCL)

目录

前言

一、POCL仿例列表及功能框图

二、POCL仿例环境

2.1 APP使用的驱动层函数不同

2.2 APP Makefile不同

2.2.1 编译应用层main.cc

2.2.2 链接APP应用程序

2.2.3 执行应用程序

三、POCL在Vortex中的功能

总结


前言

本篇内容继承上一篇"Vortex RTLSIM仿真环境简介",着重描述跑POCL(Portable OpenCL)仿例在环境上的不同点。

本系列"探索Vortex开源GPGPU:RISC-V SIMT架构"

一、POCL仿例列表及功能框图

Vortex POCL仿例位于$VORTEX_HOME/tests/opencl,共有20个例子。功能框图如下。

二、POCL仿例环境

2.1 APP使用的驱动层函数不同

POCL仿例使用了不同的头文件,如下的"CL/opencl.h"。

紫色粗体字的每个步骤中,都使用了cl*的函数,这个是POCL(Portable OpenCL)封装的驱动层函数。根据函数的近似功能,列出对比表格。之所以是"近似",是因为功能类似,传参却差别很大。

POCL仿例VX仿例
clGetPlatformIDs/clGetDeviceIDsvx_dev_open
clCreateBuffervx_mem_alloc

clCreateProgramWithSource/clBuildProgram/clCreateKernel

Makefile编译RISC-V程序+vx_upload_kernel_file
clSetKernelArgvx_upload_bytes
clCreateCommandQueueN/A
clEnqueueWriteBuffervx_copy_to_dev

clEnqueueNDRangeKernel

vx_start
clFinishvx_ready_wait
clEnqueueReadBuffervx_copy_from_dev

cl_device_id device_id = NULL;

uint8_t *kernel_bin = NULL;

#define KERNEL_NAME "vecadd"

int main (int argc, char **argv) {
............
cl_platform_id platform_id;

size_t kernel_size;

//Getting platform and device information
CL_CHECK(clGetPlatformIDs(1, &platform_id, NULL));
CL_CHECK(clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, &device_id, NULL));

printf("Create context\n");
context = CL_CHECK2(clCreateContext(NULL, 1, &device_id, NULL, NULL, &_err));

printf("Allocate device buffers\n");
size_t nbytes = size * sizeof(TYPE);
a_memobj = CL_CHECK2(clCreateBuffer(context, CL_MEM_READ_ONLY, nbytes, NULL, &_err));
b_memobj = CL_CHECK2(clCreateBuffer(context, CL_MEM_READ_ONLY, nbytes, NULL, &_err));
c_memobj = CL_CHECK2(clCreateBuffer(context, CL_MEM_WRITE_ONLY, nbytes, NULL, &_err));

printf("Create program from kernel source\n");
if (0 !=read_kernel_file("kernel.cl", &kernel_bin, &kernel_size))
return -1;
program = CL_CHECK2(clCreateProgramWithSource(
context, 1, (const char**)&kernel_bin, &kernel_size, &_err));

//Build program
CL_CHECK(clBuildProgram(program, 1, &device_id, NULL, NULL, NULL));

//Create kernel
kernel = CL_CHECK2(clCreateKernel(program, KERNEL_NAME, &_err));

//Set kernel arguments
CL_CHECK(clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&a_memobj));
CL_CHECK(clSetKernelArg(kernel, 1, sizeof(cl_mem), (void *)&b_memobj));
CL_CHECK(clSetKernelArg(kernel, 2, sizeof(cl_mem), (void *)&c_memobj));

//Allocate memories for input arrays and output arrays.
std::vector<TYPE> h_a(size);
std::vector<TYPE> h_b(size);
std::vector<TYPE> h_c(size);

//Generate input values
for (uint32_t i = 0; i < size; ++i) {
h_a[i] = Comparator<TYPE>::generate();
h_b[i] = Comparator<TYPE>::generate();
}

//Creating command queue
commandQueue= CL_CHECK2(clCreateCommandQueue(context, device_id, 0, &_err));

printf("Upload source buffers\n");
CL_CHECK(clEnqueueWriteBuffer(commandQueue, a_memobj, CL_TRUE, 0, nbytes, h_a.data(), 0, NULL, NULL));
CL_CHECK(clEnqueueWriteBuffer(commandQueue, b_memobj, CL_TRUE, 0, nbytes, h_b.data(), 0, NULL, NULL));

printf("Execute the kernel\n");
size_t global_work_size[1] = {size};
size_t local_work_size[1] = {1};
auto time_start = std::chrono::high_resolution_clock::now();
CL_CHECK(clEnqueueNDRangeKernel(commandQueue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL));
CL_CHECK(clFinish(commandQueue));
auto time_end = std::chrono::high_resolution_clock::now();
double elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_start).count();
printf("Elapsed time: %lg ms\n", elapsed);

printf("Download destination buffer\n");
CL_CHECK(clEnqueueReadBuffer(commandQueue, c_memobj, CL_TRUE, 0, nbytes, h_c.data(), 0, NULL, NULL));

......

2.2 APP Makefile不同

POCL仿例同样用blackbox.sh来跑,也是分为4个步骤(详见前一篇),前3步都一样,唯一差别是第4步,APP目录下的$ROOT_DIR/tests/opencl/vecadd/Makefile内容有很大差别。POCL仿例的Makefile主要执行了3个命令。从第1个步骤开始,就出现POCL相关目录,如红色字体所示。

CONFIGS="-DTRACING_ALL -DTRACING_ALL" ./ci/blackbox.sh--driver=rtlsim --app=vecadd --debug=1 --cores=2 --clusters=2 --args=-n64 --l2cache --l3cache --warps=4 --threads=4

2.2.1 编译应用层main.cc

g++ -std=c++17 -Wall -Wextra -Wfatal-errors -Wno-deprecated-declarations -Wno-unused-parameter -Wno-narrowing -pthread -I$TOOLDIR/pocl/include-DTRACING_ALL -DTRACING_ALL -DNUM_CORES=2 -DNUM_CLUSTERS=2 -DL2_ENABLE -DL3_ENABLE -DNUM_WARPS=4 -DNUM_THREADS=4 -g -O0 -c $VORTEX_HOME/tests/opencl/vecadd/main.cc-omain.cc.o

cp $VORTEX_HOME/tests/opencl/vecadd/kernel.cl kernel.cl
cp $VORTEX_HOME/tests/opencl/vecadd/common.h common.h

2.2.2 链接APP应用程序

g++ -std=c++17 -Wall -Wextra -Wfatal-errors -Wno-deprecated-declarations -Wno-unused-parameter -Wno-narrowing -pthread -I$TOOLDIR/pocl/include-DTRACING_ALL -DTRACING_ALL -DNUM_CORES=2 -DNUM_CLUSTERS=2 -DL2_ENABLE -DL3_ENABLE -DNUM_WARPS=4 -DNUM_THREADS=4 -g -O0main.cc.o-Wl,-rpath,$TOOLDIR/llvm-vortex/lib -L$ROOT_DIR/runtime-lvortex-L$TOOLDIR/pocl/lib-lOpenCL-ovecadd

//同样用到前3步生成的$ROOT_DIR/runtime/libvortex.so,回调函数实现C++ TB

//包含动态分配地址,控制RAM和verilog TB数据传输

//下载RISC-V GPGPU运行程序,控制内核开始运行,上传运行结果等底层驱动功能

2.2.3 执行应用程序

这一步差别很大。除了引入POCL的相关目录,多了3个跟kernel RISC-V的编译和链接有关的变量,如红色/紫色/蓝色字体所示。一般的VX仿例是用独立的命令放在Makefile里面来编译和链接。这个是包在环境变量里面,传给APP。看过去是交给APP里面的POCL函数来做这个步骤,猜测是2.1列表中的"clCreateProgramWithSource/clBuildProgram/clCreateKernel",因为如果把$ROOT_DIR/tests/opencl/vecadd/kernel.cl故意改个名称,前面几个POCL函数都执行了(可以看到log里面有"Allocate device buffers"),但是它的下一步就报错"Create program from kernel source Failed to load kernel"。从这一点可以看出POCL是runtime的编译RISC-V kernel文件。

LD_LIBRARY_PATH=$TOOLDIR/pocl/lib:$ROOT_DIR/runtime:$TOOLDIR/llvm-vortex/lib:POCL_VORTEX_XLEN=32LLVM_PREFIX=$TOOLDIR/llvm-vortexPOCL_VORTEX_BINTOOL="OBJCOPY=$TOOLDIR/llvm-vortex/bin/llvm-objcopy $VORTEX_HOME/kernel/scripts/vxbin.py"POCL_VORTEX_CFLAGS="-march=rv32imaf -mabi=ilp32f -O3 -mcmodel=medany --sysroot=$TOOLDIR/riscv32-gnu-toolchain/riscv32-unknown-elf --gcc-toolchain=$TOOLDIR/riscv32-gnu-toolchain -fno-rtti -fno-exceptions -nostartfiles -nostdlib -fdata-sections -ffunction-sections -I$ROOT_DIR/hw -I$VORTEX_HOME/kernel/include -DXLEN_32 -DNDEBUG -DTRACING_ALL -DTRACING_ALL -DNUM_CORES=2 -DNUM_CLUSTERS=2 -DL2_ENABLE -DL3_ENABLE -DNUM_WARPS=4 -DNUM_THREADS=4 -Xclang -target-feature -Xclang +vortex -Xclang -target-feature -Xclang +zicond -mllvm -disable-loop-idiom-all "POCL_VORTEX_LDFLAGS="-Wl,-Bstatic,--gc-sections,-T$VORTEX_HOME/kernel/scripts/link32.ld,--defsym=STARTUP_ADDR=0x80000000$ROOT_DIR/kernel/libvortex.a-L$TOOLDIR/libc32/lib -lm -lc $TOOLDIR/libcrt32/lib/baremetal/libclang_rt.builtins-riscv32.a"POCL_DEBUG=all VORTEX_DRIVER=rtlsim ./vecadd -n128

POCL APP目录下的kernel很简单。

#include "common.h"

__kernel void vecadd (__global const TYPE *A,
__global const TYPE *B,
__global TYPE *C)
{
int gid = get_global_id(0);
C[gid] = A[gid] + B[gid];
}

作为对比,VX仿例的kernel如下。

#include <vx_spawn.h>
#include "common.h"

void kernel_body(kernel_arg_t* __UNIFORM__ arg) {
auto src0_ptr = reinterpret_cast<TYPE*>(arg->src0_addr);
auto src1_ptr = reinterpret_cast<TYPE*>(arg->src1_addr);
auto dst_ptr = reinterpret_cast<TYPE*>(arg->dst_addr);

dst_ptr[blockIdx.x] = src0_ptr[blockIdx.x] + src1_ptr[blockIdx.x];
}

int main() {
kernel_arg_t* arg = (kernel_arg_t*)csr_read(VX_CSR_MSCRATCH);
return vx_spawn_threads(1, &arg->num_points, nullptr, (vx_kernel_func_cb)kernel_body, arg);
}

三、POCL在Vortex中的功能

综合2.2.3的描述,可以推断POCL在Vortex中的功能,对应前面的框图。

  • PC端,首先是中间层适配功能,因为标准的opencl函数和Vortex的C++ TB(参见前一章的回调函数,体现在$VORTEX_HOME/runtime/stub/vortex.cpp)差别挺大,必须有POCL来作为中间的桥梁,这些中间层代码最终要嵌入到APP,作为APP的一部分
  • PC端,其次是运行时编译功能,POCL能通过约定的环境变量来获知如何编译RISC-V kernel文件,如2.2.3中标红色/紫色/蓝色的那些变量。它们能提供的信息:用什么样的编译器,比如是ARM还是RISC-V;编译和链接的参数;link script,启动地址等等
  • DEV端,中间层适配功能,符合opencl标准的kernel.cl 和VX同样功能的仿例kernel.cpp有所差别,所以POCL要提供中间层代码
  • DEV端,KERNEL的其他库文件,这个通过环境变量约定。比如2.2.3环境变量中的$ROOT_DIR/kernel/libvortex.a,主要是6个自定义指令集的功能,warp和thread管理

总结

本文对比了Vortex RTLSIM仿真环境中运行POCL仿例与VX仿例的关键差异。POCL仿例位于$VORTEX_HOME/tests/opencl目录下,共20个。主要区别在于:1)驱动层使用POCL封装的cl*函数替代VX的vx_*函数;2)Makefile流程不同,POCL通过环境变量传递RISC-V编译参数,由POCL函数完成内核编译;3)内核代码结构差异,POCL采用标准语法而VX使用特定API。这些差异体现了两种编程接口在编程思想和实现方式上的不同。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 11:17:33

高效PDF转换工具:macOS虚拟打印机的终极解决方案

高效PDF转换工具&#xff1a;macOS虚拟打印机的终极解决方案 【免费下载链接】RWTS-PDFwriter An OSX print to pdf-file printer driver 项目地址: https://gitcode.com/gh_mirrors/rw/RWTS-PDFwriter 还在为文档格式转换而烦恼吗&#xff1f;每次需要将Word、Excel或网…

作者头像 李华
网站建设 2026/4/23 13:00:48

Outfit字体完全指南:9种字重打造专业视觉体验

Outfit字体完全指南&#xff1a;9种字重打造专业视觉体验 【免费下载链接】Outfit-Fonts The most on-brand typeface 项目地址: https://gitcode.com/gh_mirrors/ou/Outfit-Fonts 还在为寻找一款既现代又实用的字体而烦恼吗&#xff1f;你的完美解决方案来了&#xff0…

作者头像 李华
网站建设 2026/4/23 12:53:27

BasicSR图像视频修复工具箱完整使用指南

BasicSR图像视频修复工具箱完整使用指南 【免费下载链接】BasicSR 项目地址: https://gitcode.com/gh_mirrors/bas/BasicSR 在当今视觉内容爆炸式增长的时代&#xff0c;图像和视频修复技术变得愈发重要。BasicSR作为一款基于PyTorch的开源工具箱&#xff0c;为研究者和…

作者头像 李华
网站建设 2026/4/23 12:52:36

Minecraft种子自动破解工具SeedCracker深度解析

Minecraft种子自动破解工具SeedCracker深度解析 【免费下载链接】SeedCracker Fast, Automatic In-Game Seed Cracker for Minecraft. 项目地址: https://gitcode.com/gh_mirrors/se/SeedCracker 在Minecraft的世界探索中&#xff0c;获取世界种子是理解地图生成逻辑的关…

作者头像 李华
网站建设 2026/4/23 12:57:31

Windows AirPlay音频接收器Shairport4w深度解析

Windows AirPlay音频接收器Shairport4w深度解析 【免费下载链接】Shairport4w An AirPlay Audio-Receiver for your Windows-PC 项目地址: https://gitcode.com/gh_mirrors/sh/Shairport4w 在数字音频生态中&#xff0c;苹果设备的AirPlay协议以其出色的音质和便捷性广受…

作者头像 李华
网站建设 2026/4/23 13:01:37

BongoCat桌面萌宠:让输入操作变得生动有趣的键盘猫咪伴侣

BongoCat桌面萌宠&#xff1a;让输入操作变得生动有趣的键盘猫咪伴侣 【免费下载链接】BongoCat 让呆萌可爱的 Bongo Cat 陪伴你的键盘敲击与鼠标操作&#xff0c;每一次输入都充满趣味与活力&#xff01; 项目地址: https://gitcode.com/gh_mirrors/bong/BongoCat 还在…

作者头像 李华