Skip to content

Commit

Permalink
[lowrisc] Add a Nix shell for lowrisc internal use
Browse files Browse the repository at this point in the history
This adds a devShell that employees can use to bootstrap access to non-public
EDA tooling. Evaluation will fail without appropriate credentials to fetch
the repository 'lowrisc-nix-private'.

Disclaimer: EXPERIMENTAL

These shells will only be functional in the appropriate restricted environments.

This is an experiment at tracking dependencies on proprietary tooling that is
less out-of-band compared to simply assuming the underlying environment is
pre-populated. For obvious reasons, this will always have much weaker
reproducibility guarantees than freely-available software and open-source deps.
However we can still lean on Nix to make bootstrapping non-public environments
fast, ergonomic and hopefully reproducible within the constricted space.

Using a nix flake input that is a private repository, we can effectively pin a
version of the private dependencies (hash+timestamp etc) without exposing what
they are. As nix is lazily evaluated, these inputs will not attempt to be
fetched unless we evaluate an output which depends on them, and hence they
should happily co-exist with other flake attributes for most consumers.

To avoid the flake.lock in this repository from exposing the transitive deps of
the private input, that flake does not track it's inputs in the standard way.
Hence, impure evaluation mode is required when using these outputs.

e.g.
```
nix develop .#eda_shell_lowrisc --impure
nix develop .#eda_shell_lowrisc --impure --command bash -c \
"make -C dv/uvm/core_ibex SIMULATOR=xlm ITERATIONS=4 TEST=riscv_rand_instr_test"
```

Signed-off-by: Harry Callahan <hcallahan@lowrisc.org>
  • Loading branch information
hcallahan-lowrisc committed Mar 28, 2024
1 parent a557997 commit 350969a
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 20 deletions.
14 changes: 14 additions & 0 deletions dv/uvm/core_ibex/scripts/compile_tb.py
Expand Up @@ -14,6 +14,7 @@
from ibex_cmd import get_compile_opts
from scripts_lib import run_one
import riscvdv_interface
import nix_lib

import logging
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -112,6 +113,19 @@ def _main() -> int:
logger.warning(f"WARNING: Saw non-zero retcode while compiling testbench : logfile -> {md.tb_build_stdout}")
return retcode


# If in a nix shell, patch the compiled simulation executable to use our chosen shared libraries
if os.getenv("IBEX_NIX_SHELL_LIB") is not None:
if md.simulator == "xlm":
so = md.dir_tb / "xcelium.d" / "run.d" / "librun.so"

# nix_lib.patch_rpath(so) # Doesn't work
nix_lib.patch_dtneeded(so)

# Finally, strip the rpath of unecessary entries
cmd = ["patchelf", str(so), "--shrink-rpath"]
subprocess.check_output(cmd)

return 0


Expand Down
48 changes: 48 additions & 0 deletions dv/uvm/core_ibex/scripts/nix_lib.py
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

import os
import subprocess
import pathlib3x as pathlib


import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


def patch_rpath(so: pathlib.Path):
"""Patch the rpath of the simulation executable to resolve stdenv.cc correctly."""
nix_gcc_lib_path = os.getenv("IBEX_NIX_SHELL_LIB")

# Get the old rpath
cmd = ["patchelf", "--print-rpath", str(so)]
old_rpath = subprocess.check_output(cmd).decode()
logger.warning(f"Old rpath : {old_rpath}")

# Add the nix gcc lib path to the head of the shared library's RPATH
new_rpath_str = f"{nix_gcc_lib_path}:{old_rpath}"
cmd = ["patchelf", "--set-rpath", new_rpath_str, str(so)]
new_rpath_output = subprocess.check_output(cmd).decode()
logger.warning(f"Output of --set-rpath : {new_rpath_output}")

# Print the new rpath
cmd = ["patchelf", "--print-rpath", str(so)]
new_rpath = subprocess.check_output(cmd).decode()
logger.warning(f"New rpath : {new_rpath}")

def patch_dtneeded(so: pathlib.Path):
"""Patch some stdenv.cc shared library deps of the simulation .so to be static."""
# We need to setup a couple of .so deps to be static, as the 'xrun' utility
# uses it's own search paths when discovering shared-library deps for the librun.so
# when simulating.
nix_gcc_lib_path = os.getenv("IBEX_NIX_SHELL_LIB")

