| #include <cstdio> |
| #include <cstring> |
| #include <fstream> |
| |
| #include "vtr_assert.h" |
| #include "vtr_util.h" |
| #include "vtr_log.h" |
| #include "vtr_digest.h" |
| |
| #include "vpr_types.h" |
| #include "vpr_error.h" |
| |
| #include "globals.h" |
| #include "hash.h" |
| #include "read_place.h" |
| #include "read_xml_arch_file.h" |
| |
| void read_place(const char* net_file, |
| const char* place_file, |
| bool verify_file_digests, |
| const DeviceGrid& grid) { |
| std::ifstream fstream(place_file); |
| if (!fstream) { |
| VPR_FATAL_ERROR(VPR_ERROR_PLACE_F, |
| "'%s' - Cannot open place file.\n", |
| place_file); |
| } |
| |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& place_ctx = g_vpr_ctx.mutable_placement(); |
| |
| std::string line; |
| int lineno = 0; |
| bool seen_netlist_id = false; |
| bool seen_grid_dimensions = false; |
| while (std::getline(fstream, line)) { //Parse line-by-line |
| ++lineno; |
| |
| std::vector<std::string> tokens = vtr::split(line); |
| |
| if (tokens.empty()) { |
| continue; //Skip blank lines |
| |
| } else if (tokens[0][0] == '#') { |
| continue; //Skip commented lines |
| |
| } else if (tokens.size() == 4 |
| && tokens[0] == "Netlist_File:" |
| && tokens[2] == "Netlist_ID:") { |
| //Check that the netlist used to generate this placement matches the one loaded |
| // |
| //NOTE: this is an optional check which causes no errors if this line is missing. |
| // This ensures other tools can still generate placement files which can be loaded |
| // by VPR. |
| |
| if (seen_netlist_id) { |
| vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno, |
| "Duplicate Netlist_File/Netlist_ID specification"); |
| } |
| |
| std::string place_netlist_id = tokens[3]; |
| std::string place_netlist_file = tokens[1]; |
| |
| if (place_netlist_id != cluster_ctx.clb_nlist.netlist_id().c_str()) { |
| auto msg = vtr::string_fmt( |
| "The packed netlist file that generated placement (File: '%s' ID: '%s')" |
| " does not match current netlist (File: '%s' ID: '%s')", |
| place_netlist_file.c_str(), place_netlist_id.c_str(), |
| net_file, cluster_ctx.clb_nlist.netlist_id().c_str()); |
| if (verify_file_digests) { |
| vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno, msg.c_str()); |
| } else { |
| VTR_LOGF_WARN(place_file, lineno, "%s\n", msg.c_str()); |
| } |
| } |
| |
| seen_netlist_id = true; |
| |
| } else if (tokens.size() == 7 |
| && tokens[0] == "Array" |
| && tokens[1] == "size:" |
| && tokens[3] == "x" |
| && tokens[5] == "logic" |
| && tokens[6] == "blocks") { |
| //Load the device grid dimensions |
| |
| if (seen_grid_dimensions) { |
| vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno, |
| "Duplicate device grid dimensions specification"); |
| } |
| |
| size_t place_file_width = vtr::atou(tokens[2]); |
| size_t place_file_height = vtr::atou(tokens[4]); |
| if (grid.width() != place_file_width || grid.height() != place_file_height) { |
| vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno, |
| "Current FPGA size (%d x %d) is different from size when placement generated (%d x %d)", |
| grid.width(), grid.height(), place_file_width, place_file_height); |
| } |
| |
| seen_grid_dimensions = true; |
| |
| } else if (tokens.size() == 4 || (tokens.size() == 5 && tokens[4][0] == '#')) { |
| //Load the block location |
| // |
| //We should have 4 tokens of actual data, with an optional 5th (commented) token indicating VPR's |
| //internal block number |
| |
| //Grid dimensions are required by this point |
| if (!seen_grid_dimensions) { |
| vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno, |
| "Missing device grid size specification"); |
| } |
| |
| std::string block_name = tokens[0]; |
| int block_x = vtr::atoi(tokens[1]); |
| int block_y = vtr::atoi(tokens[2]); |
| int block_z = vtr::atoi(tokens[3]); |
| |
| ClusterBlockId blk_id = cluster_ctx.clb_nlist.find_block(block_name); |
| |
| if (place_ctx.block_locs.size() != cluster_ctx.clb_nlist.blocks().size()) { |
| //Resize if needed |
| place_ctx.block_locs.resize(cluster_ctx.clb_nlist.blocks().size()); |
| } |
| |
| //Set the location |
| place_ctx.block_locs[blk_id].loc.x = block_x; |
| place_ctx.block_locs[blk_id].loc.y = block_y; |
| place_ctx.block_locs[blk_id].loc.z = block_z; |
| |
| } else { |
| //Unrecognized |
| vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno, |
| "Invalid line '%s' in placement file", |
| line.c_str()); |
| } |
| } |
| |
| place_ctx.placement_id = vtr::secure_digest_file(place_file); |
| } |
| |
| void read_user_pad_loc(const char* pad_loc_file) { |
| /* Reads in the locations of the IO pads from a file. */ |
| |
| t_hash **hash_table, *h_ptr; |
| int xtmp, ytmp; |
| FILE* fp; |
| char buf[vtr::bufsize], bname[vtr::bufsize], *ptr; |
| |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& device_ctx = g_vpr_ctx.device(); |
| auto& place_ctx = g_vpr_ctx.mutable_placement(); |
| |
| VTR_LOG("\n"); |
| VTR_LOG("Reading locations of IO pads from '%s'.\n", pad_loc_file); |
| fp = fopen(pad_loc_file, "r"); |
| if (!fp) VPR_FATAL_ERROR(VPR_ERROR_PLACE_F, |
| "'%s' - Cannot find IO pads location file.\n", |
| pad_loc_file); |
| |
| hash_table = alloc_hash_table(); |
| for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { |
| if (is_io_type(physical_tile_type(blk_id))) { |
| insert_in_hash_table(hash_table, cluster_ctx.clb_nlist.block_name(blk_id).c_str(), size_t(blk_id)); |
| place_ctx.block_locs[blk_id].loc.x = OPEN; /* Mark as not seen yet. */ |
| } |
| } |
| |
| for (size_t i = 0; i < device_ctx.grid.width(); i++) { |
| for (size_t j = 0; j < device_ctx.grid.height(); j++) { |
| auto type = device_ctx.grid[i][j].type; |
| if (is_io_type(type)) { |
| for (int k = 0; k < type->capacity; k++) { |
| if (place_ctx.grid_blocks[i][j].blocks[k] != INVALID_BLOCK_ID) { |
| place_ctx.grid_blocks[i][j].blocks[k] = EMPTY_BLOCK_ID; /* Flag for err. check */ |
| } |
| } |
| } |
| } |
| } |
| |
| ptr = vtr::fgets(buf, vtr::bufsize, fp); |
| |
| while (ptr != nullptr) { |
| ptr = vtr::strtok(buf, TOKENS, fp, buf); |
| if (ptr == nullptr) { |
| ptr = vtr::fgets(buf, vtr::bufsize, fp); |
| continue; /* Skip blank or comment lines. */ |
| } |
| |
| if (strlen(ptr) + 1 < vtr::bufsize) { |
| strcpy(bname, ptr); |
| } else { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Block name exceeded buffer size of %zu characters", vtr::bufsize); |
| } |
| |
| ptr = vtr::strtok(nullptr, TOKENS, fp, buf); |
| if (ptr == nullptr) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Incomplete.\n"); |
| } |
| sscanf(ptr, "%d", &xtmp); |
| |
| ptr = vtr::strtok(nullptr, TOKENS, fp, buf); |
| if (ptr == nullptr) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Incomplete.\n"); |
| } |
| sscanf(ptr, "%d", &ytmp); |
| |
| ptr = vtr::strtok(nullptr, TOKENS, fp, buf); |
| if (ptr == nullptr) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Incomplete.\n"); |
| } |
| int k; |
| sscanf(ptr, "%d", &k); |
| |
| ptr = vtr::strtok(nullptr, TOKENS, fp, buf); |
| if (ptr != nullptr) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Extra characters at end of line.\n"); |
| } |
| |
| h_ptr = get_hash_entry(hash_table, bname); |
| if (h_ptr == nullptr) { |
| VTR_LOG_WARN("[Line %d] Block %s invalid, no such IO pad.\n", |
| vtr::get_file_line_number_of_last_opened_file(), bname); |
| ptr = vtr::fgets(buf, vtr::bufsize, fp); |
| continue; |
| } |
| ClusterBlockId bnum(h_ptr->index); |
| int i = xtmp; |
| int j = ytmp; |
| |
| if (place_ctx.block_locs[bnum].loc.x != OPEN) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Block %s is listed twice in pad file.\n", bname); |
| } |
| |
| if (i < 0 || i > int(device_ctx.grid.width() - 1) || j < 0 || j > int(device_ctx.grid.height() - 1)) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, 0, |
| "Block #%zu (%s) location, (%d,%d) is out of range.\n", size_t(bnum), bname, i, j); |
| } |
| |
| place_ctx.block_locs[bnum].loc.x = i; /* Will be reloaded by initial_placement anyway. */ |
| place_ctx.block_locs[bnum].loc.y = j; /* We need to set .x only as a done flag. */ |
| place_ctx.block_locs[bnum].loc.z = k; |
| place_ctx.block_locs[bnum].is_fixed = true; |
| |
| auto type = device_ctx.grid[i][j].type; |
| if (!is_io_type(type)) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, 0, |
| "Attempt to place IO block %s at illegal location (%d, %d).\n", bname, i, j); |
| } |
| |
| if (k >= type->capacity || k < 0) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, vtr::get_file_line_number_of_last_opened_file(), |
| "Block %s subblock number (%d) is out of range.\n", bname, k); |
| } |
| place_ctx.grid_blocks[i][j].blocks[k] = bnum; |
| place_ctx.grid_blocks[i][j].usage++; |
| |
| ptr = vtr::fgets(buf, vtr::bufsize, fp); |
| } |
| |
| for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { |
| auto type = physical_tile_type(blk_id); |
| if (is_io_type(type) && place_ctx.block_locs[blk_id].loc.x == OPEN) { |
| vpr_throw(VPR_ERROR_PLACE_F, pad_loc_file, 0, |
| "IO block %s location was not specified in the pad file.\n", cluster_ctx.clb_nlist.block_name(blk_id).c_str()); |
| } |
| } |
| |
| fclose(fp); |
| free_hash_table(hash_table); |
| VTR_LOG("Successfully read %s.\n", pad_loc_file); |
| VTR_LOG("\n"); |
| } |
| |
| /* Prints out the placement of the circuit. The architecture and * |
| * netlist files used to generate this placement are recorded in the * |
| * file to avoid loading a placement with the wrong support files * |
| * later. */ |
| void print_place(const char* net_file, |
| const char* net_id, |
| const char* place_file) { |
| FILE* fp; |
| |
| auto& device_ctx = g_vpr_ctx.device(); |
| auto& cluster_ctx = g_vpr_ctx.clustering(); |
| auto& place_ctx = g_vpr_ctx.mutable_placement(); |
| |
| fp = fopen(place_file, "w"); |
| |
| fprintf(fp, "Netlist_File: %s Netlist_ID: %s\n", |
| net_file, |
| net_id); |
| fprintf(fp, "Array size: %zu x %zu logic blocks\n\n", device_ctx.grid.width(), device_ctx.grid.height()); |
| fprintf(fp, "#block name\tx\ty\tsubblk\tblock number\n"); |
| fprintf(fp, "#----------\t--\t--\t------\t------------\n"); |
| |
| if (!place_ctx.block_locs.empty()) { //Only if placement exists |
| for (auto blk_id : cluster_ctx.clb_nlist.blocks()) { |
| fprintf(fp, "%s\t", cluster_ctx.clb_nlist.block_name(blk_id).c_str()); |
| if (strlen(cluster_ctx.clb_nlist.block_name(blk_id).c_str()) < 8) |
| fprintf(fp, "\t"); |
| |
| fprintf(fp, "%d\t%d\t%d", place_ctx.block_locs[blk_id].loc.x, place_ctx.block_locs[blk_id].loc.y, place_ctx.block_locs[blk_id].loc.z); |
| fprintf(fp, "\t#%zu\n", size_t(blk_id)); |
| } |
| } |
| fclose(fp); |
| |
| //Calculate the ID of the placement |
| place_ctx.placement_id = vtr::secure_digest_file(place_file); |
| } |