diff --git a/autotest.c b/autotest.c index ba25193..6258038 100644 --- a/autotest.c +++ b/autotest.c @@ -1681,6 +1681,57 @@ fail: return rc; } +static int test_bufg_config(struct test_state* tstate) +{ + int dev_y, dev_x, dev_tidx, i, rc; + + tstate->diff_to_null = 1; +// todo: not implemented +return 0; + i = 0; + while (1) { + rc = fdev_enum(tstate->model, DEV_BUFGMUX, i++, + &dev_y, &dev_x, &dev_tidx); + if (rc) FAIL(EINVAL); + if (dev_y == -1) break; + + rc = fdev_bufgmux(tstate->model, dev_y, dev_x, dev_tidx, + BUFG_CLK_ASYNC, BUFG_DISATTR_LOW, BUFG_SINV_Y); + if (rc) FAIL(rc); + + // stub nets for required pins? s-pin? + if ((rc = diff_printf(tstate))) FAIL(rc); + fdev_delete(tstate->model, dev_y, dev_x, DEV_BUFGMUX, dev_tidx); + } + return 0; +fail: + return rc; +} + +static int test_bufio_config(struct test_state* tstate) +{ +// todo: not implemented + return 0; +} + +static int test_pll_config(struct test_state* tstate) +{ +// todo: not implemented + return 0; +} + +static int test_dcm_config(struct test_state* tstate) +{ +// todo: not implemented + return 0; +} + +static int test_bscan_config(struct test_state* tstate) +{ +// todo: not implemented + return 0; +} + #define DEFAULT_DIFF_EXEC "./autotest_diff.sh" static void printf_help(const char* argv_0, const char** available_tests) @@ -1711,7 +1762,9 @@ int main(int argc, char** argv) char param[1024], cmdline_test[1024]; int i, param_skip, rc; const char* available_tests[] = - { "logic_cfg", "routing_sw", "io_sw", "iob_cfg", "lut_encoding", 0 }; + { "logic_cfg", "routing_sw", "io_sw", "iob_cfg", + "lut_encoding", "bufg_cfg", "bufio_cfg", "pll_cfg", + "dcm_cfg", "bscan_cfg", 0 }; // flush after every line is better for the autotest // output, tee, etc. @@ -1840,6 +1893,26 @@ int main(int argc, char** argv) rc = test_lut_encoding(&tstate); if (rc) FAIL(rc); } + if (!strcmp(cmdline_test, "bufg_cfg")) { + rc = test_bufg_config(&tstate); + if (rc) FAIL(rc); + } + if (!strcmp(cmdline_test, "bufio_cfg")) { + rc = test_bufio_config(&tstate); + if (rc) FAIL(rc); + } + if (!strcmp(cmdline_test, "pll_cfg")) { + rc = test_pll_config(&tstate); + if (rc) FAIL(rc); + } + if (!strcmp(cmdline_test, "dcm_cfg")) { + rc = test_dcm_config(&tstate); + if (rc) FAIL(rc); + } + if (!strcmp(cmdline_test, "bscan_cfg")) { + rc = test_bscan_config(&tstate); + if (rc) FAIL(rc); + } printf("\n"); printf("O Test completed.\n"); diff --git a/libs/control.c b/libs/control.c index 8ad9a5b..d2097ae 100644 --- a/libs/control.c +++ b/libs/control.c @@ -200,6 +200,97 @@ const char* fpga_iob_sitename(struct fpga_model* model, int y, int x, return 0; } +static void enum_x(struct fpga_model *model, enum fpgadev_type type, + int enum_i, int *y, int x, int *type_idx) +{ + int type_count, i, _y; + struct fpga_tile* tile; + + type_count = 0; + for (_y = 0; _y < model->y_height; _y++) { + tile = YX_TILE(model, _y, x); + for (i = 0; i < tile->num_devs; i++) { + if (tile->devs[i].type != type) + continue; + if (type_count == enum_i) { + *y = _y; + *type_idx = type_count; + return; + } + type_count++; + } + } + *y = -1; +} + +int fdev_enum(struct fpga_model* model, enum fpgadev_type type, int enum_i, + int *y, int *x, int *type_idx) +{ + struct fpga_tile* tile; + int i, j, type_count, rc; + + CHECK_RC(model); + switch (type) { + case DEV_BUFGMUX: + tile = YX_TILE(model, model->center_y, model->center_x); + if (!tile) FAIL(EINVAL); + type_count = 0; + for (i = 0; i < tile->num_devs; i++) { + if (tile->devs[i].type != DEV_BUFGMUX) + continue; + if (type_count == enum_i) { + *y = model->center_y; + *x = model->center_x; + *type_idx = type_count; + return 0; + } + type_count++; + } + *y = -1; + return 0; + case DEV_BUFIO: { + int yx_pairs[] = { + TOP_OUTER_ROW, model->center_x-CENTER_CMTPLL_O, + model->center_y, LEFT_OUTER_COL, + model->center_y, model->x_width-RIGHT_OUTER_O, + model->y_height-BOT_OUTER_ROW, model->center_x-CENTER_CMTPLL_O }; + + type_count = 0; + for (i = 0; i < sizeof(yx_pairs)/sizeof(*yx_pairs)/2; i++) { + tile = YX_TILE(model, yx_pairs[i*2], yx_pairs[i*2+1]); + for (j = 0; j < tile->num_devs; j++) { + if (tile->devs[j].type != DEV_BUFIO) + continue; + if (type_count == enum_i) { + *y = yx_pairs[i*2]; + *x = yx_pairs[i*2+1]; + *type_idx = type_count; + return 0; + } + type_count++; + } + } + *y = -1; + return 0; + } + case DEV_PLL: + case DEV_DCM: + enum_x(model, type, enum_i, y, model->center_x + - CENTER_CMTPLL_O, type_idx); + return 0; + case DEV_BSCAN: + enum_x(model, type, enum_i, y, model->x_width + - RIGHT_IO_DEVS_O, type_idx); + return 0; + default: break; + } + HERE(); + *y = -1; + return 0; +fail: + return rc; +} + static const char* dev_str[] = FPGA_DEV_STR; const char* fdev_type2str(enum fpgadev_type type) @@ -852,6 +943,26 @@ fail: return rc; } +int fdev_bufgmux(struct fpga_model* model, int y, int x, + int type_idx, int clk, int disable_attr, int s_inv) +{ + struct fpga_device* dev; + int rc; + + dev = fdev_p(model, y, x, DEV_BUFGMUX, type_idx); + if (!dev) FAIL(EINVAL); + rc = reset_required_pins(dev); + if (rc) FAIL(rc); + + dev->u.bufgmux.clk = clk; + dev->u.bufgmux.disable_attr = disable_attr; + dev->u.bufgmux.s_inv = s_inv; + dev->instantiated = 1; + return 0; +fail: + return rc; +} + static void scan_lut_digits(const char* s, int* digits) { int i; diff --git a/libs/control.h b/libs/control.h index 8068259..8e5cc94 100644 --- a/libs/control.h +++ b/libs/control.h @@ -18,13 +18,17 @@ const char* fpga_iob_sitename(struct fpga_model* model, int y, int x, // 2. The index of the device within devices of the same type in the tile. // +// If index is past the last device of that type, +// y is returned as -1. +int fdev_enum(struct fpga_model* model, enum fpgadev_type type, int enum_i, + int *y, int *x, int *type_idx); + const char* fdev_type2str(enum fpgadev_type type); enum fpgadev_type fdev_str2type(const char* str, int len); // returns 0 if device not found struct fpga_device* fdev_p(struct fpga_model* model, int y, int x, enum fpgadev_type type, dev_type_idx_t type_idx); - // Looks up a device index based on the type index. // returns NO_DEV (-1) if not found dev_idx_t fpga_dev_idx(struct fpga_model* model, @@ -94,6 +98,9 @@ int fdev_iob_slew(struct fpga_model* model, int y, int x, int fdev_iob_drive(struct fpga_model* model, int y, int x, int type_idx, int drive_strength); +int fdev_bufgmux(struct fpga_model* model, int y, int x, + int type_idx, int clk, int disable_attr, int s_inv); + int fdev_set_required_pins(struct fpga_model* model, int y, int x, int type, int type_idx); void fdev_print_required_pins(struct fpga_model* model, int y, int x, diff --git a/libs/floorplan.c b/libs/floorplan.c index e4761be..fc5c186 100644 --- a/libs/floorplan.c +++ b/libs/floorplan.c @@ -184,8 +184,8 @@ static int printf_IOB(FILE* f, struct fpga_model* model, return 0; } -static int read_IOB_attr(struct fpga_model* model, struct fpga_device* dev, - const char* w1, int w1_len, const char* w2, int w2_len) +static int read_IOB_attr(struct fpga_model *model, struct fpga_device *dev, + const char *w1, int w1_len, const char *w2, int w2_len) { // First the one-word attributes. if (!str_cmp(w1, w1_len, "O_used", ZTERM)) { @@ -283,7 +283,7 @@ inst_1: dev->instantiated = 1; return 1; inst_2: - dev->instantiated = 2; + dev->instantiated = 1; return 2; } @@ -655,6 +655,237 @@ inst_2: return 2; } +static int printf_BUFGMUX(FILE* f, struct fpga_model* model, + int y, int x, int config_only) +{ + struct fpga_tile *tile; + struct fpgadev_bufgmux *cfg; + char pref[256]; + int type_count, i, rc; + + tile = YX_TILE(model, y, x); + type_count = 0; + for (i = 0; i < tile->num_devs; i++) { + if (tile->devs[i].type != DEV_BUFGMUX) + continue; + if (config_only && !(tile->devs[i].instantiated)) { + type_count++; + continue; + } + snprintf(pref, sizeof(pref), "dev y%02i x%02i BUFGMUX %i", + y, x, type_count++); + if (!config_only) + fprintf(f, "%s\n", pref); + cfg = &tile->devs[i].u.bufgmux; + switch (cfg->clk) { + case BUFG_CLK_ASYNC: + fprintf(f, "%s clk ASYNC\n", pref); + break; + case BUFG_CLK_SYNC: + fprintf(f, "%s clk SYNC\n", pref); + break; + case 0: break; default: FAIL(EINVAL); + } + switch (cfg->disable_attr) { + case BUFG_DISATTR_LOW: + fprintf(f, "%s disable_attr LOW\n", pref); + break; + case BUFG_DISATTR_HIGH: + fprintf(f, "%s disable_attr HIGH\n", pref); + break; + case 0: break; default: FAIL(EINVAL); + } + switch (cfg->s_inv) { + case BUFG_SINV_N: + fprintf(f, "%s s_inv NO\n", pref); + break; + case BUFG_SINV_Y: + fprintf(f, "%s s_inv YES\n", pref); + break; + case 0: break; default: FAIL(EINVAL); + } + } + return 0; +fail: + return rc; +} + +static int read_BUFGMUX_attr(struct fpga_model *model, struct fpga_device *dev, + const char *w1, int w1_len, const char *w2, int w2_len) +{ + // BUFGMUX only has 2-word attributes + if (w2_len < 1) return 0; + if (!str_cmp(w1, w1_len, "clk", ZTERM)) { + if (!str_cmp(w2, w2_len, "ASYNC", ZTERM)) + dev->u.bufgmux.clk = BUFG_CLK_ASYNC; + else if (!str_cmp(w2, w2_len, "SYNC", ZTERM)) + dev->u.bufgmux.clk = BUFG_CLK_SYNC; + else return 0; + goto inst; + } + if (!str_cmp(w1, w1_len, "disable_attr", ZTERM)) { + if (!str_cmp(w2, w2_len, "LOW", ZTERM)) + dev->u.bufgmux.disable_attr = BUFG_DISATTR_LOW; + else if (!str_cmp(w2, w2_len, "HIGH", ZTERM)) + dev->u.bufgmux.disable_attr = BUFG_DISATTR_HIGH; + else return 0; + goto inst; + } + if (!str_cmp(w1, w1_len, "s_inv", ZTERM)) { + if (!str_cmp(w2, w2_len, "NO", ZTERM)) + dev->u.bufgmux.s_inv = BUFG_SINV_N; + else if (!str_cmp(w2, w2_len, "YES", ZTERM)) + dev->u.bufgmux.s_inv = BUFG_SINV_Y; + else return 0; + goto inst; + } + return 0; +inst: + dev->instantiated = 1; + return 2; +} + +static int printf_BUFIO(FILE* f, struct fpga_model* model, + int y, int x, int config_only) +{ + struct fpga_tile *tile; + struct fpgadev_bufio *cfg; + char pref[256]; + int type_count, i, rc; + + tile = YX_TILE(model, y, x); + type_count = 0; + for (i = 0; i < tile->num_devs; i++) { + if (tile->devs[i].type != DEV_BUFIO) + continue; + if (config_only && !(tile->devs[i].instantiated)) { + type_count++; + continue; + } + snprintf(pref, sizeof(pref), "dev y%02i x%02i BUFGMUX %i", + y, x, type_count++); + if (!config_only) + fprintf(f, "%s\n", pref); + cfg = &tile->devs[i].u.bufio; + if (cfg->divide) + fprintf(f, "%s divide %i\n", pref, cfg->divide); + switch (cfg->divide_bypass) { + case BUFIO_DIVIDEBP_N: + fprintf(f, "%s divide_bypass NO\n", pref); + break; + case BUFIO_DIVIDEBP_Y: + fprintf(f, "%s divide_bypass YES\n", pref); + break; + case 0: break; default: FAIL(EINVAL); + } + switch (cfg->i_inv) { + case BUFIO_IINV_N: + fprintf(f, "%s i_inv NO\n", pref); + break; + case BUFIO_IINV_Y: + fprintf(f, "%s i_inv YES\n", pref); + break; + case 0: break; default: FAIL(EINVAL); + } + } + return 0; +fail: + return rc; +} + +static int read_BUFIO_attr(struct fpga_model *model, struct fpga_device *dev, + const char *w1, int w1_len, const char *w2, int w2_len) +{ + // BUFIO only has 2-word attributes + if (w2_len < 1) return 0; + if (!str_cmp(w1, w1_len, "divide", ZTERM)) { + dev->u.bufio.divide = to_i(w2, w2_len); + goto inst; + } + if (!str_cmp(w1, w1_len, "divide_bypass", ZTERM)) { + if (!str_cmp(w2, w2_len, "NO", ZTERM)) + dev->u.bufio.divide_bypass = BUFIO_DIVIDEBP_N; + else if (!str_cmp(w2, w2_len, "YES", ZTERM)) + dev->u.bufio.divide_bypass = BUFIO_DIVIDEBP_Y; + else return 0; + goto inst; + } + if (!str_cmp(w1, w1_len, "i_inv", ZTERM)) { + if (!str_cmp(w2, w2_len, "NO", ZTERM)) + dev->u.bufio.i_inv = BUFIO_IINV_N; + else if (!str_cmp(w2, w2_len, "YES", ZTERM)) + dev->u.bufio.i_inv = BUFIO_IINV_Y; + else return 0; + goto inst; + } + return 0; +inst: + dev->instantiated = 1; + return 2; +} + +static int printf_BSCAN(FILE* f, struct fpga_model* model, + int y, int x, int config_only) +{ + struct fpga_tile *tile; + struct fpgadev_bscan *cfg; + char pref[256]; + int type_count, i, rc; + + tile = YX_TILE(model, y, x); + type_count = 0; + for (i = 0; i < tile->num_devs; i++) { + if (tile->devs[i].type != DEV_BSCAN) + continue; + if (config_only && !(tile->devs[i].instantiated)) { + type_count++; + continue; + } + snprintf(pref, sizeof(pref), "dev y%02i x%02i BUFGMUX %i", + y, x, type_count++); + if (!config_only) + fprintf(f, "%s\n", pref); + cfg = &tile->devs[i].u.bscan; + if (cfg->jtag_chain) + fprintf(f, "%s jtag_chain %i\n", pref, cfg->jtag_chain); + switch (cfg->jtag_test) { + case BSCAN_JTAG_TEST_N: + fprintf(f, "%s jtag_test NO\n", pref); + break; + case BSCAN_JTAG_TEST_Y: + fprintf(f, "%s jtag_test YES\n", pref); + break; + case 0: break; default: FAIL(EINVAL); + } + } + return 0; +fail: + return rc; +} + +static int read_BSCAN_attr(struct fpga_model *model, struct fpga_device *dev, + const char *w1, int w1_len, const char *w2, int w2_len) +{ + // BSCAN only has 2-word attributes + if (w2_len < 1) return 0; + if (!str_cmp(w1, w1_len, "jtag_chain", ZTERM)) { + dev->u.bscan.jtag_chain = to_i(w2, w2_len); + goto inst; + } + if (!str_cmp(w1, w1_len, "jtag_test", ZTERM)) { + if (!str_cmp(w2, w2_len, "NO", ZTERM)) + dev->u.bscan.jtag_test = BSCAN_JTAG_TEST_N; + else if (!str_cmp(w2, w2_len, "YES", ZTERM)) + dev->u.bscan.jtag_test = BSCAN_JTAG_TEST_Y; + else return 0; + goto inst; + } + return 0; +inst: + dev->instantiated = 1; + return 2; +} + int printf_devices(FILE* f, struct fpga_model* model, int config_only) { int x, y, i, rc; @@ -672,6 +903,24 @@ int printf_devices(FILE* f, struct fpga_model* model, int config_only) if (rc) goto fail; } } + for (x = 0; x < model->x_width; x++) { + for (y = 0; y < model->y_height; y++) { + rc = printf_BUFGMUX(f, model, y, x, config_only); + if (rc) goto fail; + } + } + for (x = 0; x < model->x_width; x++) { + for (y = 0; y < model->y_height; y++) { + rc = printf_BUFIO(f, model, y, x, config_only); + if (rc) goto fail; + } + } + for (x = 0; x < model->x_width; x++) { + for (y = 0; y < model->y_height; y++) { + rc = printf_BSCAN(f, model, y, x, config_only); + if (rc) goto fail; + } + } for (x = 0; x < model->x_width; x++) { for (y = 0; y < model->y_height; y++) { tile = YX_TILE(model, y, x); @@ -679,7 +928,10 @@ int printf_devices(FILE* f, struct fpga_model* model, int config_only) if (config_only && !(tile->devs[i].instantiated)) continue; if (tile->devs[i].type == DEV_LOGIC - || tile->devs[i].type == DEV_IOB) + || tile->devs[i].type == DEV_IOB + || tile->devs[i].type == DEV_BUFGMUX + || tile->devs[i].type == DEV_BUFIO + || tile->devs[i].type == DEV_BSCAN) continue; // handled earlier fprintf(f, "dev y%02i x%02i %s\n", y, x, fdev_type2str(tile->devs[i].type)); @@ -825,13 +1077,6 @@ fail: return rc; } -static enum fpgadev_type to_type(const char* s, int len) -{ - if (!str_cmp(s, len, "IOB", 3)) return DEV_IOB; - if (!str_cmp(s, len, "LOGIC", 5)) return DEV_LOGIC; - return DEV_NONE; -} - static int coord(const char* s, int start, int* end, int* y, int* x) { int y_beg, y_end, x_beg, x_end, rc; @@ -986,14 +1231,17 @@ static void read_dev_line(struct fpga_model* model, const char* line, int start) if (type_end == type_beg || idx_end == idx_beg || !all_digits(&line[idx_beg], idx_end-idx_beg)) { - fprintf(stderr, "error %i: %s", __LINE__, line); + HERE(); return; } - dev_type = to_type(&line[type_beg], type_end-type_beg); + dev_type = fdev_str2type(&line[type_beg], type_end-type_beg); dev_type_idx = to_i(&line[idx_beg], idx_end-idx_beg); dev_idx = fpga_dev_idx(model, y_coord, x_coord, dev_type, dev_type_idx); if (dev_idx == NO_DEV) { - fprintf(stderr, "error %i: %s", __LINE__, line); + fprintf(stderr, "%s:%i y%02i x%02i dev_type %i " + "dev_type_idx %i dev_idx %i\n", + __FILE__, __LINE__, y_coord, x_coord, dev_type, + dev_type_idx, dev_idx); return; } dev_ptr = FPGA_DEV(model, y_coord, x_coord, dev_idx); @@ -1016,6 +1264,24 @@ static void read_dev_line(struct fpga_model* model, const char* line, int start) &line[second_beg], second_end-second_beg); break; + case DEV_BUFGMUX: + words_consumed = read_BUFGMUX_attr(model, dev_ptr, + &line[next_beg], next_end-next_beg, + &line[second_beg], + second_end-second_beg); + break; + case DEV_BUFIO: + words_consumed = read_BUFIO_attr(model, dev_ptr, + &line[next_beg], next_end-next_beg, + &line[second_beg], + second_end-second_beg); + break; + case DEV_BSCAN: + words_consumed = read_BSCAN_attr(model, dev_ptr, + &line[next_beg], next_end-next_beg, + &line[second_beg], + second_end-second_beg); + break; default: fprintf(stderr, "error %i: %s", __LINE__, line); return; diff --git a/libs/model.h b/libs/model.h index 295f42e..7152fcf 100644 --- a/libs/model.h +++ b/libs/model.h @@ -343,7 +343,7 @@ typedef int dev_type_idx_t; #define FPGA_DEV(model, y, x, dev_idx) (&YX_TILE(model, y, x)->devs[dev_idx]) // -// logic device +// DEV_LOGIC // // M and L device is always at type index 0, X device @@ -444,7 +444,7 @@ struct fpgadev_logic }; // -// iob device +// DEV_IOB // enum { IOBM = 1, IOBS }; @@ -493,6 +493,47 @@ struct fpgadev_iob int out_term; }; +// +// DEV_BUFGMUX +// + +enum { BUFG_CLK_ASYNC = 1, BUFG_CLK_SYNC }; +enum { BUFG_DISATTR_LOW = 1, BUFG_DISATTR_HIGH }; +enum { BUFG_SINV_N = 1, BUFG_SINV_Y }; + +struct fpgadev_bufgmux +{ + int clk; + int disable_attr; + int s_inv; +}; + +// +// DEV_BUFIO +// + +enum { BUFIO_DIVIDEBP_N = 1, BUFIO_DIVIDEBP_Y }; +enum { BUFIO_IINV_N = 1, BUFIO_IINV_Y }; + +struct fpgadev_bufio +{ + int divide; + int divide_bypass; + int i_inv; +}; + +// +// DEV_BSCAN +// + +enum { BSCAN_JTAG_TEST_N = 1, BSCAN_JTAG_TEST_Y }; + +struct fpgadev_bscan +{ + int jtag_chain; // 1-4 + int jtag_test; +}; + // // fpga_device // @@ -526,6 +567,9 @@ struct fpga_device union { struct fpgadev_logic logic; struct fpgadev_iob iob; + struct fpgadev_bufgmux bufgmux; + struct fpgadev_bufio bufio; + struct fpgadev_bscan bscan; } u; };