cmd1 = ["patchelf", "--replace-needed", "libstdc++.so.6", f"{nix_gcc_lib_path}/libstdc++.so.6", str(so)]
cmd2 = ["patchelf", "--replace-needed", "libgcc_s.so.1", f"{nix_gcc_lib_path}/libgcc_s.so.1", str(so)]

subprocess.check_output(cmd1)
subprocess.check_output(cmd2)

52 changes: 34 additions & 18 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 24 additions & 2 deletions flake.nix
Expand Up @@ -5,6 +5,11 @@
# The input 'lowrisc-nix' contains some common dependencies that can be used
# by lowRISC projects. There is also an associated public binary cache.
lowrisc-nix.url = "github:lowRISC/lowrisc-nix";
# The input 'lowrisc-nix-private' is access-controlled.
# Outputs which depend on this input are for internal use only, and will fail
# to evaluate without the appropriate credentials.
# All outputs which depend on this input are suffixed '_lowrisc'
lowrisc-nix-private.url = "git+ssh://git@github.com/lowRISC/lowrisc-nix-private.git";

nixpkgs.follows = "lowrisc-nix/nixpkgs";
flake-utils.follows = "lowrisc-nix/flake-utils";
Expand All @@ -29,6 +34,13 @@
};
};

# This import creates internal-use only outputs, which build on
# input attributes that cannot be fetched without appropriate credentials.
lr = import ./nix/lowrisc.nix {
inherit inputs pkgs system;
extraDependencies = sim_shared_lib_deps;
};

################
# DEPENDENCIES #
################
Expand Down Expand Up @@ -101,6 +113,16 @@
'';
};

# This shell uses mkShellNoCC as the stdenv CC can interfere with EDA tools.
eda_shell = pkgs.lib.makeOverridable pkgs.mkShellNoCC {
name = "ibex-devshell-eda";
buildInputs = ibex_runtime_deps;
nativeBuildInputs = ibex_project_deps;
shellHook = ''
${ibex_profile_common}
'';
};

syn_shell = shell.override (prev: {
name = "ibex-devshell-synthesis";
nativeBuildInputs = prev.nativeBuildInputs ++ ibex_syn.deps;
Expand All @@ -110,9 +132,9 @@
in {
devShells.${system} = {
default = inputs.self.devShells.${system}.shell;

inherit shell;
inherit eda_shell;
inherit syn_shell;
};
} // lr.devShells;
};
}
48 changes: 48 additions & 0 deletions nix/lowrisc.nix
@@ -0,0 +1,48 @@
# Copyright lowRISC Contributors.
# Licensed under the MIT License, see LICENSE for details.
# SPDX-License-Identifier: MIT
{
inputs,
pkgs,
system,
extraDependencies,
...
}: let

# Proprietary EDA deps
# These dependencies are behind the 'lowrisc-nix-private' repository, which is access-controlled.
lowriscEdaDeps = (
map (pkg: pkg.override {
# The 'extraDependencies' argument can be used to add deps to the wrapped environments the
# EDA tools run inside. Just adding the deps to the devShell environments is not sufficient, as
# the EDA tool wrappers end up shadowing the same paths with their own wrappers, and hence cannot
# see the additional deps.
inherit extraDependencies;
})
(with inputs.lowrisc-nix-private.packages.${system}; [vcs xcelium])
);

lowriscProfile = ''
# Xcelium
# When building the simulation executable with the EDA tooling wrapped in an FHSenv, we are
# depending on the stdenv.cc. Therefore, the appropriate shared libraries need to be
# located at runtime for these executables to run. The rpath is not set correctly for us to
# discover the correct libraries, and it does not appear to matter as when invoking the simulator
# the search paths of the xrun utility are used, not those of the librun.so library.
# However, setting the DT_NEEDED paths to be static/absolute does resolve correctly.
# Therefore, pass the correct search paths into the build here, and patchelf the librun.so object
# to setup DT_NEEDED correctly (in compile_tb.py) for the appropriate libs (libstdc++ / libgcc_s)
export IBEX_NIX_SHELL_LIB=${pkgs.stdenv.cc.cc.lib}/lib
'';

eda_shell_lowrisc = inputs.self.devShells.${system}.eda_shell.override (prev: {
name = "ibex-devshell-eda-lowrisc";
nativeBuildInputs = prev.nativeBuildInputs ++ lowriscEdaDeps;
shellHook = prev.shellHook + lowriscProfile;
});

in rec {
devShells = {
inherit eda_shell_lowrisc;
};
}

0 comments on commit 350969a

Please sign in to comment.