diff --git a/.gitea/workflows/npc-test.yml b/.gitea/workflows/npc-test.yml
new file mode 100644
index 0000000..9885423
--- /dev/null
+++ b/.gitea/workflows/npc-test.yml
@@ -0,0 +1,34 @@
+name: Run CTests within npc
+on: [push]
+
+jobs:
+  build-abstract-machine:
+    runs-on: nix
+    steps:
+      - uses: https://github.com/cachix/cachix-action@v14
+        with:
+          name: ysyx
+          authToken: '${{ secrets.CACHIX_SIGNING_KEY }}'
+      - uses: actions/checkout@v4
+        with:
+          submodules: true
+      - name: Cache sbt dependencies
+        id: cache-sbt-dependency
+        uses: actions/cache@v4
+        with:
+          path: npc/core
+          key: core
+      - name: Fetch sbt dependencies
+        if: steps.cache-sbt-dependency.outputs.cache-hit != 'true'
+        run: |
+          cd npc/core
+          sbt update
+      - name: Run tests
+        uses: https://github.com/nicknovitski/nix-develop@v1
+        with:
+          arguments: .#npc
+      - run: mkdir -p npc/build
+      - run: cd npc/build
+      - run: cmake $cmakeFlags ../
+      - run: make -j8
+      - run: ctest -V
diff --git a/flake.nix b/flake.nix
index d3a27fc..5ecb49c 100644
--- a/flake.nix
+++ b/flake.nix
@@ -50,6 +50,7 @@
           };
         };
         packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; };
+        packages.nemu-lib = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; defconfig = "riscv32-lib_defconfig"; };
         packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; };
 
         packages.am-kernels = crossPkgs.stdenv.mkDerivation rec {
@@ -82,6 +83,7 @@
           inputsFrom = [
             self.packages.${system}.nemu
           ];
+          IMAGES_PATH = "${self.packages.${system}.am-kernels}/share/binary";
         };
 
         devShells.npc = with pkgs; mkShell {
@@ -114,6 +116,10 @@
             verilator
             nvboard
           ] ++ self.checks.${system}.pre-commit-check.enabledPackages;
+
+          cmakeFlags = [
+            "-DDIFFTEST_LIB:string=${self.packages.${system}.nemu-lib}/lib/riscv32-nemu-interpreter-so"
+          ];
         };
       }
     );
diff --git a/nemu/configs/riscv32-lib_defconfig b/nemu/configs/riscv32-lib_defconfig
new file mode 100644
index 0000000..15d8758
--- /dev/null
+++ b/nemu/configs/riscv32-lib_defconfig
@@ -0,0 +1,72 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# NEMU Configuration Menu
+#
+# CONFIG_ISA_x86 is not set
+# CONFIG_ISA_mips32 is not set
+CONFIG_ISA_riscv=y
+# CONFIG_ISA_loongarch32r is not set
+CONFIG_ISA="riscv32"
+
+#
+# ISA-dependent Options for riscv
+#
+# CONFIG_RV64 is not set
+# CONFIG_RVE is not set
+# end of ISA-dependent Options for riscv
+
+CONFIG_ENGINE_INTERPRETER=y
+CONFIG_ENGINE="interpreter"
+CONFIG_MODE_SYSTEM=y
+# CONFIG_TARGET_NATIVE_ELF is not set
+CONFIG_TARGET_SHARE=y
+# CONFIG_TARGET_AM is not set
+
+#
+# Build Options
+#
+CONFIG_CC_GCC=y
+# CONFIG_CC_GPP is not set
+# CONFIG_CC_CLANG is not set
+CONFIG_CC="gcc"
+# CONFIG_CC_O0 is not set
+# CONFIG_CC_O1 is not set
+CONFIG_CC_O2=y
+# CONFIG_CC_O3 is not set
+CONFIG_CC_OPT="-O2"
+CONFIG_CC_LTO=y
+CONFIG_CC_DEBUG=y
+# CONFIG_CC_ASAN is not set
+# end of Build Options
+
+#
+# Testing and Debugging
+#
+CONFIG_LOG_TRACE=y
+# CONFIG_LOG_INFO is not set
+# CONFIG_LOG_WARNING is not set
+# CONFIG_LOG_ERROR is not set
+CONFIG_LOG_LEVEL=4
+# CONFIG_TRACE is not set
+CONFIG_DIFFTEST_REF_PATH="none"
+CONFIG_DIFFTEST_REF_NAME="none"
+# end of Testing and Debugging
+
+#
+# Memory Configuration
+#
+CONFIG_MBASE=0x80000000
+CONFIG_MSIZE=0x8000000
+CONFIG_PC_RESET_OFFSET=0
+# CONFIG_PMEM_MALLOC is not set
+CONFIG_PMEM_GARRAY=y
+CONFIG_MEM_RANDOM=y
+# end of Memory Configuration
+
+#
+# Miscellaneous
+#
+CONFIG_TIMER_GETTIMEOFDAY=y
+# CONFIG_TIMER_CLOCK_GETTIME is not set
+CONFIG_RT_CHECK=y
+# end of Miscellaneous
diff --git a/nemu/default.nix b/nemu/default.nix
index d3d5a70..052b4a7 100644
--- a/nemu/default.nix
+++ b/nemu/default.nix
@@ -2,7 +2,8 @@
   lib,
   stdenv,
   am-kernels,
