From 4ab4784712144ab3f7800691719918333b9ba804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Wi=C5=9Bniewski?= Date: Thu, 31 Oct 2024 20:11:27 +0100 Subject: [PATCH] Redesign MemoryController --- .../Devices/MemoryTests.cs | 14 +- src/Bytom.Hardware.Tests/Devices/RAMTests.cs | 2 +- src/Bytom.Hardware.Tests/IoControllerTests.cs | 0 src/Bytom.Hardware.Tests/MotherboardTests.cs | 4 +- src/Bytom.Hardware/Address.cs | 1 + src/Bytom.Hardware/CPU/Core.cs | 2 +- src/Bytom.Hardware/Devices/BiosRom.cs | 1 - .../Devices/{MessageReceiver.cs => Device.cs} | 62 ++++--- src/Bytom.Hardware/Devices/Memory.cs | 2 +- .../Devices/MultiAddressDevice.cs | 20 +++ src/Bytom.Hardware/Devices/RAM.cs | 5 - .../Devices/SingleAddressDevice.cs | 16 ++ src/Bytom.Hardware/Devices/Terminal.cs | 0 src/Bytom.Hardware/IoController.cs | 156 +++++++++++++++++ src/Bytom.Hardware/MemoryController.cs | 157 ------------------ src/Bytom.Hardware/Motherboard.cs | 4 +- 16 files changed, 240 insertions(+), 206 deletions(-) create mode 100644 src/Bytom.Hardware.Tests/IoControllerTests.cs rename src/Bytom.Hardware/Devices/{MessageReceiver.cs => Device.cs} (75%) create mode 100644 src/Bytom.Hardware/Devices/MultiAddressDevice.cs create mode 100644 src/Bytom.Hardware/Devices/SingleAddressDevice.cs create mode 100644 src/Bytom.Hardware/Devices/Terminal.cs create mode 100644 src/Bytom.Hardware/IoController.cs delete mode 100644 src/Bytom.Hardware/MemoryController.cs diff --git a/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs b/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs index a4abc44..ae31d80 100644 --- a/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs +++ b/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; +using System.Diagnostics; namespace Bytom.Hardware.Tests { @@ -65,9 +66,16 @@ public static Memory createMemory(uint capacity = 128, uint bandwidth_bytes = 1) public void TestPushIoPowerOff() { var memory = createMemory(); - Assert.Throws( - () => memory!.pushIoMessage(new WriteMessage(Address.zero, 1)) - ); + try + { + memory!.pushIoMessage(new WriteMessage(Address.zero, 1)); + } + catch (Exception e) + { + StringAssert.Contains("Device is powered off", e.Message); + return; + } + Assert.Fail("Expected exception not thrown"); } [Test] diff --git a/src/Bytom.Hardware.Tests/Devices/RAMTests.cs b/src/Bytom.Hardware.Tests/Devices/RAMTests.cs index 2b1726b..dda973f 100644 --- a/src/Bytom.Hardware.Tests/Devices/RAMTests.cs +++ b/src/Bytom.Hardware.Tests/Devices/RAMTests.cs @@ -43,7 +43,7 @@ public class RAMTests : GenericMemoryTests public void TestAllocateAddressRange() { var ram = new RAM(1024, 1000, 1, 1, 1); - var controller = new MemoryController([ram]); + var controller = new IoController([ram], []); controller.powerOn(null); diff --git a/src/Bytom.Hardware.Tests/IoControllerTests.cs b/src/Bytom.Hardware.Tests/IoControllerTests.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/Bytom.Hardware.Tests/MotherboardTests.cs b/src/Bytom.Hardware.Tests/MotherboardTests.cs index 848cac5..7db750b 100644 --- a/src/Bytom.Hardware.Tests/MotherboardTests.cs +++ b/src/Bytom.Hardware.Tests/MotherboardTests.cs @@ -7,7 +7,7 @@ public class MotherboardTests { public RAM? ram; - public MemoryController? controller; + public IoController? controller; public Core? core; public Package? cpu; public Motherboard? motherboard; @@ -22,7 +22,7 @@ public void Setup() write_latency_cycles: 0, bandwidth_bytes: 1 ); - controller = new MemoryController([ram]); + controller = new IoController([ram], []); core = new Core(0, 500); cpu = new Package([core], 128); motherboard = new Motherboard(cpu, controller); diff --git a/src/Bytom.Hardware/Address.cs b/src/Bytom.Hardware/Address.cs index 6b3bca9..b48405d 100644 --- a/src/Bytom.Hardware/Address.cs +++ b/src/Bytom.Hardware/Address.cs @@ -7,6 +7,7 @@ public class Address { private long address { get; } public static Address zero { get { return new Address(0); } } + public static Address max_address { get { return new Address(uint.MaxValue); } } public Address(long address) { this.address = address; diff --git a/src/Bytom.Hardware/CPU/Core.cs b/src/Bytom.Hardware/CPU/Core.cs index d76366e..4ad765b 100644 --- a/src/Bytom.Hardware/CPU/Core.cs +++ b/src/Bytom.Hardware/CPU/Core.cs @@ -238,7 +238,7 @@ public virtual void powerOnInit() thread.Start(); } - public MemoryController GetMemoryController() + public IoController GetMemoryController() { return package!.motherboard!.controller; } diff --git a/src/Bytom.Hardware/Devices/BiosRom.cs b/src/Bytom.Hardware/Devices/BiosRom.cs index 8d6b455..eacd5a9 100644 --- a/src/Bytom.Hardware/Devices/BiosRom.cs +++ b/src/Bytom.Hardware/Devices/BiosRom.cs @@ -6,7 +6,6 @@ namespace Bytom.Hardware { public class FirmwareRom : Memory { - public FirmwareRom( uint capacity_bytes, uint clock_speed_hz, diff --git a/src/Bytom.Hardware/Devices/MessageReceiver.cs b/src/Bytom.Hardware/Devices/Device.cs similarity index 75% rename from src/Bytom.Hardware/Devices/MessageReceiver.cs rename to src/Bytom.Hardware/Devices/Device.cs index d35ce7a..6c4a397 100644 --- a/src/Bytom.Hardware/Devices/MessageReceiver.cs +++ b/src/Bytom.Hardware/Devices/Device.cs @@ -2,14 +2,14 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; namespace Bytom.Hardware { - public class MessageReceiver : IDisposable + public class Device : IDisposable { - public AddressRange? address_range { get; set; } public Clock clock { get; } // Queue for receiving read/write messages. @@ -20,12 +20,12 @@ public class MessageReceiver : IDisposable // dequeuing of read/write messages. public uint max_tasks_running { get; } - public MemoryController? memory_controller; + public IoController? memory_controller; public PowerStatus power_status = PowerStatus.OFF; public bool requested_power_off = false; public Thread? thread = null; - public MessageReceiver(uint max_tasks_running, Clock clock) + public Device(uint max_tasks_running, Clock clock) { this.clock = clock; io_queue = new ConcurrentQueue(); @@ -43,10 +43,7 @@ public void Dispose() public void pushIoMessage(IoMessage message) { - if (power_status != PowerStatus.ON) - { - throw new InvalidOperationException("Memory is not powered on"); - } + Debug.Assert(power_status != PowerStatus.OFF, "Device is powered off"); io_queue.Enqueue(message); } @@ -55,20 +52,26 @@ public bool isDone() return io_queue.IsEmpty && tasks_running.Count == 0; } - public virtual void powerOn(MemoryController? controller) + public virtual void powerOn(IoController? controller) { - if (power_status == PowerStatus.ON) + Debug.Assert(power_status == PowerStatus.OFF, "Device is already powered on"); + Debug.Assert(power_status != PowerStatus.STARTING, "Device is already starting"); + try + { + power_status = PowerStatus.STARTING; + memory_controller = controller; + powerOnInit(); + } + catch (Exception e) { - throw new InvalidOperationException("Memory is already powered on"); + memory_controller = null; + power_status = PowerStatus.OFF; + throw e; } - if (power_status == PowerStatus.STARTING) + finally { - return; + power_status = PowerStatus.ON; } - power_status = PowerStatus.STARTING; - memory_controller = controller; - powerOnInit(); - power_status = PowerStatus.ON; } public virtual void powerOnInit() @@ -102,7 +105,7 @@ public virtual void messageReceiverThread() public virtual void tick() { - runQueuedTasks(); + scheduleQueuedTasks(); advanceRunningTasks(); } @@ -124,7 +127,7 @@ private void advanceRunningTasks() } } - private void runQueuedTasks() + private void scheduleQueuedTasks() { while (tasks_running.Count < max_tasks_running) { @@ -160,16 +163,16 @@ public virtual IEnumerable read(ReadMessage message) public virtual void powerOff() { - if (power_status == PowerStatus.OFF) - { - throw new InvalidOperationException("Memory is already powered off"); - } + Debug.Assert(power_status != PowerStatus.OFF, "Device is already powered off"); + power_status = PowerStatus.STOPPING; // Wait for message buffers to be empty. while (!isDone()) { } powerOffTeardown(); - powerOffCheck(); + + Debug.Assert(power_status == PowerStatus.OFF, "Device failed to power off"); + Debug.Assert(thread?.IsAlive == false, "Device thread is still running"); } public virtual void powerOffTeardown() @@ -177,22 +180,15 @@ public virtual void powerOffTeardown() requested_power_off = true; thread?.Join(); } - public virtual void powerOffCheck() - { - if (power_status != PowerStatus.OFF) - { - throw new InvalidOperationException("Memory failed to power off"); - } - } public PowerStatus getPowerStatus() { return power_status; } - public bool isInMyAddressRange(Address address) + public virtual bool isInMyAddressRange(Address address) { - return address_range?.contains(address) ?? false; + throw new NotImplementedException(); } } } \ No newline at end of file diff --git a/src/Bytom.Hardware/Devices/Memory.cs b/src/Bytom.Hardware/Devices/Memory.cs index 203836d..1307094 100644 --- a/src/Bytom.Hardware/Devices/Memory.cs +++ b/src/Bytom.Hardware/Devices/Memory.cs @@ -5,7 +5,7 @@ namespace Bytom.Hardware { - public class Memory : MessageReceiver + public class Memory : SingleAddressDevice { public uint capacity_bytes { get; } public uint write_latency_cycles { get; } diff --git a/src/Bytom.Hardware/Devices/MultiAddressDevice.cs b/src/Bytom.Hardware/Devices/MultiAddressDevice.cs new file mode 100644 index 0000000..0a70118 --- /dev/null +++ b/src/Bytom.Hardware/Devices/MultiAddressDevice.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Bytom.Hardware +{ + public class MultiAddressDevice : Device + { + public List address_ranges { get; set; } + + public MultiAddressDevice(uint max_tasks_running, Clock clock) + : base(max_tasks_running, clock) + { + address_ranges = new List(); + } + + public override bool isInMyAddressRange(Address address) + { + return address_ranges.Exists(range => range.contains(address)); + } + } +} \ No newline at end of file diff --git a/src/Bytom.Hardware/Devices/RAM.cs b/src/Bytom.Hardware/Devices/RAM.cs index a60563a..b4e5000 100644 --- a/src/Bytom.Hardware/Devices/RAM.cs +++ b/src/Bytom.Hardware/Devices/RAM.cs @@ -24,11 +24,6 @@ uint bandwidth_bytes Array.Fill(memory, 0); } - public override void beforeThreadStart() - { - address_range = memory_controller!.allocateAddressRange(capacity_bytes); - } - public override void powerOff() { base.powerOff(); diff --git a/src/Bytom.Hardware/Devices/SingleAddressDevice.cs b/src/Bytom.Hardware/Devices/SingleAddressDevice.cs new file mode 100644 index 0000000..b6ceab6 --- /dev/null +++ b/src/Bytom.Hardware/Devices/SingleAddressDevice.cs @@ -0,0 +1,16 @@ +namespace Bytom.Hardware +{ + public class SingleAddressDevice : Device + { + public AddressRange? address_range { get; set; } + + public SingleAddressDevice(uint max_tasks_running, Clock clock) + : base(max_tasks_running, clock) + { } + + public override bool isInMyAddressRange(Address address) + { + return address_range?.contains(address) ?? false; + } + } +} \ No newline at end of file diff --git a/src/Bytom.Hardware/Devices/Terminal.cs b/src/Bytom.Hardware/Devices/Terminal.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/Bytom.Hardware/IoController.cs b/src/Bytom.Hardware/IoController.cs new file mode 100644 index 0000000..79934b5 --- /dev/null +++ b/src/Bytom.Hardware/IoController.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + + +namespace Bytom.Hardware +{ + public class IoController + { + public List ram_slots; + public List devices; + public List address_ranges; + public Address forward_allocation_address = Address.zero; + public Address backward_allocation_address = Address.max_address; + public AddressRange? ram_address_range; + public Motherboard? motherboard; + public PowerStatus power_status = PowerStatus.OFF; + + public IoController(List ram_slots, List devices) + { + this.ram_slots = ram_slots; + Debug.Assert(ram_slots.Count != 0, "No RAM devices found"); + Debug.Assert( + ram_slots.Aggregate( + true, + (acc, left) => acc && (left is RAM || left.GetType().IsSubclassOf(typeof(RAM))) + ), + "Not all devices are RAM devices" + ); + + this.devices = devices; + + address_ranges = new List(); + } + + public void pushIoMessage(IoMessage message) + { + Debug.Assert(power_status != PowerStatus.OFF, "MemoryController is powered off"); + + foreach (var ram in ram_slots) + { + if (ram.isInMyAddressRange(message.address)) + { + ram.pushIoMessage(message); + return; + } + } + foreach (var device in devices) + { + if (device.isInMyAddressRange(message.address)) + { + device.pushIoMessage(message); + return; + } + } + } + + public void powerOn(Motherboard? motherboard) + { + Debug.Assert(power_status != PowerStatus.ON, "MemoryController is powered on"); + power_status = PowerStatus.STARTING; + + this.motherboard = motherboard; + + foreach (var ram in ram_slots) + { + ram.powerOn(this); + address_ranges.Add(new AddressRange(forward_allocation_address, ram.getCapacityBytes())); + forward_allocation_address += ram.getCapacityBytes(); + } + Debug.Assert(address_ranges.Count != 0, "No address ranges allocated to RAM devices."); + ram_address_range = AddressRange.FromSpan(Address.zero, address_ranges.Last().end_address); + + foreach (var device in devices) + { + device.powerOn(this); + } + power_status = PowerStatus.ON; + } + + public AddressRange getRamAddressRange() + { + Debug.Assert(power_status != PowerStatus.OFF, "MemoryController is powered off"); + return ram_address_range ?? throw new Exception("RAM address range not allocated"); + } + + public void powerOff() + { + Debug.Assert(power_status != PowerStatus.OFF, "MemoryController is powered off"); + power_status = PowerStatus.STARTING; + + foreach (var device in devices) + { + device.powerOff(); + Debug.Assert(device.getPowerStatus() == PowerStatus.OFF, "Device is still powered on"); + } + foreach (var ram in ram_slots) + { + ram.powerOff(); + Debug.Assert(ram.getPowerStatus() == PowerStatus.OFF, "RAM is still powered on"); + } + address_ranges.Clear(); + motherboard = null; + power_status = PowerStatus.OFF; + + } + + public PowerStatus getPowerStatus() + { + return power_status; + } + + public AddressRange allocateAddressRange(long size) + { + Debug.Assert(power_status != PowerStatus.OFF, "MemoryController is powered off"); + + if (address_ranges.Count == 0) + { + address_ranges.Add(new AddressRange(Address.zero, size)); + return address_ranges.Last(); + } + else + { + var last_address_range = address_ranges.Last(); + var new_address_range = new AddressRange(last_address_range.end_address, size); + if (new_address_range.end_address.ToLong() > 0xFFFFFFFF) + { + // Maybe at some point it could be useful to actually implement + // some algorithm to reuse reclaimed address space. + throw new Exception("Out of address space"); + } + address_ranges.Add(new_address_range); + return new_address_range; + } + } + + public void deallocateAddressRange(Address base_address) + { + Debug.Assert(power_status != PowerStatus.OFF, "MemoryController is powered off"); + + List address_ranges = this.address_ranges; + + for (int i = 0; i < address_ranges.Count; i++) + { + if (address_ranges[i].base_address == base_address) + { + address_ranges.RemoveAt(i); + return; + } + } + throw new Exception($"Base address 0x{base_address.ToLong():X8} not found"); + } + } +} \ No newline at end of file diff --git a/src/Bytom.Hardware/MemoryController.cs b/src/Bytom.Hardware/MemoryController.cs deleted file mode 100644 index d55dc14..0000000 --- a/src/Bytom.Hardware/MemoryController.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - - -namespace Bytom.Hardware -{ - public class MemoryController - { - protected List devices; - protected List ram_address_ranges; - protected List mapped_address_ranges; - protected List address_ranges; - protected Motherboard? motherboard; - protected PowerStatus power_status = PowerStatus.OFF; - - public MemoryController(List devices) - { - this.devices = devices; - ram_address_ranges = new List(); - mapped_address_ranges = new List(); - address_ranges = ram_address_ranges; - } - - public void pushIoMessage(IoMessage message) - { - if (power_status == PowerStatus.OFF) - { - throw new System.Exception("MemoryController is powered off"); - } - foreach (var device in devices) - { - if (device.isInMyAddressRange(message.address)) - { - device.pushIoMessage(message); - } - } - } - - public void powerOn(Motherboard? motherboard) - { - if (power_status == PowerStatus.ON) - { - throw new System.Exception("MemoryController is already powered on"); - } - power_status = PowerStatus.STARTING; - - this.motherboard = motherboard; - - Func isRamLike = device => (device is RAM || device.GetType().IsSubclassOf(typeof(RAM))); - - address_ranges = ram_address_ranges; - foreach (var device in devices) - { - if (isRamLike(device)) - device.powerOn(this); - } - - if (ram_address_ranges.Count == 0) - { - throw new System.Exception("No RAM devices found"); - } - - address_ranges = mapped_address_ranges; - mapped_address_ranges.Add( - AddressRange.FromSpan(Address.zero, ram_address_ranges.Last().end_address) - ); - foreach (var device in devices) - { - if (!isRamLike(device)) - device.powerOn(this); - } - power_status = PowerStatus.ON; - } - - public AddressRange getRamAddressRange() - { - if (power_status == PowerStatus.OFF) - { - throw new System.Exception("MemoryController is powered off"); - } - if (ram_address_ranges.Count == 0) - { - throw new System.Exception("No RAM devices found"); - } - if (mapped_address_ranges.Count == 0) - { - throw new System.Exception("No mapped devices found"); - } - return mapped_address_ranges.First(); - } - - public void powerOff() - { - if (power_status == PowerStatus.OFF) - { - throw new System.Exception("MemoryController is already powered off"); - } - foreach (var device in devices) - { - device.powerOff(); - } - ram_address_ranges.Clear(); - mapped_address_ranges.Clear(); - motherboard = null; - power_status = PowerStatus.OFF; - } - - public PowerStatus getPowerStatus() - { - return power_status; - } - - public AddressRange allocateAddressRange(long size) - { - if (power_status == PowerStatus.OFF) - { - throw new System.Exception("MemoryController is powered off"); - } - if (address_ranges.Count == 0) - { - address_ranges.Add(new AddressRange(Address.zero, size)); - return address_ranges.Last(); - } - else - { - var last_address_range = address_ranges.Last(); - var new_address_range = new AddressRange(last_address_range.end_address, size); - if (new_address_range.end_address.ToLong() > 0xFFFFFFFF) - { - // Maybe at some point it could be useful to actually implement - // some algorithm to reuse reclaimed address space. - throw new System.Exception("Out of address space"); - } - address_ranges.Add(new_address_range); - return new_address_range; - } - } - - public void deallocateAddressRange(Address base_address) - { - if (power_status == PowerStatus.OFF) - { - throw new System.Exception("MemoryController is powered off"); - } - for (int i = 0; i < address_ranges.Count; i++) - { - if (address_ranges[i].base_address == base_address) - { - address_ranges.RemoveAt(i); - return; - } - } - throw new System.Exception($"Base address 0x{base_address.ToLong():X8} not found"); - } - } -} \ No newline at end of file diff --git a/src/Bytom.Hardware/Motherboard.cs b/src/Bytom.Hardware/Motherboard.cs index 93baed4..3b31d00 100644 --- a/src/Bytom.Hardware/Motherboard.cs +++ b/src/Bytom.Hardware/Motherboard.cs @@ -7,13 +7,13 @@ public enum PowerStatus { ON, OFF, STARTING, STOPPING } public class Motherboard { public CPU.Package cpu { get; } - public MemoryController controller { get; } + public IoController controller { get; } protected PowerStatus power_status; protected Clock clock; public Motherboard( CPU.Package cpu, - MemoryController controller + IoController controller ) { this.cpu = cpu;