diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/plciec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/plciec.h Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,688 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Code to be included into the code generated by the 4th stage. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + +#ifndef __PLCIEC_H +#define __PLCIEC_H + +//#include +#include "plc.h" +#include +#include + + + +/* function that generates an IEC runtime error */ +void IEC_error(void) { + /* TODO... */ + fprintf(stderr, "IEC 61131-3 runtime error.\n"); + exit(1); +} + + + +typedef bool BOOL; +#define TRUE true +#define FALSE false + +typedef i8 SINT; +typedef i16 INT; +typedef i32 DINT; +typedef i64 LINT; + +typedef u8 USINT; +typedef u16 UINT; +typedef u32 UDINT; +typedef u64 ULINT; + +typedef u8 BYTE; +typedef u16 WORD; +typedef u32 DWORD; +typedef u64 LWORD; + +typedef f32 REAL; +typedef f64 LREAL; + + + + + + +/*********************************************/ +/* TIME AND DATE data trypes */ +/*********************************************/ + +/* NOTE: All the time and date data types use a struct timespec + * internally to store the time and date. This is so as to ease all the + * operations (add, subtract, multiply and division) the standard defines + * on these same data types. + * However, in order to ease the implementation of the comparison operators + * (==, =>, <=, <, >, <>), the two elements in the timespec structure + * must be handled in such a way as to guarantee the following: + * - The stored time is always the result of the operation tv_sec + tv_nsec*1e-9 + * - tv_sec and tv_nsec will always have the same sign + * (i.e. either both positive or both negative) + * - tv_nsec always holds a value in the range ]-1, +1[ seconds. + * (note that -1 and +1 are excluded form the range) + */ + + +/* NOTE: According to the C++ standard, the result of the % and / operations is implementation dependent + * when the at least one of the operands is negative! However, whatever the result of the operations, we are + * guaranteed that (a/b)*b + (a%b) is always equal to a. + * This means that, even knowing that both tv_sec and tv_sec always have the same sign (we make it so this is true), + * we must still re-normailze the result for both the addition and subtraction operations! + */ + +static inline void __normalizesign_timespec (struct timespec *ts) { + if ((ts->tv_sec > 0) && (ts->tv_nsec < 0)) { + ts->tv_sec--; + ts->tv_nsec += 1000000000; + } + if ((ts->tv_sec < 0) && (ts->tv_nsec > 0)) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000; + } +} + + +static inline struct timespec __add_timespec (const struct timespec &t1, const struct timespec &t2) { + /* NOTE the following sum works correctly because a long can hold a litle more than 2 seconds expressed in nano seconds! */ + long nsec; + nsec = t1.tv_nsec + t2.tv_nsec; + struct timespec ts; + ts.tv_sec = t1.tv_sec + t2.tv_sec + (nsec / 1000000000); + ts.tv_nsec = nsec % 1000000000; + + __normalizesign_timespec(&ts); + +return ts; +} + + +static inline struct timespec __sub_timespec (const struct timespec &t1, const struct timespec &t2) { + /* NOTE the following subtraction works correctly because a long can hold a litle more than 2 seconds expressed in nano seconds! */ + long nsec = t1.tv_nsec - t2.tv_nsec; + struct timespec ts; + ts.tv_sec = t1.tv_sec - t2.tv_sec + nsec / 1000000000; + ts.tv_nsec = nsec % 1000000000; + + __normalizesign_timespec(&ts); + + return ts; +} + + +static inline struct timespec __mul_timespec (const struct timespec &t1, const long double value) { +#if 0 + /* A simple implementation that risks reducing the precision of the TIME value */ + long double sec_d1 = t1.tv_nsec / (long double)1e9 * value; + long double sec_d2 = t1.tv_sec * value; + long int sec = (long int)truncl(sec_d1 + sec_d2); + struct timespec ts; + ts.tv_sec = sec; + ts.tv_nsec = (long int)((sec_d1 + sec_d2 - sec)*1e9); + return ts; +#else + /* A more robust implementation that reduces the loss of precision of the TIME value */ + /* NOTE: The following assumes that the value stored in tv_nsec is never larger than 1 sec + * and is also based on the fact that tv_nsec can safely store slighlty more thanb 2 sec. + */ + long double sec_d1 = t1.tv_nsec / (long double)1e9 * value; + long double sec_d2 = t1.tv_sec * value; + long int sec1 = (long int)sec_d1; + long int sec2 = (long int)sec_d2; + struct timespec ts; + ts.tv_sec = sec1 + sec2; + ts.tv_nsec = (long int)(((sec_d1 - sec1) + (sec_d2 - sec2))*1e9); + /* re-normalize the value of tv_nsec */ + /* i.e. guarantee that it falls in the range ]-1, +1[ seconds. */ + if (ts.tv_nsec >= 1000000000) {ts.tv_nsec -= 1000000000; ts.tv_sec += 1;} + if (ts.tv_nsec <= -1000000000) {ts.tv_nsec += 1000000000; ts.tv_sec -= 1;} + /* We don't need to re-normalize the sign, since we are guaranteed that tv_sec and tv_nsec + * will still both have the same sign after being multiplied by the same value. + */ + return ts; +#endif +} + +/* Helper Macro for the comparison operators... */ +#define __compare_timespec(CMP, t1, t2) ((t1.tv_sec == t2.tv_sec)? t1.tv_nsec CMP t2.tv_nsec : t1.tv_sec CMP t2.tv_sec) + + + +/* Some necessary forward declarations... */ +class TIME; +class TOD; +class DT; +class DATE; + +typedef struct timespec __timebase_t; + + + +class TIME{ + private: + /* private variable that contains the value of time. */ + /* NOTE: The stored value is _always_ (time.tv_sec + time.tv_nsec), + no matter whether tv_sec is positive or negative, or tv_nsec is positive or negative. + */ + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + TIME (void) {time.tv_sec = 0; time.tv_nsec = 0;} + TIME (__timebase_t time) {this->time = time;} + TIME (const TIME &time) {this->time = time.time;} /* copy constructor */ + TIME(int sign, double mseconds, double seconds=0, double minutes=0, double hours=0, double days=0) { + /* sign is 1 for positive values, -1 for negative time... */ + long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3; + if (sign >= 0) sign = 1; else sign = -1; + time.tv_sec = sign * (long int)total_sec; + time.tv_nsec = sign * (long int)((total_sec - time.tv_sec)*1e9); + } + + /* used in plciec.cc to set the value of the __CURRENT_TIME variable without having to call a + * TIME((__timebase_t time) constructor followed by the copy constructor. + */ + void operator= (__timebase_t time) {this->time = time;} + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator+ (const TIME &time) {return TIME(__add_timespec(this->time, time.time));} + TIME operator- (const TIME &time) {return TIME(__sub_timespec(this->time, time.time));} + + friend TOD operator+ (const TIME &time, const TOD &tod); + friend TOD operator+ (const TOD &tod, const TIME &time); + friend TOD operator- (const TOD &tod, const TIME &time); + + friend DT operator+ (const TIME &time, const DT &dt); + friend DT operator+ (const DT &dt, const TIME &time); + friend DT operator- (const DT &dt, const TIME &time); + + friend TIME operator* (const TIME &time, const long double value); + friend TIME operator* (const long double value, const TIME &time); + friend TIME operator/ (const TIME &time, const long double value); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const TIME &time) {return __compare_timespec(>, this->time, time.time);} + BOOL operator>= (const TIME &time) {return __compare_timespec(>=, this->time, time.time);} + BOOL operator< (const TIME &time) {return __compare_timespec(<, this->time, time.time);} + BOOL operator<= (const TIME &time) {return __compare_timespec(<=, this->time, time.time);} + BOOL operator== (const TIME &time) {return __compare_timespec(==, this->time, time.time);} + BOOL operator!= (const TIME &time) {return !__compare_timespec(==, this->time, time.time);} +}; + + + +/* Time of Day */ +class TOD { + private: + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + TOD (void) {time.tv_sec = 0; time.tv_nsec = 0;} + TOD (__timebase_t time) {this->time = time;} + TOD (const TOD &tod) {this->time = tod.time;} /* copy constructor */ + TOD (double seconds, double minutes=0, double hours=0) { + long double total_sec = (hours*60 + minutes)*60 + seconds; + time.tv_sec = (long int)total_sec; + time.tv_nsec = (long int)((total_sec - time.tv_sec)*1e9); + } + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator- (const TOD &tod) {return TIME(__sub_timespec(this->time, tod.time));} + + friend TOD operator+ (const TIME &time, const TOD &tod); + friend TOD operator+ (const TOD &tod, const TIME &time); + friend TOD operator- (const TOD &tod, const TIME &time); + + /* The following operation is not in the standard, + * but will ease the implementation of the default function CONCAT_DATE_TOD + */ + friend DT operator+ (const DATE &date, const TOD &tod); + friend DT operator+ (const TOD &tod, const DATE &date); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const TOD &tod) {return __compare_timespec(>, this->time, tod.time);} + BOOL operator>= (const TOD &tod) {return __compare_timespec(>=, this->time, tod.time);} + BOOL operator< (const TOD &tod) {return __compare_timespec(<, this->time, tod.time);} + BOOL operator<= (const TOD &tod) {return __compare_timespec(<=, this->time, tod.time);} + BOOL operator== (const TOD &tod) {return __compare_timespec(==, this->time, tod.time);} + BOOL operator!= (const TOD &tod) {return !__compare_timespec(==, this->time, tod.time);} +}; + + + +//typedef DATE; +class DATE { + private: + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + DATE (void) {time.tv_sec = 0; time.tv_nsec = 0;} + DATE (__timebase_t time) {this->time = time;} + DATE (const DATE &date) {this->time = date.time;} /* copy constructor */ + DATE (int day, int month, int year) { + struct tm broken_down_time; + + broken_down_time.tm_sec = 0; + broken_down_time.tm_min = 0; + broken_down_time.tm_hour = 0; + broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ + broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ + broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ + + time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ + + if ((time_t)(-1) == epoch_seconds) + IEC_error(); + + time.tv_sec = epoch_seconds; + time.tv_nsec = 0; + } + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator- (const DATE &date) {return TIME(__sub_timespec(this->time, date.time));} + /* The following operation is not in the standard, + * but will ease the implementation of the default function CONCAT_DATE_TOD + */ + friend DT operator+ (const DATE &date, const TOD &tod); + friend DT operator+ (const TOD &tod, const DATE &date); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const DATE &date) {return __compare_timespec(>, this->time, date.time);} + BOOL operator>= (const DATE &date) {return __compare_timespec(>=, this->time, date.time);} + BOOL operator< (const DATE &date) {return __compare_timespec(<, this->time, date.time);} + BOOL operator<= (const DATE &date) {return __compare_timespec(<=, this->time, date.time);} + BOOL operator== (const DATE &date) {return __compare_timespec(==, this->time, date.time);} + BOOL operator!= (const DATE &date) {return !__compare_timespec(==, this->time, date.time);} +}; + + + + + +class DT { + private: + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + DT (void) {time.tv_sec = 0; time.tv_nsec = 0;} + DT (__timebase_t time) {this->time = time;} + DT (const DT &dt) {this->time = dt.time;} /* copy constructor */ + DT (double seconds, double minutes, double hours, int day, int month, int year) { + long double total_sec = (hours*60 + minutes)*60 + seconds; + time.tv_sec = (long int)total_sec; + time.tv_nsec = (long int)((total_sec - time.tv_sec)*1e9); + + struct tm broken_down_time; + broken_down_time.tm_sec = 0; + broken_down_time.tm_min = 0; + broken_down_time.tm_hour = 0; + broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ + broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ + broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ + + time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ + if ((time_t)(-1) == epoch_seconds) + IEC_error(); + + time.tv_sec += epoch_seconds; + if (time.tv_sec < epoch_seconds) + /* since the TOD is always positive, if the above happens then we had an overflow */ + IEC_error(); + } + + /* Helpers to conversion operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + DATE __to_DATE(void) { +#if 0 + /* slow version */ + struct tm broken_down_time; + time_t seconds = time.tv_sec; + if (NULL == gmtime_r(seconds, &broken_down_time)) /* get the UTC (GMT) broken down time */ + IEC_error(); + return DATE(broken_down_time.tm_mday, broken_down_time.tm_mon, broken_down_time.tm_year); +#else + /* Faster version, based on the fact that the date will always be a multiple of 60*60*24 seconds, + * and that the value of tv_nsec falls in the range ]-1, +1[ + */ + /* The above is true since the Unix function mktime() seems to ignore all leap seconds! */ + struct timespec date_time = {time.tv_sec - (time.tv_sec % (24*60*60)), 0}; + return DATE(date_time); +#endif + } + + TOD __to_TOD(void) { +#if 0 + /* slow version */ + struct tm broken_down_time; + time_t seconds = time.tv_sec; + if (NULL == gmtime_r(seconds, &broken_down_time)) /* get the UTC (GMT) broken down time */ + IEC_error(); + return TOD(broken_down_time.tm_sec, broken_down_time.tm_min, broken_down_time.tm_hour); +#else + /* Faster version, based on the fact that the date will always be a multiple of 60*60*24 seconds + * and that the value of tv_nsec falls in the range ]-1, +1[ + */ + /* The above is true since the Unix function mktime() seems to ignore all leap seconds! */ + struct timespec time_time = {time.tv_sec % (24*60*60), time.tv_nsec}; + return TOD(time_time); +#endif + } + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator- (const DT &dt) {return TIME(__sub_timespec(this->time, dt.time));} + + friend DT operator+ (const TIME &time, const DT &dt); + friend DT operator+ (const DT &dt, const TIME &time); + friend DT operator- (const DT &dt, const TIME &time); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const DT &dt) {return __compare_timespec(>, this->time, dt.time);} + BOOL operator>= (const DT &dt) {return __compare_timespec(>=, this->time, dt.time);} + BOOL operator< (const DT &dt) {return __compare_timespec(<, this->time, dt.time);} + BOOL operator<= (const DT &dt) {return __compare_timespec(<=, this->time, dt.time);} + BOOL operator== (const DT &dt) {return __compare_timespec(==, this->time, dt.time);} + BOOL operator!= (const DT &dt) {return !__compare_timespec(==, this->time, dt.time);} +}; + + +/* The operations on time and data types... */ +TOD operator+ (const TIME &time, const TOD &tod) {return TOD(__add_timespec(tod.time, time.time));}; +TOD operator+ (const TOD &tod, const TIME &time) {return TOD(__add_timespec(tod.time, time.time));}; +TOD operator- (const TOD &tod, const TIME &time) {return TOD(__sub_timespec(tod.time, time.time));}; + +DT operator+ (const TIME &time, const DT &dt) {return DT(__add_timespec(dt.time, time.time));}; +DT operator+ (const DT &dt, const TIME &time) {return DT(__add_timespec(dt.time, time.time));}; +DT operator- (const DT &dt, const TIME &time) {return DT(__sub_timespec(dt.time, time.time));}; + +TIME operator* (const TIME &time, const long double value) {return TIME(__mul_timespec(time.time, value));} +TIME operator* (const long double value, const TIME &time) {return TIME(__mul_timespec(time.time, value));} +TIME operator/ (const TIME &time, const long double value) {return TIME(__mul_timespec(time.time, 1.0/value));} + +/* The following operation is not in the standard, + * but will ease the implementation of the default function CONCAT_DATE_TOD + */ +DT operator+ (const DATE &date, const TOD &tod) {return DT(__add_timespec(date.time, tod.time));}; +DT operator+ (const TOD &tod, const DATE &date) {return DT(__add_timespec(date.time, tod.time));}; + + +/* global variable that will be used to implement the timers TON, TOFF and TP */ +extern TIME __CURRENT_TIME; + + + +//typedef STRING; +//typedef WSTRING; + + + +typedef union __IL_DEFVAR_T { + BOOL BOOLvar; + + SINT SINTvar; + INT INTvar; + DINT DINTvar; + LINT LINTvar; + + USINT USINTvar; + UINT UINTvar; + UDINT UDINTvar; + ULINT ULINTvar; + + BYTE BYTEvar; + WORD WORDvar; + DWORD DWORDvar; + LWORD LWORDvar; + + REAL REALvar; + LREAL LREALvar; + + /* NOTE: since the TIME, DATE, ... classes all have constructors, + * C++ does not allow them to be used as members of a union. + * The workaround is to use a base data type (in this case __timebase_t) that + * contains all the internal data these classes require, and then add an operator + * member function to each class that allows it to be converted to that same base data type, + * acompanied by a constructor using that data type. + */ + /* + TIME TIMEvar; + TOD TODvar; + DT DTvar; + DATE DATEvar; + */ + __timebase_t TIMEvar; + __timebase_t TODvar; + __timebase_t DTvar; + __timebase_t DATEvar; +} __IL_DEFVAR_T; + /*TODO TODO TODO TODO TODO TODO TODO TODO TODO + * How do we add support for the possibility of storing + * data values of derived data types into the default register, + * to be later used for calling functions, stroing in another + * variable, etc...? + * + * For example: + * TYPE + * point_t : STRUCT + * x : INT; + * y : INT; + * END_STRUCT; + * END_TYPE + * + * VAR p1, p2, p3 : point_t; + * + * LD p1 + * ST p2 + * + * + * We could do it with a pointer to void, that would contain not + * the value itself, but rather the address in which the value + * is currently stored. + * For example, we could add a + * void *generic_ptr + * to this union, and then have the above LD and ST instructions + * converted to: + * __IL_DEFVAR.generic_ptr = (void *)(&p1); + * p2 = *((point_t *)__IL_DEFVAR.generic_ptr); + * + * Unfortunately the above will only work as long as the p1 variable + * does not get a chance to change its value before the default register + * gets loaded with something esle (and therefore the value is no + * longer needed). + * Additionally, a scenario where the value of p1 may change before the + * default register gets a new value is if p1 is used in a function block + * call for an output parameter! + * For example: + * + * LD p1 + * CAL funcblock( + * param1 => p1 + * ) + * ST p2 + * + * In the above scenario, p1 gets a new value when the function block + * funcblock is called. When we get to copy the default register to + * p2, we will no longer be copying the value that got stored in the default + * register when we did 'LD p1', but rather the value returned by the + * function block call!!! + * + * How the do we implement this??? + * We will probably need to declare a default variable of the correct data + * type whenever we get these values stored to the default register. + * For example + * LD p1 + * ST p2 + * + * would be converted to: + * union { + * point_tvar point_t; + * } __IL_DEFVAR_special ; + * + * __IL_DEFVAR_special.point_tvar = p1; + * p2 = __IL_DEFVAR_special.point_tvar; + * + * The above requires that we iterate through the whole Instruction list + * before we start the conversion, in order to first determine if we need + * to declare that new variable for the default register. + * + * Since we have to do this, it would probaly be a better idea to simply + * do away with the __IL_DEFVAR_T data type we declare here, and + * declare the __IL_DEFVAR at the begining of each IL code segment + * with all the data types that get used in that segment! + */ + + +/* Names start with double underscore so as not to clash with + * names in ST or IL source code! Names including a double underscore are + * ilegal under IL and ST! + */ + +/* This is an abstract base class, that cannot be instantiated... */ +template class __ext_ref_c { + public: + virtual void operator= (value_type value) = 0; + virtual operator value_type(void) = 0; +}; + + + + + + +/* Names start with double underscore so as not to clash with + * names in ST or IL source code! Names including a double underscore are + * ilegal under IL and ST! + */ +template class __ext_element_c + : public __ext_ref_c { +//{ + + private: + value_type value; + + public: + virtual void operator= (value_type value) { + this->value = value; + } + + virtual operator value_type(void) { + return value; + } + + __ext_element_c(void) {} + + __ext_element_c(value_type value) { + this->value = value; + } +}; + + +/* Names start with double underscore so as not to clash with + * names in ST or IL source code! Names including a double underscore are + * ilegal under IL and ST! + */ +template class __plc_pt_c + : public __ext_ref_c { + + private: + plc_pt_t plc_pt; + bool valid_plc_pt; + + private: + void init_name(const char *pt_name) { + /* assume error! */ + valid_plc_pt = false; + plc_pt = plc_pt_by_name(pt_name); + if (plc_pt.valid == 0) { + plc_pt = plc_pt_null(); + return; + } + /* We can't have this check here, otherwise the boolean variables won't work correctly, + * since MatPLC uses 1 bit for boolean variables, whereas g++ uses 8 bits. + */ + /* + if (plc_pt_len(plc_pt) != size) { + plc_pt = plc_pt_null(); + return; + } + */ + valid_plc_pt = true; + } + + public: + virtual void operator= (value_type value) { + plc_set(plc_pt, *((u32 *)&value)); + } + + virtual operator value_type(void) { + u32 tmp_val = plc_get(plc_pt); + return *((value_type *)&tmp_val); + } + + __plc_pt_c(const char *pt_name) { + init_name(pt_name); + } + + __plc_pt_c(const char *pt_name, value_type init_value) { + init_name(pt_name); + *this = init_value; + } + + bool valid(void) {return valid_plc_pt;} +}; + + + +#define DEFAULT_MODULE_NAME "iec" + + + +#endif /* __PLCIEC_H */ + +