libtrellis: ecppack improvements including direct SVF output Signed-off-by: David Shah <dave@ds0.me>
diff --git a/examples/versa5g/Makefile b/examples/versa5g/Makefile index 0fb6508..76ac577 100644 --- a/examples/versa5g/Makefile +++ b/examples/versa5g/Makefile
@@ -13,10 +13,9 @@ nextpnr-ecp5 --json $< --lpf ${CONSTR} --basecfg ../../misc/basecfgs/empty_lfe5um5g-45f.config --textcfg $@ --um5g-45k --package CABGA381 %.bit: %_out.config - ecppack $< $@ + ecppack --svf-rowsize 100000 --svf ${PROJ}.svf $< $@ -%.svf: %.bit - ../../tools/bit_to_svf.py $< $@ +${PROJ}.svf: ${PROJ}.bit prog: ${PROJ}.svf openocd -f ../../misc/openocd/ecp5-versa5g.cfg -c "transport select jtag; init; svf $<; exit"
diff --git a/examples/versa5g/demo.v b/examples/versa5g/demo.v index 4c14524..d1af9da 100644 --- a/examples/versa5g/demo.v +++ b/examples/versa5g/demo.v
@@ -1,4 +1,4 @@ -module top(input clk, output [7:0] led, output [13:0] disp, output clken); +module top(input clk, output [7:0] led, output [13:0] disp); localparam div_n = 25; @@ -25,6 +25,6 @@ assign led = {clk, ~pat_ctr}; assign disp = pat_ctr[0] ? 14'h3FFF : ~(display_pat[pat_ctr[$clog2(pat_len):1]]); - assign clken = 1'b1; + endmodule
diff --git a/examples/versa5g/versa.lpf b/examples/versa5g/versa.lpf index 0c25d54..3707ae1 100644 --- a/examples/versa5g/versa.lpf +++ b/examples/versa5g/versa.lpf
@@ -1,4 +1,4 @@ -LOCATE COMP "clk" SITE "A4"; +LOCATE COMP "clk" SITE "P3"; IOBUF PORT "clk" IO_TYPE=LVDS; LOCATE COMP "led[0]" SITE "E16"; @@ -49,5 +49,3 @@ IOBUF PORT "disp[12]" IO_TYPE=LVCMOS25; IOBUF PORT "disp[13]" IO_TYPE=LVCMOS25; -LOCATE COMP "clken" SITE "C12"; -IOBUF PORT "clken" IO_TYPE=LVCMOS33; \ No newline at end of file
diff --git a/libtrellis/include/Bitstream.hpp b/libtrellis/include/Bitstream.hpp index 58db319..7e00910 100644 --- a/libtrellis/include/Bitstream.hpp +++ b/libtrellis/include/Bitstream.hpp
@@ -7,7 +7,7 @@ #include <vector> #include <string> #include <stdexcept> - +#include <map> using namespace std; namespace Trellis { @@ -43,7 +43,7 @@ static Bitstream read_bit_py(string file); // Serialise a Chip back to a bitstream - static Bitstream serialise_chip(const Chip &chip); + static Bitstream serialise_chip(const Chip &chip, const map<string, string> options); // Deserialise a bitstream to a Chip Chip deserialise_chip(); @@ -54,6 +54,8 @@ // Python variant of the above, takes filename instead of ostream void write_bit_py(string file); + // Write bitstream as a vector of bytes + vector<uint8_t> get_bytes(); // Write a Lattice .bin file (bitstream only, for flash prog.) void write_bin(ostream &out);
diff --git a/libtrellis/src/Bitstream.cpp b/libtrellis/src/Bitstream.cpp index a172480..16d6c8c 100644 --- a/libtrellis/src/Bitstream.cpp +++ b/libtrellis/src/Bitstream.cpp
@@ -369,7 +369,7 @@ } } -Bitstream Bitstream::serialise_chip(const Chip &chip) { +Bitstream Bitstream::serialise_chip(const Chip &chip, const map<string, string> options) { BitstreamReadWriter wr; // Preamble wr.write_bytes(preamble.begin(), preamble.size()); @@ -386,7 +386,16 @@ // Set control reg 0 to 0x40000000 wr.write_byte(uint8_t(BitstreamCommand::LSC_PROG_CNTRL0)); wr.insert_zeros(3); - wr.write_uint32(0x40000000); + uint32_t ctrl0 = 0x40000000; + if (options.count("freq")) { + auto freq = find_if(frequencies.begin(), frequencies.end(), [&](const pair<string, uint8_t> &fp){ + return fp.first == options.at("freq"); + }); + if (freq == frequencies.end()) + throw runtime_error("bad frequency option " + options.at("freq")); + ctrl0 |= freq->second; + } + wr.write_uint32(ctrl0); // Init address wr.write_byte(uint8_t(BitstreamCommand::LSC_INIT_ADDRESS)); wr.insert_zeros(3); @@ -470,6 +479,19 @@ out.write(reinterpret_cast<const char *>(&(data[0])), data.size()); } +vector<uint8_t> Bitstream::get_bytes() { + vector<uint8_t> bytes; + bytes.push_back(0xFF); + bytes.push_back(0x00); + for (const auto &str : metadata) { + copy(str.begin(), str.end(), back_inserter(bytes)); + bytes.push_back(0x00); + } + bytes.push_back(0xFF); + copy(data.begin(), data.end(), back_inserter(bytes)); + return bytes; +} + void Bitstream::write_bin(ostream &out) { out.write(reinterpret_cast<const char *>(&(data[0])), data.size()); }
diff --git a/libtrellis/tools/ecppack.cpp b/libtrellis/tools/ecppack.cpp index deb20d8..e333467 100644 --- a/libtrellis/tools/ecppack.cpp +++ b/libtrellis/tools/ecppack.cpp
@@ -7,9 +7,18 @@ #include <stdexcept> #include <streambuf> #include <fstream> +#include <iomanip> using namespace std; +uint8_t reverse_byte(uint8_t byte) { + uint8_t rev = 0; + for (int i = 0; i < 8; i++) + if (byte & (1 << i)) + rev |= (1 << (7 - i)); + return rev; +} + int main(int argc, char *argv[]) { using namespace Trellis; @@ -22,10 +31,15 @@ options.add_options()("verbose,v", "verbose output"); options.add_options()("db", po::value<std::string>(), "Trellis database folder location"); options.add_options()("usercode", po::value<uint32_t>(), "USERCODE to set in bitstream"); + options.add_options()("idcode", po::value<uint32_t>(), "IDCODE to override in bitstream"); + options.add_options()("freq", po::value<std::string>(), "config frequency in MHz"); + options.add_options()("svf", po::value<std::string>(), "SVF file to write"); + options.add_options()("svf-rowsize", po::value<int>(), "SVF row size in bits (default 8000)"); + po::positional_options_description pos; options.add_options()("input", po::value<std::string>()->required(), "input textual configuration"); pos.add("input", 1); - options.add_options()("bit", po::value<std::string>()->required(), "output bitstream file"); + options.add_options()("bit", po::value<std::string>(), "output bitstream file"); pos.add("bit", 1); po::variables_map vm; @@ -78,11 +92,107 @@ } Chip c = cc.to_chip(); + if (vm.count("usercode")) + c.usercode = vm["usercode"].as<uint32_t>(); + if (vm.count("idcode")) + c.info.idcode = vm["idcode"].as<uint32_t>(); - ofstream bit_file(vm["bit"].as<string>(), ios::binary); - if (!bit_file) { - cerr << "Failed to open output file" << endl; + map<string, string> bitopts; + + if (vm.count("freq")) + bitopts["freq"] = vm["freq"].as<string>(); + + Bitstream b = Bitstream::serialise_chip(c, bitopts); + if (vm.count("bit")) { + ofstream bit_file(vm["bit"].as<string>(), ios::binary); + if (!bit_file) { + cerr << "Failed to open output file" << endl; + return 1; + } + b.write_bit(bit_file); } - Bitstream::serialise_chip(c).write_bit(bit_file); + + if (vm.count("svf")) { + vector<uint8_t> bitstream = b.get_bytes(); + int max_row_size = 8000; + if (vm.count("svf-rowsize")) + max_row_size = vm["svf-rowsize"].as<int>(); + if ((max_row_size % 8) != 0 || max_row_size <= 0) { + cerr << "SVF row size must be an exact positive number of bytes" << endl; + return 1; + } + ofstream svf_file(vm["svf"].as<string>()); + if (!svf_file) { + cerr << "Failed to open output SVF file" << endl; + return 1; + } + svf_file << "HDR\t0;" << endl; + svf_file << "HIR\t0;" << endl; + svf_file << "TDR\t0;" << endl; + svf_file << "TIR\t0;" << endl; + svf_file << "ENDDR\tDRPAUSE;" << endl; + svf_file << "ENDIR\tIRPAUSE;" << endl; + svf_file << "STATE\tIDLE;" << endl; + svf_file << "SIR\t8\tTDI (E0);" << endl; + svf_file << "SDR\t32\tTDI (00000000)" << endl; + svf_file << "\t\t\tTDO (" << setw(8) << hex << uppercase << setfill('0') << c.info.idcode << ")" << endl; + svf_file << "\t\t\tMASK (FFFFFFFF);" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (1C);" << endl; + svf_file << "SDR\t510\tTDI (3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" << endl; + svf_file << "\t\t\t\tFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (C6);" << endl; + svf_file << "SDR\t8\tTDI (00);" << endl; + svf_file << "RUNTEST\tIDLE\t2 TCK\t1.00E-02 SEC;" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (3C);" << endl; + svf_file << "SDR\t32\tTDI (00000000)" << endl; + svf_file << "\t\t\tTDO (00000000)" << endl; + svf_file << "\t\t\tMASK (0000B000);" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (46);" << endl; + svf_file << "SDR\t8\tTDI (01);" << endl; + svf_file << "RUNTEST\tIDLE\t2 TCK\t1.00E-02 SEC;" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (7A);" << endl; + svf_file << "RUNTEST\tIDLE\t2 TCK\t1.00E-02 SEC;" << endl; + size_t i = 0; + while(i < bitstream.size()) { + size_t len = min(size_t(max_row_size / 8), bitstream.size() - i); + if (len == 0) + break; + svf_file << "SDR\t" << setw(0) << dec << (8 * len) << "\tTDI ("; + svf_file << hex << uppercase << setw(2) << setfill('0'); + for (int j = len - 1; j >= 0; j--) { + svf_file << setw(2) << unsigned(reverse_byte(uint8_t(bitstream[j + i]))); + if (j % 40 == 0 && j != 0) + svf_file << endl << "\t\t\t"; + } + svf_file << ");" << endl; + i += len; + } + svf_file << endl; + svf_file << "SIR\t8\tTDI (FF);" << endl; + svf_file << "RUNTEST\tIDLE\t100 TCK\t1.00E-02 SEC;" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (C0);" << endl; + svf_file << "RUNTEST\tIDLE\t2 TCK\t1.00E-03 SEC;" << endl; + svf_file << "SDR\t32\tTDI (00000000)" << endl; + svf_file << "\t\t\tTDO (00000000)" << endl; + svf_file << "\t\t\tMASK (FFFFFFFF);" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (26);" << endl; + svf_file << "RUNTEST\tIDLE\t2 TCK\t2.00E-01 SEC;" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (FF);" << endl; + svf_file << "RUNTEST\tIDLE\t2 TCK\t1.00E-03 SEC;" << endl; + svf_file << endl; + svf_file << "SIR\t8\tTDI (3C);" << endl; + svf_file << "SDR\t32\tTDI (00000000)" << endl; + svf_file << "\t\t\tTDO (00000100)" << endl; + svf_file << "\t\t\tMASK (00002100);" << endl; + } + return 0; }