public override bool VisitMethodDecl(AST.Method method)
        {
            if (!VisitDeclaration(method))
            {
                return(false);
            }

            if (!method.IsOverride)
            {
                return(false);
            }

            var overridenMethod = GetOverridenBaseMethod(method);

            if (overridenMethod == null)
            {
                return(false);
            }

            // Fix-up any types that are not equal to the overriden base.
            if (!method.ReturnType.Equals(overridenMethod.ReturnType))
            {
                method.ReturnType = overridenMethod.ReturnType;

                Diagnostics.Debug(
                    "{0} return type is co-variant with overriden base",
                    method.QualifiedOriginalName);
            }

            return(false);
        }
示例#2
0
        /// <summary>
        /// Renders a single ForStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.ForStatement s, int indentation)
        {
            var edges = s.GetStaticForLoopValues();

            var endval = edges.Item2;

            endval--;

            var indent = new string(' ', indentation);

            yield return($"{indent}for {s.LoopIndex.Name} in {edges.Item1} to {endval} loop");

            var incr = edges.Item3;

            if (incr != 1)
            {
                throw new Exception($"Expected the for loop to have an increment of 1, it has {incr}");
            }

            foreach (var n in RenderStatement(method, s.LoopBody, indentation + 4))
            {
                yield return(n);
            }

            yield return($"{indent}end loop;");
        }
示例#3
0
        /// <summary>
        /// Renders a single CommentStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.CommentStatement s, int indentation)
        {
            var indent = new string(' ', indentation);

            foreach (var m in s.Message.Split(new[] { Environment.NewLine.ToString() }, StringSplitOptions.None))
            {
                yield return($"{indent}-- {m}");
            }
        }
示例#4
0
        /// <summary>
        /// Renders a single CommentStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.CommentStatement s, int indentation)
        {
            var indent = new string(' ', indentation);

            foreach (var c in (s.Message ?? string.Empty).Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.None))
            {
                yield return($"{indent}// {s.Message}");
            }
        }
示例#5
0
        /// <summary>
        /// Renders a single ExpressionStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.ExpressionStatement s, int indentation)
        {
            var indent = new string(' ', indentation);
            var value  = RenderExpression(s.Expression);

            if (!string.IsNullOrWhiteSpace(value))
            {
                yield return($"{indent}{value};");
            }
        }
示例#6
0
 /// <summary>
 /// Renders a single BlockStatement with the given indentation
 /// </summary>
 /// <returns>The VHDL lines in the statement.</returns>
 /// <param name="method">The method the statement belongs to.</param>
 /// <param name="s">The statement to render.</param>
 /// <param name="indentation">The indentation to use.</param>
 private IEnumerable <string> RenderStatement(AST.Method method, AST.BlockStatement s, int indentation)
 {
     foreach (var n in s.Statements)
     {
         foreach (var x in RenderStatement(method, n, indentation))
         {
             yield return(x);
         }
     }
 }