-  dtc
+  dtc,
+  defconfig ? "alldefconfig",
 }:
 
 stdenv.mkDerivation rec {
@@ -31,27 +32,27 @@ stdenv.mkDerivation rec {
 
   configurePhase = ''
     export NEMU_HOME=$(pwd)
-    make alldefconfig
+    make ${defconfig}
   '';
 
   buildPhase = ''
     make
   '';
 
-  doCheck = true;
-  checkPhase = ''
+  doCheck = (defconfig == "alldefconfig");
+  checkPhase = if doCheck then ''
     export IMAGES_PATH=${am-kernels}/share/binary
     make test
-  '';
+  '' else "";
 
   installPhase = ''
     mkdir -p $out/bin
+    mkdir -p $out/lib
     make PREFIX=$out install
   '';
 
   shellHook = ''
     export NEMU_HOME=$(pwd)
-    export IMAGES_PATH=${am-kernels}/share/binary
   '';
 
   meta = with lib; {
diff --git a/nemu/scripts/build.mk b/nemu/scripts/build.mk
index f050197..a3b71d2 100644
--- a/nemu/scripts/build.mk
+++ b/nemu/scripts/build.mk
@@ -76,8 +76,13 @@ $(BINARY):: $(OBJS) $(ARCHIVES)
 	@$(LD) -o $@ $(OBJS) $(LDFLAGS) $(ARCHIVES) $(LIBS)
 
 install: $(BINARY)
+ifeq ($(SHARE),1)
+	@mkdir -p $(PREFIX)/lib
+	@cp $(BINARY) $(PREFIX)/lib/
+else
 	@mkdir -p $(PREFIX)/bin
 	@cp $(BINARY) $(PREFIX)/bin/
+endif
 
 clean:
 	-rm -rf $(BUILD_DIR)
diff --git a/npc/CMakeLists.txt b/npc/CMakeLists.txt
index 4874780..78dbfe5 100644
--- a/npc/CMakeLists.txt
+++ b/npc/CMakeLists.txt
@@ -15,6 +15,7 @@ option(BUILD_SIM_TARGET "Whether to build verilator simulation binary" ON)
 cmake_dependent_option(BUILD_SIM_NVBOARD_TARGET "Whether to build nvboard target" OFF "BUILD_SIM_TARGET" OFF)
 option(ENABLE_YSYX_GIT_TRACKER "Ysyx tracker support" OFF)
 set(TOPMODULE "Flow" CACHE STRING "Topmodule name in chisel")
+set(DIFFTEST_LIB "" CACHE STRING "Dynamic library file used as difftest reference")
 
 # -- Ysyx tracker, configure
 if(ENABLE_YSYX_GIT_TRACKER)
@@ -61,12 +62,6 @@ include_directories(include)
 
 add_subdirectory(csrc)
 
-add_test(
-  NAME V${TOPMODULE}
-  COMMAND V${TOPMODULE}
-    --no-bin -m ${PROJECT_SOURCE_DIR}/resource/addi.txt
-    --diff-lib /home/xin/repo/ysyx-workbench/nemu/build/riscv32-nemu-interpreter-so)
-
 # -- Add build tracking
 if(ENABLE_YSYX_GIT_TRACKER)
   add_custom_command(
diff --git a/npc/csrc/Flow/CMakeLists.txt b/npc/csrc/Flow/CMakeLists.txt
index 0764ea6..606fd73 100644
--- a/npc/csrc/Flow/CMakeLists.txt
+++ b/npc/csrc/Flow/CMakeLists.txt
@@ -9,3 +9,9 @@ verilate(V${TOPMODULE} TRACE COVERAGE THREADS
   VERILATOR_ARGS
     "--vpi"   # Enable VPI
 )
+
+add_test(
+  NAME V${TOPMODULE}
+  COMMAND V${TOPMODULE}
+    --no-bin -m ${PROJECT_SOURCE_DIR}/resource/addi.txt
+    --diff-lib ${DIFFTEST_LIB})