From f9808d0a4046252f9889d66f9bd108e85cb8fd57 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 4 Mar 2024 11:57:52 +0800 Subject: [PATCH 01/16] before pa2 From 3c7c5f060ea3db05eee3d585cb3ecc51d959e34e Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 4 Mar 2024 11:59:50 +0800 Subject: [PATCH 02/16] build: initial support for building nemu, am-kernels with nix --- abstract-machine/Makefile | 27 ++++++------ nemu/flake.lock => flake.lock | 35 +++------------ flake.nix | 66 +++++++++++++++++++++++++++++ nemu/default.nix | 49 +++++++++++++++++++++ nemu/flake.nix | 80 ----------------------------------- nemu/scripts/config.mk | 5 ++- 6 files changed, 139 insertions(+), 123 deletions(-) rename nemu/flake.lock => flake.lock (54%) create mode 100644 flake.nix create mode 100644 nemu/default.nix delete mode 100644 nemu/flake.nix diff --git a/abstract-machine/Makefile b/abstract-machine/Makefile index 3a5d60a..1377857 100644 --- a/abstract-machine/Makefile +++ b/abstract-machine/Makefile @@ -47,33 +47,32 @@ endif ### Create the destination directory (`build/$ARCH`) WORK_DIR = $(shell pwd) -DST_DIR = $(WORK_DIR)/build/$(ARCH) +BUILD_DIR ?= $(WORK_DIR)/build +DST_DIR = $(BUILD_DIR)/$(ARCH) $(shell mkdir -p $(DST_DIR)) ### Compilation targets (a binary image or archive) -IMAGE_REL = build/$(NAME)-$(ARCH) +IMAGE_REL = $(DST_DIR)/$(NAME)-$(ARCH) IMAGE = $(abspath $(IMAGE_REL)) -ARCHIVE = $(WORK_DIR)/build/$(NAME)-$(ARCH).a +ARCHIVE = $(BUILD_DIR)/$(NAME)-$(ARCH).a ### Collect the files to be linked: object files (`.o`) and libraries (`.a`) OBJS = $(addprefix $(DST_DIR)/, $(addsuffix .o, $(basename $(SRCS)))) LIBS := $(sort $(LIBS) am klib) # lazy evaluation ("=") causes infinite recursions LINKAGE = $(OBJS) \ - $(addsuffix -$(ARCH).a, $(join \ - $(addsuffix /build/, $(addprefix $(AM_HOME)/, $(LIBS))), \ - $(LIBS) )) + $(addsuffix -$(ARCH).a, $(addprefix $(BUILD_DIR)/, $(LIBS))) ## 3. General Compilation Flags ### (Cross) compilers, e.g., mips-linux-gnu-g++ -AS = $(CROSS_COMPILE)gcc -CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ -LD = $(CROSS_COMPILE)ld -AR = $(CROSS_COMPILE)ar -OBJDUMP = $(CROSS_COMPILE)objdump -OBJCOPY = $(CROSS_COMPILE)objcopy -READELF = $(CROSS_COMPILE)readelf +AS ?= $(CROSS_COMPILE)gcc +CC ?= $(CROSS_COMPILE)gcc +CXX ?= $(CROSS_COMPILE)g++ +LD ?= $(CROSS_COMPILE)ld +AR ?= $(CROSS_COMPILE)ar +OBJDUMP ?= $(CROSS_COMPILE)objdump +OBJCOPY ?= $(CROSS_COMPILE)objcopy +READELF ?= $(CROSS_COMPILE)readelf ### Compilation flags INC_PATH += $(WORK_DIR)/include $(addsuffix /include/, $(addprefix $(AM_HOME)/, $(LIBS))) diff --git a/nemu/flake.lock b/flake.lock similarity index 54% rename from nemu/flake.lock rename to flake.lock index 5c64744..36b9a39 100644 --- a/nemu/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1709126324, + "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "d465f4819400de7c8d874d50b982301f28a84605", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1704722960, - "narHash": "sha256-mKGJ3sPsT6//s+Knglai5YflJUF2DGj7Ai6Ynopz0kI=", + "lastModified": 1709237383, + "narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "317484b1ead87b9c1b8ac5261a8d2dd748a0492d", + "rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8", "type": "github" }, "original": { @@ -34,31 +34,10 @@ "type": "github" } }, - "nur-xin": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1704680562, - "narHash": "sha256-ffec3HL8OgbHB/TvLHILvC3ylou6N+KDtrn4qYVV+U4=", - "ref": "refs/heads/master", - "rev": "8adf33b6fdd113c645d83feda28622a0b1ef9f83", - "revCount": 144, - "type": "git", - "url": "https://git.xinyang.life/xin/nur.git" - }, - "original": { - "type": "git", - "url": "https://git.xinyang.life/xin/nur.git" - } - }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "nur-xin": "nur-xin" + "nixpkgs": "nixpkgs" } }, "systems": { diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..35d3b94 --- /dev/null +++ b/flake.nix @@ -0,0 +1,66 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, ... }@inputs: with inputs; + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + crossPkgs = import nixpkgs { + localSystem = system; + crossSystem = { + config = "riscv32-none-elf"; + abi = "ilp32"; + }; + }; + in + { + packages.nemu = pkgs.callPackage ./nemu {}; + + packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { + pname = "am-kernels"; + version = "2024.02.18"; + + src = pkgs.fetchFromGitHub { + owner = "NJU-ProjectN"; + repo = "am-kernels"; + rev = "bb725d6f8223dd7de831c3b692e8c4531e9d01af"; + hash = "sha256-ZHdrw28TN8cMvhhzM469OV7cp0Yp+8yao855HP4+P4A="; + }; + + AM_HOME = pkgs.fetchFromGitHub { + owner = "xinyangli"; + repo = "abstract-machine"; + rev = "788595aac61c6b2f3b78ca8aa7d08dc33911bca4"; + hash = "sha256-YvWHIBP9tz3HL2TyibftvvQrpkWUDPnviCF4oyLmdjg="; + }; + + ARCH = "riscv32-nemu"; + + patchPhase = '' + sed -i 's/\/bin\/echo/echo/' tests/cpu-tests/Makefile + ''; + + buildPhase = '' + AS=$CC make -C tests/cpu-tests BUILD_DIR=$(pwd)/build ARCH=$ARCH --trace + ''; + + installPhase = '' + mkdir -p $out/bin + cp build/riscv32-nemu/*.bin $out/bin + ''; + + dontFixup = true; + }; + + devShells.default = pkgs.mkShell { + packages = with pkgs; [ + gdb + ] ++ builtins.attrValues self.packages.${system}; + }; + } + ); +} + diff --git a/nemu/default.nix b/nemu/default.nix new file mode 100644 index 0000000..3368ee0 --- /dev/null +++ b/nemu/default.nix @@ -0,0 +1,49 @@ +{ pkgs, + lib, + stdenv +}: + +stdenv.mkDerivation rec { + pname = "nemu"; + version = "2024-03-02"; + + src = ./.; + + NEMU_HOME = "/build/nemu"; + nativeBuildInputs = with pkgs; [ + gnumake + flex + bison + ]; + + buildInputs = with pkgs; [ + check + readline + libllvm + ]; + + configurePhase = '' + echo pwd=$(pwd) + make alldefconfig + ''; + + buildPhase = '' + make + ''; + + checkPhase = '' + make test + ''; + + installPhase = '' + mkdir -p $out/bin + make PREFIX=$out install + ''; + + meta = with lib; { + description = "NJU EMUlator, a full system x86/mips32/riscv32/riscv64 emulator for teaching"; + homepage = "https://github.com/NJU-ProjectN/nemu.git"; + license = with licenses; [ ]; + maintainers = with maintainers; [ ]; + }; +} diff --git a/nemu/flake.nix b/nemu/flake.nix deleted file mode 100644 index 325421f..0000000 --- a/nemu/flake.nix +++ /dev/null @@ -1,80 +0,0 @@ -{ - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - nur-xin = { - url = "git+https://git.xinyang.life/xin/nur.git"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, ... }@inputs: with inputs; - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages.${system} // - { nur.xin = nur-xin.legacyPackages.${system}; }; - in - { - devShells.default = with pkgs; mkShell { - packages = [ - clang-tools - rnix-lsp - bear - - gdb - jre - - gtkwave - ]; - - inputsFrom = [ self.packages.${system}.nemu ]; - }; - - packages.nemu = with pkgs; stdenv.mkDerivation rec { - pname = "nemu"; - version = "2024-01-02"; - - src = ./.; - - nativeBuildInputs = [ - gnumake - flex - bison - pkg-config - python3 # for testing - ]; - - buildInputs = [ - check - readline - libllvm - ]; - - configurePhase = '' - echo NEMU_HOME=$NEMU_HOME - echo pwd=$(pwd) - mkdir -p $(pwd)/kconfig - WORK_DIR=$(pwd) obj=$(pwd)/kconfig make --trace -e -f scripts/config.mk WORK_DIR=$(pwd) obj=$(pwd)/kconfig rv32_defconfig - ''; - - installPhase = '' - BUILD_DIR=$out make install - ''; - - checkPhase = '' - BUILD_DIR=$out make test - ''; - - NEMU_HOME = src; - - meta = with lib; { - description = "NJU EMUlator, a full system x86/mips32/riscv32/riscv64 emulator for teaching"; - homepage = "https://github.com/NJU-ProjectN/nemu.git"; - license = with licenses; [ ]; - maintainers = with maintainers; [ ]; - }; - }; - } - ); -} - diff --git a/nemu/scripts/config.mk b/nemu/scripts/config.mk index 0525ee3..01b9d1f 100644 --- a/nemu/scripts/config.mk +++ b/nemu/scripts/config.mk @@ -48,6 +48,9 @@ menuconfig: $(MCONF) $(CONF) $(FIXDEP) savedefconfig: $(CONF) $(Q)$< $(silent) --$@=configs/defconfig $(Kconfig) +alldefconfig: $(CONF) $(FIXDEP) + $(Q)$(CONF) $(silent) --$@ $(Kconfig) + %defconfig: $(CONF) $(FIXDEP) $(Q)$< $(silent) --defconfig=configs/$@ $(Kconfig) $(Q)$< $(silent) --syncconfig $(Kconfig) @@ -60,7 +63,7 @@ help: @echo ' savedefconfig - Save current config as configs/defconfig (minimal config)' distclean: clean - -@rm -rf $(rm-distclean) + -rm -rf $(rm-distclean) .PHONY: help distclean From e764133868068abddb01e38f36ebab40a50be439 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 6 Mar 2024 12:50:56 +0800 Subject: [PATCH 03/16] pa2.1: support RV32I instructions --- nemu/Makefile | 36 +++++++++++++++++++----- nemu/default.nix | 20 ++++++++++--- nemu/src/isa/riscv32/inst.c | 56 +++++++++++++++++++++++++++++++++++-- nemu/tests/Makefile | 1 + 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/nemu/Makefile b/nemu/Makefile index 0f360fe..e5e37c3 100644 --- a/nemu/Makefile +++ b/nemu/Makefile @@ -64,12 +64,34 @@ include $(NEMU_HOME)/scripts/native.mk endif include $(NEMU_HOME)/tests/Makefile -all-tests: TEST_OBJS = $(filter-out $(OBJ_DIR)/src/nemu-main.o, $(OBJS)) -all-tests: CFLAGS += $(shell pkg-config --cflags check) -all-tests: LDFLAGS += $(shell pkg-config --libs check) -all-tests: $(TEST_SRCS:%.c=$(OBJ_DIR)/%) +unit-tests: TEST_OBJS = $(filter-out $(OBJ_DIR)/src/nemu-main.o, $(OBJS)) +unit-tests: CFLAGS += $(shell pkg-config --cflags check) +unit-tests: LDFLAGS += $(shell pkg-config --libs check) +unit-tests: $(TEST_SRCS:%.c=$(OBJ_DIR)/%) -test: all-tests - @$(OBJ_DIR)/tests/expr_test +IMAGES = $(patsubst %.bin, %, $(shell find $(IMAGES_PATH) -type f -name '*.bin')) -.PHONY: test \ No newline at end of file +COLOR_RED = \033[1;31m +COLOR_GREEN = \033[1;32m +COLOR_BLUE = \033[1;34m +COLOR_NONE = \033[0m + +RESULT = .result.tmp +$(shell > $(RESULT)) # Clear result file + +$(IMAGES): %: %.bin $(BINARY) + @echo + TEST $(notdir $<) + @$(BINARY) -b $< >/dev/null 2>&1 || printf "\t%14s\n" $(notdir $<) >> $(RESULT) + +integration-tests: $(IMAGES) + @printf "$(COLOR_BLUE)INTEGRATION TEST:$(COLOR_NONE)\n\tALL: %s\n\tFAILED: %s\n" $(words $(IMAGES)) $(shell wc -l $(RESULT) | cut -f1 -d' ') + @test ! -s $(RESULT) || printf "$(COLOR_RED)FAILED:$(COLOR_NONE)\n" + @cat $(RESULT) + @test ! -s $(RESULT); \ + r=$$?; \ + $(RM) $(RESULT); \ + test $$r -eq 0 + +test: unit-tests integration-tests + +.PHONY: test unit-tests integration-tests \ No newline at end of file diff --git a/nemu/default.nix b/nemu/default.nix index 3368ee0..a886b79 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -1,6 +1,7 @@ { pkgs, lib, - stdenv + stdenv, + am-kernels }: stdenv.mkDerivation rec { @@ -9,21 +10,25 @@ stdenv.mkDerivation rec { src = ./.; - NEMU_HOME = "/build/nemu"; nativeBuildInputs = with pkgs; [ gnumake + pkg-config flex bison ]; buildInputs = with pkgs; [ - check readline libllvm ]; + checkInputs = [ + pkgs.check + am-kernels + ]; + configurePhase = '' - echo pwd=$(pwd) + export NEMU_HOME=$(pwd) make alldefconfig ''; @@ -31,7 +36,9 @@ stdenv.mkDerivation rec { make ''; + doCheck = true; checkPhase = '' + export IMAGES_PATH=${am-kernels}/share/images make test ''; @@ -40,6 +47,11 @@ stdenv.mkDerivation rec { make PREFIX=$out install ''; + shellHook = '' + export NEMU_HOME=$(pwd) + export IMAGES_PATH=${am-kernels}/share/images + ''; + meta = with lib; { description = "NJU EMUlator, a full system x86/mips32/riscv32/riscv64 emulator for teaching"; homepage = "https://github.com/NJU-ProjectN/nemu.git"; diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index cb0c44e..2a53ba5 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -13,6 +13,7 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ +#include "common.h" #include "local-include/reg.h" #include #include @@ -23,7 +24,7 @@ #define Mw vaddr_write enum { - TYPE_I, TYPE_U, TYPE_S, + TYPE_R, TYPE_I, TYPE_I_SHIFT, TYPE_U, TYPE_S, TYPE_B, TYPE_J, TYPE_N, // none }; @@ -31,7 +32,9 @@ enum { #define src2R() do { *src2 = R(rs2); } while (0) #define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0) #define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0) -#define immS() do { *imm = (SEXT(BITS(i, 31, 25), 7) << 5) | BITS(i, 11, 7); } while(0) +#define immS() do { *imm = SEXT(BITS(i, 31, 25), 7) << 5 | BITS(i, 11, 7); } while(0) +#define immB() do { *imm = SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 30, 25) << 5 | BITS(i, 11, 8) << 1 | BITS(i, 7, 7) << 11; } while(0) +#define immJ() do { *imm = SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 30, 21) << 1 | BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12; } while(0) static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) { uint32_t i = s->isa.inst.val; @@ -39,9 +42,19 @@ static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_ int rs2 = BITS(i, 24, 20); *rd = BITS(i, 11, 7); switch (type) { + case TYPE_R: src1R(); src2R(); break; case TYPE_I: src1R(); immI(); break; case TYPE_U: immU(); break; + case TYPE_J: immJ(); break; case TYPE_S: src1R(); src2R(); immS(); break; + case TYPE_B: src1R(); src2R(); immB(); break; + } +} + +static void do_branch(Decode *s, bool condition, word_t offset) { + if (condition) { + puts(s->logbuf); + s->dnpc = s->pc + offset; } } @@ -57,9 +70,48 @@ static int decode_exec(Decode *s) { } INSTPAT_START(); + INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm); + + INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {s->dnpc = s->pc + imm; R(rd) = s->pc + 4; } while(0)); + INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; } while(0)); + INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm)); + INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm)); + INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); + INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge , B, do_branch(s, (sword_t)src1 >= (sword_t)src2, imm)); + INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu , B, do_branch(s, src1 < src2, imm)); + INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu , B, do_branch(s, src1 >= src2, imm)); + + INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb , I, R(rd) = SEXT(Mr(src1 + imm, 1), 8)); + INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh , I, R(rd) = SEXT(Mr(src1 + imm, 2), 16)); + INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw , I, R(rd) = SEXT(Mr(src1 + imm, 4), 32)); INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu , I, R(rd) = Mr(src1 + imm, 1)); + INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu , I, R(rd) = Mr(src1 + imm, 2)); INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb , S, Mw(src1 + imm, 1, src2)); + INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh , S, Mw(src1 + imm, 2, src2)); + INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw , S, Mw(src1 + imm, 4, src2)); + + INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi , I, R(rd) = src1 + imm); + INSTPAT("??????? ????? ????? 010 ????? 00100 11", slti , I, R(rd) = (sword_t)src1 < (sword_t)imm ? 1 : 0); + INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu , I, R(rd) = src1 < imm ? 1 : 0); + INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori , I, R(rd) = src1 ^ imm); + INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori , I, R(rd) = src1 | imm); + INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi , I, R(rd) = src1 & imm); + INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli , I, R(rd) = src1 << imm); + INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli , I, R(rd) = src1 >> imm); + INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai , I, R(rd) = (sword_t)src1 >> (imm & 0x01F)); + + + INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add , R, R(rd) = src1 + src2); + INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub , R, R(rd) = src1 - src2); + INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll , R, R(rd) = src1 << src2); + INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt , R, R(rd) = (sword_t)src1 < (sword_t)src2 ? 1 : 0); + INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu , R, R(rd) = src1 < src2 ? 1 : 0); + INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor , R, R(rd) = src1 ^ src2); + INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl , R, R(rd) = src1 >> src2); + INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra , R, R(rd) = (sword_t)src1 >> (src2 & 0x01F)); + INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or , R, R(rd) = src1 | src2); + INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and , R, R(rd) = src1 & src2); INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0 INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv , N, INV(s->pc)); diff --git a/nemu/tests/Makefile b/nemu/tests/Makefile index 6804b41..2e8c784 100644 --- a/nemu/tests/Makefile +++ b/nemu/tests/Makefile @@ -7,3 +7,4 @@ $(OBJ_DIR)/%: %.c $(TEST_OBJS) app @$(CC) $(CFLAGS) -o $@.o -c $< @echo + LD $@ @$(LD) $(LIBS) $(LDFLAGS) -o $@ $(TEST_OBJS) $@.o + @$@ From 833cf7b6d142e0bb3def7cb3bf3dcd6a3726a440 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Thu, 7 Mar 2024 13:19:12 +0800 Subject: [PATCH 04/16] pa2.1: add M extension support, finish pa2.1 --- flake.nix | 18 ++++++++++++---- nemu/src/isa/riscv32/inst.c | 42 +++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/flake.nix b/flake.nix index 35d3b94..6656094 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,7 @@ }; in { - packages.nemu = pkgs.callPackage ./nemu {}; + packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { pname = "am-kernels"; @@ -44,12 +44,13 @@ ''; buildPhase = '' - AS=$CC make -C tests/cpu-tests BUILD_DIR=$(pwd)/build ARCH=$ARCH --trace + AS=$CC make -C tests/cpu-tests BUILD_DIR=$(pwd)/build ARCH=$ARCH ''; installPhase = '' - mkdir -p $out/bin - cp build/riscv32-nemu/*.bin $out/bin + mkdir -p $out/share/images $out/share/dump + cp build/riscv32-nemu/*.bin $out/share/images + cp build/riscv32-nemu/*.txt $out/share/dump ''; dontFixup = true; @@ -60,6 +61,15 @@ gdb ] ++ builtins.attrValues self.packages.${system}; }; + + devShells.nemu = pkgs.mkShell { + packages = with pkgs; [ + clang-tools + ]; + inputsFrom = [ + self.packages.${system}.nemu + ]; + }; } ); } diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index 2a53ba5..9c86937 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -1,17 +1,17 @@ /*************************************************************************************** -* Copyright (c) 2014-2022 Zihao Yu, Nanjing University -* -* NEMU is licensed under Mulan PSL v2. -* You can use this software according to the terms and conditions of the Mulan PSL v2. -* You may obtain a copy of Mulan PSL v2 at: -* http://license.coscl.org.cn/MulanPSL2 -* -* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -* -* See the Mulan PSL v2 for more details. -***************************************************************************************/ + * Copyright (c) 2014-2022 Zihao Yu, Nanjing University + * + * NEMU is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan + *PSL v2. You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY + *KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + *NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + ***************************************************************************************/ #include "common.h" #include "local-include/reg.h" @@ -36,7 +36,8 @@ enum { #define immB() do { *imm = SEXT(BITS(i, 31, 31), 1) << 12 | BITS(i, 30, 25) << 5 | BITS(i, 11, 8) << 1 | BITS(i, 7, 7) << 11; } while(0) #define immJ() do { *imm = SEXT(BITS(i, 31, 31), 1) << 20 | BITS(i, 30, 21) << 1 | BITS(i, 20, 20) << 11 | BITS(i, 19, 12) << 12; } while(0) -static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) { +static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, + word_t *imm, int type) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); int rs2 = BITS(i, 24, 20); @@ -100,8 +101,6 @@ static int decode_exec(Decode *s) { INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli , I, R(rd) = src1 << imm); INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli , I, R(rd) = src1 >> imm); INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai , I, R(rd) = (sword_t)src1 >> (imm & 0x01F)); - - INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add , R, R(rd) = src1 + src2); INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub , R, R(rd) = src1 - src2); INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll , R, R(rd) = src1 << src2); @@ -114,6 +113,17 @@ static int decode_exec(Decode *s) { INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and , R, R(rd) = src1 & src2); INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0 + + // "M" + INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul , R, R(rd) = src1 * src2); + INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh , R, R(rd) = (int64_t)(sword_t)src1 * (sword_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 010 ????? 01100 11", mulhsu , R, R(rd) = (int64_t)(sword_t)src1 * (uint64_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu , R, R(rd) = (uint64_t)src1 * (uint64_t)src2 >> 32); + INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div , R, R(rd) = (sword_t)src1 / (sword_t)src2); + INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu , R, R(rd) = src1 / src2); + INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem , R, R(rd) = (sword_t)src1 % (sword_t)src2); + INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu , R, R(rd) = src1 % src2); + INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv , N, INV(s->pc)); INSTPAT_END(); From 64f891308eca0372362d6ea1e197cc554a2a9aec Mon Sep 17 00:00:00 2001 From: tracer-ysyx Date: Thu, 7 Mar 2024 18:16:48 +0800 Subject: [PATCH 05/16] =?UTF-8?q?>=20configure(npc)=20=20ysyx=5F22040000?= =?UTF-8?q?=20=E6=9D=8E=E5=BF=83=E6=9D=A8=20=20Linux=20calcite=206.6.19=20?= =?UTF-8?q?#1-NixOS=20SMP=20PREEMPT=5FDYNAMIC=20Fri=20Mar=20=201=2012:35:1?= =?UTF-8?q?1=20UTC=202024=20x86=5F64=20GNU/Linux=20=20=2018:16:48=20=20up?= =?UTF-8?q?=20=20=203:36,=20=202=20users,=20=20load=20average:=200.28,=200?= =?UTF-8?q?.25,=200.34?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- npc/core/src/main/scala/Main.scala | 13 ++++++++++++- npc/core/src/main/scala/ProgramCounter.scala | 11 +++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 npc/core/src/main/scala/ProgramCounter.scala diff --git a/npc/core/src/main/scala/Main.scala b/npc/core/src/main/scala/Main.scala index c06bc8e..95b9e61 100644 --- a/npc/core/src/main/scala/Main.scala +++ b/npc/core/src/main/scala/Main.scala @@ -2,8 +2,10 @@ package npc import chisel3._ import chisel3.util.{MuxLookup, Fill, Decoupled, Counter, Queue, Reverse} +import chisel3.util.{SRAM} import chisel3.stage.ChiselOption -import npc.util.KeyboardSegController +import npc.util.{ KeyboardSegController, RegisterFile } +import flowpc.components.ProgramCounter class Switch extends Module { val io = IO(new Bundle { @@ -31,3 +33,12 @@ class Keyboard extends Module { io.segs := seg_handler.io.segs } +<<<<<<< Updated upstream +======= +class Flowpc extends Module { + val io = IO(new Bundle { }) + val register_file = new RegisterFile(readPorts = 2); + val pc = new ProgramCounter(32); + val adder = new SRAM() +} +>>>>>>> Stashed changes diff --git a/npc/core/src/main/scala/ProgramCounter.scala b/npc/core/src/main/scala/ProgramCounter.scala new file mode 100644 index 0000000..0687f9a --- /dev/null +++ b/npc/core/src/main/scala/ProgramCounter.scala @@ -0,0 +1,11 @@ +package flowpc.components +import chisel3._ +import chisel3.util.{Valid} + +class ProgramCounter (width: Int) extends Module { + val io = new Bundle { + val next_pc = Input(Flipped(Valid(UInt(width.W)))) + val pc = Output(UInt(width.W)) + } + io.pc := Mux(io.next_pc.valid, io.next_pc.bits, io.pc) +} From d67fb1138a03905020b5651ba7d7fb5c11524ef7 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 11 Mar 2024 21:41:45 +0800 Subject: [PATCH 06/16] npc: Register File --- npc/core/build.sbt | 5 +- npc/core/src/main/scala/RegisterFile.scala | 80 +++++++++++++++++----- npc/core/src/test/scala/RegisterFile.scala | 63 +++++++++++++++++ 3 files changed, 130 insertions(+), 18 deletions(-) create mode 100644 npc/core/src/test/scala/RegisterFile.scala diff --git a/npc/core/build.sbt b/npc/core/build.sbt index 792a764..16f31f3 100644 --- a/npc/core/build.sbt +++ b/npc/core/build.sbt @@ -6,10 +6,11 @@ val chiselVersion = "5.1.0" lazy val root = (project in file(".")) .settings( - name := "ChiselLearning", + name := "flow", libraryDependencies ++= Seq( "org.chipsalliance" %% "chisel" % chiselVersion, - "edu.berkeley.cs" %% "chiseltest" % "5.0.2" % "test" + "edu.berkeley.cs" %% "chiseltest" % "5.0.2" % "test", + "com.chuusai" %% "shapeless" % "2.3.3" ), scalacOptions ++= Seq( "-language:reflectiveCalls", diff --git a/npc/core/src/main/scala/RegisterFile.scala b/npc/core/src/main/scala/RegisterFile.scala index fbf8a94..a6c5a62 100644 --- a/npc/core/src/main/scala/RegisterFile.scala +++ b/npc/core/src/main/scala/RegisterFile.scala @@ -1,25 +1,73 @@ -package npc.util +package flowpc.components import chisel3._ +import chisel3.util.log2Ceil +import chisel3.util.UIntToOH +import chisel3.util.MuxLookup -class RegisterFile(readPorts: Int) extends Module { - require(readPorts >= 0) - val io = IO(new Bundle { - val writeEnable = Input(Bool()) - val writeAddr = Input(UInt(5.W)) - val writeData = Input(UInt(32.W)) - val readAddr = Input(Vec(readPorts, UInt(5.W))) - val readData = Output(Vec(readPorts, UInt(32.W))) - }) +class RegControl extends Bundle { + val writeEnable = Input(Bool()) - val regFile = RegInit(VecInit(Seq.fill(32)(0.U(32.W)))) - for (i <- 1 until 32) { - regFile(i) := regFile(i) + object WriteSelect extends ChiselEnum { + val rAluOut, rMemOut = Value + } + val writeSelect = Input(WriteSelect()) +} + +class RegFileData[T <: Data](size:Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { + val write = new Bundle { + val addr = Input(UInt(size.W)) + val data = Vec(numWritePorts, Input(tpe)) + } + val read = Vec(numReadPorts, new Bundle { + val rs = Input(UInt(size.W)) + val src = Output(tpe) + }) +} + +class RegFileInterface[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int) extends Bundle { + val control = new RegControl + val data = new RegFileData(size, tpe, numReadPorts, numWritePorts) +} + +class RegisterFileCore[T <: Data](size: Int, tpe: T, numReadPorts: Int) extends Module { + require(numReadPorts >= 0) + val writePort = IO(new Bundle { + val enable = Input(Bool()) + val addr = Input(UInt(log2Ceil(size).W)) + val data = Input(tpe) + }) + val readPorts = IO(Vec(numReadPorts, new Bundle { + val addr = Input(UInt(log2Ceil(size).W)) + val data = Output(tpe) + })) + + val regFile = RegInit(VecInit(Seq.fill(size)(0.U(tpe.getWidth.W)))) + val writeAddrOH = UIntToOH(writePort.addr) + for ((reg, i) <- regFile.zipWithIndex.tail) { + reg := Mux(writeAddrOH(i) && writePort.enable, writePort.data, reg) } - regFile(io.writeAddr) := Mux(io.writeEnable, io.writeData, regFile(io.writeAddr)) regFile(0) := 0.U - for (i <- 0 until readPorts) { - io.readData(i) := regFile(io.readAddr(i)) + for (readPort <- readPorts) { + readPort.data := regFile(readPort.addr) + } +} + +object RegisterFile { + def apply[T <: Data](size: Int, tpe: T, numReadPorts: Int, numWritePorts: Int): RegFileInterface[T] = { + val core = Module(new RegisterFileCore(size, tpe, numReadPorts)) + val _out = Wire(new RegFileInterface(size, tpe, numReadPorts, numWritePorts)) + val clock = core.clock + for (i <- 0 until numReadPorts) { + core.readPorts(i).addr := _out.data.read(i).rs + _out.data.read(i).src := core.readPorts(i).data + } + core.writePort.addr := _out.data.write.addr + core.writePort.data := MuxLookup(_out.control.writeSelect, 0.U)( + _out.control.WriteSelect.all.map(x => (x -> _out.data.write.data(x.asUInt).asUInt)) + ) + core.writePort.enable := _out.control.writeEnable + _out } } diff --git a/npc/core/src/test/scala/RegisterFile.scala b/npc/core/src/test/scala/RegisterFile.scala new file mode 100644 index 0000000..87be171 --- /dev/null +++ b/npc/core/src/test/scala/RegisterFile.scala @@ -0,0 +1,63 @@ +package flowpc + +import chisel3._ +import chiseltest._ +import org.scalatest.freespec.AnyFreeSpec +import chiseltest.simulator.WriteVcdAnnotation + +import flowpc.components._ +class RegisterFileSpec extends AnyFreeSpec with ChiselScalatestTester { + "RegisterFileCore" - { + "register 0 is always 0" in { + test(new RegisterFileCore(32, UInt(32.W), 2)) { c => + c.readPorts(0).addr.poke(0) + c.readPorts(1).addr.poke(0) + c.writePort.enable.poke(true) + c.writePort.addr.poke(0) + c.writePort.data.poke(0x1234) + + c.readPorts(0).data.expect(0) + c.readPorts(1).data.expect(0) + c.clock.step(2) + c.readPorts(0).data.expect(0) + c.readPorts(1).data.expect(0) + } + } + "register other than 0 can be written" in { + test(new RegisterFileCore(32, UInt(32.W), 2)) { c => + import scala.util.Random + val r = new Random() + for (i <- 1 until 32) { + val v = r.nextLong() & 0xFFFFFFFFL + c.readPorts(0).addr.poke(i) + c.writePort.enable.poke(true) + c.writePort.addr.poke(i) + c.writePort.data.poke(v) + + c.clock.step(1) + c.readPorts(0).data.expect(v) + } + } + } + } + "RegisterInterface" - { + "worked" in { + class Top extends Module { + val io = IO(new RegFileInterface(32, UInt(32.W), 2, 2)) + val rf = RegisterFile(32, UInt(32.W), 2, 2) + io :<>= rf + } + test(new Top).withAnnotations(Seq(WriteVcdAnnotation)) { c => + import c.io.control.WriteSelect._ + val writePort = rAluOut.litValue.toInt + c.io.control.writeEnable.poke(true) + c.io.control.writeSelect.poke(rAluOut) + c.io.data.write.addr.poke(5) + c.io.data.write.data(writePort).poke(0xcdef) + c.io.data.read(0).rs.poke(5) + c.clock.step(1) + c.io.data.read(0).src.expect(0xcdef) + } + } + } +} From c917083554d4aa61c5778658df351537712e3100 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 13 Mar 2024 16:54:00 +0800 Subject: [PATCH 07/16] pa2.2: add ITRACE buffer --- nemu/Kconfig | 4 ++++ nemu/include/cpu/decode.h | 1 - nemu/include/debug.h | 1 + nemu/include/utils.h | 3 +++ nemu/src/cpu/cpu-exec.c | 23 +++++++++++++++-------- nemu/src/isa/riscv32/inst.c | 2 +- nemu/src/utils/log.c | 15 +++++++++++++++ 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/nemu/Kconfig b/nemu/Kconfig index a1ed68e..20f7705 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -151,6 +151,10 @@ config ITRACE_COND string "Only trace instructions when the condition is true" default "true" +config ITRACE_BUFFER + depends on ITRACE + int "Buffer size for intruction trace (unit: number of instructions)" + default 10 config DIFFTEST depends on TARGET_NATIVE_ELF diff --git a/nemu/include/cpu/decode.h b/nemu/include/cpu/decode.h index 915bcf2..a17c888 100644 --- a/nemu/include/cpu/decode.h +++ b/nemu/include/cpu/decode.h @@ -23,7 +23,6 @@ typedef struct Decode { vaddr_t snpc; // static next pc vaddr_t dnpc; // dynamic next pc ISADecodeInfo isa; - IFDEF(CONFIG_ITRACE, char logbuf[128]); } Decode; // --- pattern matching mechanism --- diff --git a/nemu/include/debug.h b/nemu/include/debug.h index 087da4d..df88556 100644 --- a/nemu/include/debug.h +++ b/nemu/include/debug.h @@ -38,6 +38,7 @@ MUXDEF(CONFIG_TARGET_AM, printf(ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__), \ (fflush(stdout), fprintf(stderr, ANSI_FMT(format, ANSI_FG_RED) "\n", ## __VA_ARGS__))); \ IFNDEF(CONFIG_TARGET_AM, extern FILE* log_fp; fflush(log_fp)); \ + IFDEF(CONFIG_ITRACE, log_itrace_print()); \ extern void assert_fail_msg(); \ assert_fail_msg(); \ assert(cond); \ diff --git a/nemu/include/utils.h b/nemu/include/utils.h index 2cd1561..59bc9df 100644 --- a/nemu/include/utils.h +++ b/nemu/include/utils.h @@ -74,4 +74,7 @@ uint64_t get_time(); } while (0) +IFDEF(CONFIG_ITRACE, void log_itrace_print()); + + #endif diff --git a/nemu/src/cpu/cpu-exec.c b/nemu/src/cpu/cpu-exec.c index 1f2940f..72e1265 100644 --- a/nemu/src/cpu/cpu-exec.c +++ b/nemu/src/cpu/cpu-exec.c @@ -13,6 +13,7 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ +#include "utils.h" #include #include #include @@ -29,15 +30,17 @@ CPU_state cpu = {}; uint64_t g_nr_guest_inst = 0; static uint64_t g_timer = 0; // unit: us static bool g_print_step = false; +IFDEF(CONFIG_ITRACE, extern char logbuf[CONFIG_ITRACE_BUFFER][128]); +IFDEF(CONFIG_ITRACE, extern int logbuf_rear); void device_update(); bool wp_eval_all(); static void trace_and_difftest(Decode *_this, vaddr_t dnpc) { #ifdef CONFIG_ITRACE_COND - if (ITRACE_COND) { log_write("%s\n", _this->logbuf); } + if (ITRACE_COND) { log_write("%s\n", logbuf[logbuf_rear]); } #endif - if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(_this->logbuf)); } + if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(logbuf[logbuf_rear])); } IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc)); } @@ -47,8 +50,9 @@ static void exec_once(Decode *s, vaddr_t pc) { isa_exec_once(s); cpu.pc = s->dnpc; #ifdef CONFIG_ITRACE - char *p = s->logbuf; - p += snprintf(p, sizeof(s->logbuf), FMT_WORD ":", s->pc); + logbuf_rear = (logbuf_rear + 1) % CONFIG_ITRACE_BUFFER; + char *p = logbuf[logbuf_rear]; + p += snprintf(p, sizeof(logbuf), FMT_WORD ":", s->pc); int ilen = s->snpc - s->pc; int i; uint8_t *inst = (uint8_t *)&s->isa.inst.val; @@ -64,7 +68,7 @@ static void exec_once(Decode *s, vaddr_t pc) { #ifndef CONFIG_ISA_loongarch32r void disassemble(char *str, int size, uint64_t pc, uint8_t *code, int nbyte); - disassemble(p, s->logbuf + sizeof(s->logbuf) - p, + disassemble(p, logbuf[logbuf_rear] + sizeof(logbuf[logbuf_rear]) - p, MUXDEF(CONFIG_ISA_x86, s->snpc, s->pc), (uint8_t *)&s->isa.inst.val, ilen); #else p[0] = '\0'; // the upstream llvm does not support loongarch32r @@ -79,7 +83,7 @@ static void execute(uint64_t n) { g_nr_guest_inst ++; trace_and_difftest(&s, cpu.pc); if (wp_eval_all()) { - puts(s.logbuf); + puts(logbuf[logbuf_rear]); break; } if (nemu_state.state != NEMU_RUNNING) break; @@ -121,13 +125,16 @@ void cpu_exec(uint64_t n) { switch (nemu_state.state) { case NEMU_RUNNING: nemu_state.state = NEMU_STOP; break; - case NEMU_END: case NEMU_ABORT: + case NEMU_END: case NEMU_ABORT: { Log("nemu: %s at pc = " FMT_WORD, (nemu_state.state == NEMU_ABORT ? ANSI_FMT("ABORT", ANSI_FG_RED) : (nemu_state.halt_ret == 0 ? ANSI_FMT("HIT GOOD TRAP", ANSI_FG_GREEN) : ANSI_FMT("HIT BAD TRAP", ANSI_FG_RED))), nemu_state.halt_pc); - // fall through + if(nemu_state.halt_ret != 0) { + log_itrace_print(); + } + } // fall through case NEMU_QUIT: statistic(); } } diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index 9c86937..b7aeac5 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -54,7 +54,7 @@ static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, static void do_branch(Decode *s, bool condition, word_t offset) { if (condition) { - puts(s->logbuf); + // puts(s->logbuf[s->logbuf_rear]); s->dnpc = s->pc + offset; } } diff --git a/nemu/src/utils/log.c b/nemu/src/utils/log.c index a9bb9a7..7939d42 100644 --- a/nemu/src/utils/log.c +++ b/nemu/src/utils/log.c @@ -35,3 +35,18 @@ bool log_enable() { (g_nr_guest_inst <= CONFIG_TRACE_END), false); } #endif + +IFDEF(CONFIG_ITRACE, char logbuf[CONFIG_ITRACE_BUFFER][128]); +IFDEF(CONFIG_ITRACE, int logbuf_rear); + +#ifdef CONFIG_ITRACE +void log_itrace_print() { + puts("ITRACE buffer:"); + for (int i = (logbuf_rear + 1) % CONFIG_ITRACE_BUFFER; i != logbuf_rear; i = (i + 1) % CONFIG_ITRACE_BUFFER) { + if (logbuf[i][0] == '\0') continue; + puts(logbuf[i]); + } + puts("Current command:"); + puts(logbuf[logbuf_rear]); +} +#endif From 0f7c6fd508ac81de10b99b276d23371c72903008 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 13 Mar 2024 18:14:17 +0800 Subject: [PATCH 08/16] pa2.2: add memory tracer --- nemu/Kconfig | 18 ++++++++++++++++ nemu/include/debug.h | 3 +++ nemu/src/memory/paddr.c | 48 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/nemu/Kconfig b/nemu/Kconfig index 20f7705..9243aba 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -156,6 +156,24 @@ config ITRACE_BUFFER int "Buffer size for intruction trace (unit: number of instructions)" default 10 +config MTRACE + depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER + bool "Enable memory tracer" + + +config MTRACE_RANGE + depends on MTRACE + string "Memory trace active range" + default "0x0-0xfffffff" + help + Memory tracer will only print memory access in these ranges. + Use comma to seperate between ranges. + +config MTRACE_RANGE_MAX + depends on MTRACE + int "Max range count in MTRACE_RANGE" + default 10 + config DIFFTEST depends on TARGET_NATIVE_ELF bool "Enable differential testing" diff --git a/nemu/include/debug.h b/nemu/include/debug.h index df88556..057f8bf 100644 --- a/nemu/include/debug.h +++ b/nemu/include/debug.h @@ -20,6 +20,9 @@ #include #include +#define Trace(format, ...) \ + _Log("[TRACE] " format "\n", ## __VA_ARGS__) + #define Log(format, ...) \ _Log(ANSI_FMT("[INFO] %s:%d %s() ", ANSI_FG_BLUE) format "\n", \ __FILE__, __LINE__, __func__, ## __VA_ARGS__) diff --git a/nemu/src/memory/paddr.c b/nemu/src/memory/paddr.c index ee30e70..437debd 100644 --- a/nemu/src/memory/paddr.c +++ b/nemu/src/memory/paddr.c @@ -13,6 +13,8 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ +#include "common.h" +#include "debug.h" #include #include #include @@ -23,6 +25,11 @@ static uint8_t *pmem = NULL; #else // CONFIG_PMEM_GARRAY static uint8_t pmem[CONFIG_MSIZE] PG_ALIGN = {}; #endif +#ifdef CONFIG_MTRACE +static word_t mtrace_start[CONFIG_MTRACE_RANGE_MAX] = {0}; +static word_t mtrace_end[CONFIG_MTRACE_RANGE_MAX] = {0}; +static int range_count = 0; +#endif uint8_t* guest_to_host(paddr_t paddr) { return pmem + paddr - CONFIG_MBASE; } paddr_t host_to_guest(uint8_t *haddr) { return haddr - pmem + CONFIG_MBASE; } @@ -41,23 +48,58 @@ static void out_of_bound(paddr_t addr) { addr, PMEM_LEFT, PMEM_RIGHT, cpu.pc); } +#ifdef CONFIG_MTRACE +static void mtrace_print(char type, word_t addr, int len, word_t data) { + for (int i = 0; i < range_count; i++) + if (addr <= mtrace_end[i] && addr >= mtrace_start[i] ) { + Trace("Mem %c " FMT_PADDR "%d D " FMT_PADDR, type, addr, len, data); + break; + } +} +#endif + void init_mem() { #if defined(CONFIG_PMEM_MALLOC) pmem = malloc(CONFIG_MSIZE); assert(pmem); +#endif +#ifdef CONFIG_MTRACE + char range[sizeof(CONFIG_MTRACE_RANGE)] = CONFIG_MTRACE_RANGE; + char *saveptr, *ptr; + ptr = strtok_r(range, ",", &saveptr); + for (range_count = 0; range_count < CONFIG_MTRACE_RANGE_MAX; ) { + word_t start, end; + Assert(sscanf(ptr, FMT_PADDR "-" FMT_PADDR, &start, &end) == 2, "Config option MTRACE_RANGE has wrong format"); + mtrace_start[range_count] = start; + mtrace_end[range_count] = end; + + range_count++; + ptr = strtok_r(NULL, ",", &saveptr); + if (!ptr) break; + } + Trace("MTRACE ranges: "); + for (int i = 0; i < range_count; i++) { + Trace("[0x%x, 0x%x]", mtrace_start[i], mtrace_end[i]); + } #endif IFDEF(CONFIG_MEM_RANDOM, memset(pmem, rand(), CONFIG_MSIZE)); Log("physical memory area [" FMT_PADDR ", " FMT_PADDR "]", PMEM_LEFT, PMEM_RIGHT); } word_t paddr_read(paddr_t addr, int len) { - if (likely(in_pmem(addr))) return pmem_read(addr, len); - IFDEF(CONFIG_DEVICE, return mmio_read(addr, len)); + word_t result = 0; + if (likely(in_pmem(addr))) { result = pmem_read(addr, len); goto mtrace;} + IFDEF(CONFIG_DEVICE, result = mmio_read(addr, len); goto mtrace) out_of_bound(addr); - return 0; + +mtrace: + IFDEF(CONFIG_MTRACE, mtrace_print('R', addr, len, result)); + + return result; } void paddr_write(paddr_t addr, int len, word_t data) { + IFDEF(CONFIG_MTRACE, mtrace_print('W', addr, len, data)); if (likely(in_pmem(addr))) { pmem_write(addr, len, data); return; } IFDEF(CONFIG_DEVICE, mmio_write(addr, len, data); return); out_of_bound(addr); From 9229e4318ea17018d6cc8ba01716e26955e77365 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 20 Mar 2024 19:46:54 +0800 Subject: [PATCH 09/16] pa2.2: add ftrace --- flake.nix | 1 + nemu/.result.tmp | 0 nemu/Kconfig | 24 ++++++- nemu/include/ftrace.h | 18 ++++++ nemu/include/macro.h | 2 + nemu/src/isa/riscv32/inst.c | 15 ++++- nemu/src/monitor/monitor.c | 10 +++ nemu/src/utils/ftrace.c | 125 ++++++++++++++++++++++++++++++++++++ 8 files changed, 190 insertions(+), 5 deletions(-) create mode 100644 nemu/.result.tmp create mode 100644 nemu/include/ftrace.h create mode 100644 nemu/src/utils/ftrace.c diff --git a/flake.nix b/flake.nix index 6656094..4492e28 100644 --- a/flake.nix +++ b/flake.nix @@ -65,6 +65,7 @@ devShells.nemu = pkgs.mkShell { packages = with pkgs; [ clang-tools + gdb ]; inputsFrom = [ self.packages.${system}.nemu diff --git a/nemu/.result.tmp b/nemu/.result.tmp new file mode 100644 index 0000000..e69de29 diff --git a/nemu/Kconfig b/nemu/Kconfig index 9243aba..ae1921f 100644 --- a/nemu/Kconfig +++ b/nemu/Kconfig @@ -143,8 +143,11 @@ config TRACE_END config ITRACE depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER - bool "Enable instruction tracer" + bool "Enable instruction tracing" default y + help + Instraction tracing will log past instructions into a ring buffer + and print them when NEMU exit unexpectedly. config ITRACE_COND depends on ITRACE @@ -158,8 +161,8 @@ config ITRACE_BUFFER config MTRACE depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER - bool "Enable memory tracer" - + bool "Enable memory tracing" + default n config MTRACE_RANGE depends on MTRACE @@ -174,6 +177,21 @@ config MTRACE_RANGE_MAX int "Max range count in MTRACE_RANGE" default 10 +config FTRACE + depends on TRACE && TARGET_NATIVE_ELF && ENGINE_INTERPRETER + bool "Enable function tracing" + default y + +config FTRACE_STACK_SIZE + depends on FTRACE + int "Max function track stack size" + default 100 + +config FTRACE_LOG + depends on FTRACE + bool "Print log when entering a funciton" + default n + config DIFFTEST depends on TARGET_NATIVE_ELF bool "Enable differential testing" diff --git a/nemu/include/ftrace.h b/nemu/include/ftrace.h new file mode 100644 index 0000000..9fcf28a --- /dev/null +++ b/nemu/include/ftrace.h @@ -0,0 +1,18 @@ +#ifndef __FUNC_DEF_H__ +#define __FUNC_DEF_H__ +#include + +#ifdef CONFIG_FTRACE +typedef struct { + vaddr_t start; + vaddr_t len; + char * name; +} func_t; + +extern func_t *func_table; +void ftrace_call(vaddr_t, vaddr_t); +void ftrace_return(vaddr_t, vaddr_t); +// const char *get_func_name(vaddr_t addr); +#endif + +#endif \ No newline at end of file diff --git a/nemu/include/macro.h b/nemu/include/macro.h index 8aa38f8..47f11b0 100644 --- a/nemu/include/macro.h +++ b/nemu/include/macro.h @@ -92,6 +92,8 @@ #define PG_ALIGN __attribute((aligned(4096))) +#define FAILED_GOTO(tag, exp) do {if((exp)) goto tag;} while(0) + #if !defined(likely) #define likely(cond) __builtin_expect(cond, 1) #define unlikely(cond) __builtin_expect(cond, 0) diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index b7aeac5..54b9044 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -18,6 +18,7 @@ #include #include #include +#include #define R(i) gpr(i) #define Mr vaddr_read @@ -59,6 +60,16 @@ static void do_branch(Decode *s, bool condition, word_t offset) { } } +static void ftrace(Decode *s, int rd, vaddr_t dst) { + uint32_t i = s->isa.inst.val; + int rs1 = BITS(i, 19, 15); + if(rs1 == 1 && rd == 0) { + ftrace_return(s->pc, dst); + } else { + ftrace_call(s->pc, dst); + } +} + static int decode_exec(Decode *s) { int rd = 0; word_t src1 = 0, src2 = 0, imm = 0; @@ -74,8 +85,8 @@ static int decode_exec(Decode *s) { INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm); - INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {s->dnpc = s->pc + imm; R(rd) = s->pc + 4; } while(0)); - INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; } while(0)); + INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {s->dnpc = s->pc + imm; R(rd) = s->pc + 4; ftrace_call(s->pc, s->pc + imm); } while(0)); + INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; ftrace(s, rd, src1 + imm); } while(0)); INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm)); INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm)); INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); diff --git a/nemu/src/monitor/monitor.c b/nemu/src/monitor/monitor.c index 2279ca0..1920ae4 100644 --- a/nemu/src/monitor/monitor.c +++ b/nemu/src/monitor/monitor.c @@ -40,6 +40,7 @@ static void welcome() { void sdb_set_batch_mode(); static char *log_file = NULL; +static char *elf_file = NULL; static char *diff_so_file = NULL; static char *img_file = NULL; static int difftest_port = 1234; @@ -72,6 +73,7 @@ static int parse_args(int argc, char *argv[]) { {"log" , required_argument, NULL, 'l'}, {"diff" , required_argument, NULL, 'd'}, {"port" , required_argument, NULL, 'p'}, + {"elf" , required_argument, NULL, 'f'}, {"help" , no_argument , NULL, 'h'}, {0 , 0 , NULL, 0 }, }; @@ -82,6 +84,7 @@ static int parse_args(int argc, char *argv[]) { case 'p': sscanf(optarg, "%d", &difftest_port); break; case 'l': log_file = optarg; break; case 'd': diff_so_file = optarg; break; + case 'f': elf_file = optarg; break; case 1: img_file = optarg; return 0; default: printf("Usage: %s [OPTION...] IMAGE [args]\n\n", argv[0]); @@ -89,6 +92,7 @@ static int parse_args(int argc, char *argv[]) { printf("\t-l,--log=FILE output log to FILE\n"); printf("\t-d,--diff=REF_SO run DiffTest with reference REF_SO\n"); printf("\t-p,--port=PORT run DiffTest with port PORT\n"); + printf("\t-f,--elf=FILE elf file with debug info\n"); printf("\n"); exit(0); } @@ -126,6 +130,12 @@ void init_monitor(int argc, char *argv[]) { /* Initialize the simple debugger. */ init_sdb(); + // printf("elf_file: %s\n", elf_file); + if(elf_file != NULL) { + void init_elf(const char *path); + init_elf(elf_file); + } + #ifndef CONFIG_ISA_loongarch32r IFDEF(CONFIG_ITRACE, init_disasm( MUXDEF(CONFIG_ISA_x86, "i686", diff --git a/nemu/src/utils/ftrace.c b/nemu/src/utils/ftrace.c new file mode 100644 index 0000000..25f097a --- /dev/null +++ b/nemu/src/utils/ftrace.c @@ -0,0 +1,125 @@ +#include "debug.h" +#include "macro.h" +#include +#include +#include +#include + +// Put this into another file +#ifdef CONFIG_FTRACE +static vaddr_t ftrace_stack[CONFIG_FTRACE_STACK_SIZE] = {0}; +static vaddr_t ftrace_stack_len = 0; +func_t *func_table = NULL; +int func_table_len = 0, func_table_size = 8; +#endif + +static int cmp_func_t(const void *a, const void *b) { + return ((func_t *)a)->start > ((func_t *)b)->start; +} + +static func_t *get_func(vaddr_t addr) { + int l = 0, r = func_table_len - 1; + while(l <= r) { + int mid = (l + r) / 2; + if(func_table[mid].start <= addr) l = mid + 1; + else r = mid - 1; + } + return l == 0 ? NULL : &func_table[l - 1]; +} + +void init_elf(const char *path) { + FILE *elf_file = fopen(path, "rb"); + Elf32_Ehdr header; + Elf32_Shdr section_header[200], *psh; + + func_table = (func_t *)calloc(func_table_size, sizeof(func_t)); + assert(func_table); + + FAILED_GOTO(failed_header, fread(&header, sizeof(Elf32_Ehdr), 1, elf_file) <= 0); + FAILED_GOTO(failed_header, fseek(elf_file, header.e_shoff, SEEK_SET) != 0); + FAILED_GOTO(failed_header, fread(section_header, header.e_shentsize, header.e_shnum, elf_file) <= 0); + + char *shstrtab = calloc(1, section_header[header.e_shstrndx].sh_size); + FAILED_GOTO(failed_shstrtab, fseek(elf_file, section_header[header.e_shstrndx].sh_offset, SEEK_SET) != 0); + FAILED_GOTO(failed_shstrtab, fread(shstrtab, section_header[header.e_shstrndx].sh_size, 1, elf_file) <= 0); + + Elf32_Shdr *symtab = NULL, *strtab = NULL; + for(int i = 0; i < header.e_shnum; i++) { + psh = section_header + i; + if (psh->sh_type == SHT_SYMTAB) { + symtab = psh; + } else if (psh->sh_type == SHT_STRTAB && strncmp(shstrtab + psh->sh_name, ".strtab", 8) == 0) { + strtab = psh; + } + } + + int sym_length = symtab->sh_size / sizeof(Elf32_Sym); + Elf32_Sym *sym = calloc(sym_length, sizeof(Elf32_Sym)); + assert(sym); + FAILED_GOTO(failed_funcname, fseek(elf_file, symtab->sh_offset, SEEK_SET) != 0); + FAILED_GOTO(failed_funcname, fread(sym, sizeof(Elf32_Sym), sym_length, elf_file) <= 0); + + for(int j = 0; j < sym_length; j++) { + if(ELF32_ST_TYPE(sym[j].st_info) != STT_FUNC) continue; + // Only read function type symbol + func_t *f = &func_table[func_table_len]; + char *func = (char *)malloc(30); + FAILED_GOTO(failed_funcname, fseek(elf_file, strtab->sh_offset + sym[j].st_name, SEEK_SET) != 0); + FAILED_GOTO(failed_funcname, fgets(func, 30, elf_file) <= 0); + f->start = sym[j].st_value; + f->len = sym[j].st_size; + f->name = func; + ++func_table_len; + if(func_table_len >= func_table_size) { + Assert(func_table_size * 2 > func_table_size, "Function table exceed memory limit"); + func_table_size *= 2; + func_table = realloc(func_table, func_table_size * sizeof(func_t)); + Assert(func_table, "Function table exceed memory limit"); + } + } + qsort(func_table, func_table_len, sizeof(func_t), cmp_func_t); + goto success; + +success: + free(sym); + free(shstrtab); + return; + +failed_funcname: + free(sym); +failed_shstrtab: + free(shstrtab); +failed_header: + for(int i = 0; i < func_table_len; i++) { + func_t *f = &func_table[i]; + if(f->name) { free(f->name); } + } + free(func_table); + Error("Failed reading elf file"); + return; +} + +void ftrace_call(vaddr_t pc, vaddr_t addr) { + func_t *f = get_func(addr); + Assert(ftrace_stack_len < CONFIG_FTRACE_STACK_SIZE, + "Ftrace stack exceed size limit, consider turn off ftrace or increase " + "FTRACE_STACK_SIZE."); + ftrace_stack[ftrace_stack_len] = pc + 4; + Trace("%*s0x%x call 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr, + f == NULL ? "???" : f->name, addr - f->start); + ftrace_stack_len++; +} + +void ftrace_return(vaddr_t pc, vaddr_t addr) { + --ftrace_stack_len; + for (; addr != ftrace_stack[ftrace_stack_len] && ftrace_stack_len >= 0; + ftrace_stack_len--) { + vaddr_t tco_addr = ftrace_stack[ftrace_stack_len]; + func_t *f = get_func(tco_addr); + Trace("%*s0x%x ret 0x%x <%s+0x%x> (TCO)", ftrace_stack_len, "", pc, tco_addr, + f == NULL ? "???" : f->name, tco_addr - f->start); + } + func_t *f = get_func(addr); + Trace("%*s0x%x ret 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr, + f == NULL ? "???" : f->name, addr - f->start); +} From a62a132587b8afd8c7e8c14ae6ca9d59be213289 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 20 Mar 2024 20:07:28 +0800 Subject: [PATCH 10/16] pa2.2: cleanup includes --- nemu/include/common.h | 19 +------------------ nemu/include/debug.h | 3 ++- nemu/include/types.h | 21 +++++++++++++++++++++ nemu/include/utils.h | 5 +---- nemu/src/cpu/cpu-exec.c | 2 +- nemu/src/isa/riscv32/inst.c | 3 ++- nemu/src/monitor/monitor.c | 1 + nemu/src/monitor/sdb/addrexp.y | 1 + nemu/src/utils/ftrace.c | 3 +-- nemu/src/utils/log.c | 1 + 10 files changed, 32 insertions(+), 27 deletions(-) create mode 100644 nemu/include/types.h diff --git a/nemu/include/common.h b/nemu/include/common.h index fbffaa5..08a46e5 100644 --- a/nemu/include/common.h +++ b/nemu/include/common.h @@ -17,12 +17,12 @@ #define __COMMON_H__ #include -#include #include #include #include #include +#include #ifdef CONFIG_TARGET_AM #include @@ -31,23 +31,6 @@ #include #endif -#if CONFIG_MBASE + CONFIG_MSIZE > 0x100000000ul -#define PMEM64 1 -#endif - -typedef MUXDEF(CONFIG_ISA64, uint64_t, uint32_t) word_t; -typedef MUXDEF(CONFIG_ISA64, int64_t, int32_t) sword_t; -static const word_t WORD_T_MAX = MUXDEF(CONFIG_ISA64, UINT64_MAX, UINT32_MAX); -static const sword_t SWORD_T_MAX = MUXDEF(CONFIG_ISA64, INT64_MAX, INT32_MAX); -static const sword_t SWORD_T_MIN = MUXDEF(CONFIG_ISA64, INT64_MIN, INT32_MIN); -#define WORD_BYTES MUXDEF(CONFIG_ISA64, 8, 4) -#define FMT_WORD MUXDEF(CONFIG_ISA64, "0x%016" PRIx64, "0x%08" PRIx32) - -typedef word_t vaddr_t; -typedef MUXDEF(PMEM64, uint64_t, uint32_t) paddr_t; -#define FMT_PADDR MUXDEF(PMEM64, "0x%016" PRIx64, "0x%08" PRIx32) -typedef uint16_t ioaddr_t; - #include #endif diff --git a/nemu/include/debug.h b/nemu/include/debug.h index 057f8bf..3aae781 100644 --- a/nemu/include/debug.h +++ b/nemu/include/debug.h @@ -16,10 +16,11 @@ #ifndef __DEBUG_H__ #define __DEBUG_H__ -#include #include #include +IFDEF(CONFIG_ITRACE, void log_itrace_print()); + #define Trace(format, ...) \ _Log("[TRACE] " format "\n", ## __VA_ARGS__) diff --git a/nemu/include/types.h b/nemu/include/types.h new file mode 100644 index 0000000..364f2ed --- /dev/null +++ b/nemu/include/types.h @@ -0,0 +1,21 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ +#include +#include +#if CONFIG_MBASE + CONFIG_MSIZE > 0x100000000ul +#define PMEM64 1 +#endif + +typedef MUXDEF(CONFIG_ISA64, uint64_t, uint32_t) word_t; +typedef MUXDEF(CONFIG_ISA64, int64_t, int32_t) sword_t; +static const word_t WORD_T_MAX = MUXDEF(CONFIG_ISA64, UINT64_MAX, UINT32_MAX); +static const sword_t SWORD_T_MAX = MUXDEF(CONFIG_ISA64, INT64_MAX, INT32_MAX); +static const sword_t SWORD_T_MIN = MUXDEF(CONFIG_ISA64, INT64_MIN, INT32_MIN); +#define WORD_BYTES MUXDEF(CONFIG_ISA64, 8, 4) +#define FMT_WORD MUXDEF(CONFIG_ISA64, "0x%016" PRIx64, "0x%08" PRIx32) + +typedef word_t vaddr_t; +typedef MUXDEF(PMEM64, uint64_t, uint32_t) paddr_t; +#define FMT_PADDR MUXDEF(PMEM64, "0x%016" PRIx64, "0x%08" PRIx32) +typedef uint16_t ioaddr_t; +#endif \ No newline at end of file diff --git a/nemu/include/utils.h b/nemu/include/utils.h index 59bc9df..f974584 100644 --- a/nemu/include/utils.h +++ b/nemu/include/utils.h @@ -16,7 +16,7 @@ #ifndef __UTILS_H__ #define __UTILS_H__ -#include +#include // ----------- state ----------- @@ -74,7 +74,4 @@ uint64_t get_time(); } while (0) -IFDEF(CONFIG_ITRACE, void log_itrace_print()); - - #endif diff --git a/nemu/src/cpu/cpu-exec.c b/nemu/src/cpu/cpu-exec.c index 72e1265..1e402ab 100644 --- a/nemu/src/cpu/cpu-exec.c +++ b/nemu/src/cpu/cpu-exec.c @@ -13,7 +13,7 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include "utils.h" +#include #include #include #include diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index 54b9044..c4d824f 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -13,12 +13,13 @@ * See the Mulan PSL v2 for more details. ***************************************************************************************/ -#include "common.h" +#include #include "local-include/reg.h" #include #include #include #include +#include #define R(i) gpr(i) #define Mr vaddr_read diff --git a/nemu/src/monitor/monitor.c b/nemu/src/monitor/monitor.c index 1920ae4..6755edf 100644 --- a/nemu/src/monitor/monitor.c +++ b/nemu/src/monitor/monitor.c @@ -15,6 +15,7 @@ #include #include +#include void init_rand(); void init_log(const char *log_file); diff --git a/nemu/src/monitor/sdb/addrexp.y b/nemu/src/monitor/sdb/addrexp.y index 8e7df9a..4094e0b 100644 --- a/nemu/src/monitor/sdb/addrexp.y +++ b/nemu/src/monitor/sdb/addrexp.y @@ -7,6 +7,7 @@ } %{ #include + #include #include #include #include diff --git a/nemu/src/utils/ftrace.c b/nemu/src/utils/ftrace.c index 25f097a..ec668c0 100644 --- a/nemu/src/utils/ftrace.c +++ b/nemu/src/utils/ftrace.c @@ -1,9 +1,8 @@ -#include "debug.h" -#include "macro.h" #include #include #include #include +#include // Put this into another file #ifdef CONFIG_FTRACE diff --git a/nemu/src/utils/log.c b/nemu/src/utils/log.c index 7939d42..a041a2d 100644 --- a/nemu/src/utils/log.c +++ b/nemu/src/utils/log.c @@ -14,6 +14,7 @@ ***************************************************************************************/ #include +#include extern uint64_t g_nr_guest_inst; From 18db852763b86e78c2209ebba94bcc3f1fec7a37 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Wed, 20 Mar 2024 20:11:21 +0800 Subject: [PATCH 11/16] pa2.2: fix ftrace switch --- nemu/src/isa/riscv32/inst.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index c4d824f..41c2098 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -15,6 +15,7 @@ #include #include "local-include/reg.h" +#include "macro.h" #include #include #include @@ -61,7 +62,7 @@ static void do_branch(Decode *s, bool condition, word_t offset) { } } -static void ftrace(Decode *s, int rd, vaddr_t dst) { +static void ftrace_jalr(Decode *s, int rd, vaddr_t dst) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); if(rs1 == 1 && rd == 0) { @@ -86,8 +87,12 @@ static int decode_exec(Decode *s) { INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui , U, R(rd) = imm); INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc , U, R(rd) = s->pc + imm); - INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do {s->dnpc = s->pc + imm; R(rd) = s->pc + 4; ftrace_call(s->pc, s->pc + imm); } while(0)); - INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do {s->dnpc = src1 + imm; R(rd) = s->pc + 4; ftrace(s, rd, src1 + imm); } while(0)); + INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal , J, do { + s->dnpc = s->pc + imm; R(rd) = s->pc + 4; + IFDEF(CONFIG_FTRACE, ftrace_call(s->pc, s->pc + imm)); } while(0)); + INSTPAT("??????? ????? ????? ??? ????? 11001 11", jalr , I, do { + s->dnpc = src1 + imm; R(rd) = s->pc + 4; + IFDEF(CONFIG_FTRACE, ftrace_jalr(s, rd, src1 + imm)); } while(0)); INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq , B, do_branch(s, src1 == src2, imm)); INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne , B, do_branch(s, src1 != src2, imm)); INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt , B, do_branch(s, (sword_t)src1 < (sword_t)src2, imm)); From da0c42422d853f528fc1e21d3482c7da1fb6c18c Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 25 Mar 2024 16:56:16 +0800 Subject: [PATCH 12/16] build: init cmake build system for am --- .gitmodules | 3 + abstract-machine/.gitignore | 25 ++--- abstract-machine/CMakeLists.txt | 87 +++++++++++++++++ abstract-machine/CMakePresets.json | 29 ++++++ abstract-machine/am/CMakeLists.txt | 10 ++ abstract-machine/am/src/CMakeLists.txt | 53 ++++++++++ abstract-machine/am/src/native/CMakeLists.txt | 26 +++++ .../am/src/riscv/nemu/CMakeLists.txt | 34 +++++++ abstract-machine/cmake/am-config.cmake.in | 9 ++ abstract-machine/cmake/klib-config.cmake.in | 6 ++ abstract-machine/cmake/nemu-settings.cmake | 11 +++ abstract-machine/cmake/riscv-settings.cmake | 2 + abstract-machine/klib/CMakeLists.txt | 12 +++ abstract-machine/klib/include/klib.h | 1 + abstract-machine/klib/src/CMakeLists.txt | 33 +++++++ abstract-machine/klib/src/stdio.c | 14 ++- abstract-machine/klib/src/string.c | 92 ++++++++++++++++-- abstract-machine/klib/tests/CMakeLists.txt | 17 ++++ abstract-machine/klib/tests/stdio.c | 5 + abstract-machine/klib/tests/string.c | 75 ++++++++++++++ abstract-machine/out/install/lib/libklib.a | Bin 0 -> 87486 bytes am-kernels | 1 + 22 files changed, 515 insertions(+), 30 deletions(-) create mode 100644 .gitmodules create mode 100644 abstract-machine/CMakeLists.txt create mode 100644 abstract-machine/CMakePresets.json create mode 100644 abstract-machine/am/CMakeLists.txt create mode 100644 abstract-machine/am/src/CMakeLists.txt create mode 100644 abstract-machine/am/src/native/CMakeLists.txt create mode 100644 abstract-machine/am/src/riscv/nemu/CMakeLists.txt create mode 100644 abstract-machine/cmake/am-config.cmake.in create mode 100644 abstract-machine/cmake/klib-config.cmake.in create mode 100644 abstract-machine/cmake/nemu-settings.cmake create mode 100644 abstract-machine/cmake/riscv-settings.cmake create mode 100644 abstract-machine/klib/CMakeLists.txt create mode 100644 abstract-machine/klib/src/CMakeLists.txt create mode 100644 abstract-machine/klib/tests/CMakeLists.txt create mode 100644 abstract-machine/klib/tests/stdio.c create mode 100644 abstract-machine/klib/tests/string.c create mode 100644 abstract-machine/out/install/lib/libklib.a create mode 160000 am-kernels diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d7bc671 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "am-kernels"] + path = am-kernels + url = ./am-kernels/ diff --git a/abstract-machine/.gitignore b/abstract-machine/.gitignore index 84c3ed2..bcba0ab 100644 --- a/abstract-machine/.gitignore +++ b/abstract-machine/.gitignore @@ -1,19 +1,6 @@ -* -!*/ -!*.h -!*.c -!*.cc -!*.S -!*.ld -!*.sh -!*.py -!*.mk -!Makefile -!README -!LICENSE -.* -_* -*~ -build/ -!.gitignore -.vscode \ No newline at end of file +**/.direnv/ +**/build/ +**/.envrc +**/.cache +.vscode +compile_commands.json diff --git a/abstract-machine/CMakeLists.txt b/abstract-machine/CMakeLists.txt new file mode 100644 index 0000000..508bc68 --- /dev/null +++ b/abstract-machine/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.22) + +project(abstract-machine) +enable_language(CXX C ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 11) + +include(CMakeDependentOption) +include(CMakePackageConfigHelpers) # Used to find libcheck +include(CTest) + +# -- General options +set(ISA CACHE STRING "Target ISA") +set_property(CACHE ISA PROPERTY STRINGS "riscv" "x86" "x86_64" "native") +string(TOUPPER ${ISA} ISA_UPPER) + +cmake_dependent_option( + __PLATFORM_NEMU__ "Run on NEMU" + ON "ISA MATCHES \"(riscv | x86)\"" OFF) +cmake_dependent_option( + __PLATFORM_NATIVE__ "Run on native" + ON "ISA MATCHES native" OFF) + +# -- Set PLATFORM according to options +set(MATCH_PLATFORM_PATTERN "^__PLATFORM_([A-Z]*)__") +get_cmake_property(CACHE_VARS CACHE_VARIABLES) + +message(STATUS "ISA: ${ISA}") +foreach(VAR IN LISTS CACHE_VARS) + if(VAR MATCHES ${MATCH_PLATFORM_PATTERN}) + # Retrieve the value of the cache variable + get_property(VAR_VALUE CACHE ${VAR} PROPERTY VALUE) + set(PLATFORM_UPPER ${CMAKE_MATCH_1}) + string(TOLOWER ${PLATFORM_UPPER} PLATFORM) + message(STATUS "Variable: ${VAR}=${VAR_VALUE}, Platform: ${PLATFORM}") + endif() +endforeach() + +if(${PLATFORM} MATCHES "native") +set(ARCH "native") +else() +set(ARCH ${ISA}-${PLATFORM}) +endif() +string(TOUPPER ${ARCH} ARCH_UPPER) + +# -- Target specific options +cmake_dependent_option( + NATIVE_USE_KLIB "Use Klib even if on native" + ON "NOT __ISA_NATIVE__" OFF) + +# -- Add compile definitions based on options +add_compile_definitions( + $ + __ISA_${ISA_UPPER}__ + __PLATFORM_${PLATFORM_UPPER}__ +) + +add_compile_definitions( + $<$:__NATIVE_USE_KLIB__> +) + +# -- Required compiler flags +add_compile_options( + # -Werror + -Wno-main + -fno-asynchronous-unwind-tables + -fno-builtin + -fno-stack-protector + -U_FORTIFY_SOURCE + $<$:-fno-exceptions> + $<$:-ffreestanding> + $<$:-fno-rtti>) + +add_link_options( + -znoexecstack +) + +# -- Include linker script here. Use this linker script at link time if INCLUDE_LINKER_SCRIPT is set to true +set(LINKER_SCRIPT linker.ld) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +add_compile_options(-march=rv32if -mabi=ilp32) +add_link_options(-march=rv32if -mabi=ilp32) + +add_subdirectory(klib) +add_subdirectory(am) diff --git a/abstract-machine/CMakePresets.json b/abstract-machine/CMakePresets.json new file mode 100644 index 0000000..d14c0b6 --- /dev/null +++ b/abstract-machine/CMakePresets.json @@ -0,0 +1,29 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "native", + "displayName": "Native", + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "ISA": "native", + "__PLATFORM_NATIVE__": true, + "NATIVE_USE_KLIB": true + } + }, + { + "name": "riscv-nemu", + "displayName": "Riscv32 NEMU", + "generator": "Unix Makefiles", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "/home/xin/repo/ysyx-workbench/abstract-machine/out/install", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "ISA": "riscv", + "__PLATFORM_NEMU__": true + } + } + ] +} \ No newline at end of file diff --git a/abstract-machine/am/CMakeLists.txt b/abstract-machine/am/CMakeLists.txt new file mode 100644 index 0000000..b0462e4 --- /dev/null +++ b/abstract-machine/am/CMakeLists.txt @@ -0,0 +1,10 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + +add_library(am_interface INTERFACE) +target_include_directories(am_interface INTERFACE + $ + $) + +add_subdirectory(src) + +install(DIRECTORY include/ DESTINATION include/abstract-machine) diff --git a/abstract-machine/am/src/CMakeLists.txt b/abstract-machine/am/src/CMakeLists.txt new file mode 100644 index 0000000..533dd3b --- /dev/null +++ b/abstract-machine/am/src/CMakeLists.txt @@ -0,0 +1,53 @@ +if(ISA MATCHES "native") +set(SOURCEDIR "./${PLATFORM}") +else() +set(SOURCEDIR "./${ISA}/${PLATFORM}") +endif() + +add_subdirectory(${SOURCEDIR}) + +target_include_directories(am-${ARCH} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PUBLIC + $ + $) +target_link_libraries(am-${ARCH} + PUBLIC klib_interface + INTERFACE m) + +# TODO: Check +target_link_options(am-${ARCH} INTERFACE + $ + $) + +# Interface compile flags +target_link_options(am-${ARCH} INTERFACE + -znoexecstack) + +target_compile_options(am-${ARCH} INTERFACE + -fno-asynchronous-unwind-tables + -fno-builtin + -fno-stack-protector + -U_FORTIFY_SOURCE + $<$:-fno-exceptions> + $<$:-ffreestanding> + $<$:-fno-rtti>) + +install(TARGETS am-${ARCH} klib_interface am_interface + EXPORT amTargets + LIBRARY DESTINATION lib) + +install(EXPORT amTargets + FILE amTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + +configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/am-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/am-${ARCH}-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) + +# TODO: check +install(FILES ${CMAKE_SOURCE_DIR}/scripts/${LINKER_SCRIPT} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/am-${ARCH}) diff --git a/abstract-machine/am/src/native/CMakeLists.txt b/abstract-machine/am/src/native/CMakeLists.txt new file mode 100644 index 0000000..e3c9303 --- /dev/null +++ b/abstract-machine/am/src/native/CMakeLists.txt @@ -0,0 +1,26 @@ +include(CheckPIESupported) +check_pie_supported() + +set(SOURCES + trap.S + cte.c + ioe.c + mpe.c + platform.c + trm.c + vme.c + ioe/audio.c + ioe/disk.c + ioe/gpu.c + ioe/input.c + ioe/timer.c +) +add_library(am-native ${SOURCES}) + +# FIXME: get free(): invalid address when user program compiled without pie +set_target_properties(am-native PROPERTIES + POSITION_INDEPENDENT_CODE TRUE + INTERFACE_POSITION_INDEPENDENT_CODE TRUE) + +find_package(SDL2 REQUIRED) +target_link_libraries(am-${ARCH} PUBLIC SDL2::SDL2) diff --git a/abstract-machine/am/src/riscv/nemu/CMakeLists.txt b/abstract-machine/am/src/riscv/nemu/CMakeLists.txt new file mode 100644 index 0000000..a6992db --- /dev/null +++ b/abstract-machine/am/src/riscv/nemu/CMakeLists.txt @@ -0,0 +1,34 @@ +include(nemu-settings) +include(riscv-settings) + +add_library(am-${ISA}-nemu + cte.c + start.S + trap.S + vme.c + ${NEMU_SOURCES} +) + +target_compile_options(am-${ISA}-nemu PRIVATE + ${NEMU_COMPILE_OPTIONS} + ${RISCV_COMPILE_OPTIONS}) +target_link_options(am-${ISA}-nemu PRIVATE + ${NEMU_LINK_OPITIONS} + ${RISCV_LINK_OPTIONS}) +target_include_directories(am-${ISA}-nemu PRIVATE + ${NEMU_INCLUDE_DIRECTORIES}) +target_link_options(am-${ISA}-nemu INTERFACE + LINKER:--defsym=_pmem_start=0x80000000 + LINKER:--defsym=_entry_offset=0x0 + LINKER:--gc-sections + LINKER:-e _start + -nostartfiles) + +target_compile_definitions(am-${ISA}-nemu PUBLIC + ARCH_H="arch/riscv.h") +target_compile_definitions(am-${ISA}-nemu PRIVATE + ISA_H="riscv/riscv.h") + +set_target_properties(am-${ISA}-nemu PROPERTIES + POSITION_INDEPENDENT_CODE OFF + INTERFACE_POSITION_INDEPENDENT_CODE OFF) diff --git a/abstract-machine/cmake/am-config.cmake.in b/abstract-machine/cmake/am-config.cmake.in new file mode 100644 index 0000000..f2fbb32 --- /dev/null +++ b/abstract-machine/cmake/am-config.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +if(${ARCH} MATCHES "native") +find_dependency(SDL2 REQUIRED) +endif() + +# Include the targets file +include("${CMAKE_CURRENT_LIST_DIR}/amTargets.cmake") diff --git a/abstract-machine/cmake/klib-config.cmake.in b/abstract-machine/cmake/klib-config.cmake.in new file mode 100644 index 0000000..6b57e7f --- /dev/null +++ b/abstract-machine/cmake/klib-config.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +# Include the targets file +include("${CMAKE_CURRENT_LIST_DIR}/klibTargets.cmake") diff --git a/abstract-machine/cmake/nemu-settings.cmake b/abstract-machine/cmake/nemu-settings.cmake new file mode 100644 index 0000000..910cdcf --- /dev/null +++ b/abstract-machine/cmake/nemu-settings.cmake @@ -0,0 +1,11 @@ +set(NEMU_COMPILE_OPTIONS -fdata-sections -ffunction-sections) +set(NEMU_LINK_OPTIONS + --defsym=_pmem_start=0x80000000 + --defsym=_entry_offset=0x0 + --gc-sections + -e _start) +set(NEMU_INCLUDE_DIRECTORIES + ${CMAKE_SOURCE_DIR}/am/src/platform/nemu/include) +file(GLOB_RECURSE NEMU_SOURCES + ${CMAKE_SOURCE_DIR}/am/src/platform/nemu/*.[cS]) +set(INCLUDE_LINKER_SCRIPT ON) diff --git a/abstract-machine/cmake/riscv-settings.cmake b/abstract-machine/cmake/riscv-settings.cmake new file mode 100644 index 0000000..1286e4c --- /dev/null +++ b/abstract-machine/cmake/riscv-settings.cmake @@ -0,0 +1,2 @@ +set(RISCV_COMPILE_OPTIONS) +set(RISCV_LINK_OPTIONS) diff --git a/abstract-machine/klib/CMakeLists.txt b/abstract-machine/klib/CMakeLists.txt new file mode 100644 index 0000000..2cf4a78 --- /dev/null +++ b/abstract-machine/klib/CMakeLists.txt @@ -0,0 +1,12 @@ +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) + +add_library(klib_interface INTERFACE) +target_include_directories(klib_interface + INTERFACE + $ + $) + +add_subdirectory(src) +# add_subdirectory(tests) + +install(DIRECTORY include/ DESTINATION include/abstract-machine) diff --git a/abstract-machine/klib/include/klib.h b/abstract-machine/klib/include/klib.h index ecb24c8..48d63e9 100644 --- a/abstract-machine/klib/include/klib.h +++ b/abstract-machine/klib/include/klib.h @@ -35,6 +35,7 @@ int atoi (const char *nptr); int printf (const char *format, ...); int sprintf (char *str, const char *format, ...); int snprintf (char *str, size_t size, const char *format, ...); +int vprintf (const char *format, va_list ap); int vsprintf (char *str, const char *format, va_list ap); int vsnprintf (char *str, size_t size, const char *format, va_list ap); diff --git a/abstract-machine/klib/src/CMakeLists.txt b/abstract-machine/klib/src/CMakeLists.txt new file mode 100644 index 0000000..bf7e136 --- /dev/null +++ b/abstract-machine/klib/src/CMakeLists.txt @@ -0,0 +1,33 @@ +# find_package(FLEX) +# find_package(BISON) + +# FLEX_TARGET(fmt_scanner fmt_scanner.l fmt_scanner.c) + +set(SOURCES + cpp.c + int64.c + stdio.c + stdlib.c + string.c + # ${FLEX_fmt_scanner_OUTPUTS} +) + +add_library(klib ${SOURCES}) +target_include_directories(klib PUBLIC $) +target_compile_definitions(klib PUBLIC $) + +install(TARGETS klib + EXPORT klibTargets + LIBRARY DESTINATION lib) + +install(EXPORT klibTargets + FILE klibTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) + +configure_package_config_file(${CMAKE_SOURCE_DIR}/cmake/klib-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/klib-config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/klib-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/klib) + diff --git a/abstract-machine/klib/src/stdio.c b/abstract-machine/klib/src/stdio.c index 1b19953..fec63bc 100644 --- a/abstract-machine/klib/src/stdio.c +++ b/abstract-machine/klib/src/stdio.c @@ -5,8 +5,20 @@ #if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) +int vprintf(const char *fmt, va_list ap) { + const char *p = fmt; + while(*p != '\0') { + putch(*p); + } + return 0; +} + int printf(const char *fmt, ...) { - panic("Not implemented"); + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + return 0; } int vsprintf(char *out, const char *fmt, va_list ap) { diff --git a/abstract-machine/klib/src/string.c b/abstract-machine/klib/src/string.c index f1a1f22..931e7dd 100644 --- a/abstract-machine/klib/src/string.c +++ b/abstract-machine/klib/src/string.c @@ -5,43 +5,115 @@ #if !defined(__ISA_NATIVE__) || defined(__NATIVE_USE_KLIB__) size_t strlen(const char *s) { - panic("Not implemented"); + const char *p = s; + size_t len = 0; + while(*(p++) != '\0') len++; + return len; } char *strcpy(char *dst, const char *src) { - panic("Not implemented"); + char *p_dst = dst; + const char *p_src = src; + for(; *p_src != '\0'; p_src++, p_dst++) { + *p_dst = *p_src; + } + *p_dst = '\0'; + return dst; } char *strncpy(char *dst, const char *src, size_t n) { - panic("Not implemented"); + int i = 0; + for(; i < n && src[i] != '\0'; i++) { + dst[i] = src[i]; + } + for(; i < n; i++) { + dst[i] = '\0'; + } + return dst; } char *strcat(char *dst, const char *src) { - panic("Not implemented"); + char *p_dst = dst; + const char *p_src = src; + while(*p_dst != '\0') p_dst++; + for(; *p_src != '\0'; p_src++, p_dst++) { + *p_dst = *p_src; + } + *p_dst = '\0'; + return dst; } int strcmp(const char *s1, const char *s2) { - panic("Not implemented"); + const char *p_s1 = s1, *p_s2 = s2; + for(; *p_s1 == *p_s2; p_s1++, p_s2++) { + if(*p_s1 == '\0' || *p_s2 == '\0') { + break; + } + } + return *p_s1 - *p_s2; } int strncmp(const char *s1, const char *s2, size_t n) { - panic("Not implemented"); + const char *p_s1 = s1, *p_s2 = s2; + int i = 0; + for(i = 0; i < n - 1; i++) { + if(s1[i] == '\0' || s2[i] == '\0') + break; + } + return s1[i] - s2[i]; } void *memset(void *s, int c, size_t n) { - panic("Not implemented"); + uint8_t *p = s; + for(int i = 0; i < n; i++) { + p[i] = c; + } + return s; } void *memmove(void *dst, const void *src, size_t n) { - panic("Not implemented"); + if (src + n > dst && src < dst) { + size_t len = dst - src; + void *p_dst = (void *)src + n; + const void *p_src = src + n - len; + while(p_dst >= dst) { + memcpy(p_dst, p_src, len); + p_src -= len; + p_dst -= len; + } + if(n % len) memcpy(dst, src, n % len); + } else if (dst < src && dst + n > src) { + size_t len = src - dst; + void *p_dst = dst; + const void *p_src = src; + while(p_src < src + n) { + memcpy(p_dst, p_src, len); + p_src += len; + p_dst += len; + } + if(n % len) memcpy(p_dst, p_src, n % len); + } else { + memcpy(dst, src, n); + } + + return dst; } void *memcpy(void *out, const void *in, size_t n) { - panic("Not implemented"); + for (size_t i = 0 ; i < n ; i++) { + *(uint8_t *)(out + i) = *(uint8_t *)(in + i); + } + return out; } int memcmp(const void *s1, const void *s2, size_t n) { - panic("Not implemented"); + const uint8_t *p1 = s1, *p2 = s2; + for (int i = 0; i < n; i++) { + if(*p1 != *p2) + return p1 - p2; + p1++; p2++; + } + return 0; } #endif diff --git a/abstract-machine/klib/tests/CMakeLists.txt b/abstract-machine/klib/tests/CMakeLists.txt new file mode 100644 index 0000000..f72c555 --- /dev/null +++ b/abstract-machine/klib/tests/CMakeLists.txt @@ -0,0 +1,17 @@ +set(TEST_SOURCES + stdio + string +) + +foreach(TEST IN LISTS TEST_SOURCES) + add_executable(${TEST} ${TEST}.c) + target_link_libraries(${TEST} am-${ARCH} klib m) + target_include_directories(${TEST} + PRIVATE $ + PRIVATE $ + ) + # TODO: Run tests in other configurations + if(__PLATFORM_NATIVE__) + add_test(NAME ${TEST} COMMAND ${TEST}) + endif() +endforeach() diff --git a/abstract-machine/klib/tests/stdio.c b/abstract-machine/klib/tests/stdio.c new file mode 100644 index 0000000..7287e83 --- /dev/null +++ b/abstract-machine/klib/tests/stdio.c @@ -0,0 +1,5 @@ +#include + +int main(void) { + return 0; +} \ No newline at end of file diff --git a/abstract-machine/klib/tests/string.c b/abstract-machine/klib/tests/string.c new file mode 100644 index 0000000..640f2d6 --- /dev/null +++ b/abstract-machine/klib/tests/string.c @@ -0,0 +1,75 @@ +#include +#include +#include + +void test_strcpy() { + char b[32]; + char *s; + b[16]='a'; b[17]='b'; b[18]='c'; b[19]=0; + panic_on((s = strcpy(b, b+16)) != b, "strcpy wrong return value"); + panic_on(strcmp(s, "abc") != 0, "strcpy gave incorrect string"); + panic_on((s = strcpy(b+1, b+16)) != b+1, "strcpy wrong return value"); + panic_on(strcmp(s, "abc") != 0, "strcpy gave incorrect string"); + + panic_on((s = strcpy(b+1, b+17)) != b+1, "strcpy wrong return value"); + panic_on(strcmp(s, "bc") != 0, "strcpy gave incorrect string"); +} + +void test_strncpy() { + char b[32]; + char *s; + int i; + b[3] = 'x'; b[4] = 0; + panic_on((s = strncpy(b, "abc", 3)) != b, "strncpy wrong return value"); + panic_on(b[2] != 'c', "strncpy fails to copy last byte"); + panic_on(b[3] != 'x', "strncpy overruns buffer to null-terminate"); +} + +void test_strncmp() { + panic_on(strncmp("abcd", "abce", 3) != 0, "strncmp compares past n"); + panic_on(strncmp("abc", "abd", 3) == 0, "strncmp fails to compare n-1st byte"); +} + +void test_memset() { + uint8_t arr[128]; + arr[120] = 0xd; + panic_on(memset(arr, 0xf, 120) != arr, "memset wrong return value"); + panic_on(arr[7] != 0xf, "memset fails to set value in range"); + panic_on(arr[120] != 0xd, "memset set value past n"); +} + +void test_memcpy() { + const uint8_t src[] = { 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x0, 0x0 }; + uint8_t dst[8] = {0}; + memcpy(dst, src, 8); + panic_on(memcmp(dst, src, 8) != 0, "memcpy fails to copy memory"); +} + +void test_memmove() { + const uint8_t ref[] = { 0x0, 0x0, 0x1, 0x2, 0x3, 0x4, 0x0, 0x0 }; + uint8_t dst[8] = {0}; + const uint8_t ans1[] = { 0x1, 0x2, 0x3, 0x4, 0x3, 0x4, 0x0, 0x0 }; + const uint8_t ans2[] = { 0x1, 0x2, 0x2, 0x3, 0x4, 0x3, 0x0, 0x0 }; + const uint8_t ans3[] = { 0x1, 0x2, 0x2, 0x1, 0x2, 0x2, 0x3, 0x4 }; + memmove(dst, ref, 8); + panic_on(memcmp(dst, ref, 8) != 0, "memmove fails to copy non-overlapping memory"); + + memmove(dst, dst + 2, 4); + panic_on(memcmp(dst, ans1, 8) != 0, "memmove fails to copy overlapping memory (dst < src)"); + + memmove(dst + 2, dst + 1, 4); + panic_on(memcmp(dst, ans2, 8) != 0, "memmove fails to copy overlapping memory (src < dst)"); + + memmove(dst + 3, dst, 5); + panic_on(memcmp(dst, ans3, 8) != 0, "memmove fails to copy overlapping memory (src < dst)"); +} + +int main(void) { + test_strcpy(); + test_strncpy(); + test_strncmp(); + test_memset(); + test_memcpy(); + test_memmove(); + return 0; +} diff --git a/abstract-machine/out/install/lib/libklib.a b/abstract-machine/out/install/lib/libklib.a new file mode 100644 index 0000000000000000000000000000000000000000..5023a30456119001268273724f42950c73659d18 GIT binary patch literal 87486 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 Ma&}3=V&(PzA8GXE6951J literal 0 HcmV?d00001 diff --git a/am-kernels b/am-kernels new file mode 160000 index 0000000..2f55982 --- /dev/null +++ b/am-kernels @@ -0,0 +1 @@ +Subproject commit 2f559823a63cf6909d5a9e32dee47d6891caf553 From 8545a92c1f7be75df075ef7b9266e5560efb0638 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 25 Mar 2024 17:02:12 +0800 Subject: [PATCH 13/16] pa2.2: nemu small fixes --- nemu/.result.tmp | 0 nemu/Makefile | 2 +- nemu/default.nix | 4 ++-- nemu/include/debug.h | 1 + nemu/src/isa/riscv32/inst.c | 2 ++ nemu/src/monitor/monitor.c | 4 ++++ nemu/src/utils/ftrace.c | 2 +- 7 files changed, 11 insertions(+), 4 deletions(-) delete mode 100644 nemu/.result.tmp diff --git a/nemu/.result.tmp b/nemu/.result.tmp deleted file mode 100644 index e69de29..0000000 diff --git a/nemu/Makefile b/nemu/Makefile index e5e37c3..e5e5838 100644 --- a/nemu/Makefile +++ b/nemu/Makefile @@ -94,4 +94,4 @@ integration-tests: $(IMAGES) test: unit-tests integration-tests -.PHONY: test unit-tests integration-tests \ No newline at end of file +.PHONY: test unit-tests integration-tests diff --git a/nemu/default.nix b/nemu/default.nix index a886b79..e746a7c 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -38,7 +38,7 @@ stdenv.mkDerivation rec { doCheck = true; checkPhase = '' - export IMAGES_PATH=${am-kernels}/share/images + export IMAGES_PATH=${am-kernels}/share/binary make test ''; @@ -49,7 +49,7 @@ stdenv.mkDerivation rec { shellHook = '' export NEMU_HOME=$(pwd) - export IMAGES_PATH=${am-kernels}/share/images + export IMAGES_PATH=${am-kernels}/share/binary ''; meta = with lib; { diff --git a/nemu/include/debug.h b/nemu/include/debug.h index 3aae781..329c64a 100644 --- a/nemu/include/debug.h +++ b/nemu/include/debug.h @@ -18,6 +18,7 @@ #include #include +#include IFDEF(CONFIG_ITRACE, void log_itrace_print()); diff --git a/nemu/src/isa/riscv32/inst.c b/nemu/src/isa/riscv32/inst.c index 41c2098..1c41c63 100644 --- a/nemu/src/isa/riscv32/inst.c +++ b/nemu/src/isa/riscv32/inst.c @@ -62,6 +62,7 @@ static void do_branch(Decode *s, bool condition, word_t offset) { } } +#ifdef CONFIG_FTRACE static void ftrace_jalr(Decode *s, int rd, vaddr_t dst) { uint32_t i = s->isa.inst.val; int rs1 = BITS(i, 19, 15); @@ -71,6 +72,7 @@ static void ftrace_jalr(Decode *s, int rd, vaddr_t dst) { ftrace_call(s->pc, dst); } } +#endif static int decode_exec(Decode *s) { int rd = 0; diff --git a/nemu/src/monitor/monitor.c b/nemu/src/monitor/monitor.c index 6755edf..0154208 100644 --- a/nemu/src/monitor/monitor.c +++ b/nemu/src/monitor/monitor.c @@ -133,8 +133,12 @@ void init_monitor(int argc, char *argv[]) { // printf("elf_file: %s\n", elf_file); if(elf_file != NULL) { +#ifdef CONFIG_FTRACE void init_elf(const char *path); init_elf(elf_file); +#else + Warning("Elf file provided, but ftrace not turned on. Ignoring elf file."); +#endif } #ifndef CONFIG_ISA_loongarch32r diff --git a/nemu/src/utils/ftrace.c b/nemu/src/utils/ftrace.c index ec668c0..ea2f2b6 100644 --- a/nemu/src/utils/ftrace.c +++ b/nemu/src/utils/ftrace.c @@ -10,7 +10,6 @@ static vaddr_t ftrace_stack[CONFIG_FTRACE_STACK_SIZE] = {0}; static vaddr_t ftrace_stack_len = 0; func_t *func_table = NULL; int func_table_len = 0, func_table_size = 8; -#endif static int cmp_func_t(const void *a, const void *b) { return ((func_t *)a)->start > ((func_t *)b)->start; @@ -122,3 +121,4 @@ void ftrace_return(vaddr_t pc, vaddr_t addr) { Trace("%*s0x%x ret 0x%x <%s+0x%x>", ftrace_stack_len, "", pc, addr, f == NULL ? "???" : f->name, addr - f->start); } +#endif From 2cc992a1818ae2c18943f0be51a55b3cfa126dff Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 25 Mar 2024 17:29:46 +0800 Subject: [PATCH 14/16] am: build with nix --- .gitignore | 3 +- .gitmodules | 2 +- abstract-machine/default.nix | 26 ++++++++++++++++ flake.nix | 60 +++++++++++++----------------------- 4 files changed, 50 insertions(+), 41 deletions(-) create mode 100644 abstract-machine/default.nix diff --git a/.gitignore b/.gitignore index 4ed3ed8..44a51ce 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ !init.sh /fceux-am /nvboard -/am-kernels +**/.cache +**/result diff --git a/.gitmodules b/.gitmodules index d7bc671..3d834b3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "am-kernels"] path = am-kernels - url = ./am-kernels/ + url = https://git.xinyang.life/xin/am-kernels.git diff --git a/abstract-machine/default.nix b/abstract-machine/default.nix new file mode 100644 index 0000000..1f1f67d --- /dev/null +++ b/abstract-machine/default.nix @@ -0,0 +1,26 @@ +{ stdenv, + lib, + cmake, + SDL2, + isa ? "native", + platform ? "NEMU" +}: +stdenv.mkDerivation { + pname = "abstract-machine"; + version = "2024.02.18"; + + src = ./.; + + cmakeFlags = [ + (lib.cmakeFeature "ISA" isa) + (lib.cmakeBool "__PLATFORM_${lib.strings.toUpper platform}__" true) + ]; + + nativeBuildInputs = [ + cmake + ]; + + buildInputs = [ + + ] ++ (if platform=="native" then [ SDL2 ] else [ ]); +} diff --git a/flake.nix b/flake.nix index 4492e28..42dccac 100644 --- a/flake.nix +++ b/flake.nix @@ -12,54 +12,37 @@ localSystem = system; crossSystem = { config = "riscv32-none-elf"; - abi = "ilp32"; + gcc = { + abi = "ilp32"; + arch = "rv32if"; + }; }; }; in { - packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; + packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels-cmake; }; + packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; }; - packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { - pname = "am-kernels"; + packages.am-kernels-cmake = crossPkgs.stdenv.mkDerivation rec { + pname = "am-kernels-cmake"; version = "2024.02.18"; - src = pkgs.fetchFromGitHub { - owner = "NJU-ProjectN"; - repo = "am-kernels"; - rev = "bb725d6f8223dd7de831c3b692e8c4531e9d01af"; - hash = "sha256-ZHdrw28TN8cMvhhzM469OV7cp0Yp+8yao855HP4+P4A="; - }; + src = ./am-kernels; - AM_HOME = pkgs.fetchFromGitHub { - owner = "xinyangli"; - repo = "abstract-machine"; - rev = "788595aac61c6b2f3b78ca8aa7d08dc33911bca4"; - hash = "sha256-YvWHIBP9tz3HL2TyibftvvQrpkWUDPnviCF4oyLmdjg="; - }; + nativeBuildInputs = [ + pkgs.cmake + ]; - ARCH = "riscv32-nemu"; + cmakeFlags = [ + (pkgs.lib.cmakeFeature "ISA" "riscv") + (pkgs.lib.cmakeFeature "PLATFORM" "nemu") + (pkgs.lib.cmakeFeature "CMAKE_INSTALL_DATADIR" "share") + ]; - patchPhase = '' - sed -i 's/\/bin\/echo/echo/' tests/cpu-tests/Makefile - ''; - - buildPhase = '' - AS=$CC make -C tests/cpu-tests BUILD_DIR=$(pwd)/build ARCH=$ARCH - ''; - - installPhase = '' - mkdir -p $out/share/images $out/share/dump - cp build/riscv32-nemu/*.bin $out/share/images - cp build/riscv32-nemu/*.txt $out/share/dump - ''; - - dontFixup = true; - }; - - devShells.default = pkgs.mkShell { - packages = with pkgs; [ - gdb - ] ++ builtins.attrValues self.packages.${system}; + buildInputs = [ + # SDL2 + self.packages.${system}.abstract-machine + ]; }; devShells.nemu = pkgs.mkShell { @@ -74,4 +57,3 @@ } ); } - From d5521806d922216fe76f21161beb67b9612a4483 Mon Sep 17 00:00:00 2001 From: xinyangli Date: Mon, 25 Mar 2024 20:46:13 +0800 Subject: [PATCH 15/16] ci: init --- .gitea/workflows/abstract-machine-build.yml | 22 +++++++++++++++++++++ flake.nix | 4 ++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 .gitea/workflows/abstract-machine-build.yml diff --git a/.gitea/workflows/abstract-machine-build.yml b/.gitea/workflows/abstract-machine-build.yml new file mode 100644 index 0000000..ded6e88 --- /dev/null +++ b/.gitea/workflows/abstract-machine-build.yml @@ -0,0 +1,22 @@ +name: Build abstract machine with nix +on: [push] + +jobs: + build-abstract-machine: + runs-on: nix + steps: + - uses: https://github.com/cachix/cachix-action@v14 + with: + name: ysyx + signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' + - uses: actions/checkout@v4 + sparse-checkout: | + flake.nix + abstract-machine + - name: Build abstract-machine + run: | + nix build .#abstract-machine + - name: Build nemu + run: | + nix build .#nemu + diff --git a/flake.nix b/flake.nix index 42dccac..3a07d79 100644 --- a/flake.nix +++ b/flake.nix @@ -20,10 +20,10 @@ }; in { - packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels-cmake; }; + packages.nemu = pkgs.callPackage ./nemu { am-kernels = self.packages.${system}.am-kernels; }; packages.abstract-machine = crossPkgs.callPackage ./abstract-machine { isa = "riscv"; platform = "nemu"; }; - packages.am-kernels-cmake = crossPkgs.stdenv.mkDerivation rec { + packages.am-kernels = crossPkgs.stdenv.mkDerivation rec { pname = "am-kernels-cmake"; version = "2024.02.18"; From d5feb71b50b5d7c87ddb0ac9f8fc9378d701900a Mon Sep 17 00:00:00 2001 From: xinyangli Date: Tue, 26 Mar 2024 01:53:17 +0800 Subject: [PATCH 16/16] nemu: try difftest --- .gitea/workflows/abstract-machine-build.yml | 5 ++--- nemu/default.nix | 4 +++- nemu/src/isa/riscv32/difftest/dut.c | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/abstract-machine-build.yml b/.gitea/workflows/abstract-machine-build.yml index ded6e88..af10d43 100644 --- a/.gitea/workflows/abstract-machine-build.yml +++ b/.gitea/workflows/abstract-machine-build.yml @@ -10,9 +10,8 @@ jobs: name: ysyx signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - uses: actions/checkout@v4 - sparse-checkout: | - flake.nix - abstract-machine + with: + submodules: true - name: Build abstract-machine run: | nix build .#abstract-machine diff --git a/nemu/default.nix b/nemu/default.nix index e746a7c..d3d5a70 100644 --- a/nemu/default.nix +++ b/nemu/default.nix @@ -1,7 +1,8 @@ { pkgs, lib, stdenv, - am-kernels + am-kernels, + dtc }: stdenv.mkDerivation rec { @@ -15,6 +16,7 @@ stdenv.mkDerivation rec { pkg-config flex bison + dtc ]; buildInputs = with pkgs; [ diff --git a/nemu/src/isa/riscv32/difftest/dut.c b/nemu/src/isa/riscv32/difftest/dut.c index c5ebf13..06748ce 100644 --- a/nemu/src/isa/riscv32/difftest/dut.c +++ b/nemu/src/isa/riscv32/difftest/dut.c @@ -18,7 +18,10 @@ #include "../local-include/reg.h" bool isa_difftest_checkregs(CPU_state *ref_r, vaddr_t pc) { - return false; + for(int i = 0; i < MUXDEF(CONFIG_RVE, 16, 32); i++) { + if(!difftest_check_reg(reg_name(i), pc, ref_r->gpr[i], gpr(i))) return false; + } + return true; } void isa_difftest_attach() {