Archived
Step 5 - 翻译引擎(单步状态机)
This commit is contained in:
@@ -123,11 +123,15 @@ namespace VMdemo.Tests.EditMode
|
||||
state.Reset();
|
||||
|
||||
Assert.That(state.CurrentRound, Is.EqualTo(0));
|
||||
Assert.That(state.CurrentStep, Is.EqualTo(TranslationStep.GenerateVA));
|
||||
Assert.That(state.CurrentVirtualAddress, Is.EqualTo(0UL));
|
||||
Assert.That(state.CurrentAddressParts.Vpn, Is.EqualTo(0UL));
|
||||
Assert.That(state.CurrentAddressParts.Offset, Is.EqualTo(0UL));
|
||||
Assert.That(state.CurrentAddressParts.L1Index, Is.EqualTo(0UL));
|
||||
Assert.That(state.CurrentAddressParts.L2Index, Is.EqualTo(0UL));
|
||||
Assert.That(state.CurrentPhysicalAddress, Is.EqualTo(0UL));
|
||||
Assert.That(state.CurrentPfn, Is.EqualTo(0UL));
|
||||
Assert.IsFalse(state.HasCurrentPfn);
|
||||
Assert.IsFalse(state.IsPageFault);
|
||||
Assert.IsFalse(state.IsTlbHit);
|
||||
Assert.IsFalse(state.IsPageTableHit);
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fc6aae2a95a0224bb777ad21e64526c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user