mibuild: add initial Lattice Diamond support (with ECP3 Versa board platform skeleton)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 16 Mar 2015 11:01:27 +0000 (12:01 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 16 Mar 2015 18:02:34 +0000 (19:02 +0100)
mibuild/lattice/__init__.py [new file with mode: 0644]
mibuild/lattice/diamond.py [new file with mode: 0644]
mibuild/lattice/programmer.py [new file with mode: 0644]
mibuild/platforms/versa.py [new file with mode: 0644]

diff --git a/mibuild/lattice/__init__.py b/mibuild/lattice/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/mibuild/lattice/diamond.py b/mibuild/lattice/diamond.py
new file mode 100644 (file)
index 0000000..f39ddd3
--- /dev/null
@@ -0,0 +1,93 @@
+# This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import os, subprocess, shutil
+
+from migen.fhdl.structure import _Fragment
+from mibuild.generic_platform import *
+from mibuild import tools
+
+def _format_constraint(c):
+       if isinstance(c, Pins):
+               return ("LOCATE COMP ", " SITE " + "\"" + c.identifiers[0] + "\"")
+       elif isinstance(c, IOStandard):
+               return ("IOBUF PORT ", " IO_TYPE=" + c.name)
+       elif isinstance(c, Misc):
+               return c.misc
+
+def _format_lpf(signame, pin, others, resname):
+       fmt_c = [_format_constraint(c) for c in ([Pins(pin)] + others)]
+       r = ""
+       for pre, suf in fmt_c:
+               r += pre + "\""+ signame +"\"" +  suf + ";\n"
+       return r
+
+def _build_lpf(named_sc, named_pc):
+       r = "BLOCK RESETPATHS;\n"
+       r += "BLOCK ASYNCPATHS;\n"
+       for sig, pins, others, resname in named_sc:
+               if len(pins) > 1:
+                       for i, p in enumerate(pins):
+                               r += _format_lpf(sig + "[" + str(i) + "]", p, others, resname)
+               else:
+                       r += _format_lpf(sig, pins[0], others, resname)
+       if named_pc:
+               r += "\n" + "\n\n".join(named_pc)
+       return r
+
+def _build_files(device, sources, vincpaths, build_name):
+       tcl = []
+       tcl.append("prj_project new -name \"%s\" -impl \"implementation\" -dev %s -synthesis \"synplify\"" %(build_name, device))
+       for filename, language in sources:
+               tcl.append("prj_src add \"" + filename.replace("\\", "/") + "\"")
+       tcl.append("prj_run Synthesis -impl implementation -forceOne")
+       tcl.append("prj_run Translate -impl implementation")
+       tcl.append("prj_run Map -impl implementation")
+       tcl.append("prj_run PAR -impl implementation")
+       tcl.append("prj_run Export -impl implementation -task Bitgen")
+       tools.write_to_file(build_name + ".tcl", "\n".join(tcl))
+
+def _run_diamond(build_name, source, ver=None):
+       if sys.platform == "win32" or sys.platform == "cygwin":
+               build_script_contents = "REM Autogenerated by mibuild\n"
+               build_script_contents = "pnmainc " + build_name + ".tcl\n"
+               build_script_file = "build_" + build_name + ".bat"
+               tools.write_to_file(build_script_file, build_script_contents)
+               r = subprocess.call([build_script_file])
+               shutil.copy(os.path.join("implementation", build_name + "_implementation.bit"), build_name + ".bit")
+       else:
+               raise NotImplementedError()
+
+       if r != 0:
+               raise OSError("Subprocess failed")
+
+class LatticeDiamondPlatform(GenericPlatform):
+       bitstream_ext = ".bit"
+       def build(self, fragment, build_dir="build", build_name="top",
+                       diamond_path="/opt/Diamond", run=True):
+               tools.mkdir_noerror(build_dir)
+               os.chdir(build_dir)
+
+               if not isinstance(fragment, _Fragment):
+                       fragment = fragment.get_fragment()
+               self.finalize(fragment)
+
+               v_src, vns = self.get_verilog(fragment)
+               named_sc, named_pc = self.resolve_signals(vns)
+               v_file = build_name + ".v"
+               tools.write_to_file(v_file, v_src)
+               sources = self.sources + [(v_file, "verilog")]
+               _build_files(self.device, sources, self.verilog_include_paths, build_name)
+
+               tools.write_to_file(build_name + ".lpf", _build_lpf(named_sc, named_pc))
+
+               if run:
+                       _run_diamond(build_name, diamond_path)
+
+               os.chdir("..")
+
+               return vns
+
+       def add_period_constraint(self, clk, period):
+               # TODO: handle differential clk
+               self.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
\ No newline at end of file
diff --git a/mibuild/lattice/programmer.py b/mibuild/lattice/programmer.py
new file mode 100644 (file)
index 0000000..726e5e1
--- /dev/null
@@ -0,0 +1,51 @@
+import os, subprocess
+
+from mibuild.generic_programmer import GenericProgrammer
+from mibuild import tools
+
+# XXX Lattice programmer need an .xcf file, will need clean up and support for more parameters
+_xcf_template = """
+<?xml version='1.0' encoding='utf-8' ?>
+<!DOCTYPE              ispXCF  SYSTEM  "IspXCF.dtd" >
+<ispXCF version="3.4.1">
+       <Comment></Comment>
+       <Chain>
+               <Comm>JTAG</Comm>
+               <Device>
+                       <SelectedProg value="TRUE"/>
+                       <Pos>1</Pos>
+                       <Vendor>Lattice</Vendor>
+                       <Family>LatticeECP3</Family>
+                       <Name>LFE3-35EA</Name>
+                       <File>{bitstream_file}</File>
+                       <Operation>Fast Program</Operation>
+               </Device>
+       </Chain>
+       <ProjectOptions>
+               <Program>SEQUENTIAL</Program>
+               <Process>ENTIRED CHAIN</Process>
+               <OperationOverride>No Override</OperationOverride>
+               <StartTAP>TLR</StartTAP>
+               <EndTAP>TLR</EndTAP>
+               <VerifyUsercode value="FALSE"/>
+       </ProjectOptions>
+       <CableOptions>
+               <CableName>USB2</CableName>
+               <PortAdd>FTUSB-0</PortAdd>
+               <USBID>Dual RS232-HS A Location 0000 Serial A</USBID>
+               <JTAGPinSetting>
+                       TRST    ABSENT;
+                       ISPEN   ABSENT;
+               </JTAGPinSetting>
+       </CableOptions>
+</ispXCF>
+"""
+
+class LatticeProgrammer(GenericProgrammer):
+       needs_bitreverse = False
+
+       def load_bitstream(self, bitstream_file):
+               xcf_file = bitstream_file.replace(".bit", ".xcf")
+               xcf_content = _xcf_template.format(bitstream_file=bitstream_file)
+               tools.write_to_file(xcf_file, xcf_content)
+               subprocess.call(["pgrcmd", "-infile", xcf_file])
diff --git a/mibuild/platforms/versa.py b/mibuild/platforms/versa.py
new file mode 100644 (file)
index 0000000..6cd58bf
--- /dev/null
@@ -0,0 +1,34 @@
+# This file is Copyright (c) 2013 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+from mibuild.generic_platform import *
+from mibuild.lattice.diamond import LatticeDiamondPlatform
+from mibuild.lattice.programmer import LatticeProgrammer
+
+_io = [
+       ("clk100", 0, Pins("L5"), IOStandard("LVDS25")),
+
+       ("user_led", 0, Pins("Y20"), IOStandard("LVCMOS33")),
+       ("user_led", 1, Pins("AA21"), IOStandard("LVCMOS33")),
+       ("user_led", 2, Pins("U18"), IOStandard("LVCMOS33")),
+       ("user_led", 3, Pins("U19"), IOStandard("LVCMOS33")),
+       ("user_led", 4, Pins("W19"), IOStandard("LVCMOS33")),
+       ("user_led", 5, Pins("V19"), IOStandard("LVCMOS33")),
+       ("user_led", 6, Pins("AB20"), IOStandard("LVCMOS33")),
+       ("user_led", 7, Pins("AA20"), IOStandard("LVCMOS33")),
+
+       ("serial", 0,
+               Subsignal("tx", Pins("B11"), IOStandard("LVCMOS33")), # X4 IO0
+               Subsignal("rx", Pins("B12"), IOStandard("LVCMOS33")), # X4 IO1
+       ),
+]
+
+class Platform(LatticeDiamondPlatform):
+       default_clk_name = "clk100"
+       default_clk_period = 10
+
+       def __init__(self):
+               LatticeDiamondPlatform.__init__(self, "LFE3-35EA-6FN484C", _io)
+
+       def create_programmer(self):
+               return LatticeProgrammer()