Support FF initialization
This commit is contained in:
parent
0325f0091a
commit
5ce6ab64f4
|
|
@ -35,7 +35,7 @@ class Die
|
||||||
static constexpr int MAX_RAM = 32;
|
static constexpr int MAX_RAM = 32;
|
||||||
static constexpr int MAX_RAM_ROWS = 8;
|
static constexpr int MAX_RAM_ROWS = 8;
|
||||||
static constexpr int MAX_RAM_COLS = 4;
|
static constexpr int MAX_RAM_COLS = 4;
|
||||||
static constexpr int LATCH_BLOCK_SIZE = 112;
|
static constexpr int LATCH_BLOCK_SIZE = 112 + 1; // Added one more byte for FF_INIT
|
||||||
static constexpr int RAM_BLOCK_SIZE = 27;
|
static constexpr int RAM_BLOCK_SIZE = 27;
|
||||||
static constexpr int MEMORY_SIZE = 5120;
|
static constexpr int MEMORY_SIZE = 5120;
|
||||||
static constexpr int MAX_PLL = 4;
|
static constexpr int MAX_PLL = 4;
|
||||||
|
|
@ -43,6 +43,8 @@ class Die
|
||||||
static constexpr int CLKIN_CFG_SIZE = 4;
|
static constexpr int CLKIN_CFG_SIZE = 4;
|
||||||
static constexpr int GLBOUT_CFG_SIZE = 8;
|
static constexpr int GLBOUT_CFG_SIZE = 8;
|
||||||
static constexpr int PLL_CONFIG_SIZE = PLL_CFG_SIZE * MAX_PLL * 2 + CLKIN_CFG_SIZE + GLBOUT_CFG_SIZE;
|
static constexpr int PLL_CONFIG_SIZE = PLL_CFG_SIZE * MAX_PLL * 2 + CLKIN_CFG_SIZE + GLBOUT_CFG_SIZE;
|
||||||
|
static constexpr int FF_INIT_RESET = 2;
|
||||||
|
static constexpr int FF_INIT_SET = 3;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Die();
|
explicit Die();
|
||||||
|
|
@ -54,6 +56,7 @@ class Die
|
||||||
int get_max_ram_col() const { return MAX_RAM_COLS; }
|
int get_max_ram_col() const { return MAX_RAM_COLS; }
|
||||||
|
|
||||||
bool is_latch_empty(int x, int y) const;
|
bool is_latch_empty(int x, int y) const;
|
||||||
|
bool is_cpe_empty(int x, int y) const;
|
||||||
bool is_ram_empty(int x, int y) const;
|
bool is_ram_empty(int x, int y) const;
|
||||||
bool is_ram_data_empty(int x, int y) const;
|
bool is_ram_data_empty(int x, int y) const;
|
||||||
bool is_pll_cfg_empty(int index) const;
|
bool is_pll_cfg_empty(int index) const;
|
||||||
|
|
@ -65,6 +68,7 @@ class Die
|
||||||
void write_ram_data(int x, int y, const std::vector<uint8_t> &data, uint16_t addr);
|
void write_ram_data(int x, int y, const std::vector<uint8_t> &data, uint16_t addr);
|
||||||
void write_pll_select(uint8_t select, const std::vector<uint8_t> &data);
|
void write_pll_select(uint8_t select, const std::vector<uint8_t> &data);
|
||||||
void write_pll(const std::vector<uint8_t> &data) { pll_cfg = data; }
|
void write_pll(const std::vector<uint8_t> &data) { pll_cfg = data; }
|
||||||
|
void write_ff_init(int x, int y, uint8_t data);
|
||||||
|
|
||||||
const std::vector<uint8_t> get_latch_config(int x, int y) const { return latch.at(std::make_pair(x, y)); }
|
const std::vector<uint8_t> get_latch_config(int x, int y) const { return latch.at(std::make_pair(x, y)); }
|
||||||
const std::vector<uint8_t> get_ram_config(int x, int y) const { return ram.at(std::make_pair(x, y)); }
|
const std::vector<uint8_t> get_ram_config(int x, int y) const { return ram.at(std::make_pair(x, y)); }
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ class TileBitDatabase : public BaseBitDatabase
|
||||||
void add_sb_drive(int index, int start);
|
void add_sb_drive(int index, int start);
|
||||||
|
|
||||||
void add_cpe(int index, int start);
|
void add_cpe(int index, int start);
|
||||||
|
void add_ff_init(int index, int start);
|
||||||
void add_inmux(int index, int plane, int start);
|
void add_inmux(int index, int plane, int start);
|
||||||
|
|
||||||
void add_gpio(int start);
|
void add_gpio(int start);
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,7 @@ Chip Bitstream::deserialise_chip()
|
||||||
uint8_t x_pos = 0, y_pos = 0;
|
uint8_t x_pos = 0, y_pos = 0;
|
||||||
uint8_t pll_select = 0x0f;
|
uint8_t pll_select = 0x0f;
|
||||||
uint16_t aclcu = 0;
|
uint16_t aclcu = 0;
|
||||||
|
std::map<std::pair<int, int>, int> tile_iteration;
|
||||||
while (!rd.is_end()) {
|
while (!rd.is_end()) {
|
||||||
rd.crc16.reset_crc16();
|
rd.crc16.reset_crc16();
|
||||||
uint8_t cmd = rd.get_command_opcode();
|
uint8_t cmd = rd.get_command_opcode();
|
||||||
|
|
@ -412,8 +413,34 @@ Chip Bitstream::deserialise_chip()
|
||||||
|
|
||||||
if (is_block_ram)
|
if (is_block_ram)
|
||||||
die.write_ram(x_pos, y_pos, block);
|
die.write_ram(x_pos, y_pos, block);
|
||||||
else
|
else {
|
||||||
|
int iteration = -1;
|
||||||
|
if (tile_iteration.count(std::make_pair(x_pos, y_pos)))
|
||||||
|
iteration = tile_iteration[std::make_pair(x_pos, y_pos)];
|
||||||
|
tile_iteration[std::make_pair(x_pos, y_pos)] = ++iteration;
|
||||||
|
|
||||||
|
// Detection of FF initialization is possible on
|
||||||
|
// last iteration
|
||||||
|
if (iteration == 2) {
|
||||||
|
std::vector<uint8_t> data = die.get_latch_config(x_pos, y_pos);
|
||||||
|
// Make sure we have CPE data
|
||||||
|
// even if uninitialized
|
||||||
|
block.resize(40, 0x00);
|
||||||
|
uint8_t val = 0x00;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
uint8_t v = block[i * 10 + 8] ^ data[i * 10 + 8];
|
||||||
|
if (v == 0x30)
|
||||||
|
val |= Die::FF_INIT_RESET << (i * 2);
|
||||||
|
else if (v == 0xc0)
|
||||||
|
val |= Die::FF_INIT_SET << (i * 2);
|
||||||
|
else
|
||||||
|
BITSTREAM_FATAL(stringf("Unknown CPE state %d on pos %d,%d\n", v, x_pos, y_pos),
|
||||||
|
rd.get_offset());
|
||||||
|
}
|
||||||
|
die.write_ff_init(x_pos, y_pos, val);
|
||||||
|
}
|
||||||
die.write_latch(x_pos, y_pos, block);
|
die.write_latch(x_pos, y_pos, block);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case CMD_PATH:
|
case CMD_PATH:
|
||||||
BITSTREAM_DEBUG("CMD_PATH");
|
BITSTREAM_DEBUG("CMD_PATH");
|
||||||
|
|
@ -614,20 +641,52 @@ Bitstream Bitstream::serialise_chip(const Chip &chip)
|
||||||
if (!pll_written)
|
if (!pll_written)
|
||||||
wr.write_cmd_pll_empty();
|
wr.write_cmd_pll_empty();
|
||||||
|
|
||||||
for (int iteration = 0; iteration < 2; iteration++) {
|
for (int iteration = 0; iteration < 3; iteration++) {
|
||||||
for (int y = 0; y < Die::MAX_ROWS; y++) {
|
for (int y = 0; y < Die::MAX_ROWS; y++) {
|
||||||
for (int x = 0; x < Die::MAX_COLS; x++) {
|
for (int x = 0; x < Die::MAX_COLS; x++) {
|
||||||
|
// Empty configuration is skipped
|
||||||
if (die.is_latch_empty(x, y))
|
if (die.is_latch_empty(x, y))
|
||||||
continue;
|
continue;
|
||||||
|
// Only tiles with CPE can have multiple iterations
|
||||||
if (iteration != 0 && is_edge_location(x, y))
|
if (iteration != 0 && is_edge_location(x, y))
|
||||||
continue;
|
continue;
|
||||||
wr.write_cmd_lxlys(x, y);
|
// If CPE empty skip other iterations
|
||||||
std::vector<uint8_t> data = die.get_latch_config(x, y);
|
if (iteration != 0 && die.is_cpe_empty(x, y))
|
||||||
if (iteration == 0 && !is_edge_location(x, y)) {
|
continue;
|
||||||
std::fill(data.begin(), data.begin() + 40, 0);
|
std::vector<uint8_t> data = std::vector<uint8_t>(die.get_latch_config(x, y));
|
||||||
|
uint8_t ff_init = data.back();
|
||||||
|
data.pop_back();
|
||||||
|
if (!is_edge_location(x, y)) {
|
||||||
|
if (iteration == 0) {
|
||||||
|
// First iteration does not setup CPE at all
|
||||||
|
std::fill(data.begin(), data.begin() + 40, 0);
|
||||||
|
}
|
||||||
|
// 2nd iteration with no changes if no FF initialization
|
||||||
|
if (iteration == 1 && ff_init) {
|
||||||
|
// Only CPE data is exported
|
||||||
|
data.resize(40);
|
||||||
|
// Set initial FFs states
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
uint8_t ff = (ff_init >> (i * 2)) & 0x03;
|
||||||
|
if (ff == Die::FF_INIT_RESET)
|
||||||
|
data[i * 10 + 8] &= 0x30 ^ 0xff;
|
||||||
|
else if (ff == Die::FF_INIT_SET)
|
||||||
|
data[i * 10 + 8] &= 0xc0 ^ 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3rd iteration only if there was FF initialization
|
||||||
|
if (iteration == 2) {
|
||||||
|
if (!ff_init)
|
||||||
|
continue;
|
||||||
|
// Only CPE data is exported
|
||||||
|
data.resize(40);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// minimize output
|
||||||
auto rit = std::find_if(data.rbegin(), data.rend(), [](uint8_t val) { return val != 0; });
|
auto rit = std::find_if(data.rbegin(), data.rend(), [](uint8_t val) { return val != 0; });
|
||||||
data.erase(rit.base(), end(data));
|
data.erase(rit.base(), end(data));
|
||||||
|
|
||||||
|
wr.write_cmd_lxlys(x, y);
|
||||||
wr.write_header(CMD_DLCU, data.size());
|
wr.write_header(CMD_DLCU, data.size());
|
||||||
wr.write_bytes(data);
|
wr.write_bytes(data);
|
||||||
wr.insert_crc16();
|
wr.insert_crc16();
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ Die::Die()
|
||||||
for (int y = 0; y < MAX_RAM_ROWS; y++) {
|
for (int y = 0; y < MAX_RAM_ROWS; y++) {
|
||||||
for (int x = 0; x < MAX_RAM_COLS; x++) {
|
for (int x = 0; x < MAX_RAM_COLS; x++) {
|
||||||
ram[std::make_pair(x, y)] = std::vector<u_int8_t>();
|
ram[std::make_pair(x, y)] = std::vector<u_int8_t>();
|
||||||
ram[std::make_pair(x, y)].reserve(LATCH_BLOCK_SIZE);
|
ram[std::make_pair(x, y)].reserve(RAM_BLOCK_SIZE);
|
||||||
ram_data[std::make_pair(x, y)] = std::vector<u_int8_t>();
|
ram_data[std::make_pair(x, y)] = std::vector<u_int8_t>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +42,15 @@ Die::Die()
|
||||||
|
|
||||||
bool Die::is_latch_empty(int x, int y) const { return latch.at(std::make_pair(x, y)).empty(); }
|
bool Die::is_latch_empty(int x, int y) const { return latch.at(std::make_pair(x, y)).empty(); }
|
||||||
|
|
||||||
|
bool Die::is_cpe_empty(int x, int y) const
|
||||||
|
{
|
||||||
|
auto &block = latch.at(std::make_pair(x, y));
|
||||||
|
for (int i = 0; i < 40; i++)
|
||||||
|
if (block[i] != 0x00)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Die::is_ram_empty(int x, int y) const { return ram.at(std::make_pair(x, y)).empty(); }
|
bool Die::is_ram_empty(int x, int y) const { return ram.at(std::make_pair(x, y)).empty(); }
|
||||||
|
|
||||||
bool Die::is_ram_data_empty(int x, int y) const { return ram_data.at(std::make_pair(x, y)).empty(); }
|
bool Die::is_ram_data_empty(int x, int y) const { return ram_data.at(std::make_pair(x, y)).empty(); }
|
||||||
|
|
@ -82,6 +91,13 @@ void Die::write_latch(int x, int y, const std::vector<uint8_t> &data)
|
||||||
block[pos++] = d;
|
block[pos++] = d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Die::write_ff_init(int x, int y, uint8_t data)
|
||||||
|
{
|
||||||
|
auto &block = latch.at(std::make_pair(x, y));
|
||||||
|
block.resize(LATCH_BLOCK_SIZE, 0x00);
|
||||||
|
block[LATCH_BLOCK_SIZE - 1] = data;
|
||||||
|
}
|
||||||
|
|
||||||
void Die::write_ram(int x, int y, const std::vector<uint8_t> &data)
|
void Die::write_ram(int x, int y, const std::vector<uint8_t> &data)
|
||||||
{
|
{
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,11 @@ void TileBitDatabase::add_sb_drive(int index, int start)
|
||||||
|
|
||||||
void TileBitDatabase::add_cpe(int index, int start) { add_word_settings(stringf("CPE_%d", index), start, 80); }
|
void TileBitDatabase::add_cpe(int index, int start) { add_word_settings(stringf("CPE_%d", index), start, 80); }
|
||||||
|
|
||||||
|
void TileBitDatabase::add_ff_init(int index, int start)
|
||||||
|
{
|
||||||
|
add_word_settings(stringf("CPE_%d.FF_INIT", index), start, 2);
|
||||||
|
}
|
||||||
|
|
||||||
void TileBitDatabase::add_inmux(int index, int plane, int start)
|
void TileBitDatabase::add_inmux(int index, int plane, int start)
|
||||||
{
|
{
|
||||||
add_word_settings(stringf("INMUX_%d_%02d", index, plane), start, 4);
|
add_word_settings(stringf("INMUX_%d_%02d", index, plane), start, 4);
|
||||||
|
|
@ -172,6 +177,7 @@ TileBitDatabase::TileBitDatabase(const int x, const int y) : BaseBitDatabase(Die
|
||||||
is_core = true;
|
is_core = true;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
add_cpe(i + 1, 10 * i * 8);
|
add_cpe(i + 1, 10 * i * 8);
|
||||||
|
add_ff_init(i + 1, (Die::LATCH_BLOCK_SIZE - 1) * 8 + i * 2);
|
||||||
}
|
}
|
||||||
int pos = 40;
|
int pos = 40;
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue