Commit 5826a3f0 authored by Damien George's avatar Damien George

Initial support for MicroPython

parent e32aae96
......@@ -215,6 +215,8 @@ procedure BuildSupport is
C_Set_Language_To_BlackBox_Device;
when Language_QGenAda => C_Set_Language_To_QGenAda;
when Language_QGenC => C_Set_Language_To_QGenC;
when Language_MicroPython =>
C_Set_Language_To_MicroPython;
when others => Exit_On_Error (True,
"Language is currently not supported: "
& Source_Language'Img);
......
......@@ -162,6 +162,7 @@ package Imported_Routines is
procedure C_Set_Language_To_GUI;
procedure C_Set_Language_To_VHDL;
procedure C_Set_Language_To_System_C;
procedure C_Set_Language_To_MicroPython;
procedure C_Set_Native_Encoding;
procedure C_Set_UPER_Encoding;
procedure C_Set_ACN_Encoding;
......@@ -249,6 +250,8 @@ private
pragma Import (C, C_Set_Language_To_GUI, "Set_Language_To_GUI");
pragma Import (C, C_Set_Language_To_VHDL, "Set_Language_To_VHDL");
pragma Import (C, C_Set_Language_To_System_C, "Set_Language_To_System_C");
pragma Import (C, C_Set_Language_To_MicroPython,
"Set_Language_To_MicroPython");
pragma Import (C, C_Set_UPER_Encoding, "Set_UPER_Encoding");
pragma Import (C, C_Set_ACN_Encoding, "Set_ACN_Encoding");
pragma Import (C, C_Set_Native_Encoding, "Set_Native_Encoding");
......
......@@ -62,6 +62,9 @@ void c_preamble(FV * fv)
else if (rtds == fv->language) {
fprintf(vm_if, "#include \"glue_%s.h\"\n\n", fv->name);
}
else if (micropython == fv->language) {
fprintf(vm_if, "#include \"%s_mpy_bindings.h\"\n\n", fv->name);
}
if (hasparam) {
fprintf(vm_if, "#include \"C_ASN1_Types.h\"\n\n");
......@@ -714,7 +717,8 @@ void GLUE_C_Backend(FV * fv)
if (c == fv->language || gui == fv->language || ada == fv->language || vdm == fv->language
|| qgenada == fv->language || qgenc == fv->language
|| rtds == fv->language || cpp == fv->language || opengeode == fv->language) {
|| rtds == fv->language || cpp == fv->language || opengeode == fv->language
|| micropython == fv->language) {
Init_C_Glue_Backend(fv);
FOREACH(i, Interface, fv->interfaces, {
GLUE_C_ProvidedInterface(i);
......
/* Buildsupport is (c) 2008-2015 European Space Agency
* contact: maxime.perrotin@esa.int
* License is LGPL, check LICENSE file */
/* build_sdl_glue.c
this program generates the code to interface an objectgeode generated application with the assert virtual machine
It creates X_mpy_bindings.h and X_mpy_bindings.c.
*/
#define ID "$Id: build_sdl_glue.c 414 2009-12-04 16:21:52Z maxime1008 $"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "my_types.h"
#include "practical_functions.h"
static FILE *mpy_bind_h, *mpy_bind_c;
static bool gen_this_ri(Interface * i)
{
/*
* There can be duplicate RI name but one sync, the other async
* In that case, discard the async one (generated by VT to allow
* a mix of sync and async PI in one block
*/
if (asynch == i->synchronism) {
FOREACH(interface, Interface, i->parent_fv->interfaces, {
if (RI == interface->direction &&
!strcmp(interface->name, i->name) &&
synch == interface->synchronism) {
return false;
}
});
}
return true;
}
void micropython_mpy_bind_preamble(FV * fv)
{
if (NULL == mpy_bind_h || NULL == mpy_bind_c)
return;
int hasparam = 0;
/* Check if any interface needs ASN.1 types */
FOREACH(i, Interface, fv->interfaces, {
CheckForAsn1Params(i, &hasparam);}
);
fprintf(mpy_bind_h,
"/* This file was generated automatically by build_micropython_glue.c: DO NOT MODIFY IT ! */\n\n");
fprintf(mpy_bind_h,
"/* Declaration of the functions that have to be provided by the user */\n\n");
fprintf(mpy_bind_h,
"#ifndef __USER_CODE_H_%s__\n#define __USER_CODE_H_%s__\n\n",
fv->name, fv->name);
fprintf(mpy_bind_h, "#include <stdint.h>\n\n");
if (hasparam) {
fprintf(mpy_bind_h, "#include \"C_ASN1_Types.h\"\n\n");
}
fprintf(mpy_bind_h,
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n\n");
fprintf(mpy_bind_h, "void %s_startup();\n\n", fv->name);
fprintf(mpy_bind_c, "#include \"py/runtime.h\"\n");
fprintf(mpy_bind_c, "#include \"py/gc.h\"\n");
fprintf(mpy_bind_c, "#include \"py/stackctrl.h\"\n");
fprintf(mpy_bind_c, "#include \"py/pystack.h\"\n");
fprintf(mpy_bind_c, "#include \"mputil.h\"\n");
fprintf(mpy_bind_c, "#include \"%s_mpy_bindings.h\"\n", fv->name);
fprintf(mpy_bind_c, "#include \"%s.mpy.h\"\n\n", fv->name);
if (has_context_param(fv)) {
char *fv_no_underscore =
underscore_to_dash(fv->name, strlen(fv->name));
fprintf(mpy_bind_c,
"/* Function static data is declared in this file : */\n");
fprintf(mpy_bind_c, "#include \"Context-%s.h\"\n\n",
fv_no_underscore);
free(fv_no_underscore);
}
fprintf(mpy_bind_c, "mp_state_ctx_t *mp_current_ctx;\n"); // TODO there should only be one of these per executable
fprintf(mpy_bind_c, "static mp_state_ctx_t mp_ctx;\n");
fprintf(mpy_bind_c, "static uint64_t mp_heap[4096];\n");
fprintf(mpy_bind_c, "static mp_obj_t mp_pystack[4096];\n\n");
FOREACH(i, Interface, fv->interfaces, {
if (i->direction == PI) {
fprintf(mpy_bind_c, "static mp_obj_t mp_global_%s_PI_%s;\n\n", i->parent_fv->name, i->name);
}
});
fprintf(mpy_bind_c, "void %s_startup()\n{\n", fv->name);
fprintf(mpy_bind_c,
" /* MicroPython VM initialisation */\n"
" mp_current_ctx = &mp_ctx;\n"
" mp_stack_ctrl_init();\n"
" mp_stack_set_limit(8192);\n"
" gc_init(mp_heap, (uint8_t*)mp_heap + sizeof(mp_heap));\n"
" mp_pystack_init(mp_pystack, (uint8_t*)mp_pystack + sizeof(mp_pystack));\n"
" mp_init();\n"
" mp_exec_mpy(mpy_script_data, mpy_script_len);\n"
);
fprintf(mpy_bind_c,
" mp_obj_t global_startup = mp_load_global(MP_QSTR_%s_startup);\n", fv->name);
FOREACH(i, Interface, fv->interfaces, {
if (i->direction == PI) {
fprintf(mpy_bind_c,
" mp_global_%s_PI_%s = mp_load_global(MP_QSTR_%s_PI_%s);\n",
i->parent_fv->name, i->name, i->parent_fv->name, i->name);
}
});
fprintf(mpy_bind_c,
" mp_call_function_0(global_startup);\n"
);
fprintf(mpy_bind_c, "}\n\n");
}
void micropython_add_PI_to_glue(Interface * i)
{
if (NULL == mpy_bind_h)
return;
char *signature = make_string("void %s_PI_%s(",
i->parent_fv->name,
i->name);
char *signature_py = make_string("def %s_PI_%s(",
i->parent_fv->name,
i->name);
size_t sig_len = strlen(signature);
char *sep = make_string(",\n%*s", sig_len, "");
char *sep_py = make_string(",\n%*s", strlen(signature_py), "");
size_t n_args = 0;
fprintf(mpy_bind_h, "%s", signature);
fprintf(mpy_bind_c, "%s", signature);
FOREACH (p, Parameter, i->in, {
char *sort = make_string("%sconst asn1Scc%s *",
n_args ? sep: "",
p->type);
fprintf(mpy_bind_h, "%s", sort);
fprintf(mpy_bind_c, "%sIN_%s",
sort,
p->name);
free(sort);
n_args += 1;
});
FOREACH (p, Parameter, i->out, {
char *sort = make_string("%sasn1Scc%s *",
n_args ? sep: "",
p->type);
fprintf(mpy_bind_h, "%s", sort);
fprintf(mpy_bind_c, "%sOUT_%s",
sort,
p->name);
free(sort);
n_args += 1;
});
fprintf(mpy_bind_h, ");\n\n");
fprintf(mpy_bind_c, ")\n{\n");
fprintf(mpy_bind_c, " mp_current_ctx = &mp_ctx;\n");
fprintf(mpy_bind_c, " mp_obj_t args[%u];\n", n_args);
fprintf(mpy_bind_c,
" mp_stack_ctrl_init();\n"
" mp_stack_set_limit(2048);\n"
" nlr_buf_t nlr;\n"
" if (nlr_push(&nlr) == 0) {\n"
);
n_args = 0;
FOREACH (p, Parameter, i->in, {
fprintf(mpy_bind_c,
" args[%u] = mp_obj_new_int(*IN_%s);\n", n_args, p->name);
n_args += 1;
});
fprintf(mpy_bind_c,
" mp_call_function_n_kw(mp_global_%s_PI_%s, %u, 0, args);\n"
" nlr_pop();\n"
" } else {\n"
" mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));\n"
" }\n"
"}\n"
"\n",
i->parent_fv->name, i->name, n_args
);
free(signature);
free(signature_py);
free(sep);
free(sep_py);
}
void micropython_add_RI_to_glue(Interface * i)
{
if (NULL == mpy_bind_h)
return;
char *signature = make_string("extern void %s_RI_%s(",
i->parent_fv->name,
i->name);
size_t sig_len = strlen(signature);
char *sep = make_string(",\n%*s", sig_len, "");
size_t n_args = 0;
fprintf(mpy_bind_c, "%s", signature);
FOREACH (p, Parameter, i->in, {
fprintf(mpy_bind_c, "%sconst asn1Scc%s *",
n_args ? sep: "",
p->type);
n_args += 1;
});
FOREACH (p, Parameter, i->out, {
fprintf(mpy_bind_c,"%sasn1Scc%s *",
n_args ? sep: "",
p->type);
n_args += 1;
});
fprintf(mpy_bind_c, ");\n\n");
free(signature);
free(sep);
fprintf(mpy_bind_c,
"STATIC mp_obj_t %s_RI_%s_wrap(size_t n_args, const mp_obj_t *args)\n"
"{\n",
i->parent_fv->name, i->name
);
n_args = 0;
FOREACH (p, Parameter, i->in, {
fprintf(mpy_bind_c,
" asn1Scc%s asn_IN_%s = mp_obj_get_int(args[%u]);\n",
p->type, p->name, n_args);
n_args += 1;
});
n_args = 0;
FOREACH (p, Parameter, i->out, {
fprintf(mpy_bind_c,
" asn1Scc%s asn_OUT_%s;\n",
p->type, p->name);
n_args += 1;
});
fprintf(mpy_bind_c, " %s_RI_%s(", i->parent_fv->name, i->name);
n_args = 0;
FOREACH (p, Parameter, i->in, {
fprintf(mpy_bind_c, "%s&asn_IN_%s", n_args ? ", " : "", p->name);
n_args += 1;
});
FOREACH (p, Parameter, i->out, {
fprintf(mpy_bind_c, "%s&asn_OUT_%s", n_args ? ", " : "", p->name);
n_args += 1;
});
fprintf(mpy_bind_c, ");\n");
fprintf(mpy_bind_c,
" return mp_const_none;\n"
"}\n"
"STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(%s_RI_%s_obj, %u, %u, %s_RI_%s_wrap);\n"
"\n",
i->parent_fv->name, i->name,
n_args, n_args,
i->parent_fv->name, i->name
);
}
int Init_MicroPython_Glue_Backend(FV * fv)
{
char *path = NULL, *filename = NULL;
if (NULL != fv->system_ast->context->output)
build_string(&path, fv->system_ast->context->output,
strlen(fv->system_ast->context->output));
build_string(&path, fv->name, strlen(fv->name));
build_string(&filename, fv->name, strlen(fv->name));
build_string(&filename, "_mpy_bindings.c", strlen("_mpy_bindings.c"));
create_file(path, filename, &mpy_bind_c);
filename[strlen(filename) - 1] = 'h';
create_file(path, filename, &mpy_bind_h);
free(path);
free(filename);
if (NULL == mpy_bind_c || NULL == mpy_bind_h)
return -1;
micropython_mpy_bind_preamble(fv);
return 0;
}
void End_MicroPython_Glue_Backend(FV *fv)
{
// Generate the taste module
fprintf(mpy_bind_c,
"STATIC const mp_rom_map_elem_t taste_module_globals_table[] = {\n"
" { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_taste) },\n"
);
FOREACH(i, Interface, fv->interfaces, {
if (i->direction == RI && gen_this_ri(i)) {
fprintf(mpy_bind_c,
" { MP_ROM_QSTR(MP_QSTR_%s_RI_%s), MP_ROM_PTR(&%s_RI_%s_obj) },\n",
i->parent_fv->name, i->name,
i->parent_fv->name, i->name
);
}
});
fprintf(mpy_bind_c,
"};\n"
"\n"
"STATIC MP_DEFINE_CONST_DICT(taste_module_globals, taste_module_globals_table);\n"
"\n"
"const mp_obj_module_t mp_module_taste = {\n"
" .base = { &mp_type_module },\n"
" .globals = (mp_obj_dict_t*)&taste_module_globals,\n"
"};\n"
);
if (NULL != mpy_bind_h) {
fprintf(mpy_bind_h,
"#ifdef __cplusplus\n"
"}\n"
"#endif\n\n");
fprintf(mpy_bind_h, "\n#endif\n");
close_file(&mpy_bind_h);
}
close_file(&mpy_bind_c);
}
/* Function to process one interface of the FV */
void GLUE_MicroPython_Interface(Interface * i)
{
switch (i->direction) {
case PI:
micropython_add_PI_to_glue(i);
break;
case RI:
if (gen_this_ri(i)) {
micropython_add_RI_to_glue(i);
}
break;
default:
break;
}
}
// External interface (the one and unique)
void GLUE_MicroPython_Backend(FV * fv)
{
if (fv->system_ast->context->onlycv)
return;
if (micropython == fv->language) {
Init_MicroPython_Glue_Backend(fv);
FOREACH(i, Interface, fv->interfaces, {
GLUE_MicroPython_Interface(i);
})
End_MicroPython_Glue_Backend(fv);
}
}
/* Buildsupport is (c) 2008-2016 European Space Agency
* contact: maxime.perrotin@esa.int
* License is LGPL, check LICENSE file */
/* build_c_skeletons.c
this program generates empty C functions respecting the interfaces defined
in the interface view. it provides functions to invoke RI.
updated 10/06/2016: properly align params and use FOREACH
updated 20/04/2009 to disable in case "-onlycv" flag is set
updated 8/10/2009 to rename the user_code.h/c to FV_name.h/c like in the Ada backend
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <assert.h>
#include "my_types.h"
#include "practical_functions.h"
static FILE *user_code_py = NULL;
static bool gen_this_ri(Interface * i)
{
/*
* There can be duplicate RI name but one sync, the other async
* In that case, discard the async one (generated by VT to allow
* a mix of sync and async PI in one block
*/
if (asynch == i->synchronism) {
FOREACH(interface, Interface, i->parent_fv->interfaces, {
if (RI == interface->direction &&
!strcmp(interface->name, i->name) &&
synch == interface->synchronism) {
return false;
}
});
}
return true;
}
/* Import the RI into the module-level namespace of the Python script */
void import_RI_to_MicroPython_skel(Interface * i)
{
if (NULL == user_code_py)
return;
bool comma = false;
fprintf(user_code_py, "# %s_RI_%s(", i->parent_fv->name, i->name);
FOREACH (p, Parameter, i->in, {
fprintf(user_code_py, "%sIN_%s: asn1Scc%s",
comma ? "," : "", p->name, p->type);
comma = true;
});
FOREACH (p, Parameter, i->out, {
fprintf(user_code_py,"%sOUT_%s: asn1Scc%s",
comma ? "," : "", p->name, p->type);
comma = true;
});
fprintf(user_code_py, ")\n");
fprintf(user_code_py, "from taste import %s_RI_%s\n", i->parent_fv->name, i->name);
}
/* Generate the preamble of the skeleton script */
void micropython_skel_preamble(FV * fv)
{
int hasparam = 0;
/* Check if any interface needs ASN.1 types */
FOREACH(i, Interface, fv->interfaces, {
CheckForAsn1Params(i, &hasparam);}
);
/* c. user_code.py preamble (if applicable) */
if (NULL != user_code_py) {
fprintf(user_code_py,
"# User code: This file will not be overwritten by TASTE.\n\n");
fprintf(user_code_py, "import micropython\n\n");
FOREACH(i, Interface, fv->interfaces, {
if (i->direction == RI && gen_this_ri(i)) {
import_RI_to_MicroPython_skel(i);
}
});
fprintf(user_code_py, "\n");
// TODO need to generate dummy variables for the types
if (has_context_param(fv)) {
char *fv_no_underscore =
underscore_to_dash(fv->name, strlen(fv->name));
fprintf(user_code_py,
"/* Function static data is declared in this file : */\n");
fprintf(user_code_py, "#include \"Context-%s.h\"\n\n",
fv_no_underscore);
free(fv_no_underscore);
}
fprintf(user_code_py, "def %s_startup():\n", fv->name);
fprintf(user_code_py,
" # Write your initialization code here,\n"
" # but do not make any call to a required interface.\n"
" pass\n"
"\n\n");
}
}
/* Creates user_code.h, and if necessary user_code.c (if it did not exist) */
int Init_MicroPython_GW_Backend(FV * fv)
{
char *path = NULL;
char *filename = NULL;
if (NULL != fv->system_ast->context->output) {
build_string(&path, fv->system_ast->context->output,
strlen(fv->system_ast->context->output));
}
build_string(&path, fv->name, strlen(fv->name));
build_string(&filename, fv->name, strlen(fv->name));
build_string(&filename, ".h", strlen(".h"));
filename[strlen(filename) - 1] = 'p'; /* change from .c to .p */
build_string(&filename, "y", 1); /* change from .p to .py */
if (!file_exists(path, filename)) {
printf("MicroPython: creating fresh skeleton %s/%s\n", path, filename);
create_file(path, filename, &user_code_py);
}
free(path);
micropython_skel_preamble(fv);
return 0;
}
void close_micropython_skel_files()
{
close_file(&user_code_py);
}
/* Add a Provided interface. Can contain in and out parameters
* Write in user_code.h the declaration of the user functions
* and if user_code.c is new, copy these declarations there too.
*/
void add_PI_to_MicroPython_skel(Interface * i)
{
if (NULL == user_code_py)
return;
char *signature_py = make_string("def %s_PI_%s(",
i->parent_fv->name,
i->name);
char *sep_py = make_string(",\n%*s", strlen(signature_py), "");
bool comma = false;
fprintf(user_code_py, "%s", signature_py);
#if 0
// This code generates type hints for the parameters, but need to have the identifiers defined as globals first...
FOREACH (p, Parameter, i->in, {
fprintf(user_code_py, "%sIN_%s : asn1Scc%s",
comma ? sep_py : "", p->name, p->type);
comma = true;
});
FOREACH (p, Parameter, i->out, {
fprintf(user_code_py, "%sOUT_%s : asn1Scc%s",
comma ? sep_py : "", p->name, p->type);
comma = true;
});
#else
// This code generates the parameter list without type hints.
FOREACH (p, Parameter, i->in, {
fprintf(user_code_py, "%sIN_%s", comma ? sep_py : "", p->name);
comma = true;
});
FOREACH (p, Parameter, i->out, {
fprintf(user_code_py, "%sOUT_%s", comma ? sep_py : "", p->name);
comma = true;
});
#endif
fprintf(user_code_py, "):\n # Write your code here!\n pass\n\n");
free(signature_py);
free(sep_py);
}
/* Add timer declarations to the C code skeletons */
void MicroPython_Add_timers(FV *fv)
{