diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1422a69..f96e18b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - zig: [ 0.12.0, master ] + zig: [ master ] os: [ ubuntu-latest] runs-on: ${{ matrix.os }} name: Zig ${{ matrix.zig }} on ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 065fc75..e1c94c6 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.direnv # zig build output zig-out/ zig-cache/ diff --git a/flake.lock b/flake.lock index 5049446..4a1880d 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,21 @@ { "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -18,6 +34,21 @@ "type": "github" } }, + "flake-utils_2": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1715087517, @@ -34,10 +65,27 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1702350026, + "narHash": "sha256-A+GNZFZdfl4JdDphYKBJ5Ef1HOiFsP18vQe9mqjmUis=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9463103069725474698139ab10f17a9d125da859", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "zig-overlay": "zig-overlay" } }, "systems": { @@ -54,6 +102,26 @@ "repo": "default", "type": "github" } + }, + "zig-overlay": { + "inputs": { + "flake-compat": "flake-compat", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1715170163, + "narHash": "sha256-EuRzY3HI9sMMqPX7Yb7xkZaBoznP0mtS2O/Kk/r6fYk=", + "owner": "mitchellh", + "repo": "zig-overlay", + "rev": "9c0a853edcab5d60d28784c10b13392d7fabb9d7", + "type": "github" + }, + "original": { + "owner": "mitchellh", + "repo": "zig-overlay", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 615b0c1..a6795ca 100644 --- a/flake.nix +++ b/flake.nix @@ -1,37 +1,50 @@ { - description = "MEMU"; + description = "MEMU"; - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - flake-utils.url = "github:numtide/flake-utils"; - }; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + zig-overlay.url = "github:mitchellh/zig-overlay"; + }; - outputs = { self, nixpkgs, flake-utils }@inputs: - flake-utils.lib.eachDefaultSystem - (system: - let - pkgs = import nixpkgs { inherit system;}; - deps = with pkgs; [ - git - gnumake autoconf automake - cmake ninja - pkgsCross.riscv64-embedded.buildPackages.gcc - ]; - in - { - legacyPackages = pkgs; - devShell = pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } { - buildInputs = deps; - RV64_TOOLCHAIN_ROOT = "${pkgs.pkgsCross.riscv64-embedded.buildPackages.gcc}"; - shellHook = '' - export EMU_CC=$RV64_TOOLCHAIN_ROOT/bin/riscv64-none-elf-gcc - export EMU_OBJCOPY=$RV64_TOOLCHAIN_ROOT/bin/riscv64-none-elf-objcopy - make test-img - unset EMU_CC - unset EMU_OBJCOPY - ''; - }; - } - ) - // { inherit inputs;}; + outputs = { + self, + zig-overlay, + nixpkgs, + flake-utils, + } @ inputs: + flake-utils.lib.eachDefaultSystem + ( + system: let + pkgs = import nixpkgs { + inherit system; + overlays = [zig-overlay.overlays.default]; + }; + deps = with pkgs; [ + git + gnumake + autoconf + automake + cmake + ninja + zigpkgs.master + pkgsCross.riscv64-embedded.buildPackages.gcc + ]; + in { + legacyPackages = pkgs; + formatter = pkgs.alejandra; + devShells.default = pkgs.mkShell.override {stdenv = pkgs.clangStdenv;} { + buildInputs = deps; + RV64_TOOLCHAIN_ROOT = "${pkgs.pkgsCross.riscv64-embedded.buildPackages.gcc}"; + shellHook = '' + export EMU_CC=$RV64_TOOLCHAIN_ROOT/bin/riscv64-none-elf-gcc + export EMU_OBJCOPY=$RV64_TOOLCHAIN_ROOT/bin/riscv64-none-elf-objcopy + make test-img + unset EMU_CC + unset EMU_OBJCOPY + ''; + }; + } + ) + // {inherit inputs;}; } diff --git a/src/arch/rv64.zig b/src/arch/rv64.zig index b1b3e07..51bfa23 100644 --- a/src/arch/rv64.zig +++ b/src/arch/rv64.zig @@ -9,6 +9,9 @@ // See the Mulan PSL v2 for more details. const std = @import("std"); +const decode = @import("../cpu/decode.zig"); +const cpu = @import("../cpu/cpu.zig"); +const setState = @import("../cpu/exu.zig").setState; pub const word_t = u64; pub const vaddr_t = u64; @@ -20,3 +23,30 @@ pub const riscv64_ISADecodeInfo = packed struct { val: u32, }; }; + +pub const MatchOp = *const fn (inst: decode.Instruction) void; + +pub fn addi(inst: decode.Instruction) void { + const op = inst.addi; + cpu.setGPRs(op.rd, cpu.GPRs(op.rs1) +% op.imm); + return; +} + +pub fn ebreak(inst: decode.Instruction) void { + if (inst.ebreak.halt == 1) { + // std.debug.print("is halt:{}\n", .{inst.ebreak.halt}); + setState(true); + } else { + const out: u8 = @intCast(cpu.cpu.gprs[11] & 0xff); + std.debug.print("{c}", .{out}); + setState(false); + } + return; +} + +pub fn inst2exu(inst: decode.Instruction) MatchOp { + return switch (inst) { + .addi => addi, + .ebreak => ebreak, + }; +} diff --git a/src/cpu/decode.zig b/src/cpu/decode.zig index d8499e3..ff59c92 100644 --- a/src/cpu/decode.zig +++ b/src/cpu/decode.zig @@ -15,6 +15,8 @@ const word_t = isa.word_t; const testing = std.testing; const expect = std.testing.expect; const print = @import("../common.zig").print; +const cpu = @import("./cpu.zig"); +const utils = @import("../util.zig"); const DECODE_TYPE = enum { TYPE_I, @@ -34,40 +36,63 @@ pub const Decode = struct { inst: u32, }; -fn BITSMASK(hi: u5, lo: u5) u32 { - return ((@as(u32, 1) << (hi - lo + 1)) - 1) << lo; -} +pub const TypeB = struct { + imm: u32, + rs1: u32, + rs2: u32, + rd: u32, +}; + +pub const TypeI = struct { + imm: u32, + rs1: u32, + rd: u32, + pub fn decode(inst: u32) TypeI { + const imm: u32 = utils.BITS(inst, 31, 20); + const rs1: u32 = utils.BITS(inst, 19, 15); + const rd: u32 = utils.BITS(inst, 11, 7); + return TypeI{ + .imm = imm, + .rs1 = rs1, + .rd = rd, + }; + } + pub fn Operand(funct3: u32) !Operand { + return switch (funct3) { + 0 => Operand.addi, + else => unreachable, + }; + } +}; -fn BITS(x: u32, hi: u5, lo: u5) u32 { - const mask = BITSMASK(hi, lo); - return (x & mask) >> @intCast(lo); -} +pub const TypeEnv = struct { + ebreak: u32, + ret: u64, + halt: u32, + const inst_ebreak: u32 = 0b00000000000100000000000001110011; + pub fn decode(inst: u32) TypeEnv { + // Halt emu when is ebreak + if (inst == inst_ebreak) { + if (cpu.GPRs(10) == 1) return TypeEnv{ .ebreak = 1, .ret = cpu.GPRs(10), .halt = 1 } else return TypeEnv{ .ebreak = 1, .ret = cpu.GPRs(10), .halt = 0 }; + } + return TypeEnv{ .ebreak = 0, .ret = cpu.GPRs(10), .halt = 0 }; + } +}; + +pub const Instruction = union(enum) { + addi: TypeI, + ebreak: TypeEnv, -pub fn decode_operand(s: *Decode, dest: *word_t, src1: *word_t, src2: *word_t, inst_type: *word_t, imm: *word_t) void { - const i: u32 = s.*.inst; - const rd: u32 = BITS(i, 11, 7); - const rs1: u32 = BITS(i, 19, 15); - const rs2: u32 = BITS(i, 24, 20); - const opcode: u32 = BITS(i, 6, 0); - const imm_i: u32 = BITS(i, 31, 20); - dest.* = rd; - src1.* = rs1; - src2.* = rs2; - inst_type.* = opcode; - imm.* = imm_i; -} + pub fn DecodePattern(op: anytype, val: anytype) Instruction { + return @unionInit(Instruction, @tagName(op), val); // Return the union with the tag name of the operation and the value + } -test "decode_operand" { - var s: Decode = undefined; - s.inst = 0x00000033; - var dest: word_t = undefined; - var src1: word_t = undefined; - var src2: word_t = undefined; - var opcode: word_t = undefined; - decode_operand(&s, &dest, &src1, &src2, &opcode); - print("dest: {}, src1: {}, src2: {}, opcode: {}\n", .{ dest, src1, src2, opcode }); - try testing.expect(dest == 0); - try testing.expect(src1 == 0); - try testing.expect(src2 == 0); - try testing.expect(opcode == 51); -} + pub fn decode32(inst: u32) anyerror!Instruction { + const opcode: u32 = utils.BITS(inst, 6, 0); + return switch (opcode) { + 0b0010011 => DecodePattern(.addi, TypeI.decode(inst)), + 0b1110011 => DecodePattern(.ebreak, TypeEnv.decode(inst)), + else => unreachable, + }; + } +}; diff --git a/src/cpu/exu.zig b/src/cpu/exu.zig index b3c1462..d0dd484 100644 --- a/src/cpu/exu.zig +++ b/src/cpu/exu.zig @@ -12,45 +12,35 @@ const std = @import("std"); const M = @import("../mem.zig").MEM; const c = @import("cpu.zig"); -const Decode = @import("decode.zig").Decode; -const decode = @import("decode.zig").decode_operand; +const decode = @import("decode.zig"); const word_t = @import("../arch/rv64.zig").word_t; -const print = @import("../common.zig").print; +const print = @import("../util.zig").print; const ifu = @import("ifu.zig"); +const isa = @import("../arch/rv64.zig"); var halt = false; pub fn exu_cycle() void { const inst: u32 = ifu.inst_fetch(c.cpu.pc, &M.memory); - var s: Decode = undefined; - s.inst = inst; - var dest: word_t = undefined; - var src1: word_t = undefined; - var src2: word_t = undefined; - var opcode: word_t = undefined; - var imm_i: word_t = undefined; - decode(&s, &dest, &src1, &src2, &opcode, &imm_i); - const nrZero: bool = if (dest != 0) true else false; - const addi: bool = if ((inst & 0x7f) == 0x13 and ((inst >> 12 & 0x7)) == 0) true else false; - const ebreak: bool = if ((inst & 0x00100073) != 0) true else false; - if (addi) { - if (nrZero) { - c.setGPRs(@intCast(dest), c.cpu.gprs[src1] + imm_i); - } - } else if (ebreak) { - if (c.cpu.gprs[10] == 0) { - const out: u8 = @intCast(c.cpu.gprs[11] & 0xff); - print("{c}", .{out}); - } else if (c.cpu.gprs[10] == 1) { - halt = true; - } else { - print("Unsupported ebreak command\n", .{}); - } - } else { - print("Unsupported instruction\n", .{}); - } + var failed: anyerror = undefined; + const inst_test: ?decode.Instruction = decode.Instruction.decode32(inst) catch |err| { + failed = err; + return; + }; + const exu = isa.inst2exu(inst_test.?); + const ret = exu(inst_test.?); + _ = ret; } -pub fn isHalt() bool { +pub fn ishalt() bool { return halt; } + +pub fn setState(x: bool) void { + if (x) { + halt = true; + } else { + halt = false; + } + return; +} diff --git a/src/imgfile/prog.c b/src/imgfile/prog.c index 7d7e63a..2c0b437 100644 --- a/src/imgfile/prog.c +++ b/src/imgfile/prog.c @@ -12,12 +12,14 @@ void _start() { putch('L'); putch('L'); putch('O'); + putch('!'); putch(' '); putch('R'); putch('I'); putch('S'); putch('C'); putch('V'); + putch('!'); putch('\n'); halt(0); } diff --git a/src/main.zig b/src/main.zig index 92ebb8f..6fb6661 100644 --- a/src/main.zig +++ b/src/main.zig @@ -10,7 +10,7 @@ const std = @import("std"); const c = @import("cpu/cpu.zig"); -const halt = @import("cpu/exu.zig").isHalt; +const halt = @import("cpu/exu.zig").ishalt; const initMemory = @import("mem.zig").initMem; pub const reg_display = @import("cpu/reg.zig").reg_display; diff --git a/src/util.zig b/src/util.zig index 2340564..f15591a 100644 --- a/src/util.zig +++ b/src/util.zig @@ -15,3 +15,16 @@ pub const print = std.debug.print; pub fn putchar(c: u8) void { std.os.write(std.os.Stdout, &c, 1); } + +pub inline fn CONCACT(a: u32, b: u32) u64 { + return @intCast(a << 32 | b); +} + +pub fn BITSMASK(hi: u5, lo: u5) u32 { + return ((@as(u32, 1) << (hi - lo + 1)) - 1) << lo; +} + +pub fn BITS(x: u32, hi: u5, lo: u5) u32 { + const mask = BITSMASK(hi, lo); + return (x & mask) >> @intCast(lo); +}