示例#7
0
        /// <summary>
        /// Renders all the statements in a state machine method as VHDL
        /// </summary>
        /// <returns>The statements in the method.</returns>
        /// <param name="method">The method to render.</param>
        public IEnumerable <string> RenderStateMachine(AST.Method method, RenderStateProcess rsp)
        {
            var proc        = method.GetNearestParent <SME.AST.Process>();
            var statesignal = proc.InternalDataElements[proc.InternalDataElements.Length - 2];
            var runvariable = method.Variables.Last();

            yield return($"{method.Name}: process (RST, FSM_Trigger)");

            foreach (var n in method.Variables.Union(proc.SharedVariables).Where(x => x != runvariable))
            {
                yield return($"    variable {n.Name}: {Parent.VHDLWrappedTypeName(n)} := reset_{n.Name};");
            }
            yield return($"    variable {runvariable.Name}: {Parent.VHDLWrappedTypeName(runvariable)} := {RenderExpression(new PrimitiveExpression("State0", runvariable.CecilType))};");

            yield return("begin");

            // The first statement is the state reset call
            foreach (var s in method.Statements.Take(1).SelectMany(x => RenderStatement(method, x, 4)))
            {
                yield return(s);
            }

            yield return("    if RST = '1' then");

            foreach (var bus in Process.OutputBusses.Concat(Process.InternalBusses).Distinct())
            {
                foreach (var signal in rsp.WrittenSignals(bus))
                {
                    foreach (var s in RenderStatement(null, Parent.GetResetStatement(signal), 8))
                    {
                        yield return(s);
                    }
                }
            }

            foreach (var variable in method.AllVariables.Union(proc.SharedVariables).Where(x => x != statesignal))
            {
                yield return($"        {variable.Name} := {Naming.ToValidName("reset_" + variable.Name)};");
            }

            yield return("        FIN <= '0';");

            yield return("    else");

            foreach (var s in method.Statements.Skip(1).SelectMany(x => RenderStatement(method, x, 8)))
            {
                yield return(s);
            }

            yield return("        FIN <= RDY;");

            yield return("    end if;");

            yield return("end process;");
        }
示例#8
0
        /// <summary>
        /// Renders a single ReturnStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.ReturnStatement s, int indentation)
        {
            if (!(s.ReturnExpression is EmptyExpression))
            {
                throw new Exception("Expected return expression to be empty");
            }

            var indent = new string(' ', indentation);

            yield return($"{indent}return {method.ReturnVariable.Name};");
        }
示例#9
0
        /// <summary>
        /// Renders a single ReturnStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.ReturnStatement s, int indentation)
        {
            var indent = new string(' ', indentation);

            if (s.ReturnExpression is EmptyExpression && method != ((AST.Process)method.Parent).MainMethod)
            {
                yield return($"{indent}return {method.ReturnVariable.Name};");
            }
            else
            {
                yield return($"{indent}return {RenderExpression(s.ReturnExpression)};");
            }
        }
示例#10
0
        /// <summary>
        /// Renders a single ForStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.ForStatement s, int indentation)
        {
            var edges  = s.GetStaticForLoopValues();
            var endval = edges.Item2;
            var incr   = edges.Item3;

            var indent = new string(' ', indentation);

            yield return($"{indent}for (size_t {s.LoopIndex.Name} = {edges.Item1}; {s.LoopIndex.Name} < {endval}; {s.LoopIndex.Name} += {incr}) {{");

            foreach (var n in RenderStatement(method, s.LoopBody, indentation + 4))
            {
                yield return(n);
            }

            yield return($"{indent}}}");
        }
示例#11
0
        /// <summary>
        /// Renders all the statements in a method as VHDL
        /// </summary>
        /// <returns>The statements in the method.</returns>
        /// <param name="method">The method to render.</param>
        public IEnumerable <string> RenderMethod(AST.Method method)
        {
            if (method == null || method.Ignore)
            {
                yield break;
            }

            foreach (var n in DeclareVariables(method.Variables, 0))
            {
                yield return(n);
            }

            foreach (var n in method.Statements.SelectMany(x => RenderStatement(method, x, 0)))
            {
                yield return(n);
            }
        }
示例#12
0
        /// <summary>
        /// Renders a single SwitchStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.SwitchStatement s, int indentation)
        {
            var indent = new string(' ', indentation);

            indentation += 4;

            var indent2 = new string(' ', indentation);

            indentation += 4;

            yield return($"{indent}switch({RenderExpression(s.SwitchExpression)}) {{");

            var hasOthers = false;

            foreach (var c in s.Cases)
            {
                if (c.Item1.Length == 1 && c.Item1.First() is EmptyExpression)
                {
                    hasOthers = true;
                    yield return($"{indent2}default:");
                }
                else
                {
                    foreach (var cs in c.Item1.Select(x => RenderExpression(x)))
                    {
                        yield return($"{indent2}case {cs}:");
                    }
                }

                foreach (var ss in c.Item2.SelectMany(x => RenderStatement(method, x, indentation)))
                {
                    yield return(ss);
                }

                yield return($"{indent2}break;");
            }

            if (!hasOthers)
            {
                yield return($"{indent2}default:");
            }

            yield return($"{indent}}}");
        }
