diff --git a/Lego/ContactInput.pm b/Lego/ContactInput.pm new file mode 100644 index 0000000..9f6ab98 --- /dev/null +++ b/Lego/ContactInput.pm @@ -0,0 +1,26 @@ +# Esterel input signal for contact sensor +#---------------------------------------- +package ContactInput; +require EsterelInput; +@ISA = (EsterelInput); +sub new { + # args: input_name type Lego_sensor + my $proto = shift; + my $self = EsterelInput->new(@_); + bless $self, $proto; + return $self; +} + +sub PrintCall { + my $self = shift; + local(*OUT) = shift; + my $fnc = $self->FunctionName(); + my $sensor = $self->LegoSensor(); + print OUT <{NAME} = shift; + $self->{ARGS} = shift; + $self->{TYPE} = shift; + $self->{CODE} = shift; + bless $self, $proto; + return $self; +} + +sub Name { + my $self = shift; + return $self->{NAME}; +} + +sub Args { + my $self = shift; + return $self->{ARGS}; +} + + +sub Type { + my $self = shift; + return $self->{TYPE}; +} + +sub Check { + my($self, $fnc, $args, $type) = @_; + + CHECK: { + last CHECK if ( GetType($type) ne $self->Type() ); + last CHECK if (@$args != @{$self->Args()}); + for (my $i = 0; $i < @$args; $i++) { + last CHECK if GetType($args->[i]) ne $self->Args()->[$i]; + } + return 1; + } + my $str_args = + warn("*** Bad function prototype. Must be:\n ", + $self->Name(), '(', (join (', ', @{$self->Args()})), ') : ', + $self->Type(), "\n"); + + return 0; +} + +sub PrintCode { + my $self = shift; + local(*OUT) = shift; + print OUT $self->{CODE}; +} +1; diff --git a/Lego/EsterelInput.pm b/Lego/EsterelInput.pm new file mode 100644 index 0000000..8713dee --- /dev/null +++ b/Lego/EsterelInput.pm @@ -0,0 +1,35 @@ +# Esterel input signal class +#--------------------------- +package EsterelInput; + +require EsterelSignal; + +@ISA = (EsterelSignal); + +sub new { + # args: input_name type Lego_sensor + my $proto = shift; + my $self = EsterelSignal->new($_[0], $_[1]); + $self->{SENSOR} = $_[2]; + bless $self, $proto; + return $self; +} + +sub LegoSensor { + my $self = shift; + return $self->{SENSOR}; +} + +sub FunctionName { + # Builds Esterel input function name + my $self = shift; + return $self->ModuleName() . '_I_' . $self->Name(); +} + +sub PrintInitialization { + # nothing +} + +sub PrintCall { +} +1; diff --git a/Lego/EsterelOutput.pm b/Lego/EsterelOutput.pm new file mode 100644 index 0000000..2bfd4ad --- /dev/null +++ b/Lego/EsterelOutput.pm @@ -0,0 +1,35 @@ +# Esterel output signal +#---------------------- +package EsterelOutput; +require EsterelSignal; +@ISA = (EsterelSignal); +sub new { + # args: name type + my $proto = shift; + my $self = EsterelSignal->new(@_); + bless $self, $proto +} + +sub FunctionName { + my $self = shift; + return $self->ModuleName() . '_O_' . $self->Name(); +} + +sub PrintCode { + my $self = shift; + local(*OUT) = shift; + my $lego_api = lc $self->Name(); + my $fnc = $self->FunctionName(); + my $type = $self->Type(); + print OUT <{NAME} = shift; + $self->{TYPE} = shift; + $self->{VARIDX} = undef; + bless $self, $proto; + return $self; +} + +sub Name { + my $self = shift; + return $self->{NAME}; +} + +sub Type { + my $self = shift; + return $self->{TYPE}; +} + + +sub SetVarIndex { + my $self = shift; + $self->{VARIDX} = shift; +} + +sub VarIndex { + my $self = shift; + return $self->{VARIDX}; +} + +# Check type. +# argument : ref array of variable types +sub CheckType { + my $self = shift; + my $var_to_type = shift; + + ERROR: { + if ( $self->Type() eq 'pure' ) { + last ERROR if defined ($self->VarIndex()) + } + else { + last ERROR if $self->Type() ne $var_to_type->[$self->VarIndex()]; + } + return 0; + } + + warn('*** signal ', $self->Name(), ' must be ', $self->Type(), "\n"); + return 1; +} + +sub PrintCode { +} +1; diff --git a/Lego/EsterelType.pm b/Lego/EsterelType.pm new file mode 100644 index 0000000..6ba8445 --- /dev/null +++ b/Lego/EsterelType.pm @@ -0,0 +1,17 @@ +package EsterelType; +require Exporter; + +@ISA = qw(Exporter); + +@EXPORT = (GetType); + +my %Type = ('$0' => 'boolean', + '$1' => 'integer', + '$2' => 'string', + '$3' => 'float', + '$4' => 'double'); + +sub GetType { + return (exists $Type{$_[0]} ? $Type{$_[0]} : "'$_[0]'"); +} +1; diff --git a/Lego/LegoDefs.pm b/Lego/LegoDefs.pm new file mode 100644 index 0000000..41a0c17 --- /dev/null +++ b/Lego/LegoDefs.pm @@ -0,0 +1,141 @@ +package LegoDefs; + +require EsterelSignal; +require ContactInput; +require MotorDirOutput; +require MotorSpeedOutput; +require LightInput; +require LightSensor; +require LightOutput; +require EsterelFunction; + +require Exporter; +@ISA = (Exporter); +@EXPORT =qw(true false %ValidInputs %ValidOutputs %ValidConstants %ValidFunctions); + +sub true { + return 1; +} + +sub false { + return 0; +} + +# Valid input signal. Mapped onto Legos API. +# Hash table. Values are signal objects. +#------------------------------------------- +%ValidInputs = ( 'TOUCH_1' => ContactInput->new('TOUCH_1', 'pure', 'SENSOR_1'), + 'TOUCH_2' => ContactInput->new('TOUCH_2', 'pure', 'SENSOR_2'), + 'TOUCH_3' => ContactInput->new('TOUCH_3', 'pure', 'SENSOR_3'), + 'LIGHT_LOW_1' => LightInput->new('LIGHT_LOW_1', + 'pure', + 'SENSOR_1', + 'LIGHT_1', + 0, + true), + 'LIGHT_HIGH_1' => LightInput->new('LIGHT_HIGH_1', + 'pure', + 'SENSOR_1', + 'LIGHT_1', + 0, + false), + 'LIGHT_1_VALUE' => LightSensor->new('LIGHT_1_VALUE', + 'integer', + 'SENSOR_1', + 'LIGHT_1', + 0), + 'LIGHT_LOW_2' => LightInput->new('LIGHT_LOW_2', + 'pure', + 'SENSOR_2', + 'LIGHT_2', + 1, + true), + 'LIGHT_HIGH_2' => LightInput->new('LIGHT_HIGH_2', + 'pure', + 'SENSOR_2', + 'LIGHT_2', + 1, + false), + 'LIGHT_2_VALUE' => LightSensor->new('LIGHT_2_VALUE', + 'integer', + 'SENSOR_2', + 'LIGHT_2', + 1), + 'LIGHT_LOW_3' => LightInput->new('LIGHT_LOW_3', + 'pure', + 'SENSOR_3', + 'LIGHT_3', + 2, + true), + 'LIGHT_HIGH_3' => LightInput->new('LIGHT_HIGH_3', + 'pure', + 'SENSOR_3', + 'LIGHT_3', + 2, + false), + 'LIGHT_3_VALUE' => LightSensor->new('LIGHT_3_VALUE', + 'integer', + 'SENSOR_3', + 'LIGHT_3', + 2) + ); + +# Valid output signal. Mapped onto Legos API +# Values are signal objects. +#------------------------------------------- +%ValidOutputs = ( 'MOTOR_A_DIR' => MotorDirOutput->new('MOTOR_A_DIR', 'integer'), + 'MOTOR_A_SPEED' => MotorSpeedOutput->new('MOTOR_A_SPEED', + 'integer'), + 'MOTOR_B_DIR' => MotorDirOutput->new('MOTOR_B_DIR', 'integer'), + 'MOTOR_B_SPEED' => MotorSpeedOutput->new('MOTOR_B_SPEED', + 'integer'), + 'MOTOR_C_DIR' => MotorDirOutput->new('MOTOR_C_DIR', 'integer'), + 'MOTOR_C_SPEED' => MotorSpeedOutput->new('MOTOR_C_SPEED', + 'integer'), + 'CPUTS' => EsterelOutput->new('CPUTS', 'string'), + 'SET_LIGHT_1_THRESHHOLD' + => LightOutput->new('SET_LIGHT_1_THRESHHOLD', + 'integer', + 0), + 'SET_LIGHT_2_THRESHHOLD' + => LightOutput->new('SET_LIGHT_2_THRESHHOLD', + 'integer', + 1), + 'SET_LIGHT_3_THRESHHOLD' + => LightOutput->new('SET_LIGHT_3_THRESHHOLD', + 'integer', + 2)); + +# Constants for motor direction +#------------------------------ +%ValidConstants = ( MOTOR_OFF => 0, + MOTOR_FWD => 1, + MOTOR_REV => 2, + MOTOR_BRAKE => 3, + MAX_SPEED => 255, + TICKS_PER_SECOND => 100, + DEFAULT_LIGHT_THRESHHOLD => 50 + ); + +%ValidFunctions = ( CHANGE_MOTOR_DIR => EsterelFunction->new('CHANGE_MOTOR_DIR', + ['integer'], + 'integer', +<new(@_); + $self->{IS_LOW} = $_[5]; + bless $self, $proto; + return $self; +} + +sub PrintCall { + my $self = shift; + local(*OUT) = shift; + my $fnc = $self->FunctionName(); + my $value = $self->LightValue(); + my $index = $self->ThreshholdIndex(); + my $op = ($self->{IS_LOW} ? '<' : '>'); + print OUT <LegoSensor()}; + $SensorInitialized{$self->LegoSensor()} = 1; + printf OUT " ds_active(&%s);\n", $self->LegoSensor(); +} +1; diff --git a/Lego/LightOutput.pm b/Lego/LightOutput.pm new file mode 100644 index 0000000..43e32c3 --- /dev/null +++ b/Lego/LightOutput.pm @@ -0,0 +1,37 @@ +# Specialized output for light threshhold +package LightOutput; +require EsterelOutput; + +@ISA = (EsterelOutput); +sub new { + # args: name type index + my $proto = shift; + my $self = EsterelOutput->new(@_[0..1]); + $self->{INDEX} = $_[2]; + bless $self, $proto +} + +sub ThreshholdIndex { + my $self = shift; + return $self->{INDEX}; +} + +sub PrintCode { + my $self = shift; + local(*OUT) = shift; + my $fnc = $self->FunctionName(); + my $type = $self->Type(); + my $index = $self->ThreshholdIndex(); + + print OUT <new(@_); + bless $self, $proto; + return $self; +} + +sub FunctionName { + # Builds Esterel sensor function name + my $self = shift; + return $self->ModuleName() . '_S_' . $self->Name(); +} + + +sub PrintCode { + my $self = shift; + local(*OUT) = shift; + my $fnc = $self->FunctionName(); + my $value = $self->LightValue(); + my $type = $self->Type(); + print OUT <new(@_[0..2]); + $self->{VALUE} = $_[3]; + $self->{INDEX} = $_[4]; + bless $self, $proto; + return $self; +} + +sub ThreshholdIndex { + my $self = shift; + return $self->{INDEX}; +} + +sub LightValue { + my $self = shift; + return $self->{VALUE}; +} +1; diff --git a/Lego/MotorDirOutput.pm b/Lego/MotorDirOutput.pm new file mode 100644 index 0000000..7ff27a3 --- /dev/null +++ b/Lego/MotorDirOutput.pm @@ -0,0 +1,17 @@ +# Specialized output for motor direction +package MotorDirOutput; +require EsterelOutput; + +@ISA = (EsterelOutput); +sub new { + # args: name type + my $proto = shift; + my $self = EsterelOutput->new(@_); + bless $self, $proto +} + +sub PrintCode { + my $self = shift; + $self->SUPER::PrintCode(@_); +} +1; diff --git a/Lego/MotorSpeedOutput.pm b/Lego/MotorSpeedOutput.pm new file mode 100644 index 0000000..8184678 --- /dev/null +++ b/Lego/MotorSpeedOutput.pm @@ -0,0 +1,17 @@ +# Specialized output for motor speed +package MotorSpeedOutput; +require EsterelOutput; + +@ISA = (EsterelOutput); +sub new { + # args: name type + my $proto = shift; + my $self = EsterelOutput->new(@_); + bless $self, $proto +} + +sub PrintCode { + my $self = shift; + $self->SUPER::PrintCode(@_); +} +; diff --git a/Lego/scLego b/Lego/scLego new file mode 100644 index 0000000..421e2eb --- /dev/null +++ b/Lego/scLego @@ -0,0 +1,570 @@ +# -*- perl -*- +eval 'exec perl $0 "$@"' + if 0; + +use lib "./Lego"; +use lib "$ENV{ESTEREL}/lib/Lego"; + + +use LegoDefs; + +use EsterelType; + +require EsterelSignal; +require ContactInput; +require MotorDirOutput; +require MotorSpeedOutput; +require LightInput; +require LightSensor; +require LightOutput; +require EsterelFunction; + +#=========================================================== + +#================== +# Arguments parsing +#================== +$dir_name = "."; +$base_name = "scLego"; + +while ( ($_ = $ARGV[0]) =~ /^-/ ) { + push(@esterel_args, $_); + ARGS: + { + /-D$/ && do { # extract dirname + shift; + $dir_name = $ARGV[0]; + push(@esterel_args, $dir_name); + last ARGS; + }; + + /-B$/ && do { # extract basename + shift; + $base_name = $ARGV[0]; + push(@esterel_args, $base_name); + last ARGS; + }; + + /-simul$/ && do { # simulation code ? + $simul_opt = 1; + last ARGS; + }; + + /-v$/ && do { + $verbose_opt = 1; + last ARGS; + }; + + exit 0 if /-access$/; + /-info$/ && do { + print STDERR <<'INFO'; +--- [sc|ssc|oc]Lego : Lego C code generator + version : p5 + author : Xavier.Fornari@sophia.inria.fr +INFO + ; + exit 0; + }; + + } + shift; +} +if ( @ARGV > 0 ) { + # last argument + $input_file = $ARGV[0]; +} + +else { + die "*** Missing input file\n"; +} + + +#======================================== +# Extracting information from main module +#++++++++++++++++++++++++++++++++++++++++ + +# open input file and parse it +# if MOTOR_* constants are present without their default values, then +# patch the file to add the value => it suppresses the .h generation, if +# it is created only because these constants are undefined + +# input file suffix +$input_file =~ /\.(ssc|sc|oc)/; +$suffix = $1; + +$error = 0; + +open(INPUT, $input_file) || die "*** Cannot open '$input_file'\n"; + +$tmp = "/tmp/lego$$.$suffix"; +open(OUTPUT, ">$tmp") || die "*** Cannot open '$tmp'\n"; + +$external_objects = 0; + +while () { + + PARSE: { + + # found number of external objects + /(?:constants|procedures|functions|tasks)\s*:\s*(\d+)/ && do { + $external_objects += $1; + }; + + # Esterel main module name + m/^module:\s*(\w+)/ && do { + EsterelSignal->SetModuleName($1); + next PARSE; + }; + + # put correct constant values for MOTOR_* + if ( /constants:/ .. /end:/ ) { + next PARSE unless /^\d+\s*:\s*(\w+)/; + + if ( ! exists $ValidConstants{$1} ) { + # not a known constants; + $external_objects-- if /value:/; + next PARSE; + } + + $constant = $1; + m/\$1/ || do { + warn "*** Constant '$constant' is not integer\n"; + $error++; + next PARSE; + }; + + my $value; + $value = ( /value:\s*\#(\d+)/ ? $1 : undef ); + CONSTANTS: { + + $constant eq 'TICKS_PER_SECOND' && do { + if (defined $value ) { + # save user value + # normalize + if ( $value > 1000 ) { + warn ("--- TICKS_PER_SECOND > 1000 : set to 1000\n"); + } + if ( $value < 1 ) { + warn ("--- TICKS_PER_SECOND < 1 : set to 1\n"); + } + $ValidConstants{$constant} = $value; + } + next CONSTANTS; + }; + + $constant eq 'DEFAULT_LIGHT_THRESHHOLD' && do { + if (defined $value ) { + # save user value + $ValidConstants{$constant} = $value; + } + next CONSTANTS; + }; + + # check if value present and correct + defined $value && do { + if ($value != $ValidConstants{$constant} ) { + $error++; + warn ("*** Constant $constant must have value ", + $ValidConstants{$constant}, " (but it may be omitted)\n"); + } + next CONSTANTS; + }; + + # !! patch intermediate code insert correct value + s/\$1/\$1 value: \#$ValidConstants{$constant}/; + } + + $external_objects--; # known object + next PARSE; + } + + # Check input/output signals and extract which are used. + if (/signals:/ .. /end:/) { + my $signal; + SIGNAL: { + # inputs + m/^\d+: (?:input|sensor):\s*(\S+)/ && do { + # inputs + $signal = $1; + if ( exists $ValidInputs{$signal} ) { + $signal = $ValidInputs{$signal}; + push(@inputs, $signal); + } + else { + $error++; + warn "*** signal '$signal' is not valid. See doc for API\n"; + next PARSE; + } + /sensor/ && $external_objects--; # for input function + next SIGNAL; + }; + + # outputs + m/^\d+: output:\s*(\S+)/ && do { + # outputs + $signal = $1; + if ( exists $ValidOutputs{$signal} ) { + $signal = $ValidOutputs{$signal}; + push(@outputs, $signal); + } + else { + $error++; + warn "*** signal '$signal' is not valid. See doc for API\n"; + next PARSE; + } + next SIGNAL; + }; + + last SIGNAL; + } + continue { + # set signal var index + m/(?:single|multiple):\s*(\d+)/ && do { + $signal->SetVarIndex($1); + }; + } + next PARSE; + } + + if (/functions:/ .. /end:/) { + /(\w+)\s*\((.*)\)\s*:\s*(\S+)/ && do { + my $fnc = $1; + my $args = $2; + my $type = $3; + $args =~ s/^\s*//; + $args =~ s/\s*$//; + my @args = split (/\s*,/, $args); + if ( exists $ValidFunctions{$fnc} ) { + $error++ unless $ValidFunctions{$fnc}->Check($fnc, + \@args, + $type); + $external_objects--; + } + }; + next PARSE; + } + + if (/variables:/ .. /end:/) { + $VarToType[$1] = GetType($2) if /^(\d+)\s*:\s*(\S+)/; + } + } + print OUTPUT; +} + +close(INPUT); +close(OUTPUT); + +# Check signal types +foreach $signal (@inputs, @outputs) { + $error++ if $signal->CheckType(\@VarToType); +} +# Checking whether two signals refer to the same Lego +# sensor or not +%Ports = (); +foreach $input (@inputs) { + $sensor = $input->LegoSensor(); + ($Ports{$sensor} = $input, next) unless exists $Ports{$sensor}; + + if ( ($Ports{$sensor}->isa(LightSensorObject) and + $input->isa(ContactInput)) or + ($Ports{$sensor}->isa(ContactInput) and + $input->isa(LightSensorObject)) ) { + # not the same type of sensors + $error++; + warn('*** Signals ', + $Ports{$sensor}->Name(), + ' and ', + $input->Name(), + " are on same sensor $sensor\n"); + } +} + +if ($error) { + unlink $tmp; + ($prog = $0) =~ s/.*\W([\w.]+)$/$1/; + die "*** $prog: $error error(s)\n"; +} + + + +#===================================================== +# Running Esterel compiler to actually generate C code +#===================================================== +# calling C code generator +{ + warn(join ' ', "$ENV{'ESTEREL'}/bin/${suffix}c", @esterel_args, $tmp, "\n") + if defined $verbose_opt; +} +$status = system("$ENV{'ESTEREL'}/bin/${suffix}c", @esterel_args, $tmp); +die "*** ${suffix}c failed\n" if $status; + +unlink($tmp); + +#=============================== +# "Hacking" the C generated code +#=============================== +$old_C = "$dir_name/$base_name.c"; +$new_C = "$dir_name/scLego$$.c"; + +open(SRC, $old_C) + || die "*** Cannot open '$old_C'\n"; +open(C_FILE, ">$new_C") + || die "*** Cannot open '$new_C'\n"; + +while () { + # skip #include "header.h" if not necessary, ie if all external + # objects are known and written in the C code. + next if (/$base_name.h/o && $external_objects == 0); + print C_FILE; +} +close SRC; +unlink $old_C; + +# Special functions for light sensors +#------------------------------------ +$c = $ValidConstants{DEFAULT_LIGHT_THRESHHOLD}; +print C_FILE <PrintCode(*C_FILE); + print C_FILE "\n"; +} + +# C code for simulation, do not add Lego code +#-------------------------------------------- +if ( ! defined $simul_opt ) { + + # Legos C definitions + #-------------------- + print C_FILE < +#include +#include +#include + +LEGOS + ; + + # Print C output functions + #------------------------- + foreach $signal (@outputs) { + $signal->PrintCode(*C_FILE); + } + + # Print C sensor functions + #------------------------- + foreach $signal (@inputs) { + $signal->PrintCode(*C_FILE); + } + + # main code + #---------- + my $module = EsterelSignal->ModuleName(); + print C_FILE <PrintInitialization(*C_FILE); + } + + # code initialization + print C_FILE <PrintCall(*C_FILE); + } + # end code + print C_FILE < into the Esterel distribution I +directory, then creates hardlinks B -> B and +B -> B. Code generators can be called using +the following commands: + +=over 3 + +=item Interpreted C code + + esterel -ILego foo.strl + +=item Automaton C code + + esterel -ALego foo.strl + +=item Equation C code + + esterel -LLego foo.strl + +=back + +Code for simulation is also possible using the B<-simul> option. + +B expects one or several predefined objects to be used. The +next section API describes the Esterel specific API that must be used. + +B parses the Esterel intermediate code given as input and +defined the value of the constants if not defined in the Esterel +code. Then it calls the actual C code generator. Finally, it modifies +the generated C code to add output function definitions and a B
+function. + +=head2 API + + % STANDARD ESTEREL / LEGO INTERFACE + %---------------------------------- + % Inputs are related to Lego sensors 1, 2 or 3. + % If input i is contected to a given type of sensor, say contact + % sensor, then one cannot use the inputs associated to another + % type. This will be checked at compile time. + % Constants without initial value are already known. There is no + % need to set them in the C generated file. + + % time control + %------------- + % Tells how many times the controller is called within one second. + % User can change the default value of 100, which may be omitted. + % Automaton is run each 1000 / TICKS_PER_SECOND ms. + constant TICKS_PER_SECOND = 100 : integer; + + % Engine control + %--------------- + constant MOTOR_OFF : integer, + MOTOR_FWD : integer, + MOTOR_REV : integer, + MOTOR_BRAKE : integer; + + % If argument is MOTOR_FWD, return MOTOR_REV and vice versa + function CHANGE_MOTOR_DIR (integer) : integer; + + constant MAX_SPEED = 255 : integer; + + output MOTOR_A_DIR := MOTOR_OFF : integer, + MOTOR_A_SPEED := 0 : integer; + + output MOTOR_B_DIR := MOTOR_OFF : integer, + MOTOR_B_SPEED := 0 : integer; + + output MOTOR_C_DIR := MOTOR_OFF : integer, + MOTOR_C_SPEED := 0 : integer; + + % Contact sensors + %---------------- + input TOUCH_1; + input TOUCH_2; + input TOUCH_3; + + % Light sensors + %-------------- + constant DEFAULT_LIGHT_THRESHHOLD = 50 : integer; + + sensor LIGHT_1_VALUE : integer; % current light measure + % set light sensor threshhold + output SET_LIGHT_1_THRESHHOLD := DEFAULT_LIGHT_THRESHHOLD : integer; + input LIGHT_LOW_1; % pure signal if below threshhold + input LIGHT_HIGH_1; % pure signal if above threshhold + + sensor LIGHT_2_VALUE : integer; + output SET_LIGHT_2_THRESHHOLD := DEFAULT_LIGHT_THRESHHOLD : integer; + input LIGHT_LOW_2; + input LIGHT_HIGH_2; + + sensor LIGHT_3_VALUE : integer; + output SET_LIGHT_3_THRESHHOLD : integer; + input LIGHT_LOW_3; + input LIGHT_HIGH_3; + + % If argument is MOTOR_FWD, returns MOTOR_REV and vice versa. + % Else returns argument. Automatically defined in C generated + % code. + function CHANGE_MOTOR_DIR(integer) : integer; + + + % Screen display + %--------------- + output CPUTS : string; + + + +=head1 OPTIONS + +Options are the same as the Esterel C code generator options. With +B<-simul> option, there is no B
function nor output functions +defined and the C generated can be compiled and tested using B. + +=head1 SEE ALSO + +L, L, L, L + +=head1 AUTHOR + +Xavier Fornari EFE + +=cut diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f47f84 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +scLego : Esterel back-end for Lego C +==================================== + +Original Website – https://web.imt-atlantique.fr/x-info/lego/ + +Usage Instructions - https://web.imt-atlantique.fr/x-info/lego/scLego.html + +## Integration with BrickOS-Bibo +Esterel examples are already bundled with BrickOS-Bibo, so by installing +the Esterel compiler and this add-on for the Esterel compiler, you will +be ready to create Esterel programs for BrickOS-Bibo. + + +## Installation +To install scLego, type: + + make ESTEREL_DISTRIB= + +You may also set the PERL macro to the name of the Perl command on +your system if it is different from the default "perl". + +Installation will copy Lego subdirectory into Esterel lib directory, +and it will creates scLego, ocLego, and sscLego scripts into the Esterel +bin directory. + + +## Usage +A man page, scLego.1 is also avalaible in Esterel man/man1 dir. Please +have a look at it, since it describes the recognized API from Esterel +to Lego. + +Then, you can call : + + esterel -LLego controller.strl + +or + + esterel -ILego controller.strl + +or + + esterel -ALego controller.strl + +Option -simul is also supported if you want to simulate the controller +using xes or csimul. You do not have to insert code for predefined +objects such as constants, or functions that belong to Esterel/Lego +API; they will be automatically defined in the resulting .c. diff --git a/scLego b/scLego new file mode 100644 index 0000000..d3337a7 --- /dev/null +++ b/scLego @@ -0,0 +1,15 @@ +#!/bin/sh +PERL=perl + +if test -z "$ESTEREL" +then + ESTEREL=/0/meije/fornari/esterel/v5_21 +fi + +export ESTEREL +if test -z "$SCLEGO" +then + SCLEGO=$ESTEREL/lib/Lego/scLego +fi + +exec $PERL $SCLEGO "$@" diff --git a/scLego.1 b/scLego.1 new file mode 100644 index 0000000..8ca17c7 --- /dev/null +++ b/scLego.1 @@ -0,0 +1,340 @@ +.rn '' }` +''' $RCSfile$$Revision$$Date$ +''' +''' $Log$ +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +''' \*(M", \*(S", \*(N" and \*(T" are the equivalent of +''' \*(L" and \*(R", except that they are used on ".xx" lines, +''' such as .IP and .SH, which do another additional levels of +''' double-quote interpretation +.ds M" """ +.ds S" """ +.ds N" """"" +.ds T" """"" +.ds L' ' +.ds R' ' +.ds M' ' +.ds S' ' +.ds N' ' +.ds T' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds M" `` +.ds S" '' +.ds N" `` +.ds T" '' +.ds L' ` +.ds R' ' +.ds M' ` +.ds S' ' +.ds N' ` +.ds T' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH SCLEGO 1 "perl 5.005, patch 03" "2/Mar/2000" "User Contributed Perl Documentation" +.UC +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +scLego ocLego sscLego \- Lego C code generator +.SH "SYNOPSIS" +.PP +.Vb 3 +\& scLego [scoc options] file.sc +\& ocLego [sscoc options] file.oc +\& sscLego [sscc options] file.ssc +.Ve +.SH "DESCRIPTION" +Generates C code for Legos. Can be used as any Esterel C code +generator. Copy \fBscLego\fR into the Esterel distribution \fIbin\fR +directory, then creates hardlinks \fBocLego\fR \-> \fBscLego\fR and +\fBsscLego\fR \-> \fBscLego\fR. Code generators can be called using +the following commands: +.Ip "Interpreted C code" 3 +.Sp +.Vb 1 +\& esterel -ILego foo.strl +.Ve +.Ip "Automaton C code" 3 +.Sp +.Vb 1 +\& esterel -ALego foo.strl +.Ve +.Ip "Equation C code" 3 +.Sp +.Vb 1 +\& esterel -LLego foo.strl +.Ve +.PP +Code for simulation is also possible using the \fB\-simul\fR option. +.PP +\fBscLego\fR expects one or several predefined objects to be used. The +next section \s-1API\s0 describes the Esterel specific \s-1API\s0 that must be used. +.PP +\fBscLego\fR parses the Esterel intermediate code given as input and +defined the value of the constants if not defined in the Esterel +code. Then it calls the actual C code generator. Finally, it modifies +the generated C code to add output function definitions and a \fBmain\fR +function. +.Sh "\s-1API\s0" +.PP +.Vb 73 +\& % STANDARD ESTEREL / LEGO INTERFACE +\& %---------------------------------- +\& % Inputs are related to Lego sensors 1, 2 or 3. +\& % If input i is contected to a given type of sensor, say contact +\& % sensor, then one cannot use the inputs associated to another +\& % type. This will be checked at compile time. +\& % Constants without initial value are already known. There is no +\& % need to set them in the C generated file. +\& +\& % time control +\& %------------- +\& % Tells how many times the controller is called within one second. +\& % User can change the default value of 100, which may be omitted. +\& % Automaton is run each 1000 / TICKS_PER_SECOND ms. +\& constant TICKS_PER_SECOND = 100 : integer; +\& +\& % Engine control +\& %--------------- +\& constant MOTOR_OFF : integer, +\& MOTOR_FWD : integer, +\& MOTOR_REV : integer, +\& MOTOR_BRAKE : integer; +\& +\& % If argument is MOTOR_FWD, return MOTOR_REV and vice versa +\& function CHANGE_MOTOR_DIR (integer) : integer; +\& +\& constant MAX_SPEED = 255 : integer; +\& +\& output MOTOR_A_DIR := MOTOR_OFF : integer, +\& MOTOR_A_SPEED := 0 : integer; +\& +\& output MOTOR_B_DIR := MOTOR_OFF : integer, +\& MOTOR_B_SPEED := 0 : integer; +\& +\& output MOTOR_C_DIR := MOTOR_OFF : integer, +\& MOTOR_C_SPEED := 0 : integer; +\& +\& % Contact sensors +\& %---------------- +\& input TOUCH_1; +\& input TOUCH_2; +\& input TOUCH_3; +\& +\& % Light sensors +\& %-------------- +\& constant DEFAULT_LIGHT_THRESHHOLD = 50 : integer; +\& +\& sensor LIGHT_1_VALUE : integer; % current light measure +\& % set light sensor threshhold +\& output SET_LIGHT_1_THRESHHOLD := DEFAULT_LIGHT_THRESHHOLD : integer; +\& input LIGHT_LOW_1; % pure signal if below threshhold +\& input LIGHT_HIGH_1; % pure signal if above threshhold +\& +\& sensor LIGHT_2_VALUE : integer; +\& output SET_LIGHT_2_THRESHHOLD := DEFAULT_LIGHT_THRESHHOLD : integer; +\& input LIGHT_LOW_2; +\& input LIGHT_HIGH_2; +\& +\& sensor LIGHT_3_VALUE : integer; +\& output SET_LIGHT_3_THRESHHOLD : integer; +\& input LIGHT_LOW_3; +\& input LIGHT_HIGH_3; +\& +\& % If argument is MOTOR_FWD, returns MOTOR_REV and vice versa. +\& % Else returns argument. Automatically defined in C generated +\& % code. +\& function CHANGE_MOTOR_DIR(integer) : integer; +\& +\& +\& % Screen display +\& %--------------- +\& output CPUTS : string; +\& +.Ve +.SH "OPTIONS" +Options are the same as the Esterel C code generator options. With +\fB\-simul\fR option, there is no \fBmain\fR function nor output functions +defined and the C generated can be compiled and tested using \fBxes\fR. +.SH "SEE ALSO" +the \fIesterel\fR manpage, the \fIscc\fR manpage, the \fIocc\fR manpage, the \fIsscc\fR manpage +.SH "AUTHOR" +Xavier Fornari <\fIXavier.Fornari@sophia.inria.fr\fR> + +.rn }` '' +.IX Title "SCLEGO 1" +.IX Name "scLego ocLego sscLego - Lego C code generator" + +.IX Header "NAME" + +.IX Header "SYNOPSIS" + +.IX Header "DESCRIPTION" + +.IX Item "Interpreted C code" + +.IX Item "Automaton C code" + +.IX Item "Equation C code" + +.IX Subsection "\s-1API\s0" + +.IX Header "OPTIONS" + +.IX Header "SEE ALSO" + +.IX Header "AUTHOR" +