build: Add initial OSFPGA/FOEDAG build backend and blinky example.
This commit is contained in:
parent
b020d4cf62
commit
71a5ef2380
|
@ -34,7 +34,7 @@ clk = platform.request("clk")
|
||||||
led = platform.request("led")
|
led = platform.request("led")
|
||||||
module = Module()
|
module = Module()
|
||||||
module.clock_domains.cd_sys = ClockDomain("sys")
|
module.clock_domains.cd_sys = ClockDomain("sys")
|
||||||
module.cd_sys.clk.eq(clk)
|
module.comb += module.cd_sys.clk.eq(clk)
|
||||||
counter = Signal(26)
|
counter = Signal(26)
|
||||||
module.comb += led.eq(counter[25])
|
module.comb += led.eq(counter[25])
|
||||||
module.sync += counter.eq(counter + 1)
|
module.sync += counter.eq(counter + 1)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
from litex.build.osfpga.platform import OSFPGAPlatform
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file is part of LiteX.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from migen import *
|
||||||
|
|
||||||
|
from litex.build.generic_platform import Pins
|
||||||
|
from litex.build.osfpga import OSFPGAPlatform
|
||||||
|
|
||||||
|
# Minimal Platform ---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_io = [
|
||||||
|
("clk", 0, Pins(1)),
|
||||||
|
("led", 0, Pins(1))
|
||||||
|
]
|
||||||
|
|
||||||
|
class Platform(OSFPGAPlatform):
|
||||||
|
def __init__(self):
|
||||||
|
OSFPGAPlatform.__init__(self, device=None, io=_io) # FIXME: Add device support.
|
||||||
|
|
||||||
|
# Minimal Design -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
platform = Platform()
|
||||||
|
clk = platform.request("clk")
|
||||||
|
led = platform.request("led")
|
||||||
|
module = Module()
|
||||||
|
module.clock_domains.cd_sys = ClockDomain("sys")
|
||||||
|
module.comb += module.cd_sys.clk.eq(clk)
|
||||||
|
counter = Signal(26)
|
||||||
|
module.comb += led.eq(counter[25])
|
||||||
|
module.sync += counter.eq(counter + 1)
|
||||||
|
|
||||||
|
# Build --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
platform.build(module, build_name="blinky", run=True)
|
|
@ -0,0 +1,14 @@
|
||||||
|
#
|
||||||
|
# This file is part of LiteX.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
from migen.fhdl.module import Module
|
||||||
|
from migen.genlib.resetsync import AsyncResetSynchronizer
|
||||||
|
|
||||||
|
from litex.build.io import *
|
||||||
|
|
||||||
|
# OS-FPGA Special Overrides --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
osfpga_special_overrides = {}
|
|
@ -0,0 +1,137 @@
|
||||||
|
#
|
||||||
|
# This file is part of LiteX.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
import subprocess
|
||||||
|
from shutil import which, copyfile
|
||||||
|
|
||||||
|
from migen.fhdl.structure import _Fragment
|
||||||
|
|
||||||
|
from litex.build.generic_platform import *
|
||||||
|
from litex.build import tools
|
||||||
|
|
||||||
|
# Timing Constraints (.sdc) ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _build_sdc(clocks, vns, build_name):
|
||||||
|
sdc = []
|
||||||
|
for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid):
|
||||||
|
sdc.append(f"create_clock -name {vns.get_name(clk)} -period {str(period)} [get_ports {{{vns.get_name(clk)}}}]")
|
||||||
|
with open(f"{build_name}.sdc", "w") as f:
|
||||||
|
f.write("\n".join(sdc))
|
||||||
|
|
||||||
|
# Script -------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _build_tcl(name, device, files, build_name):
|
||||||
|
tcl = []
|
||||||
|
|
||||||
|
# Create Design.
|
||||||
|
tcl.append(f"create_design {build_name}")
|
||||||
|
|
||||||
|
# Set Device.
|
||||||
|
# TODO (Use Macro for now).
|
||||||
|
tcl.append("set_macro P1=10 P2=20")
|
||||||
|
|
||||||
|
# Add Include Path.
|
||||||
|
# TODO.
|
||||||
|
|
||||||
|
# Add Sources.
|
||||||
|
for f, typ, lib in files:
|
||||||
|
tcl.append(f"add_design_file {f}")
|
||||||
|
|
||||||
|
# Set Top Module.
|
||||||
|
tcl.append(f"set_top_module {build_name}")
|
||||||
|
|
||||||
|
# Add Timings Constraints.
|
||||||
|
tcl.append(f"add_constraint_file {build_name}.sdc")
|
||||||
|
|
||||||
|
# Run.
|
||||||
|
tcl.append("synth")
|
||||||
|
tcl.append("packing")
|
||||||
|
tcl.append("place")
|
||||||
|
tcl.append("route")
|
||||||
|
tcl.append("sta")
|
||||||
|
tcl.append("power")
|
||||||
|
tcl.append("bitstream")
|
||||||
|
|
||||||
|
# Generate .tcl.
|
||||||
|
with open("build.tcl", "w") as f:
|
||||||
|
f.write("\n".join(tcl))
|
||||||
|
|
||||||
|
# FOEDAGToolchain -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class FOEDAGToolchain:
|
||||||
|
attr_translate = {}
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.clocks = dict()
|
||||||
|
|
||||||
|
def build(self, platform, fragment,
|
||||||
|
build_dir = "build",
|
||||||
|
build_name = "top",
|
||||||
|
run = True,
|
||||||
|
**kwargs):
|
||||||
|
|
||||||
|
# Create build directory.
|
||||||
|
cwd = os.getcwd()
|
||||||
|
os.makedirs(build_dir, exist_ok=True)
|
||||||
|
os.chdir(build_dir)
|
||||||
|
|
||||||
|
# Finalize design
|
||||||
|
if not isinstance(fragment, _Fragment):
|
||||||
|
fragment = fragment.get_fragment()
|
||||||
|
platform.finalize(fragment)
|
||||||
|
|
||||||
|
# Generate verilog
|
||||||
|
v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
|
||||||
|
named_sc, named_pc = platform.resolve_signals(v_output.ns)
|
||||||
|
v_file = build_name + ".v"
|
||||||
|
v_output.write(v_file)
|
||||||
|
platform.add_source(v_file)
|
||||||
|
|
||||||
|
# Generate constraints file.
|
||||||
|
# IOs.
|
||||||
|
# TODO.
|
||||||
|
|
||||||
|
# Timings (.sdc)
|
||||||
|
_build_sdc(
|
||||||
|
clocks = self.clocks,
|
||||||
|
vns = v_output.ns,
|
||||||
|
build_name = build_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate build script (.tcl)
|
||||||
|
script = _build_tcl(
|
||||||
|
name = platform.devicename,
|
||||||
|
device = platform.device,
|
||||||
|
files = platform.sources,
|
||||||
|
build_name = build_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Run
|
||||||
|
if run:
|
||||||
|
foedag_sh = "foedag"
|
||||||
|
if which(foedag_sh) is None:
|
||||||
|
msg = "Unable to find FOEDAG toolchain, please:\n"
|
||||||
|
msg += "- Add FOEDAG toolchain to your $PATH."
|
||||||
|
raise OSError(msg)
|
||||||
|
|
||||||
|
if subprocess.call([foedag_sh, "--batch", "--script", "build.tcl"]) != 0:
|
||||||
|
raise OSError("Error occured during FOEDAG's script execution.")
|
||||||
|
|
||||||
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
return v_output.ns
|
||||||
|
|
||||||
|
def add_period_constraint(self, platform, clk, period):
|
||||||
|
clk.attr.add("keep")
|
||||||
|
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
|
||||||
|
if clk in self.clocks:
|
||||||
|
if period != self.clocks[clk]:
|
||||||
|
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
|
||||||
|
.format(self.clocks[clk], period))
|
||||||
|
self.clocks[clk] = period
|
|
@ -0,0 +1,38 @@
|
||||||
|
#
|
||||||
|
# This file is part of LiteX.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022 Florent Kermarrec <florent@enjoy-digital.fr>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from litex.build.generic_platform import GenericPlatform
|
||||||
|
from litex.build.osfpga import common, foedag
|
||||||
|
|
||||||
|
# OSFPGAPlatform -----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class OSFPGAPlatform(GenericPlatform):
|
||||||
|
bitstream_ext = ".bin"
|
||||||
|
|
||||||
|
def __init__(self, device, *args, toolchain="foedag", devicename=None, **kwargs):
|
||||||
|
GenericPlatform.__init__(self, device, *args, **kwargs)
|
||||||
|
self.devicename = devicename
|
||||||
|
if toolchain == "foedag":
|
||||||
|
self.toolchain = foedag.FOEDAGToolchain()
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown toolchain {toolchain}")
|
||||||
|
|
||||||
|
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
|
||||||
|
so = dict(common.osfpga_special_overrides)
|
||||||
|
so.update(special_overrides)
|
||||||
|
return GenericPlatform.get_verilog(self, *args,
|
||||||
|
special_overrides = so,
|
||||||
|
attr_translate = self.toolchain.attr_translate,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
|
def build(self, *args, **kwargs):
|
||||||
|
return self.toolchain.build(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def add_period_constraint(self, clk, period):
|
||||||
|
if clk is None: return
|
||||||
|
self.toolchain.add_period_constraint(self, clk, period)
|
Loading…
Reference in New Issue