示例#13
0
        /// <summary>
        /// Renders a single SwitchStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.SwitchStatement s, int indentation)
        {
            var indent = new string(' ', indentation);

            indentation += 4;
            var indent2 = new string(' ', indentation);

            indentation += 4;

            yield return($"{indent}case {RenderExpression(s.SwitchExpression)} is");

            var others = new Statement[0];

            foreach (var c in s.Cases)
            {
                if (c.Item1.Length == 1 && c.Item1.First() is EmptyExpression)
                {
                    others = c.Item2;
                    continue;
                }
                else
                {
                    yield return(indent2 + "when " + string.Join(" | ", c.Item1.Select(x => RenderExpression(x))) + " =>");
                }

                foreach (var ss in c.Item2.SelectMany(x => RenderStatement(method, x, indentation)))
                {
                    yield return(ss);
                }
            }

            yield return($"{indent2}when others =>");

            foreach (var ss in others.SelectMany(x => RenderStatement(method, x, indentation)))
            {
                yield return(ss);
            }

            yield return($"{indent}end case;");
        }
示例#14
0
 /// <summary>
 /// Renders a single statement with the given indentation
 /// </summary>
 /// <returns>The VHDL lines in the statement.</returns>
 /// <param name="method">The method the statement belongs to.</param>
 /// <param name="statement">The statement to render.</param>
 /// <param name="indentation">The indentation to use.</param>
 private IEnumerable <string> RenderStatement(AST.Method method, AST.Statement statement, int indentation)
 {
     if (statement is AST.ForStatement)
     {
         return(RenderStatement(method, statement as AST.ForStatement, indentation));
     }
     else if (statement is AST.ReturnStatement)
     {
         return(RenderStatement(method, statement as AST.ReturnStatement, indentation));
     }
     else if (statement is AST.BlockStatement)
     {
         return(RenderStatement(method, statement as AST.BlockStatement, indentation));
     }
     else if (statement is AST.SwitchStatement)
     {
         return(RenderStatement(method, statement as AST.SwitchStatement, indentation));
     }
     else if (statement is AST.IfElseStatement)
     {
         return(RenderStatement(method, statement as AST.IfElseStatement, indentation));
     }
     else if (statement is AST.ExpressionStatement)
     {
         return(RenderStatement(method, statement as AST.ExpressionStatement, indentation));
     }
     else if (statement is AST.CommentStatement)
     {
         return(RenderStatement(method, statement as AST.CommentStatement, indentation));
     }
     else if (statement is AST.EmptyStatement)
     {
         return(new string[0]);
     }
     else
     {
         throw new Exception($"Unuspported statement type: {statement.GetType().FullName}");
     }
 }
示例#15
0
        /// <summary>
        /// Renders a single IfElseStatement with the given indentation
        /// </summary>
        /// <returns>The VHDL lines in the statement.</returns>
        /// <param name="method">The method the statement belongs to.</param>
        /// <param name="s">The statement to render.</param>
        /// <param name="indentation">The indentation to use.</param>
        private IEnumerable <string> RenderStatement(AST.Method method, AST.IfElseStatement s, int indentation)
        {
            var indent = new string(' ', indentation);

            yield return($"{indent}if ({RenderExpression(s.Condition)}) {{");

            foreach (var e in RenderStatement(method, s.TrueStatement, indentation + 4))
            {
                yield return(e);
            }

            if (s.FalseStatement != null && !(s.FalseStatement is EmptyStatement))
            {
                yield return($"{indent}}} else {{");

                foreach (var e in RenderStatement(method, s.FalseStatement, indentation + 4))
                {
                    yield return(e);
                }
            }

            yield return($"{indent}}}");
        }
