// For read and write #include #include #include #include #include #include // SystemC global header #include "sysc/communication/sc_signal.h" #include "sysc/kernel/sc_module.h" #include // Include common routines #include #include // mkdir #include #include #include #include // Include model header, generated from Verilating "isp.v" #include "Visp.h" // Write Pictures #include "bitmap_image.hpp" SC_MODULE(TB_ISP) { sc_core::sc_in_clk clk; sc_core::sc_in rst; sc_core::sc_in in_ready; // next module ready to receive data sc_core::sc_out out_valid; // next module data valid signal sc_core::sc_out out_data[3]; // next module receive data sc_core::sc_in in_valid; // this module receive data valid signal sc_core::sc_out out_ready; // this module ready to receive data sc_core::sc_in in_data; // this module receive data const uint16_t IN_WIDTH; const uint16_t IN_HEIGHT; const uint32_t IN_SIZE; const uint16_t OUT_WIDTH; const uint16_t OUT_HEIGHT; const uint32_t OUT_SIZE; const uint32_t FLAMES; const std::string OUT_DIR; bool is_done; // when receive all data std::vector image; // the data of image std::vector process_image; // after isp process, the data of image SC_CTOR(TB_ISP, const uint16_t in_width, const uint16_t in_height, const uint16_t out_width, const uint16_t out_height, const uint32_t cnt_flame, const std::string& out_dir) : IN_WIDTH(in_width), IN_HEIGHT(in_height), IN_SIZE(in_width * in_height), OUT_WIDTH(out_width), OUT_HEIGHT(out_height), OUT_SIZE(out_width * out_height), FLAMES(cnt_flame), OUT_DIR(out_dir), process_image(std::vector(out_width * out_height, 0)) { SC_CTHREAD(sendData, clk.pos()); // when clk posedge, exec sendData reset_signal_is(rst, true); // set rst signal SC_CTHREAD(readData, clk.pos()); reset_signal_is(rst, true); // set rst signal } void sendData(void) { // init var uint16_t pos_x = 0, pos_y = 0, cnt_flame = 0; bool is_finish = false; // when send all data // reset out_valid = false; for (auto &data : out_data) data = 0; while (true) { if (in_ready && !is_finish) { // valid and send data out_valid = true; out_data[0] = image[(pos_y + 0) * IN_WIDTH + pos_x]; out_data[1] = image[(pos_y + 1) * IN_WIDTH + pos_x]; out_data[2] = image[(pos_y + 2) * IN_WIDTH + pos_x]; // print data std::printf("x=%4d, y=%4d, data=0x%04x\t", pos_x, pos_y, image[(pos_y + 0) * IN_WIDTH + pos_x]); std::printf("x=%4d, y=%4d, data=0x%04x\t", pos_x, pos_y, image[(pos_y + 1) * IN_WIDTH + pos_x]); std::printf("x=%4d, y=%4d, data=0x%04x\n", pos_x, pos_y, image[(pos_y + 2) * IN_WIDTH + pos_x]); pos_x++; // calculate position and recognize when to finish if (pos_x >= IN_WIDTH) { pos_x = 0; pos_y++; } if (pos_y >= IN_HEIGHT - 1) { pos_y = 0; cnt_flame++; } if (cnt_flame >= FLAMES) { is_finish = true; } } else { out_valid = false; } // wait for next clk wait(); } } void readData(void) { // init local var uint16_t pos_x = 0, pos_y = 0, cnt_flame = 0; uint32_t last_data = 0, cnt = 0; bool is_finish = false; // reset out_ready = false; is_done = false; while (true) { if (!is_finish) { out_ready = true; // when data valid, write it down if (in_valid) { process_image[pos_y * OUT_WIDTH + pos_x] = in_data; // calculate position pos_x++; if (pos_x >= OUT_WIDTH) { pos_x = 0; pos_y++; } if (pos_y >= OUT_HEIGHT) { pos_y = 0; saveData( ("output_img_" + std::to_string(cnt_flame) + ".bmp").c_str()); cnt_flame++; } if (cnt_flame >= FLAMES) { is_finish = true; } } } else { out_ready = false; } // when no data send, give finish signal if (is_finish && (last_data == in_data)) { cnt++; if (cnt >= 100000L) { // when receive many times the same data is_done = true; std::printf("Finish Reading data; pos_x = %d, pos_y = %d\n", pos_x, pos_y); } } else { cnt = 0; } last_data = in_data; // wait for next clk wait(); } } bool saveData(const char *name) { bool ret = true; // Check Image Size if (process_image.size() > OUT_SIZE) { std::cout << "Process Image Over Size!!!\n" << "Image Size:" << process_image.size() << "\n"; return false; } // Write BMP image bitmap_image bmp(OUT_WIDTH, OUT_HEIGHT); if (!bmp) { std::cout << "Output Image Open Failed!!!\n"; return false; } for (int y = 0; y < OUT_HEIGHT; y++) for (int x = 0; x < OUT_WIDTH; x++) bmp.set_pixel(x, y, (process_image[y * OUT_WIDTH + x] & 0x00ff0000) >> 16, (process_image[y * OUT_WIDTH + x] & 0x0000ff00) >> 8, (process_image[y * OUT_WIDTH + x] & 0x000000ff) >> 0); bmp.save_image(std::string(OUT_DIR) + name); return ret; } }; // Image Parameters static const uint16_t IN_WIDTH = 1936; static const uint16_t IN_HEIGHT = 1088; static const uint32_t IN_SIZE = (IN_WIDTH * IN_HEIGHT); static const uint16_t OUT_WIDTH = 1920; static const uint16_t OUT_HEIGHT = 1080; static const uint32_t OUT_SIZE = (OUT_WIDTH * OUT_HEIGHT); static const uint32_t FLAMES = 2; // Input image path and Output directory path #ifndef INPUT_IMG const char *INPUT_IMG = "./src/transform/test.bin"; #endif #ifndef OUTPUT_DIR const char *OUTPUT_DIR = "./logs/"; #endif // color gain for correcting color struct color_gain { double red; double green; double blue; } color_gain{1.1, 0.7, 1.3}, white_gain; static const double gamma_value = 2.2; static const double sat_inc = 0.5; static const double contrast = 1.2; using namespace sc_core; using namespace sc_dt; int sc_main(int argc, char *argv[]) { std::printf("Enter into sc_main\n"); // Open Image std::ifstream image; image.open(INPUT_IMG, std::ios::in | std::ios::binary); // Check image whether is open if (!image.is_open()) { std::printf("Open Image Failed!!!\n"); exit(0); } else { std::printf("Open Image Successfully!!!\n"); } // Read and Transform Image std::vector in_image(IN_SIZE); uint8_t *buf = new uint8_t[2 * IN_SIZE]; image.read((char *)buf, 2 * IN_SIZE); uint32_t i = 0; for (int y = 0; y < IN_HEIGHT; y++) { for (int x = 0; x < IN_WIDTH; x++) { in_image[y * IN_WIDTH + x] = (uint16_t)buf[i] + ((uint16_t)buf[i + 1] << 8); i += 2; } } // Close and delete image image.close(); delete[] buf; std::printf("Finish Reading Image\n"); // This is a more complicated example, please also see the simpler // examples/make_hello_c. // Create logs/ directory in case we have traces to put under it Verilated::mkdir("logs"); // Set debug level, 0 is off, 9 is highest presently used // May be overridden by commandArgs argument parsing Verilated::debug(0); // Randomization reset policy // May be overridden by commandArgs argument parsing Verilated::randReset(2); // Before any evaluation, need to know to calculate those signals only used // for tracing Verilated::traceEverOn(true); // Pass arguments so Verilated code can see them, e.g. $value$plusargs // This needs to be called before you create any model Verilated::commandArgs(argc, argv); // General logfile std::ios::sync_with_stdio(); // Define clocks sc_clock clk{"clk", 10, SC_NS, 0.5, 3, SC_NS, true}; // Define interconnect sc_signal rst; // ISP Modules in ports sc_signal in_valid; sc_signal in_ready; sc_signal in_data[3]; // ISP Modules out ports sc_signal out_valid; sc_signal out_ready; sc_signal out_data; // ISP Modules Enable Ports sc_signal blender_enable; sc_signal gamma_enable; sc_signal white_enable; sc_signal saturation_enable; // ISP Modules Configurations Ports sc_signal gain_red; sc_signal gain_green; sc_signal gain_blue; sc_signal flame_rate; sc_signal saturation_inc; sc_signal gamma_table[256]; sc_signal white_gain[3]; // Construct the Verilated model, from inside Visp.h Visp isp("Visp"); isp.clk(clk); isp.reset(rst); isp.in_en(in_valid); isp.in_ready(in_ready); for (int i = 0; i < 3; i++) isp.in_data[i](in_data[i]); sc_signal out_receive; isp.out_receive(out_receive); isp.out_en(out_valid); isp.out_ready(out_ready); isp.out_data(out_data); isp.blender_enable(blender_enable); isp.gamma_enable(gamma_enable); isp.white_enable(white_enable); isp.saturation_enable(saturation_enable); isp.gain_red(gain_red); isp.gain_green(gain_green); isp.gain_blue(gain_blue); isp.flame_rate(flame_rate); isp.saturation_inc(saturation_inc); for (int i = 0; i < 256; i++) isp.gamma_table[i](gamma_table[i]); for (int i = 0; i < 3; i++) isp.white_gain[i](white_gain[i]); // Construct testbench module TB_ISP tb_isp("tb_isp", IN_WIDTH, IN_HEIGHT, OUT_WIDTH, OUT_HEIGHT, FLAMES, OUTPUT_DIR); tb_isp.image = std::move(in_image); tb_isp.clk(clk); tb_isp.rst(rst); // Connect input signal tb_isp.in_valid(out_valid); tb_isp.in_ready(out_ready); tb_isp.in_data(out_data); // Connect output signal tb_isp.out_valid(in_valid); tb_isp.out_ready(in_ready); for (int i = 0; i < 3; i++) tb_isp.out_data[i](in_data[i]); // Set ISP modules parameters // Color Blender blender_enable = true; gain_red = static_cast(color_gain.red * std::pow(2, 8)); gain_green = static_cast(color_gain.green * std::pow(2, 8)); gain_blue = static_cast(color_gain.blue * std::pow(2, 8)); // Gamma table gamma_enable = true; for (int i = 0; i < 256; i++) { gamma_table[i] = static_cast(255 * pow(i / 255.0, 1.0 / gamma_value)); } // White Correction white_enable = true; flame_rate = 0; white_gain[0] = 255; white_gain[1] = 255; white_gain[2] = 255; // Saturation Correction saturation_enable = true; saturation_inc = (int32_t)((sat_inc >= 0) ? (sat_inc * std::pow(2, 8)) : (sat_inc * std::pow(2, 8))); // You must do one evaluation before enabling waves, in order to allow // SystemC to interconnect everything for testing. sc_start(SC_ZERO_TIME); // If verilator was invoked with --trace argument, // and if at run time passed the +trace argument, turn on tracing VerilatedVcdSc *tfp = nullptr; const char *flag = Verilated::commandArgsPlusMatch("trace"); if (flag && 0 == std::strcmp(flag, "+trace")) { std::cout << "Enabling waves into logs/vlt_dump.vcd...\n"; tfp = new VerilatedVcdSc; isp.trace(tfp, 99); // Trace 99 levels of hierarchy Verilated::mkdir("logs"); tfp->open("logs/vlt_dump.vcd"); } // Simulate until $finish std::cout << "Ready to simulate!\n"; while (!Verilated::gotFinish()) { // Flush the wave files each cycle so we can immediately see the output // Don't do this in "real" programs, do it in an abort() handler instead if (tfp) tfp->flush(); // Apply inputs if (sc_time_stamp() < sc_time(10, SC_NS)) { rst.write(1); // Assert reset } else { rst.write(0); // Deassert reset } if (tb_isp.is_done) break; // Simulate 1ns sc_start(1, SC_NS); } // Final model cleanup isp.final(); // Close trace if opened if (tfp) { tfp->close(); tfp = nullptr; } // Return good completion status return 0; }