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