示例#16
0
        /// <summary>
        /// Renders all the statements in a method as VHDL
        /// </summary>
        /// <returns>The statements in the method.</returns>
        /// <param name="method">The method to render.</param>
        public IEnumerable <string> RenderMethod(AST.Method method)
        {
            if (method == null || method.Ignore)
            {
                yield break;
            }

            if (method != Process.MainMethod)
            {
                var margs = string.Join("; ",
                                        from n in method.Parameters
                                        let inoutargstr = ((ParameterDefinition)n.Source).GetArgumentInOut().ToString().ToLowerInvariant()

                                                          select string.Format(
                                            "{0}{1}: {2} {3}",
                                            string.Equals(inoutargstr, "in", StringComparison.OrdinalIgnoreCase) ? "constant " : "",
                                            n.Name,
                                            inoutargstr,
                                            ((ParameterDefinition)n.Source).GetAttribute <RangeAttribute>() != null
                                ? method.Name + "_" + n.Name + "_type"
                                : Parent.VHDLType(n).ToSafeVHDLName()
                                            ));

                if (method.ReturnVariable == null || method.ReturnVariable.CecilType.IsSameTypeReference(typeof(void)))
                {
                    yield return($"procedure {method.Name}({margs}) is");
                }
                else
                {
                    yield return($"pure function {method.Name}({margs}) return {Parent.VHDLWrappedTypeName(method.ReturnVariable)} is");
                }

                foreach (var n in method.AllVariables)
                {
                    yield return($"    variable {n.Name}: {Parent.VHDLWrappedTypeName(n)};");
                }

                foreach (var m in Parent.TemporaryVariables)
                {
                    if (m.Key == method)
                    {
                        foreach (var n in m.Value.Values)
                        {
                            yield return($"    variable {n.Name}: {Parent.VHDLWrappedTypeName(n)};");
                        }
                    }
                }

                yield return("begin");
            }

            foreach (var s in method.Statements.SelectMany(x => RenderStatement(method, x, method == Process.MainMethod ? 0 : 4)))
            {
                yield return(s);
            }

            if (method != Process.MainMethod)
            {
                yield return($"end {method.Name};");
            }
        }
