| // |
| // Copyright (c) 2021 Google LLC |
| // |
| // This file is licensed under the MIT License. |
| // Full license text is available in 'licenses/MIT.txt'. |
| // |
| using System; |
| using System.Linq; |
| using System.Text; |
| |
| using Antmicro.Renode.Core; |
| using Antmicro.Renode.Core.Structure.Registers; |
| using Antmicro.Renode.Exceptions; |
| using Antmicro.Renode.Logging; |
| using Antmicro.Renode.Peripherals.Miscellaneous; |
| using Antmicro.Renode.Peripherals.Bus; |
| using Antmicro.Renode.Utilities; |
| using Antmicro.Renode.Debugging; |
| |
| using Endianess = ELFSharp.ELF.Endianess; |
| |
| namespace Antmicro.Renode.Peripherals.CPU |
| { |
| public class KelvinRiscV32 : RiscV32 |
| { |
| public KelvinRiscV32(Core.Machine machine, |
| uint hartId = 0, |
| PrivilegeArchitecture privilegeArchitecture = PrivilegeArchitecture.Priv1_11, |
| Endianess endianness = Endianess.LittleEndian, |
| 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 |
| |
| Reset(); |
| } |
| |
| public override void Reset() |
| { |
| base.Reset(); |
| |
| // This core comes out of reset paused. |
| this.IsHalted = true; |
| |
| if(ControlBlockRegistered) |
| { |
| ControlBlock.Reset(); |
| } |
| } |
| |
| public void RegisterControlBlock(KelvinRiscV32_ControlBlock controlBlock) |
| { |
| ControlBlock = controlBlock; |
| ControlBlockRegistered = true; |
| } |
| |
| private KelvinRiscV32_ControlBlock ControlBlock; |
| private bool ControlBlockRegistered = false; |
| |
| private void HandleKelvinMPause(UInt64 opcode) |
| { |
| ControlBlock.ExecMPause(); |
| } |
| |
| private void HandleKelvinEBreak(UInt64 opcode) |
| { |
| ControlBlock.ExecEBreak(); |
| } |
| } |
| |
| public class KelvinRiscV32_ControlBlock : |
| IDoubleWordPeripheral, |
| IProvidesRegisterCollection<DoubleWordRegisterCollection>, |
| IKnownSize |
| { |
| |
| public KelvinRiscV32_ControlBlock(Machine machine, |
| KelvinRiscV32 core, |
| ulong memoryRangeStart) |
| { |
| Machine = machine; |
| Core = core; |
| |
| this.memoryRangestart = memoryRangeStart; |
| |
| HostReqIRQ = new GPIO(); |
| FinishIRQ = new GPIO(); |
| InstructionFaultIRQ = new GPIO(); |
| DataFaultIRQ = new GPIO(); |
| |
| Core.RegisterControlBlock(this); |
| |
| RegistersCollection = new DoubleWordRegisterCollection(this); |
| DefineRegisters(); |
| |
| Reset(); |
| } |
| |
| public void Reset() |
| { |
| mode = Mode.Freeze | Mode.SwReset; |
| RegistersCollection.Reset(); |
| } |
| |
| private void DefineRegisters() |
| { |
| Registers.IntrState.Define32(this) |
| .WithValueField(0, 4, |
| writeCallback: (_, value) => |
| { |
| this.Log(LogLevel.Noisy, "Got {0} to clear IRQ pending bits", value); |
| irqsPending = irqsPending & ~(InterruptBits)value; |
| IrqUpdate(); |
| }, |
| valueProviderCallback: (_) => |
| { |
| return (uint)irqsPending; |
| }) |
| ; |
| |
| Registers.IntrEnable.Define32(this) |
| .WithValueField(0, 4, |
| writeCallback: (_, value) => |
| { |
| this.Log(LogLevel.Noisy, "Got {0} to write IRQ enable bits", value); |
| irqsEnabled = (InterruptBits)value & InterruptBits.Mask; |
| IrqUpdate(); |
| }, |
| valueProviderCallback: (_) => |
| { |
| return (uint)irqsEnabled; |
| }) |
| ; |
| |
| Registers.IntrTest.Define32(this) |
| .WithValueField(0, 4, |
| writeCallback: (_, value) => |
| { |
| this.Log(LogLevel.Noisy, "Got {0} to set IRQ pending bits", value); |
| irqsPending = irqsPending | ((InterruptBits)value & InterruptBits.Mask); |
| IrqUpdate(); |
| }) |
| ; |
| |
| Registers.Control.Define32(this, resetValue: 0x00000002) |
| .WithValueField(0, 24, name: "FREEZE_VC_RESET_PC_START", |
| writeCallback: (_, val) => |
| { |
| Mode newMode = (Mode)val & Mode.Mask; |
| |
| // Pause the core when either freeze or swreset is asserted. |
| if ((mode == Mode.Run) && (newMode != Mode.Run)) |
| { |
| this.Log(LogLevel.Noisy, "Pausing core."); |
| Core.IsHalted = true; |
| } |
| |
| // Trigger the core's reset when SwReset is deasserted. |
| if (((mode & Mode.SwReset) != 0) && ((newMode & Mode.SwReset) == 0)) |
| { |
| 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; |
| } |
| |
| // Unpause the core when both freeze and SwReset are deasserted. |
| if ((mode != Mode.Run) && (newMode == Mode.Run)) |
| { |
| this.Log(LogLevel.Noisy, "Resuming core."); |
| Core.IsHalted = false; |
| |
| Core.Resume(); |
| } |
| |
| this.mode = newMode; |
| }) |
| .WithIgnoredBits(24, 32 - 24) |
| ; |
| |
| // To-do: Not sure how to implement disablable memory banks. |
| Registers.MemoryBankControl.Define32(this) |
| .WithValueField(0, 16, 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) |
| ; |
| |
| Registers.InitStart.Define32(this) |
| .WithValueField(0, 22, out InitStartAddress, name: "ADDRESS") |
| .WithIgnoredBits(22, 32 - 22) |
| ; |
| |
| Registers.InitEnd.Define32(this) |
| .WithValueField(0, 22, out InitEndAddress, name: "ADDRESS") |
| .WithFlag(22, name: "VALID", mode: FieldMode.Read | FieldMode.Write, |
| writeCallback: (_, val) => |
| { |
| // If valid, do the memory clear. |
| if (val) |
| { |
| var dataPageMask = ~((ulong)(DataPageSize - 1)); |
| InitStatusPending.Value = true; |
| InitStatusDone.Value = false; |
| Machine.LocalTimeSource.ExecuteInNearestSyncedState( __ => |
| { |
| for(ulong writeAddress = InitStartAddress.Value & dataPageMask; |
| writeAddress < ((InitEndAddress.Value + DataPageSize - 1) & dataPageMask); |
| writeAddress += DataPageSize) |
| { |
| Machine.SystemBus.WriteBytes(DataErasePattern, |
| memoryRangeStart + |
| (ulong)writeAddress, |
| (uint)DataPageSize, true); |
| } |
| InitStatusPending.Value = false; |
| InitStatusDone.Value = true; |
| }); |
| } |
| }) |
| .WithIgnoredBits(23, 32 - 23) |
| ; |
| |
| Registers.InitStatus.Define32(this) |
| .WithFlag(0, out InitStatusPending, name: "INIT_PENDING") |
| .WithFlag(1, out InitStatusDone, name: "INIT_DONE") |
| .WithIgnoredBits(2, 32 - 2) |
| ; |
| |
| } |
| |
| public virtual uint ReadDoubleWord(long offset) |
| { |
| return RegistersCollection.Read(offset); |
| } |
| |
| public virtual void WriteDoubleWord(long offset, uint value) |
| { |
| RegistersCollection.Write(offset, value); |
| } |
| |
| public void ExecMPause() |
| { |
| // Pause, reset the core (actual reset occurs when SwReset is cleared) and trigger a host interrupt indicating completion |
| if (mode == Mode.Run) |
| { |
| this.Log(LogLevel.Noisy, "Pausing and resetting core for host completion notification."); |
| } |
| else |
| { |
| this.Log(LogLevel.Error, "Pausing and resetting core for host completion notification, but core was not expected to be running. Did you clear IsHalted manually?"); |
| } |
| Core.IsHalted = true; |
| mode = Mode.Freeze | Mode.SwReset; |
| irqsPending |= InterruptBits.Finish; |
| IrqUpdate(); |
| } |
| |
| public void ExecEBreak() |
| { |
| // Pause and trigger a host interrupt indicating completion with fault |
| if (mode == Mode.Run) |
| { |
| this.Log(LogLevel.Noisy, "Core executed ebreak."); |
| } |
| else |
| { |
| this.Log(LogLevel.Error, "Core executed ebreak, but core was not expected to be running. Did you clear IsHalted manually?"); |
| } |
| Core.IsHalted = true; |
| mode = Mode.Freeze; |
| irqsPending |= InterruptBits.Finish | InterruptBits.InstructionFault; |
| IrqUpdate(); |
| } |
| |
| public DoubleWordRegisterCollection RegistersCollection { get; private set; } |
| |
| public GPIO HostReqIRQ { get; } |
| public GPIO FinishIRQ { get; } |
| public GPIO InstructionFaultIRQ { get; } |
| public GPIO DataFaultIRQ { get; } |
| |
| private InterruptBits irqsEnabled; |
| private InterruptBits irqsPending; |
| |
| private void IrqUpdate() |
| { |
| InterruptBits irqsPassed = irqsEnabled & irqsPending; |
| HostReqIRQ.Set((irqsPassed & InterruptBits.HostReq) != 0); |
| FinishIRQ.Set((irqsPassed & InterruptBits.Finish) != 0); |
| InstructionFaultIRQ.Set((irqsPassed & InterruptBits.InstructionFault) != 0); |
| DataFaultIRQ.Set((irqsPassed & InterruptBits.DataFault) != 0); |
| } |
| |
| // To-do: Set the erase pattern to what the hardware actually does. 0x5A is |
| // only for debugging purposes. |
| private const int DataPageSize = 64; |
| private readonly byte[] DataErasePattern = (byte[])Enumerable.Repeat((byte)0x5A, DataPageSize).ToArray(); |
| |
| // Disable unused variable warnings. These warnings will go away on their |
| // their own when each register's behavior is implemented. |
| #pragma warning disable 414 |
| private IValueRegisterField MemoryEnable; |
| private IValueRegisterField MemoryDisableAccess; |
| private IValueRegisterField InitStartAddress; |
| private IValueRegisterField InitEndAddress; |
| private IFlagRegisterField InitStatusPending; |
| private IFlagRegisterField InitStatusDone; |
| #pragma warning restore 414 |
| |
| private Mode mode; |
| private readonly Machine Machine; |
| private readonly KelvinRiscV32 Core; |
| private readonly ulong memoryRangeStart; |
| |
| // Length of register space. |
| public long Size => 0x1000; |
| private enum Registers |
| { |
| IntrState = 0x00, |
| IntrEnable = 0x04, |
| IntrTest = 0x08, |
| Control = 0x0C, |
| MemoryBankControl = 0x10, |
| ErrorStatus = 0x14, |
| InitStart = 0x18, |
| InitEnd = 0x1C, |
| InitStatus = 0x20, |
| }; |
| [Flags] |
| private enum Mode |
| { |
| Run = 0x00, |
| Freeze = 0x01, |
| SwReset = 0x02, |
| Mask = 0x03, |
| NumBits = 2, |
| }; |
| [Flags] |
| private enum InterruptBits |
| { |
| HostReq = 1, |
| Finish = 2, |
| InstructionFault = 4, |
| DataFault = 8, |
| Mask = 15, |
| }; |
| } |
| |
| } |