summaryrefslogtreecommitdiff
path: root/peripheral/keystore/chaabi/libcc54/utils/src/sep_rpc_gen/sep_rpc_gen.py
diff options
context:
space:
mode:
Diffstat (limited to 'peripheral/keystore/chaabi/libcc54/utils/src/sep_rpc_gen/sep_rpc_gen.py')
-rw-r--r--peripheral/keystore/chaabi/libcc54/utils/src/sep_rpc_gen/sep_rpc_gen.py871
1 files changed, 871 insertions, 0 deletions
diff --git a/peripheral/keystore/chaabi/libcc54/utils/src/sep_rpc_gen/sep_rpc_gen.py b/peripheral/keystore/chaabi/libcc54/utils/src/sep_rpc_gen/sep_rpc_gen.py
new file mode 100644
index 0000000..ced3d5f
--- /dev/null
+++ b/peripheral/keystore/chaabi/libcc54/utils/src/sep_rpc_gen/sep_rpc_gen.py
@@ -0,0 +1,871 @@
+#!/usr/bin/python3
+
+# (c) Copyright 2011-2012 Discretix Technologies Ltd.
+# This file is licensed under the terms provided in the file
+# libcc54/LICENSE in this directory or a parent directory
+
+# Generator for SeP RPC wrappers
+
+# Flow overview
+# 0. Get command line arguments
+# 1. Parse XML file into tree object
+# 2. Scan XML tree object: foreach function: save function return code + for each parameter: Add attributes to input, output or i/o parameres list
+# 3. H file generation:
+# - File preamble: (Generated file + license, etc.)
+# - Define API ID constant macro
+# - Foreach function define index
+# - For each function generate message structure:
+# 1. Common public part
+# 2. DMA references
+# 3. input parameters
+# 4. i/o parameters
+# 5. output parameters
+# 6. function return code
+#
+# 4. Host source file generation:
+# - File preamble
+# - Include <header> elements of API
+# - Include API header file
+# - For each function generate code:
+# 1. Prototype based on function parameters definition
+# 2. Allocate local buffer for the parameters structure
+# 3. For input/inout parameters: Check value range is given and copy data into parameters struct
+# 4. For user buf parameters: Fill memRef table and numOfMemRef
+# 5. Invoke DxDI_SepRpcCall()
+# 7. For each out "ref" parameter, copy back from message into the respective user parameter.
+# 8. Save return code from message
+# 9. Free message buffer
+# 10. Return the function return code.
+#
+# 5. SeP source file generation:
+# - File preamble
+# - Include <header> elements of API
+# - Include API header file
+# - For each function generate parsing function to invoke real function with parameters from params struct
+# - SeP Agent entry point (hun) function to switch on FuncId and invoke respective function
+# - SeP Agent (ID) init function
+# Register agent with RPC infrastructure.
+
+import sys
+import getopt
+import xml.etree.ElementTree as ET
+import re
+import string
+from datetime import datetime
+from datetime import date
+
+def usage ():
+ print("Usage: " + sys.argv[0] + " --spec=<API spec/XML file> --o-api-h=<API header file path> [--o-host-c=<Host C code file path>] [--o-sep-c=<SeP C code file path>]")
+
+# Parse the command line arguments and return the file names (or None for undefined filename)
+# Return: spec_file, api_h_file, host_c_file, sep_c_file
+def parse_cmdline_arguments():
+ spec_file, api_h_path, host_c_path, sep_c_path = None, None, None, None
+
+ args_spec = [ 'spec=' , 'o-api-h=' , 'o-host-c=' , 'o-sep-c=' ]
+ try:
+ opts, extra_args = getopt.getopt(sys.argv[1:], "", args_spec)
+ except getopt.GetoptError as err:
+ # print help information and exit:
+ print(str(err)) # will print something like "option -a not recognized"
+ usage()
+ sys.exit(2)
+
+ if len(extra_args) > 0:
+ print("Unknown argument:" , extra_args[0])
+ usage()
+ sys.exit(3)
+
+ for opt, arg in opts:
+ if opt == "--spec":
+ spec_file = arg
+ elif opt == "--o-api-h":
+ api_h_path = arg
+ elif opt == "--o-host-c":
+ host_c_path = arg
+ elif opt == "--o-sep-c":
+ sep_c_path = arg
+ else:
+ assert False, "unhandled option"
+
+ if spec_file is None:
+ print("API spec. file name is missing")
+ usage()
+ sys.exit(4)
+ if api_h_path is None:
+ print("API RPC H file path is missing")
+ usage()
+ sys.exit(4)
+
+ return spec_file, api_h_path, host_c_path, sep_c_path
+
+
+# Use sep_rpc_agents.def to look up for this agent ID based on his name
+def api_name_to_id(agent_name):
+ agent_id = None # default
+ # Agents definitions file is located in the same directory as this script
+ agents_defs_file_path = re.sub(r"/sep_rpc_gen\.py", "/sep_rpc_agents.def", sys.argv[0])
+ agents_defs_file = open(agents_defs_file_path, 'r')
+ comment_line_parser = re.compile(r"^\s*#.*$")
+ agent_def_parser = re.compile(r"^\s*(\d+)\s*,\s*(\w+)\s*,\s*(\S+)\s*$")
+ while True: # Read all defs file lines
+ cur_line = agents_defs_file.readline()
+ if len(cur_line) == 0:
+ break # end of file - not found
+ comment_line = comment_line_parser.match(cur_line)
+ if comment_line is not None:
+ continue # ignore comment
+ agent_def = agent_def_parser.match(cur_line)
+ if agent_def is None:
+ print("Failed parsing:", cur_line)
+ sys.exit(1)
+ cur_agent_id = agent_def.group(1)
+ cur_agent_name = agent_def.group(2)
+ if cur_agent_name == agent_name:
+ print(cur_line)
+ agent_id = cur_agent_id
+ break # found
+ agents_defs_file.close()
+ return agent_id
+
+
+
+# Preprocess elements tree into data strcutures that are easier to process
+def parse_api_spec (spec_file):
+ print("Parsing API spec file" , spec_file , "...")
+ api_tree = ET.parse(spec_file);
+ #ET.dump(api_tree) # Debug only
+
+ api_root = api_tree.getroot();
+ if api_root is None:
+ print("Failed finding root element")
+ sys.exit(5)
+
+ api_data = {"name" : api_root.get("name")}
+ if api_data["name"] is None:
+ print("Failed to get API name")
+ sys.exit(6)
+
+ api_data["id"] = api_name_to_id(api_data["name"])
+ if api_data["id"] is None:
+ print("Failed to get SeP RPC Agents Id")
+ sys.exit(7)
+
+ api_data["retcodes"] = api_root.find("retcodes")
+ if api_data["retcodes"] is None:
+ print("Failed to get default return codes")
+ sys.exit(8)
+
+ # headers
+ api_data["headers"] = []
+ for header in api_root.findall("header") :
+ api_data["headers"].append(header.text)
+ api_data["headers"].append("sep_rpc.h")
+
+ api_data["functions"] = []
+ for function in api_root.findall("function") :
+ func_data = { "name" : function.get("name") }
+ func_data["return"] = function.get("return")
+ func_data["retcodes"] = function.find("retcodes") # Optional return codes to override API default <retcodes>
+ if func_data["retcodes"] is None: # Use default return codes if not function-specific
+ func_data["retcodes"] = api_data["retcodes"]
+ func_data["params_buf"] = []
+ func_data["params_in"] = []
+ func_data["params_inout"] = []
+ func_data["params_out"] = []
+ func_data["params"] = [] # Simple list of all the parameters as in prototype
+ # Scan all parameters and group by direction
+ for param in function.findall("param"):
+ func_data["params"].append(param)
+ param_dir = param.get("direction")
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ if param_type == "buf":
+ func_data["params_buf"].append(param)
+ elif param_dir == "i":
+ func_data["params_in"].append(param)
+ elif param_dir == "io":
+ func_data["params_inout"].append(param)
+ elif param_dir == "o":
+ func_data["params_out"].append(param)
+ else:
+ print("Invalid parameter direction=" , param_dir , "for parameter" , param.get("name"))
+ sys.exit(8)
+ api_data["functions"].append(func_data)
+
+ # Summary
+ print("API" , api_data["name"] , "(ID =" , api_data["id"] , "):" , len(api_data["headers"]) , "headers," , len(api_data["functions"]) , "functions")
+
+ return api_data
+
+
+# Generate file preamble (license, etc.) common to all source files
+def gen_file_preamble (out_file):
+ license_str = re.sub(r"<year>", str(date.today().year),
+"""/*******************************************************************
+* (c) Copyright 2011-2012 Discretix Technologies Ltd. *
+* This file is licensed under the terms provided in the file *
+* libcc54/LICENSE in this directory or a parent directory *
+********************************************************************/\n""")
+
+ # Fetch script revision as given from SVN
+ gen_rev = re.sub(r"\$Rev:\s*([0-9]+)\s*\$", r"\1", r"$Rev: 2451 $")
+
+ out_file.write("/* Generated file - DO NOT EDIT! */\n")
+ out_file.write("/* Generated by " + sys.argv[0] + "@" + gen_rev + " at " + str(datetime.now()) + " */\n")
+ out_file.write(license_str)
+
+## Parse the ptype attribute
+# \param ptype_val The value of "ptype" attribute
+#
+# \return (type, array_size, array_size_max)
+def parse_ptype (ptype_val):
+ data_size = None
+ data_size_max = None
+
+ ptype_components = re.match(r"(val|ref|array|buf|wsbuf)(\(.+\))?", ptype_val)
+ try:
+ param_type = ptype_components.group(1)
+ except:
+ print("Invalid parameter type from:" , ptype_val)
+ sys.exit(9)
+ if param_type == "array":
+ array_components = re.match(r"\(([^,]+),([^,]+)\)", ptype_components.group(2))
+ data_size = array_components.group(1)
+ data_size_max = array_components.group(2)
+ elif param_type == "buf" or param_type == "wsbuf":
+ buf_components = re.match(r"\(([^,]+)\)", ptype_components.group(2))
+ data_size = buf_components.group(1)
+
+ return param_type, data_size, data_size_max
+
+
+## Parse "dtype" attribute
+# \param dtype_val the "dtype" attribute value
+# \return (c_type, endian_type)
+def parse_dtype (dtype_val):
+ dtype_components = re.match(r"((le|be)(16|32))\(([\w\s]+)\)", dtype_val)
+ if dtype_components is None: # No endian data
+ endian_type = None
+ c_type = dtype_val
+ else: # Has endian data
+ endian_type = dtype_components.group(1)
+ c_type = dtype_components.group(4)
+
+ return c_type, endian_type
+
+## Generate the field of a params struct
+# Used by gen_params_struct_fields
+# field_prefix may be used (i.e., not "") to denote hierarchy of field inside a structure
+def gen_a_param_struct_field (indent_level, field_prefix, param):
+ indent_str = "\t" * indent_level
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ c_type , endian_type = parse_dtype(param.get("dtype"))
+ field_name = param.get("name")
+ # Workspace buffer has no param to pass
+ if param_type == "wsbuf":
+ field_name += "_size"
+ c_type = "DxUint32_t "
+ if param_type == "ref" and field_prefix == "": field_prefix = "__" # Copied referenced data is denoted with "__" prefix
+ elif param_type == "array": field_name += "[" + array_size_max + "]"
+ endian_comment = ("/*" + endian_type + "*/" if endian_type != "" else "") if endian_type is not None else ""
+ alignment = param.get("align")
+ if alignment is not None:
+ alignment_tag = " DX_PAL_COMPILER_ALIGN(" + alignment + ")"
+ else:
+ alignment_tag = ""
+
+ if param_type == "ref" or param_type == "array":
+ flag_str = indent_str + "DxUint8_t " + field_prefix + param.get("name") + "_null_flag;\n"
+ else:
+ flag_str = ""
+ return indent_str + endian_comment + c_type + " " + field_prefix + field_name + alignment_tag + ";\n" + flag_str
+
+# Generate code for parameters structure for given parameters list
+# \return struct string
+def gen_params_struct_fields (indent_level, field_prefix, params_list):
+ return_str = ""
+ indent_str = "\t" * indent_level
+
+ for param in params_list:
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ return_str += gen_a_param_struct_field(1, field_prefix, param)
+
+ # Handle second level reference from a struct
+ # TODO: Handle unlimited reference levels, i.e., structure references within structures
+ fields = param.findall("field")
+ for field in fields: # Create place holders for struct fields which are by reference
+ if field.get("ptype") == "ref":
+ return_str += gen_a_param_struct_field(2,
+ ("__" if param_type == "ref" else "") + param.get("name") + "__", field)
+ return return_str
+
+
+# Generate a string with the given function prototype
+def gen_func_prototype (func_name, return_type, the_params):
+ proto_str = return_type + " " + func_name + "(\n"
+ for param in the_params:
+ c_type , endian_type = parse_dtype(param.get("dtype"))
+ proto_str += "\t" + c_type + " "
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ if param_type == "buf" or param_type == "ref" or param_type == "wsbuf":
+ proto_str += "*"
+ proto_str += param.get("name")
+ if param_type == "array":
+ proto_str += "[]"
+ if param is not the_params[-1]: # Is not last parameter?
+ proto_str += ",\n"
+ proto_str += ")"
+ return proto_str
+
+
+# Generate the RPC header file of the API
+def gen_h_file (api_data, base_name, h_file_path):
+ api_h_fname = base_name + "_seprpc.h"
+ print("Generating API header file at" , h_file_path + "/" + api_h_fname , "...")
+ api_file = open(h_file_path + "/" + api_h_fname, 'w')
+
+ ####### File start comments ###########
+ gen_file_preamble(api_file)
+
+
+ api_file.write("/* \\file " + api_h_fname + "\n")
+ api_file.write(" * SeP-RPC header file for " + api_data["name"] + " API */\n\n")
+ # Guards
+ guard_tag = "__" + base_name.upper() + "_SEPRPC_H__"
+ api_file.write("#ifndef " + guard_tag + "\n")
+ api_file.write("#define " + guard_tag + "\n\n")
+
+ # SRA_ID
+ api_file.write("#define SEPRPC_AGENT_ID_"+ api_data["name"] + " " + api_data["id"] + "\n\n");
+
+ # includes
+ for header in api_data["headers"] :
+ api_file.write("#include \"" + header + "\"\n")
+ api_file.write("#include \"dx_pal_compiler.h\"\n")
+
+ function_cnt = 0
+ # Per function: function index + params structure
+ for function in api_data["functions"] :
+ function_cnt += 1
+ func_name = function["name"]
+ ret_type , endian_type = parse_dtype(function["return"])
+ api_file.write("\n\n/************ " + func_name + " ***************/\n");
+ api_file.write("#define SEPRPC_FUNC_ID_" + func_name + " " + str(function_cnt) + "\n")
+ api_file.write("/*\n" + gen_func_prototype(func_name, ret_type, function["params"]) + "\n*/\n\n")
+
+ params_buf = function["params_buf"]
+ # Add a constant for number of user buffer references
+ api_file.write("#define SEPRPC_MEMREF_NUM_" + func_name + " " + str(len(params_buf)) + "\n")
+ # Compile time test for the memory references limit
+ api_file.write("#if SEPRPC_MEMREF_NUM_" + func_name + " > SEP_RPC_MAX_MEMREF_PER_FUNC\n")
+ api_file.write("#error SEPRPC_MEMREF_NUM_" + func_name + " is more than SEP_RPC_MAX_MEMREF_PER_FUNC\n")
+ api_file.write("#endif\n")
+ # Index of each buffer reference in the memRef array
+ memref_idx = 0
+ for param in params_buf :
+ api_file.write("#define SEPRPC_MEMREF_IDX_" + param.get("name") + " " + str(memref_idx) + "\n")
+ memref_idx += 1
+ api_file.write("\n")
+
+ # Create the parameters structure ("Message")
+ api_file.write("typedef struct SepRpc_" + func_name + "Params {\n")
+
+ # User buffers first
+ api_file.write("\tuint32_t num_of_memrefs; /* Number of elements in the memRef array */\n")
+ if len(params_buf) > 0: # memRef array exists only if numOfMemRef > 0
+ api_file.write("\tstruct seprpc_memref memref[SEPRPC_MEMREF_NUM_" + func_name + "];\n")
+
+ if len(function["params_in"]) > 0:
+ api_file.write("\t/* Input */\n")
+ api_file.write(gen_params_struct_fields(1, "", function["params_in"]))
+ if len(function["params_inout"]) > 0:
+ api_file.write("\t/* Input/Output */\n")
+ api_file.write(gen_params_struct_fields(1, "", function["params_inout"]))
+ if len(function["params_out"]) > 0:
+ api_file.write("\t/* Output */\n")
+ api_file.write(gen_params_struct_fields(1, "", function["params_out"]))
+ if function["return"] != "void":
+ ret_type , endian_type = parse_dtype(function["return"])
+ api_file.write("\t" + ret_type + " _funcRetCode;\n")
+
+ api_file.write("} SepRpc_" + func_name + "Params_s;\n\n")
+ # End "foreach function"
+
+ # Close guards
+ api_file.write("\n#endif /*" + guard_tag + "*/\n")
+
+ api_file.close()
+
+
+## Generate range validation code for given parameter
+# \param indent_str The base indentation string
+# \param param_val The parameter reference string
+# \param param_range The paramter "range" attribute
+# \param inval_param_retcode The return code to use in case of validation failure
+#
+# \return (A string of the generated code , iterator depth = 0 if no iterator or indent_depth if use iterator)
+def gen_a_param_verify_code (indent_depth, ref_prefix, param, inval_param_retcode):
+ iterator = "i" + str(indent_depth)
+ iter_depth = 0 # 0 until uses an iterator or gets a value from recursive invocation
+ verify_code = ""
+
+ param_range = param.get("range")
+ if param_range is not None:
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ param_val = ref_prefix + param.get("name")
+ if param_type == "ref": param_val = "*(" + param_val + ")"
+
+
+ if param_type == "array" : # Loop on array elements
+ verify_code += "\t" * indent_depth + "for (" + iterator + " = 0; " + iterator + " < (" + array_size + "); " + iterator + "++) {\n"
+ indent_depth += 1
+ param_val += "[" + iterator + "]"
+ iter_depth = indent_depth
+
+ # Generate boolean expression for given range limitation
+ range_components = re.search(r"([\(\[])(\w*),(\w*)([\)\]])", param_range)
+ #print(range_components.group(1), range_components.group(2), range_components.group(3), range_components.group(4))
+ min_val = range_components.group(2)
+ min_eq = "=" if range_components.group(1) == "[" else ""
+ max_val = range_components.group(3)
+ max_eq = "=" if range_components.group(4) == "]" else ""
+ range_bool_expr = ""
+ if min_val != "":
+ range_bool_expr = "(" + param_val + " >" + min_eq + " " + min_val + ")"
+ if max_val != "":
+ range_bool_expr = range_bool_expr + " && (" + param_val + " <" + max_eq + " " + max_val + ")"
+
+ if range_bool_expr != "":
+ verify_code += "\t" * indent_depth + "SEP_RPC_ASSERT(" + range_bool_expr + ", " + inval_param_retcode + "); /* " + param_range + " */\n"
+
+ if param_type == "array" :
+ indent_depth -= 1
+ verify_code += "\t" * indent_depth + "}\n"
+
+
+ return verify_code , iter_depth
+
+## Get the reference expression of given parameter in the params structure
+# \return The reference expression to append to "params." or "params->"
+def get_params_struct_ref (ref_prefix, param_name, param_type):
+ struct_ref_prefix = re.sub(r"(\w+)(->)", r"__\1", ref_prefix)
+ if param_type == "ref":
+ return struct_ref_prefix + "__" + param_name
+ # Not ref - direct access
+ return struct_ref_prefix + ("." if struct_ref_prefix != "" else "") + param_name
+
+# Generate code for copying data into the parameters structure
+#
+# \param indent_depth Indentation depth
+# \param param_ref Parameter reference string (e.g., structure reference)
+# \param dtype_val The value of the "dtype" attribute of the parameter item
+# \param direction The copy direction. "in" for input parameters into the params structure, and "out" for vice-versa.
+#
+# \return (The copy code , iterator depth = 0 if no iterator or indent_depth if use iterator)
+def gen_a_param_copy_code(indent_depth, ref_prefix, param, direction):
+ iterator = "i" + str(indent_depth - 1)
+ return_str = ""
+ iter_depth = 0 # 0 until uses an iterator or gets a value from recursive invocation
+
+ c_type , endian_type = parse_dtype(param.get("dtype"))
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+
+ # Construct the params structure reference matching a given parameter
+ struct_param_value = "params." + get_params_struct_ref(ref_prefix, param.get("name"), param_type)
+ func_param_value = ("*(" if param_type == "ref" else "") + ref_prefix + param.get("name") + (")" if param_type == "ref" else "")
+ if param_type == "array" and endian_type is not None : # Endianess fix requires looping over array items
+ struct_param_value += "[" + iterator + "]"
+ func_param_value += "[" + iterator + "]"
+
+ # Calculate lvalue and rvalue
+ if direction == "in":
+ lvalue = struct_param_value
+ if endian_type is None:
+ rvalue = func_param_value
+ else:
+ rvalue = "cpu_to_" + endian_type + "(" + func_param_value + ")"
+
+ else: # "out"
+ lvalue = func_param_value
+ if endian_type is None:
+ rvalue = struct_param_value
+ else:
+ rvalue = endian_type + "_to_cpu(" + struct_param_value + ")"
+
+ # Generate code
+ if param_type == "array" :
+ if endian_type is None: # Use memcpy
+ return_str += "\t" * indent_depth + "memcpy(" + lvalue + ", " + rvalue + ", sizeof(" + c_type + ") * (" + array_size + "));\n"
+ else: # Loop on array elements in order to switch endianess of each
+ return_str += "\t" * indent_depth + "for (" + iterator + " = 0; " + iterator + " < (" + array_size + "); " + iterator + "++) {\n\t"
+ indent_depth += 1
+ return_str += "\t" * indent_depth + lvalue + " = " + rvalue + ";\n" # lvalue and rvalue already include the array index reference
+ indent_depth -= 1
+ iter_depth = indent_depth;
+ return_str += "\t" * indent_depth + "}\n"
+ elif param_type == "wsbuf":
+ return_str += "\t" * indent_depth + "params." + param.get("name") + "_size = cpu_to_le32((" + array_size + "));\n"
+ else: # plain assignment
+ return_str += "\t" * indent_depth + lvalue + " = " + rvalue + ";\n"
+
+ return return_str , iter_depth
+
+
+## Generate code for parameters sizes and ranges validation in host wrapper code
+#
+# \param base_level indicates we are in the root of this function (no recursion)
+# \param direction The direction ("in" or "out") of the parameters for this wrapper code
+# \param ref_prefix A prefix string for structure fields (structure references)
+# \param indent_depth The indentation depth (for array iterators)
+# \param params_list A list of parameters/fields elements to validate (if have "range" attribute)
+# \param inval_param_retcode The return code to use in case of validation failure
+#
+# \return A string of the generated code
+def gen_params_code (base_level, direction, ref_prefix, indent_depth, params_list, inval_param_retcode):
+ return_str = ""
+ code_indent = "\t" * indent_depth
+ iterator = "i" + str(indent_depth)
+ max_iter_depth = 0
+
+ for param in params_list:
+ param_name = param.get("name")
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ fields = param.findall("field")
+ is_a_struct = (len(fields) > 0)
+ iter_depth = 0
+
+ #param_full_name = ("__" if param_type == "ref" else "") + ref_prefix + param.get("name")
+ param_full_name = get_params_struct_ref(ref_prefix,param.get("name"),param_type)
+ if is_a_struct: # A struct with fields that needs to be verified
+ # Invoke this function in recursion
+ if (base_level == 1 and param_type == "array") or param_type == "ref":
+ return_str += code_indent + "if (" + param.get("name") + " == NULL) {\n" + "\t\tparams." + param_full_name + "_null_flag = 1;\n" + "\t} else {\n"
+
+ params_code , iter_depth = gen_params_code(0, direction,
+ ref_prefix + param_name + ("." if param_type == "val" else "->"),
+ indent_depth + (2 if param_type == "array" else 0) + (1 if param_type == "ref" else 0),
+ fields, inval_param_retcode)
+ else: # Not a struct
+ params_code = ""
+ param_direction = param.get("direction")
+
+ if (base_level == 1 and param_type == "array") or param_type == "ref":
+ struct_name = ref_prefix + param.get("name")
+ return_str += code_indent + "if (" + struct_name + " == NULL) {\n"
+ return_str += code_indent + "\tparams." + param_full_name + "_null_flag = 1;\n"
+ return_str += code_indent + "} else {\n"
+ indent_depth += 1
+ code_indent = "\t" * indent_depth
+
+ # Handle input parameters in case of "in" direction
+ if direction == "in" and (param_direction == "i" or param_direction == "io"):
+ # Only input parameters are validated for range
+ verify_code , verify_iter_depth = gen_a_param_verify_code(indent_depth, ref_prefix, param, inval_param_retcode)
+ copy_code , copy_iter_depth = gen_a_param_copy_code(indent_depth, ref_prefix, param, "in")
+ params_code += verify_code + copy_code
+ iter_depth = copy_iter_depth if verify_iter_depth < copy_iter_depth else verify_iter_depth
+ # Handle output paramters in case of "out" direction
+ elif direction == "out" and (param_direction == "o" or param_direction == "io"):
+ copy_code , iter_depth = gen_a_param_copy_code(indent_depth, ref_prefix, param, "out")
+ params_code += copy_code
+
+ if direction == "in": # The array size is validated for all paramters in the input parameters valiadtion phase
+ if param_type == "array" and array_size_max != array_size: # Requires run time array size verification
+ return_str += code_indent + "/* Verify array size of " + param_name + " */\n"
+ return_str += code_indent + "SEP_RPC_ASSERT((" + array_size + ") <= (" + array_size_max + "), " + inval_param_retcode + ");\n"
+
+ return_str += params_code
+
+ if (base_level == 1 and param_type == "array") or param_type == "ref":
+ if not is_a_struct:
+ indent_depth -= 1
+ code_indent = "\t" * indent_depth
+ return_str += code_indent + "\tparams." + param_full_name + "_null_flag = 0;\n"
+ return_str += code_indent + "}\n\n"
+
+ if max_iter_depth < iter_depth:
+ max_iter_depth = iter_depth
+
+ return return_str , max_iter_depth
+
+
+## Generate memory references setup code
+def gen_memref_setup_code(params_list):
+ return_str = ""
+ for memref in params_list:
+ param_type, buf_size, void = parse_ptype(memref.get("ptype"))
+ if param_type != "buf":
+ print("Invalid ptype -", param_type, "- for parameter", memref.get("name"))
+ direction = memref.get("direction")
+ if direction == "i": dma_dir = "DXDI_DATA_TO_DEVICE"
+ elif direction == "io": dma_dir = "DXDI_DATA_BIDIR"
+ elif direction == "o": dma_dir = "DXDI_DATA_FROM_DEVICE"
+ else:
+ print("Invalid memory reference direction -", direction, "- for parameter", memref.get("name"))
+ sys.exit(9)
+ memref_idx = "SEPRPC_MEMREF_IDX_" + memref.get("name")
+ return_str += "\t/* " + memref.get("name") + " */\n"
+ return_str += "\tmemRefs[" + memref_idx + "].start_or_offset = (unsigned long)" + memref.get("name") + ";\n"
+ return_str += "\tmemRefs[" + memref_idx + "].size = " + buf_size + ";\n"
+ return_str += "\tmemRefs[" + memref_idx + "].dma_direction = " + dma_dir + ";\n"
+ return_str += "\tmemRefs[" + memref_idx + "].ref_id = DXDI_MEMREF_ID_NULL;\n"
+
+ return return_str
+
+# Generate the host side API wrapper
+def gen_host_c_file (api_data, base_name, c_file_path):
+ host_c_fname = base_name + "_seprpc_stub.c"
+ print("Generating host C file at" , c_file_path + "/" + host_c_fname , "...")
+ host_c_file = open(c_file_path + "/" + host_c_fname, 'w')
+
+ ####### File start comments ###########
+ gen_file_preamble(host_c_file)
+
+ host_c_file.write("/* \\file " + host_c_fname + "\n")
+ host_c_file.write(" * SeP-RPC host wrappers/stubs implementation for " + api_data["name"] + " API */\n\n")
+
+ # includes
+ host_c_file.write("#include <string.h>\n")
+ for header in api_data["headers"] :
+ host_c_file.write("#include \"" + header + "\"\n")
+ host_c_file.write("#include \"driver_interface.h\"\n") # DriverInterface for DxDI_SepRpcCall
+ host_c_file.write("#include \"" + base_name + "_seprpc.h\"\n") # RPC API generated header file
+
+ # functions
+ for function in api_data["functions"] :
+ func_name = function["name"]
+ ret_type , endian_type = parse_dtype(function["return"])
+ host_c_file.write("\n\n/************ " + func_name + " ***************/\n");
+ # Function prototype
+ host_c_file.write(gen_func_prototype(func_name, ret_type, function["params"]) + "\n{\n")
+ host_c_file.write("\tDxDI_RetCode_t diRc;\n")
+ host_c_file.write("\tseprpc_retcode_t rpcRc;\n")
+ numOfMemRefs = len(function["params_buf"])
+ if numOfMemRefs > 0: # Mem. Ref. data
+ host_c_file.write("\tstruct dxdi_memref memRefs[SEPRPC_MEMREF_NUM_" + func_name + "];\n")
+ # Local buffer for parameters structure
+ host_c_file.write("\tstruct SepRpc_" + func_name + "Params params;\n")
+
+ # Generate parameters handling code (copy in/out and verify)
+ inparams_code , max_in_iter_depth = gen_params_code(1,"in", "", 1,
+ function["params_in"] + function["params_inout"] + function["params_out"],
+ function["retcodes"].get("invalid_param"))
+ outparams_code , max_out_iter_depth = gen_params_code(1,"out", "", 1,
+ function["params_inout"] + function["params_out"],
+ function["retcodes"].get("invalid_param"))
+ # Generate iterators declaration code (if any)
+ if (max_in_iter_depth > max_out_iter_depth):
+ max_iter_depth = max_in_iter_depth
+ else:
+ max_iter_depth = max_out_iter_depth
+ if max_iter_depth > 0:
+ for i in range(1, max_iter_depth):
+ host_c_file.write("\tunsigned int i" + str(i) + ";\n")
+ host_c_file.write("\n")
+
+ if inparams_code != "":
+ host_c_file.write("\t/* Verify input parameters and copy into params. struct. */\n" + inparams_code + "\n")
+ del inparams_code # Free memory
+
+ memref_code = gen_memref_setup_code(function["params_buf"])
+ if memref_code != "":
+ host_c_file.write("\t/* User DMA buffers (memory references) */\n" + memref_code + "\n")
+ memref_param_str = "memRefs"
+ else:
+ memref_param_str = "NULL"
+ del memref_code # Free memory
+
+ # Set number of mem. refs.
+ host_c_file.write("\tparams.num_of_memrefs = cpu_to_le32(" + "SEPRPC_MEMREF_NUM_" + func_name + ");\n\n")
+
+ # Invoke IOCTL
+ host_c_file.write("\tdiRc = DxDI_SepRpcCall(SEPRPC_AGENT_ID_" + api_data["name"] +
+ ", SEPRPC_FUNC_ID_" + func_name + ",\n\t\t" + memref_param_str +
+ ", sizeof(params), (struct seprpc_params*)&params, &rpcRc);\n\n")
+ # Assert DxDI_SepRpcCall invocation success
+ host_c_file.write("\tSEP_RPC_ASSERT(diRc == DXDI_RET_OK, " + function["retcodes"].get("generic_error") + ");\n")
+
+ # Handle return code
+ host_c_file.write("\tif (rpcRc != SEPRPC_RET_OK) {\n")
+ host_c_file.write("\t\tSEP_RPC_LOG(\"RPC of " + func_name + " failed with RPC error code %d\\n\", rpcRc);\n")
+ if function["return"] != "void":
+ host_c_file.write("\t\tswitch (rpcRc) {\n")
+ host_c_file.write("\t\tcase SEPRPC_RET_EINVAL:\n\t\t\treturn " + function["retcodes"].get("invalid_param") + ";\n")
+ host_c_file.write("\t\tcase SEPRPC_RET_ENORSC:\n\t\t\treturn " + function["retcodes"].get("not_enough_resources") + ";\n")
+ host_c_file.write("\t\tdefault:\n\t\t\treturn " + function["retcodes"].get("generic_error") +";\n")
+ host_c_file.write("\t\t}\n")
+ host_c_file.write("\t}\n\n")
+ # Copy data out
+ if outparams_code != "":
+ host_c_file.write("\t/* Copy back output parameters */\n" + outparams_code + "\n")
+ del outparams_code # Free memory
+ if function["return"] != "void":
+ c_type , endian_type = parse_dtype(function["return"])
+ host_c_file.write("\treturn " + endian_type +"_to_cpu(params._funcRetCode);\n")
+
+ host_c_file.write("} /* " + func_name + " */\n\n") # End of function
+
+ host_c_file.close()
+
+## Generate "parser" function of a specific API function for SeP agent
+def gen_sep_rpc_func_parser (function):
+ func_name = function["name"]
+ func_code = "static inline seprpc_retcode_t SepRpc_" + func_name + "Parser(\n"
+ func_code += "\tstruct SepRpc_" + func_name + "Params *params,\n"
+ func_code += "\tDX_DmaObjHandle_t dmaObjs[])\n{\n"
+
+ # Set references (pointers) in structures
+ for param in function["params"]:
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ fields = param.findall("field")
+ for field in fields:
+ field_type, field_array_size, field_array_size_max = parse_ptype(field.get("ptype"))
+ if field_type == "ref": # Put the pointer to the ref. field copy into the pointer field
+ field_ref = ("__" if param_type == "ref" else "") + param.get("name") + "." + field.get("name")
+ func_code += "\tif (params->__" + param.get("name") + "__" + field.get("name") + "_null_flag == 0) {\n"
+ func_code += "\t\tparams->" + field_ref + " = &(params->__" + param.get("name") + "__" + field.get("name") + ");\n\t} else {\n"
+ func_code += "\t\tparams->" + field_ref + " = NULL;\n\t}\n"
+ # Add workspace buffer size check
+ for param in function["params"]:
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ if param_type == "wsbuf":
+ func_code += "\tif (params->" + param.get("name") + "_size > SEP_RPC_MAX_WORKSPACE_SIZE)\n"
+ func_code += "\t\treturn SEPRPC_RET_ENORSC;\n\n"
+
+ # Generate function invocation code
+ func_code += "\t"
+ if function["return"] != "void":
+ func_code += "params->_funcRetCode = "
+ func_code += func_name + "(\n"
+ for param in function["params"]:
+ param_name = param.get("name")
+ param_type, array_size, array_size_max = parse_ptype(param.get("ptype"))
+ if param_type == "ref" or param_type == "array":
+ func_code += "\t\tparams->" + ("__" if param_type == "ref" else "") + param_name + "_null_flag != 0 ? NULL :"
+ if param_type == "buf": # Give DMA object for that parameter
+ func_code += "\t\t(" + param.get("dtype") + " *)(dmaObjs[SEPRPC_MEMREF_IDX_" + param_name + "])"
+ elif param_type == "ref": # Dereference copy in params struct
+ func_code += "&(params->__" + param_name + ")"
+ elif param_type == "wsbuf":
+ func_code += "\t\t(" + param.get("dtype") + " *)getWorkspacePtr()"
+ else: # Give the field in "params" directly
+ func_code += "\t\tparams->" + param_name
+ if param is not function["params"][-1]: # Is not last parameter?
+ func_code += ",\n"
+ func_code += ");\n"
+ # End parser function
+ func_code += "\n\treturn SEPRPC_RET_OK;\n"
+ func_code += "}\n"
+ return func_code
+
+
+# Generate the SeP agent implementation C source file
+def gen_sep_c_file(api_data, base_name, c_file_path):
+ sep_c_fname = base_name + "_seprpc_agent.c"
+ print("Generating SeP C file at" , c_file_path + "/" + sep_c_fname , "...")
+ sep_c_file = open(c_file_path + "/" + sep_c_fname, 'w')
+
+ ####### File start comments ###########
+ gen_file_preamble(sep_c_file)
+
+ sep_c_file.write("/* \\file " + sep_c_fname + "\n")
+ sep_c_file.write(" * SeP-RPC agent implementation for " + api_data["name"] + " API */\n\n")
+
+ # includes
+ for header in api_data["headers"] :
+ sep_c_file.write("#include \"" + header + "\"\n")
+ sep_c_file.write("#include \"sep_log.h\"\n")
+ sep_c_file.write("#include \"sep_rpc_server.h\"\n") # SeP RPC agent infrastructure API (available at sep/include)
+ sep_c_file.write("#include \"" + base_name + "_seprpc.h\"\n\n") # RPC API generated header file
+
+ # Parser functions
+ for function in api_data["functions"] :
+ sep_c_file.write(gen_sep_rpc_func_parser(function) + "\n");
+
+ # "Hub" function (agent's entry point)
+ hub_func_name = "SepRpc_" + api_data["name"] + "_Agent"
+ sep_c_file.write("DX_PAL_COMPILER_FUNC_DONT_INLINE static seprpc_retcode_t " + hub_func_name + "_Parser" + "(\n")
+ sep_c_file.write("\tseprpc_agent_id_t agentId,\n")
+ sep_c_file.write("\tseprpc_func_id_t funcId,\n")
+ sep_c_file.write("\tuint16_t paramsMsgSize,\n")
+ sep_c_file.write("\tstruct seprpc_params *params,\n")
+ sep_c_file.write("\tDX_DmaObjHandle_t dmaObjs[])\n{\n")
+
+ # Function ID based switch statement
+ sep_c_file.write("\tswitch (funcId) {\n")
+
+ # static functions to be invoked by the entry-point function
+ for function in api_data["functions"] :
+ func_name = function["name"]
+ sep_c_file.write("\tcase SEPRPC_FUNC_ID_" + func_name + ":\n");
+ # Verify HMB size for requested params structure
+ sep_c_file.write("\t\tif (paramsMsgSize < sizeof(struct SepRpc_" + func_name + "Params)) {\n")
+ sep_c_file.write("\t\t\tSEP_LOG_ERR(\"Invalid HMB size for struct SepRpc_" + func_name + "Params\\n\");\n")
+ sep_c_file.write("\t\t\treturn SEPRPC_RET_EINVAL;\n")
+ sep_c_file.write("\t\t}\n")
+ sep_c_file.write("\t\treturn SepRpc_" + func_name + "Parser((struct SepRpc_" + func_name + "Params *)params, dmaObjs);\n\n")
+
+ # End of switch statement
+ sep_c_file.write("\t} /* switch (funcId) */\n\n")
+
+ # Error return code - not supposed to reach here (all functions are supposed to be handled in the switch statement
+ sep_c_file.write("\treturn SEPRPC_RET_EINVAL_FUNC;\n")
+ # End of "hub" function
+ sep_c_file.write("}\n\n");
+
+ # Wrapper function for d-cache stack replacement
+ hub_func_name = "SepRpc_" + api_data["name"] + "_Agent"
+ sep_c_file.write("static seprpc_retcode_t " + hub_func_name + "(\n")
+ sep_c_file.write("\tseprpc_agent_id_t agentId,\n")
+ sep_c_file.write("\tseprpc_func_id_t funcId,\n")
+ sep_c_file.write("\tuint16_t paramsMsgSize,\n")
+ sep_c_file.write("\tstruct seprpc_params *params,\n")
+ sep_c_file.write("\tDX_DmaObjHandle_t dmaObjs[])\n{\n")
+
+ sep_c_file.write("\tregister seprpc_retcode_t rc = 0;\n")
+ sep_c_file.write("\n\tRPC_CHANGE_STACK();\n")
+
+ sep_c_file.write("\n\trc = " + hub_func_name + "_Parser(\n")
+ sep_c_file.write("\t\tagentId,\n")
+ sep_c_file.write("\t\tfuncId,\n")
+ sep_c_file.write("\t\tparamsMsgSize,\n")
+ sep_c_file.write("\t\tparams,\n")
+ sep_c_file.write("\t\tdmaObjs);\n\n")
+
+ sep_c_file.write("\tRPC_RESTORE_STACK();\n\n")
+
+ sep_c_file.write("\treturn rc;\n}\n\n")
+
+
+ # Init. function for RPC agent: Register to RPC infrastructure
+ sep_c_file.write("seprpc_retcode_t SepRpc_Init" + api_data["name"] + "(void)\n{\n")
+ sep_c_file.write("\treturn SepRpc_RegisterHandler(" + api_data["id"] + ", " + hub_func_name + ", \"" + api_data["name"] + "\");\n")
+ sep_c_file.write("}\n")
+
+ sep_c_file.close()
+
+
+def main ():
+ spec_file , api_h_path , host_c_path , sep_c_path = parse_cmdline_arguments()
+ print("spec_file=" , spec_file , " api_h_path=" , api_h_path , " host_c_path=" , host_c_path , " sep_c_path=" , sep_c_path)
+ # Base name for all the files is taken from spec file name
+ spec_basename = re.sub(r".*/" , "", spec_file) # Remove leading directories
+ spec_basename = re.sub(r"\.[^/.]*$", "" , spec_basename) # Remove .xml suffix
+
+ my_api = parse_api_spec(spec_file)
+
+ if api_h_path is not None:
+ gen_h_file(my_api, spec_basename, api_h_path)
+
+ if host_c_path is not None:
+ gen_host_c_file(my_api, spec_basename, host_c_path)
+
+ if sep_c_path is not None:
+ gen_sep_c_file(my_api, spec_basename, sep_c_path)
+
+ print("Done.")
+
+
+#############################
+if __name__ == "__main__":
+ main()