123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320(**********************************************************************)(* *)(* This file is part of the RFSM package *)(* *)(* Copyright (c) 2018-present, Jocelyn SEROT. All rights reserved. *)(* *)(* This source code is licensed under the license found in the *)(* LICENSE file in the root directory of this source tree. *)(* *)(**********************************************************************)(**{1 Dynamic semantics. Used for simulating programs} *)(** This is a direct translation of the formal semantics described in the reference manual *)typecfg={mutableact_semantics:Misc.act_semantics;mutableverbose_level:int;}letcfg={act_semantics=Misc.Sequential;verbose_level=0;}moduletypeDYNAMIC=sigmoduleSyntax:Syntax.SYNTAXmoduleStatic:Static.TmoduleEval:Guest.EVALmoduleEvSeq:Evseq.EVSEQexceptionIllegal_stimulus_valueofLocation.texceptionNon_deterministic_transitionofstring*int*Syntax.transitionlist(** FSM name, date, transitions *)valrun:Syntax.program->Static.t->EvSeq.tendmoduleMake(Syntax:Syntax.SYNTAX)(Static:Static.TwithmoduleSyntax=Syntax)(Eval:Guest.EVALwithmoduleSyntax=Syntax.GuestandmoduleValue=Static.Value):DYNAMICwithmoduleSyntax=SyntaxandmoduleStatic=StaticandmoduleEval=Eval=structmoduleSyntax=SyntaxmoduleStatic=StaticmoduleTypes=Syntax.Guest.TypesmoduleEval=EvalmoduleEvent=Event.Make(Syntax.Guest)(Static.Value)moduleEvset=Evset.Make(Event)moduleEvSeq=Evseq.Make(Evset)moduleTrace=struct(* Execution traces *)typet={mutablecontents:EvSeq.t}letcreate()={contents=[]}letresett=t.contents<-[]letaddet=ifnot(Evset.is_emptye)thent.contents<-e::t.contentsleteventst=List.revt.contentsendexceptionIllegal_stimulus_valueofLocation.texceptionNon_deterministic_transitionofstring*int*Syntax.transitionlistlettrace=Trace.create()(* Global trace *)letvar_namemx=Ident.to_stringm.Static.name^"."^x(* TO FIX ? Prefix should already be in the ident *)letstate_namem=var_namem"state"letmk_eventte=Evset.mkt[e]letis_fireableenvme=(* \Delta(M,q,e) *)letopenStaticinList.filter(fun{Annot.desc=(q,{Annot.desc=e',gs;_},_,_,_);_}->q=m.Static.q&&e=Event.Eve'&&List.for_all(fung->Eval.eval_boolenvg=true)gs)m.model.desc.transletfireableenvmevs=(* \Delta(M,q,{e1,...,en} *)List.map(is_fireableenvm)evs|>List.concatletchoose_transition(f,t,trs)=(* Function CHOICE *)matchtrs|>Ext.List.scatter(function{Annot.desc=_,_,_,_,p;_}->p)(* Transitions, scattered by priority levels ... *)|>List.sort(fun(p1,_)(p2,_)->Stdlib.comparep1p2)(* ... then sorted by descending order of priority level ...*)|>List.hd|>snd(* ... gives all transitions with the highest priority level *)with|[]->Misc.fatal_error"Dynamic.choose_transition"(* Should not happen *)|[tr]->tr|trs'->raise(Non_deterministic_transition(f,t,trs'))letr_act~fsd(vars,env)({Annot.desc=act;_},t)=(* Rules ActUpdL, ActUpdG, ActEmitL and ActEmitG *)(* \Nu, \Gamma -- act,t | \rho_e --> \Nu', \Gamma' *)matchactwith|Syntax.Emite->Trace.add(Evset.mkt[Event.Eve])trace;ifList.mem_assocesd.Static.sharedthen(* ActEmitL *)(vars,env),(Evset.mkt[Eve])else(* ActEmitG *)(vars,env),(Evset.emptyt)|Syntax.Assign(lval,expr)->letgenv=Env.unionvarsenvinletx=Syntax.Guest.lval_base_namelvalinletpfxplval=ifp=""thenlvalelseSyntax.Guest.lval_prefixplvalinletupd?(prefix="")env=letv=Eval.eval_exprgenvexprinTrace.add(mk_eventt(Event.Upd(pfxprefixlval,v)))trace;(* Using prefixes in traces allow VCD scoping *)Eval.upd_envlvalvenvinifEnv.memxvarsthen(* ActUpdL *)(upd~prefix:(Ident.to_stringf)vars,env),(* For local updates, prefix target variable *)Evset.emptytelseifEnv.memxenvthen(* ActUpdG *)(vars,updenv),(* No prefix for updates of globals *)Evset.emptytelse(* Should not happen *)Misc.fatal_error"Dynamic.r_act"letr_acts~fsd(vars,env)(acts,t)=(* Rule ACTS *)(* \Nu, \Gamma -- <acts,t> | \rho_e --> \Nu', \Gamma' *)let(vars',env'),rs=List.fold_left_map(r_act~fsd)(vars,env)(List.map(funact->(act,t))acts)invars',env',Evset.union_alltrsletr_transsd(m,env)({Annot.desc=q,_,acts,q',_;_},t)=(* Rule TRANS *)(* \mu, \Gamma -- \tau,t | \rho_e --> \mu', \Gamma' *)letvars',env',r_e=r_acts~f:m.Static.namesd(m.Static.vars,env)(acts,t)inletm'={mwithq=q';vars=vars'}inTrace.add(mk_eventt(Event.StateMove(state_namem,Ident.to_stringq')))trace;m',env',r_eletr_reactionsd(m,env)s_e=(* Rules React1, React0 and ReactN *)(* \mu, \Gamma -- \sigma_e | \rho_e --> \mu', \Gamma' *)lett=Evset.dates_ein(* let env' = List.fold_left Env.union Env.empty [m.Static.vars; m.Static.params; env] in *)letenv'=List.fold_leftEnv.unionEnv.empty[m.Static.vars;env]in(* The folding order in the previous defn forces the _local_ definitions (vars and params) to
shadow the global ones. *)matchfireableenv'm(Evset.eventss_e)with|[tr]->r_transsd(m,env)(tr,t)(* REACT_1 *)|[]->m,env,Evset.emptyt(* REACT_0 *)|trs->r_transsd(m,env)(choose_transition(Ident.to_stringm.name,t,trs),t)(* REACT_N *)letr_react_updsd(m,env)evs=(* Rule ReactUpd *)(* M, \Gamma -- \sigma_v --> M', \Gamma' *)(* TODO: use a GADT to remove dynamic checking of event kind ? *)letupdenve=matchewith|Event.Upd(l,v')->Eval.upd_envlv'env|_->Misc.fatal_error"Simul.r_react_upd"(* Should not happen *)inTrace.addevstrace;letenv'=List.fold_leftupdenv(Evset.eventsevs)inm,env'letdump1levelfmtfarg=ifcfg.verbose_level>=levelthenFormat.fprintfFormat.std_formatterfmtfargletdump2levelfmtf1arg1f2arg2=ifcfg.verbose_level>=levelthenFormat.fprintfFormat.std_formatterfmtf1arg1f2arg2letdump3levelfmtf1arg1f2arg2f3arg3=ifcfg.verbose_level>=levelthenFormat.fprintfFormat.std_formatterfmtf1arg1f2arg2f3arg3letdump4levelfmtf1arg1f2arg2f3arg3f4arg4=ifcfg.verbose_level>=levelthenFormat.fprintfFormat.std_formatterfmtf1arg1f2arg2f3arg3f4arg4moduleFsmNode=structtypet=Static.fsmtypecontext=unitletname_off=f.Static.nameletdepends_on_mm'=(* Return true if FSM m' (in state q') depends on FSM m (in state q), i.e. if
- at least one transition starting from q' is triggered by a (shared) event emitted by at least one transition
starting from q
- at least one transition starting from q' is guarded by a condition refering to a (shared) variable modified by
an action of a transition starting from q
In other words, if we write
- [evs'] the set of triggering (shared) events associated to state q' in m'
- [rvs'] the set of (shared variables) used by the conditions associated to state q'
- [evs] the set (shared) events emitted from state q in m
- [wvs] the set of (shared variables) modified by the actions modified from state q
then m' depends on m iff [evs' \inter \evs] or [rvs' \inter wvs] is not empty *)letmoduleS=Set.Make(Ident)inletinterl1l2=not(S.is_empty(S.inter(S.of_listl1)(S.of_listl2)))inletevs',rvs',_,_=Syntax.state_iosm'.Static.modelm'.qinlet_,_,evs,wvs=Syntax.state_iosm.Static.modelm.qin(* TODO ? Filter out non shared events / vars ? *)letr=interevs'evs||interrvs'wvsin(* let open Format in
* fprintf std_formatter "*** *** *** depends_on %s %s: evs'=[%a] rvs'=[%a] evs=[%a] wvs=[%a] r=%b\n"
* m.name m'.name
* (Misc.pp_list pp_print_string) evs'
* (Misc.pp_list pp_print_string) rvs'
* (Misc.pp_list pp_print_string) evs
* (Misc.pp_list pp_print_string) wvs
* r; *)rendletdep_sortfsms=(* Sort FSMs using the ($\leq$) relation of the dynamic semantics.
The resulting order is used by [r_react_ev] to sequence the reactions of FSMs at a given instant. *)(* TODO ?: this could be pre-computed statically for each ((M,q),(M',q')) pair ? *)letmoduleD=Depg.Make(FsmNode)inD.dep_sort()fsmsletr_react_evsd(m,env)s_e=(* Rule ReactEv *)(* M, \Gamma -- \sigma_e | \rho --> M', \Gamma' *)letms=dep_sortmindump12">> >> REACT_EV: using order: %a\n"(Ext.List.pp_h~sep:"; "(Static.pp_fsm~verbose_level:0))ms;Trace.adds_etrace;let(env',_,rho),ms'=List.fold_left_map(fun(env,s,r)mu->letmu',env',r'=r_reactionsd(mu,env)sin((env',Evset.unionsr',Evset.unionrr'),mu'))(env,s_e,Evset.empty(Evset.dates_e))msinms',env',rholetr_reactsd(m,env)st=(* Rule REACT *)letpp_env=Env.ppEval.Value.ppinletpp_fsms=Ext.List.pp_v(Static.pp_fsm~verbose_level:1)inlett=Evset.datestin(* M, \Gamma -- \sigma | \rho_e --> M', \Gamma' *)dump41">> REACT t=%a@. M=%a@. G=%a@, -- %a --> ...@."Format.pp_print_inttpp_fsmsmpp_envenvEvset.ppst;letst_e,st_v=Evset.partition~f:Event.is_pure_eventstinlet_,env_v=r_react_updsd(m,env)st_vindump12">> >> REACT_UPD: G_v=%a\n"pp_envenv_v;letm',env',r_e=r_react_evsd(m,env_v)st_eindump32">> >> REACT_EV:@. M'=%a@. G'=%a@. r_e=%a@."pp_fsmsm'pp_envenv'Evset.ppr_e;dump31">> REACT ... -- [%a] ->@. M'=%a@. G'=%a@."Evset.ppr_epp_fsmsm'pp_envenv';(m',env'),r_eletdefault_value(ty:Types.typ)=Static.Value.default_valuetyletr_initsdm=(* Rule INIT *)(* M --> M_0, \Gamma_0 *)letenv0=List.fold_left(funenv(v,cc)->letty=cc.Static.ct_typinifnot(Types.is_event_typety)thenEnv.addv(Static.Value.default_valuety)envelseenv)Env.empty(sd.Static.inputs@sd.Static.outputs@sd.Static.shared)inletenv',m'=List.fold_left_map(funenvmu->letnu=mu.Static.varsinlet{Annot.desc=q0,acts;_}=mu.Static.model.Annot.desc.itransinletnu',env',r_e=r_acts~f:mu.namesd(nu,env)(acts,0)inTrace.add(mk_event0(Event.StateMove(state_namemu,Ident.to_stringq0)))trace;Trace.addr_etrace;env',{muwithStatic.q=q0;Static.vars=nu'})env0minm',env'letis_inputctxevs=letcheckev=matchevwith|Event.Evx->ifList.mem_assocxctx.Static.inputs&&Types.is_event_type((List.assocxctx.Static.inputs).ct_typ)then()elseMisc.fatal_error("Dynamic.is_input: "^Ident.to_stringx^" is not an event typed input")|Event.Upd(l,_)->letx=Syntax.Guest.lval_base_namelinifList.mem_assocxctx.Static.inputs&¬(Types.is_event_type((List.assocxctx.Static.inputs).ct_typ))then()elseMisc.fatal_error("Dynamic.is_input: "^Ident.to_stringx^" is an event typed input")|Event.StateMove_->()inList.itercheck(Evset.eventsevs)letr_exec(h:Static.t)(sts:Evset.tlist)=(* Rule EXEC *)(* H=<M,C> -- \sigma_1, ..., \sigma_n | \rho_1, ..., \rho_n --> M_n, \Gamma_n *)(* First check that all stimuli refer to input signals listed in H *)List.iter(is_inputh.Static.ctx)sts;Trace.resettrace;letm0,env0=r_inith.ctxh.fsmsinletenv1=Env.unionenv0h.globalsinlet_,_=List.fold_left_map(r_reacth.ctx)(m0,env1)stsinTrace.eventstraceletextract_stimulip=leteval(t,expr)=tryt,Eval.eval_exprEnv.emptyexprwith_->raise(Illegal_stimulus_valueexpr.Annot.loc)inletexpandidst=matchst.Annot.descwith|Syntax.Periodic(p,t1,t2)->EvSeq.mk_periodicidpt1t2|Syntax.Sporadicts->EvSeq.mk_sporadicidts|Syntax.Value_changevcs->EvSeq.mk_changesid(List.mapevalvcs)inletsts=List.fold_left(funaccio->matchio.Annot.descwith|id,Syntax.Input,_,Somest->expandidst::acc|_->acc)[]p.Syntax.globalsinEvSeq.merge_allstsletrun(p:Syntax.program)(s:Static.t)=letsts=extract_stimulipinmatchcfg.act_semanticswith|Misc.Sequential->r_execssts|Misc.Synchronous->Misc.not_implemented"Dynamic.run with synchronous semantics for actions"end