SystemC中SC_THREAD内调用wait()触发E519错误的问题求助
问题描述
使用SystemC实现卷积神经网络仿真,Conv2d模块通过SC_THREAD注册前向传播函数forward_pass,函数内使用wait()等待输入就绪信号。调用sc_start()启动仿真时触发以下错误:
Error: (E519) wait() is only allowed in SC_THREADs and SC_CTHREADs:
in SC_METHODs use next_trigger() instead
In file: ../../../src/sysc/kernel/sc_wait.cpp:94
make: *** [all] Error 1
尽管已明确将forward_pass注册为SC_THREAD,但错误仍然发生。模块定义与测试代码如下:
Conv2d模块定义(Conv2d.h)
// Conv2d.h #ifndef CONV2D_H #define CONV2D_H #include <systemc.h> SC_MODULE(Conv2d) { private: unsigned int in_channels, out_channels; unsigned int kernel_height, kernel_width; unsigned int stride_height, stride_width; unsigned int padding_height, padding_width; bool apply_relu; unsigned int input_feature_map_height, input_feature_map_width; unsigned int output_feature_map_height, output_feature_map_width; std::vector<std::vector<std::vector<std::vector<float>>>> weights; std::vector<float> bias; public: sc_vector<sc_fifo_in<float>> input_feature_map; sc_vector<sc_fifo_out<float>> output_feature_map; sc_in<bool> input_ready; sc_out<bool> output_ready; SC_HAS_PROCESS(Conv2d); Conv2d(sc_module_name name) : in_channels(1), out_channels(1), kernel_height(3), kernel_width(3), stride_height(1), stride_width(1), padding_height(1), padding_width(1), apply_relu(false), input_feature_map_height(3), input_feature_map_width(3), output_feature_map_height(3), output_feature_map_width(3), input_ready("input_ready"), output_ready("output_ready") { initialize_parameters(); SC_THREAD(forward_pass); sensitive << input_ready.pos(); dont_initialize(); } void configure(unsigned int in_c, unsigned int out_c, std::pair<unsigned int, unsigned int> kernel_size, std::pair<unsigned int, unsigned int> stride, std::pair<unsigned int, unsigned int> padding, bool relu, unsigned int in_feature_map_size, unsigned int out_feature_map_size, std::pair<unsigned int, unsigned int> in_feature_map_dimension, std::pair<unsigned int, unsigned int> out_feature_map_dimension) { in_channels = in_c; out_channels = out_c; kernel_height = kernel_size.first; kernel_width = kernel_size.second; stride_height = stride.first; stride_width = stride.second; padding_height = padding.first; padding_width = padding.second; apply_relu = relu; input_feature_map_height = in_feature_map_dimension.first; input_feature_map_width = in_feature_map_dimension.second; input_feature_map.init(in_feature_map_size); output_feature_map_height = out_feature_map_dimension.first; output_feature_map_width = out_feature_map_dimension.second; output_feature_map.init(out_feature_map_size); initialize_parameters(); } void forward_pass() { while(true) { wait(); for (unsigned int out_c = 0; out_c < out_channels; ++out_c) { for (unsigned int h = 0; h < output_feature_map_height; ++h) { for (unsigned int w = 0; w < output_feature_map_width; ++w) { float sum = 0.0; for (unsigned int in_c = 0; in_c < in_channels; ++in_c) { for (unsigned int kh = 0; kh < kernel_height; ++kh) { for (unsigned int kw = 0; kw < kernel_width; ++kw) { int h_index = h * stride_height + kh - padding_height; int w_index = w * stride_width + kw - padding_width; if (h_index >= 0 && h_index < input_feature_map_height && w_index >= 0 && w_index < input_feature_map_width) { int input_index = in_c * input_feature_map_height * input_feature_map_width + h_index * input_feature_map_width + w_index; sum += input_feature_map[input_index].read() * weights[out_c][in_c][kh][kw]; } } } } sum += bias[out_c]; if (apply_relu && sum < 0) { sum = 0.0; } int output_index = out_c * output_feature_map_height * output_feature_map_width + h * output_feature_map_width + w; output_feature_map[output_index].write(sum); } } } output_ready.write(true); wait(1, SC_NS); output_ready.write(false); } } }; #endif // CONV2D_H
测试代码(main.cpp)
// main.cpp #include <systemc.h> #include <vector> #include <tuple> #include <iostream> #include <iomanip> #include <fstream> #include <Conv2d.h> #include <helpers.h> int sc_main(int argc, char* argv[]) { Conv2d conv_layer("ConvolutionalLayer"); conv_layer.configure( 3, 64, std::make_pair(11, 11), std::make_pair(4, 4), std::make_pair(2, 2), true, 150528, 193600, std::make_pair(224, 224), std::make_pair(55, 55) ); auto conv_layer_shape = conv_layer.weight_shape(); int out_channels = std::get<0>(conv_layer_shape); int in_channels = std::get<1>(conv_layer_shape); int rows = std::get<2>(conv_layer_shape); int cols = std::get<3>(conv_layer_shape); auto weights = reshape_weights(load_weights("./data/conv1_weight.txt"), out_channels, in_channels, rows, cols); auto biases = load_weights("./data/conv1_bias.txt"); conv_layer.load_parameters(weights, biases); auto image_data = load_image("./data/cat.txt"); sc_vector<sc_fifo<float>> input_feature_map_sig("input_feature_map_sig", 150528); for (size_t i = 0; i < input_feature_map_sig.size(); i++) { conv_layer.input_feature_map[i](input_feature_map_sig[i]); } sc_vector<sc_fifo<float>> output_feature_map_sig("output_feature_map_sig", 193600); for (size_t i = 0; i < output_feature_map_sig.size(); i++) { conv_layer.output_feature_map[i](output_feature_map_sig[i]); } sc_signal<bool> input_ready_sig; conv_layer.input_ready(input_ready_sig); sc_signal<bool> output_ready_sig; conv_layer.output_ready(output_ready_sig); for (size_t i = 0; i < input_feature_map_sig.size(); i++) { input_feature_map_sig[i].write(image_data[i]); } input_ready_sig.write(true); sc_start(); return 0; }
原因分析
错误的核心原因是**wait()调用发生在非SystemC进程上下文**,而非forward_pass线程内:
- 代码中
initialize_parameters()函数在Conv2d构造函数和configure()函数中被调用,这两个函数均执行于sc_main()上下文(不属于任何SC_THREAD/SC_CTHREAD)。如果initialize_parameters()中存在wait()调用(或间接调用了会触发wait()的SystemC操作,如sc_fifo::read()),就会触发E519错误。 - 当前测试代码中,
input_ready_sig.write(true)在sc_start()之前执行,导致信号初始值为true,线程的wait()无法检测到posedge事件,但这不会直接触发错误,只是会导致线程一直挂起。
解决方案
1. 修复initialize_parameters()函数
检查并修改initialize_parameters(),移除所有SystemC同步操作:
- 权重/偏置初始化仅使用纯C++逻辑(如随机数生成、文件读取),不要调用
wait()、sc_fifo的阻塞读写等操作。 - 示例实现(仅供参考):
void initialize_parameters() { // 初始化权重:(out_channels, in_channels, kernel_height, kernel_width) weights.resize(out_channels, std::vector<std::vector<std::vector<float>>>( in_channels, std::vector<std::vector<float>>( kernel_height, std::vector<float>(kernel_width, 0.01f) ) )); // 初始化偏置 bias.resize(out_channels, 0.0f); }
2. 调整测试逻辑,确保信号触发在仿真上下文内
将input_ready_sig的触发逻辑移到SystemC进程中,确保posedge事件在仿真开始后产生:
- 封装测试逻辑到
Testbench模块(SystemC最佳实践):
SC_MODULE(Testbench) { sc_signal<bool> input_ready_sig; sc_signal<bool> output_ready_sig; sc_vector<sc_fifo<float>> input_fifos; sc_vector<sc_fifo<float>> output_fifos; Conv2d conv_layer; SC_CTOR(Testbench) : conv_layer("conv_layer"), input_fifos("input_fifos", 150528), output_fifos("output_fifos", 193600) { // 连接端口 conv_layer.input_ready(input_ready_sig); conv_layer.output_ready(output_ready_sig); for (size_t i = 0; i < input_fifos.size(); ++i) { conv_layer.input_feature_map[i](input_fifos[i]); } for (size_t i = 0; i < output_fifos.size(); ++i) { conv_layer.output_feature_map[i](output_fifos[i]); } // 注册测试线程 SC_THREAD(run_test); } void run_test() { // 加载图像数据 auto image_data = load_image("./data/cat.txt"); // 写入输入FIFO for (size_t i = 0; i < input_fifos.size(); ++i) { input_fifos[i].write(image_data[i]); } // 等待数据写入完成,触发输入就绪信号 wait(1, SC_NS); input_ready_sig.write(true); // 等待输出就绪 wait(output_ready_sig.pos()); // 读取输出数据(示例) // for (size_t i = 0; i < output_fifos.size(); ++i) { // float val = output_fifos[i].read(); // std::cout << "Output " << i << ": " << val << std::endl; // } // 停止仿真 sc_stop(); } }; int sc_main(int argc, char* argv[]) { Testbench tb("testbench"); // 配置卷积层 tb.conv_layer.configure( 3, 64, std::make_pair(11, 11), std::make_pair(4, 4), std::make_pair(2, 2), true, 150528, 193600, std::make_pair(224, 224), std::make_pair(55, 55) ); // 加载权重和偏置 auto conv_layer_shape = tb.conv_layer.weight_shape(); int out_channels = std::get<0>(conv_layer_shape); int in_channels = std::get<1>(conv_layer_shape); int rows = std::get<2>(conv_layer_shape); int cols = std::get<3>(conv_layer_shape); auto weights = reshape_weights(load_weights("./data/conv1_weight.txt"), out_channels, in_channels, rows, cols); auto biases = load_weights("./data/conv1_bias.txt"); tb.conv_layer.load_parameters(weights, biases); // 启动仿真,指定超时时间避免无限挂起 sc_start(1000, SC_NS); return 0; }
3. 其他优化建议
- 调用
sc_start()时指定仿真超时时间(如sc_start(1000, SC_NS)),避免因线程挂起导致仿真无限运行。 - 确保所有端口连接操作在仿真启动前完成,避免运行时连接错误。
内容的提问来源于stack exchange,提问作者weinfailed
相关产品推荐
相关产品推荐

