Add 1-window WindowMMU to Kelvin to support TCM offset
This CL adds an untouchable WindowMMU to Kelvin with a single window
translating addresses 0x00000000 : MemorySize to MemoryRangeStart :
MemoryRangeStart + MemorySize. This is needed in simulation because
Kelvin's software expects memory to begin at address 0x00000000 but
Renode doesn't support multiple address spaces without the use of
MMUs.
Change-Id: I08b7baf071f916f29f62470224e43ab7e7c221d8
diff --git a/kelvin.resc b/kelvin.resc
index b3ee710..4a8aead 100644
--- a/kelvin.resc
+++ b/kelvin.resc
@@ -30,9 +30,18 @@
macro reset
"""
- sysbus LoadBIN $bin 0x5A000000
- # Start the vector core at address 0 of its TCM.
- sysbus.cpu2 IsHalted true
- sysbus.cpu2 PC 0x5A000000
+ sysbus LoadBinary $bin 0x5A000000
+ # Start the vector core at address 0 of its TCM and halt / reset it.
+ sysbus.vec_controlblock WriteDoubleWord 12 3
"""
runMacro $reset
+
+# Note: GDB doesn't seem to like the way we're setting the program counter in
+# the vec_controlblock. Breakpoints set to the exact reset address don't seem to
+# get reliably triggered. We should probably root cause this later when we have
+# time, but as a workaround setting the breakpoint to reset_addr+4 instead seems
+# to work fine.
+$gdb_port?=3333
+machine StartGdbServer $gdb_port false sysbus.cpu2
+
+start
diff --git a/platforms/kelvin.repl b/platforms/kelvin.repl
index 0aa30ae..00469f3 100644
--- a/platforms/kelvin.repl
+++ b/platforms/kelvin.repl
@@ -16,8 +16,18 @@
// Kelvin single test core
// ***************************************************
-using "sim/config/platforms/ml_core.repl"
+// TODO(jonathantate): Move the cpu and dmem blocks back into ml_core.repl and
+// uncomment this 'using ".../ml_core.repl"' line.
+// using "sim/config/platforms/ml_core.repl"
+
+cpu2: CPU.KelvinRiscV32 @ sysbus
+ hartId: 2
+
+//RAM_VEC_MEM [‘h5A00_0000 - ‘h5A3F_FFFF) 4MB RAM for Vector core
+ram_vec_dmem: Memory.MappedMemory @ sysbus 0x5A000000
+ size: 0x00400000
vec_controlblock : CPU.KelvinRiscV32_ControlBlock @ sysbus 0x5C000000
core: cpu2
- memoryRangeStart: 0x5A000000
+ tcmSize: 0x00400000
+ tcmRangeStart: 0x5A000000
diff --git a/shodan_infrastructure/KelvinRiscV32.cs b/shodan_infrastructure/KelvinRiscV32.cs
index 1b7772d..8b912c1 100644
--- a/shodan_infrastructure/KelvinRiscV32.cs
+++ b/shodan_infrastructure/KelvinRiscV32.cs
@@ -30,8 +30,17 @@
string cpuType = "rv32im")
: base(null, cpuType, machine, hartId, privilegeArchitecture, endianness)
{
- InstallCustomInstruction(pattern: "00001000000000000000000001110011", handler: HandleKelvinMPause);
InstallCustomInstruction(pattern: "00000000000100000000000001110011", handler: HandleKelvinEBreak); // Kelvin doesn't implement rv32i ebreak correctly
+ InstallCustomInstruction(pattern: "00000010000000000000000001110011", handler: HandleKelvinEExit);
+ InstallCustomInstruction(pattern: "00000100000000000000000001110011", handler: HandleKelvinEYield);
+ InstallCustomInstruction(pattern: "00000110000000000000000001110011", handler: HandleKelvinECtxSw);
+ InstallCustomInstruction(pattern: "00001000000000000000000001110011", handler: HandleKelvinMPause);
+ InstallCustomInstruction(pattern: "011110000000-----000000001110111", handler: HandleKelvinFLog);
+ InstallCustomInstruction(pattern: "011110000000-----001000001110111", handler: HandleKelvinSLog);
+ InstallCustomInstruction(pattern: "011110000000-----010000001110111", handler: HandleKelvinCLog);
+ InstallCustomInstruction(pattern: "011110000000-----011000001110111", handler: HandleKelvinKLog);
+ InstallCustomInstruction(pattern: "00100110000000000000000001110111", handler: HandleKelvinFlushAll);
+ InstallCustomInstruction(pattern: "001001100000-----000000001110111", handler: HandleKelvinFlushAt);
Reset();
}
@@ -58,14 +67,65 @@
private KelvinRiscV32_ControlBlock ControlBlock;
private bool ControlBlockRegistered = false;
+ private void HandleKelvinEBreak(UInt64 opcode)
+ {
+ ControlBlock.ExecEBreak();
+ }
+
+ private void HandleKelvinEExit(UInt64 opcode)
+ {
+ ControlBlock.ExecEBreak();
+ }
+
+ private void HandleKelvinEYield(UInt64 opcode)
+ {
+ // To be implemented
+ ControlBlock.ExecEBreak();
+ }
+
+ private void HandleKelvinECtxSw(UInt64 opcode)
+ {
+ // To be implemented
+ ControlBlock.ExecEBreak();
+ }
+
private void HandleKelvinMPause(UInt64 opcode)
{
ControlBlock.ExecMPause();
}
- private void HandleKelvinEBreak(UInt64 opcode)
+ private void HandleKelvinFLog(UInt64 opcode)
{
- ControlBlock.ExecEBreak();
+ this.Log(LogLevel.Noisy, "FLog dropped (unimplemented)");
+ // To be implemented
+ }
+
+ private void HandleKelvinSLog(UInt64 opcode)
+ {
+ this.Log(LogLevel.Noisy, "SLog dropped (unimplemented)");
+ // To be implemented
+ }
+
+ private void HandleKelvinCLog(UInt64 opcode)
+ {
+ this.Log(LogLevel.Noisy, "CLog dropped (unimplemented)");
+ // To be implemented
+ }
+
+ private void HandleKelvinKLog(UInt64 opcode)
+ {
+ this.Log(LogLevel.Noisy, "KLog dropped (unimplemented)");
+ // To be implemented
+ }
+
+ private void HandleKelvinFlushAll(UInt64 opcode)
+ {
+ // No-op in simulator
+ }
+
+ private void HandleKelvinFlushAt(UInt64 opcode)
+ {
+ // No-op in simulator
}
}
@@ -77,12 +137,16 @@
public KelvinRiscV32_ControlBlock(Machine machine,
KelvinRiscV32 core,
- ulong memoryRangeStart)
+ ulong tcmSize,
+ ulong tcmRangeStart)
{
Machine = machine;
Core = core;
- this.memoryRangestart = memoryRangeStart;
+ this.tcmRangeStart = tcmRangeStart;
+ this.tcmSize = tcmSize;
+
+ Mmu = InitMmu(tcmRangeStart, tcmSize);
HostReqIRQ = new GPIO();
FinishIRQ = new GPIO();
@@ -94,6 +158,8 @@
RegistersCollection = new DoubleWordRegisterCollection(this);
DefineRegisters();
+ Core.AddHookOnMmuFault(HandleFault);
+
Reset();
}
@@ -103,6 +169,18 @@
RegistersCollection.Reset();
}
+ private ExternalMmuBase InitMmu(ulong rangeStart, ulong tcmSize)
+ {
+ var mmu = new ExternalMmuBase(this.Core, 1);
+
+ mmu.SetWindowStart(0, 0);
+ mmu.SetWindowEnd(0, tcmSize);
+ mmu.SetWindowAddend(0, tcmRangeStart);
+ mmu.SetWindowPrivileges(0, 7); // Read / write / execute
+
+ return mmu;
+ }
+
private void DefineRegisters()
{
Registers.IntrState.Define32(this)
@@ -161,9 +239,17 @@
{
this.Log(LogLevel.Noisy, "Resetting core.");
Core.Reset();
- ulong startAddress = (val >> (ulong)Mode.NumBits) + memoryRangeStart;
- this.Log(LogLevel.Noisy, "Setting PC to 0x{0:X}.", startAddress);
- Core.PC = startAddress;
+ }
+
+ if ((mode & Mode.SwReset) != 0)
+ {
+ // Always set the PC on assignment to help with GDB.
+ ulong startAddress = (val >> (int)Mode.NumBits);
+ if ((Core.PC != startAddress))
+ {
+ this.Log(LogLevel.Noisy, "Setting PC to 0x{0:X}.", startAddress);
+ Core.PC = startAddress;
+ }
}
// Unpause the core when both freeze and SwReset are deasserted.
@@ -182,20 +268,20 @@
// To-do: Not sure how to implement disablable memory banks.
Registers.MemoryBankControl.Define32(this)
- .WithValueField(0, 16, out MemoryEnable, name: "MEM_ENABLE")
+ .WithValueField(0, 4, out MemoryEnable, name: "MEM_ENABLE")
.WithIgnoredBits(16, 32 - 16)
;
// To-do: Not sure how to implement memory access range checks.
Registers.ErrorStatus.Define32(this)
.WithFlag(0, name: "MEM_OUT_OF_RANGE")
- .WithValueField(1, 9, out MemoryDisableAccess, name: "MEM_DISABLE_ACCESS")
- .WithIgnoredBits(9, 32 - 9)
+ .WithValueField(2, 4, out MemoryDisableAccess, name: "MEM_DISABLE_ACCESS")
+ .WithIgnoredBits(16, 32 - 16)
;
Registers.InitStart.Define32(this)
.WithValueField(0, 22, out InitStartAddress, name: "ADDRESS")
- .WithIgnoredBits(22, 32 - 22)
+ .WithIgnoredBits(24, 32 - 24)
;
Registers.InitEnd.Define32(this)
@@ -216,7 +302,6 @@
writeAddress += DataPageSize)
{
Machine.SystemBus.WriteBytes(DataErasePattern,
- memoryRangeStart +
(ulong)writeAddress,
(uint)DataPageSize, true);
}
@@ -282,6 +367,45 @@
public DoubleWordRegisterCollection RegistersCollection { get; private set; }
+
+ private void HandleFault(ulong faultAddress, AccessType accessType, int windowIndex)
+ {
+ LogFaultType(Mmu, faultAddress, accessType);
+ ExecEBreak();
+ }
+
+ private void LogFaultType(ExternalMmuBase mmu, ulong faultAddress, AccessType accessType)
+ {
+ var mmuName = mmu.GetType().Name;
+ var virtualWindowIndex = (uint)(faultAddress / tcmSize);
+
+ if(virtualWindowIndex >= 1)
+ {
+ this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failure, no window at address", mmuName, accessType, faultAddress);
+ }
+ else
+ {
+ var windowEnd = mmu.GetWindowEnd(virtualWindowIndex);
+ var windowPrivilege = mmu.GetWindowPrivileges(virtualWindowIndex);
+
+ if(faultAddress > windowEnd)
+ {
+ this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failure on window {3}, out of range, window ends at 0x{4:X}",
+ mmuName, accessType, faultAddress, virtualWindowIndex, windowEnd);
+ }
+ else if((windowPrivilege & (uint)accessType) == 0)
+ {
+ this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failure on window {3}, no permission, window permissions are 0x{4:X}",
+ mmuName, accessType, faultAddress, virtualWindowIndex, windowPrivilege);
+ }
+ else
+ {
+ this.Log(LogLevel.Error, "{0}: Translation request for {1} at 0x{2:X}: failed for unknown reason. This should not occur",
+ mmuName, accessType, faultAddress);
+ }
+ }
+ }
+
public GPIO HostReqIRQ { get; }
public GPIO FinishIRQ { get; }
public GPIO InstructionFaultIRQ { get; }
@@ -318,7 +442,9 @@
private Mode mode;
private readonly Machine Machine;
private readonly KelvinRiscV32 Core;
- private readonly ulong memoryRangeStart;
+ private readonly ExternalMmuBase Mmu;
+ private readonly ulong tcmSize;
+ private readonly ulong tcmRangeStart;
// Length of register space.
public long Size => 0x1000;