Step 5 - 翻译引擎(单步状态机)
This commit is contained in:
parent
38aa8e9bf4
commit
61e0034800
|
|
@ -7,8 +7,12 @@ namespace VMdemo.Simulation
|
||||||
public class SimulationState
|
public class SimulationState
|
||||||
{
|
{
|
||||||
public int CurrentRound { get; set; }
|
public int CurrentRound { get; set; }
|
||||||
|
public TranslationStep CurrentStep { get; set; }
|
||||||
public ulong CurrentVirtualAddress { get; set; }
|
public ulong CurrentVirtualAddress { get; set; }
|
||||||
public AddressParts CurrentAddressParts { get; set; }
|
public AddressParts CurrentAddressParts { get; set; }
|
||||||
|
public ulong CurrentPhysicalAddress { get; set; }
|
||||||
|
public ulong CurrentPfn { get; set; }
|
||||||
|
public bool HasCurrentPfn { get; set; }
|
||||||
public bool IsPageFault { get; set; }
|
public bool IsPageFault { get; set; }
|
||||||
public bool IsTlbHit { get; set; }
|
public bool IsTlbHit { get; set; }
|
||||||
public bool IsPageTableHit { get; set; }
|
public bool IsPageTableHit { get; set; }
|
||||||
|
|
@ -17,8 +21,12 @@ namespace VMdemo.Simulation
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
CurrentRound = 0;
|
CurrentRound = 0;
|
||||||
|
CurrentStep = TranslationStep.GenerateVA;
|
||||||
CurrentVirtualAddress = 0UL;
|
CurrentVirtualAddress = 0UL;
|
||||||
CurrentAddressParts = AddressParts.Empty;
|
CurrentAddressParts = AddressParts.Empty;
|
||||||
|
CurrentPhysicalAddress = 0UL;
|
||||||
|
CurrentPfn = 0UL;
|
||||||
|
HasCurrentPfn = false;
|
||||||
IsPageFault = false;
|
IsPageFault = false;
|
||||||
IsTlbHit = false;
|
IsTlbHit = false;
|
||||||
IsPageTableHit = false;
|
IsPageTableHit = false;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,293 @@
|
||||||
|
using System;
|
||||||
|
using VMdemo.Core;
|
||||||
|
|
||||||
|
namespace VMdemo.Simulation
|
||||||
|
{
|
||||||
|
public enum TranslationStep
|
||||||
|
{
|
||||||
|
GenerateVA = 0,
|
||||||
|
SplitVA = 1,
|
||||||
|
LookupTLB = 2,
|
||||||
|
LookupPageTable = 3,
|
||||||
|
HandlePageFault = 4,
|
||||||
|
ComposePA = 5,
|
||||||
|
Finalize = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct AccessExecutionResult
|
||||||
|
{
|
||||||
|
public AccessExecutionResult(
|
||||||
|
int round,
|
||||||
|
ulong virtualAddress,
|
||||||
|
AddressParts addressParts,
|
||||||
|
ulong pfn,
|
||||||
|
ulong physicalAddress,
|
||||||
|
bool tlbHit,
|
||||||
|
bool pageTableHit,
|
||||||
|
bool pageFault,
|
||||||
|
EvictionInfo eviction)
|
||||||
|
{
|
||||||
|
Round = round;
|
||||||
|
VirtualAddress = virtualAddress;
|
||||||
|
AddressParts = addressParts;
|
||||||
|
Pfn = pfn;
|
||||||
|
PhysicalAddress = physicalAddress;
|
||||||
|
TlbHit = tlbHit;
|
||||||
|
PageTableHit = pageTableHit;
|
||||||
|
PageFault = pageFault;
|
||||||
|
Eviction = eviction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Round { get; }
|
||||||
|
public ulong VirtualAddress { get; }
|
||||||
|
public AddressParts AddressParts { get; }
|
||||||
|
public ulong Pfn { get; }
|
||||||
|
public ulong PhysicalAddress { get; }
|
||||||
|
public bool TlbHit { get; }
|
||||||
|
public bool PageTableHit { get; }
|
||||||
|
public bool PageFault { get; }
|
||||||
|
public EvictionInfo Eviction { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TranslatorEngine
|
||||||
|
{
|
||||||
|
private readonly SimulationConfig _config;
|
||||||
|
private readonly ConfigValidator.DerivedConfig _derivedConfig;
|
||||||
|
private readonly int? _seed;
|
||||||
|
|
||||||
|
private AddressGenerator _addressGenerator;
|
||||||
|
private TlbCache _tlbCache;
|
||||||
|
private TwoLevelPageTable _pageTable;
|
||||||
|
private PhysicalMemoryManager _physicalMemoryManager;
|
||||||
|
|
||||||
|
private bool _hasPendingForcedVirtualAddress;
|
||||||
|
private ulong _pendingForcedVirtualAddress;
|
||||||
|
private EvictionInfo _lastEviction;
|
||||||
|
|
||||||
|
public TranslatorEngine(SimulationConfig config, int? seed = null)
|
||||||
|
{
|
||||||
|
if (!ConfigValidator.TryValidateAndBuildDerived(config, out var derived, out var errorMessage))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(errorMessage, nameof(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
_config = config;
|
||||||
|
_derivedConfig = derived;
|
||||||
|
_seed = seed;
|
||||||
|
State = new SimulationState();
|
||||||
|
|
||||||
|
AddressTranslatorUtils.GetPageTableBitLayout(derived.VpnBits, out var l1Bits, out var l2Bits);
|
||||||
|
L1Bits = l1Bits;
|
||||||
|
L2Bits = l2Bits;
|
||||||
|
|
||||||
|
_addressGenerator = new AddressGenerator(_config.vaBits, _seed);
|
||||||
|
_tlbCache = new TlbCache(_config.tlbEntries);
|
||||||
|
_pageTable = new TwoLevelPageTable(L1Bits, L2Bits);
|
||||||
|
_physicalMemoryManager = new PhysicalMemoryManager(_derivedConfig.FrameCount);
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimulationState State { get; }
|
||||||
|
public TranslationStep CurrentStep => State.CurrentStep;
|
||||||
|
public int OffsetBits => _derivedConfig.OffsetBits;
|
||||||
|
public int L1Bits { get; }
|
||||||
|
public int L2Bits { get; }
|
||||||
|
public ulong MaxVirtualAddress => _addressGenerator.MaxVirtualAddress;
|
||||||
|
public TlbCache TlbCache => _tlbCache;
|
||||||
|
public TwoLevelPageTable PageTable => _pageTable;
|
||||||
|
public PhysicalMemoryManager PhysicalMemoryManager => _physicalMemoryManager;
|
||||||
|
public EvictionInfo LastEviction => _lastEviction;
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
State.Reset();
|
||||||
|
_lastEviction = EvictionInfo.None;
|
||||||
|
_hasPendingForcedVirtualAddress = false;
|
||||||
|
_pendingForcedVirtualAddress = 0UL;
|
||||||
|
|
||||||
|
_addressGenerator = new AddressGenerator(_config.vaBits, _seed);
|
||||||
|
_tlbCache = new TlbCache(_config.tlbEntries);
|
||||||
|
_pageTable = new TwoLevelPageTable(L1Bits, L2Bits);
|
||||||
|
_physicalMemoryManager = new PhysicalMemoryManager(_derivedConfig.FrameCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetNextVirtualAddress(ulong virtualAddress)
|
||||||
|
{
|
||||||
|
EnsureVirtualAddressInRange(virtualAddress);
|
||||||
|
_pendingForcedVirtualAddress = virtualAddress;
|
||||||
|
_hasPendingForcedVirtualAddress = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool StepOnce()
|
||||||
|
{
|
||||||
|
switch (State.CurrentStep)
|
||||||
|
{
|
||||||
|
case TranslationStep.GenerateVA:
|
||||||
|
ExecuteGenerateVa();
|
||||||
|
return false;
|
||||||
|
case TranslationStep.SplitVA:
|
||||||
|
ExecuteSplitVa();
|
||||||
|
return false;
|
||||||
|
case TranslationStep.LookupTLB:
|
||||||
|
ExecuteLookupTlb();
|
||||||
|
return false;
|
||||||
|
case TranslationStep.LookupPageTable:
|
||||||
|
ExecuteLookupPageTable();
|
||||||
|
return false;
|
||||||
|
case TranslationStep.HandlePageFault:
|
||||||
|
ExecuteHandlePageFault();
|
||||||
|
return false;
|
||||||
|
case TranslationStep.ComposePA:
|
||||||
|
ExecuteComposePa();
|
||||||
|
return false;
|
||||||
|
case TranslationStep.Finalize:
|
||||||
|
ExecuteFinalize();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"未知翻译步骤:{State.CurrentStep}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccessExecutionResult RunOneAccess(ulong? forcedVirtualAddress = null)
|
||||||
|
{
|
||||||
|
if (State.CurrentStep != TranslationStep.GenerateVA)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("RunOneAccess 只能在 GenerateVA 起始步骤调用。");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forcedVirtualAddress.HasValue)
|
||||||
|
{
|
||||||
|
SetNextVirtualAddress(forcedVirtualAddress.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!StepOnce())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AccessExecutionResult(
|
||||||
|
State.CurrentRound,
|
||||||
|
State.CurrentVirtualAddress,
|
||||||
|
State.CurrentAddressParts,
|
||||||
|
State.CurrentPfn,
|
||||||
|
State.CurrentPhysicalAddress,
|
||||||
|
State.IsTlbHit,
|
||||||
|
State.IsPageTableHit,
|
||||||
|
State.IsPageFault,
|
||||||
|
_lastEviction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteGenerateVa()
|
||||||
|
{
|
||||||
|
State.IsTlbHit = false;
|
||||||
|
State.IsPageTableHit = false;
|
||||||
|
State.IsPageFault = false;
|
||||||
|
State.CurrentCost = 0;
|
||||||
|
State.CurrentAddressParts = AddressParts.Empty;
|
||||||
|
State.CurrentPhysicalAddress = 0UL;
|
||||||
|
State.CurrentPfn = 0UL;
|
||||||
|
State.HasCurrentPfn = false;
|
||||||
|
_lastEviction = EvictionInfo.None;
|
||||||
|
|
||||||
|
if (_hasPendingForcedVirtualAddress)
|
||||||
|
{
|
||||||
|
State.CurrentVirtualAddress = _pendingForcedVirtualAddress;
|
||||||
|
_hasPendingForcedVirtualAddress = false;
|
||||||
|
_pendingForcedVirtualAddress = 0UL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State.CurrentVirtualAddress = _addressGenerator.NextVirtualAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
State.CurrentStep = TranslationStep.SplitVA;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteSplitVa()
|
||||||
|
{
|
||||||
|
State.CurrentAddressParts = AddressTranslatorUtils.SplitVirtualAddress(
|
||||||
|
State.CurrentVirtualAddress,
|
||||||
|
_config.vaBits,
|
||||||
|
_derivedConfig.OffsetBits);
|
||||||
|
|
||||||
|
State.CurrentStep = TranslationStep.LookupTLB;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteLookupTlb()
|
||||||
|
{
|
||||||
|
if (_tlbCache.TryLookup(State.CurrentAddressParts.Vpn, out var pfn))
|
||||||
|
{
|
||||||
|
State.IsTlbHit = true;
|
||||||
|
State.CurrentPfn = pfn;
|
||||||
|
State.HasCurrentPfn = true;
|
||||||
|
State.CurrentStep = TranslationStep.ComposePA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
State.CurrentStep = TranslationStep.LookupPageTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteLookupPageTable()
|
||||||
|
{
|
||||||
|
if (_pageTable.TryGetPresentPfn(
|
||||||
|
State.CurrentAddressParts.L1Index,
|
||||||
|
State.CurrentAddressParts.L2Index,
|
||||||
|
out var pfn))
|
||||||
|
{
|
||||||
|
State.IsPageTableHit = true;
|
||||||
|
State.CurrentPfn = pfn;
|
||||||
|
State.HasCurrentPfn = true;
|
||||||
|
_tlbCache.InsertOrUpdate(State.CurrentAddressParts.Vpn, pfn);
|
||||||
|
State.CurrentStep = TranslationStep.ComposePA;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
State.CurrentStep = TranslationStep.HandlePageFault;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteHandlePageFault()
|
||||||
|
{
|
||||||
|
var result = MemoryAccessResolver.Resolve(
|
||||||
|
State.CurrentAddressParts.Vpn,
|
||||||
|
State.CurrentAddressParts.L1Index,
|
||||||
|
State.CurrentAddressParts.L2Index,
|
||||||
|
_pageTable,
|
||||||
|
_physicalMemoryManager,
|
||||||
|
_tlbCache);
|
||||||
|
|
||||||
|
State.IsPageTableHit = result.PageTableHit;
|
||||||
|
State.IsPageFault = result.PageFault;
|
||||||
|
State.CurrentPfn = result.Pfn;
|
||||||
|
State.HasCurrentPfn = true;
|
||||||
|
_lastEviction = result.Eviction;
|
||||||
|
State.CurrentStep = TranslationStep.ComposePA;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteComposePa()
|
||||||
|
{
|
||||||
|
if (!State.HasCurrentPfn)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ComposePA 阶段缺少 PFN,无法合成物理地址。");
|
||||||
|
}
|
||||||
|
|
||||||
|
State.CurrentPhysicalAddress =
|
||||||
|
(State.CurrentPfn << _derivedConfig.OffsetBits) | State.CurrentAddressParts.Offset;
|
||||||
|
|
||||||
|
State.CurrentStep = TranslationStep.Finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteFinalize()
|
||||||
|
{
|
||||||
|
State.CurrentRound += 1;
|
||||||
|
State.CurrentStep = TranslationStep.GenerateVA;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureVirtualAddressInRange(ulong virtualAddress)
|
||||||
|
{
|
||||||
|
if (_config.vaBits < 64 && virtualAddress > _addressGenerator.MaxVirtualAddress)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(virtualAddress), "virtualAddress 超出 vaBits 可表示范围。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 573321a5f8ef63248bc3d9baa95c450d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -123,11 +123,15 @@ namespace VMdemo.Tests.EditMode
|
||||||
state.Reset();
|
state.Reset();
|
||||||
|
|
||||||
Assert.That(state.CurrentRound, Is.EqualTo(0));
|
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.CurrentVirtualAddress, Is.EqualTo(0UL));
|
||||||
Assert.That(state.CurrentAddressParts.Vpn, Is.EqualTo(0UL));
|
Assert.That(state.CurrentAddressParts.Vpn, Is.EqualTo(0UL));
|
||||||
Assert.That(state.CurrentAddressParts.Offset, Is.EqualTo(0UL));
|
Assert.That(state.CurrentAddressParts.Offset, Is.EqualTo(0UL));
|
||||||
Assert.That(state.CurrentAddressParts.L1Index, Is.EqualTo(0UL));
|
Assert.That(state.CurrentAddressParts.L1Index, Is.EqualTo(0UL));
|
||||||
Assert.That(state.CurrentAddressParts.L2Index, 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.IsPageFault);
|
||||||
Assert.IsFalse(state.IsTlbHit);
|
Assert.IsFalse(state.IsTlbHit);
|
||||||
Assert.IsFalse(state.IsPageTableHit);
|
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:
|
||||||
|
|
@ -123,21 +123,21 @@
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
- [ ] 新建 `TranslatorEngine.cs`
|
- [x] 新建 `TranslatorEngine.cs`
|
||||||
- [ ] 定义步骤枚举:
|
- [x] 定义步骤枚举:
|
||||||
- [ ] `GenerateVA`
|
- [x] `GenerateVA`
|
||||||
- [ ] `SplitVA`
|
- [x] `SplitVA`
|
||||||
- [ ] `LookupTLB`
|
- [x] `LookupTLB`
|
||||||
- [ ] `LookupPageTable`
|
- [x] `LookupPageTable`
|
||||||
- [ ] `HandlePageFault`
|
- [x] `HandlePageFault`
|
||||||
- [ ] `ComposePA`
|
- [x] `ComposePA`
|
||||||
- [ ] `Finalize`
|
- [x] `Finalize`
|
||||||
- [ ] 提供 `StepOnce()` 与 `RunOneAccess()` 两种执行接口
|
- [x] 提供 `StepOnce()` 与 `RunOneAccess()` 两种执行接口
|
||||||
|
|
||||||
### 完成标准
|
### 完成标准
|
||||||
|
|
||||||
- [ ] 单步执行能暂停在每个阶段并暴露当前状态
|
- [x] 单步执行能暂停在每个阶段并暴露当前状态
|
||||||
- [ ] 一次完整访问流程结果与预期一致
|
- [x] 一次完整访问流程结果与预期一致
|
||||||
|
|
||||||
## 7. Step 6 - 统计模块
|
## 7. Step 6 - 统计模块
|
||||||
|
|
||||||
|
|
|
||||||
Reference in New Issue