// // Author: Wolfgang Spraul // // This is free and unencumbered software released into the public domain. // For details see the UNLICENSE file at the root of the source tree. // #include #include "model.h" static const char* fpga_ttstr[] = // tile type strings { [NA] = "NA", [ROUTING] = "ROUTING", [ROUTING_BRK] = "ROUTING_BRK", [ROUTING_VIA] = "ROUTING_VIA", [HCLK_ROUTING_XM] = "HCLK_ROUTING_XM", [HCLK_ROUTING_XL] = "HCLK_ROUTING_XL", [HCLK_LOGIC_XM] = "HCLK_LOGIC_XM", [HCLK_LOGIC_XL] = "HCLK_LOGIC_XL", [LOGIC_XM] = "LOGIC_XM", [LOGIC_XL] = "LOGIC_XL", [REGH_ROUTING_XM] = "REGH_ROUTING_XM", [REGH_ROUTING_XL] = "REGH_ROUTING_XL", [REGH_LOGIC_XM] = "REGH_LOGIC_XM", [REGH_LOGIC_XL] = "REGH_LOGIC_XL", [BRAM_ROUTING] = "BRAM_ROUTING", [BRAM_ROUTING_BRK] = "BRAM_ROUTING_BRK", [BRAM] = "BRAM", [BRAM_ROUTING_TERM_T] = "BRAM_ROUTING_TERM_T", [BRAM_ROUTING_TERM_B] = "BRAM_ROUTING_TERM_B", [BRAM_ROUTING_VIA_TERM_T] = "BRAM_ROUTING_VIA_TERM_T", [BRAM_ROUTING_VIA_TERM_B] = "BRAM_ROUTING_VIA_TERM_B", [BRAM_TERM_LT] = "BRAM_TERM_LT", [BRAM_TERM_RT] = "BRAM_TERM_RT", [BRAM_TERM_LB] = "BRAM_TERM_LB", [BRAM_TERM_RB] = "BRAM_TERM_RB", [HCLK_BRAM_ROUTING] = "HCLK_BRAM_ROUTING", [HCLK_BRAM_ROUTING_VIA] = "HCLK_BRAM_ROUTING_VIA", [HCLK_BRAM] = "HCLK_BRAM", [REGH_BRAM_ROUTING] = "REGH_BRAM_ROUTING", [REGH_BRAM_ROUTING_VIA] = "REGH_BRAM_ROUTING_VIA", [REGH_BRAM_L] = "REGH_BRAM_L", [REGH_BRAM_R] = "REGH_BRAM_R", [MACC] = "MACC", [HCLK_MACC_ROUTING] = "HCLK_MACC_ROUTING", [HCLK_MACC_ROUTING_VIA] = "HCLK_MACC_ROUTING_VIA", [HCLK_MACC] = "HCLK_MACC", [REGH_MACC_ROUTING] = "REGH_MACC_ROUTING", [REGH_MACC_ROUTING_VIA] = "REGH_MACC_ROUTING_VIA", [REGH_MACC_L] = "REGH_MACC_L", [PLL_T] = "PLL_T", [DCM_T] = "DCM_T", [PLL_B] = "PLL_B", [DCM_B] = "DCM_B", [REG_T] = "REG_T", [REG_TERM_T] = "REG_TERM_T", [REG_TERM_B] = "REG_TERM_B", [REG_B] = "REG_B", [REGV_TERM_T] = "REGV_TERM_T", [REGV_TERM_B] = "REGV_TERM_B", [HCLK_REGV] = "HCLK_REGV", [REGV] = "REGV", [REGV_BRK] = "REGV_BRK", [REGV_T] = "REGV_T", [REGV_B] = "REGV_B", [REGV_MIDBUF_T] = "REGV_MIDBUF_T", [REGV_HCLKBUF_T] = "REGV_HCLKBUF_T", [REGV_HCLKBUF_B] = "REGV_HCLKBUF_B", [REGV_MIDBUF_B] = "REGV_MIDBUF_B", [REGC_ROUTING] = "REGC_ROUTING", [REGC_LOGIC] = "REGC_LOGIC", [REGC_CMT] = "REGC_CMT", [CENTER] = "CENTER", [IO_T] = "IO_T", [IO_B] = "IO_B", [IO_TERM_T] = "IO_TERM_T", [IO_TERM_B] = "IO_TERM_B", [IO_ROUTING] = "IO_ROUTING", [IO_LOGIC_TERM_T] = "IO_LOGIC_TERM_T", [IO_LOGIC_TERM_B] = "IO_LOGIC_TERM_B", [IO_OUTER_T] = "IO_OUTER_T", [IO_INNER_T] = "IO_INNER_T", [IO_OUTER_B] = "IO_OUTER_B", [IO_INNER_B] = "IO_INNER_B", [IO_BUFPLL_TERM_T] = "IO_BUFPLL_TERM_T", [IO_LOGIC_REG_TERM_T] = "IO_LOGIC_REG_TERM_T", [IO_BUFPLL_TERM_B] = "IO_BUFPLL_TERM_B", [IO_LOGIC_REG_TERM_B] = "IO_LOGIC_REG_TERM_B", [LOGIC_ROUTING_TERM_B] = "LOGIC_ROUTING_TERM_B", [LOGIC_NOIO_TERM_B] = "LOGIC_NOIO_TERM_B", [MACC_ROUTING_TERM_T] = "MACC_ROUTING_TERM_T", [MACC_ROUTING_TERM_B] = "MACC_ROUTING_TERM_B", [MACC_VIA_TERM_T] = "MACC_VIA_TERM_T", [MACC_TERM_TL] = "MACC_TERM_TL", [MACC_TERM_TR] = "MACC_TERM_TR", [MACC_TERM_BL] = "MACC_TERM_BL", [MACC_TERM_BR] = "MACC_TERM_BR", [ROUTING_VIA_REGC] = "ROUTING_VIA_REGC", [ROUTING_VIA_IO] = "ROUTING_VIA_IO", [ROUTING_VIA_IO_DCM] = "ROUTING_VIA_IO_DCM", [ROUTING_VIA_CARRY] = "ROUTING_VIA_CARRY", [CORNER_TERM_L] = "CORNER_TERM_L", [CORNER_TERM_R] = "CORNER_TERM_R", [IO_TERM_L_UPPER_TOP] = "IO_TERM_L_UPPER_TOP", [IO_TERM_L_UPPER_BOT] = "IO_TERM_L_UPPER_BOT", [IO_TERM_L_LOWER_TOP] = "IO_TERM_L_LOWER_TOP", [IO_TERM_L_LOWER_BOT] = "IO_TERM_L_LOWER_BOT", [IO_TERM_R_UPPER_TOP] = "IO_TERM_R_UPPER_TOP", [IO_TERM_R_UPPER_BOT] = "IO_TERM_R_UPPER_BOT", [IO_TERM_R_LOWER_TOP] = "IO_TERM_R_LOWER_TOP", [IO_TERM_R_LOWER_BOT] = "IO_TERM_R_LOWER_BOT", [IO_TERM_L] = "IO_TERM_L", [IO_TERM_R] = "IO_TERM_R", [HCLK_TERM_L] = "HCLK_TERM_L", [HCLK_TERM_R] = "HCLK_TERM_R", [REGH_IO_TERM_L] = "REGH_IO_TERM_L", [REGH_IO_TERM_R] = "REGH_IO_TERM_R", [REG_L] = "REG_L", [REG_R] = "REG_R", [IO_PCI_L] = "IO_PCI_L", [IO_PCI_R] = "IO_PCI_R", [IO_RDY_L] = "IO_RDY_L", [IO_RDY_R] = "IO_RDY_R", [IO_L] = "IO_L", [IO_R] = "IO_R", [IO_PCI_CONN_L] = "IO_PCI_CONN_L", [IO_PCI_CONN_R] = "IO_PCI_CONN_R", [CORNER_TERM_T] = "CORNER_TERM_T", [CORNER_TERM_B] = "CORNER_TERM_B", [ROUTING_IO_L] = "ROUTING_IO_L", [HCLK_ROUTING_IO_L] = "HCLK_ROUTING_IO_L", [HCLK_ROUTING_IO_R] = "HCLK_ROUTING_IO_R", [REGH_ROUTING_IO_L] = "REGH_ROUTING_IO_L", [REGH_ROUTING_IO_R] = "REGH_ROUTING_IO_R", [ROUTING_IO_L_BRK] = "ROUTING_IO_L_BRK", [ROUTING_GCLK] = "ROUTING_GCLK", [REGH_IO_L] = "REGH_IO_L", [REGH_IO_R] = "REGH_IO_R", [REGH_MCB] = "REGH_MCB", [HCLK_MCB] = "HCLK_MCB", [ROUTING_IO_VIA_L] = "ROUTING_IO_VIA_L", [ROUTING_IO_VIA_R] = "ROUTING_IO_VIA_R", [ROUTING_IO_PCI_CE_L] = "ROUTING_IO_PCI_CE_L", [ROUTING_IO_PCI_CE_R] = "ROUTING_IO_PCI_CE_R", [CORNER_TL] = "CORNER_TL", [CORNER_BL] = "CORNER_BL", [CORNER_TR_UPPER] = "CORNER_TR_UPPER", [CORNER_TR_LOWER] = "CORNER_TR_LOWER", [CORNER_BR_UPPER] = "CORNER_BR_UPPER", [CORNER_BR_LOWER] = "CORNER_BR_LOWER", [HCLK_IO_TOP_UP_L] = "HCLK_IO_TOP_UP_L", [HCLK_IO_TOP_UP_R] = "HCLK_IO_TOP_UP_R", [HCLK_IO_TOP_SPLIT_L] = "HCLK_IO_TOP_SPLIT_L", [HCLK_IO_TOP_SPLIT_R] = "HCLK_IO_TOP_SPLIT_R", [HCLK_IO_TOP_DN_L] = "HCLK_IO_TOP_DN_L", [HCLK_IO_TOP_DN_R] = "HCLK_IO_TOP_DN_R", [HCLK_IO_BOT_UP_L] = "HCLK_IO_BOT_UP_L", [HCLK_IO_BOT_UP_R] = "HCLK_IO_BOT_UP_R", [HCLK_IO_BOT_SPLIT_L] = "HCLK_IO_BOT_SPLIT_L", [HCLK_IO_BOT_SPLIT_R] = "HCLK_IO_BOT_SPLIT_R", [HCLK_IO_BOT_DN_L] = "HCLK_IO_BOT_DN_L", [HCLK_IO_BOT_DN_R] = "HCLK_IO_BOT_DN_R", }; int init_tiles(struct fpga_model* model); int run_wires(struct fpga_model* model); int fpga_build_model(struct fpga_model* model, int fpga_rows, const char* columns, const char* left_wiring, const char* right_wiring) { int rc; memset(model, 0, sizeof(*model)); model->cfg_rows = fpga_rows; strncpy(model->cfg_columns, columns, sizeof(model->cfg_columns)-1); strncpy(model->cfg_left_wiring, left_wiring, sizeof(model->cfg_left_wiring)-1); strncpy(model->cfg_right_wiring, right_wiring, sizeof(model->cfg_right_wiring)-1); strarray_init(&model->str); rc = init_tiles(model); if (rc) return rc; rc = run_wires(model); if (rc) return rc; return 0; } const char* pf(const char* fmt, ...) { // safe to call it 8 times in 1 expression (such as function params) static char pf_buf[8][128]; static int last_buf = 0; va_list list; last_buf = (last_buf+1)%8; pf_buf[last_buf][0] = 0; va_start(list, fmt); vsnprintf(pf_buf[last_buf], sizeof(pf_buf[0]), fmt, list); va_end(list); return pf_buf[last_buf]; } const char* wpref(int flags, const char* wire_name) { static char buf[8][128]; static int last_buf = 0; char* prefix; if (flags & TF_CHIP_HORIZ_AXSYMM_CENTER) prefix = "REGC_INT_"; else if (flags & TF_CHIP_HORIZ_AXSYMM) prefix = "REGH_"; else if (flags & TF_ROW_HORIZ_AXSYMM) prefix = "HCLK_"; else if (flags & TF_UNDER_TOPMOST_TILE) prefix = "IOI_TTERM_"; else if (flags & TF_ABOVE_BOTTOMMOST_TILE) prefix = "IOI_BTERM_"; else prefix = ""; last_buf = (last_buf+1)%8; buf[last_buf][0] = 0; strcpy(buf[last_buf], prefix); strcat(buf[last_buf], wire_name); return buf[last_buf]; } #define CONN_NAMES_INCREMENT 128 #define CONNS_INCREMENT 128 #undef DBG_ADD_CONN_UNI int add_conn_uni(struct fpga_model* model, int y1, int x1, const char* name1, int y2, int x2, const char* name2) { struct fpga_tile* tile1; uint16_t name1_i, name2_i; uint16_t* new_ptr; int conn_start, num_conns_for_this_wire, rc, i, j; tile1 = &model->tiles[y1 * model->tile_x_range + x1]; rc = strarray_find_or_add(&model->str, name1, &name1_i); if (!rc) return -1; rc = strarray_find_or_add(&model->str, name2, &name2_i); if (!rc) return -1; // Search for a connection set under name1. for (i = 0; i < tile1->num_conn_names; i++) { if (tile1->conn_names[i*2+1] == name1_i) break; } // If this is the first connection under name1, add name1. if (i >= tile1->num_conn_names) { if (!(tile1->num_conn_names % CONN_NAMES_INCREMENT)) { new_ptr = realloc(tile1->conn_names, (tile1->num_conn_names+CONN_NAMES_INCREMENT)*2*sizeof(uint16_t)); if (!new_ptr) { fprintf(stderr, "Out of memory %s:%i\n", __FILE__, __LINE__); return 0; } tile1->conn_names = new_ptr; } tile1->conn_names[tile1->num_conn_names*2] = tile1->num_conns; tile1->conn_names[tile1->num_conn_names*2+1] = name1_i; tile1->num_conn_names++; } conn_start = tile1->conn_names[i*2]; if (i+1 >= tile1->num_conn_names) num_conns_for_this_wire = tile1->num_conns - conn_start; else num_conns_for_this_wire = tile1->conn_names[(i+1)*2] - conn_start; // Is the connection made a second time? for (j = conn_start; j < conn_start + num_conns_for_this_wire; j++) { if (tile1->conns[j*3] == x2 && tile1->conns[j*3+1] == y2 && tile1->conns[j*3+2] == name2_i) { fprintf(stderr, "Duplicate conn (num_conns %i): y%02i x%02i %s - y%02i x%02i %s.\n", num_conns_for_this_wire, y1, x1, name1, y2, x2, name2); for (j = conn_start; j < conn_start + num_conns_for_this_wire; j++) { fprintf(stderr, "c%i: y%02i x%02i %s -> y%02i x%02i %s\n", j, y1, x1, name1, tile1->conns[j*3+1], tile1->conns[j*3], strarray_lookup(&model->str, tile1->conns[j*3+2])); } return 0; } } if (!(tile1->num_conns % CONNS_INCREMENT)) { new_ptr = realloc(tile1->conns, (tile1->num_conns+CONNS_INCREMENT)*3*sizeof(uint16_t)); if (!new_ptr) { fprintf(stderr, "Out of memory %s:%i\n", __FILE__, __LINE__); return 0; } tile1->conns = new_ptr; } if (tile1->num_conns > j) memmove(&tile1->conns[(j+1)*3], &tile1->conns[j*3], (tile1->num_conns-j)*3*sizeof(uint16_t)); tile1->conns[j*3] = x2; tile1->conns[j*3+1] = y2; tile1->conns[j*3+2] = name2_i; tile1->num_conns++; while (i+1 < tile1->num_conn_names) { tile1->conn_names[(i+1)*2]++; i++; } #if DBG_ADD_CONN_UNI printf("conns for y%02i x%02i %s now:\n", y1, x1, name1); for (j = conn_start; j < conn_start + num_conns_for_this_wire+1; j++) { fprintf(stderr, "c%i: y%02i x%02i %s -> y%02i x%02i %s\n", j, y1, x1, name1, tile1->conns[j*3+1], tile1->conns[j*3], strarray_lookup(&model->str, tile1->conns[j*3+2])); } #endif return 0; } int add_conn_bi(struct fpga_model* model, int y1, int x1, const char* name1, int y2, int x2, const char* name2) { int rc = add_conn_uni(model, y1, x1, name1, y2, x2, name2); if (rc) return rc; return add_conn_uni(model, y2, x2, name2, y1, x1, name1); } int add_conn_range(struct fpga_model* model, int y1, int x1, const char* name1, int start1, int count, int y2, int x2, const char* name2, int start2) { char buf1[128], buf2[128]; int rc, i; if (count < 1) return add_conn_bi(model, y1, x1, name1, y2, x2, name2); for (i = 0; i < count; i++) { snprintf(buf1, sizeof(buf1), name1, start1+i); snprintf(buf2, sizeof(buf2), name2, start2+i); rc = add_conn_bi(model, y1, x1, buf1, y2, x2, buf2); if (rc) return rc; } return 0; } struct w_point // wire point { const char* name; int start_count; // if there is a %i in the name, this is the start number int y, x; }; struct w_net { int wire_count; // if > 0, %i in the name will be incremented this many times struct w_point pts[8]; }; int add_conn_net(struct fpga_model* model, struct w_net* net) { int i, j, rc; for (i = 0; net->pts[i].name[0] && i < sizeof(net->pts)/sizeof(net->pts[0]); i++) { for (j = i+1; net->pts[j].name[0] && j < sizeof(net->pts)/sizeof(net->pts[0]); j++) { rc = add_conn_range(model, net->pts[i].y, net->pts[i].x, wpref(model->tiles[net->pts[i].y * model->tile_x_range + net->pts[i].x].flags, net->pts[i].name), net->pts[i].start_count, net->wire_count, net->pts[j].y, net->pts[j].x, wpref(model->tiles[net->pts[j].y * model->tile_x_range + net->pts[j].x].flags, net->pts[j].name), net->pts[j].start_count); if (rc) goto xout; } } return 0; xout: return rc; } int run_wires(struct fpga_model* model) { struct fpga_tile* tile, *tile_up1, *tile_up2, *tile_dn1, *tile_dn2; int x, y, i, rc; rc = -1; for (y = 0; y < model->tile_y_range; y++) { for (x = 0; x < model->tile_x_range; x++) { tile = &model->tiles[y * model->tile_x_range + x]; tile_up1 = &model->tiles[(y-1) * model->tile_x_range + x]; tile_up2 = &model->tiles[(y-2) * model->tile_x_range + x]; tile_dn1 = &model->tiles[(y+1) * model->tile_x_range + x]; tile_dn2 = &model->tiles[(y+2) * model->tile_x_range + x]; if (tile->flags & TF_VERT_ROUTING) { // NR1B-NR1E if (tile_up1->flags & TF_UNDER_TOPMOST_TILE) { { struct w_net net = { 4, {{ "NR1B%i", 0, y, x }, { "NR1B%i", 0, y-1, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else if (tile_up1->flags & (TF_ROW_HORIZ_AXSYMM | TF_CHIP_HORIZ_AXSYMM | TF_CHIP_HORIZ_AXSYMM_CENTER)) { { struct w_net net = { 4, {{ "NR1B%i", 0, y, x }, { "NR1E%i", 0, y-1, x }, { "NR1E%i", 0, y-2, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else { { struct w_net net = { 4, {{ "NR1B%i", 0, y, x }, { "NR1E%i", 0, y-1, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } if (tile->flags & TF_MACC_COL && tile_dn1->flags & TF_ABOVE_BOTTOMMOST_TILE) { { struct w_net net = { 4, {{ "NR1E%i", 0, y, x }, { "NR1E%i", 0, y+1, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } } // NN2E_S0 if (tile_up1->flags & TF_UNDER_TOPMOST_TILE) { { struct w_net net = { -1, {{ "NN2E_S0", 0, y, x }, { "NN2E_S0", 0, y-1, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else if (tile_up2->flags & TF_UNDER_TOPMOST_TILE) { } else if (tile_up1->flags & (TF_ROW_HORIZ_AXSYMM | TF_CHIP_HORIZ_AXSYMM | TF_CHIP_HORIZ_AXSYMM_CENTER)) { if ((rc = add_conn_bi(model, y-1, x, wpref(tile_up1->flags, "NN2M0"), y-2, x, "NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y-3, x, "NN2E0", y-2, x, "NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y, x, "NN2B0", y-2, x, "NN2E_S0"))) goto xout; } else if (tile_up2->flags & (TF_ROW_HORIZ_AXSYMM | TF_CHIP_HORIZ_AXSYMM | TF_CHIP_HORIZ_AXSYMM_CENTER)) { if ((rc = add_conn_bi(model, y, x, "NN2B0", y-1, x, "NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y, x, "NN2B0", y-2, x, wpref(tile_up2->flags, "NN2E_S0")))) goto xout; if ((rc = add_conn_bi(model, y-2, x, wpref(tile_up2->flags, "NN2E0"), y-1, x, "NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y-2, x, wpref(tile_up2->flags, "NN2E_S0"), y-1, x, "NN2M0"))) goto xout; if ((rc = add_conn_bi(model, y-2, x, wpref(tile_up2->flags, "NN2E_S0"), y-1, x, "NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y-2, x, wpref(tile_up2->flags, "NN2E_S0"), y-3, x, "NN2E0"))) goto xout; if ((rc = add_conn_bi(model, y-3, x, "NN2E0", y-1, x, "NN2E_S0"))) goto xout; } else { if ((rc = add_conn_bi(model, y, x, "NN2B0", y-1, x, "NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y-2, x, "NN2E0", y-1, x, "NN2E_S0"))) goto xout; if (tile_dn1->flags & TF_ABOVE_BOTTOMMOST_TILE) { if ((rc = add_conn_bi(model, y, x, "NN2E_S0", y-1, x, "NN2E0"))) goto xout; if (!(tile->flags & TF_BRAM_COL)) { if ((rc = add_conn_bi(model, y, x, "NN2E0", y+1, x, "IOI_BTERM_NN2E_S0"))) goto xout; if ((rc = add_conn_bi(model, y, x, "NN2E_S0", y+1, x, "IOI_BTERM_NN2M0"))) goto xout; } } } } if (tile->flags & TF_DIRWIRE_START) { if (tile_up1->flags & TF_UNDER_TOPMOST_TILE) { { struct w_net net = { 4, {{ "NN2B%i", 0, y, x }, { "NN2B%i", 0, y-1, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else if (tile_up2->flags & TF_UNDER_TOPMOST_TILE) { { struct w_net net = { 4, {{ "NN2B%i", 0, y, x }, { "NN2M%i", 0, y-1, x }, { "NN2M%i", 0, y-2, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else if (tile_up1->flags & (TF_ROW_HORIZ_AXSYMM|TF_CHIP_HORIZ_AXSYMM|TF_CHIP_HORIZ_AXSYMM_CENTER)) { { struct w_net net = { 4, {{ "NN2B%i", 0, y, x }, { "NN2M%i", 0, y-1, x }, { "NN2M%i", 0, y-2, x }, { "NN2E%i", 0, y-3, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else if (tile_up2->flags & (TF_ROW_HORIZ_AXSYMM|TF_CHIP_HORIZ_AXSYMM|TF_CHIP_HORIZ_AXSYMM_CENTER)) { { struct w_net net = { 4, {{ "NN2B%i", 0, y, x }, { "NN2M%i", 0, y-1, x }, { "NN2E%i", 0, y-2, x }, { "NN2E%i", 0, y-3, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } else { { struct w_net net = { 4, {{ "NN2B%i", 0, y, x }, { "NN2M%i", 0, y-1, x }, { "NN2E%i", 0, y-2, x }, { "" }}}; if ((rc = add_conn_net(model, &net))) goto xout; } } } } } return 0; xout: return rc; } int init_tiles(struct fpga_model* model) { int tile_rows, tile_columns, i, j, k, l, x, y, row_top_y, center_row, left_side; int start, end; struct fpga_tile* tile_i0; tile_rows = 1 /* middle */ + (8+1+8)*model->cfg_rows + 2+2 /* two extra tiles at top and bottom */; tile_columns = 5 /* left */ + 5 /* right */; for (i = 0; model->cfg_columns[i] != 0; i++) { tile_columns += 2; // 2 for logic blocks L/M and minimum for others if (model->cfg_columns[i] == 'B' || model->cfg_columns[i] == 'D') tile_columns++; // 3 for bram or macc else if (model->cfg_columns[i] == 'R') tile_columns+=2; // 2+2 for middle IO+logic+PLL/DCM } model->tile_x_range = tile_columns; model->tile_y_range = tile_rows; model->tiles = calloc(tile_columns * tile_rows, sizeof(struct fpga_tile)); if (!model->tiles) { fprintf(stderr, "%i: Out of memory.\n", __LINE__); return -1; } for (i = 0; i < tile_rows * tile_columns; i++) model->tiles[i].type = NA; if (!(tile_rows % 2)) fprintf(stderr, "Unexpected even number of tile rows (%i).\n", tile_rows); center_row = 2 /* top IO files */ + (model->cfg_rows/2)*(8+1/*middle of row clock*/+8); // flag horizontal rows for (x = 0; x < model->tile_x_range; x++) { model->tiles[x].flags |= TF_TOPMOST_TILE; model->tiles[model->tile_x_range + x].flags |= TF_UNDER_TOPMOST_TILE; for (i = model->cfg_rows-1; i >= 0; i--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-i)*(8+1/*middle of row clock*/+8); if (i<(model->cfg_rows/2)) row_top_y++; // middle system tiles model->tiles[(row_top_y+8)*model->tile_x_range + x].flags |= TF_ROW_HORIZ_AXSYMM; model->tiles[(row_top_y+16)*model->tile_x_range + x].flags |= TF_BOTTOM_OF_ROW; } model->tiles[center_row * model->tile_x_range + x].flags |= TF_CHIP_HORIZ_AXSYMM; model->tiles[(model->tile_y_range-2)*model->tile_x_range + x].flags |= TF_ABOVE_BOTTOMMOST_TILE; model->tiles[(model->tile_y_range-1)*model->tile_x_range + x].flags |= TF_BOTTOMMOST_TILE; } // // top, bottom, center: // go through columns from left to right, rows from top to bottom // left_side = 1; // turn off (=right side) when reaching the 'R' middle column i = 5; // skip left IO columns for (j = 0; model->cfg_columns[j]; j++) { if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'l' || model->cfg_columns[j] == 'M' || model->cfg_columns[j] == 'm' || model->cfg_columns[j] == 'D' || model->cfg_columns[j] == 'B' || model->cfg_columns[j] == 'R') { for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles (center row) for (l = 0; l < 16; l++) model->tiles[(row_top_y+(l<8?l:l+1))*model->tile_x_range + i].flags |= TF_VERT_ROUTING; } } switch (model->cfg_columns[j]) { case 'L': case 'l': case 'M': case 'm': for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles (center row) for (l = 0; l < 16; l++) { tile_i0 = &model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i]; tile_i0->flags |= TF_LOGIC_COL; tile_i0[1].flags |= TF_LOGIC_COL; } start = ((k == model->cfg_rows-1 && (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'M')) ? 2 : 0); end = ((k == 0 && (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'M')) ? 14 : 16); for (l = start; l < end; l++) { tile_i0 = &model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i]; if (l < 15 || (!k && (model->cfg_columns[j] == 'l' || model->cfg_columns[j] == 'm'))) { tile_i0->type = ROUTING; tile_i0->flags |= TF_DIRWIRE_START; } else tile_i0->type = ROUTING_BRK; tile_i0[1].type = (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'l') ? LOGIC_XL : LOGIC_XM; } if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'l') { model->tiles[(row_top_y+8)*tile_columns + i].type = HCLK_ROUTING_XL; model->tiles[(row_top_y+8)*tile_columns + i + 1].type = HCLK_LOGIC_XL; } else { model->tiles[(row_top_y+8)*tile_columns + i].type = HCLK_ROUTING_XM; model->tiles[(row_top_y+8)*tile_columns + i + 1].type = HCLK_LOGIC_XM; } } if (j && model->cfg_columns[j-1] == 'R') { model->tiles[tile_columns + i].type = IO_BUFPLL_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i].type = IO_BUFPLL_TERM_B; } else { model->tiles[tile_columns + i].type = IO_TERM_T; if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'M') model->tiles[(tile_rows-2)*tile_columns + i].type = IO_TERM_B; else model->tiles[(tile_rows-2)*tile_columns + i].type = LOGIC_ROUTING_TERM_B; } if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'M') { model->tiles[i].type = IO_T; model->tiles[(tile_rows-1)*tile_columns + i].type = IO_B; model->tiles[2*tile_columns + i].type = IO_ROUTING; model->tiles[2*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[3*tile_columns + i].type = IO_ROUTING; model->tiles[3*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[(tile_rows-4)*tile_columns + i].type = IO_ROUTING; model->tiles[(tile_rows-4)*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[(tile_rows-3)*tile_columns + i].type = IO_ROUTING; model->tiles[(tile_rows-3)*tile_columns + i].flags |= TF_DIRWIRE_START; } if (j && model->cfg_columns[j-1] == 'R') { model->tiles[tile_columns + i + 1].type = IO_LOGIC_REG_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i + 1].type = IO_LOGIC_REG_TERM_B; } else { model->tiles[tile_columns + i + 1].type = IO_LOGIC_TERM_T; if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'M') model->tiles[(tile_rows-2)*tile_columns + i + 1].type = IO_LOGIC_TERM_B; else model->tiles[(tile_rows-2)*tile_columns + i + 1].type = LOGIC_NOIO_TERM_B; } if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'M') { model->tiles[2*tile_columns + i + 1].type = IO_OUTER_T; model->tiles[3*tile_columns + i + 1].type = IO_INNER_T; model->tiles[(tile_rows-4)*tile_columns + i + 1].type = IO_INNER_B; model->tiles[(tile_rows-3)*tile_columns + i + 1].type = IO_OUTER_B; } if (model->cfg_columns[j] == 'L' || model->cfg_columns[j] == 'l') { model->tiles[center_row*tile_columns + i].type = REGH_ROUTING_XL; model->tiles[center_row*tile_columns + i + 1].type = REGH_LOGIC_XL; } else { model->tiles[center_row*tile_columns + i].type = REGH_ROUTING_XM; model->tiles[center_row*tile_columns + i + 1].type = REGH_LOGIC_XM; } i += 2; break; case 'B': for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles for (l = 0; l < 16; l++) { tile_i0 = &model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i]; tile_i0->flags |= TF_BRAM_COL; tile_i0[1].flags |= TF_BRAM_COL; tile_i0[2].flags |= TF_BRAM_COL; if (l < 15) { tile_i0->type = BRAM_ROUTING; tile_i0->flags |= TF_DIRWIRE_START; } else tile_i0->type = BRAM_ROUTING_BRK; model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i + 1].type = ROUTING_VIA; if (!(l%4)) model->tiles[(row_top_y+3+(l<8?l:l+1))*tile_columns + i + 2].type = BRAM; } model->tiles[(row_top_y+8)*tile_columns + i].type = HCLK_BRAM_ROUTING; model->tiles[(row_top_y+8)*tile_columns + i + 1].type = HCLK_BRAM_ROUTING_VIA; model->tiles[(row_top_y+8)*tile_columns + i + 2].type = HCLK_BRAM; } model->tiles[tile_columns + i].type = BRAM_ROUTING_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i].type = BRAM_ROUTING_TERM_B; model->tiles[tile_columns + i + 1].type = BRAM_ROUTING_VIA_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i + 1].type = BRAM_ROUTING_VIA_TERM_B; model->tiles[tile_columns + i + 2].type = left_side ? BRAM_TERM_LT : BRAM_TERM_RT; model->tiles[(tile_rows-2)*tile_columns + i + 2].type = left_side ? BRAM_TERM_LB : BRAM_TERM_RB; model->tiles[center_row*tile_columns + i].type = REGH_BRAM_ROUTING; model->tiles[center_row*tile_columns + i + 1].type = REGH_BRAM_ROUTING_VIA; model->tiles[center_row*tile_columns + i + 2].type = left_side ? REGH_BRAM_L : REGH_BRAM_R; i += 3; break; case 'D': for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles for (l = 0; l < 16; l++) { tile_i0 = &model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i]; tile_i0->flags |= TF_MACC_COL; tile_i0[1].flags |= TF_MACC_COL; tile_i0[2].flags |= TF_MACC_COL; if (l < 15) { tile_i0->type = ROUTING; tile_i0->flags |= TF_DIRWIRE_START; } else tile_i0->type = ROUTING_BRK; tile_i0[1].type = ROUTING_VIA; if (!(l%4)) model->tiles[(row_top_y+3+(l<8?l:l+1))*tile_columns + i + 2].type = MACC; } model->tiles[(row_top_y+8)*tile_columns + i].type = HCLK_MACC_ROUTING; model->tiles[(row_top_y+8)*tile_columns + i + 1].type = HCLK_MACC_ROUTING_VIA; model->tiles[(row_top_y+8)*tile_columns + i + 2].type = HCLK_MACC; } model->tiles[tile_columns + i].type = MACC_ROUTING_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i].type = MACC_ROUTING_TERM_B; model->tiles[tile_columns + i + 1].type = MACC_VIA_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i + 1].type = IO_LOGIC_TERM_B; model->tiles[tile_columns + i + 2].type = left_side ? MACC_TERM_TL : MACC_TERM_TR; model->tiles[(tile_rows-2)*tile_columns + i + 2].type = left_side ? MACC_TERM_BL : MACC_TERM_BR; model->tiles[center_row*tile_columns + i].type = REGH_MACC_ROUTING; model->tiles[center_row*tile_columns + i + 1].type = REGH_MACC_ROUTING_VIA; model->tiles[center_row*tile_columns + i + 2].type = REGH_MACC_L; i += 3; break; case 'R': if (model->cfg_columns[j+1] != 'M') { // We expect a LOGIC_XM column to follow the center for // the top and bottom bufpll and reg routing. fprintf(stderr, "Expecting LOGIC_XM after center but found '%c'\n", model->cfg_columns[j+1]); } // flag vertical axis of symmetry for (y = 0; y < model->tile_y_range; y++) model->tiles[y*model->tile_x_range + i + 3].flags |= TF_CHIP_VERT_AXSYMM; left_side = 0; for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles for (l = 0; l < 16; l++) { tile_i0 = &model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i]; tile_i0->flags |= TF_LOGIC_COL; tile_i0[1].flags |= TF_LOGIC_COL; if ((k < model->cfg_rows-1 || l >= 2) && (k || l<14)) { if (l < 15) { tile_i0->type = ROUTING; tile_i0->flags |= TF_DIRWIRE_START; } else tile_i0->type = ROUTING_BRK; if (l == 7) tile_i0[1].type = ROUTING_VIA_IO; else if (l == 8) tile_i0[1].type = (k%2) ? ROUTING_VIA_CARRY : ROUTING_VIA_IO_DCM; else if (l == 15 && k==model->cfg_rows/2) tile_i0[1].type = ROUTING_VIA_REGC; else tile_i0[1].type = LOGIC_XL; } if (l == 7 || (l == 8 && !(k%2))) { // even row, together with DCM tile_i0->type = IO_ROUTING; tile_i0->flags |= TF_DIRWIRE_START; } if (l == 7) { if (k%2) // odd model->tiles[(row_top_y+l)*tile_columns + i + 2].type = (k<(model->cfg_rows/2)) ? PLL_B : PLL_T; else // even model->tiles[(row_top_y+l)*tile_columns + i + 2].type = (k<(model->cfg_rows/2)) ? DCM_B : DCM_T; } // four midbuf tiles, in the middle of the top and bottom halves if (l == 15) { if (k == model->cfg_rows*3/4) model->tiles[(row_top_y+l+1)*tile_columns + i + 3].type = REGV_MIDBUF_T; else if (k == model->cfg_rows/4) model->tiles[(row_top_y+l+1)*tile_columns + i + 3].type = REGV_HCLKBUF_B; else model->tiles[(row_top_y+l+1)*tile_columns + i + 3].type = REGV_BRK; } else if (l == 0 && k == model->cfg_rows*3/4-1) { model->tiles[(row_top_y+l)*tile_columns + i + 3].type = REGV_HCLKBUF_T; } else if (l == 0 && k == model->cfg_rows/4-1) { model->tiles[(row_top_y+l)*tile_columns + i + 3].type = REGV_MIDBUF_B; } else if (l == 8) { model->tiles[(row_top_y+l+1)*tile_columns + i + 3].type = (kcfg_rows/2) ? REGV_B : REGV_T; } else model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + i + 3].type = REGV; } model->tiles[(row_top_y+8)*tile_columns + i].type = HCLK_ROUTING_XL; model->tiles[(row_top_y+8)*tile_columns + i + 1].type = HCLK_LOGIC_XL; model->tiles[(row_top_y+8)*tile_columns + i + 3].type = HCLK_REGV; } model->tiles[i].type = IO_T; model->tiles[(tile_rows-1)*tile_columns + i].type = IO_B; model->tiles[tile_columns + i].type = IO_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i].type = IO_TERM_B; model->tiles[2*tile_columns + i].type = IO_ROUTING; model->tiles[2*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[3*tile_columns + i].type = IO_ROUTING; model->tiles[3*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[(tile_rows-4)*tile_columns + i].type = IO_ROUTING; model->tiles[(tile_rows-4)*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[(tile_rows-3)*tile_columns + i].type = IO_ROUTING; model->tiles[(tile_rows-3)*tile_columns + i].flags |= TF_DIRWIRE_START; model->tiles[tile_columns + i + 1].type = IO_LOGIC_REG_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i + 1].type = IO_LOGIC_REG_TERM_B; model->tiles[2*tile_columns + i + 1].type = IO_OUTER_T; model->tiles[3*tile_columns + i + 1].type = IO_INNER_T; model->tiles[(tile_rows-4)*tile_columns + i + 1].type = IO_INNER_B; model->tiles[(tile_rows-3)*tile_columns + i + 1].type = IO_OUTER_B; model->tiles[i + 2].type = REG_T; model->tiles[tile_columns + i + 2].type = REG_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i + 2].type = REG_TERM_B; model->tiles[(tile_rows-1)*tile_columns + i + 2].type = REG_B; model->tiles[tile_columns + i + 3].type = REGV_TERM_T; model->tiles[(tile_rows-2)*tile_columns + i + 3].type = REGV_TERM_B; model->tiles[center_row*tile_columns + i].type = REGC_ROUTING; model->tiles[center_row*tile_columns + i].flags |= TF_CHIP_HORIZ_AXSYMM_CENTER; model->tiles[center_row*tile_columns + i + 1].type = REGC_LOGIC; model->tiles[center_row*tile_columns + i + 1].flags |= TF_CHIP_HORIZ_AXSYMM_CENTER; model->tiles[center_row*tile_columns + i + 2].type = REGC_CMT; model->tiles[center_row*tile_columns + i + 2].flags |= TF_CHIP_HORIZ_AXSYMM_CENTER; model->tiles[center_row*tile_columns + i + 3].type = CENTER; model->tiles[center_row*tile_columns + i + 3].flags |= TF_CHIP_HORIZ_AXSYMM_CENTER; i += 4; break; default: fprintf(stderr, "Unexpected column identifier '%c'\n", model->cfg_columns[j]); break; } } // flag left and right vertical routing for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles (center row) for (l = 0; l < 16; l++) { model->tiles[(row_top_y+(l<8?l:l+1))*model->tile_x_range + 2].flags |= TF_VERT_ROUTING; model->tiles[(row_top_y+(l<8?l:l+1))*model->tile_x_range + model->tile_x_range - 5].flags |= TF_VERT_ROUTING; } } // // left IO // for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles for (l = 0; l < 16; l++) { // // +0 // if (model->cfg_left_wiring[(model->cfg_rows-1-k)*16+l] == 'W') model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns].type = IO_L; // // +1 // if ((k == model->cfg_rows-1 && !l) || (!k && l==15)) model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 1].type = CORNER_TERM_L; else if (k == model->cfg_rows/2 && l == 12) model->tiles[(row_top_y+l+1)*tile_columns + 1].type = IO_TERM_L_UPPER_TOP; else if (k == model->cfg_rows/2 && l == 13) model->tiles[(row_top_y+l+1)*tile_columns + 1].type = IO_TERM_L_UPPER_BOT; else if (k == (model->cfg_rows/2)-1 && !l) model->tiles[(row_top_y+l)*tile_columns + 1].type = IO_TERM_L_LOWER_TOP; else if (k == (model->cfg_rows/2)-1 && l == 1) model->tiles[(row_top_y+l)*tile_columns + 1].type = IO_TERM_L_LOWER_BOT; else model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 1].type = IO_TERM_L; // // +2 // if (model->cfg_left_wiring[(model->cfg_rows-1-k)*16+l] == 'W') { if (l == 15 && k && k != model->cfg_rows/2) model->tiles[(row_top_y+l+1)*tile_columns + 2].type = ROUTING_IO_L_BRK; else model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 2].type = ROUTING_IO_L; } else { // unwired if (k && k != model->cfg_rows/2 && l == 15) model->tiles[(row_top_y+l+1)*tile_columns + 2].type = ROUTING_BRK; else if (k == model->cfg_rows/2 && l == 14) model->tiles[(row_top_y+l+1)*tile_columns + 2].type = ROUTING_GCLK; else { model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 2].type = ROUTING; model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 2].flags |= TF_DIRWIRE_START; } } // // +3 // if (model->cfg_left_wiring[(model->cfg_rows-1-k)*16+l] == 'W') { model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 3].type = ROUTING_IO_VIA_L; } else { // unwired if (k == model->cfg_rows-1 && !l) { model->tiles[(row_top_y+l)*tile_columns + 3].type = CORNER_TL; } else if (!k && l == 15) { model->tiles[(row_top_y+l+1)*tile_columns + 3].type = CORNER_BL; } else { if (k && k != model->cfg_rows/2 && l == 15) model->tiles[(row_top_y+l+1)*tile_columns + 3].type = ROUTING_VIA_CARRY; else model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + 3].type = ROUTING_VIA; } } } model->tiles[(row_top_y+8)*tile_columns + 1].type = HCLK_TERM_L; model->tiles[(row_top_y+8)*tile_columns + 2].type = HCLK_ROUTING_IO_L; if (k >= model->cfg_rows/2) { // top half if (k > (model->cfg_rows*3)/4) model->tiles[(row_top_y+8)*tile_columns + 3].type = HCLK_IO_TOP_UP_L; else if (k == (model->cfg_rows*3)/4) model->tiles[(row_top_y+8)*tile_columns + 3].type = HCLK_IO_TOP_SPLIT_L; else model->tiles[(row_top_y+8)*tile_columns + 3].type = HCLK_IO_TOP_DN_L; } else { // bottom half if (k < model->cfg_rows/4 - 1) model->tiles[(row_top_y+8)*tile_columns + 3].type = HCLK_IO_BOT_DN_L; else if (k == model->cfg_rows/4 - 1) model->tiles[(row_top_y+8)*tile_columns + 3].type = HCLK_IO_BOT_SPLIT_L; else model->tiles[(row_top_y+8)*tile_columns + 3].type = HCLK_IO_BOT_UP_L; } model->tiles[(row_top_y+8)*tile_columns + 4].type = HCLK_MCB; } model->tiles[(center_row-3)*tile_columns].type = IO_PCI_L; model->tiles[(center_row-2)*tile_columns].type = IO_PCI_CONN_L; model->tiles[(center_row-1)*tile_columns].type = IO_PCI_CONN_L; model->tiles[center_row*tile_columns].type = REG_L; model->tiles[(center_row+1)*tile_columns].type = IO_RDY_L; model->tiles[center_row*tile_columns + 1].type = REGH_IO_TERM_L; model->tiles[tile_columns + 2].type = CORNER_TERM_T; model->tiles[(tile_rows-2)*tile_columns + 2].type = CORNER_TERM_B; model->tiles[center_row*tile_columns + 2].type = REGH_ROUTING_IO_L; model->tiles[tile_columns + 3].type = ROUTING_IO_PCI_CE_L; model->tiles[(tile_rows-2)*tile_columns + 3].type = ROUTING_IO_PCI_CE_L; model->tiles[center_row*tile_columns + 3].type = REGH_IO_L; model->tiles[center_row*tile_columns + 4].type = REGH_MCB; // // right IO // for (k = model->cfg_rows-1; k >= 0; k--) { row_top_y = 2 /* top IO tiles */ + (model->cfg_rows-1-k)*(8+1/*middle of row clock*/+8); if (k<(model->cfg_rows/2)) row_top_y++; // middle system tiles for (l = 0; l < 16; l++) { // // -1 // if (k == model->cfg_rows/2 && l == 13) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 1].type = IO_RDY_R; else if (k == model->cfg_rows/2 && l == 14) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 1].type = IO_PCI_CONN_R; else if (k == model->cfg_rows/2 && l == 15) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 1].type = IO_PCI_CONN_R; else if (k == model->cfg_rows/2-1 && !l) model->tiles[(row_top_y+l)*tile_columns + tile_columns - 1].type = IO_PCI_R; else { if (model->cfg_right_wiring[(model->cfg_rows-1-k)*16+l] == 'W') model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 1].type = IO_R; } // // -2 // if ((k == model->cfg_rows-1 && (!l || l == 1)) || (!k && (l==15 || l==14))) model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 2].type = CORNER_TERM_R; else if (k == model->cfg_rows/2 && l == 12) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 2].type = IO_TERM_R_UPPER_TOP; else if (k == model->cfg_rows/2 && l == 13) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 2].type = IO_TERM_R_UPPER_BOT; else if (k == (model->cfg_rows/2)-1 && !l) model->tiles[(row_top_y+l)*tile_columns + tile_columns - 2].type = IO_TERM_R_LOWER_TOP; else if (k == (model->cfg_rows/2)-1 && l == 1) model->tiles[(row_top_y+l)*tile_columns + tile_columns - 2].type = IO_TERM_R_LOWER_BOT; else model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 2].type = IO_TERM_R; // // -3 // // // -4 // if (model->cfg_right_wiring[(model->cfg_rows-1-k)*16+l] == 'W') model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 4].type = ROUTING_IO_VIA_R; else { if (k == model->cfg_rows-1 && l == 0) model->tiles[(row_top_y+l)*tile_columns + tile_columns - 4].type = CORNER_TR_UPPER; else if (k == model->cfg_rows-1 && l == 1) model->tiles[(row_top_y+l)*tile_columns + tile_columns - 4].type = CORNER_TR_LOWER; else if (k && k != model->cfg_rows/2 && l == 15) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 4].type = ROUTING_VIA_CARRY; else if (!k && l == 14) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 4].type = CORNER_BR_UPPER; else if (!k && l == 15) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 4].type = CORNER_BR_LOWER; else model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 4].type = ROUTING_VIA; } // // -5 // if (model->cfg_right_wiring[(model->cfg_rows-1-k)*16+l] == 'W') { model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 5].type = IO_ROUTING; model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 5].flags |= TF_DIRWIRE_START; } else { if (k && k != model->cfg_rows/2 && l == 15) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 5].type = ROUTING_BRK; else if (k == model->cfg_rows/2 && l == 14) model->tiles[(row_top_y+l+1)*tile_columns + tile_columns - 5].type = ROUTING_GCLK; else { model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 5].type = ROUTING; model->tiles[(row_top_y+(l<8?l:l+1))*tile_columns + tile_columns - 5].flags |= TF_DIRWIRE_START; } } } model->tiles[(row_top_y+8)*tile_columns + tile_columns - 2].type = HCLK_TERM_R; model->tiles[(row_top_y+8)*tile_columns + tile_columns - 3].type = HCLK_MCB; model->tiles[(row_top_y+8)*tile_columns + tile_columns - 5].type = HCLK_ROUTING_IO_R; if (k >= model->cfg_rows/2) { // top half if (k > (model->cfg_rows*3)/4) model->tiles[(row_top_y+8)*tile_columns + tile_columns - 4].type = HCLK_IO_TOP_UP_R; else if (k == (model->cfg_rows*3)/4) model->tiles[(row_top_y+8)*tile_columns + tile_columns - 4].type = HCLK_IO_TOP_SPLIT_R; else model->tiles[(row_top_y+8)*tile_columns + tile_columns - 4].type = HCLK_IO_TOP_DN_R; } else { // bottom half if (k < model->cfg_rows/4 - 1) model->tiles[(row_top_y+8)*tile_columns + tile_columns - 4].type = HCLK_IO_BOT_DN_R; else if (k == model->cfg_rows/4 - 1) model->tiles[(row_top_y+8)*tile_columns + tile_columns - 4].type = HCLK_IO_BOT_SPLIT_R; else model->tiles[(row_top_y+8)*tile_columns + tile_columns - 4].type = HCLK_IO_BOT_UP_R; } } model->tiles[tile_columns + tile_columns - 5].type = CORNER_TERM_T; model->tiles[(tile_rows-2)*tile_columns + tile_columns - 5].type = CORNER_TERM_B; model->tiles[tile_columns + tile_columns - 4].type = ROUTING_IO_PCI_CE_R; model->tiles[(tile_rows-2)*tile_columns + tile_columns - 4].type = ROUTING_IO_PCI_CE_R; model->tiles[center_row*tile_columns + tile_columns - 1].type = REG_R; model->tiles[center_row*tile_columns + tile_columns - 2].type = REGH_IO_TERM_R; model->tiles[center_row*tile_columns + tile_columns - 3].type = REGH_MCB; model->tiles[center_row*tile_columns + tile_columns - 4].type = REGH_IO_R; model->tiles[center_row*tile_columns + tile_columns - 5].type = REGH_ROUTING_IO_R; return 0; } void fpga_free_model(struct fpga_model* model) { if (!model) return; strarray_free(&model->str); free(model->tiles); memset(model, 0, sizeof(*model)); } const char* fpga_tiletype_str(enum fpga_tile_type type) { if (type >= sizeof(fpga_ttstr)/sizeof(fpga_ttstr[0]) || !fpga_ttstr[type]) return "UNK"; return fpga_ttstr[type]; } // Dan Bernstein's hash function uint32_t hash_djb2(const unsigned char* str) { uint32_t hash = 5381; int c; while ((c = *str++) != 0) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ return hash; } // // The format of each entry in a bin is. // uint16_t idx // uint16_t entry len including 4-byte header // char[] zero-terminated string // // Offsets point to the zero-terminated string, so the len // is at off-2, the index at off-4. bin0 offset0 can thus be // used as a special value to signal 'no entry'. // const char* strarray_lookup(struct hashed_strarray* array, uint16_t idx) { int bin, offset; if (!array->index_to_bin || !array->bin_offsets || !idx) return 0; bin = array->index_to_bin[idx]; offset = array->bin_offsets[idx]; // bin 0 offset 0 is a special value that signals 'no // entry'. Normal offsets cannot be less than 4. if (!bin && !offset) return 0; if (!array->bin_strings[bin] || offset >= array->bin_len[bin] || offset < 4) { // This really should never happen and is an internal error. fprintf(stderr, "Internal error.\n"); return 0; } return &array->bin_strings[bin][offset]; } #define BIN_INCREMENT 32768 int strarray_find_or_add(struct hashed_strarray* array, const char* str, uint16_t* idx) { int bin, search_off, str_len, i, free_index; int new_alloclen, start_index; unsigned long hash; void* new_ptr; hash = hash_djb2((const unsigned char*) str); str_len = strlen(str); bin = hash % (sizeof(array->bin_strings)/sizeof(array->bin_strings[0])); // iterate over strings in bin to find match if (array->bin_strings[bin]) { search_off = 4; while (search_off < array->bin_len[bin]) { if (!strcmp(&array->bin_strings[bin][search_off], str)) { *idx = *(uint16_t*)&array->bin_strings [bin][search_off-4]; if (!(*idx)) { fprintf(stderr, "Internal error - index 0.\n"); return 0; } return 1; } search_off += *(uint16_t*)&array->bin_strings [bin][search_off-2]; } } // search free index start_index = (uint16_t) ((hash >> 16) ^ (hash & 0xFFFF)); for (i = 0; i < HASHARRAY_NUM_INDICES; i++) { int cur_i = (start_index+i)%HASHARRAY_NUM_INDICES; if (!cur_i) // never issue index 0 continue; if (!array->bin_offsets[cur_i]) break; } if (i >= HASHARRAY_NUM_INDICES) { fprintf(stderr, "All array indices full.\n"); return 0; } free_index = (start_index+i)%HASHARRAY_NUM_INDICES; // check whether bin needs expansion if (!(array->bin_len[bin]%BIN_INCREMENT) || array->bin_len[bin]%BIN_INCREMENT + 4+str_len+1 > BIN_INCREMENT) { new_alloclen = ((array->bin_len[bin] + 4+str_len+1)/BIN_INCREMENT + 1) * BIN_INCREMENT; new_ptr = realloc(array->bin_strings[bin], new_alloclen); if (!new_ptr) { fprintf(stderr, "Out of memory.\n"); return 0; } array->bin_strings[bin] = new_ptr; } // append new string at end of bin *(uint16_t*)&array->bin_strings[bin][array->bin_len[bin]] = free_index; *(uint16_t*)&array->bin_strings[bin][array->bin_len[bin]+2] = 4+str_len+1; strcpy(&array->bin_strings[bin][array->bin_len[bin]+4], str); array->index_to_bin[free_index] = bin; array->bin_offsets[free_index] = array->bin_len[bin]+4; array->bin_len[bin] += 4+str_len+1; *idx = free_index; return 1; } int strarray_used_slots(struct hashed_strarray* array) { int i, num_used_slots; num_used_slots = 0; if (!array->bin_offsets) return 0; for (i = 0; i < sizeof(array->bin_offsets)/sizeof(*array->bin_offsets); i++) { if (array->bin_offsets[i]) num_used_slots++; } return num_used_slots; } void strarray_init(struct hashed_strarray* array) { memset(array, 0, sizeof(*array)); } void strarray_free(struct hashed_strarray* array) { int i; for (i = 0; i < sizeof(array->bin_strings)/ sizeof(array->bin_strings[0]); i++) { free(array->bin_strings[i]); array->bin_strings[i] = 0; } }