artiq-fast: build gateware from a self-contained separate source derivation #23
|
@ -52,15 +52,14 @@ let
|
||||||
name = "artiq-board-${target}-${variant}-${version}";
|
name = "artiq-board-${target}-${variant}-${version}";
|
||||||
installPath = "${pkgs.python3Packages.python.sitePackages}/artiq/board-support/${target}-${variant}";
|
installPath = "${pkgs.python3Packages.python.sitePackages}/artiq/board-support/${target}-${variant}";
|
||||||
|
|
||||||
boardModule =
|
boardModule =
|
||||||
# Board packages are Python modules so that they get added to the ARTIQ Python
|
# Board packages are Python modules so that they get added to the ARTIQ Python
|
||||||
# environment, and artiq_flash finds them.
|
# environment, and artiq_flash finds them.
|
||||||
pkgs.python3Packages.toPythonModule (pkgs.stdenv.mkDerivation {
|
pkgs.python3Packages.toPythonModule (pkgs.stdenv.mkDerivation {
|
||||||
name = "${name}-firmware";
|
name = "${name}-firmware";
|
||||||
inherit version src;
|
inherit version src;
|
||||||
phases = [ "buildPhase" "installCheckPhase" "installPhase" "checkPhase" ];
|
phases = [ "buildPhase" "installCheckPhase" "installPhase" "checkPhase" ];
|
||||||
nativeBuildInputs = [
|
buildInputs = [
|
||||||
vivado
|
|
||||||
pkgs.gnumake pkgs.which
|
pkgs.gnumake pkgs.which
|
||||||
artiqpkgs.cargo
|
artiqpkgs.cargo
|
||||||
artiqpkgs.rustc
|
artiqpkgs.rustc
|
||||||
|
@ -94,11 +93,28 @@ let
|
||||||
'';
|
'';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
vivadoIdentifiers = import (pkgs.stdenv.mkDerivation {
|
||||||
|
name = "${name}-vivado-identifiers.nix";
|
||||||
|
src = "${boardModule}/src/gateware";
|
||||||
|
buildInputs = [ pkgs.python ];
|
||||||
|
phases = [ "unpackPhase" "buildPhase" ];
|
||||||
|
buildPhase = "python ${./scripts/get-identifiers.py} top.v > $out";
|
||||||
|
});
|
||||||
|
|
||||||
|
vivadoInput = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "${name}-vivado-input";
|
||||||
|
src = "${boardModule}/src/gateware";
|
||||||
|
buildInputs = [ pkgs.python ];
|
||||||
|
phases = [ "unpackPhase" "patchPhase" "installPhase" ];
|
||||||
|
patchPhase = "python ${./scripts/remove-identifiers.py} top.v";
|
||||||
|
installPhase = "cp -ar . $out";
|
||||||
|
};
|
||||||
|
|
||||||
vivadoInputArchive = pkgs.stdenv.mkDerivation {
|
vivadoInputArchive = pkgs.stdenv.mkDerivation {
|
||||||
name = "${name}-vivado-input.nar.base64";
|
name = "${name}-vivado-input.nar.base64";
|
||||||
buildInputs = [ pkgs.nix ];
|
buildInputs = [ pkgs.nix ];
|
||||||
phases = [ "installPhase" ];
|
phases = [ "installPhase" ];
|
||||||
installPhase = "nix-store --dump ${boardModule}/src/gateware | base64 -w0 > $out";
|
installPhase = "nix-store --dump ${vivadoInput} | base64 -w0 > $out";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Funnelling the source code through a Nix string allows dropping
|
# Funnelling the source code through a Nix string allows dropping
|
||||||
|
@ -110,22 +126,49 @@ let
|
||||||
));
|
));
|
||||||
|
|
||||||
# Depends on just Vivado and the generated Bitstream source
|
# Depends on just Vivado and the generated Bitstream source
|
||||||
vivadoOutput = pkgs.stdenvNoCC.mkDerivation {
|
vivadoCheckpoint = pkgs.stdenvNoCC.mkDerivation {
|
||||||
name = builtins.unsafeDiscardStringContext "${name}-vivado-output";
|
name = builtins.unsafeDiscardStringContext "${name}-vivado-checkpoint";
|
||||||
|
|
||||||
unpackPhase = "base64 -d < ${pureVivadoInputArchive} | nix-store --restore gateware";
|
unpackPhase = "base64 -d < ${pureVivadoInputArchive} | nix-store --restore gateware";
|
||||||
buildInputs = [ vivado pkgs.nix ];
|
buildInputs = [ vivado pkgs.nix ];
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
cd gateware
|
cd gateware
|
||||||
vivado -mode batch -source top.tcl
|
vivado -mode batch -source top_route.tcl
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
|
chmod a+r top_route.dcp
|
||||||
|
cp top_route.dcp $out
|
||||||
|
cp top_bitstream.tcl $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
vivadoOutput = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
name = builtins.unsafeDiscardStringContext "${name}-vivado-output";
|
||||||
|
src = vivadoCheckpoint;
|
||||||
|
buildInputs = [ vivado ];
|
||||||
|
buildPhase =
|
||||||
|
''
|
||||||
|
cat >top.tcl <<EOF
|
||||||
|
open_checkpoint top_route.dcp
|
||||||
|
'' +
|
||||||
|
(pkgs.lib.concatMapStrings ({ cell, init }:
|
||||||
|
''
|
||||||
|
set_property INIT ${init} [get_cell ${cell}]
|
||||||
|
''
|
||||||
|
) vivadoIdentifiers) +
|
||||||
|
''
|
||||||
|
source "top_bitstream.tcl"
|
||||||
|
EOF
|
||||||
|
vivado -mode batch -source top.tcl
|
||||||
|
'';
|
||||||
installPhase = ''
|
installPhase = ''
|
||||||
TARGET_DIR=$out/${builtins.unsafeDiscardStringContext installPath}
|
TARGET_DIR=$out/${builtins.unsafeDiscardStringContext installPath}
|
||||||
mkdir -p $TARGET_DIR
|
mkdir -p $TARGET_DIR
|
||||||
|
|
||||||
chmod a+r top.bit
|
chmod a+r top.bit
|
||||||
cp top.bit $TARGET_DIR
|
cp top.bit $TARGET_DIR/
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# temporarily disabled because there is currently always at least one Kasli bitstream
|
# temporarily disabled because there is currently always at least one Kasli bitstream
|
||||||
|
@ -146,7 +189,8 @@ let
|
||||||
check_log "Timing constraints are not met\."
|
check_log "Timing constraints are not met\."
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
in pkgs.buildEnv rec {
|
in
|
||||||
|
pkgs.buildEnv rec {
|
||||||
inherit name;
|
inherit name;
|
||||||
paths = [ boardModule vivadoOutput ];
|
paths = [ boardModule vivadoOutput ];
|
||||||
pathsToLink = [ "/${installPath}" ];
|
pathsToLink = [ "/${installPath}" ];
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
raise ValueError('Filename argument missing')
|
||||||
|
|
||||||
|
filename = sys.argv[1]
|
||||||
|
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
identifiers = re.findall(r"\.INIT\((\d+'d\d+\)\s*\)\s*identifier_str\d+)", data)
|
||||||
|
|
||||||
|
f = sys.stdout
|
||||||
|
f.write("[\n")
|
||||||
|
for identifier in identifiers:
|
||||||
|
m = re.match(r"(\d+)'d(\d+)\)\s*\)\s*(\S+)", identifier)
|
||||||
|
if m is not None:
|
||||||
|
cell = m.group(3)
|
||||||
|
# the literal must be converted to hex for Vivado Tcl `set_property`
|
||||||
|
init = "{}'h{:X}".format(m.group(1), int(m.group(2)))
|
||||||
|
f.write(' {{ cell = "{}"; init = "{}"; }}\n'.format(cell, init))
|
||||||
|
f.write("]\n")
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys, re
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
raise ValueError('Filename argument missing')
|
||||||
|
|
||||||
|
filename = sys.argv[1]
|
||||||
|
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
data = re.sub(r"\.INIT\((\d+'d\d+)\)(\s*)\)(\s*)identifier_str", r".INIT(32'hAAAAAAAA)\2)\3identifier_str", data)
|
||||||
|
|
||||||
|
with open(filename, 'w') as f:
|
||||||
|
f.write(data)
|
Loading…
Reference in New Issue