Commit 30a5fdb9 authored by Evgeny Kusmenko's avatar Evgeny Kusmenko

Merge branch 'generic-emulator' into 'master'

Generic emulator: SoftwareSimulator

See merge request !6
parents ad15651b 7fde6cc1
Pipeline #205576 passed with stages
in 7 minutes and 28 seconds
......@@ -13,4 +13,7 @@ zydis/dependencies/zycore/Release/
.settings/
.vscode/
.classpath
target/
\ No newline at end of file
target/
unicorn/
zydis/
pe-parse/
\ No newline at end of file
# Hardware Emulator
- Dynamic Interface
- SoftwareSimulator
- HardwareEmulator
- Controller
## Overview
This project allows to run **EmbeddedMontiArc** models and let them communicate with the MontiSim simulation. It allows the time evaluation of the model execution using a parametric computer model. The *Hardware Emulator* can execute multiple models independently of each other.
This project allows to run **EmbeddedMontiArc** models that have a **DynamicInterface** and let them communicate with the MontiSim simulation. It allows the time evaluation of the model execution using a parametric computer model.
......@@ -12,7 +16,7 @@ The project contains a **C++ CMake project** and a **Maven project**.
The C++ project contains the logic of the Hardware Emulator.
The Maven project takes this C++ library and wraps it in a Jar alongside its Java Interfaces. The resulting Jar is avaiable as dependency in the **nexus**.
## C++ Project
### C++ Project
To compile the C++ project (under the [hardware_emulator](hardware_emulator) folder), use the `build_emulator` script for your system (located in the [scripts](scripts) folder).
> For Visual Studio, the script has to be started inside the Visual Studio **Developer Command Prompt**.
......@@ -23,13 +27,13 @@ The `build_emulator` script will directly install the compiled library inside th
_**NOTE:** Any changes to the C++ project **MUST** be compiled and installed under both **Linux and Windows** before being merged into the Master branch so that both updated versions are included in the maven artifact._
## Maven Project
### Maven Project
Simply use the `mvn install` command in the main directory to compile the maven project.
> The project uses the [LibraryService](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/simulators/commons/blob/master/src/main/java/commons/utils/LibraryService.java) system to make libraries stored inside the jar resources available at runtime (depending on the system).
## C++ dependencies
### C++ dependencies
The libraries required by the C++ project are pre-compiled under [hardware_emulator/libs](hardware_emulator/libs). They can be recompiled using the `build_dependencies` scripts.
......@@ -53,8 +57,20 @@ To use the Hardware Emulator inside another Maven project add the following depe
```
Where `montisim.hardware_emulator.version` specifies the artifact version.
The interface to use the Hardware Emulator is [HardwareEmulatorInterface.java](src/main/java/de/rwth_aachen/git/monticore/EmbeddedMontiArc/simulators/hardware_emulator/HardwareEmulatorInterface.java)
The main interface for a simulator is [SoftwareSimulator](src/main/java/de/rwth/monticore/EmbeddedMontiArc/simulators/hardware_emulator/interfaces/SoftwareSimulator.java).
> **NOTE**: The Hardware Emulator is currently used by the [RMIModelServer](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/simulators/RMIModelServer) to start models on seperate machines using RMI (Remote Method Invocation).
>
> It is also used by the [basic-simulator](https://git.rwth-aachen.de/monticore/EmbeddedMontiArc/simulators/basic-simulator) directly.
Depending on whether the usage of the SoftwareSimulator is remote or not, it has to be allocated through a different instance of a [SoftwareSimulatorManager](src/main/java/de/rwth/monticore/EmbeddedMontiArc/simulators/hardware_emulator/interfaces/SoftwareSimulatorManager.java): `DirectSoftwareSimulatorManager` or `RemoteSoftwareSimulatorManager`. The remote version will return a RMI reference to the remote simulator.
## Details
This project is build from the following main components:
![Project Structure](docs/Structure.svg)
The components used to discover the ports of a *DynamicInterface* software and responsible for the communication between the MontiSim simulator and the software are shown in the following:
![Port Structure](docs/PortStructureVert.svg)
The main idea is to discover the name and types of the ports (performed by the `DynamicInterfaceResolver`), to store these informations in `PortInformation` structures, then depending on the actual simulator implementation used, allocate actual `Port` instances that can store and transfer the specific data type (`PortSimpleIntDirect`, `PortArrayIntEmu`, ...).
The templated `PortSimple` and `PortArray` implementations already define how the port communicates with the Java simulator (through JNI), but how the port data is given to the software is depending on how the software is loaded (as native library or in the Computer emulation), which is specified in the variants `Port...Emu` and `Port...Direct`.
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
......@@ -30,25 +30,35 @@ set(LIBRARY_NAME "HardwareEmulator")
#Basic source files
list(APPEND HARDWARE_EMU_SOURCEFILES
src/debug.h
src/debug.cpp
src/tests.h
src/tests.cpp
src/utility.h
src/utility.cpp
src/config.h
src/config.cpp
src/dll_interface.h
src/dll_interface.cpp
src/software_simulator_manager.h
src/software_simulator_manager.cpp
src/software_simulator.h
src/software_simulator.cpp
src/hardware_emulator.h
src/hardware_emulator.cpp
src/direct_software_simulator.h
src/direct_software_simulator.cpp
src/utility/debug.h
src/utility/debug.cpp
src/utility/utility.h
src/utility/utility.cpp
src/utility/config.h
src/utility/config.cpp
src/utility/dll_interface.h
src/utility/dll_interface.cpp
src/utility/jni_interface.h
src/utility/jni_interface.cpp
src/emulator/emulator_manager.h
src/emulator/emulator_manager.cpp
src/emulator/hardware_emulator.h
src/emulator/hardware_emulator.cpp
src/emulator/function_value.h
src/emulator/function_value.cpp
src/port/port.h
src/port/port_simple.h
src/port/port_array.h
src/dynamic_interface/resolver.h
src/dynamic_interface/resolver.cpp
src/dynamic_interface/port_info.h
src/dynamic_interface/port_info.cpp
src/computer/instruction_time.h
src/computer/instruction_time.cpp
......@@ -91,13 +101,14 @@ list(APPEND HARDWARE_EMU_SOURCEFILES
#Source files for the test project
list(APPEND HARDWARE_EMU_TEST_SOURCEFILES
src/main.cpp
src/utility/main.cpp
src/utility/tests.h
src/utility/tests.cpp
)
#Source files for the Autopilot Library
list(APPEND HARDWARE_EMU_LIB_SOURCEFILES
src/emulator/de_rwth_monticore_EmbeddedMontiArc_simulators_hardware_emulator_HardwareEmulatorInterface.h
src/emulator/emulator_server.cpp
src/de_rwth_monticore_EmbeddedMontiArc_simulators_hardware_emulator_CppBridge.h
)
#Setup the project file structure for Visual Studio projects (OPTIONAL)
......@@ -142,6 +153,7 @@ if ( CMAKE_COMPILER_IS_GNUCC )
-std=c++17
-fPIC
-fpermissive
-static-libgcc
-static-libstdc++
/DZYDIS_STATIC_DEFINE
)
......@@ -176,7 +188,7 @@ endif()
#Test project
LINK_DIRECTORIES(${PROJECT_NAME}-test ${HARDWARE_EMU_LIB_DIRS})
link_directories(${PROJECT_NAME}-test ${HARDWARE_EMU_LIB_DIRS})
add_executable(${PROJECT_NAME}-test ${HARDWARE_EMU_SOURCEFILES} ${HARDWARE_EMU_TEST_SOURCEFILES})
target_include_directories(${PROJECT_NAME}-test PUBLIC ${HARDWARE_EMU_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME}-test ${HARDWARE_EMU_LIBRARIES})
......@@ -186,7 +198,7 @@ set_target_properties(${PROJECT_NAME}-test PROPERTIES VS_DEBUGGER_WORKING_DIRECT
#HardwareEmulatorLibrary
LINK_DIRECTORIES(${LIBRARY_NAME} ${HARDWARE_EMU_LIB_DIRS} ${HARDWARE_EMU_LIB_LIB_DIRS})
link_directories(${LIBRARY_NAME} ${HARDWARE_EMU_LIB_DIRS} ${HARDWARE_EMU_LIB_LIB_DIRS})
add_library(${LIBRARY_NAME} SHARED ${HARDWARE_EMU_SOURCEFILES} ${HARDWARE_EMU_LIB_SOURCEFILES})
target_include_directories(${LIBRARY_NAME} PUBLIC ${HARDWARE_EMU_INCLUDE_DIRS} ${HARDWARE_EMU_LIB_INCLUDE_DIRS} )
target_link_libraries(${LIBRARY_NAME} ${HARDWARE_EMU_LIBRARIES} ${HARDWARE_EMU_LIB_LIBRARIES})
......
No preview for this file type
/**
* (c) https://github.com/MontiCore/monticore
*
* The license generally applicable for this project
* can be found under https://github.com/MontiCore/monticore.
*/
#include "autopilot_functions.h"
const char *AutopilotFunction::module_name = "Java_simulator_integration_AutopilotAdapter_";
AutopilotFunction AutopilotFunction::autopilot_inputs[AUTOPILOT_INPUT_COUNT] = {
#include "autopilot_inputs.h"
};
AutopilotFunction AutopilotFunction::autopilot_outputs[AUTOPILOT_OUTPUT_COUNT] = {
#include "autopilot_outputs.h"
};
/**
* (c) https://github.com/MontiCore/monticore
*
* The license generally applicable for this project
* can be found under https://github.com/MontiCore/monticore.
*/
#pragma once
#include <string>
#include "utility.h"
#include "autopilot_port_info.h"
struct AutopilotFunction {
static const char *module_name;
static AutopilotFunction autopilot_inputs[AUTOPILOT_INPUT_COUNT];
static AutopilotFunction autopilot_outputs[AUTOPILOT_OUTPUT_COUNT];
const char *name;
VALUE_TYPE type;
static std::string get_jni_name( const std::string &name ) {
auto u_pos = name.find( '_' );
if ( u_pos == std::string::npos )
return name;
std::string res;
size_t last_pos = 0;
do {
++u_pos;
res += name.substr( last_pos, u_pos - last_pos );
res += "1";
last_pos = u_pos;
u_pos = name.find( '_', u_pos );
} while ( u_pos != std::string::npos );
res += name.substr( last_pos );
return res;
}
std::string get_input_name() {
return std::string( module_name ) + "set_1" + get_jni_name( name );
}
std::string get_output_name() {
return std::string( module_name ) + "get_1" + get_jni_name( name );
}
static std::string get_name( const char *name ) {
return std::string( module_name ) + get_jni_name( name );
}
};
/**
* (c) https://github.com/MontiCore/monticore
*
* The license generally applicable for this project
* can be found under https://github.com/MontiCore/monticore.
*/
{ "timeIncrement", VALUE_TYPE::DOUBLE },
{ "currentVelocity", VALUE_TYPE::DOUBLE },
{ "x", VALUE_TYPE::DOUBLE },
{ "y", VALUE_TYPE::DOUBLE },
{ "compass", VALUE_TYPE::DOUBLE },
{ "currentEngine", VALUE_TYPE::DOUBLE },
{ "currentSteering", VALUE_TYPE::DOUBLE },
{ "currentBrakes", VALUE_TYPE::DOUBLE },
{ "trajectory_length", VALUE_TYPE::INT },
{ "trajectory_x", VALUE_TYPE::DOUBLE_ARRAY },
{ "trajectory_y", VALUE_TYPE::DOUBLE_ARRAY }
/**
* (c) https://github.com/MontiCore/monticore
*
* The license generally applicable for this project
* can be found under https://github.com/MontiCore/monticore.
*/
#include "autopilot_interface.h"
#include "utility.h"
#include "os_windows/windows_calls.h"
using namespace std;
AutopilotInterface AutopilotInterface::instance;
bool AutopilotInterface::test_main() {
computer.debug.debug = true;
computer.debug.d_mem = false;
computer.debug.d_regs = false;
computer.debug.d_reg_update = false;
computer.debug.d_syscalls = false;
Log::debug << "AutopilotInterface::init()\n";
if ( !init() )
return false;
Log::debug << "init(0,0)\n";
emulator.call_void( emulator.init_address );
/*
{ "timeIncrement", VALUE_TYPE::DOUBLE }, //0
{ "currentVelocity", VALUE_TYPE::DOUBLE }, //1
{ "x", VALUE_TYPE::DOUBLE }, //2
{ "y", VALUE_TYPE::DOUBLE }, //3
{ "compass", VALUE_TYPE::DOUBLE }, //4
{ "currentEngine", VALUE_TYPE::DOUBLE }, //5
{ "currentSteering", VALUE_TYPE::DOUBLE }, //6
{ "currentBrakes", VALUE_TYPE::DOUBLE }, //7
{ "trajectory_length", VALUE_TYPE::INT }, //8
{ "trajectory_x", VALUE_TYPE::DOUBLE_ARRAY }, //9
{ "trajectory_y", VALUE_TYPE::DOUBLE_ARRAY } //10
*/
Log::debug << "set_timeIncrement(1)\n";
emulator.get_input( 0 ).init( 1 );
emulator.call_input( 0 );
Log::debug << "currentVelocity(0.0)\n";
emulator.get_input( 1 ).init( 0.0 );
emulator.call_input( 1 );
Log::debug << "set_x(0.01)\n";
emulator.get_input( 2 ).init( 0.01 );
emulator.call_input( 2 );
Log::debug << "set_y(0.01)\n";
emulator.get_input( 3 ).init( 0.01 );
emulator.call_input( 3 );
Log::debug << "set_compass(0.0)\n";
emulator.get_input( 4 ).init( 0.0 );
emulator.call_input( 4 );
Log::debug << "currentEngine(0.0)\n";
emulator.get_input( 5 ).init( 0.0 );
emulator.call_input( 5 );
Log::debug << "currentSteering(0.0)\n";
emulator.get_input( 6 ).init( 0.0 );
emulator.call_input( 6 );
Log::debug << "set_currentBrakes(0.0)\n";
emulator.get_input( 7 ).init( 0.0 );
emulator.call_input( 7 );
Log::debug << "set_trajectory_length(5)\n";
emulator.get_input( 8 ).init( 5 );
emulator.call_input( 8 );
double x[6] = {0.01, 0.02, 0.03, 0.04, 0.05, 0.06};
double y[6] = { 0.01, 0.01, 0.02, 0.02, 0.01, 0.01 };
Log::debug << "set_trajectory_x({0.01, 0.02, 0.03, 0.04, 0.05, 0.06}, 6)\n";
emulator.get_input( 9 ).init( 6, x );
emulator.call_input( 9 );
Log::debug << "set_trajectory_y({ 0.01, 0.01, 0.02, 0.02, 0.01, 0.01 }, 6)\n";
emulator.get_input( 10 ).init( 6, y );
emulator.call_input( 10 );
Log::debug << "exec()\n";
emulator.simulation_time = 1000000000L;
emulator.call_void( emulator.exec_address );
/*
{ "engine", VALUE_TYPE::DOUBLE }, //0
{ "steering", VALUE_TYPE::DOUBLE }, //1
{ "brakes", VALUE_TYPE::DOUBLE } //2
*/
Log::debug << "jni_get_engine()=";
emulator.call_output( 0 );
double engine = emulator.get_output( 0 ).double_value;
Log::debug << to_string( engine ) << "\n";
Log::debug << "jni_get_steering()=";
emulator.call_output( 1 );
double steering = emulator.get_output( 1 ).double_value;
Log::debug << to_string( steering ) << "\n";
Log::debug << "jni_get_brakes()=";
emulator.call_output( 2 );
double brakes = emulator.get_output( 2 ).double_value;
Log::debug << to_string( brakes ) << "\n";
return emulator.call_success;
}
bool AutopilotInterface::init() {
//computer.debug.debug = false;
computer.init();
if ( !computer.loaded() )
return false;
const char *file_name = "AutopilotModel.dll";
const char *module_name = "AutopilotAdapter.dll";
os_windows.init( computer );
WindowsCalls::add_windows_calls( computer.sys_calls, os_windows );
Log::debug << "Load DLL AutopilotModel.dll\n";
if ( !os_windows.load_dll( file_name ) )
return false;
emulator.init( computer, module_name );
loaded = true;
return true;
}
bool str_equal( const char *first, uint size, const char *second ) {
for ( uint i : Range( size ) )
if ( second[i] == '\0' || second[i] != first[i] )
return false;
return second[size] == '\0';
}
std::string AutopilotInterface::message( const char *msg ) {
MessageParser parser( msg );
if ( !parser.has_cmd )
return "false";
if ( parser.is_cmd( "inc" ) ) {
slong time_delta;
if ( !parser.get_long( time_delta ) )
return "err=Did not find long parameter";
emulator.add_time( time_delta );
Log::debug << Log::tag << "Time 'inc' msg recieved: " << time_delta << "\n";
return "true";
}
else if ( parser.is_cmd( "get_computer_time" ) )
return "res=" + to_string( computer.computing_time );
else
Log::debug << Log::tag << "Unkown message recieved: " << msg << "\n";
return "err=Unknown Command";
}
MessageParser::MessageParser( const char *msg ) {
this->msg = msg;
pos = 0;
while ( msg[pos] != 0 && msg[pos] != '=' )
++pos;
if ( pos == 0 ) {
this->has_cmd = false;
return;
}
this->has_cmd = true;
this->cmd_size = pos;
this->rest = msg + pos + 1;
}
bool MessageParser::is_cmd( const char *cmd ) {
return str_equal( msg, cmd_size, cmd );
}
bool MessageParser::get_long( slong &target ) {
const char *new_ptr;
ulong val = strtoll( rest, ( char ** )&new_ptr, 10 );
if ( new_ptr == rest )
return false;
target = val;
rest = new_ptr;
to_comma();
if ( *rest != '\0' )
++rest;
return true;
}
void MessageParser::to_non_ws() {
while ( *rest != '\0' && iswspace( *rest ) )
++rest;
}
void MessageParser::to_comma() {
while ( *rest != '\0' && *rest != ',' )
++rest;
}
/**
* (c) https://github.com/MontiCore/monticore
*
* The license generally applicable for this project
* can be found under https://github.com/MontiCore/monticore.
*/
#pragma once
#include "os_windows/os_windows.h"
#include "computer/computer.h"
#include "emulation/hardware_emulator.h"
struct MessageParser {
const char *msg;
uint pos;
uint cmd_size;
const char *rest;
bool has_cmd;
MessageParser( const char *msg );
bool is_cmd( const char *cmd );
bool get_long( slong &target );
void to_non_ws();
void to_comma();
};
struct AutopilotInterface {
static AutopilotInterface instance;
bool loaded;
AutopilotInterface() : loaded( false ) {}
OS::Windows os_windows;
Computer computer;
HardwareEmulator emulator;
bool test_main();
bool init();
std::string message( const char *msg );
};
/**
* (c) https://github.com/MontiCore/monticore
*
* The license generally applicable for this project
* can be found under https://github.com/MontiCore/monticore.
*/
#pragma once
static constexpr int AUTOPILOT_INPUT_COUNT = 11;
static constexpr int AUTOPILOT_OUTPUT_COUNT = 3;
......@@ -6,7 +6,7 @@
*/
#pragma once
#include "utility.h"
#include "utility/utility.h"
#include "memory.h"
#include "instruction_time.h"
......@@ -42,7 +42,7 @@ struct FifoCache : public MemoryAccessInterface {
void init(std::vector<ulong> &data ) {
clear();
this->data = data.data();
this->size = data.size();
this->size = (uint) data.size();
}
void clear() {
count = 0;
......
......@@ -19,9 +19,8 @@ void Computer::init() {
internal->err = uc_open( UC_ARCH_X86, UC_MODE_64, &internal->uc );
if ( internal->err != UC_ERR_OK ) {
Log::err << Log::tag << "Failed on uc_open(), returned error: " << internal->err << "\n";
internal->uc = nullptr;
return;
throw_error(Error::hardware_emu_init_error(std::string("uc_open(): ") + unicorn_error()));
}
......@@ -44,8 +43,6 @@ void Computer::init() {
uc_hook_add( internal->uc, &internal->trace3, UC_HOOK_MEM_INVALID, ( void * )Computer::hook_mem_err, this, 1, 0 );
exit_code_addr = sys_calls.add_syscall( SysCall( "exit", "SYSTEM", exit_callback ), "Computer" );
}
void Computer::drop() {
......@@ -58,18 +55,13 @@ void Computer::drop() {
}
bool Computer::call( ulong address, const char *name ) {
void Computer::call( ulong address, const char *name ) {
debug.debug_call( address, name );
//Log::info << name << "()\n";
stopped = false;
stack.push_long( exit_code_addr );
internal->err = uc_emu_start( internal->uc, address, 0xFFFFFFFFFFFFFFFF, 0, 0 );
if ( internal->err ) {
Log::err << Log::tag << "Failed on uc_emu_start() with error returned "
<< internal->err << ": " << uc_strerror( internal->err ) << "\n";
return false;
}
return true;
if (internal->err)
throw_error(std::string("Software emulation error: (call to uc_emu_start() ): \n\t")+ unicorn_error());
}
void Computer::set_os( OS::OS *os ) {
......@@ -124,7 +116,65 @@ bool Computer::exit_callback( Computer &inter ) {
return true;
}
const char* Computer::unicorn_error()
{
return uc_strerror(internal->err);
}
void Computer::exit_emulation() {
uc_emu_stop( internal->uc );
stopped = true;
}
MemoryAccessInterface *setup_cache( Computer &computer, CacheSettings::Cache &cache ) {
auto cache_layer = new FifoCache( computer.memory, cache.size );
cache_layer->block_size = cache.block_size;
cache_layer->set_ticks( computer.time, cache.read_ticks, cache.write_ticks );
return cache_layer;
}
void CacheSettings::handle_config( MessageParser &parser ) {
Cache *cache = nullptr;
if ( parser.is_cmd( "cache_IL1" ) )
cache = &IL1;
else if ( parser.is_cmd( "cache_DL1" ) )
cache = &DL1;
else if ( parser.is_cmd( "cache_L2" ) )
cache = &L2;
else if ( parser.is_cmd( "cache_L3" ) )
cache = &L3;
else {
Log::err << Log::tag << "Unknown cache level\n";
return;
}
slong size;
slong r_ticks;
slong w_ticks;
if ( parser.get_long( size ) ) {
if ( size == 0 )
*cache = Cache();
else {
if ( parser.get_long( r_ticks ) && parser.get_long( w_ticks ) )
*cache = Cache( ( uint ) r_ticks, ( uint )w_ticks, ( uint )size );
else
Log::err << Log::tag << "Could not read tick parameters of cache config\n";
}
}
else
Log::err << Log::tag << "Could not read size parameter of cache config\n";
}
void CacheSettings::setup_