diff --git a/flake.lock b/flake.lock index 64e2cd89..953c6de0 100644 --- a/flake.lock +++ b/flake.lock @@ -42,11 +42,11 @@ "poetry2nix": "poetry2nix" }, "locked": { - "lastModified": 1704383370, - "narHash": "sha256-r1g68fd//ylpOodTU1eTq3AY9AIlXl2x6SMKFNQb/YQ=", + "lastModified": 1704974373, + "narHash": "sha256-VFUAGHki3ml2AgHplx5GV6lH4zFaETYuLLvIvyXM6t0=", "owner": "lowRISC", "repo": "lowrisc-nix", - "rev": "09e6009e25a2d1b75c1b2612cad083275b876b3d", + "rev": "b1408676451386a567aad93cd94133aa01105f50", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 746bf7e0..f4783e61 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,9 @@ # Add extra packages we might need # Currently this contains spike deps.overlay_pkgs + (final: prev: { + inherit (lowrisc-nix.packages.${system}) lowrisc-toolchain-gcc-rv32imcb; + }) ]; }; @@ -45,6 +48,14 @@ ]; }; in { + legacyPackages.sw = let + rules = import ./rules {inherit pkgs;}; + common = pkgs.callPackage sw/c/common {inherit rules;}; + demo = pkgs.callPackage sw/c/demo {inherit rules common;}; + in { + inherit common demo; + }; + devShells.default = pkgs.mkShellNoCC { name = "labenv"; buildInputs = with pkgs; [ diff --git a/rules/cc.nix b/rules/cc.nix new file mode 100644 index 00000000..b5c55756 --- /dev/null +++ b/rules/cc.nix @@ -0,0 +1,175 @@ +{ + pkgs, + lib, + stdenv, + toolchain ? { + cc = "/no-toolchain-defined"; + cflags = []; + ld = "/no-toolchain-defined"; + ldflags = []; + asm = "/no-toolchain-defined"; + asmflags = []; + }, +}: let + include = { + src, + deps ? [], + }: + pkgs.runCommandLocal (builtins.baseNameOf src) {} '' + mkdir -p $out/include + + ARGS=() + for dep in $deps; do + if [ -d $dep/include ]; then + ln -s $src/include/* $out/include/ + fi + done + + ln -s ${src} $out/include/${builtins.baseNameOf src} + ''; + + # If a raw file path is specified, turn that into a header derivation. + autowrap_header = f: + if !(builtins.isPath f) + then f + else if lib.hasSuffix ".h" f + then include {src = f;} + else throw "unknown file type: ${f}"; + + # Turn all raw file paths into derivations. + # Files will be grouped so that source files can depend on header files. + autowrap_deps = deps: let + src = builtins.filter (f: builtins.isPath f && !(lib.hasSuffix ".h" f)) deps; + other = builtins.filter (f: !(builtins.isPath f) || lib.hasSuffix ".h" f) deps; + wrapped = map autowrap_header other; + src_drv = + map ( + f: + if lib.hasSuffix ".S" f + then + asm { + src = f; + deps = wrapped; + } + else if lib.hasSuffix ".c" f + then + object { + src = f; + deps = wrapped; + } + else throw "unknown file type: ${f}" + ) + src; + in + src_drv ++ wrapped; + + asm = { + src, + deps ? [], + extra-asmflags ? [], + asmflags ? toolchain.asmflags ++ extra-asmflags, + }: + stdenv.mkDerivation { + name = (lib.removeSuffix ".S" (builtins.baseNameOf src)) + ".o"; + inherit src asmflags; + inherit (toolchain) asm; + deps = autowrap_deps deps; + dontUnpack = true; + buildPhase = '' + ARGS=() + for dep in $deps; do + if [ -d $dep/include ]; then + ARGS+=(-I$dep/include) + fi + done + + mkdir -p $out/obj + $asm $asmflags -c -o $out/obj/$name $src ''${ARGS[@]} + ''; + }; + + object = { + src, + deps ? [], + extra-cflags ? [], + cflags ? toolchain.cflags ++ extra-cflags, + }: + stdenv.mkDerivation { + name = (lib.removeSuffix ".c" (builtins.baseNameOf src)) + ".o"; + inherit src cflags; + inherit (toolchain) cc; + deps = autowrap_deps deps; + dontUnpack = true; + buildPhase = '' + ARGS=() + for dep in $deps; do + if [ -d $dep/include ]; then + ARGS+=(-I$dep/include) + fi + done + + mkdir -p $out/obj + $cc $cflags -c -o $out/obj/$name $src ''${ARGS[@]} + ''; + }; + + static = { + name, + deps, + }: + stdenv.mkDerivation { + inherit name; + srcs = autowrap_deps deps; + dontUnpack = true; + buildPhase = '' + ARGS=() + for src in $srcs; do + if [ -d $src/obj ]; then + ARGS+=($src/obj/*) + fi + if [ -d $src/include ]; then + mkdir -p $out/include + ln -s $src/include/* $out/include/ + fi + done + + mkdir -p $out/lib + ar rcs $out/lib/$name ''${ARGS[@]} + ''; + }; + + binary = { + name, + deps ? [], + extra-ldflags ? [], + ldflags ? toolchain.ldflags ++ extra-ldflags, + }: + stdenv.mkDerivation { + inherit name ldflags; + inherit (toolchain) ld; + srcs = autowrap_deps deps; + dontUnpack = true; + buildPhase = '' + ARGS=() + for src in $srcs; do + if [ -d $src/obj ]; then + ARGS+=($src/obj/*) + fi + if [ -d $src/lib ]; then + ARGS+=($src/lib/*) + fi + done + + mkdir -p $out/bin + $ld $ldflags -o $out/bin/$name ''${ARGS[@]} + ''; + }; + + set = deps: + pkgs.symlinkJoin { + name = ""; + paths = autowrap_deps deps; + }; +in { + inherit asm include object static binary set; +} diff --git a/rules/default.nix b/rules/default.nix new file mode 100644 index 00000000..48982fe1 --- /dev/null +++ b/rules/default.nix @@ -0,0 +1,3 @@ +{pkgs}: { + cc = pkgs.callPackage ./cc.nix {}; +} diff --git a/sw/c/common/default.nix b/sw/c/common/default.nix new file mode 100644 index 00000000..75548ac2 --- /dev/null +++ b/sw/c/common/default.nix @@ -0,0 +1,64 @@ +{ + rules, + pkgs, + lowrisc-toolchain-gcc-rv32imcb, +}: let + toolchain = rec { + cc = "${lowrisc-toolchain-gcc-rv32imcb}/bin/riscv32-unknown-elf-gcc"; + cflags = ["-march=rv32imc" "-mabi=ilp32" "-mcmodel=medany" "-Wall" "-fvisibility=hidden" "-ffreestanding"]; + ld = cc; + ldflags = ["-nostartfiles" "-T" "${../../common/link.ld}"]; + asm = cc; + asmflags = ["-march=rv32imc"]; + }; + + rules_cc = rules.cc.override { + inherit toolchain; + }; + + passthru = with rules_cc; rec { + inherit toolchain rules_cc; + + crt0 = asm { + src = ./crt0.S; + deps = [./demo_system_regs.h]; + }; + + gpio = object { + src = ./gpio.c; + deps = [./gpio.h ./uart.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + extra-cflags = ["-O3"]; + }; + + uart = object { + src = ./uart.c; + deps = [./gpio.h ./uart.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + }; + + pwm = object { + src = ./pwm.c; + deps = [./gpio.h ./uart.h ./pwm.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + }; + + spi = object { + src = ./spi.c; + deps = [./gpio.h ./uart.h ./spi.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + }; + + timer = object { + src = ./timer.c; + deps = [./gpio.h ./uart.h ./timer.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + }; + + demo_system = object { + src = ./demo_system.c; + deps = [./gpio.h ./uart.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + }; + + common = static { + name = "common.a"; + deps = [crt0 gpio uart pwm spi timer ./demo_system.c] ++ [./gpio.h ./uart.h ./timer.h ./spi.h ./pwm.h ./dev_access.h ./demo_system.h ./demo_system_regs.h]; + }; + }; +in + passthru.common // passthru diff --git a/sw/c/demo/default.nix b/sw/c/demo/default.nix new file mode 100644 index 00000000..1d8a2158 --- /dev/null +++ b/sw/c/demo/default.nix @@ -0,0 +1,11 @@ +{ + rules, + common, + lowrisc-toolchain-gcc-rv32imcb, +}: +with common.rules_cc; { + hello_world = binary { + name = "hello_world"; + deps = [hello_world/main.c common]; + }; +}