blob: 57f5bfde51a2494c66629135fca4b518653ba7cf [file]
//
// Copyright (c) 2010-2024 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.Linq;
using System.Threading;
using System.Runtime.InteropServices;
using Antmicro.Renode.Core;
using Antmicro.Renode.Exceptions;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Peripherals.CPU;
using Antmicro.Renode.Peripherals.CFU;
using Antmicro.Renode.Peripherals.Timers;
using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection;
using Antmicro.Renode.Plugins.CoSimulationPlugin.Connection.Protocols;
using Antmicro.Renode.Utilities;
using Antmicro.Renode.Utilities.Binding;
using Antmicro.Renode.Core.Structure;
namespace Antmicro.Renode.Peripherals.CoSimulated
{
public class CoSimulatedCFU : ICFU, ICoSimulationConnectible, IDisposable
{
public CoSimulatedCFU(Machine machine, long frequency = 0, ulong limitBuffer = LimitBuffer, int timeout = DefaultTimeout, string simulationFilePathLinux = null, string simulationFilePathWindows = null, string simulationFilePathMacOS = null)
{
// Multiple CoSimulatedCFUs per CoSimulationConnection are currently not supported.
RenodeToCosimIndex = 0;
CosimToRenodeIndex = 0;
connection = new CoSimulationConnection(machine, "cosimulation_connection", frequency,
simulationFilePathLinux, simulationFilePathWindows, simulationFilePathMacOS,
null, null, null,
limitBuffer, timeout, null);
connection.AttachTo(this);
errorPointer = IntPtr.Zero;
}
public virtual void OnConnectionAttached(CoSimulationConnection connection)
{
}
public virtual void OnConnectionDetached(CoSimulationConnection connection)
{
}
public void Reset()
{
connection?.Reset();
}
public void Dispose()
{
connection.Dispose();
executeBinder?.Dispose();
Marshal.FreeHGlobal(errorPointer);
}
public string SimulationContextLinux
{
get => connection.SimulationContextLinux;
set
{
AssureIsConnected();
connection.SimulationContextLinux = value;
}
}
public string SimulationContextWindows
{
get => connection.SimulationContextWindows;
set
{
AssureIsConnected();
connection.SimulationContextWindows = value;
}
}
public string SimulationContextMacOS
{
get => connection.SimulationContextMacOS;
set
{
AssureIsConnected();
connection.SimulationContextMacOS = value;
}
}
public string SimulationContext
{
get => connection.SimulationContext;
set
{
AssureIsConnected();
connection.SimulationContext = value;
}
}
public string SimulationFilePathLinux
{
get => connection.SimulationFilePathLinux;
set
{
AssureIsConnected();
connection.SimulationFilePathLinux = value;
}
}
public string SimulationFilePathWindows
{
get => connection.SimulationFilePathWindows;
set
{
AssureIsConnected();
connection.SimulationFilePathWindows = value;
}
}
public string SimulationFilePathMacOS
{
get => connection.SimulationFilePathMacOS;
set
{
AssureIsConnected();
connection.SimulationFilePathMacOS = value;
}
}
public string SimulationFilePath
{
get => connection.SimulationFilePath;
set
{
AssureIsConnected();
if(String.IsNullOrWhiteSpace(value))
{
this.Log(LogLevel.Warning, "SimulationFilePath not set!");
return;
}
else if(!String.IsNullOrWhiteSpace(SimulationFilePath))
{
LogAndThrowRE("Verilated peripheral already connected, cannot change the file name!");
}
else if(connectedCpu.Children.Any(child => child.Peripheral.SimulationFilePath == value))
{
LogAndThrowRE("Another CFU already connected to provided library!");
}
else
{
try
{
errorPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));
executeBinder = new NativeBinder(this, value);
connection.SimulationFilePath = value;
}
catch(Exception e)
{
LogAndThrowRE(e.Message);
}
}
}
}
public ICPU ConnectedCpu
{
get
{
return connectedCpu;
}
set
{
if(ConnectedCpu != null)
{
LogAndThrowRE("CFU already connected to CPU, cannot change CPU!");
}
else
{
connectedCpu = value as BaseRiscV;
if(connectedCpu == null)
{
LogAndThrowRE("CFU is supported for RISCV-V CPUs only!");
}
RegisterCFU();
}
}
}
// This function is not used here but it is required to properly bind with libVtop.so
// so it is left empty on purpose.
[Export]
private void HandleSenderMessage(IntPtr received)
{
}
private void LogAndThrowRE(string info)
{
this.Log(LogLevel.Error, info);
throw new RecoverableException(info);
}
private void RegisterCFU()
{
string opcodePattern = "";
int connectedCfus = connectedCpu.ChildCollection.Count;
switch(connectedCfus)
{
case 1:
opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC0001011";
break;
case 2:
opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC0101011";
break;
case 3:
opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC1001011";
break;
case 4:
opcodePattern = "FFFFFFFAAAAABBBBBIIICCCCC1101011";
break;
default:
this.LogAndThrowRE("Can't handle more than 4 CFUs!");
break;
}
connectedCpu.InstallCustomInstruction(pattern: opcodePattern, handler: opcode => HandleCustomInstruction(opcode));
this.Log(LogLevel.Noisy, "CFU {0} registered", connectedCfus);
}
private void HandleCustomInstruction(ulong opcode)
{
int rD = (int)BitHelper.GetValue(opcode, 7, 5);
int rs1 = (int)BitHelper.GetValue(opcode, 15, 5);
UInt32 rs1Value = Convert.ToUInt32(connectedCpu.GetRegister(rs1).RawValue);
int rs2 = (int)BitHelper.GetValue(opcode, 20, 5);
UInt32 rs2Value = Convert.ToUInt32(connectedCpu.GetRegister(rs2).RawValue);
UInt32 funct3 = Convert.ToUInt32(BitHelper.GetValue(opcode, 12, 3));
UInt32 funct7 = Convert.ToUInt32(BitHelper.GetValue(opcode, 25, 7));
UInt32 functionID = (funct7 << 3) + funct3;
UInt64 result = 0UL;
result = execute(functionID, rs1Value, rs2Value, errorPointer);
CfuStatus status = (CfuStatus)Marshal.ReadInt32(errorPointer);
switch(status)
{
case CfuStatus.CfuOk:
connectedCpu.SetRegister(rD, result);
break;
case CfuStatus.CfuFail:
this.Log(LogLevel.Error, "CFU custom instruction error, opcode: 0x{0:x}, error: {1}", opcode, status);
break;
case CfuStatus.CfuTimeout:
this.Log(LogLevel.Error, "CFU operation timeout, opcode: 0x{0:x}, error: {1}", opcode, status);
break;
default:
this.Log(LogLevel.Error, "CFU unknown error, opcode: 0x{0:x}, error: {1}", opcode, status);
break;
}
}
private void AssureIsConnected(string message = null)
{
if(connection == null)
{
throw new RecoverableException("CoSimulatedPeripheral is not attached to a CoSimulationConnection.");
}
}
public int RenodeToCosimIndex { get; }
public int CosimToRenodeIndex { get; }
protected const ulong LimitBuffer = 100000;
protected const int DefaultTimeout = 3000;
private NativeBinder executeBinder;
private BaseRiscV connectedCpu;
private IntPtr errorPointer;
#pragma warning disable 649
[Import(UseExceptionWrapper = false)]
private FuncUInt64UInt32UInt32UInt32IntPtr execute;
#pragma warning restore 649
private enum CfuStatus
{
CfuOk = 0,
CfuFail = 1,
CfuTimeout = 2
}
protected CoSimulationConnection connection;
}
}