| // |
| // Copyright (c) 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| using System; |
| using System.Linq; |
| using System.Runtime.InteropServices; |
| using System.Threading; |
| using System.Collections.Generic; |
| using System.Collections.Concurrent; |
| using Antmicro.Renode.Core; |
| using Antmicro.Renode.Debugging; |
| using Antmicro.Renode.Exceptions; |
| using Antmicro.Renode.Hooks; |
| using Antmicro.Renode.Logging; |
| using Antmicro.Renode.Peripherals; |
| using Antmicro.Renode.Peripherals.Bus; |
| using Antmicro.Renode.Peripherals.CPU; |
| using Antmicro.Renode.Peripherals.Timers; |
| using Antmicro.Renode.Peripherals.CPU.Disassembler; |
| using Antmicro.Renode.Peripherals.CPU.Registers; |
| using Antmicro.Renode.Utilities; |
| using Antmicro.Renode.Time; |
| using Antmicro.Renode.Utilities.Binding; |
| using ELFSharp.ELF; |
| using ELFSharp.UImage; |
| |
| namespace Antmicro.Renode.Peripherals.MpactCPU |
| { |
| |
| // This interface is implemented by MpactCheriotCPU and used by |
| // MpactCheriotPeripheral to access memory in mpact_cheriot simulator |
| public interface IMpactPeripheral : IBytePeripheral, IWordPeripheral, |
| IDoubleWordPeripheral, |
| IQuadWordPeripheral, |
| IMultibyteWritePeripheral {} |
| |
| |
| // Struct containing info for loading binary image files. |
| public struct BinFileLoadInfo { |
| public string file_name; |
| public UInt64 load_address; |
| public UInt64 entry_point; |
| } |
| |
| // The MpactCheriotCPU class. This class derives from BaseCPU, which implements |
| // a CPU in ReNode. It is the interface between ReNode and the mpact_cheriot |
| // simulator library. |
| public class MpactCheriotCPU : BaseCPU, ICpuSupportingGdb, ICPUWithRegisters, |
| ICPUWithHooks, IGPIOReceiver, ITimeSink, |
| IDisposable, IMpactPeripheral { |
| |
| // Structure for ReNode register information that is obtained from the |
| // simulator. |
| [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi, Pack = 1)] |
| private struct RegInfo { |
| [FieldOffset(0)] |
| public Int32 index; |
| [FieldOffset(4)] |
| public Int32 width; |
| [FieldOffset(8)] |
| public bool isGeneral; |
| [FieldOffset(9)] |
| public bool isReadOnly; |
| } |
| |
| public MpactCheriotCPU(uint id, UInt64 memoryBase, UInt64 memorySize, |
| UInt64 revocationMemoryBase, string cpuType, |
| IMachine machine, Endianess endianness, |
| CpuBitness bitness = CpuBitness.Bits32) |
| : base(id, cpuType, machine, endianness, bitness) { |
| this.memoryBase = memoryBase; |
| this.memorySize = memorySize; |
| revocationMemBase = revocationMemoryBase; |
| // Allocate space for marshaling data to/from simulator. |
| error_ptr = Marshal.AllocHGlobal(4); |
| value_ptr = Marshal.AllocHGlobal(8); |
| reg_info_ptr = Marshal.AllocHGlobal(Marshal.SizeOf<RegInfo>()); |
| string_ptr = Marshal.AllocHGlobal(maxStringLen); |
| } |
| |
| ~MpactCheriotCPU() { |
| Marshal.FreeHGlobal(error_ptr); |
| Marshal.FreeHGlobal(value_ptr); |
| Marshal.FreeHGlobal(reg_info_ptr); |
| Marshal.FreeHGlobal(string_ptr); |
| } |
| |
| |
| public override string Architecture { get { return "MpactCheriot";} } |
| |
| public override void Start() { |
| base.Start(); |
| |
| // Set up configuration array. |
| var config_names = new List<string>(); |
| var config_values = new List<string>(); |
| config_names.Add("memoryBase"); |
| config_names.Add("memorySize"); |
| config_names.Add("revocationMemoryBase"); |
| config_values.Add("0x" + memoryBase.ToString("X")); |
| config_values.Add("0x" + memorySize.ToString("X")); |
| config_values.Add("0x" + revocationMemBase.ToString("X")); |
| if (cli_port != 0) { |
| this.Log(LogLevel.Info, "Adding cli port"); |
| config_names.Add("cliPort"); |
| config_values.Add("0x" + cli_port.ToString("X")); |
| config_names.Add("waitForCLI"); |
| config_values.Add("0x" + (wait_for_cli ? 1 : 0).ToString("X")); |
| } |
| Int32 cfg_res = set_config(mpact_id, config_names.ToArray(), |
| config_values.ToArray(), config_names.Count); |
| if (cfg_res < 0) { |
| LogAndThrowRE("Failed to set configuration information"); |
| } |
| if (mpact_id < 0) { |
| LogAndThrowRE("Failed to create simulator instance"); |
| return; |
| } |
| // Load executable file or bin file if specified. Otherwise it is |
| // handled by the system. |
| if (!executable_file.Equals("")) { |
| var entry_point = load_executable(mpact_id, executable_file, |
| error_ptr); |
| Int32 result = Marshal.ReadInt32(error_ptr); |
| if (result < 0) { |
| LogAndThrowRE("Failed to load executable"); |
| } |
| PC = entry_point; |
| } else if (!bin_file_info.file_name.Equals("")) { |
| Int32 res = load_image(mpact_id, bin_file_info.file_name, |
| bin_file_info.load_address); |
| if (res < 0) { |
| LogAndThrowRE("Failed to load binary image"); |
| } |
| PC = bin_file_info.entry_point; |
| } |
| } |
| |
| public override void Reset() { |
| base.Reset(); |
| instructionsExecutedThisRound = 0; |
| totalExecutedInstructions = 0; |
| // Call simulator to reset. |
| reset(mpact_id); |
| } |
| |
| public override void Dispose() { |
| base.Dispose(); |
| // Cleanup: simulator and any unmanaged resources. |
| this.Log(LogLevel.Info, "destruct mpact"); |
| destruct(mpact_id); |
| this.Log(LogLevel.Info, "done"); |
| } |
| |
| public string CpuLibraryPath { |
| get { |
| return cpu_library_path; |
| } |
| set { |
| this.Log(LogLevel.Info, "Lib: " + value); |
| if (String.IsNullOrWhiteSpace(value)) { |
| cpu_library_path = value; |
| return; |
| } |
| if (cpu_library_bound) { |
| LogAndThrowRE("CPU library already bound to: " |
| + cpu_library_path); |
| } |
| cpu_library_path = value; |
| try { |
| binder = new NativeBinder(this, cpu_library_path); |
| } |
| catch (System.Exception e) { |
| LogAndThrowRE("Failed to load CPU library: " + e.Message); |
| } |
| cpu_library_bound = true; |
| |
| // Instantiate the simulator, passing in callbacks to read and write |
| // from/to the system bus. |
| read_sysmem_delegate = |
| new FuncInt32UInt64IntPtrInt32(ReadSysMemory); |
| write_sysmem_delegate = |
| new FuncInt32UInt64IntPtrInt32(WriteSysMemory); |
| mpact_id = construct_with_sysbus(maxStringLen, |
| read_sysmem_delegate, |
| write_sysmem_delegate); |
| } |
| } |
| |
| public string ExecutableFile { |
| get => executable_file; |
| set => executable_file = value; |
| } |
| |
| public BinFileLoadInfo BinFileInfo { |
| get => bin_file_info; |
| set => bin_file_info = value; |
| } |
| |
| public ushort CLIPort { get => cli_port; set => cli_port = value; } |
| |
| public bool WaitForCLI { get => wait_for_cli; set => wait_for_cli = value; } |
| |
| public override ulong ExecutedInstructions => totalExecutedInstructions; |
| |
| public override ExecutionMode ExecutionMode { |
| get { return executionMode; } |
| set { |
| lock(singleStepSynchronizer.Guard) { |
| if (executionMode == value) return; |
| |
| executionMode = value; |
| |
| singleStepSynchronizer.Enabled = IsSingleStepMode; |
| UpdateHaltedState(); |
| } |
| } |
| } |
| |
| [Register] |
| public override RegisterValue PC { |
| get { |
| Int32 error = read_register(mpact_id, PC_ID, value_ptr); |
| if (error < 0) { |
| this.Log(LogLevel.Error, "Failed to read PC"); |
| } |
| Int64 value = Marshal.ReadInt64(value_ptr); |
| return Convert.ToUInt64(value); |
| } |
| |
| set { |
| Int32 error = write_register(mpact_id, PC_ID, value); |
| if (error < 0) { |
| this.Log(LogLevel.Error, "Failed to write PC"); |
| } |
| } |
| } |
| |
| protected override ExecutionResult ExecuteInstructions( |
| ulong numberOfInstructionsToExecute, |
| out ulong numberOfExecutedInstructions) { |
| UInt64 instructionsExecutedThisRound = 0UL; |
| ExecutionResult result = ExecutionResult.Ok; |
| try { |
| // Invoke simulator for the number of instructions. |
| instructionsExecutedThisRound = |
| step(mpact_id, numberOfInstructionsToExecute, error_ptr); |
| // Parse the result. |
| Int32 step_result = Marshal.ReadInt32(error_ptr); |
| switch (step_result) { |
| case -1: |
| result = ExecutionResult.Aborted; |
| break; |
| case 0: |
| result = ExecutionResult.Ok; |
| break; |
| case 1: |
| result = ExecutionResult.Interrupted; |
| break; |
| case 2: |
| result = ExecutionResult.WaitingForInterrupt; |
| break; |
| case 3: |
| result = ExecutionResult.StoppedAtBreakpoint; |
| break; |
| case 4: |
| result = ExecutionResult.StoppedAtWatchpoint; |
| break; |
| case 5: |
| result = ExecutionResult.ExternalMmuFault; |
| break; |
| default: |
| LogAndThrowRE("Unknown return value from step - " |
| + step_result); |
| break; |
| } |
| } |
| catch (Exception) { |
| this.NoisyLog("CPU exception detected, halting."); |
| InvokeHalted(new HaltArguments(HaltReason.Abort, Id)); |
| return ExecutionResult.Aborted; |
| } |
| finally { |
| numberOfExecutedInstructions = instructionsExecutedThisRound; |
| totalExecutedInstructions += instructionsExecutedThisRound; |
| } |
| return result; |
| } |
| |
| // ICPUWithRegisters methods implementations. |
| public void SetRegisterUnsafe(int register, RegisterValue value) { |
| var status = write_register((Int32)mpact_id, (Int32)register, |
| (UInt64)value); |
| if (status < 0) { |
| LogAndThrowRE("Failed to write register " + register); |
| } |
| } |
| |
| public RegisterValue GetRegisterUnsafe(int register) { |
| var status = read_register(mpact_id, register, value_ptr); |
| if (status < 0) { |
| LogAndThrowRE("Failed to read register " + register); |
| } |
| Int64 value = Marshal.ReadInt64(value_ptr); |
| return (UInt64)value; |
| } |
| |
| private void GetMpactRegisters() { |
| if (registerMap.Count != 0) return; |
| Int32 num_regs = get_reg_info_size(mpact_id); |
| for (Int32 i = 0; i < num_regs; i++) { |
| Int32 result = get_reg_info(mpact_id, i, string_ptr, reg_info_ptr); |
| if (result < 0) { |
| this.Log(LogLevel.Error, |
| "Failed to get register info for index " + i); |
| continue; |
| } |
| var reg_info = Marshal.PtrToStructure<RegInfo>(reg_info_ptr); |
| var cpu_reg = new CPURegister(reg_info.index, reg_info.width, |
| reg_info.isGeneral, |
| reg_info.isReadOnly); |
| var reg_name = Marshal.PtrToStringAuto(string_ptr); |
| registerMap.Add(reg_info.index, cpu_reg); |
| registerNamesMap.Add(reg_name, reg_info.index); |
| } |
| } |
| |
| public IEnumerable<CPURegister> GetRegisters() { |
| if (registerMap.Count == 0) { |
| GetMpactRegisters(); |
| } |
| return registerMap.Values.OrderBy(x => x.Index); |
| } |
| |
| public new string[,] GetRegistersValues() { |
| if (registerMap.Count == 0) { |
| GetMpactRegisters(); |
| } |
| var result = new Dictionary<string, ulong>(); |
| foreach (var reg in registerNamesMap) { |
| var status = read_register(mpact_id, reg.Value, value_ptr); |
| if (status < 0) continue; |
| Int64 value = Marshal.ReadInt64(value_ptr); |
| result.Add(reg.Key, Convert.ToUInt64(value)); |
| } |
| var table = new Table().AddRow("Name", "Value"); |
| table.AddRows(result, x=> x.Key, x=> "0x{0:X}".FormatWith(x.Value)); |
| return table.ToArray(); |
| } |
| |
| private void LogAndThrowRE(string info) { |
| this.Log(LogLevel.Error, info); |
| throw new RecoverableException(info); |
| } |
| |
| public Int32 LoadImageFile(String file_name, UInt64 address) { |
| return load_image(mpact_id, file_name, address); |
| } |
| |
| public void OnGPIO(int number, bool value) { |
| if (mpact_id <= 0) { |
| this.Log(LogLevel.Noisy, "OnGPIO: no simulator, discard gpio {0}:{1}", number, value); |
| return; |
| } |
| Int32 status = set_irq_value(mpact_id, number, value); |
| if (status < 0) { |
| Console.Error.WriteLine("Failure in setting irq value"); |
| } |
| } |
| |
| public Int32 ReadSysMemory(UInt64 address, IntPtr buffer, Int32 length) { |
| // Read from System Bus. |
| switch (length) { |
| case 1: |
| var data8 = new byte[length]; |
| data8[0] = machine.SystemBus.ReadByte(address, this); |
| Marshal.Copy(data8, 0, buffer, length); |
| break; |
| case 2: |
| var data16 = new Int16[1]; |
| data16[0] = (Int16) machine.SystemBus.ReadWord(address, this); |
| Marshal.Copy(data16, 0, buffer, 1); |
| break; |
| case 4: |
| var data32 = new Int32[1]; |
| data32[0] = (Int32) machine.SystemBus.ReadDoubleWord(address, |
| this); |
| Marshal.Copy(data32, 0, buffer, 1); |
| break; |
| case 8: |
| var data64 = new Int64[1]; |
| data64[0] = (Int64) machine.SystemBus.ReadQuadWord(address, |
| this); |
| Marshal.Copy(data64, 0, buffer, 1); |
| break; |
| default: |
| var dataN = new byte[length]; |
| machine.SystemBus.ReadBytes(address, length, dataN, 0, false, |
| this); |
| Marshal.Copy(dataN, 0, buffer, length); |
| break; |
| } |
| return length; |
| } |
| |
| public Int32 WriteSysMemory(UInt64 address, IntPtr buffer, Int32 length) { |
| switch (length) { |
| case 1: |
| var data8 = new byte[1]; |
| Marshal.Copy(buffer, data8, 0, 1); |
| machine.SystemBus.WriteByte(address, data8[0], this); |
| break; |
| case 2: |
| var data16 = new Int16[1]; |
| Marshal.Copy(buffer, data16, 0, 1); |
| machine.SystemBus.WriteWord(address, (ushort)data16[0], this); |
| break; |
| case 4: |
| var data32 = new Int32[1]; |
| Marshal.Copy(buffer, data32, 0, 1); |
| machine.SystemBus.WriteDoubleWord(address, (uint)data32[0], |
| this); |
| break; |
| case 8: |
| var data64 = new Int64[1]; |
| Marshal.Copy(buffer, data64, 0, 1); |
| machine.SystemBus.WriteQuadWord(address, (ulong)data64[0], |
| this); |
| break; |
| default: |
| var dataN = new byte[length]; |
| Marshal.Copy(buffer, dataN, 0, length); |
| machine.SystemBus.WriteBytes(dataN, address); |
| break; |
| } |
| return length; |
| } |
| |
| // ICPUWithHooks methods. |
| |
| public void AddHookAtInterruptBegin(Action<ulong> hook) { |
| throw new RecoverableException( |
| "Interrupt hooks not currently supported"); |
| } |
| public void AddHookAtInterruptEnd(Action<ulong> hook) { |
| throw new RecoverableException( |
| "Interrupt hooks not currently supported"); |
| } |
| public void AddHookAtWfiStateChange(Action<bool> hook) { |
| throw new RecoverableException( |
| "Wfi state change hook not currently supported"); |
| } |
| |
| public void AddHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook) { |
| throw new RecoverableException("Hooks not currently supported"); |
| } |
| public void RemoveHook(ulong addr, Action<ICpuSupportingGdb, ulong> hook) { |
| /* empty */ |
| } |
| public void RemoveHooksAt(ulong addr) { /* empty */ } |
| public void RemoveAllHooks() { /* empty */ } |
| |
| // ICpuSuportingGdb methods. |
| |
| public void EnterSingleStepModeSafely(HaltArguments args, |
| bool? blocking = null) { |
| CheckCpuThreadId(); |
| ChangeExecutionModeToSingleStep(blocking); |
| UpdateHaltedState(); |
| InvokeHalted(args); |
| } |
| |
| public string GDBArchitecture { get { return "riscv:cheriot"; } } |
| |
| public List<GDBFeatureDescriptor> GDBFeatures { |
| get { |
| if (gdbFeatures.Any()) return gdbFeatures; |
| |
| // Populate the register map if it hasn't been done yet. |
| if (registerNamesMap.Count == 0) { |
| GetMpactRegisters(); |
| } |
| var cpuGroup = new GDBFeatureDescriptor("org.gnu.gdb.riscv.cpu"); |
| var intType = $"uint32"; |
| var c0_index = registerNamesMap["c0"]; |
| for (var i = 0u; i < 16; ++i) { |
| cpuGroup.Registers.Add( |
| new GDBRegisterDescriptor((uint)c0_index + i, 32u, |
| $"c{i}", intType, "general")); |
| } |
| var pcc_index = registerNamesMap["pcc"]; |
| cpuGroup.Registers.Add( |
| new GDBRegisterDescriptor((uint)pcc_index, 32u, "pcc", |
| "code_ptr", "general")); |
| gdbFeatures.Add(cpuGroup); |
| |
| return gdbFeatures; |
| } |
| } |
| |
| // End of ICpuSupportingGdb methods. |
| |
| // IMpactPeripheral methods. |
| public void WriteByte(long address, byte value) { |
| var data8 = new byte[1]; |
| data8[0] = (byte) value; |
| Marshal.Copy(data8, 0, value_ptr, 1); |
| write_memory(mpact_id, (UInt64) address + base_address, value_ptr, 1); |
| } |
| |
| public byte ReadByte(long address) { |
| var data8 = new byte[1]; |
| read_memory(mpact_id, (UInt64) address + base_address, value_ptr, 1); |
| Marshal.Copy(value_ptr, data8, 0, 1); |
| return (byte) data8[0]; |
| } |
| |
| public void WriteWord(long address, ushort value) { |
| var data16 = new Int16[1]; |
| data16[0] = (Int16) value; |
| Marshal.Copy(data16, 0, value_ptr, 1); |
| write_memory(mpact_id, (UInt64) address + base_address, value_ptr, 2); |
| } |
| |
| public ushort ReadWord(long address) { |
| var data16 = new Int16[1]; |
| read_memory(mpact_id, (UInt64) address + base_address, value_ptr, 2); |
| Marshal.Copy(value_ptr, data16, 0, 1); |
| return (ushort) data16[0]; |
| } |
| |
| public void WriteDoubleWord(long address, uint value) { |
| var data32 = new Int32[1]; |
| data32[0] = (Int32) value; |
| Marshal.Copy(data32, 0, value_ptr, 1); |
| write_memory(mpact_id, (UInt64) address + base_address, value_ptr, 4); |
| } |
| |
| public uint ReadDoubleWord(long address) { |
| var data32 = new Int32[1]; |
| read_memory(mpact_id, (UInt64) address + base_address, value_ptr, 4); |
| Marshal.Copy(value_ptr, data32, 0, 1); |
| return (uint) data32[0]; |
| } |
| |
| public void WriteQuadWord(long address, ulong value) { |
| var data64 = new Int64[1]; |
| data64[0] = (Int64) value; |
| Marshal.Copy(data64, 0, value_ptr, 1); |
| write_memory(mpact_id, (UInt64) address + base_address, value_ptr, 8); |
| } |
| |
| public ulong ReadQuadWord(long address) { |
| var data64 = new Int64[1]; |
| read_memory(mpact_id, (UInt64) address + base_address, value_ptr, 8); |
| Marshal.Copy(value_ptr, data64, 0, 1); |
| return (ulong) data64[0]; |
| } |
| |
| public byte[] ReadBytes(long offset, int count, ICPU context = null) { |
| var bytes = new byte[count]; |
| var byte_array_ptr = Marshal.AllocHGlobal(count); |
| read_memory(mpact_id, (UInt64) offset + base_address, byte_array_ptr, |
| count); |
| Marshal.Copy(value_ptr, bytes, 0, count); |
| Marshal.FreeHGlobal(byte_array_ptr); |
| return bytes; |
| } |
| |
| public void WriteBytes(long offset, byte[] array, int startingIndex, |
| int count, ICPU context = null) { |
| var byte_array_ptr = Marshal.AllocHGlobal(count); |
| Marshal.Copy(array, startingIndex, byte_array_ptr, count); |
| write_memory(mpact_id, (UInt64) offset + base_address, byte_array_ptr, |
| count); |
| Marshal.FreeHGlobal(byte_array_ptr); |
| } |
| |
| // End of IMpactPeripheral methods. |
| |
| private ushort cli_port = 0; |
| private bool wait_for_cli = false; |
| private UInt64 base_address = 0; |
| private UInt64 size = 0; |
| private List<GDBFeatureDescriptor> |
| gdbFeatures = new List<GDBFeatureDescriptor>(); |
| private UInt64 memoryBase; |
| private UInt64 memorySize; |
| private UInt64 revocationMemBase; |
| private Dictionary<Int32, CPURegister> |
| registerMap = new Dictionary<Int32, CPURegister>(); |
| private Dictionary<string, Int32> |
| registerNamesMap = new Dictionary<string, Int32>(); |
| private string executable_file = ""; |
| private BinFileLoadInfo |
| bin_file_info = new BinFileLoadInfo {file_name = "", |
| load_address = 0, |
| entry_point = 0}; |
| private string cpu_library_path = ""; |
| private bool cpu_library_bound = false; |
| private NativeBinder binder; |
| private readonly object nativeLock; |
| private readonly Int32 maxStringLen = 32; |
| |
| // Pointers used in marshaling data to/from the simulator library. |
| private IntPtr value_ptr {get; set;} |
| private IntPtr error_ptr {get; set;} |
| private IntPtr reg_info_ptr {get; set;} |
| private IntPtr string_ptr {get; set;} |
| |
| // Declare some additional function signatures. |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| public delegate Int32 ConnectWithSysbus(Int32 param0, Int32 param1, |
| FuncInt32UInt64IntPtrInt32 param2, |
| FuncInt32UInt64IntPtrInt32 param3); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| public delegate Int32 ConstructWithSysbus(Int32 param0, |
| FuncInt32UInt64IntPtrInt32 param1, |
| FuncInt32UInt64IntPtrInt32 param2); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| public delegate Int32 SetConfig(Int32 param0, string[] param1, |
| string[] param2, Int32 param3); |
| |
| [UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
| public delegate Int32 SetIrqValue(Int32 param0, Int32 param1, bool param2); |
| |
| // Int32 read_sysmem_delegate(UInt64 address, IntPtr buffer, Int32 size) |
| private FuncInt32UInt64IntPtrInt32 read_sysmem_delegate; |
| // Int32 write_sysmem_delegate(UInt64 address, IntPtr buffer, Int32 size) |
| private FuncInt32UInt64IntPtrInt32 write_sysmem_delegate; |
| |
| // Functions that are imported from the mpact sim library. |
| #pragma warning disable 649 |
| [Import(UseExceptionWrapper = false)] |
| // Int32 construct_with_sysbus(Int32 id, |
| // Int32 read_callback(UInt64, IntPtr, Int32), |
| // Int32 write_callback(UInt64, IntPtr, Int32)); |
| private ConstructWithSysbus construct_with_sysbus; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 connect_with_sybsus(Int32 id, |
| // Int32 read_callback(UInt64, IntPtr, Int32), |
| // Int32 write_callback(UInt64, IntPtr, Int32)); |
| private ConnectWithSysbus connect_with_sysbus; |
| |
| [Import(UseExceptionWrapper = false)] |
| // void destruct(Int32 id); |
| private ActionInt32 destruct; |
| |
| [Import(UseExceptionWrapper = false)] |
| // void reset(Int32 id); |
| private ActionInt32 reset; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 get_reg_info_size(Int32 id) |
| private FuncInt32Int32 get_reg_info_size; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 get_reg_info(Int32 id, IntPtr *name, IntPtr *struct); |
| private FuncInt32Int32Int32IntPtrIntPtr get_reg_info; |
| |
| [Import(UseExceptionWrapper = false)] |
| // UInt64 load_executable(Int32 id, String file_name); |
| private FuncUInt64Int32StringIntPtr load_executable; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 load_image(Int32 id, String file_name, UInt64 address); |
| private FuncInt32Int32StringUInt64 load_image; |
| |
| [Import(UseExceptionWrapper = false)] |
| // UInt64 step(Int32 id, UInt64 step, IntPtr *error); |
| private FuncUInt64Int32UInt64IntPtr step; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 read_register(Int32 id, Int32 reg_id, IntPtr *value); |
| private FuncInt32Int32Int32IntPtr read_register; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 write_register(Int32 id, Int32 reg_id, UInt64 value); |
| private FuncInt32Int32Int32UInt64 write_register; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 read_memory(Int32 id, UInt64 address, IntPtr buffer, Int32 size); |
| private FuncInt32Int32UInt64IntPtrInt32 read_memory; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 write_memory(Int32 id, UInt64 address, IntPtr buffer, Int32 size); |
| private FuncInt32Int32UInt64IntPtrInt32 write_memory; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 set_config(Int32 id, string[] names string[] values, Int32 size); |
| private SetConfig set_config; |
| |
| [Import(UseExceptionWrapper = false)] |
| // Int32 set_irq_value(Int32 id, Int32 irq_no, bool value); |
| private SetIrqValue set_irq_value; |
| |
| #pragma warning restore 649 |
| |
| private Int32 mpact_id = -1; |
| private ulong instructionsExecutedThisRound {get; set;} |
| private ulong totalExecutedInstructions {get; set;} |
| private const int PC_ID = 0x07b1; |
| } // class MpactCheriotCPU |
| |
| |
| // Peripheral interface class. |
| public class MpactCheriotPeripheral : IKnownSize, IBytePeripheral, |
| IWordPeripheral, IDoubleWordPeripheral, |
| IQuadWordPeripheral, |
| IMultibyteWritePeripheral, IDisposable { |
| |
| public MpactCheriotPeripheral(IMachine machine, long baseAddress, |
| long size, IMpactPeripheral mpactCpu) { |
| if (size == 0) { |
| throw new ConstructionException("Memory size cannot be 0"); |
| } |
| this.machine = machine; |
| this.size = size; |
| this.base_address = baseAddress; |
| this.mpact_cpu = mpactCpu; |
| } |
| |
| public void Reset() {} |
| |
| public void Dispose() {} |
| |
| // All memory accesses are forwarded to the mpactCpu with the base_address |
| // added to the address field. |
| public void WriteByte(long address, byte value) { |
| mpact_cpu.WriteByte(address + base_address, value); |
| } |
| |
| public byte ReadByte(long address) { |
| return mpact_cpu.ReadByte(address + base_address); |
| } |
| |
| public void WriteWord(long address, ushort value) { |
| mpact_cpu.WriteWord(address + base_address, value); |
| } |
| |
| public ushort ReadWord(long address) { |
| return mpact_cpu.ReadWord(address + base_address); |
| } |
| |
| public void WriteDoubleWord(long address, uint value) { |
| mpact_cpu.WriteDoubleWord(address + base_address, value); |
| } |
| |
| public uint ReadDoubleWord(long address) { |
| return mpact_cpu.ReadDoubleWord(address + base_address); |
| } |
| |
| public void WriteQuadWord(long address, ulong value) { |
| mpact_cpu.WriteQuadWord(address + base_address, value); |
| } |
| |
| public ulong ReadQuadWord(long address) { |
| return mpact_cpu.ReadQuadWord(address + base_address); |
| } |
| |
| public byte[] ReadBytes(long offset, int count, ICPU context = null) { |
| return mpact_cpu.ReadBytes(offset + base_address, count, context); |
| } |
| |
| public void WriteBytes(long offset, byte[] array, int startingIndex, |
| int count, ICPU context = null) { |
| mpact_cpu.WriteBytes(offset + base_address, array, startingIndex, |
| count, context); |
| } |
| |
| public long Size { get { return size;} } |
| private IMpactPeripheral mpact_cpu; |
| private long size; |
| private long base_address = 0; |
| private IMachine machine; |
| |
| } // class MpactCheriotPeripheral |
| |
| } // namespace Antmicro.Renode.Peripherals.MpactCPU |
| |