diff --git a/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs b/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs new file mode 100644 index 0000000..99b453b --- /dev/null +++ b/src/Bytom.Hardware.Tests/Devices/MemoryTests.cs @@ -0,0 +1,273 @@ + +using System.Collections.Concurrent; + +namespace Bytom.Hardware.Tests +{ + public class MemoryTests + { + public class DebugMemory : Memory + { + public DebugMemory( + uint capacity_bytes, + uint write_latency_cycles, + uint read_latency_cycles, + uint bandwidth_bytes + ) : base( + capacity_bytes: capacity_bytes, + write_latency_cycles: write_latency_cycles, + read_latency_cycles: read_latency_cycles, + bandwidth_bytes: bandwidth_bytes, + new Clock(1000) + ) + { } + + public override void powerOnInit() + { } + } + + public static Memory createDebugMemory(uint capacity = 128, uint bandwidth_bytes = 1) + { + return new DebugMemory( + capacity_bytes: capacity, + write_latency_cycles: 1, + read_latency_cycles: 1, + bandwidth_bytes: bandwidth_bytes + ); + } + + public static Memory createMemory(uint capacity = 128, uint bandwidth_bytes = 1) + { + return new Memory( + capacity_bytes: capacity, + write_latency_cycles: 1, + read_latency_cycles: 1, + bandwidth_bytes: bandwidth_bytes, + new Clock(1000) + ); + } + + [Test] + public void TestPushIoPowerOff() + { + var memory = createMemory(); + Assert.Throws( + () => memory!.pushIoMessage(new WriteMessage(Address.zero, 1)) + ); + } + + [Test] + public void TestPowerOn() + { + var memory = createMemory(); + memory!.powerOn(null); + Assert.That(memory!.getPowerStatus(), Is.EqualTo(PowerStatus.ON)); + } + + [Test] + public void TestManualPowerCycle() + { + var memory = createMemory(); + memory!.powerOn(null); + Assert.That(memory!.getPowerStatus(), Is.EqualTo(PowerStatus.ON)); + memory!.powerOff(); + Assert.That(memory!.getPowerStatus(), Is.EqualTo(PowerStatus.OFF)); + Assert.That(memory!.thread!.IsAlive, Is.False); + } + + [Test] + public void TestDisposePowerOff() + { + using (var memory = createMemory()) + { + Assert.That(memory!.getPowerStatus(), Is.EqualTo(PowerStatus.OFF)); + } + } + + [Test] + public void TestAutoPowerOff() + { + Thread? thread_handle; + using (var memory = createMemory()) + { + memory!.powerOn(null); + thread_handle = memory.thread; + Assert.That(memory!.getPowerStatus(), Is.EqualTo(PowerStatus.ON)); + } + Assert.That(thread_handle!.IsAlive, Is.False); + } + + [Test] + public void TestWriteScheduleIo() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + scheduleWriteIo(capacity, memory); + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(capacity)); + } + + private static void scheduleWriteIo(uint capacity, Memory memory) + { + for (var i = 0; i < capacity; i++) + { + memory!.pushIoMessage(new WriteMessage(new Address(i), 1)); + } + } + + [Test] + public void TestWriteStartRunningIo() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + scheduleWriteIo(capacity, memory); + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(capacity)); + + memory!.tick(); + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(capacity - 1)); + Assert.That(memory.queue.tasks_running.Count, Is.EqualTo(1)); + } + + [Test] + public void TestWriteManualTickAll() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + scheduleWriteIo(capacity, memory); + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(capacity)); + + for (var i = 0; i < capacity * 2; i++) + { + memory!.tick(); + } + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(0)); + Assert.That(memory.queue.tasks_running.Count, Is.EqualTo(0)); + + for (var i = 0; i < capacity; i++) + { + Assert.That(memory.memory[i], Is.EqualTo(1)); + } + } + + [Test] + public void TestWriteRunAuto() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + scheduleWriteIo(capacity, memory); + + memory!.startWorkerThread(); + + Thread.Sleep(2_000); + Assert.That(memory!.queue.isDone(), Is.True); + + memory!.powerOff(); + + Assert.That(memory!.getPowerStatus(), Is.EqualTo(PowerStatus.OFF)); + Assert.That(memory!.thread!.IsAlive, Is.False); + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(0)); + Assert.That(memory.queue.tasks_running.Count, Is.EqualTo(0)); + + for (var i = 0; i < capacity; i++) + { + Assert.That(memory.memory[i], Is.EqualTo(1)); + } + } + + [Test] + public void TestReadScheduleIo() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + var writeBackBuffer = new ConcurrentQueue(); + + for (var i = 0; i < capacity; i++) + { + memory!.pushIoMessage(new ReadMessage(new Address(i), writeBackBuffer)); + } + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(capacity)); + Assert.That(memory.queue.tasks_running.Count, Is.EqualTo(0)); + Assert.That(writeBackBuffer.Count, Is.EqualTo(0)); + } + + [Test] + public void TestReadManualIo() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + var writeBackBuffer = new ConcurrentQueue(); + + for (var i = 0; i < capacity; i++) + { + memory!.pushIoMessage(new ReadMessage(new Address(i), writeBackBuffer)); + } + for (var i = 0; i < capacity * 2; i++) + { + memory!.tick(); + } + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(0)); + Assert.That(memory.queue.tasks_running.Count, Is.EqualTo(0)); + Assert.That(writeBackBuffer.Count, Is.EqualTo(capacity)); + + for (var i = 0; i < capacity; i++) + { + Assert.That(writeBackBuffer.TryDequeue(out var message), Is.True); + Assert.That(message!.data, Is.EqualTo(0)); + } + } + + [Test] + public void TestReadAutoIo() + { + var capacity = 128u; + var memory = createDebugMemory(capacity); + + memory!.powerOn(null); + memory!.address_range = new AddressRange(new Address(0), capacity); + var writeBackBuffer = new ConcurrentQueue(); + + for (var i = 0; i < capacity; i++) + { + memory!.pushIoMessage(new ReadMessage(new Address(i), writeBackBuffer)); + } + + memory!.startWorkerThread(); + + Thread.Sleep(2_000); + Assert.That(memory!.queue.isDone(), Is.True); + + Assert.That(memory.queue.io_queue.Count, Is.EqualTo(0)); + Assert.That(writeBackBuffer.Count, Is.EqualTo(capacity)); + + for (var i = 0; i < capacity; i++) + { + Assert.That(writeBackBuffer.TryDequeue(out var message), Is.True); + Assert.That(message!.data, Is.EqualTo(0)); + } + + memory!.powerOff(); + } + } +} diff --git a/src/Bytom.Hardware.Tests/MotherboardTests.cs b/src/Bytom.Hardware.Tests/MotherboardTests.cs index 24bf709..848cac5 100644 --- a/src/Bytom.Hardware.Tests/MotherboardTests.cs +++ b/src/Bytom.Hardware.Tests/MotherboardTests.cs @@ -15,7 +15,13 @@ public class MotherboardTests [SetUp] public void Setup() { - ram = new RAM(256, 500, 0, 0); + ram = new RAM( + capacity_bytes: 256, + clock_speed_hz: 500, + read_latency_cycles: 0, + write_latency_cycles: 0, + bandwidth_bytes: 1 + ); controller = new MemoryController([ram]); core = new Core(0, 500); cpu = new Package([core], 128); diff --git a/src/Bytom.Hardware/BiosRom.cs b/src/Bytom.Hardware/BiosRom.cs deleted file mode 100644 index 5f598d7..0000000 --- a/src/Bytom.Hardware/BiosRom.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; - -namespace Bytom.Hardware -{ - public class FirmwareRom : MessageReceiver - { - public Latency read_latency { get; } - public Latency write_latency { get; } - public long capacity_bytes { get; } - private byte[] memory; - - public FirmwareRom( - long capacity_bytes, - Latency read_latency, - Latency write_latency - ) : base(new Clock(1)) - { - this.capacity_bytes = capacity_bytes; - this.read_latency = read_latency; - this.write_latency = write_latency; - if (capacity_bytes < 0) - { - throw new Exception("capacity_bytes must be greater than 0"); - } - memory = new byte[this.capacity_bytes]; - } - - public override void write(WriteMessage message) - { - clock.waitMicroseconds(write_latency.ToMicroseconds()); - if (isInMyAddressRange(message.address)) - { - var address = message.address - address_range!.base_address; - memory[address.ToLong()] = message.data; - } - else - { - throw new Exception($"Address 0x{message.address.ToLong():X8} out of range"); - } - } - - public override void read(ReadMessage message) - { - clock.waitMicroseconds(read_latency.ToMicroseconds()); - if (isInMyAddressRange(message.address)) - { - var address = message.address - address_range!.base_address; - var value = memory[address.ToLong()]; - message.writeBackQueue.Enqueue(new WriteMessage(message.address, value)); - } - else - { - throw new Exception($"Address 0x{message.address.ToLong():X8} out of range"); - } - } - } -} \ No newline at end of file diff --git a/src/Bytom.Hardware/CPU/MicoOp.cs b/src/Bytom.Hardware/CPU/MicoOp.cs index a84b62f..2ee70a6 100644 --- a/src/Bytom.Hardware/CPU/MicoOp.cs +++ b/src/Bytom.Hardware/CPU/MicoOp.cs @@ -40,7 +40,7 @@ public override IEnumerable execute(Core core) for (int i = 0; i < register_size; i++) { - core.GetMemoryController().pushRead(new ReadMessage(address_value + i, writeBackQueue)); + core.GetMemoryController().pushIoMessage(new ReadMessage(address_value + i, writeBackQueue)); } var bytes_read = 0; @@ -70,7 +70,7 @@ public override IEnumerable execute(Core core) for (int i = 0; i < register_size; i++) { - core.GetMemoryController().pushWrite(new WriteMessage(address_value + i, buffer[i])); + core.GetMemoryController().pushIoMessage(new WriteMessage(address_value + i, buffer[i])); } yield break; } diff --git a/src/Bytom.Hardware/Clock.cs b/src/Bytom.Hardware/Clock.cs index 920a86d..dcb9b88 100644 --- a/src/Bytom.Hardware/Clock.cs +++ b/src/Bytom.Hardware/Clock.cs @@ -1,4 +1,6 @@ +using System; using System.Diagnostics; +using System.Threading; namespace Bytom.Hardware @@ -20,6 +22,11 @@ public long getCycleLengthMicroseconds() return 1_000_000 / frequency_hz; } + public virtual TickDisposable startTick() + { + return new TickDisposable(this); + } + public void waitForCycles(uint cycles) { var microseconds = cycles * getCycleLengthMicroseconds(); @@ -44,4 +51,31 @@ public void waitMicroseconds(long microseconds) stopwatch.Stop(); } } + + public class TickDisposable : IDisposable + { + private Clock clock; + private Stopwatch stopwatch; + public TickDisposable(Clock clock) + { + this.clock = clock; + this.stopwatch = Stopwatch.StartNew(); + } + + public void Dispose() + { + var ticks_per_cycle = Stopwatch.Frequency / clock.frequency_hz; + while (stopwatch.ElapsedTicks < ticks_per_cycle) + { + // Busy-wait + } + } + } + + public class PerformanceClock : Clock + { + public PerformanceClock(uint frequency_hz) : base(frequency_hz) + { + } + } } \ No newline at end of file diff --git a/src/Bytom.Hardware/Devices/BiosRom.cs b/src/Bytom.Hardware/Devices/BiosRom.cs new file mode 100644 index 0000000..8d6b455 --- /dev/null +++ b/src/Bytom.Hardware/Devices/BiosRom.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using Bytom.Tools; + +namespace Bytom.Hardware +{ + public class FirmwareRom : Memory + { + + public FirmwareRom( + uint capacity_bytes, + uint clock_speed_hz, + uint write_latency_cycles, + uint read_latency_cycles, + uint bandwidth_bytes + ) : base(capacity_bytes, write_latency_cycles, read_latency_cycles, bandwidth_bytes, new Clock(clock_speed_hz)) + { } + } +} \ No newline at end of file diff --git a/src/Bytom.Hardware/RAM.cs b/src/Bytom.Hardware/Devices/Memory.cs similarity index 70% rename from src/Bytom.Hardware/RAM.cs rename to src/Bytom.Hardware/Devices/Memory.cs index 105fafd..203836d 100644 --- a/src/Bytom.Hardware/RAM.cs +++ b/src/Bytom.Hardware/Devices/Memory.cs @@ -1,43 +1,36 @@ using System; +using System.Collections; +using Bytom.Tools; namespace Bytom.Hardware { - public class RAM : MessageReceiver + public class Memory : MessageReceiver { public uint capacity_bytes { get; } public uint write_latency_cycles { get; } public uint read_latency_cycles { get; } public byte[] memory { get; } - public RAM( + public Memory( uint capacity_bytes, - uint clock_speed_hz, uint write_latency_cycles, - uint read_latency_cycles - ) : base(new Clock(clock_speed_hz)) + uint read_latency_cycles, + uint bandwidth_bytes, + Clock clock + ) : base(bandwidth_bytes, clock) { this.capacity_bytes = capacity_bytes; this.write_latency_cycles = write_latency_cycles; this.read_latency_cycles = read_latency_cycles; memory = new byte[capacity_bytes]; - Array.Fill(memory, 0); } - public override void beforeThreadStart() + public override IEnumerable write(WriteMessage message) { - address_range = memory_controller!.allocateAddressRange(capacity_bytes); - } + foreach (var _ in Itertools.yieldVoidTimes(write_latency_cycles)) + { yield return null; } - public override void powerOff() - { - base.powerOff(); - Array.Fill(memory, 0); - } - - public override void write(WriteMessage message) - { - clock.waitForCycles(write_latency_cycles); if (isInMyAddressRange(message.address)) { var address = message.address - address_range!.base_address; @@ -49,9 +42,11 @@ public override void write(WriteMessage message) } } - public override void read(ReadMessage message) + public override IEnumerable read(ReadMessage message) { - clock.waitForCycles(read_latency_cycles); + foreach (var _ in Itertools.yieldVoidTimes(read_latency_cycles)) + { yield return null; } + if (isInMyAddressRange(message.address)) { var address = message.address - address_range!.base_address; @@ -64,6 +59,11 @@ public override void read(ReadMessage message) } } + public uint getCapacityBytes() + { + return capacity_bytes; + } + public void writeDebug(byte[] data, int index) { data.CopyTo(memory, index); diff --git a/src/Bytom.Hardware/Devices/MessageReceiver.cs b/src/Bytom.Hardware/Devices/MessageReceiver.cs new file mode 100644 index 0000000..871a201 --- /dev/null +++ b/src/Bytom.Hardware/Devices/MessageReceiver.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + + +namespace Bytom.Hardware +{ + + public class IoQueue + { + public ConcurrentQueue io_queue { get; } + public List tasks_running { get; } + public uint max_tasks_running { get; } + + public IoQueue(uint max_tasks_running = 1) + { + io_queue = new ConcurrentQueue(); + tasks_running = new List(); + this.max_tasks_running = max_tasks_running; + } + + public bool isDone() + { + return io_queue.IsEmpty && tasks_running.Count == 0; + } + + public void push(IoMessage message) + { + io_queue.Enqueue(message); + } + } + + public class MessageReceiver : IDisposable + { + public AddressRange? address_range { get; set; } + public Clock clock { get; } + public IoQueue queue { get; } + public MemoryController? 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) + { + this.clock = clock; + queue = new IoQueue(max_tasks_running); + } + + public void Dispose() + { + if (power_status == PowerStatus.ON && !requested_power_off) + { + powerOff(); + } + } + + public void pushIoMessage(IoMessage message) + { + if (power_status != PowerStatus.ON) + { + throw new InvalidOperationException("Memory is not powered on"); + } + queue.push(message); + } + + public virtual void powerOn(MemoryController? controller) + { + if (power_status == PowerStatus.ON) + { + throw new InvalidOperationException("Memory is already powered on"); + } + power_status = PowerStatus.STARTING; + memory_controller = controller; + powerOnInit(); + power_status = PowerStatus.ON; + } + + public virtual void powerOnInit() + { + beforeThreadStart(); + startWorkerThread(); + } + + public virtual void beforeThreadStart() + { + } + + public void startWorkerThread() + { + thread = new Thread(messageReceiverThread); + thread.Start(); + } + + public virtual void messageReceiverThread() + { + while (!requested_power_off) + { + using (clock.startTick()) + { + tick(); + } + } + requested_power_off = false; + power_status = PowerStatus.OFF; + } + + public virtual void tick() + { + runQueuedTasks(); + advanceRunningTasks(); + } + + private void advanceRunningTasks() + { + var tasks_to_remove = new List(); + + foreach (var task in queue.tasks_running) + { + if (!task.MoveNext()) + { + tasks_to_remove.Add(task); + } + } + + foreach (var task in tasks_to_remove) + { + queue.tasks_running.Remove(task); + } + } + + private void runQueuedTasks() + { + while (queue.tasks_running.Count < queue.max_tasks_running) + { + if (queue.io_queue.TryDequeue(out var message)) + { + if (message is WriteMessage) + queue.tasks_running.Add( + write((WriteMessage)message).GetEnumerator() + ); + else if (message is ReadMessage) + queue.tasks_running.Add( + read((ReadMessage)message).GetEnumerator() + ); + else + throw new InvalidOperationException("Unknown message type"); + } + else + { + break; + } + } + } + + public virtual IEnumerable write(WriteMessage message) + { + throw new NotImplementedException(); + } + + public virtual IEnumerable read(ReadMessage message) + { + throw new NotImplementedException(); + } + + public virtual void powerOff() + { + if (power_status == PowerStatus.OFF) + { + throw new InvalidOperationException("Memory is already powered off"); + } + power_status = PowerStatus.STOPPING; + // Wait for message buffers to be empty. + while (!queue.isDone()) + { } + powerOffTeardown(); + powerOffCheck(); + } + + 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) + { + return address_range?.contains(address) ?? false; + } + } +} \ No newline at end of file diff --git a/src/Bytom.Hardware/Devices/RAM.cs b/src/Bytom.Hardware/Devices/RAM.cs new file mode 100644 index 0000000..a60563a --- /dev/null +++ b/src/Bytom.Hardware/Devices/RAM.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using Bytom.Tools; + + +namespace Bytom.Hardware +{ + public class RAM : Memory + { + public RAM( + uint capacity_bytes, + uint clock_speed_hz, + uint write_latency_cycles, + uint read_latency_cycles, + uint bandwidth_bytes + ) : base( + capacity_bytes, + write_latency_cycles, + read_latency_cycles, + bandwidth_bytes, + new Clock(clock_speed_hz) + ) + { + Array.Fill(memory, 0); + } + + public override void beforeThreadStart() + { + address_range = memory_controller!.allocateAddressRange(capacity_bytes); + } + + public override void powerOff() + { + base.powerOff(); + Array.Fill(memory, 0); + } + } +} \ No newline at end of file diff --git a/src/Bytom.Hardware/Latency.cs b/src/Bytom.Hardware/Latency.cs deleted file mode 100644 index ed38444..0000000 --- a/src/Bytom.Hardware/Latency.cs +++ /dev/null @@ -1,30 +0,0 @@ - -namespace Bytom.Hardware -{ - public class Latency - { - public long microseconds { get; } - - public static Latency zero { get { return new Latency(0); } } - - public Latency(long microseconds) - { - this.microseconds = microseconds; - } - - public Latency FromMicroseconds(long microseconds) - { - return new Latency(microseconds); - } - - public Latency FromMilliseconds(long milliseconds) - { - return new Latency(milliseconds * 1000); - } - - public long ToMicroseconds() - { - return microseconds; - } - } -} \ No newline at end of file diff --git a/src/Bytom.Hardware/MemoryController.cs b/src/Bytom.Hardware/MemoryController.cs index 84b6698..e293192 100644 --- a/src/Bytom.Hardware/MemoryController.cs +++ b/src/Bytom.Hardware/MemoryController.cs @@ -21,7 +21,7 @@ public MemoryController(List devices) address_ranges = ram_address_ranges; } - public void pushWrite(WriteMessage message) + public void pushIoMessage(IoMessage message) { if (power_status == PowerStatus.OFF) { @@ -31,22 +31,7 @@ public void pushWrite(WriteMessage message) { if (device.isInMyAddressRange(message.address)) { - device.pushWrite(message); - } - } - } - - public void pushRead(ReadMessage 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.pushRead(message); + device.pushIoMessage(message); } } } diff --git a/src/Bytom.Hardware/MessageReceiver.cs b/src/Bytom.Hardware/MessageReceiver.cs deleted file mode 100644 index 2a7d47f..0000000 --- a/src/Bytom.Hardware/MessageReceiver.cs +++ /dev/null @@ -1,130 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Threading; - - -namespace Bytom.Hardware -{ - - public class MessageReceiver - { - public AddressRange? address_range { get; set; } - protected Clock clock { get; } - protected ConcurrentQueue write_queue { get; } - protected ConcurrentQueue read_queue { get; } - protected MemoryController? memory_controller; - protected PowerStatus power_status = PowerStatus.OFF; - protected bool requested_power_off = false; - protected Thread? thread = null; - - public MessageReceiver(Clock clock) - { - this.clock = clock; - write_queue = new ConcurrentQueue(); - read_queue = new ConcurrentQueue(); - } - - public void pushRead(ReadMessage message) - { - read_queue.Enqueue(message); - } - - public void pushWrite(WriteMessage message) - { - write_queue.Enqueue(message); - } - - public virtual void powerOn(MemoryController controller) - { - if (power_status == PowerStatus.ON) - { - throw new InvalidOperationException("Memory is already powered on"); - } - power_status = PowerStatus.STARTING; - memory_controller = controller; - powerOnInit(); - power_status = PowerStatus.ON; - } - - public virtual void powerOnInit() - { - beforeThreadStart(); - thread = new Thread(messageReceiverThread); - thread.Start(); - } - - public virtual void beforeThreadStart() - { - } - - public virtual void messageReceiverThread() - { - while (!requested_power_off) - { - receiveTick(); - } - requested_power_off = false; - power_status = PowerStatus.OFF; - } - - public virtual void receiveTick() - { - clock.waitForCycles(1); - if (write_queue.TryDequeue(out var write_message)) - { - write(write_message); - } - if (read_queue.TryDequeue(out var read_message)) - { - read(read_message); - } - } - - public virtual void write(WriteMessage message) - { - throw new NotImplementedException(); - } - - public virtual void read(ReadMessage message) - { - throw new NotImplementedException(); - } - - public virtual void powerOff() - { - if (power_status == PowerStatus.OFF) - { - throw new InvalidOperationException("Memory is already powered off"); - } - power_status = PowerStatus.STOPPING; - // Wait for message buffers to be empty. - while (write_queue.Count > 0 || read_queue.Count > 0) - { } - powerOffTeardown(); - powerOffCheck(); - } - - 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) - { - return address_range?.contains(address) ?? false; - } - } -} \ No newline at end of file diff --git a/src/Bytom.Tools/Itertools.cs b/src/Bytom.Tools/Itertools.cs new file mode 100644 index 0000000..bbc2653 --- /dev/null +++ b/src/Bytom.Tools/Itertools.cs @@ -0,0 +1,15 @@ +using System.Collections; + +namespace Bytom.Tools +{ + public static class Itertools + { + public static IEnumerable yieldVoidTimes(long count) + { + for (long i = 0; i < count; i++) + { + yield return null; + } + } + } +} \ No newline at end of file