This repository has been archived on 2026-04-18. You can view files and clone it, but cannot push or open issues or pull requests.
Virtual-Memory-Demo/Assets/Tests/EditMode/Step5TranslatorEngineTests.cs

193 lines
7.2 KiB
C#

using System.Collections.Generic;
using NUnit.Framework;
using VMdemo.Core;
using VMdemo.Simulation;
namespace VMdemo.Tests.EditMode
{
public class Step5TranslatorEngineTests
{
[Test]
public void StepOnce_FirstAccess_VisitsAllStagesInOrder()
{
var engine = CreateEngine(frameCountMb: 1, tlbEntries: 4, seed: 77);
engine.SetNextVirtualAddress(0x0000_1234UL);
var visited = new List<TranslationStep>();
bool completed;
do
{
visited.Add(engine.CurrentStep);
completed = engine.StepOnce();
} while (!completed);
var expected = new[]
{
TranslationStep.GenerateVA,
TranslationStep.SplitVA,
TranslationStep.LookupTLB,
TranslationStep.LookupPageTable,
TranslationStep.HandlePageFault,
TranslationStep.ComposePA,
TranslationStep.Finalize
};
CollectionAssert.AreEqual(expected, visited);
Assert.That(engine.State.CurrentRound, Is.EqualTo(1));
Assert.That(engine.CurrentStep, Is.EqualTo(TranslationStep.GenerateVA));
}
[Test]
public void StepOnce_TlbHitPath_SkipsPageTableAndFaultStages()
{
var engine = CreateEngine(frameCountMb: 1, tlbEntries: 4, seed: 88);
const ulong va = 0x0000_4567UL;
engine.RunOneAccess(va); // warmup: make resident + populate tlb
engine.SetNextVirtualAddress(va);
var visited = new List<TranslationStep>();
bool completed;
do
{
visited.Add(engine.CurrentStep);
completed = engine.StepOnce();
} while (!completed);
var expected = new[]
{
TranslationStep.GenerateVA,
TranslationStep.SplitVA,
TranslationStep.LookupTLB,
TranslationStep.ComposePA,
TranslationStep.Finalize
};
CollectionAssert.AreEqual(expected, visited);
Assert.IsTrue(engine.State.IsTlbHit);
Assert.IsFalse(engine.State.IsPageFault);
}
[Test]
public void StepOnce_PageTableHitPath_WritesBackToTlb()
{
var engine = CreateEngine(frameCountMb: 1, tlbEntries: 2, seed: 99);
const ulong va = 0x0000_89ABUL;
var parts = AddressTranslatorUtils.SplitVirtualAddress(va, 32, engine.OffsetBits);
engine.PageTable.SetPresent(parts.L1Index, parts.L2Index, pfn: 3UL);
engine.SetNextVirtualAddress(va);
var visited = new List<TranslationStep>();
bool completed;
do
{
visited.Add(engine.CurrentStep);
completed = engine.StepOnce();
} while (!completed);
var expected = new[]
{
TranslationStep.GenerateVA,
TranslationStep.SplitVA,
TranslationStep.LookupTLB,
TranslationStep.LookupPageTable,
TranslationStep.ComposePA,
TranslationStep.Finalize
};
CollectionAssert.AreEqual(expected, visited);
Assert.IsFalse(engine.State.IsTlbHit);
Assert.IsTrue(engine.State.IsPageTableHit);
Assert.IsFalse(engine.State.IsPageFault);
Assert.IsTrue(engine.TlbCache.TryLookup(parts.Vpn, out var cachedPfn));
Assert.That(cachedPfn, Is.EqualTo(3UL));
}
[Test]
public void RunOneAccess_WhenMemoryFull_ProducesEvictionInfoAndSyncsState()
{
var config = new SimulationConfig
{
vaBits = 32,
pageSizeKB = 1024, // 1MB per page
physicalMemoryMB = 1, // 1 frame total
tlbEntries = 4,
accessCount = 10,
pageFaultPenalty = 100
};
var engine = new TranslatorEngine(config, seed: 11);
var first = engine.RunOneAccess(0x0000_0123UL);
var second = engine.RunOneAccess(0x0010_0123UL); // different VPN
Assert.IsTrue(first.PageFault);
Assert.IsTrue(second.PageFault);
Assert.IsTrue(second.Eviction.HasEvicted);
Assert.That(second.Eviction.EvictedVpn, Is.EqualTo(first.AddressParts.Vpn));
Assert.IsFalse(engine.TlbCache.TryLookup(first.AddressParts.Vpn, out _));
Assert.That(engine.LastEviction.EvictedVpn, Is.EqualTo(first.AddressParts.Vpn));
}
[Test]
public void RunOneAccess_ForcedVa_MatchesStepByStepOutcome()
{
const ulong va = 0x00AB_CDEFUL;
var engineA = CreateEngine(frameCountMb: 1, tlbEntries: 4, seed: 123);
var result = engineA.RunOneAccess(va);
var engineB = CreateEngine(frameCountMb: 1, tlbEntries: 4, seed: 123);
engineB.SetNextVirtualAddress(va);
while (!engineB.StepOnce())
{
}
Assert.That(engineB.State.CurrentVirtualAddress, Is.EqualTo(result.VirtualAddress));
Assert.That(engineB.State.CurrentAddressParts.Vpn, Is.EqualTo(result.AddressParts.Vpn));
Assert.That(engineB.State.CurrentPfn, Is.EqualTo(result.Pfn));
Assert.That(engineB.State.CurrentPhysicalAddress, Is.EqualTo(result.PhysicalAddress));
Assert.That(engineB.State.IsPageFault, Is.EqualTo(result.PageFault));
Assert.That(engineB.State.IsPageTableHit, Is.EqualTo(result.PageTableHit));
Assert.That(engineB.State.IsTlbHit, Is.EqualTo(result.TlbHit));
Assert.That(engineB.State.CurrentRound, Is.EqualTo(result.Round));
}
[Test]
public void Reset_ClearsStateAndRuntimeCaches()
{
var engine = CreateEngine(frameCountMb: 1, tlbEntries: 4, seed: 222);
engine.RunOneAccess(0x0000_1000UL);
Assert.That(engine.State.CurrentRound, Is.EqualTo(1));
Assert.That(engine.TlbCache.Count, Is.GreaterThan(0));
Assert.That(engine.PhysicalMemoryManager.ResidentCount, Is.GreaterThan(0));
engine.Reset();
Assert.That(engine.State.CurrentRound, Is.EqualTo(0));
Assert.That(engine.State.CurrentStep, Is.EqualTo(TranslationStep.GenerateVA));
Assert.That(engine.State.CurrentVirtualAddress, Is.EqualTo(0UL));
Assert.That(engine.State.HasCurrentPfn, Is.False);
Assert.That(engine.TlbCache.Count, Is.EqualTo(0));
Assert.That(engine.PhysicalMemoryManager.ResidentCount, Is.EqualTo(0));
Assert.That(engine.LastEviction.HasEvicted, Is.False);
}
private static TranslatorEngine CreateEngine(int frameCountMb, int tlbEntries, int seed)
{
var config = new SimulationConfig
{
vaBits = 32,
pageSizeKB = 4,
physicalMemoryMB = frameCountMb,
tlbEntries = tlbEntries,
accessCount = 10,
pageFaultPenalty = 100
};
return new TranslatorEngine(config, seed);
}
}
}