From 6518fcc68b67146cf5bb8a099a4a7c67327ff6b1 Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Fri, 15 Sep 2023 14:21:16 -0500 Subject: [PATCH] feat: implement basic cp15 coprocessor --- lib/arm32 | 2 +- src/core/nds9.zig | 1 + src/core/nds9/Cp15.zig | 102 +++++++++++++++++++++++++++++++++++++++++ src/main.zig | 6 ++- 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 src/core/nds9/Cp15.zig diff --git a/lib/arm32 b/lib/arm32 index c949128..30cf951 160000 --- a/lib/arm32 +++ b/lib/arm32 @@ -1 +1 @@ -Subproject commit c94912887e0c403dc2116162e3f43e32f9412e59 +Subproject commit 30cf951d2a4ccba3ff7ce70ceeae44780af5f1a1 diff --git a/src/core/nds9.zig b/src/core/nds9.zig index 48c2904..6d6724a 100644 --- a/src/core/nds9.zig +++ b/src/core/nds9.zig @@ -1,6 +1,7 @@ const std = @import("std"); pub const Bus = @import("nds9/Bus.zig"); +pub const Cp15 = @import("nds9/Cp15.zig"); pub const io = @import("nds9/io.zig"); pub const Scheduler = @import("nds9/Scheduler.zig"); pub const Arm946es = @import("arm32").Arm946es; diff --git a/src/core/nds9/Cp15.zig b/src/core/nds9/Cp15.zig new file mode 100644 index 0000000..15a41f8 --- /dev/null +++ b/src/core/nds9/Cp15.zig @@ -0,0 +1,102 @@ +const std = @import("std"); + +const log = std.log.scoped(.cp15); + +control: u32 = 0x0005_2078, +dtcm_size_base: u32 = 0x0300_000A, +itcm_size_base: u32 = 0x0000_0020, + +// Protection Unit +// cache_bits_data_unified: u32 = 0x0000_0000, +// cache_write_bufability: u32 = 0x0000_0000, // For Data Protection Regions +// cache_bits_instr: u32 = 0x0000_0000, + +/// Used in ARMv5TE MRC +pub fn read(self: *const @This(), op1: u3, cn: u4, cm: u4, op2: u3) u32 { + return switch (addr(op1, cn, cm, op2)) { + // c0, c0, ? + 0b000_0000_0000_000 => 0x4105_9461, // Main ID Register + 0b000_0000_0000_001 => 0x0F0D_2112, // Cache Type Register + 0b000_0000_0000_010 => 0x00140180, // TCM Size Register (ICTM is 0x8000 bytes, DTCM is 0x4000 bytes) + 0b000_0000_0000_011...0b000_0000_0000_111 => 0x4105_9461, // Unused, return Main ID Register + + 0b000_0001_0000_000 => self.control, // Control Register + + // 0b000_0010_0000_000 => return self.cache_bits_data_unified & ~@as(u32, 0xFFFF_FF00), + // 0b000_0010_0000_001 => return self.cache_bits_instr & ~@as(u32, 0xFFFF_FF00), + // 0b000_0011_0000_000 => return self.cache_write_bufability & ~@as(u32, 0xFFFF_FF00), + 0b000_1001_0001_000 => return self.dtcm_size_base, // Data TCM Size / Base + 0b000_1001_0001_001 => return self.itcm_size_base, // Instruction TCM Size / Base + else => panic("TODO: implement read from register {}, c{}, c{}, {}", .{ op1, cn, cm, op2 }), + }; +} + +// Used in ARMv5TE MCR +pub fn write(self: *@This(), op1: u3, cn: u4, cm: u4, op2: u3, value: u32) void { + switch (addr(op1, cn, cm, op2)) { + 0b000_0001_0000_000 => { // Control Register + + const zeroes: u32 = 0b11111111_11110000_00001111_00000010; // every bit except one_mask + 0, 2, 7, 12..19 are zero + const ones: u32 = 0b00000000_00000000_00000000_01111000; // bits 3..6 are always set + + self.control = (value & ~zeroes) | ones; + }, + 0b000_1001_0001_000 => { // Data TCM Size / Base + const zeroes: u32 = 0b00000000_00000000_00001111_11000001; + + self.dtcm_size_base = value & ~zeroes; + + const size_shamt: u5 = blk: { + const size = self.dtcm_size_base >> 1 & 0x1F; + + if (size < 3) break :blk 3; + if (size > 23) break :blk 23; + + break :blk @intCast(size); + }; + + log.debug("DTCM Virtual Size: {}B", .{@as(u32, 0x200) << size_shamt}); + log.debug("DTCM Region Base: 0x{X:0>8}", .{self.dtcm_size_base & 0xFFFF_F000}); + }, + 0b000_1001_0001_001 => { // Instruction TCM Size / Base + const zeroes: u32 = 0b00000000_00000000_00001111_11000001; + const itcm_specific: u32 = 0b11111111_11111111_11110000_00000000; + + self.itcm_size_base = value & ~(zeroes | itcm_specific); + + const size_shamt: u5 = blk: { + const size = self.dtcm_size_base >> 1 & 0x1F; + + if (size < 3) break :blk 3; + if (size > 23) break :blk 23; + + break :blk @intCast(size); + }; + + log.debug("ICTM Virtual Size: {}B", .{@as(u32, 0x200) << size_shamt}); + log.debug("ICTM Region Base: 0x{X:0>8}", .{0x0000_0000}); + }, + else => _ = panic("TODO: implement write to register {}, c{}, c{}, {}", .{ op1, cn, cm, op2 }), + } +} + +fn addr(op1: u3, cn: u4, cm: u4, op2: u3) u14 { + // 111nnnnmmmm222 + // zig fmt: off + return @as(u14, op1) << 1 + | @as(u14, cn) << 7 + | @as(u14, cm) << 3 + | @as(u14, op2) << 0; + // zig fmt: on +} + +pub fn reset(self: *@This()) void { + _ = self; + @panic("TODO: implement ability to reinit coprocessor"); +} + +fn panic(comptime format: []const u8, args: anytype) u32 { + log.err(format, args); + // @panic("Coprocessor invariant broken"); + return 0; +} diff --git a/src/main.zig b/src/main.zig index 5035c0f..e95c3b1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -7,6 +7,8 @@ const emu = @import("core/emu.zig"); const IBus = @import("arm32").Bus; const IScheduler = @import("arm32").Scheduler; +const ICoprocessor = @import("arm32").Coprocessor; + const Ui = @import("platform.zig").Ui; const SharedContext = @import("core/emu.zig").SharedContext; @@ -42,7 +44,9 @@ pub fn main() !void { const nds9_group: nds9.Group = blk: { var scheduler = try nds9.Scheduler.init(allocator); var bus = try nds9.Bus.init(allocator, &scheduler, shared_ctx); - var arm946es = nds9.Arm946es.init(IScheduler.init(&scheduler), IBus.init(&bus)); + var cp15 = nds9.Cp15{}; + + var arm946es = nds9.Arm946es.init(IScheduler.init(&scheduler), IBus.init(&bus), ICoprocessor.init(&cp15)); break :blk .{ .cpu = &arm946es, .bus = &bus, .scheduler = &scheduler }; };