news 2026/4/29 18:23:55

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/29 14:50:48

解锁B站宝藏!BiliTools跨平台工具箱完整使用攻略

解锁B站宝藏&#xff01;BiliTools跨平台工具箱完整使用攻略 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持视频、音乐、番剧、课程下载……持续更新 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools…

作者头像 李华
网站建设 2026/4/29 14:50:32

Open-AutoGLM支持模拟器吗?多环境兼容性测试报告

Open-AutoGLM支持模拟器吗&#xff1f;多环境兼容性测试报告 1. 引言&#xff1a;Open-AutoGLM – 智谱开源的手机端AI Agent框架 随着大模型技术向终端设备下沉&#xff0c;AI智能体在移动场景中的应用正逐步从概念走向落地。Open-AutoGLM 是由智谱AI开源的一款面向手机端的…

作者头像 李华
网站建设 2026/4/28 19:20:23

开源数据分析工具终极指南:免费统计分析的完整解决方案

开源数据分析工具终极指南&#xff1a;免费统计分析的完整解决方案 【免费下载链接】jasp-desktop JASP aims to be a complete statistical package for both Bayesian and Frequentist statistical methods, that is easy to use and familiar to users of SPSS 项目地址: …

作者头像 李华
网站建设 2026/4/25 4:03:11

开源大模型新星:Qwen3 Embedding系列行业应用趋势分析

开源大模型新星&#xff1a;Qwen3 Embedding系列行业应用趋势分析 1. 技术背景与趋势洞察 随着大语言模型在自然语言处理领域的广泛应用&#xff0c;文本嵌入&#xff08;Text Embedding&#xff09;作为连接语义理解与下游任务的关键技术&#xff0c;正迎来新一轮的技术迭代…

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

OpenCore Simplify:5分钟搞定黑苹果EFI配置的终极指南

OpenCore Simplify&#xff1a;5分钟搞定黑苹果EFI配置的终极指南 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 还在为复杂的黑苹果EFI配置而烦恼吗…

作者头像 李华
网站建设 2026/4/23 9:28:11

Qwen3-Reranker-4B参数详解:4B模型优化配置指南

Qwen3-Reranker-4B参数详解&#xff1a;4B模型优化配置指南 1. 引言 随着信息检索和自然语言处理任务的不断演进&#xff0c;文本重排序&#xff08;Re-ranking&#xff09;作为提升搜索质量的关键环节&#xff0c;受到了广泛关注。传统的检索系统往往依赖于BM25等词频匹配算…

作者头像 李华