349 lines
13 KiB
C#
349 lines
13 KiB
C#
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();
|
||
Stats = new StatsCollector();
|
||
|
||
AddressTranslatorUtils.GetPageTableBitLayout(derived.VpnBits, out var l1Bits, out var l2Bits);
|
||
L1Bits = l1Bits;
|
||
L2Bits = l2Bits;
|
||
|
||
_addressGenerator = CreateAddressGenerator();
|
||
_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 MachineBits => _config.machineBits;
|
||
public int VaBits => _config.vaBits;
|
||
public int PageSizeKB => _config.pageSizeKB;
|
||
public int PhysicalMemoryMB => _config.physicalMemoryMB;
|
||
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 StatsCollector Stats { get; }
|
||
public EvictionInfo LastEviction => _lastEviction;
|
||
|
||
public void Reset()
|
||
{
|
||
State.Reset();
|
||
_lastEviction = EvictionInfo.None;
|
||
_hasPendingForcedVirtualAddress = false;
|
||
_pendingForcedVirtualAddress = 0UL;
|
||
Stats.Reset();
|
||
|
||
_addressGenerator = CreateAddressGenerator();
|
||
_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;
|
||
State.LastDecisionReason = "已生成本轮虚拟地址,准备进行地址拆分。";
|
||
_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.LastDecisionReason = "已拆分 VA 为 VPN/Offset,并从 VPN 拆分出 L1/L2 索引。";
|
||
|
||
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.LastDecisionReason =
|
||
$"TLB 命中:VPN={State.CurrentAddressParts.Vpn} 命中 PFN={pfn},跳过页表,直接合成物理地址。";
|
||
State.CurrentStep = TranslationStep.ComposePA;
|
||
return;
|
||
}
|
||
|
||
State.LastDecisionReason =
|
||
$"TLB 未命中:VPN={State.CurrentAddressParts.Vpn} 未找到映射,转入页表查询。";
|
||
State.CurrentStep = TranslationStep.LookupPageTable;
|
||
}
|
||
|
||
private void ExecuteLookupPageTable()
|
||
{
|
||
var probe = _pageTable.Probe(State.CurrentAddressParts.L1Index, State.CurrentAddressParts.L2Index);
|
||
if (probe.Status == PageTableProbeStatus.PresentEntry)
|
||
{
|
||
State.IsPageTableHit = true;
|
||
State.CurrentPfn = probe.Entry.Pfn;
|
||
State.HasCurrentPfn = true;
|
||
_tlbCache.InsertOrUpdate(State.CurrentAddressParts.Vpn, probe.Entry.Pfn);
|
||
State.LastDecisionReason =
|
||
$"页表命中:L1={State.CurrentAddressParts.L1Index}, L2={State.CurrentAddressParts.L2Index}, PFN={probe.Entry.Pfn},并写回 TLB。";
|
||
State.CurrentStep = TranslationStep.ComposePA;
|
||
return;
|
||
}
|
||
|
||
State.LastDecisionReason = probe.Status switch
|
||
{
|
||
PageTableProbeStatus.MissingL1Table =>
|
||
$"页表未命中:L1={State.CurrentAddressParts.L1Index} 对应二级页表不存在,触发缺页处理。",
|
||
PageTableProbeStatus.MissingL2Entry =>
|
||
$"页表未命中:L2={State.CurrentAddressParts.L2Index} 页表项不存在,触发缺页处理。",
|
||
PageTableProbeStatus.NotPresentEntry =>
|
||
$"页表项无效:L2={State.CurrentAddressParts.L2Index} present=false,触发缺页处理。",
|
||
_ => "页表未命中,触发缺页处理。"
|
||
};
|
||
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.LastDecisionReason = result.Eviction.HasEvicted
|
||
? $"缺页处理完成:装入 PFN={result.Pfn},并按 FIFO 淘汰 VPN={result.Eviction.EvictedVpn}。"
|
||
: $"缺页处理完成:装入 PFN={result.Pfn},当前仍有空闲物理帧。";
|
||
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.LastDecisionReason =
|
||
$"物理地址合成:PA = (PFN << offsetBits) | offset = ({State.CurrentPfn} << {_derivedConfig.OffsetBits}) | {State.CurrentAddressParts.Offset}。";
|
||
|
||
State.CurrentStep = TranslationStep.Finalize;
|
||
}
|
||
|
||
private void ExecuteFinalize()
|
||
{
|
||
State.CurrentCost = Stats.CalculateCost(
|
||
State.IsTlbHit,
|
||
State.IsPageTableHit,
|
||
State.IsPageFault,
|
||
_config.pageFaultPenalty);
|
||
Stats.RecordAccess(
|
||
State.IsTlbHit,
|
||
State.IsPageTableHit,
|
||
State.IsPageFault,
|
||
State.CurrentCost);
|
||
State.LastDecisionReason = State.IsPageFault
|
||
? $"本轮完成:TLB 未命中 + 页表未命中,发生缺页,开销={State.CurrentCost}。"
|
||
: State.IsTlbHit
|
||
? $"本轮完成:TLB 命中,开销={State.CurrentCost}。"
|
||
: $"本轮完成:TLB 未命中 + 页表命中,开销={State.CurrentCost}。";
|
||
|
||
State.CurrentRound += 1;
|
||
State.CurrentStep = TranslationStep.GenerateVA;
|
||
}
|
||
|
||
private void EnsureVirtualAddressInRange(ulong virtualAddress)
|
||
{
|
||
if (virtualAddress > _addressGenerator.MaxVirtualAddress)
|
||
{
|
||
throw new ArgumentOutOfRangeException(nameof(virtualAddress), "virtualAddress 超出 vaBits 可表示范围。");
|
||
}
|
||
}
|
||
|
||
private AddressGenerator CreateAddressGenerator()
|
||
{
|
||
return new AddressGenerator(
|
||
_config.vaBits,
|
||
_seed,
|
||
_config.addressGenerationMode,
|
||
_config.arrayLengthBytes,
|
||
_config.arrayElementBytes,
|
||
_config.arrayBaseAddress);
|
||
}
|
||
}
|
||
}
|