Intermediate Predicate Format for Design Automation Tools Michael F. Dossis
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis Higher Technological Education Institute of Western Macedonia
[email protected] doi: 10.4156/jnit.vol1.issue1.9
Abstract This paper describes the Intermediate Predicate Format (IPF) which is used in Design Automation for complex digital hardware. Programs written in standard high-level programming languages are translated into the IPF file, using an optimizing front-end compiler phase. The IPF file is a tabular structure containing lists of Prolog language predicate facts, in the same manner as in Artificial Intelligence applications. IPF files are suitable to hold the algorithmic information, the hierarchy, the granularity, the control & data flow, the object typing and operator attributes of the original source code programs which specify the functionality of the intended designs. The syntax and semantics of the IPF predicate facts are formally defined here. The generated IPF facts are then loaded into the knowledge-base of the back-end compiler. This back-end phase is built with Prolog predicates which process the IPF facts in order to transform them into optimized state schedules. A standalone Finite State Machine, is generated from each subprogram in the designer’s original source code. In this way, high-level program code is automatically and formally transformed into custom hardware. The generated custom hardware is then implemented in VLSI technology (e.g. FPGAs or ASICs) using standard industrial ECAD tools. It is shown in the present work that compact IPF files “guide” the knowledge-base back-end compiler into quickly taking optimum decisions during mapping of the abstract source program operations into functionally equivalent digital hardware states.
Keywords: IPF, Design Automation for complex digital hardware, Optimizing front-end compiler phase
1. Introduction Advances in chip integration technology during the last and the current decade have made possible to implement extremely complex systems into single chip or small set of chips. This proliferation of custom hardware in such a small physical space has made a definite impact on the targeted cost and performance of small and medium - scale computing equipment, which are used in multimedia and consumer services, scientific data processing, telecommunications, banking, aerospace, naval and other type of transport infrastructure. The rapid increase in integration level allowed embedded applications to become extremely complex and very difficult to design, develop and verify on time, compared with the lifetime of products in the market. This has motivated industry and academia to invest in rapid methodologies to achieve greater automation in the design of systems. Nowadays, a higher level of code abstraction is used as input to automated design & implementation tools. Furthermore, methodologies such as high-level synthesis employ established and new techniques, borrowed from the computer language program compilers and successful ECAD tools. Early research studying techniques for synthesis of behavioral algorithmic descriptions, coded in the language DSL, into hardware structures is reported in [1]. Tools targeting particular high-level specification formats, domain of applications (e.g. DSP) or targeted hardware architectures can be found in [2], [3], [4]. Specific high-level synthesis tasks such as the scheduling problem have been studied in [5], [6]. Very important goals during hardware compilation from behavioral algorithmic code have been the ability to formally describe various tasks, the intermediate representations and the optimizing transformations of compilation, so as to achieve provably-correct compilation and formal equivalence of the functionality of the implementations with that of the executable specifications [7], [8], [9]. In this direction, parts of the hardware compilers are automatically generated from compiler-generator engines and formally defined source code syntax [7], [9]. Various intermediate formats have emerged
100
Journal of Next Generation Information Technology Volume 1, Number 1, May 2010 through the years, such as the postfix notation, the n-tuples, the three-address code, the flow and directed acyclic graphs, the computation graphs, the abstract syntax trees, the threaded code, the pseudo machine code, the p-code, and table-based forms, and these were utilized for the internal compiler tasks and design representations [7], [9], [10]. The formal methodology discussed in this paper is motivated by the benefits of using predicate logic to describe the intermediate representations of compilation steps, and of employing the resolution of Horn clauses as the building blocks of a hardware mapping and optimizing engine [11]. This logic inference engine constitutes the most critical (high-level synthesis - oriented back-end) part of the hardware compiler, which is leveraging to the implementation of hardware compilation tasks, such as the mapping of complex data and control structures into equivalent hardware architectures in an optimal way, as well as scheduling and grouping the abstract data operations of the source programs into hardware machine (FSM) control states. The choice of logic predicates, has been encouraged by other successful uses of the Prolog language, such as e.g. in solving the scheduling problem of airflight control and aircraft landing. Another important decision in the design of the IPF 1 was to position the abstraction level of IPF in the middle of the range between the very close to the source programming language level, and the very close to the target hardware detailed level. IPF contains all the essential constructs to describe algorithms coded in high-level programs, without inheriting any of the specific to the programming language features. On the other hand, IPF is the result of automated analysis of the source programs into a set of elementary (two-operand) operations using simple data types and operators, as well as logic relations between them, but it is not bound to any attributes or details of any target hardware architecture template or any implementation technology. These two characteristic properties of IPF explain its success for hardware compilation and synthesis. IPF’s independence from any particular source programming language, enables easy compiler upgrades for other languages, such as e.g. C, Java, behavioral VHDL, or System Verilog. Such input language extension can be achieved by simply updating the formal description of the language syntax which is given as input to the compilergenerator. In this way the language-specific part of the front-end hardware compiler is generated. Furthermore, additional target (hardware description) language formats can be written by the hardware compiler, by simply updating the Prolog code which implements the hardware writer of the back-end compiler. A prototype set of compilation tools 2 have been developed by the author to demonstrate the feasibility and benefits of the presented approach. The IPF format has evolved through many years of work absorbing all the source code algorithmic features. The existing prototype compiler transforms a set of subroutines captured in an ADA package (library of programs), into the IPF file and subsequently into a corresponding set of synchronous hardware Finite State Machines (FSMs), which are coded in the VHDL hardware description language. These two compilation phases exchange data via an IPF design database file, which is also formally specified. In this way, custom co-processing hardware can be rapidly generated from abstract executable specifications (program code). The formal generation and use of custom hardware replacements of the original program code subroutines, can benefit the host computing system in terms of performance and security. This paper presents the IPF format and discusses its syntax and semantics, as well as the usability and benefits of this format in the prototype hardware compilation system. Section 2 discusses related work on other contemporary intermediate forms and languages used elsewhere. The declarative nature of the IPF file and its beneficial use as an inference engine drive are illustrated in section 3. In section 4 it is shown, that although its declarative nature, IPF allows for easy implementation of sequential processing on its statements via indexing, which makes it suitable for various optimization and transformation algorithms. In section 5 it is proved that IPF statements are relational via the use of inter-fact referencing and via the grouping of programming objects into positional predicate parameters. Section 6 gives the formal definition of IPF and lists the EBNF description of its syntax. The role of IPF in the hardware compilation flow is explained in section 7. Section 8 discusses the logical link 1
The formal intermediate format is patented with patent number: 1006354, 15/4/2009, from the Greek Industrial Property Organization 2 This hardware compiler method is patented with patent number: 1005308, 5/10/2006, from the Greek Industrial Property Organization
101
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis between the IPF Prolog facts and the Prolog inference engine rules of the back-end hardware compiler. Section 9 describes the transformations of the source code down to the RTL VHDL custom hardware models. Sections 0 and 10 discuss experimental IPF results, draw useful conclusions on the overall evaluation of IPF, and propose future work.
2. Related and Previous Work A rather complex and software platform - specific intermediate language was designed in Microsoft research to serve the needs of the .NET development environment [12]. Microsoft IL is processed by the Microsoft Common Language Runtime and it is an object-oriented intermediate language form, but it is too complicated for the purposes of the work discussed in this paper. Amongst other features Microsoft IL includes type-checking of .NET components, in order to prevent errors in the target code which the compiler generates. Another popular multilevel intermediate form is the Jikes Intermediate Code Representation [13]. The Intermediate Representation (IR) has three levels, the HIR (High Level IR), the LIR (Low Level IR), and the MIR (Machine Level IR). Their names denote the abstraction level, the use and the type of these three IR levels. Every one of IR levels allows for level-specific optimization as well as translation to the immediately lower level of representation. Furthermore, the lowest of all three which is the MIR level, allows for direct translation into the JVM code. Nevertheless, the focus of the work which is discussed here is on the programming language – independent, universal, single level, and formally defined IPF to be used primarily for hardware synthesis from regular high-level algorithmic program code. An intermediate form which extends the algebraic data types from standard ML with object oriented features, constitutes an attempt to achieve compiler portability and retargetability as well as efficiency of target code generation [14]. The Abstract Intermediate Representation (AIR) integrates the abstract syntax of high-level languages with the internal data structures used in the various compilation tasks. It provides a formalism which aims to support a great variety of data structure types such as tree types and graph types, with the goal of expressing all the representations needed in the construction of compilers, which makes it rather complex.
3. The Declarative Nature of IPF Files The declarative nature of the IPF statements enables the knowledge-based back-end part of the hardware compiler to “draw conclusions” in the same manner that the declarative nature of logic programs allows these programs to “conclude” and “extract knowledge” from usually a great number of facts and relations between Prolog clauses. … type_def(10,"positive",32,"standard",2, "single_t",0,0,0) type_def(11,"memory_40000_words",1280000,"user",0, “vectorarray_t",0,40000,2) … op_def(85,"+","binop",9,9,9,1) … hierarchy_part(5,"module5",1,"libpart", 5, 5, 0) … prog_stmt("module5", 4,0,14,3,9,10,5) … data_stmt("module5","i",3,2,"var",sym("node")) … call_stmt("all_proc", 2, 3, [1,6,7,8,9,2]) … Figure 1. Example of IPF file statements from a sample program compilation
102
Journal of Next Generation Information Technology Volume 1, Number 1, May 2010 After a long period of research work and experiments two essential goals were achieved: the declarative manner of the statements of the IPF tables, combined with the efficiency of their parameter semantics. These allow for fast and efficient optimizing transformations on the IPF statements, which generate the targeted hardware machine descriptions. The declarative nature of the IPF syntax is demonstrated in the small excerpt from an IPF compilation in Figure 1, where the generated from the front-end, IPF Prolog atomic formulas (facts) are shown. The following list contains the basic set of IPF predicate symbols used in the prototype hardware compiler (brief explanation given in parenthesis, next to each symbol): type_def op_def hierarchy_part prog_stmt data_stmt call_stmt
(definition of an object type) (definition of an operator) (definition of a hierarchical entity) (a two-operand and a single result operation statement) (a data object declaration statement) (a subroutine call statement)
These predicate symbols constitute means of relating the various positional parameters of the IPF facts to each other, as well as relating e.g. prog_stmt facts with data_stmt facts via the use of the fact’s index (entry numbers in the terminology of IPF) in the relating fact parameter value. In Figure 1, the following IPF table statements are shown. First two type definitions are listed: a standard definition of the type “positive” which includes the positive integers and a user-defined type definition of a multidimensional array called “memory_4000_words”; their entry numbers in the type definition table are 10 and 11 respectively. After these, the definition of the addition operator of width equal to that of data type 9 is listed with the attribute of being a binary operator (binop, to distinguish from the + prefix in some expressions which is the unary sign operator). The declaration of one module of the source code program hierarchy called “module5” follows. The next two statements declare a simple, 2-operand program operation and a data object declaration of the module “module5”, from the program and data statement tables, respectively. The last is a subroutine call statement from the calls table of top-level module “all_proc” which declares a call event with a list of 6 actual parameter entry numbers.
4. Working with sequences and lists The large majority of optimization algorithms and other processing routines which are executed within design automation tools such as hardware compilers assume a sequential arrangement of the data nodes to be processed, such as e.g. linear object lists or branches of graphs. The indexing of the declarative IPF facts and their grouping into homogeneous lists called the “IPF tables” allows for both random and sequential processing of them by the back-end compiler predicates, in the same way as processing of linear lists of objects. This property of IPF allows for easy implementation of many transformation algorithms executed on the IPF’s Prolog predicate facts. The approach presented here combines the declarative nature of Prolog predicate facts (which allows for random access) with the sequential nature of most of the transformation routines (for linear access). This is achieved via the use of the entry (index) numbers of facts in every relevant table (or in other words group of facts) in the IPF file. This indexing is shown in Figure 1, where e.g. the type_def facts are indexed, with their entry number being the first variable of the respective type_def predicate (numbers 10 and 11 in Figure 1). Therefore in the example of Figure 1 the type definitions for the data types “positive” and “memory_4000_words” found in the source program have entry numbers 10 and 11 respectively, in the generated IPF file. Further down the list of Figure 1 it is shown that e.g. the 4th parameter of the data_stmt fact is equal to 2 which is the entry number of the integer data type in IPF (a type_def referenced in a data_stmt fact). This is a method of inter-fact indexing. As another example of referencing a fact in the parameter values of another fact via its entry number, the following two IPF facts of the code excerpt in Figure 1, are statements of the program operations table and the data declarations table of the subprogram “module5” of the source code of the
103
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis compilation. The 4th, 5th and 6th parameters of the program table fact are indexing the left, the right operands and the result of the operation. The 2 nd parameter is the entry number of the operator that executes the particular operation: … prog_stmt("module5", 4,0,14,3,9,10,5) … data_stmt("module5","i",3,2,"var",sym("node")) … The first of these two facts describes the “less or equal” comparison operation found inside the source code program statements. It has two source operands with entry numbers 3 and 9 (5th and 6th parameters from the left) and the destination (Boolean) operand with entry number 10 (7th parameter from the left) to hold the result of the comparison. The second fact is part of the data declarations table of the same subprogram (“module5”). The value “var” in the 5th position of this fact’s parameters (carrying specific values for the Prolog predicate’s variables) denotes a variable declaration which is extracted from the source program. This data_stmt fact is indexed with entry number value 3 which is placed in the 3rd parameter of the fact. This value of 3 is used to reference this data object in the prog_stmt (the program table) fact’s left operand position. The left source operand of the prog_stmt operation, is referenced in the 5th parameter (from the left) of the respective Prolog predicate.
5. Relational semantics of IPF facts IPF constitutes also a method of expressing relational semantics using formulas (formal expressions). As a practical example let’s consider the following expression: “John is the son of Eric”
(expr. 1)
This expression states a relation which is also a fact. It relates constant Eric to constant John with the “parental” relation. A logic programming equivalent of this could be the following predicate fact: is_the_son_of(john, eric)
(expr. 2)
The formula in expr. 2 expresses the same relation as the one in expr. 1 but in a formal way. In the same manner the relational semantics of IPF are formally defined in the corresponding Prolog predicates which are the back-end compiler’s definitions of the IPF file statements. The IPF facts are formal relations between values, types, objects and other programming entities as well as other IPF facts. The general syntax of the IPF facts follows the following format: fact_id(object1, object2, …, objectN)
(expr. 3)
This N-tuple relation has the identifier (fact_id), which denotes the name of the IPF table it belongs to (or it is grouped in) and N positional parameters, which can hold values of objects (e.g. constant values) or references (entry numbers) of facts from other tables. In this way parameter values object1 to objectN are all formally related to each other, and their relation has the identifier fact_id. This identifier also formally defines the semantics of the relation between the above objects, or some of these objects and other facts (or relations) that are referenced in fact fact_id. As an example of this inter-fact relation, the facts of the IPF program operations table prog_stmt are defined by defining relations of the operator fact (op_def) reference which defines which elementary operation is denoted (e.g. addition, multiplication, comparison) with the source and result data operands of the operation being references to data declaration facts (data_stmt). In the IPF paradigm this inter-fact relation is done via a reference (entry) number of the related fact rather than the infinite depth of entities enclosed in parenthesis in the case of relational languages, e.g. in Lisp, or the intermediate form EDIF which is used in Electronic CAD tools [15], [16]. However, the method by reference (entry) number which is followed here, is not at all restrictive in terms of the depth of the
104
Journal of Next Generation Information Technology Volume 1, Number 1, May 2010 reference levels (which can be potentially infinite as well). Moreover, the method of relating facts via the use of their reference numbers (or indexing numbers) allows for a more efficient processing of the IPF with the linear and graph-based transformation algorithms of the high-level synthesis – oriented compiler.
6. Formal Definition of IPF EBNF (Extended Backus-Naur Form) is used here to formally define the IPF structure and syntax. Without harming the aim of this paper, only the most essential part of the EBNF definition for the IPF constructs is listed bellow. These constructs include the hierarchical module statements table (hierarchy_part), the table which contains type definitions of data objects (type_def), the operator definitions table (op_def), the program operations table for each subprogram in the source code (prog_stmt), the data object declarations table for each subprogram (data_stmt), and the subroutine call statement table (call_stmt). Each of these tables groups a homogeneous list of facts with the same fact identifier: a) Hierarchical part statements table HierListStatements ::= [ HierListStatements ] HierListStatement HierListStatement ::= HIERARCHY_PART ( EntryNumber , Hierarchical_part_name , HierLevel , UseMode , ProgTableRef , DataTableRef , CallsListRef ) EntryNumber ::= Hierarchical_module_name ::= HierLevel ::= UseMode ::= LIBPART | MACRO | EXTERNAL ProgTableRef ::= DataTableRef ::= CallsListRef ::= b) Types definitions table TypeTableStatementList ::= [ TypeTableStatementList ] TypeTableStatement TypeTableStatement ::= TYPE_DEF ( EntryNumber , TypeName , TypeLength , Indicator , ParentEntry , TypeKind , FirstIndex , DimSize , ComponentEntry ) TypeName ::= TypeLength ::=
105
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis Indicator ::= STANDARD | USER | RESERVED ParentEntry ::= TypeKind ::= SINGLE_T | VECTORARRAY_T | MULTARRAY_T | RECORD_T DimSize ::= FirstIndex ::= ComponentEntry ::= c) Operators definitions table OpTableStatementList ::= [ OpTableStatementList ] OpTableStatement OpTableStatement ::= OP_DEF ( EntryNumber , OperatorID , UnBin , LeftType , RightType , ResultType , NumbOfCycles ) OperatorID ::= UnBin ::= UNOP | BINOP LeftType ::= RightType ::= ResultType ::= NumbOfCycles ::= d) Program operations table ProgTableStatementList ::= [ ProgTableStatementList ] ProgTableStatement ProgTableStatement ::= PROG_STMT ( Hierarchical_part_name , EntryNumber , StateNumber , OperatorEntry , LeftOpEntry , RightOpEntry , ResultOpEntry , NextOperation ) StateNumber ::= OperatorEntry ::= LeftOpEntry ::= RightOpEntry ::= ResultOpEntry ::= NextOperation ::=
106
Journal of Next Generation Information Technology Volume 1, Number 1, May 2010
e) Data object declarations table DataTableStatementList ::= [ DataTableStatementList ] DataTableStatement DataTableStatement ::= DATA_STMT ( Hierarchical_part_name , ObjectID , EntryNumber , TypeEntry , DataKind , Value ) ObjectID ::= TypeEntry ::= DataKind ::= \\ "par_in" or "par_out" or \\ "par_inout" or "const" or \\ "var" Value ::= SYM ( ) | \\ for variables, and IO params I ( ) | \\ for integer constant values R ( ) | \\ for real constant values BOL ( Boolean_value ) | \\ for Boolean constant values REC_FIELD ( Parent_record_type , Field_type_offset ) \\ for composite record object fields Boolean_value ::= 0 | 1 RightOpEntry ::= ResultOpEntry ::= NextOperation ::= f) Subrounine calls definitions table CalledList ::= [ CalledList ] CalledStatement CalledStatement ::= CALL_STMT ( Hierarchical_part_name , EntryNumber , CalledNumber , '[' ActualParList ']' ) \\ '[' and ']' are used instead of just plain brackets \\ to indicate that these syntactic symbols are expected \\ in this production CalledNumber ::= ActualParList ::= [ ActualParList , ] ActualPar ActualPar
::=
where in the above EBNF definitions, double backslashes (\\) indicate the beginning of comments. All keywords are in capital letters (to comply to the tool’s BNF convention) but in the real IPF file they are all lowercase letters (to comply with Prolog’s symbol convention). Special symbols such as the brackets [, ] are enclosed within single quotes when they constitute part of the syntactic tokens (e.g. as
107
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis in the CalledStatement production). The standard syntactic terminal classes such as the integer literals are denoted with the class name of such terminals enclosed in the brackets (e.g. ). A subset of the above syntax (the type and operator definitions tables) was used to automatically generate the front-end parser of a library compiler, which is used to update/enhance the available type and operator resources of the provably-correct hardware compiler.
7. IPF and compilation flow IPF constitutes the information exchange link between the front-end and the back-end parts of the hardware compiler. The front-end compiler transforms a set of source code subprograms into an IPF file. The IPF file captures the typing, structural and algorithmic (control & data flow) information of the source code subprograms. The back-end phase of the hardware compiler transforms the generated IPF file into a set of standalone hardware FSMs. Each FSM functions in a way which is equivalent to that of the corresponding source code subprogram. The descriptive design flow is demonstrated in Figure 2.
High-level source code programs
front-end compiler
IPF file
back-end compiler
RTL implementable hardware descriptions
Figure 2. Hardware design and synthesis flow using the IPF file The user of the hardware compiler tool-set can upgrade the available data types and operators, by updating a library file given as an IPF subset, and integrating his updates into the design flow by using a library compiler. The front-end compiler performs the typical tasks of a program compiler such as lexical and syntactic analysis, production of syntactic and semantic error messages, production and processing of abstract syntax trees, type-checking of data declarations and program statements, optimization of auxiliary variables and constants etc. It is important to note that the front-end compiler is developed with the aid of compiler-generator techniques in order to automate the production of the language-
108
Journal of Next Generation Information Technology Volume 1, Number 1, May 2010 specific tasks (lexical & syntax analysis of source code). Therefore functional errors during compilation are eliminated. The back-end compiler is built using logic programming techniques. It reads and incorporates the IPF tables’ information into its internal knowledge-base of logic predicates and relations. Thus, from an other view-point, the IPF tables “guide” the back-end compiler knowledge-base of rules and its inference engine, in such a way that the latter infers the appropriate “conclusions” during transformations of the source programs into the functionally-equivalent hardware models. The backend compiler consists of a great number of definite clauses of the following form: A0 ← A1 ∧ … ∧ An (where n ≥ 0)
(form 1)
where ← is the logical implication symbol (A ← B means that if B applies then A applies), and A 0, … , An are atomic formulas (facts) of the form: predicate_symbol(Var_1, Var_2, …, Var_N)
(form 2)
where the positional parameters Var_1,…,Var_N of the above predicate_symbol are either variable names (in the case of the back-end compiler “inference rules”), or constants (in the case of the IPF table statements) [11]. By utilizing the definite clauses of the produced IPF file, in combination with the back-end compiler’s internal definite clauses and logic inference rules, it can be argued that formally equivalent to the input programs hardware descriptions are generated by this compiler. In essence, the IPF file consists of a number of such atomic formulas, which are grouped in the IPF tables. Each such table contains a list of homogeneous facts which describe a certain aspect of the compiled program. E.g. all prog_stmt facts for a given subprogram are grouped together in the listing of the program statements table. The syntax of the table facts follows the formal definition of IPF shown already in section 6.
8. Inference logic and back-end transformations As already mentioned, the inference engine of the back-end compiler consists of Prolog predicates with logic rules such as the one in form 1. A new design to be synthesized is loaded via its IPF representation into the back-end compiler’s inference engine. Therefore, the atomic formulas of the IPF file “drive” the logic rules of the back-end compiler which generate the correct hardware architectures. The produced hardware is coded in implementable (using widely available commercial tools) RTL hardware description language (HDL) descriptions. The generated (V)HDL code is directly implementable to silicon via the use of commercial RTL HDL synthesis tools. The following example shows a small part of the back-end compiler’s implementation code in the Prolog language. In these predicate examples listed bellow, the comma (,) is used in place of the conjunction symbol (∧) of form 1, but with the same meaning. The syntax of form 2 for the atomic formulas still applies, the only difference being that the last (rightmost) atomic formula of the rule is terminated with the full-stop symbol (.) to indicate the end of the inference rule’s expression. The following definite clause gives a Prolog example of the inference rules of the back-end knowledge-base. It is part of the back-end compiler’s code that updates the list of successors of a specific program operation, in order to be used for scheduling operations into hardware states. In order to achieve the dependency analysis of the design’s program, it processes the program (operations) statements of the input IPF description of the design’s algorithm operations. If the two processed operations are found to have a dependency from each other then the candidate operation is added in the current operation’s successors list. update_successors1(In_list, Module, Operation, Candidate, Out_list) ← prog_stmt(Module, Operation, _, _, _, _, ResOp, _), ResOp > 0, prog_stmt(Module, Candidate, _, Cand_operator, ResOp, _, _, _),
109
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis Cand_operator 109, % is not a operation append_op(In_list, Candidate, Out_list). The case when the candidate operation to be examined is not a subprogram call operation is shown in this example. That applies when the operator’s entry number referenced in the program operation fact, is different than 109, since 109 denotes subprogram calls (the 4th parameter of the prog_stmt predicate has a different meaning when the prog_stmt fact describes a subprogram call). According to the above rule, the atomic formula with predicate symbol update_successors1 applies when every one of the following five atomic formulas are true: prog_stmt(Module, Operation, _, _, _, _, ResOp, _), ResOp > 0, prog_stmt(Module, Candidate, _, Cand_operator, ResOp, _, _, _), Cand_operator 109, % is not a operation append_op(In_list, Candidate, Out_list). Comments to this part of Prolog code start with special character %. The above rule means that if the current (to be examined) operation has a non-zero result entry number (ResOp > 0), and the left operand of the candidate operation is the same as the result of the current operation, and the candidate operation is not a call operation (a call operation has a different IPF meaning for the left operand position), then the above rule applies. A (desired) side-effect of the rule’s implication is that the existing list of successor operations (In_list) is updated with the addition of the candidate operation entry number using the following predicate: append_op(In_list, Candidate, Out_list) which is part of the implication rule (therefore it is activated when it applies). In the Prolog programming terminology it is said that variable Out_list is “free” before, but it becomes “bound” to the updated successor list value when predicate append_op applies. In this way Out_list is returned after the activation of the implication rule’s left side predicate (update_successors1).
9. From high-Level program code to RTL implementation The well-known high-level synthesis benchmark of the differential equation solver [17], was coded and executed through the front-end and back-end compilers and the results are discussed in this section. The main part of the source code which was given as input to the front-end compiler is shown in the code list of Figure 3. The body of the main subroutine (procedure) is 10 lines of ADA (or behavioural VHDL) code. This contains a wide range of simple and complex operation types including conditionally executed loop. The IPF code which was generated from the whole program code (including the subroutine in Figure 3) contains 159 Prolog predicate facts. The program statements table, and parts of the hierarchical modules table and the subroutine’s data object declarations table are shown in Figure 4. It can be seen that in all of the example’s IPF code statements the module name of the subroutine is referenced ("diffeq") so that it is differentiated from other modules of this design (which do not exist for this design but, they are a rule of thumb for most of practical designs in real applications). Therefore, multi-module designs are easily accepted but also are encouraged in the hardware compilation flow of this paper. The constant "libpart" in the hierarchical module declaration statement, is used for precompiled and re-usable designs. At the bottom of the list in this IPF code excerpt two of the data object table statements are shown that describe the subroutine’s input parameter declarations for parameters A and DX. For these two data declarations the constant “par_in” denotes an input parameter of the source program or input port for the generated hardware co-processor block. Amongst the other constants in the program table statements, their 4 th positional parameter constants give the reference of the operator used in every elementary operation, taken from a library of operators which is defined as well in the operators table (not shown in this example) of the same IPF file.
110
Journal of Next Generation Information Technology Volume 1, Number 1, May 2010
… procedure DIFFEQ(A,DX,X,U : in integer; Y : in out integer) is XIN,UIN,XL,UL,YL : integer;
…
begin XIN := X; UIN := U; while XIN < A loop XL := XIN + DX; UL := UIN - 3*XIN*UIN*DX - 3*Y*DX; YL := Y + UIN*DX; XIN := XL; UIN := UL; Y := YL; end loop; end DIFFEQ;
Figure 3. The source code excerpt of the differential equation solver high-level synthesis benchmark
…
…
hierarchy_part( 2,"diffeq", 1,"libpart", 2, 2, 0) prog_stmt("diffeq", 1, 0, 102, 0, 3, 6, 2) prog_stmt("diffeq", 2, 0, 102, 0, 4, 7, 3) prog_stmt("diffeq", 3, 0, 13, 6, 1, 11, 4) prog_stmt("diffeq", 4, 0, 106, 0, 11, 19, 5) prog_stmt("diffeq", 5, 0, 62, 6, 2, 8, 6) prog_stmt("diffeq", 6, 0, 64, 7, 2, 13, 7) prog_stmt("diffeq", 7, 0, 64, 6, 13, 14, 8) prog_stmt("diffeq", 8, 0, 64, 12, 14, 15, 9) prog_stmt("diffeq", 9, 0, 64, 5, 2, 16, 10) prog_stmt("diffeq", 10, 0, 64, 12, 16, 17, 11) prog_stmt("diffeq", 11, 0, 62, 15, 17, 18, 12) prog_stmt("diffeq", 12, 0, 63, 7, 18, 9, 13) prog_stmt("diffeq", 13, 0, 64, 7, 2, 19, 14) prog_stmt("diffeq", 14, 0, 62, 5, 19, 10, 15) prog_stmt("diffeq", 15, 0, 102, 0, 8, 6, 16) prog_stmt("diffeq", 16, 0, 102, 0, 9, 7, 17) prog_stmt("diffeq", 17, 0, 102, 0, 10, 5, 18) prog_stmt("diffeq", 18, 0, 110, 0, 0, 3, 19) data_stmt("diffeq","a", 1, 2,"par_in",sym("a")) data_stmt("diffeq","dx", 2, 2,"par_in",sym("dx"))
Figure 4. The main part of the benchmark’s IPF statements
111
Intermediate Predicate Format for Design Automation Tools Michael F. Dossis Part of the VHDL RTL code hardware description generated from the back-end compilation is shown in Figure 5. The demonstrated part of implementable VHDL code contains the two declarations for ports A and DX, as well as some critical hardware FSM states for the subroutine’s body. These include the loop’s branching decision processing (states 3, 4 and 18) as well as the scheduled first operation from the loop’s body (state 5). It is obvious from the listed VHDL code that it is free from any particular implementation technology attributes, in contradiction to produced code by most of the existing synthesis systems.
… ENTITY diffeq IS port(
… a : IN std_logic_vector(31 DOWNTO 0); dx : IN std_logic_vector(31 DOWNTO 0);
… ); END diffeq ; ARCHITECTURE rtl OF diffeq IS
… fsm_core : PROCESS (clock, reset) BEGIN
… WHEN state_3 => state