Step 2 - 地址生成与拆分
This commit is contained in:
parent
7eab504f4a
commit
4ab93d7806
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
|
||||
namespace VMdemo.Core
|
||||
{
|
||||
public static class AddressTranslatorUtils
|
||||
{
|
||||
public static AddressParts SplitVirtualAddress(ulong virtualAddress, int vaBits, int offsetBits)
|
||||
{
|
||||
if (vaBits <= 0 || vaBits > 64)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(vaBits), "vaBits 必须在 1 到 64 之间。");
|
||||
}
|
||||
|
||||
if (offsetBits < 0 || offsetBits >= vaBits)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offsetBits), "offsetBits 必须满足 0 <= offsetBits < vaBits。");
|
||||
}
|
||||
|
||||
var vaMask = GetBitMask(vaBits);
|
||||
if (vaBits < 64 && (virtualAddress & ~vaMask) != 0UL)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(virtualAddress), "virtualAddress 超出 vaBits 可表示范围。");
|
||||
}
|
||||
|
||||
var vpnBits = vaBits - offsetBits;
|
||||
GetPageTableBitLayout(vpnBits, out _, out var l2Bits);
|
||||
|
||||
var offset = virtualAddress & GetBitMask(offsetBits);
|
||||
var vpn = virtualAddress >> offsetBits;
|
||||
var l2Index = vpn & GetBitMask(l2Bits);
|
||||
var l1Index = vpn >> l2Bits;
|
||||
|
||||
return new AddressParts(vpn, offset, l1Index, l2Index);
|
||||
}
|
||||
|
||||
public static void GetPageTableBitLayout(int vpnBits, out int l1Bits, out int l2Bits)
|
||||
{
|
||||
if (vpnBits <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(vpnBits), "vpnBits 必须是正整数。");
|
||||
}
|
||||
|
||||
l1Bits = (vpnBits + 1) / 2;
|
||||
l2Bits = vpnBits / 2;
|
||||
}
|
||||
|
||||
public static string FormatSplitResult(
|
||||
ulong virtualAddress,
|
||||
int vaBits,
|
||||
int offsetBits,
|
||||
AddressParts parts)
|
||||
{
|
||||
return $"VA={virtualAddress}, VPN={parts.Vpn}, Offset={parts.Offset}, L1={parts.L1Index}, L2={parts.L2Index}, vaBits={vaBits}, offsetBits={offsetBits}";
|
||||
}
|
||||
|
||||
private static ulong GetBitMask(int bits)
|
||||
{
|
||||
if (bits <= 0)
|
||||
{
|
||||
return 0UL;
|
||||
}
|
||||
|
||||
if (bits >= 64)
|
||||
{
|
||||
return ulong.MaxValue;
|
||||
}
|
||||
|
||||
return (1UL << bits) - 1UL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b0967dfe5b0eb64993e8c207604d159
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
|
||||
namespace VMdemo.Simulation
|
||||
{
|
||||
public class AddressGenerator
|
||||
{
|
||||
public const int DefaultSeed = 12345;
|
||||
|
||||
private readonly int _vaBits;
|
||||
private readonly ulong _vaMask;
|
||||
private readonly Random _random;
|
||||
private readonly byte[] _buffer = new byte[8];
|
||||
|
||||
public AddressGenerator(int vaBits, int? seed = null)
|
||||
{
|
||||
if (vaBits != 32 && vaBits != 48 && vaBits != 64)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(vaBits), "vaBits 仅支持 32、48、64。");
|
||||
}
|
||||
|
||||
_vaBits = vaBits;
|
||||
_vaMask = vaBits == 64 ? ulong.MaxValue : (1UL << vaBits) - 1UL;
|
||||
_random = new Random(seed ?? DefaultSeed);
|
||||
}
|
||||
|
||||
public ulong MaxVirtualAddress => _vaMask;
|
||||
|
||||
public ulong NextVirtualAddress()
|
||||
{
|
||||
return NextRawUlong() & _vaMask;
|
||||
}
|
||||
|
||||
private ulong NextRawUlong()
|
||||
{
|
||||
_random.NextBytes(_buffer);
|
||||
return BitConverter.ToUInt64(_buffer, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a3e8a9161af494c43a623da1945f4373
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
using NUnit.Framework;
|
||||
using VMdemo.Core;
|
||||
using VMdemo.Simulation;
|
||||
|
||||
namespace VMdemo.Tests.EditMode
|
||||
{
|
||||
public class Step2AddressSplitTests
|
||||
{
|
||||
[TestCase(32)]
|
||||
[TestCase(48)]
|
||||
[TestCase(64)]
|
||||
public void AddressGenerator_NextVirtualAddress_StaysWithinRange(int vaBits)
|
||||
{
|
||||
var generator = new AddressGenerator(vaBits, seed: 7);
|
||||
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
var va = generator.NextVirtualAddress();
|
||||
Assert.That(va, Is.LessThanOrEqualTo(generator.MaxVirtualAddress));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddressGenerator_SameSeed_GeneratesDeterministicSequence()
|
||||
{
|
||||
var a = new AddressGenerator(48, seed: 99);
|
||||
var b = new AddressGenerator(48, seed: 99);
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
Assert.That(a.NextVirtualAddress(), Is.EqualTo(b.NextVirtualAddress()));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddressGenerator_DifferentSeed_GeneratesDifferentSequence()
|
||||
{
|
||||
var a = new AddressGenerator(48, seed: 99);
|
||||
var b = new AddressGenerator(48, seed: 100);
|
||||
var hasDifference = false;
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
if (a.NextVirtualAddress() != b.NextVirtualAddress())
|
||||
{
|
||||
hasDifference = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.IsTrue(hasDifference);
|
||||
}
|
||||
|
||||
[TestCase(0x12345ABCUL, 32, 12, 0x12345UL, 0xABCUL, 0x48UL, 0x345UL)]
|
||||
[TestCase(0x0000ABCDEF123456UL, 48, 12, 0xABCDEF123UL, 0x456UL, 0x2AF37UL, 0x2F123UL)]
|
||||
[TestCase(0xFEDCBA9876543210UL, 64, 16, 0xFEDCBA987654UL, 0x3210UL, 0xFEDCBAUL, 0x987654UL)]
|
||||
public void SplitVirtualAddress_SupportedVaBits_ReturnsExpectedParts(
|
||||
ulong va,
|
||||
int vaBits,
|
||||
int offsetBits,
|
||||
ulong expectedVpn,
|
||||
ulong expectedOffset,
|
||||
ulong expectedL1,
|
||||
ulong expectedL2)
|
||||
{
|
||||
var parts = AddressTranslatorUtils.SplitVirtualAddress(va, vaBits, offsetBits);
|
||||
TestContext.WriteLine(AddressTranslatorUtils.FormatSplitResult(va, vaBits, offsetBits, parts));
|
||||
|
||||
Assert.That(parts.Vpn, Is.EqualTo(expectedVpn));
|
||||
Assert.That(parts.Offset, Is.EqualTo(expectedOffset));
|
||||
Assert.That(parts.L1Index, Is.EqualTo(expectedL1));
|
||||
Assert.That(parts.L2Index, Is.EqualTo(expectedL2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitVirtualAddress_OddVpnBits_UsesCeilFloorBitSplit()
|
||||
{
|
||||
const int vaBits = 32;
|
||||
const int offsetBits = 13;
|
||||
const ulong va = 0xDEADBEEFUL;
|
||||
|
||||
var parts = AddressTranslatorUtils.SplitVirtualAddress(va, vaBits, offsetBits);
|
||||
AddressTranslatorUtils.GetPageTableBitLayout(vaBits - offsetBits, out var l1Bits, out var l2Bits);
|
||||
|
||||
Assert.That(l1Bits, Is.EqualTo(10));
|
||||
Assert.That(l2Bits, Is.EqualTo(9));
|
||||
Assert.That(parts.Vpn, Is.EqualTo(0x6F56DUL));
|
||||
Assert.That(parts.Offset, Is.EqualTo(0x1EEFUL));
|
||||
Assert.That(parts.L1Index, Is.EqualTo(0x37AUL));
|
||||
Assert.That(parts.L2Index, Is.EqualTo(0x16DUL));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetPageTableBitLayout_AlwaysSumsToVpnBits()
|
||||
{
|
||||
for (var vpnBits = 1; vpnBits <= 63; vpnBits++)
|
||||
{
|
||||
AddressTranslatorUtils.GetPageTableBitLayout(vpnBits, out var l1Bits, out var l2Bits);
|
||||
|
||||
Assert.That(l1Bits + l2Bits, Is.EqualTo(vpnBits));
|
||||
Assert.That(l1Bits, Is.GreaterThanOrEqualTo(l2Bits));
|
||||
Assert.That(l1Bits - l2Bits, Is.LessThanOrEqualTo(1));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SplitVirtualAddress_AddressOutOfRange_Throws()
|
||||
{
|
||||
Assert.Throws<System.ArgumentOutOfRangeException>(() =>
|
||||
AddressTranslatorUtils.SplitVirtualAddress(0x1_0000_0000UL, 32, 12));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 53e95a021ddfb5b48bbc5194223d8736
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -63,21 +63,21 @@
|
|||
|
||||
### TODO
|
||||
|
||||
- [ ] 新建 `AddressGenerator.cs`:生成 `[0, 2^vaBits - 1]` 范围虚拟地址
|
||||
- [ ] 新建 `AddressTranslatorUtils.cs`:拆分 `VPN + offset`
|
||||
- [ ] 实现二级页表索引位拆分
|
||||
- [x] 新建 `AddressGenerator.cs`:生成 `[0, 2^vaBits - 1]` 范围虚拟地址
|
||||
- [x] 新建 `AddressTranslatorUtils.cs`:拆分 `VPN + offset`
|
||||
- [x] 实现二级页表索引位拆分
|
||||
|
||||
### 拆分规则
|
||||
|
||||
- [ ] `vpnBits = vaBits - offsetBits`
|
||||
- [ ] `l1Bits = ceil(vpnBits / 2)`
|
||||
- [ ] `l2Bits = floor(vpnBits / 2)`
|
||||
- [ ] 从 `VPN` 拆出 `L1Index` 和 `L2Index`
|
||||
- [x] `vpnBits = vaBits - offsetBits`
|
||||
- [x] `l1Bits = ceil(vpnBits / 2)`
|
||||
- [x] `l2Bits = floor(vpnBits / 2)`
|
||||
- [x] 从 `VPN` 拆出 `L1Index` 和 `L2Index`
|
||||
|
||||
### 完成标准
|
||||
|
||||
- [ ] 控制台可打印每次地址拆分结果
|
||||
- [ ] 32/48/64 三种位数下拆分结果均正确
|
||||
- [x] 控制台可打印每次地址拆分结果
|
||||
- [x] 32/48/64 三种位数下拆分结果均正确
|
||||
|
||||
## 4. Step 3 - TLB(全相联 + LRU)
|
||||
|
||||
|
|
@ -206,5 +206,5 @@
|
|||
## 11. 首日执行建议(直接开工)
|
||||
|
||||
- [x] 先完成 Step 1(参数模型 + 校验)
|
||||
- [ ] 紧接 Step 2(地址拆分)
|
||||
- [x] 紧接 Step 2(地址拆分)
|
||||
- [ ] 当天收尾前完成 Step 3(TLB LRU 最小可运行)
|
||||
|
|
|
|||
Reference in New Issue