OMCompiler/Compiler/Template/CodegenEmbeddedC.tpl
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of OpenModelica. | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), | ||
| 5 | * c/o Linköpings universitet, Department of Computer and Information Science, | ||
| 6 | * SE-58183 Linköping, Sweden. | ||
| 7 | * | ||
| 8 | * All rights reserved. | ||
| 9 | * | ||
| 10 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR | ||
| 11 | * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. | ||
| 12 | * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES | ||
| 13 | * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL | ||
| 14 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 15 | * | ||
| 16 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) | ||
| 17 | * Public License (OSMC-PL) are obtained from OSMC, either from the above | ||
| 18 | * address, from the URLs: | ||
| 19 | * http://www.openmodelica.org or | ||
| 20 | * https://github.com/OpenModelica/ or | ||
| 21 | * http://www.ida.liu.se/projects/OpenModelica, | ||
| 22 | * and in the OpenModelica distribution. | ||
| 23 | * | ||
| 24 | * GNU AGPL version 3 is obtained from: | ||
| 25 | * https://www.gnu.org/licenses/licenses.html#GPL | ||
| 26 | * | ||
| 27 | * This program is distributed WITHOUT ANY WARRANTY; without | ||
| 28 | * even the implied warranty of MERCHANTABILITY or FITNESS | ||
| 29 | * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH | ||
| 30 | * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL. | ||
| 31 | * | ||
| 32 | * See the full OSMC Public License conditions for more details. | ||
| 33 | * | ||
| 34 | */ | ||
| 35 | |||
| 36 | // This file defines templates for transforming Modelica code to embeddedC | ||
| 37 | // code. They are used in the code generator phase of the compiler to write | ||
| 38 | // target code. | ||
| 39 | // | ||
| 40 | // There are two root templates intended to be called from the code generator: | ||
| 41 | // translateModel and translateFunctions. These templates do not return any | ||
| 42 | // result but instead write the result to files. All other templates return | ||
| 43 | // text and are used by the root templates (most of them indirectly). | ||
| 44 | // | ||
| 45 | // To future maintainers of this file: | ||
| 46 | // | ||
| 47 | // - A line like this | ||
| 48 | // # var = "" | ||
| 49 | // declares a text buffer that you can later append text to. It can also be | ||
| 50 | // passed to other templates that in turn can append text to it. In the new | ||
| 51 | // version of Susan it should be written like this instead: | ||
| 52 | // let &var = buffer "" | ||
| 53 | // | ||
| 54 | // - A line like this | ||
| 55 | // ..., Text var, ... | ||
| 56 | // declares that a template takes a tmext buffer as input parameter. In the | ||
| 57 | // new version of Susan it should be written like this instead: | ||
| 58 | // ..., Text &var, ... | ||
| 59 | // | ||
| 60 | // - A line like this: | ||
| 61 | // ..., var, ... | ||
| 62 | // passes a text buffer to a template. In the new version of Susan it should | ||
| 63 | // be written like this instead: | ||
| 64 | // ..., &var, ... | ||
| 65 | // | ||
| 66 | // - Style guidelines: | ||
| 67 | // | ||
| 68 | // - Try (hard) to limit each row to 80 characters | ||
| 69 | // | ||
| 70 | // - Code for a template should be indented with 2 spaces | ||
| 71 | // | ||
| 72 | // - Exception to this rule is if you have only a single case, then that | ||
| 73 | // single case can be written using no indentation | ||
| 74 | // | ||
| 75 | // This single case can be seen as a clarification of the input to the | ||
| 76 | // template | ||
| 77 | // | ||
| 78 | // - Code after a case should be indented with 2 spaces if not written on the | ||
| 79 | // same line | ||
| 80 | |||
| 81 | package CodegenEmbeddedC | ||
| 82 | |||
| 83 | import interface SimCodeTV; | ||
| 84 | import interface SimCodeBackendTV; | ||
| 85 | |||
| 86 | import CodegenUtil.*; | ||
| 87 | import CodegenUtilSimulation.*; | ||
| 88 | import ExpressionDumpTpl; | ||
| 89 | import DAEDumpTpl; | ||
| 90 | |||
| 91 | ✗ | template mainFile(SimCode simCode) | |
| 92 | ::= | ||
| 93 | let modelNamePrefixStr = modelNamePrefix(simCode) | ||
| 94 | match simCode | ||
| 95 | case simCode as SIMCODE(simulationSettingsOpt=NONE()) then | ||
| 96 | error(sourceInfo(), "Missing simulation settings") | ||
| 97 | case simCode as SIMCODE(modelInfo=MODELINFO(functions=functions, varInfo=varInfo as VARINFO(__), vars=vars as SIMVARS(__)), | ||
| 98 | extObjInfo=extObjInfo as EXTOBJINFO(vars=extObjVars), | ||
| 99 | simulationSettingsOpt=SOME(settings as SIMULATION_SETTINGS(__))) then | ||
| 100 | << | ||
| 101 | #define fmi2TypesPlatform_h | ||
| 102 | |||
| 103 | #define fmi2TypesPlatform "default" /* Compatible */ | ||
| 104 | |||
| 105 | typedef struct <%symbolName(modelNamePrefixStr,"fmi2Component_s")%>* fmi2Component; | ||
| 106 | typedef void* fmi2ComponentEnvironment; /* Pointer to FMU environment */ | ||
| 107 | typedef void* fmi2FMUstate; /* Pointer to internal FMU state */ | ||
| 108 | typedef unsigned int fmi2ValueReference; | ||
| 109 | typedef double fmi2Real; | ||
| 110 | typedef int fmi2Integer; | ||
| 111 | typedef int fmi2Boolean; | ||
| 112 | typedef char fmi2Char; | ||
| 113 | typedef const fmi2Char* fmi2String; | ||
| 114 | typedef char fmi2Byte; | ||
| 115 | |||
| 116 | #define fmi2True 1 | ||
| 117 | #define fmi2False 0 | ||
| 118 | |||
| 119 | #include "fmi2/fmi2Functions.h" | ||
| 120 | |||
| 121 | #include <stdint.h> | ||
| 122 | #include <stdio.h> | ||
| 123 | |||
| 124 | void ModelicaFormatMessage(const char *fmt, ...) | ||
| 125 | { | ||
| 126 | va_list args; | ||
| 127 | va_start(args, fmt); | ||
| 128 | vprintf(fmt, args); | ||
| 129 | va_end(args); | ||
| 130 | } | ||
| 131 | |||
| 132 | typedef struct <%symbolName(modelNamePrefixStr,"fmi2Component_s")%> { | ||
| 133 | fmi2Real currentTime; | ||
| 134 | <% match nVariablesReal(varInfo) | ||
| 135 | case 0 then "" | ||
| 136 | case n then 'fmi2Real fmi2RealVars[<%n%>];<%\n%>' | ||
| 137 | %><% match varInfo.numIntAlgVars | ||
| 138 | case 0 then "" | ||
| 139 | case n then 'fmi2Integer fmi2IntegerVars[<%n%>];<%\n%>' | ||
| 140 | %><% match varInfo.numBoolAlgVars | ||
| 141 | case 0 then "" | ||
| 142 | case n then 'fmi2Boolean fmi2BooleanVars[<%n%>];<%\n%>' | ||
| 143 | %><% match varInfo.numStringAlgVars | ||
| 144 | case 0 then "" | ||
| 145 | else error(sourceInfo(), "String variables not supported yet") | ||
| 146 | %><% match varInfo.numParams | ||
| 147 | case 0 then "" | ||
| 148 | case n then 'fmi2Real fmi2RealParameter[<%n%>];<%\n%>' | ||
| 149 | %><% match varInfo.numIntParams | ||
| 150 | case 0 then "" | ||
| 151 | case n then 'fmi2Integer fmi2IntegerParameter[<%n%>];<%\n%>' | ||
| 152 | %><% match varInfo.numBoolParams | ||
| 153 | case 0 then "" | ||
| 154 | case n then 'fmi2Boolean fmi2BooleanParameter[<%n%>];<%\n%>' | ||
| 155 | %><% match varInfo.numIntParams | ||
| 156 | case 0 then "" | ||
| 157 | case n then 'fmi2String fmi2StringParameter[<%n%>];<%\n%>' | ||
| 158 | %><% match listLength(extObjVars) | ||
| 159 | case 0 then "" | ||
| 160 | case n then 'void* extObjs[<%n%>];<%\n%>' | ||
| 161 | %> | ||
| 162 | } <%symbolName(modelNamePrefixStr,"fmi2Component")%>; | ||
| 163 | |||
| 164 | <%symbolName(modelNamePrefixStr,"fmi2Component")%> <%symbolName(modelNamePrefixStr,"component")%> = { | ||
| 165 | <% match nVariablesReal(varInfo) | ||
| 166 | case 0 then "" | ||
| 167 | else | ||
| 168 | << | ||
| 169 | .fmi2RealVars = { | ||
| 170 | <%vars.stateVars |> var => startValue(var) %> | ||
| 171 | <%vars.derivativeVars |> var => startValue(var) %> | ||
| 172 | <%vars.algVars |> var => startValue(var) %> | ||
| 173 | <%vars.discreteAlgVars |> var => startValue(var) %> | ||
| 174 | <%vars.realOptimizeConstraintsVars |> var => startValue(var) %> | ||
| 175 | <%vars.realOptimizeFinalConstraintsVars |> var => startValue(var) %> | ||
| 176 | },<%\n%> | ||
| 177 | >> | ||
| 178 | %><% match varInfo.numIntAlgVars | ||
| 179 | case 0 then "" | ||
| 180 | else | ||
| 181 | << | ||
| 182 | .fmi2IntegerVars = { | ||
| 183 | <%vars.intAlgVars |> var => startValue(var) %> | ||
| 184 | },<%\n%> | ||
| 185 | >> | ||
| 186 | %><% match varInfo.numBoolAlgVars | ||
| 187 | case 0 then "" | ||
| 188 | else | ||
| 189 | << | ||
| 190 | .fmi2BooleanVars = { | ||
| 191 | <%vars.boolAlgVars |> var => startValue(var) %> | ||
| 192 | },<%\n%> | ||
| 193 | >> | ||
| 194 | %><% match varInfo.numStringAlgVars | ||
| 195 | case 0 then "" | ||
| 196 | else | ||
| 197 | << | ||
| 198 | .fmi2StringVars = { | ||
| 199 | <%vars.stringAlgVars |> var => startValue(var) %> | ||
| 200 | },<%\n%> | ||
| 201 | >> | ||
| 202 | %><% match varInfo.numParams | ||
| 203 | case 0 then "" | ||
| 204 | else | ||
| 205 | << | ||
| 206 | .fmi2RealParameter = { | ||
| 207 | <%vars.paramVars |> var => startValue(var) %> | ||
| 208 | },<%\n%> | ||
| 209 | >> | ||
| 210 | %><% match varInfo.numIntParams | ||
| 211 | case 0 then "" | ||
| 212 | else | ||
| 213 | << | ||
| 214 | .fmi2IntegerParameter = { | ||
| 215 | <%vars.intParamVars |> var => startValue(var) %> | ||
| 216 | },<%\n%> | ||
| 217 | >> | ||
| 218 | %><% match varInfo.numBoolParams | ||
| 219 | case 0 then "" | ||
| 220 | else | ||
| 221 | << | ||
| 222 | .fmi2BooleanParameter = { | ||
| 223 | <%vars.boolParamVars |> var => startValue(var) %> | ||
| 224 | },<%\n%> | ||
| 225 | >> | ||
| 226 | %><% match varInfo.numStringParamVars | ||
| 227 | case 0 then "" | ||
| 228 | else | ||
| 229 | << | ||
| 230 | .fmi2StringParameter = { | ||
| 231 | <%vars.stringParamVars |> var => startValue(var) %> | ||
| 232 | },<%\n%> | ||
| 233 | >> | ||
| 234 | %> | ||
| 235 | }; | ||
| 236 | |||
| 237 | #include <math.h> | ||
| 238 | /* TODO: Generate used builtin functions before SimCode */ | ||
| 239 | static inline double om_mod(double x, double y) | ||
| 240 | { | ||
| 241 | return x-floor(x/y)*y; | ||
| 242 | } | ||
| 243 | |||
| 244 | <%functionsFile(functions, literals, externalFunctionIncludes)%> | ||
| 245 | |||
| 246 | fmi2Component <%symbolName(modelNamePrefixStr,"fmi2Instantiate")%>(fmi2String name, fmi2Type ty, fmi2String GUID, fmi2String resources, const fmi2CallbackFunctions* functions, fmi2Boolean visible, fmi2Boolean loggingOn) | ||
| 247 | { | ||
| 248 | static int initDone=0; | ||
| 249 | if (initDone) { | ||
| 250 | return NULL; | ||
| 251 | } | ||
| 252 | return &<%symbolName(modelNamePrefixStr,"component")%>; | ||
| 253 | } | ||
| 254 | |||
| 255 | fmi2Status <%symbolName(modelNamePrefixStr,"fmi2SetupExperiment")%>(fmi2Component comp, fmi2Boolean toleranceDefined, fmi2Real tolerance, fmi2Real startTime, fmi2Boolean stopTimeDefined, fmi2Real stopTime) | ||
| 256 | { | ||
| 257 | return fmi2OK; | ||
| 258 | } | ||
| 259 | |||
| 260 | fmi2Status <%symbolName(modelNamePrefixStr,"fmi2EnterInitializationMode")%>(fmi2Component comp) | ||
| 261 | { | ||
| 262 | <%callExternalObjectConstructors(extObjInfo)%> | ||
| 263 | return fmi2OK; | ||
| 264 | } | ||
| 265 | |||
| 266 | fmi2Status <%symbolName(modelNamePrefixStr,"fmi2ExitInitializationMode")%>(fmi2Component comp) | ||
| 267 | { | ||
| 268 | return fmi2OK; | ||
| 269 | } | ||
| 270 | |||
| 271 | static fmi2Status <%symbolName(modelNamePrefixStr,"functionODE")%>(fmi2Component comp) | ||
| 272 | { | ||
| 273 | <% match odeEquations | ||
| 274 | case {} then "" | ||
| 275 | case {eqs} then (eqs |> eq => equation_(eq); separator="\n") | ||
| 276 | else error(sourceInfo(), "TODO") // List.flatten(odeEquations) |> eq => equation_(eq); separator="\n" | ||
| 277 | %> | ||
| 278 | } | ||
| 279 | |||
| 280 | static fmi2Status <%symbolName(modelNamePrefixStr,"functionOutputs")%>(fmi2Component comp) | ||
| 281 | { | ||
| 282 | <% match allEquations | ||
| 283 | case {} then "" | ||
| 284 | case {eqs} then (eqs |> eq => equation_(eq); separator="\n") | ||
| 285 | else (allEquations |> eqs => (eqs |> eq => equation_(eq); separator="\n"); separator="\n") | ||
| 286 | %> | ||
| 287 | } | ||
| 288 | |||
| 289 | fmi2Status <%symbolName(modelNamePrefixStr,"fmi2DoStep")%>(fmi2Component comp, fmi2Real currentCommunicationPoint, fmi2Real communicationStepSize, fmi2Boolean noSetFMUStatePriorToCurrentPoint) | ||
| 290 | { | ||
| 291 | comp->currentTime = currentCommunicationPoint; | ||
| 292 | <%match varInfo.numStateVars | ||
| 293 | case 0 then "" | ||
| 294 | else | ||
| 295 | << | ||
| 296 | int i=0; | ||
| 297 | for (i=0; i<<%varInfo.numStateVars%>; i++) { | ||
| 298 | comp->fmi2RealVars[i] += comp->fmi2RealVars[i+<%varInfo.numStateVars%>]*communicationStepSize; | ||
| 299 | } | ||
| 300 | >> | ||
| 301 | %> | ||
| 302 | /* TODO: Calculate time/state-dependent variables here... */ | ||
| 303 | <%symbolName(modelNamePrefixStr,"functionOutputs")%>(comp); | ||
| 304 | return fmi2OK; | ||
| 305 | } | ||
| 306 | |||
| 307 | int main(int argc, char **argv) | ||
| 308 | { | ||
| 309 | int terminateSimulation = 0; | ||
| 310 | fmi2Status status = fmi2OK; | ||
| 311 | fmi2CallbackFunctions cbf = { | ||
| 312 | .logger = NULL, | ||
| 313 | .allocateMemory = NULL /*calloc*/, | ||
| 314 | .freeMemory = NULL /*free*/, | ||
| 315 | .stepFinished = NULL, //synchronous execution | ||
| 316 | .componentEnvironment = NULL | ||
| 317 | }; | ||
| 318 | |||
| 319 | fmi2Component comp = <%symbolName(modelNamePrefixStr,"fmi2Instantiate")%>("", fmi2CoSimulation, "", "", &cbf, fmi2False, fmi2False); | ||
| 320 | if (comp==NULL) { | ||
| 321 | return 1; | ||
| 322 | } | ||
| 323 | <%symbolName(modelNamePrefixStr,"fmi2SetupExperiment")%>(comp, fmi2False, 0.0, <%settings.startTime%>, fmi2False, <%settings.stopTime%>); | ||
| 324 | <%symbolName(modelNamePrefixStr,"fmi2EnterInitializationMode")%>(comp); | ||
| 325 | // Set start-values? Nah... | ||
| 326 | <%symbolName(modelNamePrefixStr,"fmi2ExitInitializationMode")%>(comp); | ||
| 327 | |||
| 328 | double currentTime = <%settings.startTime%>; | ||
| 329 | double h = <%settings.stepSize%>; | ||
| 330 | uint32_t i = 0; | ||
| 331 | |||
| 332 | while (status == fmi2OK) { | ||
| 333 | //retrieve outputs | ||
| 334 | // fmi2GetReal(m, ..., 1, &y1); | ||
| 335 | //set inputs | ||
| 336 | // fmi2SetReal(m, ..., 1, &y2); | ||
| 337 | |||
| 338 | //call slave and check status | ||
| 339 | status = <%symbolName(modelNamePrefixStr,"fmi2DoStep")%>(comp, currentTime, h, fmi2True); | ||
| 340 | switch (status) { | ||
| 341 | case fmi2Discard: | ||
| 342 | case fmi2Error: | ||
| 343 | case fmi2Fatal: | ||
| 344 | case fmi2Pending /* Cannot happen */: | ||
| 345 | terminateSimulation = 1; | ||
| 346 | break; | ||
| 347 | case fmi2OK: | ||
| 348 | case fmi2Warning: | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | if (terminateSimulation) { | ||
| 352 | break; | ||
| 353 | } | ||
| 354 | i++; | ||
| 355 | /* increment master time */ | ||
| 356 | currentTime = <%settings.startTime%> + h*i; | ||
| 357 | } | ||
| 358 | |||
| 359 | #if 0 | ||
| 360 | if ((status != fmi2Error) && (status != fmi2Fatal)) { | ||
| 361 | fmi2Terminate(m); | ||
| 362 | } | ||
| 363 | if (status != fmi2Fatal) { | ||
| 364 | fmi2FreeInstance(m); | ||
| 365 | } | ||
| 366 | #endif | ||
| 367 | } | ||
| 368 | |||
| 369 | >> | ||
| 370 | end mainFile; | ||
| 371 | |||
| 372 | ✗ | template equation_(SimEqSystem eq) | |
| 373 | ::= | ||
| 374 | match eq | ||
| 375 | case SES_SIMPLE_ASSIGN(__) then | ||
| 376 | << | ||
| 377 | <%cref(cref)%> = <%daeExp(exp)%>; /* equation <%index%> */ | ||
| 378 | >> | ||
| 379 | case SES_ALGORITHM(__) then | ||
| 380 | (statements |> stmt => statement(stmt) ; separator="\n") | ||
| 381 | else error(sourceInfo(), 'Unsupported equation: ...') | ||
| 382 | end equation_; | ||
| 383 | |||
| 384 | ✗ | template statement(DAE.Statement stmt) | |
| 385 | ::= | ||
| 386 | match stmt | ||
| 387 | case STMT_ASSIGN(type_=T_ARRAY(__)) | ||
| 388 | then error(sourceInfo(), "Array assignments are not supported") | ||
| 389 | case STMT_ASSIGN(exp1=CREF(componentRef=cr)) | ||
| 390 | then '<%cref(cr)%> = <%daeExp(exp)%>;' | ||
| 391 | case STMT_NORETCALL(__) | ||
| 392 | then '<%daeExp(exp)%>;' | ||
| 393 | case STMT_IF(__) then | ||
| 394 | << | ||
| 395 | if (<%daeExp(exp)%>) { | ||
| 396 | <%statementLst |> stmt => statement(stmt) ; separator="\n"%> | ||
| 397 | }<%elseStatement(else_)%> | ||
| 398 | >> | ||
| 399 | else error(sourceInfo(), 'Unsupported statement: <%DAEDumpTpl.dumpStatement(stmt)%>') | ||
| 400 | end statement; | ||
| 401 | |||
| 402 | ✗ | template elseStatement(Else else_) | |
| 403 | ::= | ||
| 404 | match else_ | ||
| 405 | case NOELSE(__) then "" | ||
| 406 | case ELSEIF(__) then | ||
| 407 | << | ||
| 408 | else if (<%daeExp(exp)%>) { | ||
| 409 | <%statementLst |> stmt => statement(stmt) ; separator="\n"%> | ||
| 410 | }<%elseStatement(else_)%> | ||
| 411 | >> | ||
| 412 | case ELSE(__) then | ||
| 413 | << | ||
| 414 | else { | ||
| 415 | <%statementLst |> stmt => statement(stmt) ; separator="\n"%> | ||
| 416 | } | ||
| 417 | >> | ||
| 418 | end elseStatement; | ||
| 419 | |||
| 420 | ✗ | template cref(ComponentRef cr) | |
| 421 | "Generates C equivalent name for component reference. | ||
| 422 | used in Compiler/Template/CodegenFMU.tpl" | ||
| 423 | ::= | ||
| 424 | match cr | ||
| 425 | case CREF_IDENT(ident = "time") then "comp->currentTime" | ||
| 426 | case WILD(__) then '' | ||
| 427 | else crefToCStr(cr, 0, false) | ||
| 428 | end cref; | ||
| 429 | |||
| 430 | ✗ | template crefLocal(ComponentRef cr) | |
| 431 | "Generates C equivalent name for component reference. | ||
| 432 | used in Compiler/Template/CodegenFMU.tpl" | ||
| 433 | ::= | ||
| 434 | match cr | ||
| 435 | case CREF_IDENT(__) then "om_"+ident | ||
| 436 | else error(sourceInfo(), "Only CREF_IDENT as local identifiers (for now)") | ||
| 437 | end crefLocal; | ||
| 438 | |||
| 439 | ✗ | template crefToCStr(ComponentRef cr, Integer ix, Boolean isPre) | |
| 440 | "Helper function to cref." | ||
| 441 | ::= | ||
| 442 | match cr | ||
| 443 | case CREF_QUAL(ident="$PRE", subscriptLst={}) then | ||
| 444 | (if isPre then error(sourceInfo(), 'Got $PRE for something that is already pre: <%CodegenUtil.crefStr(cr)%>') | ||
| 445 | else crefToCStr(componentRef, ix, true)) | ||
| 446 | else match cref2simvar(cr, getSimCode()) | ||
| 447 | case var as SIMVAR(index=-1) then error(sourceInfo(), 'crefToCStr got index=-1 for <%variabilityString(varKind)%> <%CodegenUtil.crefStr(name)%>') | ||
| 448 | case var as SIMVAR(__) then '<%varArrayNameValues(var, ix, isPre)%>[<%index%>] /* <%Util.escapeModelicaStringToCString(CodegenUtil.crefStr(name))%> <%variabilityString(varKind)%> */' | ||
| 449 | else "CREF_NOT_IDENT_OR_QUAL" | ||
| 450 | end crefToCStr; | ||
| 451 | |||
| 452 | ✗ | template crefShortType(ComponentRef cr) "template crefType | |
| 453 | Like cref but with cast if type is integer." | ||
| 454 | ::= | ||
| 455 | match cr | ||
| 456 | case CREF_IDENT(__) then expTypeShort(identType) | ||
| 457 | case CREF_QUAL(__) then crefShortType(componentRef) | ||
| 458 | else "crefType:ERROR" | ||
| 459 | end match | ||
| 460 | end crefShortType; | ||
| 461 | |||
| 462 | ✗ | template expTypeShort(DAE.Type type) | |
| 463 | "Generate type helper." | ||
| 464 | ::= | ||
| 465 | match type | ||
| 466 | case T_INTEGER(__) then "fmi2Integer" | ||
| 467 | case T_REAL(__) then "fmi2Real" | ||
| 468 | case T_STRING(__) then "fmi2String" | ||
| 469 | case T_BOOL(__) then "fmi2Boolean" | ||
| 470 | case T_ENUMERATION(__) then "fmi2Integer" | ||
| 471 | case T_SUBTYPE_BASIC(__) then expTypeShort(complexType) | ||
| 472 | case T_ARRAY(__) then expTypeShort(ty) | ||
| 473 | case T_COMPLEX(complexClassType=EXTERNAL_OBJ(__)) then "void*" | ||
| 474 | else error(sourceInfo(),'expTypeShort: <%unparseType(type)%>') | ||
| 475 | end expTypeShort; | ||
| 476 | |||
| 477 | ✗ | template daeExp(Exp exp) | |
| 478 | ::= | ||
| 479 | match exp | ||
| 480 | case ICONST(__) then integer | ||
| 481 | case RCONST(__) then real | ||
| 482 | case SCONST(__) then '"<%Util.escapeModelicaStringToCString(string)%>"' | ||
| 483 | case BCONST(__) then (if bool then "fmi2True" else "fmi2False") | ||
| 484 | case ENUM_LITERAL(__) then index | ||
| 485 | case LUNARY(operator=NOT(__)) then '!(<%daeExp(exp)%>)' | ||
| 486 | case UNARY(operator=UMINUS(__)) then '-(<%daeExp(exp)%>)' | ||
| 487 | case BINARY(__) then daeExpBinary(exp1,operator,exp2,exp) | ||
| 488 | case RELATION(__) then daeExpBinary(exp1,operator,exp2,exp) | ||
| 489 | case IFEXP(__) then '(<%daeExp(expCond)%>) ? (<%daeExp(expThen)%>) : (<%daeExp(expElse)%>)' | ||
| 490 | case CALL(attr=CALL_ATTR(builtin=true)) then daeExpCallBuiltin(exp) | ||
| 491 | case CALL(__) then daeExpCall(exp) | ||
| 492 | case CREF(ty=T_ARRAY(__)) then error(sourceInfo(), 'CREF array... <%ExpressionDumpTpl.dumpExp(exp,"\"")%>') | ||
| 493 | case CREF(__) then cref(componentRef) | ||
| 494 | case CAST(ty=T_REAL(__), exp=e) then daeExp(e) | ||
| 495 | else error(sourceInfo(), 'daeExp: Not supporting <%ExpressionDumpTpl.dumpExp(exp,"\"")%>') | ||
| 496 | end daeExp; | ||
| 497 | |||
| 498 | ✗ | template daeExpBinary(Exp exp1, Operator op, Exp exp2, Exp origExp) | |
| 499 | ::= | ||
| 500 | match op | ||
| 501 | case ADD(__) then '(<%daeExp(exp1)%>)+(<%daeExp(exp2)%>)' | ||
| 502 | case SUB(__) then '(<%daeExp(exp1)%>)-(<%daeExp(exp2)%>)' | ||
| 503 | case MUL(__) then '(<%daeExp(exp1)%>)*(<%daeExp(exp2)%>)' | ||
| 504 | case DIV(__) then '(<%daeExp(exp1)%>)/(<%daeExp(exp2)%>)' // TODO: some division by zero handling will be needed | ||
| 505 | case POW(__) then 'pow((<%daeExp(exp1)%>),(<%daeExp(exp2)%>))' // TODO: check sqrt of < 0 | ||
| 506 | |||
| 507 | case GREATER(__) then '(<%daeExp(exp1)%>)>(<%daeExp(exp2)%>)' | ||
| 508 | case GREATEREQ(__) then '(<%daeExp(exp1)%>)>=(<%daeExp(exp2)%>)' | ||
| 509 | case LESS(__) then '(<%daeExp(exp1)%>)<(<%daeExp(exp2)%>)' | ||
| 510 | |||
| 511 | case EQUAL(__) then '(<%daeExp(exp1)%>)==(<%daeExp(exp2)%>)' | ||
| 512 | case NEQUAL(__) then '(<%daeExp(exp1)%>)!=(<%daeExp(exp2)%>)' | ||
| 513 | |||
| 514 | else error(sourceInfo(), 'daeExpBinary: Not supporting operator? <%ExpressionDumpTpl.dumpExp(origExp,"\"")%>') | ||
| 515 | end daeExpBinary; | ||
| 516 | |||
| 517 | ✗ | template daeExpCallBuiltin(Exp exp) | |
| 518 | ::= | ||
| 519 | match exp | ||
| 520 | case CALL(path=IDENT(name="DIVISION"),expLst=exp1::exp2::_) then '(<%daeExp(exp1)%>)/(<%daeExp(exp2)%>)' | ||
| 521 | case CALL(path=IDENT(name="smooth"),expLst={exp1,exp2}) then daeExp(exp2) | ||
| 522 | case CALL(path=IDENT(name="integer"),expLst=exp1::_) then '((int)<%daeExp(exp1)%>)' | ||
| 523 | case CALL(path=IDENT(name="abs"),expLst={exp1}) then 'fabs(<%daeExp(exp1)%>)' | ||
| 524 | case CALL(path=IDENT(name="min"),expLst={exp1,exp2},attr=CALL_ATTR(ty=T_REAL(__))) then 'fmin(<%daeExp(exp1)%>,<%daeExp(exp2)%>)' | ||
| 525 | case CALL(path=IDENT(name="max"),expLst={exp1,exp2},attr=CALL_ATTR(ty=T_REAL(__))) then 'fmax(<%daeExp(exp1)%>,<%daeExp(exp2)%>)' | ||
| 526 | case CALL(path=IDENT(name=name as "sin"),expLst={exp1}) | ||
| 527 | case CALL(path=IDENT(name=name as "cos"),expLst={exp1}) | ||
| 528 | case CALL(path=IDENT(name=name as "tan"),expLst={exp1}) | ||
| 529 | case CALL(path=IDENT(name=name as "asin"),expLst={exp1}) | ||
| 530 | case CALL(path=IDENT(name=name as "acos"),expLst={exp1}) | ||
| 531 | case CALL(path=IDENT(name=name as "atan"),expLst={exp1}) | ||
| 532 | case CALL(path=IDENT(name=name as "sinh"),expLst={exp1}) | ||
| 533 | case CALL(path=IDENT(name=name as "cosh"),expLst={exp1}) | ||
| 534 | case CALL(path=IDENT(name=name as "tanh"),expLst={exp1}) | ||
| 535 | case CALL(path=IDENT(name=name as "exp"),expLst={exp1}) | ||
| 536 | case CALL(path=IDENT(name=name as "log"),expLst={exp1}) | ||
| 537 | case CALL(path=IDENT(name=name as "log10"),expLst={exp1}) | ||
| 538 | then '<%name%>(<%daeExp(exp1)%>)' | ||
| 539 | case CALL(path=IDENT(name=name as "atan2"),expLst={exp1,exp2}) | ||
| 540 | then '<%name%>(<%daeExp(exp1)%>,<%daeExp(exp2)%>)' | ||
| 541 | /* TODO: Generate used builtin functions before SimCode */ | ||
| 542 | case CALL(path=IDENT(name="mod"),expLst=exp1::exp2::_) then 'om_mod(<%daeExp(exp1)%>,<%daeExp(exp2)%>)' | ||
| 543 | /* no events for foor and ceil */ | ||
| 544 | case CALL(path=IDENT(name="floor"),expLst=exp1::exp2::_) then 'floor(<%daeExp(exp1)%>)' | ||
| 545 | case CALL(path=IDENT(name="ceil"),expLst=exp1::exp2::_) then 'ceil(<%daeExp(exp1)%>)' | ||
| 546 | |||
| 547 | /* TODO: pre needs to be handled in a special way */ | ||
| 548 | case CALL(path=IDENT(name="pre"),expLst={exp1}) then daeExp(exp1) | ||
| 549 | case CALL(__) then error(sourceInfo(), 'daeExpCallBuiltin: Not supported: <%ExpressionDumpTpl.dumpExp(exp,"\"")%>') | ||
| 550 | end daeExpCallBuiltin; | ||
| 551 | |||
| 552 | ✗ | template daeExpCall(Exp exp) | |
| 553 | ::= | ||
| 554 | match exp | ||
| 555 | case CALL(path=path, attr=CALL_ATTR(__)) then '<%underscorePath(path)%>(comp<%expLst |> e => ', <%daeExp(e)%>'%>)' | ||
| 556 | case CALL(__) then error(sourceInfo(), 'daeExpCall: Not supported: <%ExpressionDumpTpl.dumpExp(exp,"\"")%>') | ||
| 557 | end daeExpCall; | ||
| 558 | |||
| 559 | ✗ | template varArrayNameValues(SimVar var, Integer ix, Boolean isPre) | |
| 560 | ::= | ||
| 561 | match ix case 0 then | ||
| 562 | (match var | ||
| 563 | case SIMVAR(varKind=PARAM()) | ||
| 564 | case SIMVAR(varKind=OPT_TGRID()) | ||
| 565 | then 'comp-><%crefShortType(name)%>Parameter' | ||
| 566 | case SIMVAR(varKind=EXTOBJ()) then 'comp->extObjs' | ||
| 567 | case SIMVAR(__) then 'comp<%if isPre then "XXXPreVars???" else ''%>-><%crefShortType(name)%>Vars<%if isPre then "Pre"%>') | ||
| 568 | else error(sourceInfo(), "varArrayNameValues ix>0") | ||
| 569 | end varArrayNameValues; | ||
| 570 | |||
| 571 | ✗ | template constVal(Exp value, Type ty_) | |
| 572 | "Returns initial value of ScalarVariable." | ||
| 573 | ::= | ||
| 574 | match value | ||
| 575 | case ICONST(__) then integer | ||
| 576 | case RCONST(__) then real | ||
| 577 | case SCONST(__) then '"<%Util.escapeModelicaStringToCString(string)%>"' | ||
| 578 | case BCONST(__) then (if bool then "fmi2True" else "fmi2False") | ||
| 579 | case ENUM_LITERAL(__) then index | ||
| 580 | else (match ty_ | ||
| 581 | case T_REAL(__) then "0.0" | ||
| 582 | else error(sourceInfo(), 'No start value for variable... <%ExpressionDumpTpl.dumpExp(value,"\"")%>') | ||
| 583 | ) | ||
| 584 | end constVal; | ||
| 585 | |||
| 586 | ✗ | template startValue(SimVar var) | |
| 587 | ::= | ||
| 588 | match var | ||
| 589 | case SIMVAR(initialValue=SOME(e), type_=ty) then '<%constVal(e,ty)%> /*<%CodegenUtil.crefStr(name)%>*/,<%\n%>' | ||
| 590 | case SIMVAR(type_=T_REAL(__)) then '0.0 /*<%CodegenUtil.crefStr(name)%>*/,<%\n%>' | ||
| 591 | case SIMVAR(type_=T_INTEGER(__)) then '0 /*<%CodegenUtil.crefStr(name)%>*/,<%\n%>' | ||
| 592 | case SIMVAR(type_=T_BOOL(__)) then 'fmi2False /*<%CodegenUtil.crefStr(name)%>*/,<%\n%>' | ||
| 593 | case SIMVAR(type_=T_STRING(__)) then '"" /*<%CodegenUtil.crefStr(name)%>*/,<%\n%>' | ||
| 594 | case SIMVAR(__) then error(sourceInfo(), 'No start value for variable <%CodegenUtil.crefStr(name)%>.') | ||
| 595 | end startValue; | ||
| 596 | |||
| 597 | ✗ | template functionsFile(list<SimCodeFunction.Function> functions, | |
| 598 | list<Exp> literals, | ||
| 599 | list<String> externalFunctionIncludes) | ||
| 600 | "Generates the contents of the main C file for the function case." | ||
| 601 | ::= | ||
| 602 | << | ||
| 603 | <% /* Note: The literals may not be part of the header due to separate compilation */ | ||
| 604 | literals |> literal hasindex i0 fromindex 0 => literalExpConst(literal,i0) ; separator="\n";empty | ||
| 605 | %> | ||
| 606 | <%externalFunctionIncludes |> inc => inc; separator="\n"%> | ||
| 607 | |||
| 608 | <%functions |> func => functionDeclaration(func) ; separator="\n" %> | ||
| 609 | |||
| 610 | <%functions |> func => functionBody(func) ; separator="\n" %> | ||
| 611 | >> | ||
| 612 | end functionsFile; | ||
| 613 | |||
| 614 | ✗ | template functionBody(SimCodeFunction.Function fn) | |
| 615 | "Generates the body for a function." | ||
| 616 | ::= | ||
| 617 | match fn | ||
| 618 | case fn as FUNCTION(__) then functionBodyRegularFunction(fn) | ||
| 619 | case fn as EXTERNAL_FUNCTION(__) then functionBodyExternalFunction(fn) | ||
| 620 | case fn as RECORD_CONSTRUCTOR(__) then error(sourceInfo(), "No records in embedded C yet") // functionBodyRecordConstructor(fn) | ||
| 621 | case fn as KERNEL_FUNCTION(__) then error(sourceInfo(), "No kernel functions in embedded C") | ||
| 622 | end functionBody; | ||
| 623 | |||
| 624 | ✗ | template functionDeclaration(SimCodeFunction.Function fn) | |
| 625 | "Generates the body for a function." | ||
| 626 | ::= | ||
| 627 | match fn | ||
| 628 | case fn as FUNCTION(__) then functionPrototype(underscorePath(name), functionArguments, outVars)+";" | ||
| 629 | case fn as EXTERNAL_FUNCTION(__) then "static inline "+functionPrototype(underscorePath(name), funArgs, outVars)+";" | ||
| 630 | case fn as RECORD_CONSTRUCTOR(__) then error(sourceInfo(), "No records in embedded C yet") // functionBodyRecordConstructor(fn) | ||
| 631 | case fn as KERNEL_FUNCTION(__) then error(sourceInfo(), "No kernel functions in embedded C") | ||
| 632 | end functionDeclaration; | ||
| 633 | |||
| 634 | ✗ | template functionBodyRegularFunction(SimCodeFunction.Function fn) | |
| 635 | "Generates the body for a function." | ||
| 636 | ::= | ||
| 637 | match fn | ||
| 638 | case fn as FUNCTION(__) then | ||
| 639 | let fname = underscorePath(name) | ||
| 640 | let prototype = functionPrototype(fname, functionArguments, outVars) | ||
| 641 | let bodyPart = body |> stmt => statement(stmt) ; separator="\n" | ||
| 642 | |||
| 643 | << | ||
| 644 | <%prototype%> | ||
| 645 | { | ||
| 646 | <% /* No tail recursion in MISRA C? */ /* _tailrecursive: OMC_LABEL_UNUSED */%> | ||
| 647 | <% /* varInits */ %> | ||
| 648 | <%bodyPart%> | ||
| 649 | <% /* _return: OMC_LABEL_UNUSED */ /* No return label; avoid setjmp/longjmp in embedded C */ %> | ||
| 650 | <% /* outVarAssign */ %> | ||
| 651 | <% /* freeConstructedExternalObjects */ %> | ||
| 652 | <%match outVars | ||
| 653 | case v::_ then 'return <%varName(v)%>;' | ||
| 654 | else "" | ||
| 655 | %> | ||
| 656 | } | ||
| 657 | >> | ||
| 658 | end functionBodyRegularFunction; | ||
| 659 | |||
| 660 | ✗ | template functionBodyExternalFunction(SimCodeFunction.Function fn) | |
| 661 | "Generates the body for a function." | ||
| 662 | ::= | ||
| 663 | match fn | ||
| 664 | case fn as EXTERNAL_FUNCTION(language="C") then | ||
| 665 | let fname = underscorePath(name) | ||
| 666 | let prototype = functionPrototype(fname, funArgs, outVars) | ||
| 667 | let args = (extArgs |> arg => extArg(arg) ;separator=", ") | ||
| 668 | let varDecl = (outVars |> arg => '<%varType(arg)%> <%varName(arg)%>;' ;separator="\n") | ||
| 669 | let returnAssign = match extReturn case SIMEXTARG(cref=c) then '<%crefLocal(c)%> = ' | ||
| 670 | let returnStatement = (match outVars | ||
| 671 | case {} then "" | ||
| 672 | case VARIABLE(name=cref)::_ then 'return <%crefLocal(cref)%>;' | ||
| 673 | else error(sourceInfo(), "Not variable return")) | ||
| 674 | let varAssign = if outVars then | ||
| 675 | (listRest(outVars) |> var => '*out<%varName(var)%> = <%varName(var)%>;') | ||
| 676 | << | ||
| 677 | static inline <%prototype%> | ||
| 678 | { | ||
| 679 | <%varDecl%> | ||
| 680 | <%returnAssign%><%extName%>(<%args%>); | ||
| 681 | <%varAssign%> | ||
| 682 | <%returnStatement%> | ||
| 683 | } | ||
| 684 | >> | ||
| 685 | else error(sourceInfo(), "Unknown external language") | ||
| 686 | end functionBodyExternalFunction; | ||
| 687 | |||
| 688 | ✗ | template extArg(SimExtArg extArg) | |
| 689 | ::= | ||
| 690 | match extArg | ||
| 691 | case SIMEXTARG(isInput=true, isArray=false) then crefLocal(cref) | ||
| 692 | case SIMEXTARGEXP(type_=T_REAL()) | ||
| 693 | case SIMEXTARGEXP(type_=T_INTEGER()) | ||
| 694 | case SIMEXTARGEXP(type_=T_STRING()) | ||
| 695 | case SIMEXTARGEXP(type_=T_BOOL()) then daeExp(exp) | ||
| 696 | else error(sourceInfo(), "Unknown extArg") | ||
| 697 | end extArg; | ||
| 698 | |||
| 699 | ✗ | template functionPrototype(Text fname, list<Variable> fargs, list<Variable> outVars) | |
| 700 | ::= | ||
| 701 | let fargsStr = (fargs |> var => ', <%varType(var)%> <%varName(var)%>') | ||
| 702 | let outarg = (match outVars | ||
| 703 | case var::_ then (match var | ||
| 704 | case VARIABLE(__) then varType(var) | ||
| 705 | else error(sourceInfo(), "modelica_fnptr")) | ||
| 706 | else "void") | ||
| 707 | let outargs = if outVars then (listRest(outVars) |> var => ', <%varType(var)%> *out<%varName(var)%>') | ||
| 708 | '<%outarg%> <%fname%>(fmi2Component comp<%fargsStr%><%outargs%>)' | ||
| 709 | end functionPrototype; | ||
| 710 | |||
| 711 | ✗ | template varName(Variable var) | |
| 712 | ::= | ||
| 713 | match var | ||
| 714 | case VARIABLE(__) then crefLocal(name) | ||
| 715 | else error(sourceInfo(), "Not VARIABLE(__)") | ||
| 716 | end varName; | ||
| 717 | |||
| 718 | ✗ | template varType(Variable var) | |
| 719 | ::= | ||
| 720 | match var | ||
| 721 | case VARIABLE(__) then expTypeShort(ty) | ||
| 722 | else error(sourceInfo(), "Not VARIABLE(__)") | ||
| 723 | end varType; | ||
| 724 | |||
| 725 | ✗ | template literalExpConst(Exp e, Integer i0) | |
| 726 | ::= | ||
| 727 | match e | ||
| 728 | case SCONST(__) then 'static const char * const OMCLIT<%i0%> = "<%Util.escapeModelicaStringToCString(string)%>";' | ||
| 729 | else error(sourceInfo(), 'Literal expression: <%ExpressionDumpTpl.dumpExp(e,"\"")%>') | ||
| 730 | end literalExpConst; | ||
| 731 | |||
| 732 | ✗ | template callExternalObjectConstructors(ExtObjInfo extObjInfo) | |
| 733 | "Generates function in simulation file." | ||
| 734 | ::= | ||
| 735 | match extObjInfo | ||
| 736 | case EXTOBJINFO(__) then | ||
| 737 | (vars |> var as SIMVAR(initialValue=SOME(exp)) => | ||
| 738 | '<%cref(var.name)%> = <%daeExp(exp)%>;' | ||
| 739 | ;separator="\n") | ||
| 740 | end match | ||
| 741 | end callExternalObjectConstructors; | ||
| 742 | |||
| 743 | ✗ | template callExternalObjectDestructors(ExtObjInfo extObjInfo) | |
| 744 | "Generates function in simulation file." | ||
| 745 | ::= | ||
| 746 | match extObjInfo | ||
| 747 | case EXTOBJINFO(__) then | ||
| 748 | (vars |> var as SIMVAR(varKind=ext as EXTOBJ(__)) => | ||
| 749 | 'omc_<%underscorePath(ext.fullClassName)%>_destructor(threadData,<%cref(var.name)%>);' | ||
| 750 | ;separator="\n") | ||
| 751 | end match | ||
| 752 | end callExternalObjectDestructors; | ||
| 753 | |||
| 754 | annotation(__OpenModelica_Interface="codegen"); | ||
| 755 | end CodegenEmbeddedC; | ||
| 756 | |||
| 757 | // vim: filetype=susan sw=2 sts=2 | ||
| 758 |