Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fud] Add save_temps option to Xilinx synthesis #851

Merged
merged 9 commits into from
Jan 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/fud/synthesis.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,14 @@ Hopefully someone will figure this out and document it in the future.
The first step in the Xilinx toolchain is to generate [an `xclbin` executable file][xclbin].
Here's an example of going all the way from a Calyx program to that:

fud e --to xclbin examples/futil/dot-product.futil
fud e examples/futil/dot-product.futil -o foo.xclbin

On our machines, compiling even a simple example like the above for simulation takes about 2 minutes, end to end.
On our machines, compiling even a simple example like the above for simulation takes about 5 minutes, end to end.
A failed run takes about 2 minutes to produce an error.

By default, the Xilinx tools run in a temporary directory that is deleted when `fud` finishes.
To instead keep the sandbox directory, use `-s xclbin.save_temps true`.
You can then find the results in a directory named `fud-out-N` for some number `N`.

### How it Works

Expand Down
14 changes: 8 additions & 6 deletions fud/fud/stages/remote_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,18 +120,20 @@ def run_remote(client: SourceType.UnTyped, tmpdir: SourceType.String):

run_remote(client, tmpdir)

def _close(self, client, remote_tmpdir):
def _close(self, client, remote_tmpdir, keep_tmpdir=False):
"""Close the SSH connection to the server.

Also removes the remote temporary directory.
Also removes the remote temporary directory, unless the
`keep_tmpdir` flag is set.
"""

@self.stage.step()
def finalize_ssh(client: SourceType.UnTyped, tmpdir: SourceType.String):
"""
Remove created temporary files and close ssh connection.
"""
client.exec_command(f"rm -r {tmpdir}")
if not keep_tmpdir:
client.exec_command(f"rm -r {tmpdir}")
client.close()

finalize_ssh(client, remote_tmpdir)
Expand Down Expand Up @@ -159,7 +161,7 @@ def copy_back(
copy_back(client, remote_tmpdir, local_tmpdir)
self._close(client, remote_tmpdir)

def close_and_get(self, client, remote_tmpdir, path):
def close_and_get(self, client, remote_tmpdir, path, keep_tmpdir=False):
"""Close the SSH connection and retrieve a single file.

Produces the resulting downloaded file.
Expand All @@ -176,8 +178,8 @@ def fetch_file(
dest_path = tmpfile.name
with self.SCPClient(client.get_transport()) as scp:
scp.get(src_path, dest_path)
return dest_path.open("rb")
return Path(dest_path)

local_path = fetch_file(client, remote_tmpdir)
self._close(client, remote_tmpdir)
self._close(client, remote_tmpdir, keep_tmpdir=keep_tmpdir)
return local_path
21 changes: 14 additions & 7 deletions fud/fud/stages/xilinx/xclbin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from fud.stages import Source, SourceType, Stage
from fud.stages.remote_context import RemoteExecution
from fud.stages.futil import FutilStage
from fud.utils import TmpDir, shell
from fud.utils import TmpDir, FreshDir, shell


class XilinxStage(Stage):
Expand Down Expand Up @@ -40,6 +40,10 @@ def __init__(self, config):
self.remote_exec = RemoteExecution(self)
self.temp_location = self.config["stages", self.name, "temp_location"]

# As a debugging aid, the pass can optionally preserve the
# (local or remote) sandbox where the Xilinx commands ran.
self.save_temps = bool(self.config["stages", self.name, "save_temps"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a comment explaining why this is added would prove useful for future readers. It wasn't obvious to me until I read your PR description.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call; added a short comment.


self.mode = self.config["stages", self.name, "mode"]
self.device = self.config["stages", self.name, "device"]

Expand Down Expand Up @@ -75,7 +79,10 @@ def copy_file(
"""Copy an input file."""
shutil.copyfile(src_path, Path(tmpdir) / dest_path)

tmpdir = Source(TmpDir(), SourceType.Directory)
tmpdir = Source(
FreshDir() if self.save_temps else TmpDir(),
SourceType.Directory,
)
for src_path, dest_path in input_files.items():
if not isinstance(src_path, Source):
src_path = Source(src_path, SourceType.Path)
Expand Down Expand Up @@ -173,9 +180,9 @@ def compile_xclbin(client: SourceType.UnTyped, tmpdir: SourceType.String):
def read_file(
tmpdir: SourceType.Directory,
name: SourceType.String,
) -> SourceType.Stream:
) -> SourceType.Path:
"""Read an output file."""
return Path(tmpdir.name) / name.data
return Path(tmpdir.name) / name

if self.remote_exec.use_ssh:
self.remote_exec.import_libs()
Expand All @@ -200,14 +207,14 @@ def read_file(
compile_xclbin(client, tmpdir)

if self.remote_exec.use_ssh:
xclbin = self.remote_exec.close_and_get(
return self.remote_exec.close_and_get(
client,
tmpdir,
"xclbin/kernel.xclbin",
keep_tmpdir=self.save_temps,
)
else:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super nit: else not really needed here, but understand some people like the symmetry.

xclbin = read_file(
return read_file(
tmpdir,
Source("xclbin/kernel.xclbin", SourceType.String),
)
return xclbin
25 changes: 25 additions & 0 deletions fud/fud/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ def remove(self):


class TmpDir(Directory):
"""A temporary directory that is automatically deleted."""

def __init__(self):
self.tmpdir_obj = TemporaryDirectory()
self.name = self.tmpdir_obj.name
Expand All @@ -82,6 +84,29 @@ def __str__(self):
return self.name


class FreshDir(Directory):
"""A new empty directory for saving results into.

The directory is created in the current working directory with an
arbitrary name. This way, `FreshDir` works like `TmpDir` except the
directory is not automatically removed. (It can still be manually
deleted, of course.)
"""

def __init__(self):
# Select a name that doesn't exist.
i = 0
while True:
name = "fud-out-{}".format(i)
if not os.path.exists(name):
break
i += 1

# Create the directory.
os.mkdir(name)
self.name = os.path.abspath(name)


class Conversions:
@staticmethod
def path_to_directory(data):
Expand Down