A Nix overlay for microchip development – intended for use in per-project environment flakes and/or home-manager configuration.
This provides a Nix package for Microchip PIC24/dsPIC33 development, comprising the MPLAB-X IDE and an associated installation of the XC16 compiler. It should be readily adaptable to provide similar packages for XC8 and/or XC32.
The packages included here are primarily the work of https://github.com/nyadiia, in a merge request raised against nixpkgs here: NixOS/nixpkgs#301317
I’ve refactored them a bit and packaged as a flake for instant gratification.
Currently this provides packages for the following, to meet my own immediate needs:
- xc16 v1.61
- xc16 v2.10
- mplab-x v6.15
- mplab-x v6.20
Adding additional versions is trivial – see instructions in the section Adding packages.
Pull requests adding additional packages and/or addressing any of the Improvement opportunities listed below (or others I haven’t considered) welcome.
The microchip compilers and mplab-x are dynamically linked binaries, requiring them to wrapped in an fhsEnv to be used on NixOS anyway. The compiler packages are unwrapped – installing via the mplab-x package wraps mplab-x and the compiler(s) in an fhsEnv so they all function as intended. I haven’t tested with microchip programmers etc. as yet, as my dev flow for current microchip projects doesn’t require them (hex image programming via a bootloader, and debugging via log trace rather than ICD).
Here’s a sample flake illustrating use from a microchip project. This installs MPLAB-X and XC16 in a bubble-wrapped environment. … (this overlay is hard-coded to x86_64-linux for now, so the forEachSupportedSystem stuff is aspirational )…
N.B. I’m using nixpkgs-unstable, as that’s what I’m tracking for NixOS on my dev machines. Because of the wrapping you’ll want to use a nixpkgs version with the same(?ish?) version of libc as your OS – otherwise this’ll fail to run. I.e. you may need to pass a different nixpkgs via inherit for compatibility with your own OS.
{
description = "A Nix-flake-based ceedling (c+ruby) environment";
#N.B. microchip compiler not nixpkg-ed, so assuming installed via system package manager
#N.B. ruby dependencies controlled via Gemfile, bundler and bundix pfte
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
microchip.url = "github:cormacc/nix-microchip";
};
outputs = { self, nixpkgs, microchip }:
let
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forEachSupportedSystem = f: nixpkgs.lib.genAttrs supportedSystems (system: f {
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
overlays = [
microchip.overlays.default
];
};
});
in
{
devShells = forEachSupportedSystem ({ pkgs }: {
default = pkgs.mkShell {
buildInputs = with pkgs; [
mplab-x #build dependencies - incorporates xc16 v1.61
cmake clang-tools #editor support (emacs/lsp)
];
};
});
};
}
The xc16 package has been refactored to put most of nyadiia’s work into a shared common.nix, and pass the version and installer archive hash in as parameters. So adding a new compiler version just requires creating a new file containing the version number and hash in ./pkgs/xc16.
See the example below for xc16 v1.61 (from ./pkgs/xc16/1.61.nix):
import ./common.nix {
version = "1.61";
hash = "sha256-Wi0vhJWt+WiNq41daf7e7tJeJmt3/M3t2TJbkJQTNEg=";
}
And then add a new line to ./pkgs/default.nix:
xc16_1_61 = pkgs.callPackage ./xc16/1.61.nix { };
The mplab-x-unwrapped has been refactored in a similar manner to xc16. I.e. adding a new version should just require creating a new file containing the version number and hash in ./pkgs/mplab-x-unwrapped/.
See the example below for MPLAB-X v6.20 (from ./pkgs/mplab-x-unwrapped/6.20.nix):
import ./common.nix {
version = "6.20";
hash = "sha256-zs77CsuKFUCGYwUiv4ZZLm8HZLskxm3zP8HoGMUHdWA=";
}
And then add a new line to ./pkgs/default.nix:
mplab-x-unwrapped_6_20 = pkgs.callPackage ./mplab-x-unwrapped/6.20.nix { };
Adding packages for the other Microchip compilers should (I believe) be as simple as copying the XC16 subtree and making some minor adaptations. Another github user, Fuwn
, has forked this repo and already done so for XC32 v4.40 – here.
Currently I’ve setup mplab-x to
Can probably do this at project level using a local overlay to override the default? Though would be nice to support multiple versions in the one devshell (e.g. for a multi-component build, or to facilitate comparisons)
Currently I (and most others working on Microchip projects?) rely on MPLAB-X for some aspects of build configuration. However it must be possible to do without – and pulling MPLAB-X out of the mix would make this flake more suitable for per-project dev flake use using direnv, which is my preferred flow.
Currently I’m installing this as part of my home-manager owned environment rather than per-project, purely because MPLAB-X is such a heavyweight dependency.
My nix-fu is weak. But my inner anal retentive is strong. On the other hand, life is short…
I want pkgs/default.nix to look something like this…
pkgs : rec {
import ./xc16 { };
import ./xc32 { };
import ./mplab-x { }
}
where pkgs/xc16/default.nix looks something like this
pkgs : rec {
xc16_2_10 = pkgs.callPackage ./2.10.nix { };
xc16_1_61 = pkgs.callPackage ./1.61.nix { };
xc16 = xc16_2_10; #i.e. default to latest
}
However my initial attempts have failed and pkgs/default.nix currently looks like this:
pkgs : rec {
xc16_2_10 = pkgs.callPackage ./xc16/2.10.nix { };
xc16_1_61 = pkgs.callPackage ./xc16/1.61.nix { };
xc16 = xc16_1_61; #i.e. default to the version we're using for current production builds
mplab-x-unwrapped = pkgs.callPackage ./mplab-x-unwrapped { };
mplab-x = pkgs.callPackage ./mplab-x { };
}
Sidestepping the license integration issues referenced above.
This is likely too much effort… though there’s some prior art for xc32 here: https://github.com/ElectricRCAircraftGuy/Microchip_XC32_Compiler
Building from mplab-x works fine in my limited testing, though I see some noisy error message along the following lines in the build console…
Error getting handle for device 0: Access denied (insufficient permissions) Error getting handle for device 1: Access denied (insufficient permissions) Error getting handle for device 2: Access denied (insufficient permissions) Error getting handle for device 3: Access denied (insufficient permissions) Error getting handle for device 5: Access denied (insufficient permissions) Error getting handle for device 6: Access denied (insufficient permissions) Error getting handle for device 7: Access denied (insufficient permissions) Error getting handle for device 8: Access denied (insufficient permissions) Error getting handle for device 9: Access denied (insufficient permissions) Error getting handle for device 10: Access denied (insufficient permissions) Error getting handle for device 11: Access denied (insufficient permissions) Error getting handle for device 12: Access denied (insufficient permissions) Error getting handle for device 13: Access denied (insufficient permissions) Error getting handle for device 14: Access denied (insufficient permissions) Error getting handle for device 15: Access denied (insufficient permissions) Error getting handle for device 16: Access denied (insufficient permissions) Error getting handle for device 17: Access denied (insufficient permissions) Error getting handle for device 18: Access denied (insufficient permissions) Error getting handle for device 19: Access denied (insufficient permissions) Error getting handle for device 20: Access denied (insufficient permissions) Error getting handle for device 21: Access denied (insufficient permissions) Error getting handle for device 22: Access denied (insufficient permissions) Error getting handle for device 23: Access denied (insufficient permissions) Error getting handle for device 24: Access denied (insufficient permissions)