2720 lines
79 KiB
C
2720 lines
79 KiB
C
//
|
|
// 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 "model.h"
|
|
#include "bit.h"
|
|
#include "control.h"
|
|
|
|
static uint8_t* get_first_minor(struct fpga_bits* bits, int row, int major)
|
|
{
|
|
int i, num_frames;
|
|
|
|
if (row < 0) { HERE(); return 0; }
|
|
num_frames = 0;
|
|
for (i = 0; i < major; i++)
|
|
num_frames += get_major_minors(XC6SLX9, i);
|
|
return &bits->d[(row*FRAMES_PER_ROW + num_frames)*FRAME_SIZE];
|
|
}
|
|
|
|
static int get_bit(struct fpga_bits* bits,
|
|
int row, int major, int minor, int bit_i)
|
|
{
|
|
return frame_get_bit(get_first_minor(bits, row, major)
|
|
+ minor*FRAME_SIZE, bit_i);
|
|
}
|
|
|
|
static void set_bit(struct fpga_bits* bits,
|
|
int row, int major, int minor, int bit_i)
|
|
{
|
|
return frame_set_bit(get_first_minor(bits, row, major)
|
|
+ minor*FRAME_SIZE, bit_i);
|
|
}
|
|
|
|
static void clear_bit(struct fpga_bits* bits,
|
|
int row, int major, int minor, int bit_i)
|
|
{
|
|
return frame_clear_bit(get_first_minor(bits, row, major)
|
|
+ minor*FRAME_SIZE, bit_i);
|
|
}
|
|
|
|
struct bit_pos
|
|
{
|
|
int row;
|
|
int major;
|
|
int minor;
|
|
int bit_i;
|
|
};
|
|
|
|
static int get_bitp(struct fpga_bits* bits, struct bit_pos* pos)
|
|
{
|
|
return get_bit(bits, pos->row, pos->major, pos->minor, pos->bit_i);
|
|
}
|
|
|
|
static void set_bitp(struct fpga_bits* bits, struct bit_pos* pos)
|
|
{
|
|
set_bit(bits, pos->row, pos->major, pos->minor, pos->bit_i);
|
|
}
|
|
|
|
static void clear_bitp(struct fpga_bits* bits, struct bit_pos* pos)
|
|
{
|
|
clear_bit(bits, pos->row, pos->major, pos->minor, pos->bit_i);
|
|
}
|
|
|
|
static struct bit_pos s_default_bits[] = {
|
|
{ 0, 0, 3, 66 },
|
|
{ 0, 1, 23, 1034 },
|
|
{ 0, 1, 23, 1035 },
|
|
{ 0, 1, 23, 1039 },
|
|
{ 2, 0, 3, 66 }};
|
|
|
|
struct sw_yxpos
|
|
{
|
|
int y;
|
|
int x;
|
|
swidx_t idx;
|
|
};
|
|
|
|
#define MAX_YX_SWITCHES 1024
|
|
|
|
struct extract_state
|
|
{
|
|
struct fpga_model* model;
|
|
struct fpga_bits* bits;
|
|
// yx switches are fully extracted ones pointing into the
|
|
// model, stored here for later processing into nets.
|
|
int num_yx_pos;
|
|
struct sw_yxpos yx_pos[MAX_YX_SWITCHES]; // needs to be dynamically alloced...
|
|
};
|
|
|
|
static int find_es_switch(struct extract_state* es, int y, int x, swidx_t sw)
|
|
{
|
|
int i;
|
|
|
|
RC_CHECK(es->model);
|
|
if (sw == NO_SWITCH) { HERE(); return 0; }
|
|
for (i = 0; i < es->num_yx_pos; i++) {
|
|
if (es->yx_pos[i].y == y
|
|
&& es->yx_pos[i].x == x
|
|
&& es->yx_pos[i].idx == sw)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int write_type2(struct fpga_bits* bits, struct fpga_model* model)
|
|
{
|
|
int i, y, x, type_idx, part_idx, dev_idx, first_iob, rc;
|
|
struct fpga_device* dev;
|
|
uint64_t u64;
|
|
const char* name;
|
|
|
|
RC_CHECK(model);
|
|
first_iob = 0;
|
|
for (i = 0; (name = fpga_enum_iob(model, i, &y, &x, &type_idx)); i++) {
|
|
dev_idx = fpga_dev_idx(model, y, x, DEV_IOB, type_idx);
|
|
if (dev_idx == NO_DEV) FAIL(EINVAL);
|
|
dev = FPGA_DEV(model, y, x, dev_idx);
|
|
if (!dev->instantiated)
|
|
continue;
|
|
|
|
part_idx = find_iob_sitename(XC6SLX9, name);
|
|
if (part_idx == -1) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
|
|
if (!first_iob) {
|
|
first_iob = 1;
|
|
// todo: is this right on the other sides?
|
|
set_bit(bits, /*row*/ 0, get_rightside_major(XC6SLX9),
|
|
/*minor*/ 22, 64*15+XC6_HCLK_BITS+4);
|
|
}
|
|
|
|
if (dev->u.iob.istandard[0]) {
|
|
if (!dev->u.iob.I_mux
|
|
|| !dev->u.iob.bypass_mux
|
|
|| dev->u.iob.ostandard[0])
|
|
HERE();
|
|
|
|
u64 = XC6_IOB_INPUT | XC6_IOB_INSTANTIATED;
|
|
|
|
if (dev->u.iob.I_mux == IMUX_I_B)
|
|
u64 |= XC6_IOB_IMUX_I_B;
|
|
|
|
if (!strcmp(dev->u.iob.istandard, IO_LVCMOS33)
|
|
|| !strcmp(dev->u.iob.istandard, IO_LVCMOS25)
|
|
|| !strcmp(dev->u.iob.istandard, IO_LVTTL))
|
|
u64 |= XC6_IOB_INPUT_LVCMOS33_25_LVTTL;
|
|
else if (!strcmp(dev->u.iob.istandard, IO_LVCMOS18)
|
|
|| !strcmp(dev->u.iob.istandard, IO_LVCMOS15)
|
|
|| !strcmp(dev->u.iob.istandard, IO_LVCMOS12))
|
|
u64 |= XC6_IOB_INPUT_LVCMOS18_15_12;
|
|
else if (!strcmp(dev->u.iob.istandard, IO_LVCMOS18_JEDEC)
|
|
|| !strcmp(dev->u.iob.istandard, IO_LVCMOS15_JEDEC)
|
|
|| !strcmp(dev->u.iob.istandard, IO_LVCMOS12_JEDEC))
|
|
u64 |= XC6_IOB_INPUT_LVCMOS18_15_12_JEDEC;
|
|
else if (!strcmp(dev->u.iob.istandard, IO_SSTL2_I))
|
|
u64 |= XC6_IOB_INPUT_SSTL2_I;
|
|
else
|
|
HERE();
|
|
|
|
frame_set_u64(&bits->d[IOB_DATA_START
|
|
+ part_idx*IOB_ENTRY_LEN], u64);
|
|
} else if (dev->u.iob.ostandard[0]) {
|
|
if (!dev->u.iob.drive_strength
|
|
|| !dev->u.iob.slew
|
|
|| !dev->u.iob.suspend
|
|
|| dev->u.iob.istandard[0])
|
|
HERE();
|
|
|
|
u64 = XC6_IOB_INSTANTIATED;
|
|
// for now we always turn on O_PINW even if no net
|
|
// is connected to the pinw
|
|
u64 |= XC6_IOB_O_PINW;
|
|
if (!strcmp(dev->u.iob.ostandard, IO_LVTTL)) {
|
|
switch (dev->u.iob.drive_strength) {
|
|
case 2: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_2; break;
|
|
case 4: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_4; break;
|
|
case 6: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_6; break;
|
|
case 8: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_8; break;
|
|
case 12: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_12; break;
|
|
case 16: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_16; break;
|
|
case 24: u64 |= XC6_IOB_OUTPUT_LVTTL_DRIVE_24; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
} else if (!strcmp(dev->u.iob.ostandard, IO_LVCMOS33)) {
|
|
switch (dev->u.iob.drive_strength) {
|
|
case 2: u64 |= XC6_IOB_OUTPUT_LVCMOS33_25_DRIVE_2; break;
|
|
case 4: u64 |= XC6_IOB_OUTPUT_LVCMOS33_DRIVE_4; break;
|
|
case 6: u64 |= XC6_IOB_OUTPUT_LVCMOS33_DRIVE_6; break;
|
|
case 8: u64 |= XC6_IOB_OUTPUT_LVCMOS33_DRIVE_8; break;
|
|
case 12: u64 |= XC6_IOB_OUTPUT_LVCMOS33_DRIVE_12; break;
|
|
case 16: u64 |= XC6_IOB_OUTPUT_LVCMOS33_DRIVE_16; break;
|
|
case 24: u64 |= XC6_IOB_OUTPUT_LVCMOS33_DRIVE_24; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
} else if (!strcmp(dev->u.iob.ostandard, IO_LVCMOS25)) {
|
|
switch (dev->u.iob.drive_strength) {
|
|
case 2: u64 |= XC6_IOB_OUTPUT_LVCMOS33_25_DRIVE_2; break;
|
|
case 4: u64 |= XC6_IOB_OUTPUT_LVCMOS25_DRIVE_4; break;
|
|
case 6: u64 |= XC6_IOB_OUTPUT_LVCMOS25_DRIVE_6; break;
|
|
case 8: u64 |= XC6_IOB_OUTPUT_LVCMOS25_DRIVE_8; break;
|
|
case 12: u64 |= XC6_IOB_OUTPUT_LVCMOS25_DRIVE_12; break;
|
|
case 16: u64 |= XC6_IOB_OUTPUT_LVCMOS25_DRIVE_16; break;
|
|
case 24: u64 |= XC6_IOB_OUTPUT_LVCMOS25_DRIVE_24; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
} else if (!strcmp(dev->u.iob.ostandard, IO_LVCMOS18)
|
|
|| !strcmp(dev->u.iob.ostandard, IO_LVCMOS18_JEDEC)) {
|
|
switch (dev->u.iob.drive_strength) {
|
|
case 2: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_2; break;
|
|
case 4: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_4; break;
|
|
case 6: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_6; break;
|
|
case 8: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_8; break;
|
|
case 12: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_12; break;
|
|
case 16: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_16; break;
|
|
case 24: u64 |= XC6_IOB_OUTPUT_LVCMOS18_DRIVE_24; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
} else if (!strcmp(dev->u.iob.ostandard, IO_LVCMOS15)
|
|
|| !strcmp(dev->u.iob.ostandard, IO_LVCMOS15_JEDEC)) {
|
|
switch (dev->u.iob.drive_strength) {
|
|
case 2: u64 |= XC6_IOB_OUTPUT_LVCMOS15_DRIVE_2; break;
|
|
case 4: u64 |= XC6_IOB_OUTPUT_LVCMOS15_DRIVE_4; break;
|
|
case 6: u64 |= XC6_IOB_OUTPUT_LVCMOS15_DRIVE_6; break;
|
|
case 8: u64 |= XC6_IOB_OUTPUT_LVCMOS15_DRIVE_8; break;
|
|
case 12: u64 |= XC6_IOB_OUTPUT_LVCMOS15_DRIVE_12; break;
|
|
case 16: u64 |= XC6_IOB_OUTPUT_LVCMOS15_DRIVE_16; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
} else if (!strcmp(dev->u.iob.ostandard, IO_LVCMOS12)
|
|
|| !strcmp(dev->u.iob.ostandard, IO_LVCMOS12_JEDEC)) {
|
|
switch (dev->u.iob.drive_strength) {
|
|
case 2: u64 |= XC6_IOB_OUTPUT_LVCMOS12_DRIVE_2; break;
|
|
case 4: u64 |= XC6_IOB_OUTPUT_LVCMOS12_DRIVE_4; break;
|
|
case 6: u64 |= XC6_IOB_OUTPUT_LVCMOS12_DRIVE_6; break;
|
|
case 8: u64 |= XC6_IOB_OUTPUT_LVCMOS12_DRIVE_8; break;
|
|
case 12: u64 |= XC6_IOB_OUTPUT_LVCMOS12_DRIVE_12; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
} else FAIL(EINVAL);
|
|
switch (dev->u.iob.slew) {
|
|
case SLEW_SLOW: u64 |= XC6_IOB_SLEW_SLOW; break;
|
|
case SLEW_FAST: u64 |= XC6_IOB_SLEW_FAST; break;
|
|
case SLEW_QUIETIO: u64 |= XC6_IOB_SLEW_QUIETIO; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
switch (dev->u.iob.suspend) {
|
|
case SUSP_LAST_VAL: u64 |= XC6_IOB_SUSP_LAST_VAL; break;
|
|
case SUSP_3STATE: u64 |= XC6_IOB_SUSP_3STATE; break;
|
|
case SUSP_3STATE_PULLUP: u64 |= XC6_IOB_SUSP_3STATE_PULLUP; break;
|
|
case SUSP_3STATE_PULLDOWN: u64 |= XC6_IOB_SUSP_3STATE_PULLDOWN; break;
|
|
case SUSP_3STATE_KEEPER: u64 |= XC6_IOB_SUSP_3STATE_KEEPER; break;
|
|
case SUSP_3STATE_OCT_ON: u64 |= XC6_IOB_SUSP_3STATE_OCT_ON; break;
|
|
default: FAIL(EINVAL);
|
|
}
|
|
|
|
frame_set_u64(&bits->d[IOB_DATA_START
|
|
+ part_idx*IOB_ENTRY_LEN], u64);
|
|
} else HERE();
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int extract_iobs(struct extract_state* es)
|
|
{
|
|
int i, num_iobs, iob_y, iob_x, iob_idx, dev_idx, first_iob, rc;
|
|
uint64_t u64;
|
|
const char* iob_sitename;
|
|
struct fpga_device* dev;
|
|
struct fpgadev_iob cfg;
|
|
|
|
RC_CHECK(es->model);
|
|
num_iobs = get_num_iobs(XC6SLX9);
|
|
first_iob = 0;
|
|
for (i = 0; i < num_iobs; i++) {
|
|
u64 = frame_get_u64(&es->bits->d[
|
|
IOB_DATA_START + i*IOB_ENTRY_LEN]);
|
|
if (!u64) continue;
|
|
|
|
iob_sitename = get_iob_sitename(XC6SLX9, i);
|
|
if (!iob_sitename) {
|
|
// The space for 6 IOBs on all four sides
|
|
// (6*8 = 48 bytes, *4=192 bytes) is used
|
|
// for clocks etc, so we ignore them here.
|
|
continue;
|
|
}
|
|
rc = fpga_find_iob(es->model, iob_sitename, &iob_y, &iob_x, &iob_idx);
|
|
if (rc) FAIL(rc);
|
|
dev_idx = fpga_dev_idx(es->model, iob_y, iob_x, DEV_IOB, iob_idx);
|
|
if (dev_idx == NO_DEV) FAIL(EINVAL);
|
|
dev = FPGA_DEV(es->model, iob_y, iob_x, dev_idx);
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
|
|
if (!first_iob) {
|
|
first_iob = 1;
|
|
// todo: is this right on the other sides?
|
|
if (!get_bit(es->bits, /*row*/ 0, get_rightside_major(XC6SLX9),
|
|
/*minor*/ 22, 64*15+XC6_HCLK_BITS+4))
|
|
HERE();
|
|
clear_bit(es->bits, /*row*/ 0, get_rightside_major(XC6SLX9),
|
|
/*minor*/ 22, 64*15+XC6_HCLK_BITS+4);
|
|
}
|
|
if (u64 & XC6_IOB_INSTANTIATED)
|
|
u64 &= ~XC6_IOB_INSTANTIATED;
|
|
else
|
|
HERE();
|
|
|
|
switch (u64 & XC6_IOB_MASK_IO) {
|
|
case XC6_IOB_INPUT:
|
|
cfg.bypass_mux = BYPASS_MUX_I;
|
|
|
|
if (u64 & XC6_IOB_IMUX_I_B) {
|
|
cfg.I_mux = IMUX_I_B;
|
|
u64 &= ~XC6_IOB_IMUX_I_B;
|
|
} else
|
|
cfg.I_mux = IMUX_I;
|
|
|
|
switch (u64 & XC6_IOB_MASK_IN_TYPE) {
|
|
case XC6_IOB_INPUT_LVCMOS33_25_LVTTL:
|
|
u64 &= ~XC6_IOB_MASK_IN_TYPE;
|
|
strcpy(cfg.istandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_INPUT_LVCMOS18_15_12:
|
|
u64 &= ~XC6_IOB_MASK_IN_TYPE;
|
|
strcpy(cfg.istandard, IO_LVCMOS12);
|
|
break;
|
|
case XC6_IOB_INPUT_LVCMOS18_15_12_JEDEC:
|
|
u64 &= ~XC6_IOB_MASK_IN_TYPE;
|
|
strcpy(cfg.istandard, IO_LVCMOS12_JEDEC);
|
|
break;
|
|
case XC6_IOB_INPUT_SSTL2_I:
|
|
u64 &= ~XC6_IOB_MASK_IN_TYPE;
|
|
strcpy(cfg.istandard, IO_SSTL2_I);
|
|
break;
|
|
default: HERE(); break;
|
|
}
|
|
break;
|
|
|
|
case XC6_IOB_OUTPUT_LVCMOS33_25_DRIVE_2:
|
|
cfg.drive_strength = 2;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS33_DRIVE_4:
|
|
cfg.drive_strength = 4;
|
|
strcpy(cfg.ostandard, IO_LVCMOS33);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS33_DRIVE_6:
|
|
cfg.drive_strength = 6;
|
|
strcpy(cfg.ostandard, IO_LVCMOS33);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS33_DRIVE_8:
|
|
cfg.drive_strength = 8;
|
|
strcpy(cfg.ostandard, IO_LVCMOS33);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS33_DRIVE_12:
|
|
cfg.drive_strength = 12;
|
|
strcpy(cfg.ostandard, IO_LVCMOS33);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS33_DRIVE_16:
|
|
cfg.drive_strength = 16;
|
|
strcpy(cfg.ostandard, IO_LVCMOS33);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS33_DRIVE_24:
|
|
cfg.drive_strength = 24;
|
|
strcpy(cfg.ostandard, IO_LVCMOS33);
|
|
break;
|
|
|
|
case XC6_IOB_OUTPUT_LVCMOS25_DRIVE_4:
|
|
cfg.drive_strength = 4;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS25_DRIVE_6:
|
|
cfg.drive_strength = 6;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS25_DRIVE_8:
|
|
cfg.drive_strength = 8;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS25_DRIVE_12:
|
|
cfg.drive_strength = 12;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS25_DRIVE_16:
|
|
cfg.drive_strength = 16;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS25_DRIVE_24:
|
|
cfg.drive_strength = 24;
|
|
strcpy(cfg.ostandard, IO_LVCMOS25);
|
|
break;
|
|
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_2:
|
|
cfg.drive_strength = 2;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_4:
|
|
cfg.drive_strength = 4;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_6:
|
|
cfg.drive_strength = 6;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_8:
|
|
cfg.drive_strength = 8;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_12:
|
|
cfg.drive_strength = 12;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_16:
|
|
cfg.drive_strength = 16;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVTTL_DRIVE_24:
|
|
cfg.drive_strength = 24;
|
|
strcpy(cfg.ostandard, IO_LVTTL);
|
|
break;
|
|
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_2:
|
|
cfg.drive_strength = 2;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_4:
|
|
cfg.drive_strength = 4;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_6:
|
|
cfg.drive_strength = 6;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_8:
|
|
cfg.drive_strength = 8;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_12:
|
|
cfg.drive_strength = 12;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_16:
|
|
cfg.drive_strength = 16;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS18_DRIVE_24:
|
|
cfg.drive_strength = 24;
|
|
strcpy(cfg.ostandard, IO_LVCMOS18);
|
|
break;
|
|
|
|
case XC6_IOB_OUTPUT_LVCMOS15_DRIVE_2:
|
|
cfg.drive_strength = 2;
|
|
strcpy(cfg.ostandard, IO_LVCMOS15);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS15_DRIVE_4:
|
|
cfg.drive_strength = 4;
|
|
strcpy(cfg.ostandard, IO_LVCMOS15);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS15_DRIVE_6:
|
|
cfg.drive_strength = 6;
|
|
strcpy(cfg.ostandard, IO_LVCMOS15);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS15_DRIVE_8:
|
|
cfg.drive_strength = 8;
|
|
strcpy(cfg.ostandard, IO_LVCMOS15);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS15_DRIVE_12:
|
|
cfg.drive_strength = 12;
|
|
strcpy(cfg.ostandard, IO_LVCMOS15);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS15_DRIVE_16:
|
|
cfg.drive_strength = 16;
|
|
strcpy(cfg.ostandard, IO_LVCMOS15);
|
|
break;
|
|
|
|
case XC6_IOB_OUTPUT_LVCMOS12_DRIVE_2:
|
|
cfg.drive_strength = 2;
|
|
strcpy(cfg.ostandard, IO_LVCMOS12);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS12_DRIVE_4:
|
|
cfg.drive_strength = 4;
|
|
strcpy(cfg.ostandard, IO_LVCMOS12);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS12_DRIVE_6:
|
|
cfg.drive_strength = 6;
|
|
strcpy(cfg.ostandard, IO_LVCMOS12);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS12_DRIVE_8:
|
|
cfg.drive_strength = 8;
|
|
strcpy(cfg.ostandard, IO_LVCMOS12);
|
|
break;
|
|
case XC6_IOB_OUTPUT_LVCMOS12_DRIVE_12:
|
|
cfg.drive_strength = 12;
|
|
strcpy(cfg.ostandard, IO_LVCMOS12);
|
|
break;
|
|
|
|
default: HERE(); break;
|
|
}
|
|
if (cfg.istandard[0] || cfg.ostandard[0])
|
|
u64 &= ~XC6_IOB_MASK_IO;
|
|
if (cfg.ostandard[0]) {
|
|
cfg.O_used = 1;
|
|
u64 &= ~XC6_IOB_O_PINW;
|
|
|
|
if (!strcmp(cfg.ostandard, IO_LVCMOS12)
|
|
|| !strcmp(cfg.ostandard, IO_LVCMOS15)
|
|
|| !strcmp(cfg.ostandard, IO_LVCMOS18)
|
|
|| !strcmp(cfg.ostandard, IO_LVCMOS25)
|
|
|| !strcmp(cfg.ostandard, IO_LVCMOS33)
|
|
|| !strcmp(cfg.ostandard, IO_LVTTL)) {
|
|
switch (u64 & XC6_IOB_MASK_SLEW) {
|
|
case XC6_IOB_SLEW_SLOW:
|
|
u64 &= ~XC6_IOB_MASK_SLEW;
|
|
cfg.slew = SLEW_SLOW;
|
|
break;
|
|
case XC6_IOB_SLEW_FAST:
|
|
u64 &= ~XC6_IOB_MASK_SLEW;
|
|
cfg.slew = SLEW_FAST;
|
|
break;
|
|
case XC6_IOB_SLEW_QUIETIO:
|
|
u64 &= ~XC6_IOB_MASK_SLEW;
|
|
cfg.slew = SLEW_QUIETIO;
|
|
break;
|
|
default: HERE();
|
|
}
|
|
}
|
|
switch (u64 & XC6_IOB_MASK_SUSPEND) {
|
|
case XC6_IOB_SUSP_3STATE:
|
|
u64 &= ~XC6_IOB_MASK_SUSPEND;
|
|
cfg.suspend = SUSP_3STATE;
|
|
break;
|
|
case XC6_IOB_SUSP_3STATE_OCT_ON:
|
|
u64 &= ~XC6_IOB_MASK_SUSPEND;
|
|
cfg.suspend = SUSP_3STATE_OCT_ON;
|
|
break;
|
|
case XC6_IOB_SUSP_3STATE_KEEPER:
|
|
u64 &= ~XC6_IOB_MASK_SUSPEND;
|
|
cfg.suspend = SUSP_3STATE_KEEPER;
|
|
break;
|
|
case XC6_IOB_SUSP_3STATE_PULLUP:
|
|
u64 &= ~XC6_IOB_MASK_SUSPEND;
|
|
cfg.suspend = SUSP_3STATE_PULLUP;
|
|
break;
|
|
case XC6_IOB_SUSP_3STATE_PULLDOWN:
|
|
u64 &= ~XC6_IOB_MASK_SUSPEND;
|
|
cfg.suspend = SUSP_3STATE_PULLDOWN;
|
|
break;
|
|
case XC6_IOB_SUSP_LAST_VAL:
|
|
u64 &= ~XC6_IOB_MASK_SUSPEND;
|
|
cfg.suspend = SUSP_LAST_VAL;
|
|
break;
|
|
default: HERE();
|
|
}
|
|
}
|
|
if (!u64) {
|
|
frame_set_u64(&es->bits->d[IOB_DATA_START
|
|
+ i*IOB_ENTRY_LEN], 0);
|
|
dev->instantiated = 1;
|
|
dev->u.iob = cfg;
|
|
} else HERE();
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int extract_type2(struct extract_state* es)
|
|
{
|
|
int i, bits_off;
|
|
uint16_t u16;
|
|
|
|
RC_CHECK(es->model);
|
|
extract_iobs(es);
|
|
for (i = 0; i < es->model->pkg->num_gclk_pins; i++) {
|
|
if (!es->model->pkg->gclk_pin[i])
|
|
continue;
|
|
bits_off = IOB_DATA_START
|
|
+ es->model->pkg->gclk_type2_o[i]*XC6_WORD_BYTES
|
|
+ XC6_TYPE2_GCLK_REG_SW/XC6_WORD_BITS;
|
|
u16 = frame_get_u16(&es->bits->d[bits_off]);
|
|
if (!u16)
|
|
continue;
|
|
if (u16 & (1<<(XC6_TYPE2_GCLK_REG_SW%XC6_WORD_BITS))) {
|
|
int iob_y, iob_x, iob_idx;
|
|
struct fpga_device *iob_dev;
|
|
struct switch_to_yx_l2 switch_to_yx_l2;
|
|
struct switch_to_rel switch_to_rel;
|
|
|
|
//
|
|
// find and enable reg-switch for gclk_pin[i]
|
|
// the writing equivalent is in write_inner_term_sw()
|
|
//
|
|
|
|
fpga_find_iob(es->model, es->model->pkg->gclk_pin[i],
|
|
&iob_y, &iob_x, &iob_idx);
|
|
RC_CHECK(es->model);
|
|
iob_dev = fdev_p(es->model, iob_y, iob_x, DEV_IOB, iob_idx);
|
|
RC_ASSERT(es->model, iob_dev);
|
|
|
|
switch_to_yx_l2.l1.yx_req = YX_X_CENTER_CMTPLL | YX_Y_CENTER;
|
|
switch_to_yx_l2.l1.flags = SWTO_YX_CLOSEST;
|
|
switch_to_yx_l2.l1.model = es->model;
|
|
switch_to_yx_l2.l1.y = iob_y;
|
|
switch_to_yx_l2.l1.x = iob_x;
|
|
switch_to_yx_l2.l1.start_switch = iob_dev->pinw[IOB_OUT_I];
|
|
switch_to_yx_l2.l1.from_to = SW_FROM;
|
|
fpga_switch_to_yx_l2(&switch_to_yx_l2);
|
|
RC_CHECK(es->model);
|
|
if (!switch_to_yx_l2.l1.set.len)
|
|
{ HERE(); continue; }
|
|
|
|
switch_to_rel.model = es->model;
|
|
switch_to_rel.start_y = switch_to_yx_l2.l1.dest_y;
|
|
switch_to_rel.start_x = switch_to_yx_l2.l1.dest_x;
|
|
switch_to_rel.start_switch = switch_to_yx_l2.l1.dest_connpt;
|
|
switch_to_rel.from_to = SW_FROM;
|
|
switch_to_rel.flags = SWTO_REL_WEAK_TARGET;
|
|
switch_to_rel.rel_y = es->model->center_y - switch_to_rel.start_y;
|
|
switch_to_rel.rel_x = es->model->center_x - switch_to_rel.start_x;
|
|
switch_to_rel.target_connpt = STRIDX_NO_ENTRY;
|
|
fpga_switch_to_rel(&switch_to_rel);
|
|
RC_CHECK(es->model);
|
|
if (!switch_to_rel.set.len)
|
|
{ HERE(); continue; }
|
|
if (switch_to_rel.set.len != 1) HERE();
|
|
|
|
RC_ASSERT(es->model, es->num_yx_pos < MAX_YX_SWITCHES);
|
|
es->yx_pos[es->num_yx_pos].y = switch_to_rel.start_y;
|
|
es->yx_pos[es->num_yx_pos].x = switch_to_rel.start_x;
|
|
es->yx_pos[es->num_yx_pos].idx = switch_to_rel.set.sw[0];
|
|
es->num_yx_pos++;
|
|
|
|
u16 &= ~(1<<(XC6_TYPE2_GCLK_REG_SW%XC6_WORD_BITS));
|
|
}
|
|
if (u16) HERE();
|
|
frame_set_u16(&es->bits->d[bits_off], u16);
|
|
}
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int lut2str(uint64_t lut, int lut_pos, int lut5_used,
|
|
char *lut6_buf, char** lut6_p, char *lut5_buf, char** lut5_p)
|
|
{
|
|
int lut_map[64], rc;
|
|
uint64_t lut_mapped;
|
|
const char* str;
|
|
|
|
if (lut5_used) {
|
|
xc6_lut_bitmap(lut_pos, &lut_map, 32);
|
|
lut_mapped = map_bits(lut, 64, lut_map);
|
|
|
|
// lut6
|
|
str = bool_bits2str(ULL_HIGH32(lut_mapped), 32);
|
|
if (!str) FAIL(EINVAL);
|
|
snprintf(lut6_buf, MAX_LUT_LEN, "(A6+~A6)*(%s)", str);
|
|
*lut6_p = lut6_buf;
|
|
|
|
// lut5
|
|
str = bool_bits2str(ULL_LOW32(lut_mapped), 32);
|
|
if (!str) FAIL(EINVAL);
|
|
strcpy(lut5_buf, str);
|
|
*lut5_p = lut5_buf;
|
|
} else {
|
|
xc6_lut_bitmap(lut_pos, &lut_map, 64);
|
|
lut_mapped = map_bits(lut, 64, lut_map);
|
|
str = bool_bits2str(lut_mapped, 64);
|
|
if (!str) FAIL(EINVAL);
|
|
strcpy(lut6_buf, str);
|
|
*lut6_p = lut6_buf;
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int extract_logic(struct extract_state* es)
|
|
{
|
|
int row, row_pos, x, y, i, byte_off, last_minor, lut5_used, rc;
|
|
int latch_ml, latch_x, l_col, lut;
|
|
struct fpgadev_logic cfg_ml, cfg_x;
|
|
uint64_t lut_X[4], lut_ML[4]; // LUT_A-LUT_D
|
|
uint64_t mi20, mi23_M, mi2526;
|
|
uint8_t* u8_p;
|
|
char lut6_ml[NUM_LUTS][MAX_LUT_LEN];
|
|
char lut5_ml[NUM_LUTS][MAX_LUT_LEN];
|
|
char lut6_x[NUM_LUTS][MAX_LUT_LEN];
|
|
char lut5_x[NUM_LUTS][MAX_LUT_LEN];
|
|
struct fpga_device* dev_ml;
|
|
|
|
RC_CHECK(es->model);
|
|
for (x = LEFT_SIDE_WIDTH; x < es->model->x_width-RIGHT_SIDE_WIDTH; x++) {
|
|
if (!is_atx(X_FABRIC_LOGIC_COL|X_CENTER_LOGIC_COL, es->model, x))
|
|
continue;
|
|
for (y = TOP_IO_TILES; y < es->model->y_height - BOT_IO_TILES; y++) {
|
|
if (!has_device(es->model, y, x, DEV_LOGIC))
|
|
continue;
|
|
row = which_row(y, es->model);
|
|
row_pos = pos_in_row(y, es->model);
|
|
if (row == -1 || row_pos == -1 || row_pos == 8) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
if (row_pos > 8) row_pos--;
|
|
u8_p = get_first_minor(es->bits, row, es->model->x_major[x]);
|
|
byte_off = row_pos * 8;
|
|
if (row_pos >= 8) byte_off += XC6_HCLK_BYTES;
|
|
|
|
//
|
|
// Step 1:
|
|
//
|
|
// read all configuration bits for the 2 logic devices
|
|
// into local variables
|
|
//
|
|
|
|
mi20 = frame_get_u64(u8_p + 20*FRAME_SIZE + byte_off) & XC6_MI20_LOGIC_MASK;
|
|
if (has_device_type(es->model, y, x, DEV_LOGIC, LOGIC_M)) {
|
|
mi23_M = frame_get_u64(u8_p + 23*FRAME_SIZE + byte_off);
|
|
mi2526 = frame_get_u64(u8_p + 26*FRAME_SIZE + byte_off);
|
|
lut_ML[LUT_A] = frame_get_lut64(u8_p + 24*FRAME_SIZE, row_pos*2+1);
|
|
lut_ML[LUT_B] = frame_get_lut64(u8_p + 21*FRAME_SIZE, row_pos*2+1);
|
|
lut_ML[LUT_C] = frame_get_lut64(u8_p + 24*FRAME_SIZE, row_pos*2);
|
|
lut_ML[LUT_D] = frame_get_lut64(u8_p + 21*FRAME_SIZE, row_pos*2);
|
|
lut_X[LUT_A] = frame_get_lut64(u8_p + 27*FRAME_SIZE, row_pos*2+1);
|
|
lut_X[LUT_B] = frame_get_lut64(u8_p + 29*FRAME_SIZE, row_pos*2+1);
|
|
lut_X[LUT_C] = frame_get_lut64(u8_p + 27*FRAME_SIZE, row_pos*2);
|
|
lut_X[LUT_D] = frame_get_lut64(u8_p + 29*FRAME_SIZE, row_pos*2);
|
|
l_col = 0;
|
|
} else if (has_device_type(es->model, y, x, DEV_LOGIC, LOGIC_L)) {
|
|
mi23_M = 0;
|
|
mi2526 = frame_get_u64(u8_p + 25*FRAME_SIZE + byte_off);
|
|
lut_ML[LUT_A] = frame_get_lut64(u8_p + 23*FRAME_SIZE, row_pos*2+1);
|
|
lut_ML[LUT_B] = frame_get_lut64(u8_p + 21*FRAME_SIZE, row_pos*2+1);
|
|
lut_ML[LUT_C] = frame_get_lut64(u8_p + 23*FRAME_SIZE, row_pos*2);
|
|
lut_ML[LUT_D] = frame_get_lut64(u8_p + 21*FRAME_SIZE, row_pos*2);
|
|
lut_X[LUT_A] = frame_get_lut64(u8_p + 26*FRAME_SIZE, row_pos*2+1);
|
|
lut_X[LUT_B] = frame_get_lut64(u8_p + 28*FRAME_SIZE, row_pos*2+1);
|
|
lut_X[LUT_C] = frame_get_lut64(u8_p + 26*FRAME_SIZE, row_pos*2);
|
|
lut_X[LUT_D] = frame_get_lut64(u8_p + 28*FRAME_SIZE, row_pos*2);
|
|
l_col = 1;
|
|
} else {
|
|
HERE();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Step 2:
|
|
//
|
|
// If all is zero, assume the devices are not instantiated.
|
|
// todo: we could check pin connectivity and infer 0-bit
|
|
// configured devices
|
|
//
|
|
|
|
if (!mi20 && !mi23_M && !mi2526
|
|
&& !lut_X[0] && !lut_X[1] && !lut_X[2] && !lut_X[3]
|
|
&& !lut_ML[0] && !lut_ML[1] && !lut_ML[2] && !lut_ML[3])
|
|
continue;
|
|
|
|
//
|
|
// Step 3:
|
|
//
|
|
// Parse all bits from minors 20 and 25/26 into more
|
|
// easily usable cfg_ml and cfg_x structures.
|
|
//
|
|
|
|
memset(&cfg_ml, 0, sizeof(cfg_ml));
|
|
memset(&cfg_x, 0, sizeof(cfg_x));
|
|
latch_ml = 0;
|
|
latch_x = 0;
|
|
|
|
// minor20
|
|
if (mi20 & (1ULL<<XC6_ML_D5_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_D].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_ML_D5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_X_D5_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_D].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_X_D5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_ML_C5_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_C].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_ML_C5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_X_C_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_C].ff_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_X_C_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_X_C5_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_C].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_X_C5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_ML_B5_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_B].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_ML_B5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_X_B5_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_B].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_X_B5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_M_A_FFSRINIT_1)) {
|
|
if (l_col) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
cfg_ml.a2d[LUT_A].ff_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_M_A_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_X_A5_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_A].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_X_A5_FFSRINIT_1);
|
|
}
|
|
if (mi20 & (1ULL<<XC6_ML_A5_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_A].ff5_srinit = FF_SRINIT1;
|
|
mi20 &= ~(1ULL<<XC6_ML_A5_FFSRINIT_1);
|
|
}
|
|
|
|
// minor 25/26
|
|
if (mi2526 & (1ULL<<XC6_ML_D_CY0_O5)) {
|
|
cfg_ml.a2d[LUT_D].cy0 = CY0_O5;
|
|
mi2526 &= ~(1ULL<<XC6_ML_D_CY0_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_D_OUTMUX_O5)) {
|
|
cfg_x.a2d[LUT_D].out_mux = MUX_O5;
|
|
mi2526 &= ~(1ULL<<XC6_X_D_OUTMUX_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_C_OUTMUX_O5)) {
|
|
cfg_x.a2d[LUT_C].out_mux = MUX_O5;
|
|
mi2526 &= ~(1ULL<<XC6_X_C_OUTMUX_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_D_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_D].ff_srinit = 1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_D_FFSRINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_C_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_C].ff_srinit = 1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_C_FFSRINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_D_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_D].ff_srinit = 1;
|
|
mi2526 &= ~(1ULL<<XC6_X_D_FFSRINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_C_CY0_O5)) {
|
|
cfg_ml.a2d[LUT_C].cy0 = CY0_O5;
|
|
mi2526 &= ~(1ULL<<XC6_ML_C_CY0_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_B_OUTMUX_O5)) {
|
|
cfg_x.a2d[LUT_B].out_mux = MUX_O5;
|
|
mi2526 &= ~(1ULL<<XC6_X_B_OUTMUX_O5);
|
|
}
|
|
if (mi2526 & XC6_ML_D_OUTMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_D_OUTMUX_MASK) >> XC6_ML_D_OUTMUX_O) {
|
|
case XC6_ML_D_OUTMUX_O6:
|
|
cfg_ml.a2d[LUT_D].out_mux = MUX_O6;
|
|
break;
|
|
case XC6_ML_D_OUTMUX_XOR:
|
|
cfg_ml.a2d[LUT_D].out_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_D_OUTMUX_O5:
|
|
cfg_ml.a2d[LUT_D].out_mux = MUX_O5;
|
|
break;
|
|
case XC6_ML_D_OUTMUX_CY:
|
|
cfg_ml.a2d[LUT_D].out_mux = MUX_CY;
|
|
break;
|
|
case XC6_ML_D_OUTMUX_5Q:
|
|
cfg_ml.a2d[LUT_D].out_mux = MUX_5Q;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_D_OUTMUX_MASK;
|
|
}
|
|
if (mi2526 & XC6_ML_D_FFMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_D_FFMUX_MASK) >> XC6_ML_D_FFMUX_O) {
|
|
case XC6_ML_D_FFMUX_O5:
|
|
cfg_ml.a2d[LUT_D].ff_mux = MUX_O5;
|
|
break;
|
|
case XC6_ML_D_FFMUX_X:
|
|
cfg_ml.a2d[LUT_D].ff_mux = MUX_X;
|
|
break;
|
|
case XC6_ML_D_FFMUX_XOR:
|
|
cfg_ml.a2d[LUT_D].ff_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_D_FFMUX_CY:
|
|
cfg_ml.a2d[LUT_D].ff_mux = MUX_CY;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_D_FFMUX_MASK;
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_CLK_B)) {
|
|
cfg_x.clk_inv = CLKINV_B;
|
|
mi2526 &= ~(1ULL<<XC6_X_CLK_B);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_ALL_LATCH)) {
|
|
latch_ml = 1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_ALL_LATCH);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_SR_USED)) {
|
|
cfg_ml.sr_used = 1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_SR_USED);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_SYNC)) {
|
|
cfg_ml.sync_attr = SYNCATTR_SYNC;
|
|
mi2526 &= ~(1ULL<<XC6_ML_SYNC);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_CE_USED)) {
|
|
cfg_ml.ce_used = 1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_CE_USED);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_D_FFMUX_X)) {
|
|
cfg_x.a2d[LUT_D].ff_mux = MUX_X;
|
|
mi2526 &= ~(1ULL<<XC6_X_D_FFMUX_X);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_C_FFMUX_X)) {
|
|
cfg_x.a2d[LUT_C].ff_mux = MUX_X;
|
|
mi2526 &= ~(1ULL<<XC6_X_C_FFMUX_X);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_CE_USED)) {
|
|
cfg_x.ce_used = 1;
|
|
mi2526 &= ~(1ULL<<XC6_X_CE_USED);
|
|
}
|
|
if (mi2526 & XC6_ML_C_OUTMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_C_OUTMUX_MASK) >> XC6_ML_C_OUTMUX_O) {
|
|
case XC6_ML_C_OUTMUX_XOR:
|
|
cfg_ml.a2d[LUT_C].out_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_C_OUTMUX_O6:
|
|
cfg_ml.a2d[LUT_C].out_mux = MUX_O6;
|
|
break;
|
|
case XC6_ML_C_OUTMUX_5Q:
|
|
cfg_ml.a2d[LUT_C].out_mux = MUX_5Q;
|
|
break;
|
|
case XC6_ML_C_OUTMUX_CY:
|
|
cfg_ml.a2d[LUT_C].out_mux = MUX_CY;
|
|
break;
|
|
case XC6_ML_C_OUTMUX_O5:
|
|
cfg_ml.a2d[LUT_C].out_mux = MUX_O5;
|
|
break;
|
|
case XC6_ML_C_OUTMUX_F7:
|
|
cfg_ml.a2d[LUT_C].out_mux = MUX_F7;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_C_OUTMUX_MASK;
|
|
}
|
|
if (mi2526 & XC6_ML_C_FFMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_C_FFMUX_MASK) >> XC6_ML_C_FFMUX_O) {
|
|
case XC6_ML_C_FFMUX_O5:
|
|
cfg_ml.a2d[LUT_C].ff_mux = MUX_O5;
|
|
break;
|
|
case XC6_ML_C_FFMUX_X:
|
|
cfg_ml.a2d[LUT_C].ff_mux = MUX_X;
|
|
break;
|
|
case XC6_ML_C_FFMUX_F7:
|
|
cfg_ml.a2d[LUT_C].ff_mux = MUX_F7;
|
|
break;
|
|
case XC6_ML_C_FFMUX_XOR:
|
|
cfg_ml.a2d[LUT_C].ff_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_C_FFMUX_CY:
|
|
cfg_ml.a2d[LUT_C].ff_mux = MUX_CY;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_C_FFMUX_MASK;
|
|
}
|
|
if (mi2526 & XC6_ML_B_OUTMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_B_OUTMUX_MASK) >> XC6_ML_B_OUTMUX_O) {
|
|
case XC6_ML_B_OUTMUX_5Q:
|
|
cfg_ml.a2d[LUT_B].out_mux = MUX_5Q;
|
|
break;
|
|
case XC6_ML_B_OUTMUX_F8:
|
|
cfg_ml.a2d[LUT_B].out_mux = MUX_F8;
|
|
break;
|
|
case XC6_ML_B_OUTMUX_XOR:
|
|
cfg_ml.a2d[LUT_B].out_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_B_OUTMUX_CY:
|
|
cfg_ml.a2d[LUT_B].out_mux = MUX_CY;
|
|
break;
|
|
case XC6_ML_B_OUTMUX_O6:
|
|
cfg_ml.a2d[LUT_B].out_mux = MUX_O6;
|
|
break;
|
|
case XC6_ML_B_OUTMUX_O5:
|
|
cfg_ml.a2d[LUT_B].out_mux = MUX_O5;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_B_OUTMUX_MASK;
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_B_FFMUX_X)) {
|
|
cfg_x.a2d[LUT_B].ff_mux = MUX_X;
|
|
mi2526 &= ~(1ULL<<XC6_X_B_FFMUX_X);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_A_FFMUX_X)) {
|
|
cfg_x.a2d[LUT_A].ff_mux = MUX_X;
|
|
mi2526 &= ~(1ULL<<XC6_X_A_FFMUX_X);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_B_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_B].ff_srinit = FF_SRINIT1;
|
|
mi2526 &= ~(1ULL<<XC6_X_B_FFSRINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_A_OUTMUX_O5)) {
|
|
cfg_x.a2d[LUT_A].out_mux = MUX_O5;
|
|
mi2526 &= ~(1ULL<<XC6_X_A_OUTMUX_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_SR_USED)) {
|
|
cfg_x.sr_used = 1;
|
|
mi2526 &= ~(1ULL<<XC6_X_SR_USED);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_SYNC)) {
|
|
cfg_x.sync_attr = SYNCATTR_SYNC;
|
|
mi2526 &= ~(1ULL<<XC6_X_SYNC);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_ALL_LATCH)) {
|
|
latch_x = 1;
|
|
mi2526 &= ~(1ULL<<XC6_X_ALL_LATCH);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_CLK_B)) {
|
|
cfg_ml.clk_inv = CLKINV_B;
|
|
mi2526 &= ~(1ULL<<XC6_ML_CLK_B);
|
|
}
|
|
if (mi2526 & XC6_ML_B_FFMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_B_FFMUX_MASK) >> XC6_ML_B_FFMUX_O) {
|
|
case XC6_ML_B_FFMUX_XOR:
|
|
cfg_ml.a2d[LUT_B].ff_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_B_FFMUX_O5:
|
|
cfg_ml.a2d[LUT_B].ff_mux = MUX_O5;
|
|
break;
|
|
case XC6_ML_B_FFMUX_CY:
|
|
cfg_ml.a2d[LUT_B].ff_mux = MUX_CY;
|
|
break;
|
|
case XC6_ML_B_FFMUX_X:
|
|
cfg_ml.a2d[LUT_B].ff_mux = MUX_X;
|
|
break;
|
|
case XC6_ML_B_FFMUX_F8:
|
|
cfg_ml.a2d[LUT_B].ff_mux = MUX_F8;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_B_FFMUX_MASK;
|
|
}
|
|
if (mi2526 & XC6_ML_A_FFMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_A_FFMUX_MASK) >> XC6_ML_A_FFMUX_O) {
|
|
case XC6_ML_A_FFMUX_XOR:
|
|
cfg_ml.a2d[LUT_A].ff_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_A_FFMUX_X:
|
|
cfg_ml.a2d[LUT_A].ff_mux = MUX_X;
|
|
break;
|
|
case XC6_ML_A_FFMUX_O5:
|
|
cfg_ml.a2d[LUT_A].ff_mux = MUX_O5;
|
|
break;
|
|
case XC6_ML_A_FFMUX_CY:
|
|
cfg_ml.a2d[LUT_A].ff_mux = MUX_CY;
|
|
break;
|
|
case XC6_ML_A_FFMUX_F7:
|
|
cfg_ml.a2d[LUT_A].ff_mux = MUX_F7;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_A_FFMUX_MASK;
|
|
}
|
|
if (mi2526 & XC6_ML_A_OUTMUX_MASK) {
|
|
switch ((mi2526 & XC6_ML_A_OUTMUX_MASK) >> XC6_ML_A_OUTMUX_O) {
|
|
case XC6_ML_A_OUTMUX_5Q:
|
|
cfg_ml.a2d[LUT_A].out_mux = MUX_5Q;
|
|
break;
|
|
case XC6_ML_A_OUTMUX_F7:
|
|
cfg_ml.a2d[LUT_A].out_mux = MUX_F7;
|
|
break;
|
|
case XC6_ML_A_OUTMUX_XOR:
|
|
cfg_ml.a2d[LUT_A].out_mux = MUX_XOR;
|
|
break;
|
|
case XC6_ML_A_OUTMUX_CY:
|
|
cfg_ml.a2d[LUT_A].out_mux = MUX_CY;
|
|
break;
|
|
case XC6_ML_A_OUTMUX_O6:
|
|
cfg_ml.a2d[LUT_A].out_mux = MUX_O6;
|
|
break;
|
|
case XC6_ML_A_OUTMUX_O5:
|
|
cfg_ml.a2d[LUT_A].out_mux = MUX_O5;
|
|
break;
|
|
default: HERE(); continue;
|
|
}
|
|
mi2526 &= ~XC6_ML_A_OUTMUX_MASK;
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_B_CY0_O5)) {
|
|
cfg_ml.a2d[LUT_B].cy0 = CY0_O5;
|
|
mi2526 &= ~(1ULL<<XC6_ML_B_CY0_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_PRECYINIT_AX)) {
|
|
cfg_ml.precyinit = PRECYINIT_AX;
|
|
mi2526 &= ~(1ULL<<XC6_ML_PRECYINIT_AX);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_X_A_FFSRINIT_1)) {
|
|
cfg_x.a2d[LUT_A].ff_srinit = FF_SRINIT1;
|
|
mi2526 &= ~(1ULL<<XC6_X_A_FFSRINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_PRECYINIT_1)) {
|
|
cfg_ml.precyinit = PRECYINIT_1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_PRECYINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_B_FFSRINIT_1)) {
|
|
cfg_ml.a2d[LUT_B].ff_srinit = FF_SRINIT1;
|
|
mi2526 &= ~(1ULL<<XC6_ML_B_FFSRINIT_1);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_ML_A_CY0_O5)) {
|
|
cfg_ml.a2d[LUT_A].cy0 = CY0_O5;
|
|
mi2526 &= ~(1ULL<<XC6_ML_A_CY0_O5);
|
|
}
|
|
if (mi2526 & (1ULL<<XC6_L_A_FFSRINIT_1)) {
|
|
if (!l_col) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
cfg_ml.a2d[LUT_A].ff_srinit = FF_SRINIT1;
|
|
mi2526 &= ~(1ULL<<XC6_L_A_FFSRINIT_1);
|
|
}
|
|
|
|
//
|
|
// Step 4:
|
|
//
|
|
// If any bits remain in the configuration, do not
|
|
// instantiate the logic devices.
|
|
//
|
|
|
|
if (mi20) {
|
|
fprintf(stderr, "#E %s:%i y%i x%i l%i "
|
|
"mi20 0x%016lX\n",
|
|
__FILE__, __LINE__, y, x, l_col, mi20);
|
|
continue;
|
|
}
|
|
if (mi23_M) {
|
|
fprintf(stderr, "#E %s:%i y%i x%i l%i "
|
|
"mi23_M 0x%016lX\n",
|
|
__FILE__, __LINE__, y, x, l_col, mi23_M);
|
|
continue;
|
|
}
|
|
if (mi2526) {
|
|
fprintf(stderr, "#E %s:%i y%i x%i l%i "
|
|
"mi2526 0x%016lX\n",
|
|
__FILE__, __LINE__, y, x, l_col, mi2526);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Step 5:
|
|
//
|
|
// Do device-global sanity checking pre-LUT
|
|
//
|
|
|
|
// cfg_ml.cout_used
|
|
dev_ml = fdev_p(es->model, y, x, DEV_LOGIC, DEV_LOG_M_OR_L);
|
|
if (!dev_ml) FAIL(EINVAL);
|
|
if (find_es_switch(es, y, x, fpga_switch_first(
|
|
es->model, y, x, dev_ml->pinw[LO_COUT], SW_FROM)))
|
|
cfg_ml.cout_used = 1;
|
|
|
|
// todo: if srinit=1, the matching ff/latch must be on
|
|
// todo: handle all_latch
|
|
// todo: precyinit=0 has no bits
|
|
// todo: cy0=X has no bits, check connectivity
|
|
// todo: the x-device ffmux must not be left as MUX_X unless the ff is really in use
|
|
|
|
//
|
|
// Step 6:
|
|
//
|
|
// Determine lut5_usage and read the luts.
|
|
//
|
|
|
|
// todo: in ML, if srinit=0 cannot decide between ff_mux=O6 or
|
|
// direct-out, need to check pin connectivity, is_pin_connected()
|
|
// todo: in ML devs, ffmux=O6 has no bits set
|
|
// todo: 5Q-ff in X devices
|
|
|
|
// ML-A
|
|
if (lut_ML[LUT_A]
|
|
|| !all_zero(&cfg_ml.a2d[LUT_A], sizeof(cfg_ml.a2d[LUT_A]))) {
|
|
if (lut_ML[LUT_A]
|
|
&& cfg_ml.a2d[LUT_A].out_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_A].out_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_A].out_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_A].out_mux != MUX_F7
|
|
&& cfg_ml.a2d[LUT_A].ff_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_A].ff_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_A].ff_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_A].ff_mux != MUX_F7)
|
|
cfg_ml.a2d[LUT_A].out_used = 1;
|
|
|
|
lut5_used = (cfg_ml.a2d[LUT_A].ff_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_A].out_mux == MUX_5Q
|
|
|| cfg_ml.a2d[LUT_A].out_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_A].cy0 == CY0_O5);
|
|
rc = lut2str(lut_ML[LUT_A], l_col
|
|
? XC6_LMAP_XL_L_A : XC6_LMAP_XM_M_A,
|
|
lut5_used,
|
|
lut6_ml[LUT_A], &cfg_ml.a2d[LUT_A].lut6,
|
|
lut5_ml[LUT_A], &cfg_ml.a2d[LUT_A].lut5);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
// ML-B
|
|
if (lut_ML[LUT_B]
|
|
|| !all_zero(&cfg_ml.a2d[LUT_B], sizeof(cfg_ml.a2d[LUT_B]))) {
|
|
if (lut_ML[LUT_B]
|
|
&& cfg_ml.a2d[LUT_B].out_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_B].out_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_B].out_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_B].out_mux != MUX_F7
|
|
&& cfg_ml.a2d[LUT_B].ff_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_B].ff_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_B].ff_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_B].ff_mux != MUX_F7)
|
|
cfg_ml.a2d[LUT_B].out_used = 1;
|
|
|
|
lut5_used = (cfg_ml.a2d[LUT_B].ff_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_B].out_mux == MUX_5Q
|
|
|| cfg_ml.a2d[LUT_B].out_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_B].cy0 == CY0_O5);
|
|
rc = lut2str(lut_ML[LUT_B], l_col
|
|
? XC6_LMAP_XL_L_B : XC6_LMAP_XM_M_B,
|
|
lut5_used,
|
|
lut6_ml[LUT_B], &cfg_ml.a2d[LUT_B].lut6,
|
|
lut5_ml[LUT_B], &cfg_ml.a2d[LUT_B].lut5);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
// ML-C
|
|
if (lut_ML[LUT_C]
|
|
|| !all_zero(&cfg_ml.a2d[LUT_C], sizeof(cfg_ml.a2d[LUT_C]))) {
|
|
if (lut_ML[LUT_C]
|
|
&& cfg_ml.a2d[LUT_C].out_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_C].out_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_C].out_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_C].out_mux != MUX_F7
|
|
&& cfg_ml.a2d[LUT_C].ff_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_C].ff_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_C].ff_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_C].ff_mux != MUX_F7)
|
|
cfg_ml.a2d[LUT_C].out_used = 1;
|
|
|
|
lut5_used = (cfg_ml.a2d[LUT_C].ff_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_C].out_mux == MUX_5Q
|
|
|| cfg_ml.a2d[LUT_C].out_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_C].cy0 == CY0_O5);
|
|
rc = lut2str(lut_ML[LUT_C], l_col
|
|
? XC6_LMAP_XL_L_C : XC6_LMAP_XM_M_C,
|
|
lut5_used,
|
|
lut6_ml[LUT_C], &cfg_ml.a2d[LUT_C].lut6,
|
|
lut5_ml[LUT_C], &cfg_ml.a2d[LUT_C].lut5);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
// ML-D
|
|
if (lut_ML[LUT_D]
|
|
|| !all_zero(&cfg_ml.a2d[LUT_D], sizeof(cfg_ml.a2d[LUT_D]))) {
|
|
if (lut_ML[LUT_D]
|
|
&& cfg_ml.a2d[LUT_D].out_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_D].out_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_D].out_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_D].out_mux != MUX_F7
|
|
&& cfg_ml.a2d[LUT_D].ff_mux != MUX_O6
|
|
&& cfg_ml.a2d[LUT_D].ff_mux != MUX_XOR
|
|
&& cfg_ml.a2d[LUT_D].ff_mux != MUX_CY
|
|
&& cfg_ml.a2d[LUT_D].ff_mux != MUX_F7)
|
|
cfg_ml.a2d[LUT_D].out_used = 1;
|
|
|
|
lut5_used = (cfg_ml.a2d[LUT_D].ff_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_D].out_mux == MUX_5Q
|
|
|| cfg_ml.a2d[LUT_D].out_mux == MUX_O5
|
|
|| cfg_ml.a2d[LUT_D].cy0 == CY0_O5);
|
|
rc = lut2str(lut_ML[LUT_D], l_col
|
|
? XC6_LMAP_XL_L_D : XC6_LMAP_XM_M_D,
|
|
lut5_used,
|
|
lut6_ml[LUT_D], &cfg_ml.a2d[LUT_D].lut6,
|
|
lut5_ml[LUT_D], &cfg_ml.a2d[LUT_D].lut5);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
// X-A
|
|
if (lut_X[LUT_A]
|
|
|| !all_zero(&cfg_x.a2d[LUT_A], sizeof(cfg_x.a2d[LUT_A]))) {
|
|
if (lut_X[LUT_A]
|
|
&& cfg_x.a2d[LUT_A].ff_mux != MUX_O6)
|
|
cfg_x.a2d[LUT_A].out_used = 1;
|
|
lut5_used = cfg_x.a2d[LUT_A].out_mux != 0;
|
|
rc = lut2str(lut_X[LUT_A], l_col
|
|
? XC6_LMAP_XL_X_A : XC6_LMAP_XM_X_A,
|
|
lut5_used,
|
|
lut6_x[LUT_A], &cfg_x.a2d[LUT_A].lut6,
|
|
lut5_x[LUT_A], &cfg_x.a2d[LUT_A].lut5);
|
|
if (rc) FAIL(rc);
|
|
|
|
}
|
|
// X-B
|
|
if (lut_X[LUT_B]
|
|
|| !all_zero(&cfg_x.a2d[LUT_B], sizeof(cfg_x.a2d[LUT_B]))) {
|
|
if (lut_X[LUT_B]
|
|
&& cfg_x.a2d[LUT_B].ff_mux != MUX_O6)
|
|
cfg_x.a2d[LUT_B].out_used = 1;
|
|
lut5_used = cfg_x.a2d[LUT_B].out_mux != 0;
|
|
rc = lut2str(lut_X[LUT_B], l_col
|
|
? XC6_LMAP_XL_X_B : XC6_LMAP_XM_X_B,
|
|
lut5_used,
|
|
lut6_x[LUT_B], &cfg_x.a2d[LUT_B].lut6,
|
|
lut5_x[LUT_B], &cfg_x.a2d[LUT_B].lut5);
|
|
if (rc) FAIL(rc);
|
|
|
|
}
|
|
// X-C
|
|
if (lut_X[LUT_C]
|
|
|| !all_zero(&cfg_x.a2d[LUT_C], sizeof(cfg_x.a2d[LUT_C]))) {
|
|
if (lut_X[LUT_C]
|
|
&& cfg_x.a2d[LUT_C].ff_mux != MUX_O6)
|
|
cfg_x.a2d[LUT_C].out_used = 1;
|
|
lut5_used = cfg_x.a2d[LUT_C].out_mux != 0;
|
|
rc = lut2str(lut_X[LUT_C], l_col
|
|
? XC6_LMAP_XL_X_C : XC6_LMAP_XM_X_C,
|
|
lut5_used,
|
|
lut6_x[LUT_C], &cfg_x.a2d[LUT_C].lut6,
|
|
lut5_x[LUT_C], &cfg_x.a2d[LUT_C].lut5);
|
|
if (rc) FAIL(rc);
|
|
|
|
}
|
|
// X-D
|
|
if (lut_X[LUT_D]
|
|
|| !all_zero(&cfg_x.a2d[LUT_D], sizeof(cfg_x.a2d[LUT_D]))) {
|
|
if (lut_X[LUT_D]
|
|
&& cfg_x.a2d[LUT_D].ff_mux != MUX_O6)
|
|
cfg_x.a2d[LUT_D].out_used = 1;
|
|
lut5_used = cfg_x.a2d[LUT_D].out_mux != 0;
|
|
rc = lut2str(lut_X[LUT_D], l_col
|
|
? XC6_LMAP_XL_X_D : XC6_LMAP_XM_X_D,
|
|
lut5_used,
|
|
lut6_x[LUT_D], &cfg_x.a2d[LUT_D].lut6,
|
|
lut5_x[LUT_D], &cfg_x.a2d[LUT_D].lut5);
|
|
if (rc) FAIL(rc);
|
|
|
|
}
|
|
|
|
//
|
|
// Step 7:
|
|
//
|
|
// Do more and final sanity checks before instantiation.
|
|
//
|
|
|
|
// todo: latch cannot be combined with out_mux=5Q or ff5_srinit
|
|
if (latch_ml
|
|
&& !cfg_ml.a2d[LUT_A].ff_mux
|
|
&& !cfg_ml.a2d[LUT_B].ff_mux
|
|
&& !cfg_ml.a2d[LUT_C].ff_mux
|
|
&& !cfg_ml.a2d[LUT_D].ff_mux) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
if (latch_x
|
|
&& !cfg_x.a2d[LUT_A].ff_mux
|
|
&& !cfg_x.a2d[LUT_B].ff_mux
|
|
&& !cfg_x.a2d[LUT_C].ff_mux
|
|
&& !cfg_x.a2d[LUT_D].ff_mux) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
// todo: latch and2l and or2l need to be determined
|
|
// from vcc connectivity, srinit etc.
|
|
for (lut = LUT_A; lut <= LUT_D; lut++) {
|
|
if (cfg_ml.a2d[lut].ff_mux) {
|
|
cfg_ml.a2d[lut].ff = latch_ml ? FF_LATCH : FF_FF;
|
|
if (!cfg_ml.a2d[lut].ff_srinit)
|
|
cfg_ml.a2d[lut].ff_srinit = FF_SRINIT0;
|
|
if (!cfg_ml.clk_inv)
|
|
cfg_ml.clk_inv = CLKINV_CLK;
|
|
if (!cfg_ml.sync_attr)
|
|
cfg_ml.sync_attr = SYNCATTR_ASYNC;
|
|
}
|
|
if (cfg_x.a2d[lut].ff_mux) {
|
|
cfg_x.a2d[lut].ff = latch_x ? FF_LATCH : FF_FF;
|
|
if (!cfg_x.a2d[lut].ff_srinit)
|
|
cfg_x.a2d[lut].ff_srinit = FF_SRINIT0;
|
|
if (!cfg_x.clk_inv)
|
|
cfg_x.clk_inv = CLKINV_CLK;
|
|
if (!cfg_x.sync_attr)
|
|
cfg_x.sync_attr = SYNCATTR_ASYNC;
|
|
}
|
|
// todo: ff5_srinit
|
|
// todo: 5Q ff also needs clock/sync check
|
|
}
|
|
|
|
// Check whether we should default to PRECYINIT_0
|
|
if (!cfg_ml.precyinit
|
|
&& (cfg_ml.a2d[LUT_A].out_mux == MUX_XOR
|
|
|| cfg_ml.a2d[LUT_A].ff_mux == MUX_XOR
|
|
|| cfg_ml.a2d[LUT_A].cy0)) {
|
|
int connpt_dests_o, num_dests, cout_y, cout_x;
|
|
str16_t cout_str;
|
|
|
|
if ((fpga_connpt_find(es->model, y, x,
|
|
dev_ml->pinw[LI_CIN], &connpt_dests_o,
|
|
&num_dests) == NO_CONN)
|
|
|| num_dests != 1) {
|
|
HERE();
|
|
} else {
|
|
fpga_conn_dest(es->model, y, x, connpt_dests_o,
|
|
&cout_y, &cout_x, &cout_str);
|
|
if (find_es_switch(es, cout_y, cout_x,fpga_switch_first(
|
|
es->model, cout_y, cout_x, cout_str, SW_TO)))
|
|
cfg_ml.precyinit = PRECYINIT_0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Step 8:
|
|
//
|
|
// Remove all bits.
|
|
//
|
|
frame_set_u64(u8_p + 20*FRAME_SIZE + byte_off,
|
|
frame_get_u64(u8_p + 20*FRAME_SIZE + byte_off)
|
|
& ~XC6_MI20_LOGIC_MASK);
|
|
last_minor = l_col ? 29 : 30;
|
|
for (i = 21; i <= last_minor; i++)
|
|
frame_set_u64(u8_p + i*FRAME_SIZE + byte_off, 0);
|
|
|
|
//
|
|
// Step 9:
|
|
//
|
|
// Instantiate configuration.
|
|
//
|
|
|
|
if (!all_zero(&cfg_ml, sizeof(cfg_ml))) {
|
|
rc = fdev_logic_setconf(es->model, y, x, DEV_LOG_M_OR_L, &cfg_ml);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
if (!all_zero(&cfg_x, sizeof(cfg_x))) {
|
|
rc = fdev_logic_setconf(es->model, y, x, DEV_LOG_X, &cfg_x);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int bitpos_is_set(struct extract_state *es, int y, int x,
|
|
struct xc6_routing_bitpos *swpos, int *is_set)
|
|
{
|
|
int row_num, row_pos, start_in_frame, two_bits_val, rc;
|
|
|
|
RC_CHECK(es->model);
|
|
*is_set = 0;
|
|
is_in_row(es->model, y, &row_num, &row_pos);
|
|
if (row_num == -1 || row_pos == -1
|
|
|| row_pos == HCLK_POS) FAIL(EINVAL);
|
|
if (row_pos > HCLK_POS)
|
|
start_in_frame = (row_pos-1)*64 + 16;
|
|
else
|
|
start_in_frame = row_pos*64;
|
|
|
|
if (swpos->minor == 20) {
|
|
two_bits_val = ((get_bit(es->bits, row_num, es->model->x_major[x],
|
|
20, start_in_frame + swpos->two_bits_o) != 0) << 1)
|
|
| ((get_bit(es->bits, row_num, es->model->x_major[x],
|
|
20, start_in_frame + swpos->two_bits_o+1) != 0) << 0);
|
|
if (two_bits_val != swpos->two_bits_val)
|
|
return 0;
|
|
|
|
if (!get_bit(es->bits, row_num, es->model->x_major[x], 20,
|
|
start_in_frame + swpos->one_bit_o))
|
|
return 0;
|
|
} else {
|
|
two_bits_val =
|
|
((get_bit(es->bits, row_num, es->model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o/2) != 0) << 1)
|
|
| ((get_bit(es->bits, row_num, es->model->x_major[x], swpos->minor+1,
|
|
start_in_frame + swpos->two_bits_o/2) != 0) << 0);
|
|
if (two_bits_val != swpos->two_bits_val)
|
|
return 0;
|
|
|
|
if (!get_bit(es->bits, row_num, es->model->x_major[x],
|
|
swpos->minor + (swpos->one_bit_o&1),
|
|
start_in_frame + swpos->one_bit_o/2))
|
|
return 0;
|
|
}
|
|
*is_set = 1;
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int bitpos_clear_bits(struct extract_state* es, int y, int x,
|
|
struct xc6_routing_bitpos* swpos)
|
|
{
|
|
int row_num, row_pos, start_in_frame, rc;
|
|
|
|
RC_CHECK(es->model);
|
|
is_in_row(es->model, y, &row_num, &row_pos);
|
|
if (row_num == -1 || row_pos == -1
|
|
|| row_pos == HCLK_POS) FAIL(EINVAL);
|
|
if (row_pos > HCLK_POS)
|
|
start_in_frame = (row_pos-1)*64 + 16;
|
|
else
|
|
start_in_frame = row_pos*64;
|
|
|
|
if (swpos->minor == 20) {
|
|
clear_bit(es->bits, row_num, es->model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o);
|
|
clear_bit(es->bits, row_num, es->model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o+1);
|
|
clear_bit(es->bits, row_num, es->model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->one_bit_o);
|
|
} else {
|
|
clear_bit(es->bits, row_num, es->model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o/2);
|
|
clear_bit(es->bits, row_num, es->model->x_major[x], swpos->minor + 1,
|
|
start_in_frame + swpos->two_bits_o/2);
|
|
clear_bit(es->bits, row_num, es->model->x_major[x], swpos->minor + (swpos->one_bit_o&1),
|
|
start_in_frame + swpos->one_bit_o/2);
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int bitpos_set_bits(struct fpga_bits* bits, struct fpga_model* model,
|
|
int y, int x, struct xc6_routing_bitpos* swpos)
|
|
{
|
|
int row_num, row_pos, start_in_frame, rc;
|
|
|
|
RC_CHECK(model);
|
|
is_in_row(model, y, &row_num, &row_pos);
|
|
if (row_num == -1 || row_pos == -1
|
|
|| row_pos == HCLK_POS) FAIL(EINVAL);
|
|
if (row_pos > HCLK_POS)
|
|
start_in_frame = (row_pos-1)*64 + 16;
|
|
else
|
|
start_in_frame = row_pos*64;
|
|
|
|
if (swpos->minor == 20) {
|
|
if (swpos->two_bits_val & 0x02)
|
|
set_bit(bits, row_num, model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o);
|
|
if (swpos->two_bits_val & 0x01)
|
|
set_bit(bits, row_num, model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o+1);
|
|
set_bit(bits, row_num, model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->one_bit_o);
|
|
} else {
|
|
if (swpos->two_bits_val & 0x02)
|
|
set_bit(bits, row_num, model->x_major[x], swpos->minor,
|
|
start_in_frame + swpos->two_bits_o/2);
|
|
if (swpos->two_bits_val & 0x01)
|
|
set_bit(bits, row_num, model->x_major[x], swpos->minor + 1,
|
|
start_in_frame + swpos->two_bits_o/2);
|
|
set_bit(bits, row_num, model->x_major[x], swpos->minor + (swpos->one_bit_o&1),
|
|
start_in_frame + swpos->one_bit_o/2);
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int extract_routing_switches(struct extract_state* es, int y, int x)
|
|
{
|
|
struct fpga_tile* tile;
|
|
swidx_t sw_idx;
|
|
int i, is_set, rc;
|
|
|
|
RC_CHECK(es->model);
|
|
tile = YX_TILE(es->model, y, x);
|
|
|
|
for (i = 0; i < es->model->num_bitpos; i++) {
|
|
rc = bitpos_is_set(es, y, x, &es->model->sw_bitpos[i], &is_set);
|
|
if (rc) RC_FAIL(es->model, rc);
|
|
if (!is_set) continue;
|
|
|
|
sw_idx = fpga_switch_lookup(es->model, y, x,
|
|
fpga_wire2str_i(es->model, es->model->sw_bitpos[i].from),
|
|
fpga_wire2str_i(es->model, es->model->sw_bitpos[i].to));
|
|
if (sw_idx == NO_SWITCH) RC_FAIL(es->model, EINVAL);
|
|
// todo: es->model->sw_bitpos[i].bidir handling
|
|
|
|
if (tile->switches[sw_idx] & SWITCH_BIDIRECTIONAL)
|
|
HERE();
|
|
if (tile->switches[sw_idx] & SWITCH_USED)
|
|
HERE();
|
|
if (es->num_yx_pos >= MAX_YX_SWITCHES)
|
|
{ RC_FAIL(es->model, ENOTSUP); }
|
|
es->yx_pos[es->num_yx_pos].y = y;
|
|
es->yx_pos[es->num_yx_pos].x = x;
|
|
es->yx_pos[es->num_yx_pos].idx = sw_idx;
|
|
es->num_yx_pos++;
|
|
rc = bitpos_clear_bits(es, y, x, &es->model->sw_bitpos[i]);
|
|
if (rc) RC_FAIL(es->model, rc);
|
|
}
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int extract_logic_switches(struct extract_state* es, int y, int x)
|
|
{
|
|
int row, row_pos, byte_off, minor, rc;
|
|
uint8_t* u8_p;
|
|
|
|
RC_CHECK(es->model);
|
|
row = which_row(y, es->model);
|
|
row_pos = pos_in_row(y, es->model);
|
|
if (row == -1 || row_pos == -1 || row_pos == 8) FAIL(EINVAL);
|
|
if (row_pos > 8) row_pos--;
|
|
u8_p = get_first_minor(es->bits, row, es->model->x_major[x]);
|
|
byte_off = row_pos * 8;
|
|
if (row_pos >= 8) byte_off += XC6_HCLK_BYTES;
|
|
|
|
if (has_device_type(es->model, y, x, DEV_LOGIC, LOGIC_M))
|
|
minor = 26;
|
|
else if (has_device_type(es->model, y, x, DEV_LOGIC, LOGIC_L))
|
|
minor = 25;
|
|
else
|
|
FAIL(EINVAL);
|
|
|
|
if (frame_get_bit(u8_p + minor*FRAME_SIZE, byte_off*8 + XC6_ML_CIN_USED)) {
|
|
struct fpga_device* dev;
|
|
int connpt_dests_o, num_dests, cout_y, cout_x;
|
|
str16_t cout_str;
|
|
swidx_t cout_sw;
|
|
|
|
dev = fdev_p(es->model, y, x, DEV_LOGIC, DEV_LOG_M_OR_L);
|
|
if (!dev) FAIL(EINVAL);
|
|
|
|
if ((fpga_connpt_find(es->model, y, x,
|
|
dev->pinw[LI_CIN], &connpt_dests_o,
|
|
&num_dests) == NO_CONN)
|
|
|| num_dests != 1) {
|
|
HERE();
|
|
} else {
|
|
fpga_conn_dest(es->model, y, x, connpt_dests_o,
|
|
&cout_y, &cout_x, &cout_str);
|
|
cout_sw = fpga_switch_first(es->model, cout_y,
|
|
cout_x, cout_str, SW_TO);
|
|
if (cout_sw == NO_SWITCH) HERE();
|
|
else {
|
|
if (es->num_yx_pos >= MAX_YX_SWITCHES)
|
|
{ FAIL(ENOTSUP); }
|
|
es->yx_pos[es->num_yx_pos].y = cout_y;
|
|
es->yx_pos[es->num_yx_pos].x = cout_x;
|
|
es->yx_pos[es->num_yx_pos].idx = cout_sw;
|
|
es->num_yx_pos++;
|
|
|
|
frame_clear_bit(u8_p + minor*FRAME_SIZE,
|
|
byte_off*8 + XC6_ML_CIN_USED);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
#define MAX_IOLOGIC_SWBLOCK 4
|
|
#define MAX_IOLOGIC_BITS 4
|
|
struct iologic_sw_pos
|
|
{
|
|
const char* to[MAX_IOLOGIC_SWBLOCK];
|
|
const char* from[MAX_IOLOGIC_SWBLOCK];
|
|
int minor[MAX_IOLOGIC_BITS];
|
|
int b64[MAX_IOLOGIC_BITS];
|
|
};
|
|
|
|
// todo: can we assume the maximum range for iologic
|
|
// minors to be 21..29? that would be a total of
|
|
// 9*64=675 bits for ilogic/ologic/iodelay?
|
|
struct iologic_sw_pos s_left_io_swpos[] = { {{0}} };
|
|
struct iologic_sw_pos s_right_io_swpos[] = { {{0}} };
|
|
struct iologic_sw_pos s_top_outer_io_swpos[] = { {{0}} };
|
|
struct iologic_sw_pos s_top_inner_io_swpos[] = { {{0}} };
|
|
|
|
struct iologic_sw_pos s_bot_inner_io_swpos[] =
|
|
{
|
|
// input
|
|
{{"D_ILOGIC_IDATAIN_IODELAY_S"},
|
|
{"BIOI_INNER_IBUF0"},
|
|
{23, 23, -1},
|
|
{28, 29}},
|
|
|
|
{{"D_ILOGIC_SITE"},
|
|
{"D_ILOGIC_IDATAIN_IODELAY"},
|
|
{26, -1},
|
|
{20}},
|
|
|
|
{{"D_ILOGIC_SITE_S"},
|
|
{"D_ILOGIC_IDATAIN_IODELAY_S"},
|
|
{26, -1},
|
|
{23}},
|
|
|
|
{{"DFB_ILOGIC_SITE"},
|
|
{"D_ILOGIC_SITE"},
|
|
{28, -1},
|
|
{63}},
|
|
|
|
{{"DFB_ILOGIC_SITE_S"},
|
|
{"D_ILOGIC_SITE_S"},
|
|
{28, -1},
|
|
{0}},
|
|
|
|
{{"FABRICOUT_ILOGIC_SITE"},
|
|
{"D_ILOGIC_SITE"},
|
|
{29, -1},
|
|
{49}},
|
|
|
|
{{"FABRICOUT_ILOGIC_SITE_S"},
|
|
{"D_ILOGIC_SITE_S"},
|
|
{29, -1},
|
|
{14}},
|
|
|
|
// output
|
|
{{"OQ_OLOGIC_SITE", "BIOI_INNER_O0"},
|
|
{"D1_OLOGIC_SITE", "OQ_OLOGIC_SITE"},
|
|
{26, 27, 28, -1},
|
|
{40, 21, 57}},
|
|
|
|
{{"OQ_OLOGIC_SITE_S", "BIOI_INNER_O1"},
|
|
{"D1_OLOGIC_SITE_S", "OQ_OLOGIC_SITE_S"},
|
|
{26, 27, 28, -1},
|
|
{43, 56, 6}},
|
|
|
|
{{"IOI_LOGICOUT0"}, {"IOI_INTER_LOGICOUT0"}, {-1}},
|
|
{{"IOI_LOGICOUT7"}, {"IOI_INTER_LOGICOUT7"}, {-1}},
|
|
{{"IOI_INTER_LOGICOUT0"}, {"FABRICOUT_ILOGIC_SITE"}, {-1}},
|
|
{{"IOI_INTER_LOGICOUT7"}, {"FABRICOUT_ILOGIC_SITE_S"}, {-1}},
|
|
{{"D_ILOGIC_IDATAIN_IODELAY"}, {"BIOI_INNER_IBUF0"}, {-1}},
|
|
{{"D_ILOGIC_IDATAIN_IODELAY_S"}, {"BIOI_INNER_IBUF1"}, {-1}},
|
|
{{"D1_OLOGIC_SITE"}, {"IOI_LOGICINB31"}, {-1}},
|
|
{{0}}
|
|
};
|
|
|
|
struct iologic_sw_pos s_bot_outer_io_swpos[] =
|
|
{
|
|
// input
|
|
{{"D_ILOGIC_IDATAIN_IODELAY_S"},
|
|
{"BIOI_OUTER_IBUF0"},
|
|
{23, 23, -1},
|
|
{28, 29}},
|
|
|
|
{{"D_ILOGIC_SITE"},
|
|
{"D_ILOGIC_IDATAIN_IODELAY"},
|
|
{26, -1},
|
|
{20}},
|
|
|
|
{{"D_ILOGIC_SITE_S"},
|
|
{"D_ILOGIC_IDATAIN_IODELAY_S"},
|
|
{26, -1},
|
|
{23}},
|
|
|
|
{{"DFB_ILOGIC_SITE"},
|
|
{"D_ILOGIC_SITE"},
|
|
{28, -1},
|
|
{63}},
|
|
|
|
{{"DFB_ILOGIC_SITE_S"},
|
|
{"D_ILOGIC_SITE_S"},
|
|
{28, -1},
|
|
{0}},
|
|
|
|
{{"FABRICOUT_ILOGIC_SITE"},
|
|
{"D_ILOGIC_SITE"},
|
|
{29, -1},
|
|
{49}},
|
|
|
|
{{"FABRICOUT_ILOGIC_SITE_S"},
|
|
{"D_ILOGIC_SITE_S"},
|
|
{29, -1},
|
|
{14}},
|
|
|
|
// output
|
|
{{"OQ_OLOGIC_SITE", "BIOI_OUTER_O0"},
|
|
{"D1_OLOGIC_SITE", "OQ_OLOGIC_SITE"},
|
|
{26, 27, 28, -1},
|
|
{40, 21, 57}},
|
|
|
|
{{"OQ_OLOGIC_SITE_S", "BIOI_OUTER_O1"},
|
|
{"D1_OLOGIC_SITE_S", "OQ_OLOGIC_SITE_S"},
|
|
{26, 27, 28, -1},
|
|
{43, 56, 6}},
|
|
|
|
{{"IOI_LOGICOUT0"}, {"IOI_INTER_LOGICOUT0"}, {-1}},
|
|
{{"IOI_LOGICOUT7"}, {"IOI_INTER_LOGICOUT7"}, {-1}},
|
|
{{"IOI_INTER_LOGICOUT0"}, {"FABRICOUT_ILOGIC_SITE"}, {-1}},
|
|
{{"IOI_INTER_LOGICOUT7"}, {"FABRICOUT_ILOGIC_SITE_S"}, {-1}},
|
|
{{"D_ILOGIC_IDATAIN_IODELAY"}, {"BIOI_INNER_IBUF0"}, {-1}},
|
|
{{"D_ILOGIC_IDATAIN_IODELAY_S"}, {"BIOI_INNER_IBUF1"}, {-1}},
|
|
{{"D1_OLOGIC_SITE"}, {"IOI_LOGICINB31"}, {-1}},
|
|
{{0}}
|
|
};
|
|
|
|
static int add_yx_switch(struct extract_state *es,
|
|
int y, int x, const char *from, const char *to)
|
|
{
|
|
str16_t from_str_i, to_str_i;
|
|
swidx_t sw_idx;
|
|
|
|
RC_CHECK(es->model);
|
|
|
|
from_str_i = strarray_find(&es->model->str, from);
|
|
to_str_i = strarray_find(&es->model->str, to);
|
|
RC_ASSERT(es->model, from_str_i != STRIDX_NO_ENTRY
|
|
&& to_str_i != STRIDX_NO_ENTRY);
|
|
|
|
sw_idx = fpga_switch_lookup(es->model, y, x,
|
|
from_str_i, to_str_i);
|
|
RC_ASSERT(es->model, sw_idx != NO_SWITCH);
|
|
|
|
RC_ASSERT(es->model, es->num_yx_pos < MAX_YX_SWITCHES);
|
|
es->yx_pos[es->num_yx_pos].y = y;
|
|
es->yx_pos[es->num_yx_pos].x = x;
|
|
es->yx_pos[es->num_yx_pos].idx = sw_idx;
|
|
es->num_yx_pos++;
|
|
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int extract_iologic_switches(struct extract_state* es, int y, int x)
|
|
{
|
|
int row_num, row_pos, bit_in_frame, i, j, rc;
|
|
uint8_t* minor0_p;
|
|
struct iologic_sw_pos* sw_pos;
|
|
|
|
RC_CHECK(es->model);
|
|
// From y/x coordinate, determine major, row, bit offset
|
|
// in frame (v64_i) and pointer to first minor.
|
|
is_in_row(es->model, y, &row_num, &row_pos);
|
|
if (row_num == -1 || row_pos == -1
|
|
|| row_pos == HCLK_POS) FAIL(EINVAL);
|
|
if (row_pos > HCLK_POS)
|
|
bit_in_frame = (row_pos-1)*64 + XC6_HCLK_BITS;
|
|
else
|
|
bit_in_frame = row_pos*64;
|
|
minor0_p = get_first_minor(es->bits, row_num, es->model->x_major[x]);
|
|
|
|
if (x < LEFT_SIDE_WIDTH) {
|
|
if (x != LEFT_IO_DEVS) FAIL(EINVAL);
|
|
sw_pos = s_left_io_swpos;
|
|
} else if (x >= es->model->x_width-RIGHT_SIDE_WIDTH) {
|
|
if (x != es->model->x_width - RIGHT_IO_DEVS_O) FAIL(EINVAL);
|
|
sw_pos = s_right_io_swpos;
|
|
} else if (y == TOP_OUTER_IO)
|
|
sw_pos = s_top_outer_io_swpos;
|
|
else if (y == TOP_INNER_IO)
|
|
sw_pos = s_top_inner_io_swpos;
|
|
else if (y == es->model->y_height-BOT_INNER_IO)
|
|
sw_pos = s_bot_inner_io_swpos;
|
|
else if (y == es->model->y_height-BOT_OUTER_IO)
|
|
sw_pos = s_bot_outer_io_swpos;
|
|
else FAIL(EINVAL);
|
|
|
|
// Go through switches
|
|
for (i = 0; sw_pos[i].to[0]; i++) {
|
|
for (j = 0; sw_pos[i].minor[j] != -1; j++) {
|
|
if (!frame_get_bit(&minor0_p[sw_pos[i].minor[j]
|
|
*FRAME_SIZE], bit_in_frame+sw_pos[i].b64[j]))
|
|
break;
|
|
}
|
|
if (!j || sw_pos[i].minor[j] != -1)
|
|
continue;
|
|
for (j = 0; j < MAX_IOLOGIC_SWBLOCK && sw_pos[i].to[j]; j++)
|
|
add_yx_switch(es, y, x, sw_pos[i].from[j], sw_pos[i].to[j]);
|
|
for (j = 0; sw_pos[i].minor[j] != -1; j++)
|
|
frame_clear_bit(&minor0_p[sw_pos[i].minor[j]
|
|
*FRAME_SIZE], bit_in_frame+sw_pos[i].b64[j]);
|
|
}
|
|
// todo: we could implement an all-or-nothing system where
|
|
// the device bits are first copied into an u64 array,
|
|
// and switches rewound by resetting num_yx_pos - unless
|
|
// all bits have been processed...
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int extract_center_switches(struct extract_state *es)
|
|
{
|
|
int center_row, center_major, word, i;
|
|
uint8_t *minor_p;
|
|
|
|
RC_CHECK(es->model);
|
|
center_row = es->model->die->num_rows/2;
|
|
center_major = xc_die_center_major(es->model->die);
|
|
minor_p = get_first_minor(es->bits, center_row,
|
|
center_major) + XC6_CENTER_GCLK_MINOR*FRAME_SIZE;
|
|
word = frame_get_pinword(minor_p + 15*8+XC6_HCLK_BYTES);
|
|
if (word) {
|
|
for (i = 0; i < 16; i++) {
|
|
if (!(word & (1<<i))) continue;
|
|
add_yx_switch(es,
|
|
es->model->center_y, es->model->center_x,
|
|
pf("CLKC_CKLR%i", i), pf("CLKC_GCLK%i", i));
|
|
}
|
|
frame_set_pinword(minor_p + 15*8+XC6_HCLK_BYTES, 0);
|
|
}
|
|
word = frame_get_pinword(minor_p + 15*8+XC6_HCLK_BYTES + XC6_WORD_BYTES);
|
|
if (word) {
|
|
for (i = 0; i < 16; i++) {
|
|
if (!(word & (1<<i))) continue;
|
|
add_yx_switch(es,
|
|
es->model->center_y, es->model->center_x,
|
|
pf("CLKC_CKTB%i", i), pf("CLKC_GCLK%i", i));
|
|
}
|
|
frame_set_pinword(minor_p + 15*8+XC6_HCLK_BYTES + XC6_WORD_BYTES, 0);
|
|
}
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int write_center_sw(struct fpga_bits *bits,
|
|
struct fpga_model *model, int y, int x)
|
|
{
|
|
struct fpga_tile *tile;
|
|
const char *from_str, *to_str;
|
|
int center_row, center_major, word;
|
|
int i, j, gclk_pin_from, gclk_pin_to;
|
|
uint8_t *minor_p;
|
|
|
|
RC_CHECK(model);
|
|
|
|
center_row = model->die->num_rows/2;
|
|
center_major = xc_die_center_major(model->die);
|
|
minor_p = get_first_minor(bits, center_row,
|
|
center_major) + XC6_CENTER_GCLK_MINOR*FRAME_SIZE;
|
|
|
|
tile = YX_TILE(model, y, x);
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
|
|
from_str = fpga_switch_str(model, y, x, i, SW_FROM);
|
|
to_str = fpga_switch_str(model, y, x, i, SW_TO);
|
|
RC_ASSERT(model, from_str && to_str);
|
|
|
|
j = sscanf(from_str, "CLKC_CKLR%i", &gclk_pin_from);
|
|
if (j == 1) {
|
|
j = sscanf(to_str, "CLKC_GCLK%i", &gclk_pin_to);
|
|
RC_ASSERT(model, j == 1 && gclk_pin_to == gclk_pin_from);
|
|
word = frame_get_pinword(minor_p + 15*8+XC6_HCLK_BYTES);
|
|
word |= 1 << gclk_pin_from;
|
|
frame_set_pinword(minor_p + 15*8+XC6_HCLK_BYTES, word);
|
|
continue;
|
|
}
|
|
j = sscanf(from_str, "CLKC_CKTB%i", &gclk_pin_from);
|
|
if (j == 1) {
|
|
j = sscanf(to_str, "CLKC_GCLK%i", &gclk_pin_to);
|
|
RC_ASSERT(model, j == 1 && gclk_pin_to == gclk_pin_from);
|
|
word = frame_get_pinword(minor_p + 15*8+XC6_HCLK_BYTES + XC6_WORD_BYTES);
|
|
word |= 1 << gclk_pin_from;
|
|
frame_set_pinword(minor_p + 15*8+XC6_HCLK_BYTES + XC6_WORD_BYTES, word);
|
|
continue;
|
|
}
|
|
// todo: it's possible that other switches need bits
|
|
// todo: we can manually detect used switches that do not
|
|
// need bits, such as
|
|
// I0_GCLK_SITE0:15 -> O_GCLK_SITE0:15
|
|
// O_GCLK_SITE0:15 -> CLKC_GCLK_MAIN0:15
|
|
// CLKC_GCLK%i -> I0_GCLK_SITE%i
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
static int extract_gclk_center_vert_sw(struct extract_state *es)
|
|
{
|
|
int word, cur_row, cur_minor, cur_pin, i, hclk_y;
|
|
uint8_t *ma0_bits;
|
|
|
|
RC_CHECK(es->model);
|
|
// Switches in the vertical center column that are routing gclk
|
|
// signals to the left and right side of the chip.
|
|
for (cur_row = 0; cur_row < es->model->die->num_rows; cur_row++) {
|
|
hclk_y = row_to_hclk(cur_row, es->model);
|
|
RC_ASSERT(es->model, hclk_y != -1);
|
|
ma0_bits = get_first_minor(es->bits, cur_row, XC6_NULL_MAJOR);
|
|
for (cur_minor = 0; cur_minor <= 2; cur_minor++) {
|
|
// left
|
|
word = frame_get_pinword(ma0_bits + cur_minor*FRAME_SIZE + 8*8+XC6_HCLK_BYTES);
|
|
if (word) {
|
|
for (i = 0; i <= 16/3; i++) { // 0-5
|
|
cur_pin = cur_minor + i*3;
|
|
if (cur_pin > 15) break;
|
|
if (!(word & (1<<cur_pin))) continue;
|
|
add_yx_switch(es, hclk_y, es->model->center_x,
|
|
pf("CLKV_GCLKH_MAIN%i_FOLD", cur_pin),
|
|
pf("CLKV_GCLKH_L%i", cur_pin));
|
|
word &= ~(1<<cur_pin);
|
|
}
|
|
if (word) HERE();
|
|
frame_set_pinword(ma0_bits + cur_minor*FRAME_SIZE + 8*8+XC6_HCLK_BYTES, word);
|
|
}
|
|
// right
|
|
word = frame_get_pinword(ma0_bits + cur_minor*FRAME_SIZE + 8*8+XC6_HCLK_BYTES + 4);
|
|
if (word) {
|
|
for (i = 0; i <= 16/3; i++) { // 0-5
|
|
cur_pin = cur_minor + i*3;
|
|
if (cur_pin > 15) break;
|
|
if (!(word & (1<<cur_pin))) continue;
|
|
add_yx_switch(es, hclk_y, es->model->center_x,
|
|
pf("CLKV_GCLKH_MAIN%i_FOLD", cur_pin),
|
|
pf("CLKV_GCLKH_R%i", cur_pin));
|
|
word &= ~(1<<cur_pin);
|
|
}
|
|
if (word) HERE();
|
|
frame_set_pinword(ma0_bits + cur_minor*FRAME_SIZE + 8*8+XC6_HCLK_BYTES + 4, word);
|
|
}
|
|
}
|
|
}
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int write_gclk_center_vert_sw(struct fpga_bits *bits,
|
|
struct fpga_model *model, int y, int x)
|
|
{
|
|
struct fpga_tile *tile;
|
|
const char *from_str, *to_str;
|
|
int i, j, gclk_pin_from, gclk_pin_to, cur_pin, cur_minor, minor_found, word;
|
|
uint8_t *ma0_bits;
|
|
|
|
RC_CHECK(model);
|
|
ma0_bits = get_first_minor(bits, which_row(y, model), XC6_NULL_MAJOR);
|
|
RC_ASSERT(model, ma0_bits);
|
|
tile = YX_TILE(model, y, x);
|
|
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
|
|
from_str = fpga_switch_str(model, y, x, i, SW_FROM);
|
|
to_str = fpga_switch_str(model, y, x, i, SW_TO);
|
|
RC_ASSERT(model, from_str && to_str);
|
|
|
|
// left
|
|
j = sscanf(from_str, "CLKV_GCLKH_MAIN%i_FOLD", &gclk_pin_from);
|
|
if (j == 1) {
|
|
j = sscanf(to_str, "CLKV_GCLKH_L%i", &gclk_pin_to);
|
|
RC_ASSERT(model, j == 1 && gclk_pin_to == gclk_pin_from);
|
|
|
|
minor_found = -1;
|
|
for (cur_minor = 0; minor_found == -1 && cur_minor <= 2; cur_minor++) {
|
|
for (j = 0; j <= 16/3; j++) { // 0-5
|
|
cur_pin = cur_minor + j*3;
|
|
if (cur_pin > 15) break;
|
|
if (cur_pin == gclk_pin_from) {
|
|
minor_found = cur_minor;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (minor_found != -1) {
|
|
word = frame_get_pinword(ma0_bits + minor_found*FRAME_SIZE + 8*8+XC6_HCLK_BYTES);
|
|
word |= 1 << gclk_pin_from;
|
|
frame_set_pinword(ma0_bits + minor_found*FRAME_SIZE + 8*8+XC6_HCLK_BYTES, word);
|
|
continue;
|
|
}
|
|
HERE(); // fall-through to unsupported
|
|
}
|
|
// right
|
|
j = sscanf(from_str, "CLKV_GCLKH_MAIN%i_FOLD", &gclk_pin_from);
|
|
if (j == 1) {
|
|
j = sscanf(to_str, "CLKV_GCLKH_R%i", &gclk_pin_to);
|
|
RC_ASSERT(model, j == 1 && gclk_pin_to == gclk_pin_from);
|
|
|
|
minor_found = -1;
|
|
for (cur_minor = 0; minor_found == -1 && cur_minor <= 2; cur_minor++) {
|
|
for (j = 0; j <= 16/3; j++) { // 0-5
|
|
cur_pin = cur_minor + j*3;
|
|
if (cur_pin > 15) break;
|
|
if (cur_pin == gclk_pin_from) {
|
|
minor_found = cur_minor;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (minor_found != -1) {
|
|
word = frame_get_pinword(ma0_bits + minor_found*FRAME_SIZE + 8*8+XC6_HCLK_BYTES + 4);
|
|
word |= 1 << gclk_pin_from;
|
|
frame_set_pinword(ma0_bits + minor_found*FRAME_SIZE + 8*8+XC6_HCLK_BYTES + 4, word);
|
|
continue;
|
|
}
|
|
HERE(); // fall-through to unsupported
|
|
}
|
|
// todo: we can manually detect used switches that do not
|
|
// need bits, such as
|
|
// CLKV_GCLKH_L0:15 -> I_BUFH_LEFT_SITE0:15
|
|
// I_BUFH_LEFT_SITE0:15 -> O_BUFH_LEFT_SITE0:15
|
|
// O_BUFH_LEFT_SITE0:15 -> CLKV_BUFH_LEFT_L0:15
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
static int extract_gclk_hclk_updown_sw(struct extract_state *es)
|
|
{
|
|
int word, cur_row, x, gclk_pin, hclk_y;
|
|
uint8_t *mi0_bits;
|
|
|
|
RC_CHECK(es->model);
|
|
// Switches in each horizontal hclk row that are routing
|
|
// gclk signals and and down to the clocked devices.
|
|
|
|
// todo: clk access in left and right IO devs, center devs,
|
|
// bram and macc devs as well as special devs not tested
|
|
// yet (only XM logic devs tested).
|
|
for (cur_row = 0; cur_row < es->model->die->num_rows; cur_row++) {
|
|
hclk_y = row_to_hclk(cur_row, es->model);
|
|
RC_ASSERT(es->model, hclk_y != -1);
|
|
for (x = LEFT_IO_ROUTING; x <= es->model->x_width-RIGHT_IO_ROUTING_O; x++) {
|
|
if (!is_atx(X_ROUTING_COL, es->model, x))
|
|
continue;
|
|
mi0_bits = get_first_minor(es->bits, cur_row, es->model->x_major[x]);
|
|
// each minor (0:15) stores the configuration bits for one gclk
|
|
// pin (in the hclk bytes of the minor)
|
|
for (gclk_pin = 0; gclk_pin <= 15; gclk_pin++) {
|
|
word = frame_get_pinword(mi0_bits + gclk_pin*FRAME_SIZE + XC6_HCLK_POS);
|
|
if (word & (1<<XC6_HCLK_GCLK_UP_PIN)) {
|
|
add_yx_switch(es, hclk_y, x,
|
|
pf("HCLK_GCLK%i_INT", gclk_pin),
|
|
pf("HCLK_GCLK_UP%i", gclk_pin));
|
|
word &= ~(1<<XC6_HCLK_GCLK_UP_PIN);
|
|
}
|
|
if (word & (1<<XC6_HCLK_GCLK_DOWN_PIN)) {
|
|
add_yx_switch(es, hclk_y, x,
|
|
pf("HCLK_GCLK%i_INT", gclk_pin),
|
|
pf("HCLK_GCLK%i", gclk_pin));
|
|
word &= ~(1<<XC6_HCLK_GCLK_DOWN_PIN);
|
|
}
|
|
if (word) HERE();
|
|
frame_set_pinword(mi0_bits + gclk_pin*FRAME_SIZE + XC6_HCLK_POS, word);
|
|
}
|
|
}
|
|
}
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int write_hclk_sw(struct fpga_bits *bits, struct fpga_model *model,
|
|
int y, int x)
|
|
{
|
|
uint8_t *mi0_bits;
|
|
struct fpga_tile *tile;
|
|
const char *from_str, *to_str;
|
|
int i, j, gclk_pin_from, gclk_pin_to, up, word;
|
|
|
|
RC_CHECK(model);
|
|
|
|
mi0_bits = get_first_minor(bits, which_row(y, model), model->x_major[x]);
|
|
RC_ASSERT(model, mi0_bits);
|
|
|
|
tile = YX_TILE(model, y, x);
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
from_str = fpga_switch_str(model, y, x, i, SW_FROM);
|
|
to_str = fpga_switch_str(model, y, x, i, SW_TO);
|
|
|
|
j = sscanf(from_str, "HCLK_GCLK%i_INT", &gclk_pin_from);
|
|
RC_ASSERT(model, j == 1);
|
|
j = sscanf(to_str, "HCLK_GCLK%i", &gclk_pin_to);
|
|
if (j == 1)
|
|
up = 0; // down
|
|
else {
|
|
j = sscanf(to_str, "HCLK_GCLK_UP%i", &gclk_pin_to);
|
|
RC_ASSERT(model, j == 1);
|
|
up = 1;
|
|
}
|
|
RC_ASSERT(model, gclk_pin_from == gclk_pin_to);
|
|
|
|
word = frame_get_pinword(mi0_bits + gclk_pin_from*FRAME_SIZE + XC6_HCLK_POS);
|
|
word |= 1 << (up ? XC6_HCLK_GCLK_UP_PIN : XC6_HCLK_GCLK_DOWN_PIN);
|
|
frame_set_pinword(mi0_bits + gclk_pin_from*FRAME_SIZE + XC6_HCLK_POS, word);
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
static int extract_switches(struct extract_state *es)
|
|
{
|
|
int x, y;
|
|
|
|
RC_CHECK(es->model);
|
|
for (x = 0; x < es->model->x_width; x++) {
|
|
for (y = 0; y < es->model->y_height; y++) {
|
|
// routing switches
|
|
if (is_atx(X_ROUTING_COL, es->model, x)
|
|
&& y >= TOP_IO_TILES
|
|
&& y < es->model->y_height-BOT_IO_TILES
|
|
&& !is_aty(Y_ROW_HORIZ_AXSYMM|Y_CHIP_HORIZ_REGS,
|
|
es->model, y)) {
|
|
extract_routing_switches(es, y, x);
|
|
}
|
|
// logic switches
|
|
if (has_device(es->model, y, x, DEV_LOGIC)) {
|
|
extract_logic_switches(es, y, x);
|
|
}
|
|
// iologic switches
|
|
if (has_device(es->model, y, x, DEV_ILOGIC)) {
|
|
extract_iologic_switches(es, y, x);
|
|
}
|
|
}
|
|
}
|
|
extract_center_switches(es);
|
|
extract_gclk_center_vert_sw(es);
|
|
extract_gclk_hclk_updown_sw(es);
|
|
RC_RETURN(es->model);
|
|
}
|
|
|
|
static int construct_extract_state(struct extract_state* es,
|
|
struct fpga_model* model)
|
|
{
|
|
RC_CHECK(model);
|
|
memset(es, 0, sizeof(*es));
|
|
es->model = model;
|
|
return 0;
|
|
}
|
|
|
|
int extract_model(struct fpga_model* model, struct fpga_bits* bits)
|
|
{
|
|
struct extract_state es;
|
|
net_idx_t net_idx;
|
|
int i, rc;
|
|
|
|
RC_CHECK(model);
|
|
rc = construct_extract_state(&es, model);
|
|
if (rc) RC_FAIL(model, rc);
|
|
es.bits = bits;
|
|
for (i = 0; i < sizeof(s_default_bits)/sizeof(s_default_bits[0]); i++) {
|
|
if (!get_bitp(bits, &s_default_bits[i]))
|
|
RC_FAIL(model, EINVAL);
|
|
clear_bitp(bits, &s_default_bits[i]);
|
|
}
|
|
|
|
rc = extract_switches(&es);
|
|
if (rc) RC_FAIL(model, rc);
|
|
rc = extract_type2(&es);
|
|
if (rc) RC_FAIL(model, rc);
|
|
rc = extract_logic(&es);
|
|
if (rc) RC_FAIL(model, rc);
|
|
|
|
// turn switches into nets
|
|
if (model->nets)
|
|
HERE(); // should be empty here
|
|
for (i = 0; i < es.num_yx_pos; i++) {
|
|
rc = fnet_new(model, &net_idx);
|
|
if (rc) RC_FAIL(model, rc);
|
|
rc = fnet_add_sw(model, net_idx, es.yx_pos[i].y,
|
|
es.yx_pos[i].x, &es.yx_pos[i].idx, 1);
|
|
if (rc) RC_FAIL(model, rc);
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
int printf_swbits(struct fpga_model* model)
|
|
{
|
|
char bit_str[129];
|
|
int i, j, width;
|
|
|
|
RC_CHECK(model);
|
|
for (i = 0; i < model->num_bitpos; i++) {
|
|
|
|
width = (model->sw_bitpos[i].minor == 20) ? 64 : 128;
|
|
for (j = 0; j < width; j++)
|
|
bit_str[j] = '0';
|
|
bit_str[j] = 0;
|
|
|
|
if (model->sw_bitpos[i].two_bits_val & 2)
|
|
bit_str[model->sw_bitpos[i].two_bits_o] = '1';
|
|
if (model->sw_bitpos[i].two_bits_val & 1)
|
|
bit_str[model->sw_bitpos[i].two_bits_o+1] = '1';
|
|
bit_str[model->sw_bitpos[i].one_bit_o] = '1';
|
|
printf("mi%02i %s %s %s %s\n", model->sw_bitpos[i].minor,
|
|
fpga_wire2str(model->sw_bitpos[i].to),
|
|
bit_str,
|
|
fpga_wire2str(model->sw_bitpos[i].from),
|
|
model->sw_bitpos[i].bidir ? "<->" : "->");
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
static int find_bitpos(struct fpga_model* model, int y, int x, swidx_t sw)
|
|
{
|
|
enum extra_wires from_w, to_w;
|
|
const char* from_str, *to_str;
|
|
int i;
|
|
|
|
RC_CHECK(model);
|
|
from_str = fpga_switch_str(model, y, x, sw, SW_FROM);
|
|
to_str = fpga_switch_str(model, y, x, sw, SW_TO);
|
|
from_w = fpga_str2wire(from_str);
|
|
to_w = fpga_str2wire(to_str);
|
|
|
|
if (from_w == NO_WIRE || to_w == NO_WIRE) {
|
|
HERE();
|
|
return -1;
|
|
}
|
|
for (i = 0; i < model->num_bitpos; i++) {
|
|
if (model->sw_bitpos[i].from == from_w
|
|
&& model->sw_bitpos[i].to == to_w)
|
|
return i;
|
|
if (model->sw_bitpos[i].bidir
|
|
&& model->sw_bitpos[i].to == from_w
|
|
&& model->sw_bitpos[i].from == to_w) {
|
|
if (!fpga_switch_is_bidir(model, y, x, sw))
|
|
HERE();
|
|
return i;
|
|
}
|
|
}
|
|
fprintf(stderr, "#E switch %s (%i) to %s (%i) not in model\n",
|
|
from_str, from_w, to_str, to_w);
|
|
return -1;
|
|
}
|
|
|
|
static int write_routing_sw(struct fpga_bits* bits, struct fpga_model* model, int y, int x)
|
|
{
|
|
struct fpga_tile* tile;
|
|
int i, bit_pos, rc;
|
|
|
|
RC_CHECK(model);
|
|
// go through enabled switches, lookup in sw_bitpos
|
|
// and set bits
|
|
tile = YX_TILE(model, y, x);
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
bit_pos = find_bitpos(model, y, x, i);
|
|
if (bit_pos == -1) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
rc = bitpos_set_bits(bits, model, y, x,
|
|
&model->sw_bitpos[bit_pos]);
|
|
if (rc) FAIL(rc);
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
struct str_sw
|
|
{
|
|
const char* from_str;
|
|
const char* to_str;
|
|
};
|
|
|
|
static int get_used_switches(struct fpga_model* model, int y, int x,
|
|
struct str_sw** str_sw, int* num_sw)
|
|
{
|
|
int i, num_used, rc;
|
|
struct fpga_tile* tile;
|
|
|
|
RC_CHECK(model);
|
|
tile = YX_TILE(model, y, x);
|
|
num_used = 0;
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (tile->switches[i] & SWITCH_USED)
|
|
num_used++;
|
|
}
|
|
if (!num_used) {
|
|
*num_sw = 0;
|
|
*str_sw = 0;
|
|
return 0;
|
|
}
|
|
*str_sw = malloc(num_used * sizeof(**str_sw));
|
|
if (!(*str_sw)) FAIL(ENOMEM);
|
|
*num_sw = 0;
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
(*str_sw)[*num_sw].from_str =
|
|
fpga_switch_str(model, y, x, i, SW_FROM);
|
|
(*str_sw)[*num_sw].to_str =
|
|
fpga_switch_str(model, y, x, i, SW_TO);
|
|
(*num_sw)++;
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
// returns the number of found to-from pairs from search_sw that
|
|
// were found in the str_sw array, but only if all of the to-from
|
|
// pairs in search_sw were found. The found array then contains
|
|
// the indices into str_sw were the matches were made.
|
|
// If not all to-from pairs were found (or none), returns 0.
|
|
static int find_switches(struct iologic_sw_pos* search_sw,
|
|
struct str_sw* str_sw, int num_str_sw, int (*found)[MAX_IOLOGIC_SWBLOCK])
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < MAX_IOLOGIC_SWBLOCK && search_sw->to[i]; i++) {
|
|
for (j = 0; j < num_str_sw; j++) {
|
|
if (!str_sw[j].to_str)
|
|
continue;
|
|
if (strcmp(str_sw[j].to_str, search_sw->to[i])
|
|
|| strcmp(str_sw[j].from_str, search_sw->from[i]))
|
|
continue;
|
|
(*found)[i] = j;
|
|
break;
|
|
}
|
|
if (j >= num_str_sw)
|
|
return 0;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int write_iologic_sw(struct fpga_bits* bits, struct fpga_model* model,
|
|
int y, int x)
|
|
{
|
|
int i, j, row_num, row_pos, start_in_frame, rc;
|
|
struct iologic_sw_pos* sw_pos;
|
|
struct str_sw* str_sw;
|
|
int found_i[MAX_IOLOGIC_SWBLOCK];
|
|
int num_sw, num_found;
|
|
|
|
RC_CHECK(model);
|
|
if (x < LEFT_SIDE_WIDTH) {
|
|
if (x != LEFT_IO_DEVS) FAIL(EINVAL);
|
|
sw_pos = s_left_io_swpos;
|
|
} else if (x >= model->x_width-RIGHT_SIDE_WIDTH) {
|
|
if (x != model->x_width - RIGHT_IO_DEVS_O) FAIL(EINVAL);
|
|
sw_pos = s_right_io_swpos;
|
|
} else if (y == TOP_OUTER_IO)
|
|
sw_pos = s_top_outer_io_swpos;
|
|
else if (y == TOP_INNER_IO)
|
|
sw_pos = s_top_inner_io_swpos;
|
|
else if (y == model->y_height-BOT_INNER_IO)
|
|
sw_pos = s_bot_inner_io_swpos;
|
|
else if (y == model->y_height-BOT_OUTER_IO)
|
|
sw_pos = s_bot_outer_io_swpos;
|
|
else FAIL(EINVAL);
|
|
|
|
is_in_row(model, y, &row_num, &row_pos);
|
|
if (row_num == -1 || row_pos == -1
|
|
|| row_pos == HCLK_POS) FAIL(EINVAL);
|
|
if (row_pos > HCLK_POS)
|
|
start_in_frame = (row_pos-1)*64 + 16;
|
|
else
|
|
start_in_frame = row_pos*64;
|
|
|
|
rc = get_used_switches(model, y, x, &str_sw, &num_sw);
|
|
if (rc) FAIL(rc);
|
|
|
|
for (i = 0; sw_pos[i].to[0]; i++) {
|
|
if (!(num_found = find_switches(&sw_pos[i], str_sw,
|
|
num_sw, &found_i))) continue;
|
|
for (j = 0; sw_pos[i].minor[j] != -1; j++) {
|
|
set_bit(bits, row_num, model->x_major[x],
|
|
sw_pos[i].minor[j], start_in_frame
|
|
+ sw_pos[i].b64[j]);
|
|
}
|
|
// remove switches from 'used' array
|
|
for (j = 0; j < num_found; j++)
|
|
str_sw[found_i[j]].to_str = 0;
|
|
}
|
|
|
|
// check whether all switches were found
|
|
for (i = 0; i < num_sw; i++) {
|
|
if (!str_sw[i].to_str)
|
|
continue;
|
|
fprintf(stderr, "#E %s:%i unsupported switch "
|
|
"y%i x%i %s -> %s\n", __FILE__, __LINE__,
|
|
y, x, str_sw[i].from_str, str_sw[i].to_str);
|
|
}
|
|
free(str_sw);
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
static int write_inner_term_sw(struct fpga_bits *bits,
|
|
struct fpga_model *model, int y, int x)
|
|
{
|
|
struct fpga_tile *tile;
|
|
const char *from_str, *from_found, *to_str, *to_found;
|
|
int i, j, from_idx, to_idx;
|
|
|
|
RC_CHECK(model);
|
|
|
|
tile = YX_TILE(model, y, x);
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
|
|
from_str = fpga_switch_str(model, y, x, i, SW_FROM);
|
|
to_str = fpga_switch_str(model, y, x, i, SW_TO);
|
|
RC_ASSERT(model, from_str && to_str);
|
|
|
|
if (strstr(from_str, "IBUF")
|
|
&& strstr(to_str, "CLKPIN"))
|
|
continue;
|
|
if ((from_found = strstr(from_str, "CLKPIN"))
|
|
&& (to_found = strstr(to_str, "CKPIN"))) {
|
|
struct switch_to_yx_l2 switch_to_yx_l2;
|
|
int iob_y, iob_x, iob_idx;
|
|
struct fpga_device *iob_dev;
|
|
|
|
from_idx = atoi(&from_found[6]);
|
|
to_idx = atoi(&to_found[5]);
|
|
RC_ASSERT(model, from_idx == to_idx);
|
|
|
|
// follow switches backwards to IOB
|
|
switch_to_yx_l2.l1.yx_req = YX_DEV_IOB;
|
|
switch_to_yx_l2.l1.flags = SWTO_YX_DEF;
|
|
switch_to_yx_l2.l1.model = model;
|
|
switch_to_yx_l2.l1.y = y;
|
|
switch_to_yx_l2.l1.x = x;
|
|
switch_to_yx_l2.l1.start_switch = fpga_switch_str_i(model, y, x, i, SW_TO);
|
|
switch_to_yx_l2.l1.from_to = SW_TO;
|
|
fpga_switch_to_yx_l2(&switch_to_yx_l2);
|
|
RC_ASSERT(model, switch_to_yx_l2.l1.set.len);
|
|
|
|
// find matching gclk pin
|
|
for (j = 0; j < model->pkg->num_gclk_pins; j++) {
|
|
if (!model->pkg->gclk_pin[j])
|
|
continue;
|
|
|
|
fpga_find_iob(model, model->pkg->gclk_pin[j],
|
|
&iob_y, &iob_x, &iob_idx);
|
|
RC_CHECK(model);
|
|
if (iob_y != switch_to_yx_l2.l1.dest_y
|
|
|| iob_x != switch_to_yx_l2.l1.dest_x)
|
|
continue;
|
|
iob_dev = fdev_p(model, iob_y, iob_x, DEV_IOB, iob_idx);
|
|
RC_ASSERT(model, iob_dev);
|
|
|
|
if (fpga_switch_lookup(model, iob_y, iob_x,
|
|
iob_dev->pinw[IOB_OUT_I],
|
|
switch_to_yx_l2.l1.dest_connpt) != NO_SWITCH)
|
|
break;
|
|
}
|
|
// set bit
|
|
if (j < model->pkg->num_gclk_pins) {
|
|
uint16_t u16;
|
|
int bits_off;
|
|
|
|
bits_off = IOB_DATA_START
|
|
+ model->pkg->gclk_type2_o[j]*XC6_WORD_BYTES
|
|
+ XC6_TYPE2_GCLK_REG_SW/XC6_WORD_BITS;
|
|
u16 = frame_get_u16(&bits->d[bits_off]);
|
|
u16 |= 1<<(XC6_TYPE2_GCLK_REG_SW%XC6_WORD_BITS);
|
|
frame_set_u16(&bits->d[bits_off], u16);
|
|
continue;
|
|
}
|
|
// fall-through to unsupported
|
|
}
|
|
fprintf(stderr, "#E %s:%i unsupported switch "
|
|
"y%i x%i %s -> %s\n", __FILE__, __LINE__,
|
|
y, x, from_str, to_str);
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
static int write_switches(struct fpga_bits* bits, struct fpga_model* model)
|
|
{
|
|
struct fpga_tile* tile;
|
|
int x, y, i;
|
|
|
|
RC_CHECK(model);
|
|
// We need to identify and take out each enabled switch, whether it
|
|
// leads to enabled bits or not. That way we can print unsupported
|
|
// switches at the end and keep our model alive and maintainable
|
|
// over time.
|
|
for (x = 0; x < model->x_width; x++) {
|
|
for (y = 0; y < model->y_height; y++) {
|
|
tile = YX_TILE(model, y, x);
|
|
if (is_atx(X_ROUTING_COL, model, x)
|
|
&& y >= TOP_IO_TILES
|
|
&& y < model->y_height-BOT_IO_TILES
|
|
&& !is_aty(Y_ROW_HORIZ_AXSYMM|Y_CHIP_HORIZ_REGS,
|
|
model, y)) {
|
|
write_routing_sw(bits, model, y, x);
|
|
continue;
|
|
}
|
|
if (is_atyx(YX_DEV_ILOGIC, model, y, x)) {
|
|
write_iologic_sw(bits, model, y, x);
|
|
continue;
|
|
}
|
|
if (is_atyx(YX_DEV_LOGIC|YX_DEV_IOB, model, y, x))
|
|
continue;
|
|
if (is_atx(X_ROUTING_COL, model, x)
|
|
&& is_aty(Y_ROW_HORIZ_AXSYMM, model, y)) {
|
|
write_hclk_sw(bits, model, y, x);
|
|
continue;
|
|
}
|
|
if (is_atyx(YX_INNER_TERM, model, y, x)) {
|
|
write_inner_term_sw(bits, model, y, x);
|
|
continue;
|
|
}
|
|
if (is_atyx(YX_CENTER, model, y, x)) {
|
|
write_center_sw(bits, model, y, x);
|
|
continue;
|
|
}
|
|
if (is_atx(X_CENTER_REGS_COL, model, x)) {
|
|
if (is_aty(Y_ROW_HORIZ_AXSYMM, model, y)) {
|
|
write_gclk_center_vert_sw(bits, model, y, x);
|
|
continue;
|
|
}
|
|
if (tile->flags & TF_CENTER_MIDBUF
|
|
|| (y < model->center_y
|
|
&& YX_TILE(model, y+1, x)->flags & TF_CENTER_MIDBUF)
|
|
|| (y > model->center_y
|
|
&& YX_TILE(model, y-1, x)->flags & TF_CENTER_MIDBUF))
|
|
continue;
|
|
}
|
|
// print unsupported switches
|
|
for (i = 0; i < tile->num_switches; i++) {
|
|
if (!(tile->switches[i] & SWITCH_USED))
|
|
continue;
|
|
fprintf(stderr, "#E %s:%i unsupported switch "
|
|
"y%i x%i %s\n", __FILE__, __LINE__,
|
|
y, x,
|
|
fpga_switch_print(model, y, x, i));
|
|
}
|
|
}
|
|
}
|
|
RC_RETURN(model);
|
|
}
|
|
|
|
static int write_logic(struct fpga_bits* bits, struct fpga_model* model)
|
|
{
|
|
int dev_idx, row, row_pos, xm_col, rc;
|
|
int x, y, byte_off;
|
|
struct fpga_device* dev;
|
|
uint64_t u64;
|
|
uint8_t* u8_p;
|
|
|
|
RC_CHECK(model);
|
|
for (x = LEFT_SIDE_WIDTH; x < model->x_width-RIGHT_SIDE_WIDTH; x++) {
|
|
xm_col = is_atx(X_FABRIC_LOGIC_XM_COL, model, x);
|
|
if (!xm_col && !is_atx(X_FABRIC_LOGIC_XL_COL, model, x))
|
|
continue;
|
|
|
|
for (y = TOP_IO_TILES; y < model->y_height - BOT_IO_TILES; y++) {
|
|
if (!has_device(model, y, x, DEV_LOGIC))
|
|
continue;
|
|
|
|
row = which_row(y, model);
|
|
row_pos = pos_in_row(y, model);
|
|
if (row == -1 || row_pos == -1 || row_pos == 8) {
|
|
HERE();
|
|
continue;
|
|
}
|
|
if (row_pos > 8) row_pos--;
|
|
u8_p = get_first_minor(bits, row, model->x_major[x]);
|
|
byte_off = row_pos * 8;
|
|
if (row_pos >= 8) byte_off += XC6_HCLK_BYTES;
|
|
|
|
if (xm_col) {
|
|
// X device
|
|
dev_idx = fpga_dev_idx(model, y, x, DEV_LOGIC, DEV_LOG_X);
|
|
if (dev_idx == NO_DEV) FAIL(EINVAL);
|
|
dev = FPGA_DEV(model, y, x, dev_idx);
|
|
if (dev->instantiated) {
|
|
u64 = 0x000000B000600086ULL;
|
|
frame_set_u64(u8_p + 26*FRAME_SIZE + byte_off, u64);
|
|
|
|
if (dev->u.logic.a2d[LUT_A].lut6
|
|
&& dev->u.logic.a2d[LUT_A].lut6[0]) {
|
|
HERE(); // not supported
|
|
}
|
|
if (dev->u.logic.a2d[LUT_B].lut6
|
|
&& dev->u.logic.a2d[LUT_B].lut6[0]) {
|
|
HERE(); // not supported
|
|
}
|
|
if (dev->u.logic.a2d[LUT_C].lut6
|
|
&& dev->u.logic.a2d[LUT_C].lut6[0]) {
|
|
HERE(); // not supported
|
|
}
|
|
if (dev->u.logic.a2d[LUT_D].lut6
|
|
&& dev->u.logic.a2d[LUT_D].lut6[0]) {
|
|
rc = parse_boolexpr(dev->u.logic.a2d[LUT_D].lut6, &u64);
|
|
if (rc) FAIL(rc);
|
|
write_lut64(u8_p + 29*FRAME_SIZE, byte_off*8, u64);
|
|
}
|
|
}
|
|
|
|
// M device
|
|
dev_idx = fpga_dev_idx(model, y, x, DEV_LOGIC, DEV_LOG_M_OR_L);
|
|
if (dev_idx == NO_DEV) FAIL(EINVAL);
|
|
dev = FPGA_DEV(model, y, x, dev_idx);
|
|
if (dev->instantiated) {
|
|
HERE();
|
|
}
|
|
} else {
|
|
// X device
|
|
dev_idx = fpga_dev_idx(model, y, x, DEV_LOGIC, DEV_LOG_X);
|
|
if (dev_idx == NO_DEV) FAIL(EINVAL);
|
|
dev = FPGA_DEV(model, y, x, dev_idx);
|
|
if (dev->instantiated) {
|
|
HERE();
|
|
}
|
|
|
|
// L device
|
|
dev_idx = fpga_dev_idx(model, y, x, DEV_LOGIC, DEV_LOG_M_OR_L);
|
|
if (dev_idx == NO_DEV) FAIL(EINVAL);
|
|
dev = FPGA_DEV(model, y, x, dev_idx);
|
|
if (dev->instantiated) {
|
|
HERE();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
int write_model(struct fpga_bits* bits, struct fpga_model* model)
|
|
{
|
|
int i;
|
|
|
|
RC_CHECK(model);
|
|
|
|
for (i = 0; i < sizeof(s_default_bits)/sizeof(s_default_bits[0]); i++)
|
|
set_bitp(bits, &s_default_bits[i]);
|
|
write_switches(bits, model);
|
|
write_type2(bits, model);
|
|
write_logic(bits, model);
|
|
|
|
RC_RETURN(model);
|
|
}
|