示例#17
0
        /// <summary>
        /// Applies the transformation
        /// </summary>
        /// <returns>The transformed item.</returns>
        /// <param name="item">The item to visit.</param>
        public ASTItem Transform(ASTItem item)
        {
            if (!(item is AST.Method) || !((Method)item).IsStateMachine)
            {
                return(item);
            }

            var method = item as AST.Method;

            if (!method.All().OfType <AwaitExpression>().Any())
            {
                return(item);
            }

            var enumname = "FSM_" + method.Name + "_State";

            // Construct an enum type that matches the desired states
            var enumtype = new Mono.Cecil.TypeDefinition("", enumname, Mono.Cecil.TypeAttributes.Public | Mono.Cecil.TypeAttributes.AutoClass | Mono.Cecil.TypeAttributes.AnsiClass | Mono.Cecil.TypeAttributes.Sealed, method.SourceMethod.Module.ImportReference(typeof(System.Enum)))
            {
                IsSealed = true,
            };

            enumtype.DeclaringType = method.SourceMethod.DeclaringType;
            enumtype.Fields.Add(new Mono.Cecil.FieldDefinition($"value__", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.SpecialName | Mono.Cecil.FieldAttributes.RTSpecialName, method.SourceMethod.Module.ImportReference(typeof(int))));

            var statenametemplate = $"{enumtype.DeclaringType.FullName}_{enumname}_State";

            var fragments = SplitIntoFragments(method.Statements);

            var statecount = fragments.Count;
            var enumfields = new Mono.Cecil.FieldDefinition[statecount];

            // Add each of the states to the type
            for (var i = 0; i < statecount; i++)
            {
                enumtype.Fields.Add(enumfields[i] = new Mono.Cecil.FieldDefinition($"State{i}", Mono.Cecil.FieldAttributes.Public | Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Literal, enumtype)
                {
                    Constant = i
                });
            }

            // The variable being updated internally in the method
            var run_state_var = new AST.Variable("FSM_RunState", enumfields[0])
            {
                CecilType    = enumtype,
                DefaultValue = 0,
                Source       = new Mono.Cecil.ParameterDefinition("FSM_RunState", Mono.Cecil.ParameterAttributes.None, enumtype)
            };

            // The current state used in the state machine process
            var current_state_signal = new AST.Signal("FSM_CurrentState", enumfields[0])
            {
                CecilType    = enumtype,
                DefaultValue = 0,
                Source       = new Mono.Cecil.ParameterDefinition("FSM_CurrentState", Mono.Cecil.ParameterAttributes.None, enumtype)
            };

            // The next state that is propagated to
            var next_state_signal = new AST.Signal("FSM_NextState", enumfields[0])
            {
                CecilType    = enumtype,
                DefaultValue = 0,
                Source       = new Mono.Cecil.ParameterDefinition("FSM_NextState", Mono.Cecil.ParameterAttributes.None, enumtype)
            };

            // Construct a state-machine method, that will be rendered as a process
            var stateMachineProcess = new AST.Method()
            {
                Name           = "FSM_" + method.Name + "_Method",
                Parameters     = new AST.Parameter[0],
                ReturnVariable = null,
                Parent         = method.Parent,
                Variables      = method.AllVariables.Concat(new Variable[] { run_state_var }).ToArray(),
                AllVariables   = method.AllVariables.Concat(new Variable[] { }).ToArray(),
                IsStateMachine = true
            };

            var enumdataitems = enumfields.Select(x => new Constant(x.Name, x)
            {
                CecilType = enumtype
            }).ToArray();

            var isSimpleStatePossible = fragments.SelectMany(x => x.SelectMany(y => y.All().OfType <CaseGotoStatement>())).All(x => !x.FallThrough);

            List <Statement> cases;

            // If we have no fallthrough states, we build a switch
            if (isSimpleStatePossible)
            {
                cases = new List <Statement>(new[] {
                    new EmptyStatement(),
                    CreateSwitchStatement(fragments, current_state_signal, enumdataitems)
                });
            }
            // Otherwise, we build an if-based state machine
            else
            {
                cases = WrapFragmentsWithLabels(fragments, run_state_var, enumdataitems);
                cases.Insert(0, new ExpressionStatement(
                                 new AssignmentExpression(
                                     new IdentifierExpression(run_state_var),
                                     new IdentifierExpression(current_state_signal)
                                     )
                                 ));
            }

            stateMachineProcess.Statements = cases.ToArray();
            foreach (var v in stateMachineProcess.Statements)
            {
                v.Parent = stateMachineProcess;
                v.UpdateParents();
                ReplaceGotoWithVariables(v, run_state_var, next_state_signal, enumdataitems);
            }

            method.Statements = new Statement[] {
                new ExpressionStatement(
                    new AssignmentExpression(
                        new IdentifierExpression(current_state_signal),
                        new IdentifierExpression(next_state_signal)
                        )
                    )
                {
                    Parent = method
                }
            };

            method.IsStateMachine = false;


            var proc = method.GetNearestParent <Process>();

            proc.Methods = proc.Methods.Concat(new[] { stateMachineProcess }).ToArray();

            // Move variables into the shared area
            proc.InternalDataElements =
                proc.InternalDataElements
                .Union(new[] {
                current_state_signal,
                next_state_signal
            })
                //.Union(method.AllVariables)
                .ToArray();

            proc.SharedVariables = proc.SharedVariables.Union(method.AllVariables).ToArray();
            method.Variables     = new Variable[0];
            method.AllVariables  = new Variable[0];

            return(stateMachineProcess);
        }