Skip to content

Refactored read_place and read_user_block_loc functions into two new … #1490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Sep 8, 2020
298 changes: 133 additions & 165 deletions vpr/src/base/read_place.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <cstdio>
#include <cstring>
#include <fstream>
#include <algorithm>

#include "vtr_assert.h"
#include "vtr_util.h"
Expand All @@ -15,25 +16,78 @@
#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) {
void read_place_header(
std::ifstream& placement_file,
const char* net_file,
const char* place_file,
bool verify_file_hashes,
const DeviceGrid& grid);

void read_place_body(
std::ifstream& placement_file,
const char* place_file,
bool is_place_file);

void read_place(
const char* net_file,
const char* place_file,
bool verify_file_digests,
const DeviceGrid& grid,
bool is_place_file) {
std::ifstream fstream(place_file);
if (!fstream) {
VPR_FATAL_ERROR(VPR_ERROR_PLACE_F,
"'%s' - Cannot open place file.\n",
place_file);
}

VTR_LOG("Reading %s.\n", place_file);
VTR_LOG("\n");

read_place_header(fstream, net_file, place_file, verify_file_digests, grid);
read_place_body(fstream, place_file, is_place_file);

VTR_LOG("Successfully read %s.\n", place_file);
VTR_LOG("\n");
}

void read_constraints(const char* constraints_file,
bool is_place_file) {
std::ifstream fstream(constraints_file);
if (!fstream) {
VPR_FATAL_ERROR(VPR_ERROR_PLACE_F,
"'%s' - Cannot open constraints file.\n",
constraints_file);
}

VTR_LOG("Reading %s.\n", constraints_file);
VTR_LOG("\n");

read_place_body(fstream, constraints_file, is_place_file);

VTR_LOG("Successfully read %s.\n", constraints_file);
VTR_LOG("\n");
}

/**
* This function reads the header (first two lines) of a placement file.
* It checks whether the packed netlist file that generated the placement matches the current netlist file.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Say what the header is - two lines in any order that give netlist and grid files used to generate placement. Also mention grid gives an error if it doesn't match

* It also checks whether the FPGA grid size has stayed the same from when the placement was generated.
* The verify_file_digests bool is used to decide whether to give a warning or an error if the netlist files do not match.
*/
void read_place_header(std::ifstream& placement_file,
const char* net_file,
const char* place_file,
bool verify_file_digests,
const DeviceGrid& grid) {
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

while (std::getline(placement_file, line) && (!seen_netlist_id || !seen_grid_dimensions)) { //Parse line-by-line
++lineno;

std::vector<std::string> tokens = vtr::split(line);
Expand Down Expand Up @@ -84,11 +138,6 @@ void read_place(const char* net_file,
&& 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) {
Expand All @@ -97,194 +146,113 @@ void read_place(const char* net_file,
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 sub_tile_index = 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.sub_tile = sub_tile_index;
seen_grid_dimensions = true; //if you have read the grid dimensions you are done reading the place file header
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix comment at this line


} else {
//Unrecognized
vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno,
"Invalid line '%s' in placement file",
"Invalid line '%s' in placement file header",
line.c_str());
}
}

place_ctx.placement_id = vtr::secure_digest_file(place_file);
}

/** Reads in blocks whose locations are specified in a constraints file. Constraint file is in .place format.
* All blocks specified in this file will be locked down at their specified x, y, subtile locations.
* (Will not be moved by optimizers during placement
/**
* This function reads either the body of a placement file or a constraints file.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add that constraints file is same format as place

* If it is reading a place file it sets the x, y, and subtile locations of the blocks in the placement context.
* If it is reading a constraints file it does the same and also marks the blocks as locked and marks the grid usage.
* The bool is_place_file indicates if the file should be read as a place file (is_place_file = true)
* or a constraints file (is_place_file = false).
*/
void read_user_block_loc(const char* constraints_file) {
t_hash **hash_table, *h_ptr;
int xtmp, ytmp;
FILE* fp;
char buf[vtr::bufsize], bname[vtr::bufsize], *ptr;
std::unordered_set<ClusterBlockId> constrained_blocks;

void read_place_body(std::ifstream& placement_file,
const char* place_file,
bool is_place_file) {
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 blocks from '%s'.\n", constraints_file);
fp = fopen(constraints_file, "r");
if (!fp) VPR_FATAL_ERROR(VPR_ERROR_PLACE_F,
"'%s' - Cannot find block locations file.\n",
constraints_file);

hash_table = alloc_hash_table();
for (auto blk_id : cluster_ctx.clb_nlist.blocks()) {
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_empty_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 */
}
}
}
}
}
std::string line;
int lineno = 0;

