build: Add initial OSFPGA/FOEDAG build backend and blinky example.

This commit is contained in:
Florent Kermarrec 2022-05-16 16:25:47 +02:00
parent b020d4cf62
commit 71a5ef2380
6 changed files with 232 additions and 1 deletions

View File

@ -34,7 +34,7 @@ clk = platform.request("clk")
led = platform.request("led")
module = Module()
module.clock_domains.cd_sys = ClockDomain("sys")
module.cd_sys.clk.eq(clk)
module.comb += module.cd_sys.clk.eq(clk)
counter = Signal(26)
module.comb += led.eq(counter[25])
module.sync += counter.eq(counter + 1)

View File

@ -0,0 +1 @@
from litex.build.osfpga.platform import OSFPGAPlatform

41
litex/build/osfpga/blinky.py Executable file
View File

@ -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)

View File

@ -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 = {}

View File

@ -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

View File

@ -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)