diff --git a/himbaechel/uarch/xilinx/CMakeLists.txt b/himbaechel/uarch/xilinx/CMakeLists.txt index 246ca3f6..b67dd60c 100644 --- a/himbaechel/uarch/xilinx/CMakeLists.txt +++ b/himbaechel/uarch/xilinx/CMakeLists.txt @@ -3,6 +3,7 @@ set(SOURCES constids.inc extra_data.h fasm.cc + mmcm_tables.cc pack_carry.cc pack.cc pack_clocking.cc diff --git a/himbaechel/uarch/xilinx/constids.inc b/himbaechel/uarch/xilinx/constids.inc index 7e5024b3..9bc7b9c7 100644 --- a/himbaechel/uarch/xilinx/constids.inc +++ b/himbaechel/uarch/xilinx/constids.inc @@ -192,6 +192,10 @@ X(BUFGCE_DIV) X(BUFCE_BUFG_PS) X(CMT_L) +X(IS_PSEN_INVERTED) +X(IS_PSINCDEC_INVERTED) +X(BANDWIDTH) + X(INTENT_DEFAULT) X(NODE_OUTPUT) X(NODE_DEDICATED) diff --git a/himbaechel/uarch/xilinx/fasm.cc b/himbaechel/uarch/xilinx/fasm.cc index 9ca5e074..f3526a6c 100644 --- a/himbaechel/uarch/xilinx/fasm.cc +++ b/himbaechel/uarch/xilinx/fasm.cc @@ -37,6 +37,15 @@ #include "himbaechel_constids.h" NEXTPNR_NAMESPACE_BEGIN + +namespace Xc7MMCM { +extern const uint16_t filter_lookup_low[]; +extern const uint16_t filter_lookup_low_ss[]; +extern const uint16_t filter_lookup_high[]; +extern const uint16_t filter_lookup_optimized[]; +extern const int64_t lk_table[]; +}; + namespace { struct FasmBackend { @@ -1204,6 +1213,8 @@ struct FasmBackend pop(2); } else if (ci->type == id_PLLE2_ADV_PLLE2_ADV) { write_pll(ci); + } else if (ci->type == id_MMCME2_ADV_MMCME2_ADV) { + write_mmcm(ci); } blank(); } @@ -1491,6 +1502,142 @@ struct FasmBackend pop(2); } + + void write_mmcm_clkout(const std::string &name, CellInfo *ci) + { + // FIXME: variable duty cycle + int high = 1, low = 1, phasemux = 0, delaytime = 0, frac = 0; + bool no_count = false, edge = false; + double divide = float_or_default(ci, name + ((name == "CLKFBOUT") ? "_MULT_F" : + (name == "CLKOUT0" ? "_DIVIDE_F" : "_DIVIDE")), 1); + double phase = float_or_default(ci, name + "_PHASE", 1); + if (divide <= 1) { + no_count = true; + } else { + high = floor(divide / 2); + low = int(floor(divide) - high); + if (high != low) + edge = true; + if (name == "CLKOUT0" || name == "CLKFBOUT") + frac = floor(divide * 8) - floor(divide) * 8; + int phase_eights = floor((phase / 360) * divide * 8); + phasemux = phase_eights % 8; + delaytime = phase_eights / 8; + } + bool used = false; + if (name == "DIVCLK" || name == "CLKFBOUT") { + used = true; + } else { + used = ci->getPort(ctx->id(name)) != nullptr; + } + if (name == "DIVCLK") { + write_int_vector("DIVCLK_DIVCLK_HIGH_TIME[5:0]", high, 6); + write_int_vector("DIVCLK_DIVCLK_LOW_TIME[5:0]", low, 6); + write_bit("DIVCLK_DIVCLK_EDGE[0]", edge); + write_bit("DIVCLK_DIVCLK_NO_COUNT[0]", no_count); + } else if (used) { + auto is_clkout_5_or_6 = name == "CLKOUT5" || name == "CLKOUT6"; + auto is_clkout0 = name == "CLKOUT0"; + auto is_clkfbout = name == "CLKFBOUT"; + + if ((is_clkout0 || is_clkfbout) && frac != 0) { + --high; + --low; + + auto frac_shifted = frac >> 1; + // CLKOUT0 controls CLKOUT5_CLKOUT2, CLKFBOUT controls CLKOUT6_CLKOUT2 + std::string frac_conf_name = is_clkout0 ? "CLKOUT5_CLKOUT2_" : "CLKOUT6_CLKOUT2_"; + + if (1 <= frac_shifted) { + write_bit(frac_conf_name + "FRACTIONAL_FRAC_WF_F[0]"); + write_int_vector(frac_conf_name + "FRACTIONAL_PHASE_MUX_F[1:0]", frac_shifted, 2); + } + } + + write_bit(name + "_CLKOUT1_OUTPUT_ENABLE[0]"); + write_int_vector(name + "_CLKOUT1_HIGH_TIME[5:0]", high, 6); + write_int_vector(name + "_CLKOUT1_LOW_TIME[5:0]", low, 6); + + auto phase_mux_feature = name + (is_clkout_5_or_6 ? "_CLKOUT2_FRACTIONAL_PHASE_MUX_F[0]" : "_CLKOUT2_PHASE_MUX[0]"); + write_int_vector(name + "_CLKOUT1_PHASE_MUX[2:0]", phasemux, 3); + + auto edge_feature = name + (is_clkout_5_or_6 ? "_CLKOUT2_FRACTIONAL_EDGE[0]" : "_CLKOUT2_EDGE[0]"); + write_bit(edge_feature, edge); + + auto no_count_feature = name + (is_clkout_5_or_6 ? "_CLKOUT2_FRACTIONAL_NO_COUNT[0]" : "_CLKOUT2_NO_COUNT[0]"); + write_bit(no_count_feature, no_count); + + auto delay_time_feature = name + (is_clkout_5_or_6 ? "_CLKOUT2_FRACTIONAL_DELAY_TIME[5:0]" : "_CLKOUT2_DELAY_TIME[5:0]"); + write_int_vector(delay_time_feature, delaytime, 6); + + if (!is_clkout_5_or_6 && frac != 0) { + write_bit(name + "_CLKOUT2_FRAC_EN[0]", 1); + write_bit(name + "_CLKOUT2_FRAC_WF_R[0]", 1); + write_int_vector(name + "_CLKOUT2_FRAC[2:0]", frac, 3); + } + } + } + + // From openXC7 + void write_mmcm(CellInfo *ci) + { + push(uarch->tile_name(ci->bel.tile)); + push("MMCME2_ADV"); + write_bit("IN_USE"); + // FIXME: should be INV not ZINV (XRay error?) + write_bit("ZINV_PWRDWN", bool_or_default(ci->params, id_IS_PWRDWN_INVERTED, false)); + write_bit("ZINV_RST", bool_or_default(ci->params, id_IS_RST_INVERTED, false)); + write_bit("ZINV_PSEN", bool_or_default(ci->params, id_IS_PSEN_INVERTED, false)); + write_bit("ZINV_PSINCDEC", bool_or_default(ci->params, id_IS_PSINCDEC_INVERTED, false)); + write_bit("INV_CLKINSEL", bool_or_default(ci->params, id_IS_CLKINSEL_INVERTED, false)); + write_mmcm_clkout("DIVCLK", ci); + write_mmcm_clkout("CLKFBOUT", ci); + write_mmcm_clkout("CLKOUT0", ci); + write_mmcm_clkout("CLKOUT1", ci); + write_mmcm_clkout("CLKOUT2", ci); + write_mmcm_clkout("CLKOUT3", ci); + write_mmcm_clkout("CLKOUT4", ci); + write_mmcm_clkout("CLKOUT5", ci); + write_mmcm_clkout("CLKOUT6", ci); + + std::string comp = str_or_default(ci->params, id_COMPENSATION, "INTERNAL"); + push("COMP"); + if (comp == "INTERNAL" || comp == "ZHOLD") { + // does not seem to make a difference in vivado + // both modes set this bit + write_bit("Z_ZHOLD"); + } else { + log_error("unsupported COMPENSATION type '%s' for MMCM (supported compensation types: INTERNAL, ZHOLD)\n", comp.c_str()); + } + pop(); + + auto clkfbout_mult = (int)float_or_default(ci, "CLKFBOUT_MULT_F", 5.000); + if (63 < clkfbout_mult) + log_error("MMCME2_ADV: CLKFBOUT_MULT_F must not be greater than 63"); + if (0 == clkfbout_mult) + log_error("MMCME2_ADV: CLKFBOUT_MULT_F must not be 0"); + write_int_vector("LKTABLE[39:0]", Xc7MMCM::lk_table[clkfbout_mult - 1], 40); + + std::string bandwidth = str_or_default(ci->params, id_BANDWIDTH, "OPTIMIZED"); + const uint16_t *filter_lookup; + if (bandwidth == "LOW") + filter_lookup = Xc7MMCM::filter_lookup_low; + else if (bandwidth == "LOW_SS") + filter_lookup = Xc7MMCM::filter_lookup_low_ss; + else if (bandwidth == "HIGH") + filter_lookup = Xc7MMCM::filter_lookup_high; + else + filter_lookup = Xc7MMCM::filter_lookup_optimized; + write_int_vector("FILTREG1_RESERVED[11:0]", filter_lookup[clkfbout_mult - 1], 12); + + // 0x9900 enables fractional counters + // only int counters would be 0x1 << 8 + // 0xffff enables everything, I suppose, this is what is used in xap888 + write_int_vector("POWER_REG_POWER_REG_POWER_REG[15:0]", 0xffff, 16); + write_bit("LOCKREG3_RESERVED[0]"); + write_int_vector("TABLE[9:0]", 0x3d4, 10); + pop(2); + } void write_dsp_cell(CellInfo *ci) { auto tile_name = uarch->tile_name(ci->bel.tile); diff --git a/himbaechel/uarch/xilinx/meta b/himbaechel/uarch/xilinx/meta index 57de9216..491aefcc 160000 --- a/himbaechel/uarch/xilinx/meta +++ b/himbaechel/uarch/xilinx/meta @@ -1 +1 @@ -Subproject commit 57de9216639b0670949664cfdc61b2679064eb7b +Subproject commit 491aefcc15be159efc8ad8bff2a1a4b93fe487fe diff --git a/himbaechel/uarch/xilinx/mmcm_tables.cc b/himbaechel/uarch/xilinx/mmcm_tables.cc new file mode 100644 index 00000000..98234443 --- /dev/null +++ b/himbaechel/uarch/xilinx/mmcm_tables.cc @@ -0,0 +1,365 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2019-2023 gatecat + * Copyright (C) 2023 Hans Baier + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "nextpnr.h" +#include "xilinx.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace Xc7MMCM { +extern const uint16_t filter_lookup_low [] = { + 0b0010111100, // 1 + 0b0010111100, // 2 + 0b0010111100, // 3 + 0b0010111100, // 4 + 0b0010011100, // .... + 0b0010101100, + 0b0010110100, + 0b0010001100, + 0b0010010100, + 0b0010010100, + 0b0010100100, + 0b0010111000, + 0b0010111000, + 0b0010111000, + 0b0010111000, + 0b0010000100, + 0b0010000100, + 0b0010000100, + 0b0010011000, + 0b0010011000, + 0b0010011000, + 0b0010011000, + 0b0010011000, + 0b0010011000, + 0b0010011000, + 0b0010101000, + 0b0010101000, + 0b0010101000, + 0b0010101000, + 0b0010101000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010110000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, + 0b0010001000, // .... + 0b0010001000, // 61 + 0b0010001000, // 62 + 0b0010001000, // 63 + 0b0010001000 // 64 +}; + +extern const uint16_t filter_lookup_low_ss [] = { + 0b0010111111, // 1 + 0b0010111111, // 2 + 0b0010111111, // 3 + 0b0010111111, // 4 + 0b0010011111, // .... + 0b0010101111, + 0b0010110111, + 0b0010001111, + 0b0010010111, + 0b0010010111, + 0b0010100111, + 0b0010111011, + 0b0010111011, + 0b0010111011, + 0b0010111011, + 0b0010000111, + 0b0010000111, + 0b0010000111, + 0b0010011011, + 0b0010011011, + 0b0010011011, + 0b0010011011, + 0b0010011011, + 0b0010011011, + 0b0010011011, + 0b0010101011, + 0b0010101011, + 0b0010101011, + 0b0010101011, + 0b0010101011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010110011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, + 0b0010001011, // .... + 0b0010001011, // 61 + 0b0010001011, // 62 + 0b0010001011, // 63 + 0b0010001011 // 64 +}; + +extern const uint16_t filter_lookup_high [] = { + 0b0010111100, // 1 + 0b0100111100, // 2 + 0b0101101100, // 3 + 0b0111011100, // 4 + 0b1101011100, // .... + 0b1110101100, + 0b1110110100, + 0b1111001100, + 0b1110010100, + 0b1111010100, + 0b1111100100, + 0b1101000100, + 0b1111100100, + 0b1111100100, + 0b1111100100, + 0b1111100100, + 0b1111010100, + 0b1111010100, + 0b1100000100, + 0b1100000100, + 0b1100000100, + 0b0101110000, + 0b0101110000, + 0b0101110000, + 0b0101110000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0010100000, + 0b0010100000, + 0b0010100000, + 0b0010100000, + 0b0010100000, + 0b0111000100, + 0b0111000100, + 0b0100110000, + 0b0100110000, + 0b0100110000, + 0b0100110000, + 0b0110000100, + 0b0110000100, + 0b0101011000, + 0b0101011000, + 0b0101011000, + 0b0010010000, + 0b0010010000, + 0b0010010000, // .... + 0b0010010000, // 61 + 0b0100101000, // 62 + 0b0011110000, // 63 + 0b0011110000 // 64 +}; + +extern const uint16_t filter_lookup_optimized [] = { + 0b0010111100, // 1 + 0b0100111100, // 2 + 0b0101101100, // 3 + 0b0111011100, // 4 + 0b1101011100, // .... + 0b1110101100, + 0b1110110100, + 0b1111001100, + 0b1110010100, + 0b1111010100, + 0b1111100100, + 0b1101000100, + 0b1111100100, + 0b1111100100, + 0b1111100100, + 0b1111100100, + 0b1111010100, + 0b1111010100, + 0b1100000100, + 0b1100000100, + 0b1100000100, + 0b0101110000, + 0b0101110000, + 0b0101110000, + 0b0101110000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0011010000, + 0b0010100000, + 0b0010100000, + 0b0010100000, + 0b0010100000, + 0b0010100000, + 0b0111000100, + 0b0111000100, + 0b0100110000, + 0b0100110000, + 0b0100110000, + 0b0100110000, + 0b0110000100, + 0b0110000100, + 0b0101011000, + 0b0101011000, + 0b0101011000, + 0b0010010000, + 0b0010010000, + 0b0010010000, // .... + 0b0010010000, // 61 + 0b0100101000, // 62 + 0b0011110000, // 63 + 0b0011110000 // 64 +}; + +extern const int64_t lk_table [] = { + // LockRefDly(5) LockFBDly(5) LockCnt(10) LockSatHigh(10) UnlockCnt(10) + 0b0011000110111110100011111010010000000001UL, + 0b0011000110111110100011111010010000000001UL, + 0b0100001000111110100011111010010000000001UL, + 0b0101101011111110100011111010010000000001UL, + 0b0111001110111110100011111010010000000001UL, + 0b1000110001111110100011111010010000000001UL, + 0b1001110011111110100011111010010000000001UL, + 0b1011010110111110100011111010010000000001UL, + 0b1100111001111110100011111010010000000001UL, + 0b1110011100111110100011111010010000000001UL, + 0b1111111111111000010011111010010000000001UL, + 0b1111111111110011100111111010010000000001UL, + 0b1111111111101110111011111010010000000001UL, + 0b1111111111101011110011111010010000000001UL, + 0b1111111111101000101011111010010000000001UL, + 0b1111111111100111000111111010010000000001UL, + 0b1111111111100011111111111010010000000001UL, + 0b1111111111100010011011111010010000000001UL, + 0b1111111111100000110111111010010000000001UL, + 0b1111111111011111010011111010010000000001UL, + 0b1111111111011101101111111010010000000001UL, + 0b1111111111011100001011111010010000000001UL, + 0b1111111111011010100111111010010000000001UL, + 0b1111111111011001000011111010010000000001UL, + 0b1111111111011001000011111010010000000001UL, + 0b1111111111010111011111111010010000000001UL, + 0b1111111111010101111011111010010000000001UL, + 0b1111111111010101111011111010010000000001UL, + 0b1111111111010100010111111010010000000001UL, + 0b1111111111010100010111111010010000000001UL, + 0b1111111111010010110011111010010000000001UL, + 0b1111111111010010110011111010010000000001UL, + 0b1111111111010010110011111010010000000001UL, + 0b1111111111010001001111111010010000000001UL, + 0b1111111111010001001111111010010000000001UL, + 0b1111111111010001001111111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL, + 0b1111111111001111101011111010010000000001UL +}; + +} + +NEXTPNR_NAMESPACE_END +