From 839f9f51f61a09bb7397a122333bc212521543c5 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 8 Sep 2023 18:19:11 -0400 Subject: [PATCH] Improve robustness of ecppack JED file generator. Allow NOTEs to be generated. --- libtrellis/tools/ecppack.cpp | 68 +++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/libtrellis/tools/ecppack.cpp b/libtrellis/tools/ecppack.cpp index 5e2bb973..73f71e6f 100644 --- a/libtrellis/tools/ecppack.cpp +++ b/libtrellis/tools/ecppack.cpp @@ -42,15 +42,33 @@ uint16_t calc_checksum(std::string bytes) } uint32_t get_num_config_fuses(Trellis::ChipInfo &ci) { - if(ci.name == "LCMXO2-4000") { + if(ci.name == "LCMXO2-256") { + return (575 + 0 + 1)*128; // No UFM, 1 dummy page at end. + } else if(ci.name == "LCMXO2-640") { + return (1151 + 191 + 1)*128; // UFM is 0 bytes after end of CFG, 1 dummy page at end. + } else if(ci.name == "LCMXO2-1200" || ci.name == "LCMXO2-640U") { + return (2175 + 511 + 1)*128; // UFM is 0 bytes after end of CFG, 1 dummy page at end. + } else if(ci.name == "LCMXO2-2000" || ci.name == "LCMXO2-1200U") { + return (3198 + 639 + 1)*128; // UFM is 0 bytes after end of CFG, 1 dummy page at end. + } else if(ci.name == "LCMXO2-4000" || ci.name == "LCMXO2-2000U") { return (5758 + 767 + 1)*128; // UFM is 0 bytes after end of CFG, 1 dummy page at end. - } if(ci.name == "LCMXO2-7000") { + } else if(ci.name == "LCMXO2-7000") { return (9211 + 1 + 2046 + 2)*128; // UFM is 16 bytes after end of CFG, 2 dummy pages at end. } else { throw runtime_error(fmt("Can not extract number of config fuses from FPGA family " << ci.name)); } } +int num_digits(uint32_t num) { + int count = 0; + do { + num /= 10; + count++; + } while(num != 0); + + return count; +} + int main(int argc, char *argv[]) { using namespace Trellis; @@ -68,6 +86,7 @@ int main(int argc, char *argv[]) options.add_options()("svf", po::value(), "output SVF file"); options.add_options()("svf-rowsize", po::value(), "SVF row size in bits (default 8000)"); options.add_options()("jed", po::value(), "output JED file"); + options.add_options()("jed-note", po::value>(), "emit NOTE field in JED file"); options.add_options()("compress", "compress bitstream to reduce size"); options.add_options()("spimode", po::value(), "SPI Mode to use (fast-read, dual-spi, qspi)"); options.add_options()("background", "enable background reconfiguration in bitstream"); @@ -377,8 +396,15 @@ int main(int argc, char *argv[]) jed_file << "\x02*" << endl; // STX plus "design specification" (not filled in). full_checksum += calc_checksum("\x02*\n"); - // jed_file << "\x03" << hex << uppercase << setw(4) << full_checksum << endl; - // return 0; + + if (vm.count("jed-note")) { + ostringstream note_field; + for(auto &n: vm["jed-note"].as>()) { + note_field << "NOTE " << n << "*" << endl; + full_checksum += calc_checksum(note_field.str()); + jed_file << note_field.str(); + } + } ostringstream fusecnt_field; uint32_t fusecnt; @@ -389,6 +415,7 @@ int main(int argc, char *argv[]) return 1; } + // TODO: QP (package information not implied in textual representation). fusecnt_field << "QF" << fusecnt << '*' << endl; full_checksum += calc_checksum(fusecnt_field.str()); jed_file << fusecnt_field.str(); @@ -397,9 +424,26 @@ int main(int argc, char *argv[]) full_checksum += calc_checksum("G0*\n"); jed_file << "F0*" << endl; // Default fuse value. full_checksum += calc_checksum("F0*\n"); - jed_file << "L0" << endl; - full_checksum += calc_checksum("L0\n"); + // The JEDEC spec says leading 0s are optional. My own experience is + // that some programmers require leading 0s. + ostringstream list_field; + list_field << "L" << setw(num_digits(fusecnt)) << setfill('0') << 0 << endl; + full_checksum += calc_checksum(list_field.str()); + jed_file << list_field.str(); + + // Strip the leading comment- it wastes precious fuse space and + // some programmers (e.g STEP-MXO2) even rely on the preamble being + // the first bytes. + vector preamble = {0xFF, 0xFF, 0xBD, 0xB3, 0xFF, 0xFF }; + auto start_iter = search(begin(bitstream), end(bitstream), begin(preamble), end(preamble)); + + if(start_iter == end(bitstream)) { + cerr << "Could not extract preamble from bitstream" << endl; + return 1; + } + + auto start_offs = start_iter - bitstream.begin(); size_t i = 0; while(i < fusecnt/8) { @@ -407,9 +451,9 @@ int main(int argc, char *argv[]) size_t len = min(size_t(16), bitstream.size() - i); for (unsigned int j = 0; j < len; j++) { - uint8_t byte = uint8_t(bitstream[j + i]); + uint8_t byte = uint8_t(bitstream[j + i + start_offs]); checksum += reverse_byte(byte); - full_checksum += calc_checksum(std::bitset<8>{byte}.to_string()); + full_checksum += calc_checksum(bitset<8>{byte}.to_string()); jed_file << std::bitset<8>{byte}; } @@ -442,10 +486,10 @@ int main(int argc, char *argv[]) jed_file << "*" << endl; full_checksum += calc_checksum("*\n"); - ostringstream oss; - oss << "C" << hex << uppercase << setfill('0') << setw(4) << checksum << '*' << endl; - full_checksum += calc_checksum(oss.str()); - jed_file << oss.str(); + ostringstream checksum_field; + checksum_field << "C" << hex << uppercase << setfill('0') << setw(4) << checksum << '*' << endl; + full_checksum += calc_checksum(checksum_field.str()); + jed_file << checksum_field.str(); full_checksum += calc_checksum("\x03"); jed_file << "\x03" << hex << uppercase << setw(4) << setfill('0') << full_checksum << endl;