ptr = vtr::fgets(buf, vtr::bufsize, fp);
std::vector<ClusterBlockId> seen_blocks;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment what this is

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be a vtr::vector_map<ClusterBlockId, int>


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. */
}
while (std::getline(placement_file, line)) { //Parse line-by-line
++lineno;

if (strlen(ptr) + 1 < vtr::bufsize) {
strcpy(bname, ptr);
} else {
vpr_throw(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Block name exceeded buffer size of %zu characters", vtr::bufsize);
}
std::vector<std::string> tokens = vtr::split(line);

ptr = vtr::strtok(nullptr, TOKENS, fp, buf);
if (ptr == nullptr) {
vpr_throw(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Incomplete.\n");
}
sscanf(ptr, "%d", &xtmp);
if (tokens.empty()) {
continue; //Skip blank lines

ptr = vtr::strtok(nullptr, TOKENS, fp, buf);
if (ptr == nullptr) {
vpr_throw(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Incomplete.\n");
}
sscanf(ptr, "%d", &ytmp);
} else if (tokens[0][0] == '#') {
continue; //Skip commented lines

ptr = vtr::strtok(nullptr, TOKENS, fp, buf);
if (ptr == nullptr) {
vpr_throw(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Incomplete.\n");
}
int k;
sscanf(ptr, "%d", &k);
} 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

ptr = vtr::strtok(nullptr, TOKENS, fp, buf);
if (ptr != nullptr) {
vpr_throw(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Extra characters at end of line.\n");
}
std::string block_name = tokens[0];
int block_x = vtr::atoi(tokens[1]);
int block_y = vtr::atoi(tokens[2]);
int sub_tile_index = vtr::atoi(tokens[3]);

h_ptr = get_hash_entry(hash_table, bname);
if (h_ptr == nullptr) {
VTR_LOG_WARN("[Line %d] Block %s invalid, no such block.\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;
ClusterBlockId blk_id = cluster_ctx.clb_nlist.find_block(block_name);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find what this returns if it doesn't find the block name - use for invalid block name check


if (place_ctx.block_locs[bnum].loc.x != OPEN) {
VPR_THROW(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Block %s is listed twice in constraints file.\n", bname);
}
//Check if block is listed twice in constraints file
if (find(seen_blocks.begin(), seen_blocks.end(), blk_id) != seen_blocks.end()) {
VPR_THROW(VPR_ERROR_PLACE, "The block with ID %d is listed twice in the constraints file.\n", blk_id);
}

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, constraints_file, 0,
"Block #%zu (%s) location, (%d,%d) is out of range.\n", size_t(bnum), bname, i, j);
}
//Check if block location is out of range of grid dimensions
if (block_x < 0 || block_x > int(device_ctx.grid.width() - 1)
|| block_y < 0 || block_y > int(device_ctx.grid.height() - 1)) {
VPR_THROW(VPR_ERROR_PLACE, "The block with ID %d is out of range at location (%d, %d). \n", blk_id, block_x, block_y);
}

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.sub_tile = k;
place_ctx.block_locs[bnum].is_fixed = true;
if (place_ctx.block_locs.size() != cluster_ctx.clb_nlist.blocks().size()) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if we need this. if we do, should be out of the loop

//Resize if needed
place_ctx.block_locs.resize(cluster_ctx.clb_nlist.blocks().size());
}

auto physical_tile = device_ctx.grid[i][j].type;
auto logical_block = cluster_ctx.clb_nlist.block_type(bnum);
if (!is_sub_tile_compatible(physical_tile, logical_block, place_ctx.block_locs[bnum].loc.sub_tile)) {
VPR_THROW(VPR_ERROR_PLACE_F, constraints_file, 0,
"Attempt to place block %s at illegal location (%d, %d).\n", bname, i, j);
}
//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.sub_tile = sub_tile_index;

if (k >= physical_tile->capacity || k < 0) {
VPR_THROW(VPR_ERROR_PLACE_F, constraints_file, vtr::get_file_line_number_of_last_opened_file(),
"Block %s subtile 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++;
//Check if block is at an illegal location
auto physical_tile = device_ctx.grid[block_x][block_y].type;
auto logical_block = cluster_ctx.clb_nlist.block_type(blk_id);
if (!is_sub_tile_compatible(physical_tile, logical_block, place_ctx.block_locs[blk_id].loc.sub_tile)) {
VPR_THROW(VPR_ERROR_PLACE, place_file, 0, "Attempt to place block %d at illegal location (%d, %d). \n", blk_id, block_x, block_y);
}

constrained_blocks.insert(bnum);
if (sub_tile_index >= physical_tile->capacity || sub_tile_index < 0) {
VPR_THROW(VPR_ERROR_PLACE, place_file, vtr::get_file_line_number_of_last_opened_file(), "Block %d subtile number (%d) is out of range. \n", blk_id, sub_tile_index);
}

ptr = vtr::fgets(buf, vtr::bufsize, fp);
}
//need to lock down blocks and mark grid block usage if it is a constraints file
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explain why you need to do this (if place file, initial placement does instead)

if (!is_place_file) {
place_ctx.block_locs[blk_id].is_fixed = true;
place_ctx.grid_blocks[block_x][block_y].blocks[sub_tile_index] = blk_id;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try setting grid all the time, check that it works

place_ctx.grid_blocks[block_x][block_y].usage++;
}

for (auto blk_id : cluster_ctx.clb_nlist.blocks()) {
auto result = constrained_blocks.find(blk_id);
if (result == constrained_blocks.end()) {
continue;
}
//add to vector of blocks that have been seen
seen_blocks.push_back(blk_id);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change once you refactor to vector map


if (place_ctx.block_locs[blk_id].loc.x == OPEN) {
VPR_THROW(VPR_ERROR_PLACE_F, constraints_file, 0,
"Block %s location was not specified in the constraints file.\n", cluster_ctx.clb_nlist.block_name(blk_id).c_str());
} else {
//Unrecognized
vpr_throw(VPR_ERROR_PLACE_F, place_file, lineno,
"Invalid line '%s' in file",
line.c_str());
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add in check for place file to see if it has checked all blocks

fclose(fp);
free_hash_table(hash_table);
VTR_LOG("Successfully read %s.\n", constraints_file);
VTR_LOG("\n");
if (is_place_file) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add comment to explain why

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for entire place file, want to make hash so we can write into routing file for later error checking, dont need for constraint

place_ctx.placement_id = vtr::secure_digest_file(place_file);
}
}

/**
Expand Down
Loading