Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JEDEC file support #233

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 157 additions & 0 deletions libtrellis/tools/ecppack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,46 @@ uint32_t convert_hexstring(std::string value_str)
return uint32_t(strtoul(value_str.c_str(), nullptr, 0));
}

uint16_t calc_checksum(std::string bytes)
{
uint16_t checksum = 0;

for(auto &c: bytes)
{
checksum += c;
}

return checksum;
}

uint32_t get_num_config_fuses(Trellis::ChipInfo &ci) {
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.
} 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;
Expand All @@ -45,6 +85,8 @@ int main(int argc, char *argv[])
options.add_options()("freq", po::value<std::string>(), "config frequency in MHz");
options.add_options()("svf", po::value<std::string>(), "output SVF file");
options.add_options()("svf-rowsize", po::value<int>(), "SVF row size in bits (default 8000)");
options.add_options()("jed", po::value<std::string>(), "output JED file");
options.add_options()("jed-note", po::value<vector<std::string>>(), "emit NOTE field in JED file");
options.add_options()("compress", "compress bitstream to reduce size");
options.add_options()("spimode", po::value<std::string>(), "SPI Mode to use (fast-read, dual-spi, qspi)");
options.add_options()("background", "enable background reconfiguration in bitstream");
Expand Down Expand Up @@ -338,5 +380,120 @@ int main(int argc, char *argv[])
}
}

if (vm.count("jed")) {
// Create JTAG bitstream without SPI flash related settings, as these
// seem to confuse the chip sometimes when configuring over JTAG
if (!bitopts.empty() && !(bitopts.size() == 1 && bitopts.count("compress"))) {
bitopts.erase("spimode");
bitopts.erase("freq");
b = Bitstream::serialise_chip(c, bitopts);
}

vector<uint8_t> bitstream = b.get_bytes();
ofstream jed_file(vm["jed"].as<string>(), ios_base::binary);
uint16_t checksum = 0;
uint16_t full_checksum = 0;

jed_file << "\x02*" << endl; // STX plus "design specification" (not filled in).
full_checksum += calc_checksum("\x02*\n");

if (vm.count("jed-note")) {
ostringstream note_field;
for(auto &n: vm["jed-note"].as<vector<string>>()) {
note_field << "NOTE " << n << "*" << endl;
full_checksum += calc_checksum(note_field.str());
jed_file << note_field.str();
}
}

ostringstream fusecnt_field;
uint32_t fusecnt;
try {
fusecnt = get_num_config_fuses(c.info);
} catch (runtime_error &e) {
cerr << "Failed to extract JED file size: " << e.what() << endl;
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();

jed_file << "G0*" << endl; // Security fuse not supported yet.
full_checksum += calc_checksum("G0*\n");
jed_file << "F0*" << endl; // Default fuse value.
full_checksum += calc_checksum("F0*\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<uint8_t> 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) {
if(i < bitstream.size()) {
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 + start_offs]);
checksum += reverse_byte(byte);
full_checksum += calc_checksum(bitset<8>{byte}.to_string());
jed_file << std::bitset<8>{byte};
}

// Pad to 128 bits if at end of bitstream.
if(len < 16) {
for(unsigned int k = 0; k < 16 - len; k++) {
uint8_t byte = 0;
checksum += reverse_byte(byte);
full_checksum += calc_checksum(std::bitset<8>{byte}.to_string());
jed_file << std::bitset<8>{byte};
}
}


} else {
// Fill in remaining 128-bit rows
for(unsigned int j = 0; j < 16; j++) {
uint8_t byte = 0;
checksum += reverse_byte(byte);
full_checksum += calc_checksum(std::bitset<8>{byte}.to_string());
jed_file << std::bitset<8>{byte};
}
}

jed_file << endl;
full_checksum += calc_checksum("\n");
i += 16;
}

jed_file << "*" << endl;
full_checksum += calc_checksum("*\n");

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;
}

return 0;
}
Loading