Esterel Language v7 Ref Man

Download as pdf or txt
Download as pdf or txt
You are on page 1of 207

The Esterel v7 Reference Manual Version v7 30 initial IEEE standardization proposal

Esterel Technologies 679 av. Dr. J. Lefebvre 06270 Villeneuve-Loubet, France www.esterel-technologies.com c Esterel Technologies, 2005 3 November 2005

Contents
1 Introduction 1.1 The Esterel v7 30 version . . . . . . . . . . . . . . . . . . . . . 1.1.1 Esterel v7 30 feature summary . . . . . . . . . . . . . . 1.1.2 Clocking features in Esterel v7 30 . . . . . . . . . . . . 1.2 Structure of the Language Reference Manual . . . . . . . . . . 1.2.1 Structure of Part A . . . . . . . . . . . . . . . . . . . . 1.2.2 Improvements in v7 30 vs. v7 20 for single-clock design 1.3 Lexical issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1 Identiers and keywords . . . . . . . . . . . . . . . . . 1.3.2 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3 Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 3 4 4 5 5 6 6 6 6 9 9 10 11 11 12 12 13 13 14 15 18 18 19 19 20 20 21 22 23 24

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

2 Units and Genericity 2.1 Data, interface, and module units . . . . . . . . . . . . . . . . . . . . . 2.2 A basic example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Making the units generic . . . . . . . . . . . . . . . . . . . . . . 2.2.2 Instantiating a generic unit . . . . . . . . . . . . . . . . . . . . . 2.3 A memory example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 A non-generic memory . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Making the memory generic . . . . . . . . . . . . . . . . . . . . . 2.3.3 Reconstructing a specic memory . . . . . . . . . . . . . . . . . . 2.3.4 Ports and run statements . . . . . . . . . . . . . . . . . . . . . . 2.3.5 Multiple instantiation of generic data: renaming dened objects 2.3.6 Composing genericity . . . . . . . . . . . . . . . . . . . . . . . . 2.3.7 Implicit generic instantiation . . . . . . . . . . . . . . . . . . . . 2.4 Data genericity specication . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.1 Data declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Generic data extension . . . . . . . . . . . . . . . . . . . . . . . 2.4.3 Generic extension example . . . . . . . . . . . . . . . . . . . . . 2.4.4 Using interfaces in ports and runs . . . . . . . . . . . . . . . . . 2.4.5 Name spaces, visibility, and declaration uniqueness . . . . . . . . 2.4.6 Host objects are global . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Forgetting unit components . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

3 Data 25 3.1 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.1.1 The bool primitive type . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.1.2 The unsigned primitive types . . . . . . . . . . . . . . . . . . . . . . 25 i

ii 3.1.3 The signed primitive types 3.1.4 The oat primitive type . . 3.1.5 The double primitive type 3.1.6 The string primitive type . 3.1.7 Enum types . . . . . . . . 3.1.8 Host types . . . . . . . . . 3.1.9 Dened types . . . . . . . 3.1.10 Array types . . . . . . . . . 3.1.11 Bitvectors . . . . . . . . . 3.1.12 Generic types . . . . . . . Constants . . . . . . . . . . . . . . 3.2.1 Dened constants . . . . . 3.2.2 Host constants . . . . . . . 3.2.3 Generic constants . . . . . Functions . . . . . . . . . . . . . . 3.3.1 Predened functions . . . . 3.3.2 Host functions . . . . . . . 3.3.3 Generic functions . . . . . Procedures . . . . . . . . . . . . . 3.4.1 Host procedures . . . . . . 3.4.2 Generic procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CONTENTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 28 28 28 28 29 29 30 31 32 32 32 32 33 33 33 33 34 34 34 34 35 35 36 36 36 36 37 37 37 37 38 38 39 39 39 40 40 41 41 41 42 42 42 43 43 43

3.2

3.3

3.4

4 Arithmetic and Bitvectors 4.1 Boolean operators . . . . . . . . . . . . 4.2 Unsigned arithmetic . . . . . . . . . . . 4.2.1 Unsigned addition . . . . . . . . 4.2.2 Unsigned subtraction . . . . . . 4.2.3 Unsigned multiplication . . . . . 4.2.4 Unsigned power . . . . . . . . . 4.2.5 Unsigned division . . . . . . . . 4.2.6 Unsigned modulo . . . . . . . . 4.2.7 Unsigned binary size . . . . . . 4.2.8 Unsigned saturation . . . . . . . 4.2.9 Unsigned binary truncation . . . 4.2.10 Unsigned assert . . . . . . . . . 4.2.11 Unsigned comparisons . . . . . . 4.2.12 Unsigned size checks . . . . . . 4.3 Signed arithmetic . . . . . . . . . . . . 4.3.1 Signed unary plus and minus . . 4.3.2 Signed addition and subtraction 4.3.3 Signed multiplication . . . . . . 4.3.4 Signed division . . . . . . . . . . 4.3.5 Signed binary size . . . . . . . . 4.3.6 Signed saturation . . . . . . . . 4.3.7 Signed binary truncation . . . . 4.3.8 Signed absolute value . . . . . . 4.3.9 Signed comparisons . . . . . . . 4.3.10 Signed assert . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

CONTENTS 4.3.11 Signed assert unsigned . . . . . . . . . . 4.3.12 Mixed signed / unsigned arithmetic . . . Bitvector operations . . . . . . . . . . . . . . . . 4.4.1 Bitvector equality . . . . . . . . . . . . . 4.4.2 Bitvector shifts . . . . . . . . . . . . . . 4.4.3 Bitvector concatenation . . . . . . . . . . 4.4.4 Bitvector extension . . . . . . . . . . . . 4.4.5 Bitvector reverse . . . . . . . . . . . . . . Bitvector maps . . . . . . . . . . . . . . . . . . . Encoders: from unsigned to bitvectors and back 4.6.1 Binary encoding / decoding . . . . . . . 4.6.2 Onehot and Gray encoding . . . . . . . . 4.6.3 User-dened encoding . . . . . . . . . . . 4.6.4 Combining encodings . . . . . . . . . . . 4.6.5 From signed to bitvectors and back . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iii 43 43 43 44 44 44 45 45 46 47 47 48 49 49 49 51 51 52 52 53 53 54 54 55 56 57 57 58 58 59 59 60 60 61 62 62 63 63 64 64 65 65 65 66 66 67 67

4.4

4.5 4.6

5 Signals and Variables 5.1 Interface and local signals, assertions, and oracles . . 5.2 Signal status and value . . . . . . . . . . . . . . . . . 5.2.1 Signal status . . . . . . . . . . . . . . . . . . . 5.2.2 Signal Value . . . . . . . . . . . . . . . . . . . 5.2.3 Pure, full, and valueonly signals . . . . . . . . 5.3 Standard signals . . . . . . . . . . . . . . . . . . . . . 5.3.1 Standard signal emission and reception . . . . 5.3.2 Value persistency and initialization . . . . . . 5.3.3 The status pre(S) operator . . . . . . . . . . . 5.3.4 The value pre(?S) operator . . . . . . . . . . . 5.3.5 Standard signal submodule connection . . . . 5.4 Registered signals . . . . . . . . . . . . . . . . . . . . 5.4.1 Registered signal declaration and emission . . 5.4.2 Value persistency and initialization . . . . . . . 5.4.3 The next(R) status operator . . . . . . . . . . 5.4.4 The next(?R) value operator . . . . . . . . . . 5.4.5 Registered signals submodule connection . . . 5.4.6 Building data pipelines with registered signals . 5.5 Temporary signals . . . . . . . . . . . . . . . . . . . . 5.5.1 Temporary signal declaration . . . . . . . . . . 5.5.2 Temporary signals submodule connection . . . 5.5.3 Temporary signal initialization . . . . . . . . . 5.5.4 Temporary signal value denition . . . . . . . 5.6 Single vs. combined signals . . . . . . . . . . . . . . . 5.7 Signal arrays . . . . . . . . . . . . . . . . . . . . . . . 5.7.1 Signal array status . . . . . . . . . . . . . . . 5.7.2 Signal array value . . . . . . . . . . . . . . . . 5.7.3 Combined arrays . . . . . . . . . . . . . . . . . 5.8 Variables . . . . . . . . . . . . . . . . . . . . . . . . . 5.8.1 Variable declaration and scope . . . . . . . . . 5.8.2 Persistent vs. temporary variables . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

iv 5.8.3

CONTENTS Variables cannot be shared . . . . . . . . . . . . . . . . . . . . . . . 67 69 69 69 70 71 72 73 73 74 74 75 76 76 77 79 79 79 80 80 81 81 82 82 85 86 86 86 87 88 88 88 88 89 90 90 93 93 94 94 95 95 95 96 96

6 Interfaces and Module Headers 6.1 Signal declaration overview . . . . . . . . . . . . . . . . . . . . . 6.1.1 Signal declaration syntax . . . . . . . . . . . . . . . . . . 6.1.2 Signal declaration attributes . . . . . . . . . . . . . . . . 6.1.3 Interface vs. module attributes . . . . . . . . . . . . . . . 6.1.4 Interface signal renement . . . . . . . . . . . . . . . . . 6.2 Interfaces and Ports . . . . . . . . . . . . . . . . . . . . . . . . . 6.2.1 Simple interfaces . . . . . . . . . . . . . . . . . . . . . . . 6.2.2 The mirror of a simple interface . . . . . . . . . . . . . . 6.2.3 Ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2.4 Port arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3 General Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Full interface extension and port denition . . . . . . . . 6.3.2 Selective interface extension . . . . . . . . . . . . . . . . . 6.3.3 Data renaming for generic interface extension . . . . . . 6.3.4 Interface extension does not share components . . . . . . 6.4 Relations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.4.1 Input relations . . . . . . . . . . . . . . . . . . . . . . . . 6.4.2 Output relations . . . . . . . . . . . . . . . . . . . . . . . 6.4.3 Relation inheritance . . . . . . . . . . . . . . . . . . . . . 6.4.4 Why not allowing more general relations . . . . . . . . . . 6.5 Module headers . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.5.1 Memory assignment control by mem and reg renements 7 Expressions 7.1 Expression Leaves . . . . . . . . . . . 7.1.1 Signal status tests . . . . . . . 7.1.2 Testing the previous status of a 7.1.3 Reading signal values . . . . . 7.1.4 Reading signal previous values 7.1.5 Reading variable values . . . . 7.1.6 Port component access . . . . 7.2 Operators . . . . . . . . . . . . . . . . 7.2.1 Applying operators to arrays . 7.3 Function calls . . . . . . . . . . . . . 7.4 Array indexation . . . . . . . . . . . . 8 Statements 8.1 Statement overview . . . . . 8.1.1 Syntactic matters . . 8.2 Basic control statements . . 8.3 Variable handling statements 8.3.1 Assignment . . . . . . 8.3.2 Procedure call . . . . 8.4 Emit, sustain, and assert . . 8.4.1 Emission overview . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

. . . . . . . .

CONTENTS 8.4.2 Simple emission . . . . . . . . . . . . . . . . . . 8.4.3 Pure simple emission . . . . . . . . . . . . . . . 8.4.4 Pure array emission . . . . . . . . . . . . . . . . 8.4.5 Valued simple emission . . . . . . . . . . . . . . 8.4.6 Valued array emission . . . . . . . . . . . . . . . 8.4.7 Assertions . . . . . . . . . . . . . . . . . . . . . 8.4.8 Simple emissions with case lists in rhs . . . . . . 8.4.9 Concurrent emission . . . . . . . . . . . . . . . . 8.4.10 Conditional emissions . . . . . . . . . . . . . . . 8.4.11 Sequenced emission emit seq . . . . . . . . . . 8.4.12 Which case or concurrent structure to choose? . Sequencing . . . . . . . . . . . . . . . . . . . . . . . . . Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.6.1 Basic loops . . . . . . . . . . . . . . . . . . . . . 8.6.2 Instantaneous loops . . . . . . . . . . . . . . . . 8.6.3 Always loops . . . . . . . . . . . . . . . . . . . . 8.6.4 Repeat loops . . . . . . . . . . . . . . . . . . . . The if test statement . . . . . . . . . . . . . . . . . . . 8.7.1 Case tests . . . . . . . . . . . . . . . . . . . . . Concurrency . . . . . . . . . . . . . . . . . . . . . . . . 8.8.1 The parallel operator . . . . . . . . . . . . . . . 8.8.2 The for-dopar replication statement . . . . . . . 8.8.3 Replication examples . . . . . . . . . . . . . . . . Delays . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.9.1 Simple delays . . . . . . . . . . . . . . . . . . . 8.9.2 Immediate delays . . . . . . . . . . . . . . . . . 8.9.3 Count delays . . . . . . . . . . . . . . . . . . . . Temporal statements . . . . . . . . . . . . . . . . . . . 8.10.1 The await statement . . . . . . . . . . . . . . . 8.10.2 The abort-when statements . . . . . . . . . . . . 8.10.3 The abort-after statements . . . . . . . . . . . . 8.10.4 Temporal loops . . . . . . . . . . . . . . . . . . Traps . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8.11.1 Nested traps . . . . . . . . . . . . . . . . . . . . 8.11.2 Trap Handlers . . . . . . . . . . . . . . . . . . . 8.11.3 Concurrent Traps . . . . . . . . . . . . . . . . . . 8.11.4 Valued Traps . . . . . . . . . . . . . . . . . . . . Suspension statements . . . . . . . . . . . . . . . . . . . 8.12.1 The suspend statement . . . . . . . . . . . . . . 8.12.2 The weak suspend statement . . . . . . . . . . . 8.12.3 Suspension and weak suspension examples . . . 8.12.4 Interaction with traps . . . . . . . . . . . . . . . The nalize statement . . . . . . . . . . . . . . . . . . . Local signal declaration . . . . . . . . . . . . . . . . . . 8.14.1 Local signal and port declaration and renement 8.14.2 Local signals and suspension . . . . . . . . . . . 8.14.3 Local signal reincarnation . . . . . . . . . . . . . Open port declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

v 96 97 98 98 99 99 100 100 101 103 105 107 108 108 108 109 110 111 111 112 112 112 113 114 114 115 115 116 116 117 119 120 121 121 121 122 122 123 123 124 125 130 132 134 134 135 136 137

8.5 8.6

8.7 8.8

8.9

8.10

8.11

8.12

8.13 8.14

8.15

vi

CONTENTS 8.16 Local variable declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

9 The run module instantiation statement 9.1 Basic syntax . . . . . . . . . . . . . . . . . . . . . . . . . 9.1.1 Naming a submodule instance . . . . . . . . . . . 9.2 Argumentless run statement . . . . . . . . . . . . . . . . . 9.3 Data substitution . . . . . . . . . . . . . . . . . . . . . . 9.4 Signal binding . . . . . . . . . . . . . . . . . . . . . . . . 9.4.1 Expression binding . . . . . . . . . . . . . . . . . 9.5 Input binding . . . . . . . . . . . . . . . . . . . . . . . . 9.5.1 Full signal input binding . . . . . . . . . . . . . . . 9.5.2 Value-only input binding . . . . . . . . . . . . . . 9.5.3 Input initialization examples . . . . . . . . . . . . 9.6 Output binding . . . . . . . . . . . . . . . . . . . . . . . 9.6.1 Output binding behavior . . . . . . . . . . . . . . 9.6.2 Output signal initial value transmission . . . . . . 9.7 Inputoutput binding . . . . . . . . . . . . . . . . . . . . . 9.8 Port binding . . . . . . . . . . . . . . . . . . . . . . . . . 9.9 Signal binding example . . . . . . . . . . . . . . . . . . . . 9.10 Null binding . . . . . . . . . . . . . . . . . . . . . . . . . 9.11 Value passing rules . . . . . . . . . . . . . . . . . . . . . . 9.11.1 Value passing examples . . . . . . . . . . . . . . . 9.11.2 Passing values from registered submodule outputs 9.12 Resetting submodules . . . . . . . . . . . . . . . . . . . . 9.12.1 Strong reset . . . . . . . . . . . . . . . . . . . . . . 9.12.2 Weak reset . . . . . . . . . . . . . . . . . . . . . . 9.12.3 Why passing reset signals . . . . . . . . . . . . . . 10 Oracles 11 Macro-expansion of Derived Constructs 11.1 Expansion of signal constructs . . . . . 11.1.1 Expansion of pre . . . . . . . . . 11.1.2 Expansion of next . . . . . . . . 11.2 Expansion of delays . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . .

141 141 142 142 143 143 144 144 144 145 146 147 147 148 149 149 149 150 150 150 151 151 151 152 152 153

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

155 . 155 . 155 . 156 . 156 157 . 157 . 157 . 157 . 157 . 157 . 157 . 158 . 158 . 158 . 158 . 158 . 159

12 Run-time errors 12.1 Data-related errors . . . . . . . . . . . . . . . . . 12.1.1 Unsigned subtraction error . . . . . . . . . 12.1.2 Unsigned division error . . . . . . . . . . . 12.1.3 Unsigned modulo error . . . . . . . . . . . 12.1.4 Unsigned binsize error . . . . . . . . . . . . 12.1.5 Unsigned out of range error . . . . . . . . 12.1.6 Signed division error . . . . . . . . . . . . 12.1.7 Signed out of range error . . . . . . . . . . 12.1.8 Bitvector onehot2u conversion error . . . . 12.2 Signal-related errors . . . . . . . . . . . . . . . . . 12.2.1 Access to uninitialized signal value error . 12.2.2 Bad access to temporary signal value error

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

12.2.3 Multiple emission of single signal error . 12.2.4 Bad access to a signal array error . . . . 12.3 Variable-related errors . . . . . . . . . . . . . . . 12.3.1 Access to uninitialized variable error . . 12.3.2 Illegal access to temporary variable error 12.3.3 Bad access to a variable array error . . . 12.3.4 Sharing a variable error . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

159 159 159 159 159 159 159 161 161 162 165 166 167 168 168 170 172 173 175 176 178 178 179 180 180 180 180 181 181

13 Introduction to multiclock Esterel design 13.1 RTL design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.1.1 Translating Esterel programs into RTL circuits . . . . . . . 13.2 The need for multiple clocks . . . . . . . . . . . . . . . . . . . . . 13.2.1 Metastability issues . . . . . . . . . . . . . . . . . . . . . . 13.2.2 Some multiclock design terminology . . . . . . . . . . . . . 13.2.3 Globally Asynchronous Locally Synchronous (GALS) design 13.3 Multiclock design in Esterel . . . . . . . . . . . . . . . . . . . . . . 13.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3.2 The importance of input sampling . . . . . . . . . . . . . . 13.3.3 Synchronizers . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3.4 Clock denition . . . . . . . . . . . . . . . . . . . . . . . . 13.3.5 Hierarchical design . . . . . . . . . . . . . . . . . . . . . . 13.4 Semantics of multiclock designs in Classic Esterel . . . . . . . . . 13.4.1 Replacing clocking by weak suspension . . . . . . . . . . . 13.4.2 The mcrun statement . . . . . . . . . . . . . . . . . . . . . 13.4.3 Simulating clock generation . . . . . . . . . . . . . . . . . 13.4.4 Simulating metastability issues . . . . . . . . . . . . . . . . 13.5 Hardware synthesis from multiclock designs . . . . . . . . . . . . . 13.5.1 True multiclock synthesis . . . . . . . . . . . . . . . . . . . 13.5.2 Simulated multiclock synthesis . . . . . . . . . . . . . . . . 13.5.3 Dealing with falling-edge clocks . . . . . . . . . . . . . . . 14 Esterel clocks and multiclock units 14.1 Clocks . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.1.1 Clock declarations . . . . . . . . . . . . . . . . . 14.1.2 Clock usage . . . . . . . . . . . . . . . . . . . . 14.2 Multiclock units . . . . . . . . . . . . . . . . . . . . . . 14.2.1 Multiclock headers . . . . . . . . . . . . . . . . 14.2.2 Multiclock unit extension . . . . . . . . . . . . . 14.2.3 Multiclock body structure . . . . . . . . . . . . 14.2.4 Combinational computations in multiclock units 14.2.5 Clock equations . . . . . . . . . . . . . . . . . . 14.2.6 Running modules in multiclock units . . . . . . 14.2.7 Running multiclock units in multiclock units . . 14.2.8 Running multiclock units in modules . . . . . . Bibliography index

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

183 . 183 . 183 . 183 . 184 . 184 . 185 . 185 . 186 . 186 . 187 . 187 . 187 188 189

CONTENTS

Chapter 1

Introduction
This document is the reference manual of the Esterel v7 language, release v 30. Esterel v7 is a major evolution of the previous version Esterel v5 [4, 5, 6], which makes it possible to design much richer systems and in particular hardware and software systems that combine complex data path and control path features.

1.1

The Esterel v7 30 version

Esterel v7 30 is a major evolution of Esterel v7. Compared to the previous version v7 20, Esterel v7 30 involves two main extensions: Extension to the base Esterel language: introduction of the new weak suspend statement that provides support for clock gating, richer syntax for conditional emission in emit and sustain statements, and a few local improvements. Extension to multiclock design. This is a major step, since Esterel was strictly single-clock until now. This extension is conservative: all correct v7 20 programs are accepted and have the same semantics.

1.1.1

Esterel v7 30 feature summary

Here is a summary of the main features of Esterel v7 30: Synthesizability of all programs in hardware and software, including multiclock designs (new in v7 30). Separation of data, interface, and module units for better program structuring and reuse. Data genericity of interface and module units. Arrays of any dimension and arrays of arrays of any base type. Array expressions, with array slicing on any number of dimensions. Bitvectors seen as arrays of Booleans. Arbitrary precision exact arithmetic, dealing with abstract unsigned and signed numbers. 3

CHAPTER 1. INTRODUCTION Multiple number encodings to switch between numbers and their representations as bitvectors . p Equational denitions of signals, which nicely complement Esterel imperative statements and are of major use in data path descriptions (improved in v7 30). Powerful temporal statements to handle complex control (improved in v7 30). Static replication of statements, fundamental for architectural design. Built-in assertions to check dynamic properties by simulation or formal verication.

1.1.2

Clocking features in Esterel v7 30

As far as clocking features are concerned, Esterel now fully supports multiclock designs and clock gating Multiclock design relies on the addition of a new clock kind of signal and of a new multiclock unit that composes standard single-clocked Esterel modules and denes a base clock for each of them. Sequential computation occurs only within the classic Esterel single-clocked modules. Multiclock units can only perform instantaneous (combinational) computations and derive new clocks from existing clocks by downsampling. This separation of concerns between single-clocked sequential modules that perform sequential commputations and multiclock networks that connect them combinationally is faithful to the classical Globally Asynchronous Locally Synchronous (GALS) multiclock design paradigm. The exact eect of clock gating in RTL circuits is provided by the new weak suspend statement, originally introduced by K. Schneider in [13]: combinational calculations of the body are performed normally, but its state is unchanged when the suspension condition is true. As far as hardware implementation is concerned, a design that uses weak suspend (or suspend) can be implemented with or without clock gating using compilation options. For instance, clock gating can be used for ASIC implementation while register disabling logic can be used for FPGA implementation (see the Esterel v7 compiler documentation for details). Thus, clock gating is promoted from an implementation-related energy-saving feature to a truly behavioral construct fully compatible with the rest of the language. Clock gating is available for single-clock and multiclock designs.

1.2

Structure of the Language Reference Manual

We chose to present the manual in two parts: Part A deals with single-clock design, while Part B with multiclock design. Part A is a mild extension of the previous Esterel v7 20 Language Reference Manual. It is self-contained and sucient for single-clock design. We think that providing only an incremental change from the previous version will make it easier for the reader to upgrade from the previous version. Multiclock design requires preliminary understanding of single-clock design. Therefore, we think it is wiser to present it separately in Part B.

1.2. STRUCTURE OF THE LANGUAGE REFERENCE MANUAL

1.2.1

Structure of Part A

For Part A, Chapter 2 presents the data, interface, and module units, and details the data genericity mechanism. Chapter 3 presents the data objects. Chapter 4 presents the unsigned and signed exact arithmetic. Chapter 5 presents signals and variables. Chapter 6 presents signal declarations in interfaces and modules. Chapter 7 presents signal and data expressions. Chapter 8 presents the Esterel v7 statements. Chapter 9 presents the submodule run statement. Chapter 10 present oracles, a special kind of uncontrolled signals to model non-determinism. Chapter 11 presents the macro-expansion of complex constructs into simpler ones. Finally, Chapter 12 presents the run-time errors that Esterel v7 programs can trigger. For Part B, Chapter 13 presents the basics of multiclock design. Since multiclock design is much more delicate than single-clock design, the chapter presents a thorough studies of examples. Finally, Chapter 14 presents the syntax and constraints of multiclock units.

1.2.2

Improvements in v7 30 vs. v7 20 for single-clock design

It is now possible to use an identiers that has the same name as a keyword, see Section 1.3.1. One can use the character in numerical or bitvector constants, see Section 3.1.2 and Section 3.1.11. One can convert a Boolean into an unsigned number using the bool2u function, see Section 3.1.1 and Section 3.3.1. The new assert_unsigned<N>(exp) assertion expression asserts that a signed expression has a positive or null value, see Section 4.3.11. Bitvector maps can be named to ease data visualization during program simulation and debugging, see Section 4.5. Interface selective extension makes it possible to only extend the inputs and output signals of an interface. It is described in Section 6.3.1. Variable and signal arrays can be initialized using a single literal instead of an array literal when the whole array is initialized by a single value, see Section 3.1.10, Section 5.1, Section 5.8.1, Section 8.14, and Section 8.16. The emit and sustain statements have been extended by allowing cases in the right-hand-sides and conditional structures around equations, see Section 8.4.10. same condition. The new weak suspend statement for clock-gating is presented in Section 8.12.2, with extensive examples. In local signal declarations, one can now use the form extends Intf to declare all signals and ports of Intf, see Section 8.14.1. In run statements, the new null binding / S makes it possible not to bind a submodule interface signal sets S, the signal tehn becoming local to the submodule. See Section 9.1 and Section 9.10 for details.

CHAPTER 1. INTRODUCTION

1.3
1.3.1

Lexical issues
Identiers and keywords

Identiers start with a letter and can contain letter, digits, and characters. They are case-sensitive and of unlimited length. The list of keywords is as follows:
abort abs a f t e r always and a s s e r t a s s e r t u n s i g n e d await bin2u bin2s b i n s i z e bool bool2u c a l l case c l o c k code2u combine constant data d e f a u l t do dodown dopar double doup each e l s e emit end enum every e x i t extends f a l s e f i n a l i z e f l o a t for function g e n e r i c gray2u h a l t handle host i f immediate i n i n i t inout input inputoutput i n t e g e r i n t e r f a c e l c a t loop main map mcat mcrun mem mirror mod module mul t i cl oc k mux never next not nothing onehot2u open or o r a c l e out output pause port p o s i t i v e pre pre1 procedure r e f i n e reg reg1 r e l a t i o n repeat r e s e t r e v e r s e run s2bin s a t seq sextend s i g n a l s i g n signed s t r i n g suspend s u s t a i n temp then t i c k times trap true type u2bin u2code u2gray u2onehot unsigned upto value var weak when with xor

It is possible to use a keyword as an identier by prexing it by \. For instance, \emit is a valid identier whose actual name is emit.

1.3.2

Comments

Single-line comments start with //, and multiline comments are bracketed by /* and */ (for compatibility with Esterel v5, one can also use % for single-line comments and % and % to bracket multiline comments). Comments starting by /// or /** are propagated in special ways in generated code and documentation, see the compiler doceumentation for details.

1.3.3

Labels

One can use the @ symbol after a keyword or operator to dene a label to be propagated to the generated code in some compiler-dened way. Here are examples:
pause@WAIT1 halt@ " hello x + @smart y

world "

The way labels are propagated is compiler-dependent, see the compiler documentation for details.

1.3. LEXICAL ISSUES

Acknowledgements: A preliminary design was performed by G. Berry, from Esterel technologies, and M. Kishinevsky from Intel Strategic CAD Lab, Portland. An early document about this design is available on www.estereltechnologies.com. Some constructs have been borrowed from the Esterel++ design done at Dassault Aviation. The language has beneted of many discussions with Stephen Edwards, Columbia University, Olivier Tardieu, INRIA, and many users at Texas Instruments, ST Micro, Philips, and Xilinx.

CHAPTER 1. INTRODUCTION

Chapter 2

Units and Genericity


2.1 Data, interface, and module units

An Esterel v7 30 program is composed of toplevel named units, which can be of three kinds: data units, interface units, and module units. The role of the dierent units is as follows: A data unit declares data types, constants, functions, or procedures. These objects can be either dened in place, host, i.e. dened in the host language to which Esterel is compiled, or generic for better reuse, as explained below. A data unit can extend one or several other data units by importing all the objects they declare. Simple or multiple data extension will be fully studied in Section 2.4.5. An interface unit primarily declares input / output signals. It can extend other data and interface units by importing all their data and signal declarations. Extension can be selective, i.e. limited to the input or output signals of the extended interface. The mirror mirror Inf of an interface Intf is an interface with the same signals but input / output directionalities exchanged. An interface unit can also declare data objects and extend data units locally. Interface units can be generic w.r.t. data objects. Interface extension will be introduced in Section 2.4.5 and fully studied in Chapter 6. A module unit denes a reactive behavior. It is composed of a module header, which contains declarations, and of a body which is an executable statement. As the other units, a module can be generic w.r.t. data objects. A module header can extend data and interface units by importing all the objects they declare. There is no module behavior extension, this notion being much less clear than data and interface extension. It can also declare locally data objects and interface signals. Data genericity means that units and modules can be dened in an abstract way, depending on generic parameters. For instance, a module that delays a value by some number of ticks can be declared with data a generic value type T and a generic delay constant D. When instantiating the module, one can for instance specify the type T to be bool and the delay D to be 3. Genericity is studied in Section 2.2.1. Modules can instantiate the behavior of other modules using the run executable statement described in Chapter 9. One of the modules must be called the main module. It is the one to be executed. 9

10

CHAPTER 2. UNITS AND GENERICITY

All the unit names share the same namespace. Therefore, data, interface, and module units must have distinct names. Units make object names visible either directly when using extension or through the dot notation X.A for port elds. There should also be no conict between unit names and object names. The visibility rules will be made precise below for each kind of unit. Before entering into details about units and genericity, we give two illustrative examples to gradually introduce these concepts.

2.2

A basic example

Here is a main module written in the most compact form, grouping all data and interface declarations in the module header. The module takes a signed number as input and doubles it on the output. There is precise size control on the input and output values, which are respectively in the ranges [8..7] and [16..15]:
main module Double : constant N : unsigned = 8; input I : signed <N >; output O : signed <2* N >; every I do emit ? O <= 2 * ? I end every end module

Splitting data and interface signals in separate units make it possible to separate the data / interface / behavior concerns for better readability and reuse. Here is the same example with maximally split data and interface units:
data SizeData : constant N : unsigned = 8; end data i n t e r f a c e DoubleIntf : extends data SizeData ; input I : signed <N >; output O : signed <2* N >; end i n t e r f a c e module Double : extends i n t e r f a c e DoubleIntf ; every I do emit ? O <= 2 * ? I end every end module

The extends declarations specify that all the objects dened in the extended unit are imported in the current unit, in a recursive way. Therefore, the constant denition N = 8 is imported within Double since Double extends DoubleIntf which itself extends SizeData. It is also possible and sometimes better to split concerns a little less, in order to avoid introducing too many small units. Here, we can suppress the data unit and directly declare the constant N in the interface:

2.2. A BASIC EXAMPLE


i n t e r f a c e DoubleIntf : constant N : unsigned = 8; input I : signed <N >; output O : signed <2* N >; end i n t e r f a c e

11

An interface can extend another interface by importing all its data and signal components:
i n t e r f a c e ExtendedIntf : extends i n t e r f a c e DoubleIntf ; output X : bool [ N ]; end i n t e r f a c e

The components of ExtendedIntf are N, I, O, and X. The latter is a bitvector of size N, see Chapter 3. If one is only interested in importing the constant N, one can extend only the data part of DoubleIntf using the extend data keywords, thus forgetting about the signals I and O:
data P ar tiall yExte ndedI ntf : extends data DoubleIntf ; output X : bool [ N ]; end data

Then, the only components of PartiallyExtendedIntf are N and X.

2.2.1

Making the units generic

So far, we have hard-wired the value 8 for N in its declaration. It is possible to make the units generic w.r.t. the value of N, by prexing the declaration with generic and not giving the value. Here is a generic interface declaration:
i n t e r f a c e DoubleIntf : g e n e r i c constant N : unsigned ; input I : signed <N >; output O : signed <2* N >; end i n t e r f a c e

The generic constant N acts as a size parameter for the input signal I and the output signal O. The type signed<N> of I is that of all signed numbers from N to N 1 included, while the type signed<2*N> of O is that of all signed numbers from 2 N to 2 N 1, see Section 4.3 below for details. These dened types themselves depend on the generic parameter N; we call them dened generic types. Genericity is inherited by simple extension: without having to change its code, Double above automatically becomes generic in the same constant N since it imports the generic declaration of N. Notice that the generic parameters are declared together with normal data objects, unlike in C++-like languages where generics are declared in a specic parameter list placed before the class declaration.

2.2.2

Instantiating a generic unit

The generic interface and module are instantiated by substituting actual objects for generic ones using the [./.] substitution notation below:

12

CHAPTER 2. UNITS AND GENERICITY


i n t e r f a c e DoubleIntf8 : extends i n t e r f a c e DoubleIntf [ constant 8 / N ] end i n t e r f a c e module Double8 : extends i n t e r f a c e DoubleIntf8 ; run Double [ constant 8 / N ] end module

For the interface, the extends declaration simply includes the extended interface into the new one after substitution of actual data objects to generic ones. For the module, the run statement does more than instantiating the generic module: it also executes its code. Here, the run statement is executed at toplevel, realizing the simplest behavior instantiation. Generally speaking, run statements can appear anywhere a statement can.

2.3

A memory example

The next example is that of a simple memory. We start with a non-generic memory, then make it generic, and instantiate it in dierent ways.

2.3.1

A non-generic memory

A non-generic memory may have object type, size, and address type, explicitly dened as follows:
data MemData : type ObjType = bool [32]; constant MemSize : unsigned = 256; type Address = unsigned < MemSize >; end data

The memory interface is declared as follows:


i n t e r f a c e MemIntf : extends data MemData ; input Write : Address ; input DataIn : value ObjType ; input Read : Address ; output DataOut : value ObjType ; end i n t e r f a c e

The extends data declaration imports all data objects of MemData into MemIntf. The other objects declared in MemIntf are interface signals. Valued signals such as Read or Write carry two kinds of information: a Boolean status, absent or present (also called low or high, unset or set, 0 or 1, etc.), and a value of the type that follows the colon. The status is tested by giving the name of the signal, as in if Write then..., and the value is read using the ? operator, as for ?Write which denotes the address carried by Write. A value-only signal such as DataOut is declared with the additional value keyword. It has no status. In MemIntf, the status of Write is used as a write enable for the value DataIn at address ?Write, while that of Read is used as a read enable at address ?Read, with the read operation returning the value ?DataOut. The memory behavior is written as follows:

2.3. A MEMORY EXAMPLE


module Mem : extends i n t e r f a c e MemIntf ; var MemArray : ObjType [ MemSize ] i n always i f Write then MemArray [? Write ] := ? DataIn end i f ; i f Read then emit ? DataOut <= MemArray [? Read ] end i f end always end var end module

13

The semicolon ; denotes sequencing: if write and read occur at the same address, the value read is the one just written.

2.3.2

Making the memory generic

Making the memory generic consists in specifying the value type and memory size as generic parameters, without changing the interface and module codes. For this, we simply write the following:
data MemData : g e n e r i c type ObjType ; g e n e r i c constant MemSize : unsigned ; type Address = unsigned < MemSize >; end data

In MemData, the generic parameters are explicitly indicated by the generic keyword. The Address dened generic type is not a generic parameter, but it is indirectly generic in some sense since it depends on the generic parameter ObjType. When making the data unit generic, the memory interface and behavior module automatically become generic in the same parameters since basic extension of a data unit simply imports its declaration. One can also make interfaces and modules directly generic by declaring generic objects within them, without resorting to a separate data unit.

2.3.3

Reconstructing a specic memory

Our initial word memory interface can be reconstructed as follows, with more explicit type and size names:
data WordData : type Word = bool [32]; constant WordMemSize : unsigned = 256; end data i n t e r f a c e WordMemIntf : extends WordData ; extends MemIntf [ type Word / ObjType ; constant WordMemSize / MemSize ] end i n t e r f a c e

14

CHAPTER 2. UNITS AND GENERICITY

Here, extension of MemIntf substitutes actuals for generic parameters and imports the other data and signal objects from MemIntf after this substitution. Therefore, the visible data objects within WordIntf are the type Word, the constant WordMemSize, and the Address type dened by Address = unsigned<WordMemSize>. The generic parameter names Objtype and MemSize are not visible any more. The signals of MemIntf are as follows:
input Write : Address ; input DataIn : value Word ; input Read : Address ; output DataOut : value Word ;

Here is the instantiation of the behavior module:


module WordMem : extends i n t e r f a c e WordMemIntf ; run Mem [ type Word / ObjType ; constant WordMemSize / MemSize ]; end module

Because we have split the interface and module declaration , we need to repeat the substitution in the run executable statement; otherwise, that statement would still have generics ObjType and MemSize. Substitution in the interface extension from MemIntf to WordMemIntf has no eect on the Mem module, which still has MemIntf interface: extends and run are uncorrelated.

2.3.4

Ports and run statements

Assume we want to use a pair of word memories in an application. It is clearly not possible to directly extend twice WordMemIntf, since this would doubly declare its component with the same names. We do not provide ways of renaming signals in interface instantiation. The solution we propose is to use ports, which are groups of signals typed by an interface. One uses a dot-notation to access port elds, as for conventional record elds. Here is a compound of two concurrent word memories accessed by two ports:
module DoubleWordMem : port P1 : WordMemIntf ; port P2 : WordMemIntf ; { run WordMem1 / WordMem [ P1 . Write / Write , P1 . DataIn / DataIn , P1 . Read / Read , P1 . DataOut / DataOut ] || run WordMem2 / WordMem [ P2 . Write / Write , P2 . DataIn / DataIn , P2 . Read / Read , P2 . DataOut / DataOut ] } end module

As far as behavior is concerned. two instances of the WordMem module are run in parallel using the Esterel || concurrency operator. To distinguish the instances, they are renamed into WordMem1 and WordMem2 in the run statement. One writes into memory 1 using P1.Write, and one reads from memory 2 using P2.Read. In the run statements, the / character is used to connect signals in the caller to interface signals in the callee. For instance, the port eld P1.Write declared in DoubleWordMem is connected to the Write input of WordMem1.

2.3. A MEMORY EXAMPLE

15

To further simplify the writing, on can use the open statement described in Section 8.15 that gives direct access to port elds:
{ open P1 i n run WordMem1 / WordMem end open || open P2 i n WordMem2 / WordMem end open }

In the body of the open P1 port opening statement,simple identiers such as Write become synonyms to the eld P1.Write. Therefore, the implicit renaming Write / Write silently performs the actual renaming to P1.Write / Write. Notice that signal instantiation uses the same syntax as data instantiation: the actual signal is on the left of the / instantiation symbol and the instantiated interface parameter is on the right. This is true independently of the input / output directionality of the interface signals. Data instantiation and signal instantiation can both appear in an instantiation list, with data instantiation rst, see Chapter 9.. Port declaration and run statements are not extensions A port declaration refers to an interface but it does not provoke extension of this interface. Similarly, a run statement does not extend the data and interface of the module it refers to. Therefore, in the body of DoubleWordMem, the data object Word, WordMemSize, and Address are not visible. This is not a problem here since we do not need to refer to them. However, we might need to be able to use them, for instance to code additional check or trace operations. This is easily done by adding the following declaration:
extends data WordMemIntf ;

2.3.5

Multiple instantiation of generic data: renaming dened objects

Substituting actual data to generic parameters is not enough to deal with multiple heterogeneous instantiation of a generic unit. For instance, assume we want to dene an interface for a pair of memories of dierent types and sizes, with an access port for a 128 bytes memory and an access port for a 256 words memory. A fully explicit description would be as follows:
data ByteMemData : type Byte = bool [8]; constant ByteMemSize = 128; type ByteAddress = unsigned < ByteMemSize >; end data data WordMemData : type Word = bool [32]; constant WordMemSize = 256; type WordAddress = unsigned < WordMemSize >; end data

16

CHAPTER 2. UNITS AND GENERICITY

i n t e r f a c e ByteMemIntf : extends ByteMemData ; input Write : ByteAddress ; input DataIn : value Byte ; input Read : ByteAddress ; output DataOut : value Byte ; end i n t e r f a c e i n t e r f a c e WordMemIntf : extends WordMemData ; input Write : WordAddress ; input DataIn : value Word ; input Read : WordAddress ; output DataOut : value Word ; end i n t e r f a c e i n t e r f a c e MemoryPair : extends ByteMemData ; extends WordMemData ; port BytePort : ByteMemIntf ; port WordPort : WordMemIntf ; end i n t e r f a c e

Obviously, the goal of genericity is to factor out these declarations by instantiating the same generic units twice. The rst obvious idea would be to write the data units hierarchically as follows:
data ByteMemData : type Byte = bool [8]; constant ByteMemSize = 128; extends MemData [ type Byte / ObjType ; constant ByteMemSize / MemSize ] end data data WordMemData : type Word = bool [8]; constant WordMemSize = 256; extends MemData [ type Word / ObjType ; constant WordMemSize / MemSize ] end data

data DoubleMemData : extends ByteMemData ; extends WordMemData ; end data

with automatic building of address types by the extension scheme. But this simple method would not work since it would provoke a double declaration of the Address type:
type Address = unsigned < ByteMemSize >; type Address = unsigned < WordMemSize >; // from ByteMemData // from WordMemData

2.3. A MEMORY EXAMPLE

17

Therefore, we need a way to distinguish between byte and word addresses as we did in the non-generic design. To solve this problem, Esterel provides the user with the possibility of renaming dened types at extension time:
data ByteMemData : type Byte = bool [8]; extends data MemData [ type Byte / ObjType , ByteAddress / Address ; constant ByteMemSize / MemSize ] end data data WordMemData : type Word = bool [32]; extends data MemData [ type Word / ObjType , WordAddress / Address ; constant WordMemSize / MemSize ] end data data DoubleMemData : extends data ByteMemData ; extends data WordMemData ; end data

Although they use the same substitution symbol /, the substitution Byte / ObjType and ByteAddress / Address are not quite of the same nature: the rst one passes the actual type Byte dened in ByteMemData to replace the generic type ObjType of MemData, while the second one renames the dened generic type Address generated by MemData into ByteAddress for inclusion in DoubleMemData. To avoid performing the same substitution again for interfaces, one can do the substitution directly for both data and signals at interface level:
i n t e r f a c e ByteMemIntf : extends i n t e r f a c e MemIntf [ type Byte / Obj , ByteAddress / Address ; constant ByteMemSize / MemSize ] end i n t e r f a c e i n t e r f a c e WordMemIntf : extends i n t e r f a c e MemIntf [ type Word / Obj , WordAddress / Address ; constant WordMemSize / MemSize ] end i n t e r f a c e module ByteWordMem : extends data ByteMemIntf ; // forgetting signals extends data WordMemIntf ; // forgetting signals port BytePort : ByteMemIntf ; port WordPort : PortMemIntf ; run Mem [ type Byte / ObjType ; constant ByteMemSize / MemSize ; s i g n a l BytePort . Write / Write , BytePort . DataIn / Write , BytePort . Read / Read , BytePort . DataOut / DataOut ]

18
||

CHAPTER 2. UNITS AND GENERICITY

run Mem [ type Word / ObjType ; constant WordMemSize / MemSize ; s i g n a l WordPort . Write / Write , WordPort . DataIn / Write , WordPort . Read / Read , WordPort . DataOut / DataOut ] end module

2.3.6

Composing genericity

The byte and word memories share a common property: they both store bitsets of dierent lengths. Therefore, it can also be useful to dene bitset memories of variable length as intermediate structure. Here is how to do this:
data BitsetMemData : g e n e r i c constant BitsetLength : unsigned ; type BitsetType = bool [ BitsetLength ]; end data i n t e r f a c e BitsetMemIntf : extends BitsetMemData ; extends MemIntf [ type BitsetType / ObjType ]; end i n t e r f a c e module BitsetMem : extends BitsetMemIntf ; run Mem [ type BitsetType / ObjType ]; end module

Here, we have changed the level of genericity: the former generic type ObjType has been replaced by a generic parameter BitSetLength determining the size of the bitset type. This is what we call genericity composition. The other generic parameter was MemSize. Since it has not been instantiated, its declaration is simply imported in BitSetMemIntf and BitSetMem and it remains generic there. The implicitly created interface unit is as follows:
i n t e r f a c e BitsetMemIntf : g e n e r i c constant BitsetLength : unsigned ; type BitsetType = bool [ BitsetLength ]; g e n e r i c constant MemSize : unsigned ; type Address = unsigned < MemSize >; input Write : Address ; input DataIn : value BitsetType ; input Read : Address ; output DataOut : value BitsetType ; end data

2.3.7

Implicit generic instantiation

Finally, if there is only one instantiation of the memory in the design, simply denes the generic parameters with the same names before instantiating the design:

2.4. DATA GENERICITY SPECIFICATION


i n t e r f a c e WordMemIntf : constant MemSize = 256; type Address = unsigned < MemSize > type ObjType = bool [32]; extends MemIntf ; end i n t e r f a c e

19

Here, the simple extension extends MemIntf acts as a short-hand for


extends WordMemIntf [ type Addess / Address , ObjType / ObjType ; constant MemSize / MemSize ]

This capture-by-name mechanism is very convenient for large argument lists.

2.4
2.4.1

Data genericity specication


Data declaration

Units declare data objects of four dierent kinds: type, constant, function, and procedure. Data objects can be declared in three dierent ways: Generic, declared with the generic keyword. Generic objects only have a kind, a name, and possibly a type. Fully dened , with no reference to generic objects. They can be given a denition in the data unit, or they can be declared host, in which case their denition is to be given in the host language. Dened generic, with a denition, that refers to other generic or indirectly generic object. Host objects cannot be dened generic; in particular they cannot have generic types. Consider the following example:
data D : g e n e r i c constant WordLength : unsigned ; type Word = bool [ WordLength ]; host type Time ; g e n e r i c type T ; g e n e r i c constant A : T ; g e n e r i c f u n c t i o n F (T , T ) : T ; constant C : T = F (A , A ); end data

Here, WordLength, T, A, and F are generic, while Time is fully dened and Word and C are dened generic. There is an additional dependency of A, F, and C on T since their types involves T; these types will be viewed as a constraints to be respected at instantiation time. For instance, if one writes bool / T, one must substitute A by a Boolean constant and F by a Boolean binary function. In that case, C will indeed type-check as a bool.

20

CHAPTER 2. UNITS AND GENERICITY

2.4.2

Generic data extension

Data extension of a generic unit Sub within a current unit U is peformed by declaration of the form extends data Sub, optionally with a bracketed substitution list. A substitution list has semicolon-separated sections, each identied by a keyword type, constant, function, or procedure. There can be serveral sections starting with the same keyword. Within each section, Substitutions are comma-separated. A substitution has the form X / Y where X is a data object already declared in U and Y is a data object of the same kind declared in Sub. The rules are as follows: If Y is a generic parameter in Sub, there are two subcases: If there is already an object called Y in U, then this object is substituted to Y in the denition of all the subsequent data objects imported from Sub. Otherwise, Y becomes a generic parameter of U. If Y is a dened or dened generic object in Sub, its denition in Sub becomes the denition of X in U after generic parameter substitution.

2.4.3

Generic extension example

Generic extension is mostly useful for interfaces and modules. Consider the following generic interface that extends D above:
i n t e r f a c e Intf1 : extends D ; input I : T ; output O : Word ; end i n t e r f a c e

A partial extension of Intf1 can be dened as follows:


i n t e r f a c e Intf2 : extends Intf1 [ type bool / T ; constant true / A ; f u n c t i o n and / F ] end i n t e r f a c e

Then Intf2 is actually the following interface:


i n t e r f a c e Intf2 : g e n e r i c constant WordLength : unsigned ; type Word = bool [ WordLength ]; host type Time ; constant C : bool = true ; input I : bool ; output O : Word ; end i n t e r f a c e

Notice that the generic parameter names T, A, and F do not occur any more in Intf2 since they have been instantiated. From Intf2, we can derive a fully dened interface Intf3 as follows:
i n t e r f a c e Intf3 : extends Intf2 [ constant 32 / WordLength ] end i n t e r f a c e

2.4. DATA GENERICITY SPECIFICATION

21

This makes the name WordLength disappear in Intf3. To keep this convenient name alive, one can write the extension as follows:
i n t e r f a c e Intf4 : constant WordLength : unsigned = 32; extends Intf2 ; end i n t e r f a c e

Then, WordLength is dened when Intf2 is extended and it replaces Intf2s WordLength generic parameter during the extension. The nal interface Intf4 is fully dened, and it could have been written as follows:
i n t e r f a c e Intf4 : constant WordLength : unsigned = 32; type Word = bool [ WordLength ]; host type Time ; constant C : bool = and( true , true ); input I : bool ; output O : Word ; end i n t e r f a c e

If needed, one could have renamed some dened components to give them more explicit names:
i n t e r f a c e Intf5 : constant WordLength : unsigned = 32; extends Intf2 [ type Word32 / Word ] end i n t e r f a c e

Then, the resulting interface would have been:


i n t e r f a c e Intf5 : constant WordLength : unsigned = 32; type Word32 = bool [ WordLength ]; host type Time ; constant C : bool = and( true , true ); input I : bool ; output O : Word32 ; end i n t e r f a c e

2.4.4

Using interfaces in ports and runs

When an interface Intf is refererred to in a port declaration port P : Intf, it is not considered as being extended. Therefore, all its generic parameters must be either already dened in the context or renamed in the port declaration, as in the following declarations:
type Word = bool [32]; port P : Intf [ type Word / ObjType ]

Since there is no name exported from Intf to the current context, dened objects of Intf cannot be renamed in a port interface substitution list. Similarly, the interface of a module M is not extended by a run M [...] statement. All generic parameters must be either already dened or explicitly instantiated, and no dened object can be renamed.

22

CHAPTER 2. UNITS AND GENERICITY

2.4.5

Name spaces, visibility, and declaration uniqueness

The named objects in a program are units, data objects, signals, ports, variables, and traps. Units and statements dene the visibility scope at each point of the program. All the unit names are visible from everywhere in a program. Data objects are visible at a point if they were already declared in the current unit or imported from other units, this directly or indirectly. Signals, variables, and traps follow classical static scoping visibility rules for statements, explained in Chapter 8. At each point, there is a single namespace for all the visible objects. Therefore, no two distinct objects can have the same name if they are visible from a common point. Consider the following program:
data D1 : constant C : unsigned = 213; end data ;

module M1 : extends D1 ; output O : unsigned ; emit ? O <= C end module

data D2 : constant C : unsigned = 347; end data ; module M2 : extends D2 ; output O : unsigned ; emit ? O <= C end module main module Main : output O : unsigned ; run M1 ; pause ; run M2 end module

Since there is no point at which the two dierent declarations of the C identier are visible, the program is legal and emits 213 at rst instant and 347 at second instant. It would be illegal to write:
main module Main : extends data D1 ; extends data D2 ; output O : unsigned ; run M1 ; pause ; run M2 end module

2.4. DATA GENERICITY SPECIFICATION

23

because the two distinct denitions of a constant named C would be visible in the same scope. Nevertheless, relying only on visibility rules to give the same name to two dierent constants can be confusing for the program reader and should we done with care. Multiple data extension It is legal to import twice the very same declaration during unit extension, as in the following multiple extension scheme:
data D : constant N : i n t e g e r = 3; end data data D1 : extends D ; type T1 = unsigned <N >; end data data D2 : extends D ; type T2 = unsigned <N >; end data data D3 : extends D1 ; extends D2 ; end data

Since D1 and D2 both extend D, they share the very same denition of N. On the contrary, the following scheme is illegal:
data D1 : constant N : i n t e g e r = 3; type T1 : unsigned <N >; end data data D2 : constant N : i n t e g e r = 3; type T2 : unsigned <N >; end data data D3 : extends D1 ; extends D2 ; end data

Here, there are two objects called N, which is forbidden even though their denitions happen to coincide.

2.4.6

Host objects are global

Host objects are considered as global to the whole application (in practice, they are most often dened with their name in the host language, although this is not strictly manda-

24

CHAPTER 2. UNITS AND GENERICITY

tory). Therefore, it is forbidden to declare two host objects with the same name, even if there is no visibility scope where both declarations are visible.

2.5

Forgetting unit components

There is a strict hierarchy between the three kinds of units: any interface unit can be viewed as a data unit of the same name by forgetting its signals, and any module unit can be viewed as an interface unit by forgetting its behavior. A data or interface keyword species what is retained (not forgotten). Here is an example:
i n t e r f a c e Intf1 : g e n e r i c type T1 ; input I : T1 ; output O : T1 ; end i n t e r f a c e data D2 : extends data Intf1 ; host type T2 ; end data module M3 : extends data D2 ; input I : T1 ; output O : T2 ; end module module M4 : extends data M3 ; input J : T1 ; end module

// forget I and O

// forget I and O of Intf1 and M3

For D2, the extends data Intf1 keywords are used to specify that we extend only the data part of Intf1, forgetting about its interface signals. Similarly, extends data M3 extends only the data part of M3 and disregard its interface. Notice that there is no double declaration of I and O in M3 since their declarations in Intf1 were forgotten. To shorten writing, the keywords data or interface can be omitted in an extends declaration when there is no possible confusion, The rules are as follows, where Intf is an interface and M is a module assumed to be already declared, for any unit U and any unit V that contains extends U: If U is a data unit, then extends U is the same as extends data U. Otherwise, if V is a data unit, then extends U is the same as extends data U. Otherwise, U and V are interfaces or modules, and extends U is the same as extends interface U.

Chapter 3

Data
The data objects handled in Esterel v7 30 are types, constants, functions, and procedures. These objects can be dened either directly in the Esterel program or in the host language to which the Esterel program is compiled. In the latter case, the data objects are called host data objects and are known only by their name and type at Esterel level. Furthermore, Esterel supports generic data denitions for parametric design, as explained in Section 2.4.

3.1

Types

Types can be of four kinds: primitive, i.e. dened by the language; host, i.e. dened in the host language; generic, i.e. type parameters of a unit. dened, i.e. dened in the program from other types and constants. Since their contents are unknown, host and generic types are collectively called abstract.

3.1.1

The bool primitive type

The bool type contains the true and false Bool values, with constants named true or 1 and false or 0. It is used in test expressions and is also the basis of bitvectors, see Section 3.1.11. The primitive function bool2u takes a bool argument and returns the unsigned number 0 for argument false and the number 1 for argument true. Its result type is unsigned<2>, see Section 3.1.2 below.

3.1.2

The unsigned primitive types

For each natural number M > 0, the type unsigned<M > is a primitive type that denotes natural numbers from 0 to M 1 included. There is no maximum to the range since Esterel supports arbitrary precision exact unsigned arithmetic, see Section 4.2. Note that M represents the size of the set being encoded, not the number of bits in binary encoding. Therefore, unsigned<21> represents all the natural numbers for 0 to 20, not the set of numbers one can write with 21 bits, which is unsigned<2**21>. 25

26

CHAPTER 3. DATA

Of course, it is very common to take a power of 2 for N. We provide the user with the syntactic sugar unsigned<[M ]> for unsigned<2**M >. Therefore, unsigned<2**21> can also be written unsigned<[21]>. The number of bits it takes to implement unsigned<M> can be computed as binsize(M-1), where the primitive function binsize(m) returns the number of bits it takes to write the number m in binary, see Section 4.2.7. To be compatible with more classical type declarations, one can also omit the size declaration altogether and write unsigned as a shorth-hand for unsigned<[32]>. In designs, the unsigned types play two dierent roles: At run-time, they are fundamental to implement various kinds of calculations: counting, addressing, coloring pictures, etc. At compile-time, they are fundamental to parametrize designs, size arrays, index them, count events, etc. Esterel unsigned arithmetic is exact, which means that + always means exact addition, - always means exact subtraction, etc. Operations never drop bits in an implicit way, unlike in classical software languages or HDLs. All bit-dropping calculations must be explicitly done by the user. For this reason, there is no overow in this arithmetic. Unsigned arithmetic is presented in details in Section 4.2. Abstract numbers vs. bit endodings Esterel unsigned numbers are dened in an abtract way, independently of any internal representation. The user or the compiler may choose to use other internal representations than classical binary: onehot, Gray, redundant, etc. This is why the size parameter is a set size and not a bit size: the number 21 takes 5 bits in binary or Gray encoding, 22 bits in onehot encoding, and 10 bits in redundant binary encodings. Of course, bitvector manipulations are also essential, for instance because addresses or values must be extracted from bit frames. The user can convert numbers to bitvector representations and conversely using encoding and decoding functions such as u2bin or bin2u, see Section 4.6. Three primitive encodings are predened in Esterel: binary, onehot, and Gray. Specic encodings can be added by the user, for example for fast arithmetic using redundant representations. Distinguishing the semantics of numbers from their internal representation provides higher-level programnming, since algorithms are expressed at a more abstract level. It also yields more optimization potential and an easier link to the newest verication systems that do understand numbers and not only bits, but better understand exact arithmetic that truncated one. Unsigned literals Unsigned literals can be written in various numbering systems: decimal, either without prex as in 15 or with a 0d prex as in 0d15; binary, with a Ob prex, as in 0b1111; octal, with a 0o prex, as in 0o17; hexadecimal, with a 0x prex, as in 0xF.

3.1. TYPES

27

For all prexed forms, one can use _ underline characters to make the value more readable, as for 0b_110_001. This character can appear at any place. It is ignored to compute the constant value. It is not allowed in non-prexed decimal constants: 123_456 is illegal, the legal form being 0d123_456. An unsigned constant M declared in Esterel has type the minimal unsigned type that contains it, which is unsigned<M +1>. This type is automatically extended as needed to larger unsigned sets during arithmetic operations, see Section 4.2. Therefore, only the numerical value of a literal matters to dene its type. For instance, since leading 0s dont change the value, the literals 0xA and 0x0A have the same type unsigned<11>. (See Section 3.1.11 for the dierence with bitvector constants, which are not numerically evaluated: xA has type bool[4] while xOA has type bool[8]). Unsigned constant tight declarations When declaring an unsigned dened constant, one gives the value of the constant after the = sign. To control intermediate result sizes in expressions that use this constant, it is always a good idea to declare the type of the constant as the smallest unsigned type that contains its value. This type is unsigned<N +1> if the value is N . Here are explicit declarations of this kind, where A and B are already declared unsigned constants:
constant C1 : unsigned <5 > = 4; constant C2 : unsigned < A + B +1 > = A + B ;

To make such declarations simpler, Esterel v7 provides the user with a simpler dened unsigned constant tight declaration syntax, where the type is simply declared as unsigned<> to abbreviate unsigned<N +1> if the value is N . The previous examples can be rewritten as follows:
constant C1 : unsigned < > = 4; constant C2 : unsigned < > = A + B ;

The tight declaration syntax is available only for dened unsigned constant declaration and cannot be used elsewhere.

3.1.3

The signed primitive types

Given a positive integer M , the type signed<M > denotes the set positive or negative integers x such that M x < M , i.e. the range [M, M 1]. Note that the cardinality of this set is 2M . The notation signed<[M ]> is a shorthand for signed<2**(M -1)>, which exactly denotes the set of signed integers one can represent with M bits in 2s complement binary notation. One can use integer as a synonym for signed<[32]>. As for unsigned arithmetic, signed arithmetic is always exact and no implicit bitdropping is performed. Signed arithmetic is presented in Section 4.3. Although the language does not enforce signed numbers to be represented in 2s complement binary form, the bounds are chosen to conform to it, and predened functions s2bin and bin2s converts signed numbers to and from bitvectors in this form. There are no signed literals. Signed constants can be built from unsigned literals by adding the + or - signs, as for +3 or -5, see Section 4.3.1. For compatibility with common use, we provide the user with the integer keyword as an abbreviation of signed<[32]>. (Beware if you lazily use this type for synthesis, you may generate unwanted 32-bit registers).

28 Signed constants tight declarations

CHAPTER 3. DATA

Signed constants tight declarations are similar to unsigned constants tight declarations described in Section 3.1.2. When declaring an signed dened constant, one can use the tight type denotation signed<> to mean the smallest signed type that contains the value of the expression. If the expression is E, that type is computed as follows: If E evaluates to a positive number n, the type is signed<n+1> If E evaluates to a negative number n, n > 0, the type is signed<n> If E cannot be completely evaluatedin the current scope because it contains generic constants, the type is signed<abs(E)+1>, where abs be the is the absolute value function described in Section 4.3.8 below. the +1 term is needed to handle the case where E is positive. Here are examples, where X and Y are already declared signed constants:
constant C1 : signed < > = -5; // type signed <5 > g e n e r i c constant D1 , D2 : signed <[32] > constant C2 : signed < > = D1 + D2 ; // type signed < abs ( D1 + D2 )+1 >

The tight signed declaration syntax is available only for dened constant declaration and cannot be used elsewhere.

3.1.4

The oat primitive type

The float type denotes positive or negative oating-point numbers. The constants are written as in C++ or Java: 12.3f, .123E2F, or -1.23E-1F. The range of supported oats depends on the host language.

3.1.5

The double primitive type

The double type denotes positive or negative double-precision oating-point numbers. The constants are written as in C++ or Java: 12.3, .123E2, or -1.23E-1. The range of supported doubles depends on the host language.

3.1.6

The string primitive type

The string type is a primitive type. It denotes character strings. Constant strings are written between double quotes, e.g., "a string", with doubled double quotes as in "a "" double quote".

3.1.7

Enum types

An enum type denes a list of distinct values, which are given symbolic names:
type Color = enum { Red , Blue , Green };

The only computation primitives on an enum type are equality = and dierence <>: Red = Red is true, Red <> Red is false, Red = Green is false, and Red <> Green is true.

3.1. TYPES

29

In host language translation, enum values are implemented by integers 0, 1, etc. This internal encoding cannot be used in Esterel programs to perform operations such as addition, comparison with unsigned, etc, but knowing values may be important for interfacing and debugging purposes. To further ease debugging, it may be important to specify explicit encodings for enum values. This is done as follows:
type Color = enum { Blue , Red = 3 , Yellow }; // 0 // 3 // 4

The implicit incremental numbering is restarted afresh when an explicit value is given. The numbers must be increasing. Since the encodings are not usable in the program, unsigned constants should be preferred to enum elements if there is a need to actually decode the values, as for a structured ISS (instruction set).

3.1.8

Host types

An host type is a type whose contents and implementation are not specied in Esterel but only in the host language to which the type is compiled. A host type is declared using the host keyword, as in
host type Time ;

The Time type can be implemented in C by a structure with hours, minutes, and seconds elds, using the usual Babylonian carry propagation. The interest of host types is that such datapath details that can be irrelevant for the behavior of an Esterel program and better handled in separate host libraries to separate concerns. One can declare a constant, a signal, or a variable of host type. These objects are then called host objects, and they are the only way to manipulate objects of the type. One can compare abstract objects by = or <>, and pass host objects to functions, procedures, or units. Here are examples:
module M : host type T ; host constant C : T ; host f u n c t i o n F ( T ) : T ; input I : T ; output O : T ; s u s t a i n ? O <= F (? I ) i f ? I <> C end module

See Chapter 9 for details on the run statement.

3.1.9

Dened types

A dened type is a type declared to be equal to another type, which must be primitive or declared beforehand:
type Temperature = unsigned <90 >; type Word = bool [32];

30

CHAPTER 3. DATA

The types are identical for any usage, except for bitvector types for which maps can be dened as explained in Section 4.5.

3.1.10

Array types

Data arrays can be dened from types by adding dimensions. The dimension expressions must be statically evaluable unsigned or positive signed numbers, see Section 7. Arrays and arrays of arrays are supported up to any size and depth. Array types can be named or unnamed. An unnamed array type directly appears in the declaration of an Esterel object:
input Opcode : bool [8]; var X : i n t e g e r [3][5] i n ... end

A named array type is an array type appearing in a type denition:


type Image = unsigned <[8] > [100][100]; type Word = bool [32]; constant MemorySize : unsigned ; type Memory = Word [ MemorySize ];

Notice that objects of Memory type are arrays of arrays declared in an indirect way. When indexing such indirectly dened arrays, the indices appear in the denition dependency order: Memory[i] is a Word, and Memory[i][j] is the j-th bit of this word. This indexing scheme denes the unfolded type of the indirect type, which obtained by successively removing all dened type declarations. Here, the unfolded type of Memory is bool[MemorySize][32], not bool[32][MemorySize]. Because the type-prex notation bool[32], array type unfolding is a little awkward:

Word[MemSize] (bool[32])[MemorySize] bool[MemorySize][32] To avoid any confusion, we forbid to dene arrays from direct arrays , thus to write types of the form (bool[32])[MemorySize]. We enforce the use of intermediate dened array types such as Word. Notice that the same unfolding rules are clearer with a type-postx notation a la VHDL. There, one simply has

array MemorySize of (array 32 of bool) array MemorySize of array 32 of bool Two array types are considered identical if and only if they have the same base type, the same number of dimensions and the same dimensions, this after unfolding for indirect array types. Data arrays can be indexed and sliced as explained in Section 7.1.3. Their relation with signal arrays is presented in Section 7.2.1.

3.1. TYPES Array literals

31

Array literals are used to dene values for constant arrays, and they can be used in array expressions. Their syntax follows that of System Verilog [1]. First, for single-dimensional arrays, one can rst give a list of values between curly brackets, the length of the list being exactly the array dimension:
constant C : bool [3] = { 0 , 1 , 0};

One can repeat a subrange by adding a constant unsigned expression acting as a repetition factor followed by a bracket and a sublist:
constant C1 : bool [3] = { 1 , 2{ 0}}; // same as { 1 , 0 , 0} constant C2 : bool [ N ] = {( N /2){ 0 , 1}} // same as { 0 , 1 , 0 , 1 , 0 , 1...} // provided N is even

After expansion of the repeated elements, the literal size must match the array size. For arrays of arrays, one nests subarray denitions, rst index varying slowest, with mandatory dimension match at all levels. Repetition factors can be applied to whole subarrays. Here are examples:
var V : unsigned [2][2] = { {1 ,2} , {3 ,4} } i n ... end // V [0][0]=1 , V [0][1]=2 , V [1][0]=3 , V [1][1]=4 s i g n a l S : i n t e g e r [5][5] i n i t {4{{5{0}}} , { -1 , -2 , -3 , -4 , -5}} i n pause ; emit ? S <= {2{{5{ -1}}} , 3{{5{0}}}} // lines 0 -1 filled with 1 s , lines 2 -4 filled with 0 s end s i g n a l

For denition of constants and for initialization of variables and signals, one can use a single literal instead of an array constant to initialize all components of the array with a single value:
constant C : unsigned [10] = 0; // shorthhand for {10{0}} type Word = bool [32]; constant B : Word [100] = 0; // shorthand for {100{{32{0}}} s i g n a l mem : Word [100] i n i t 0 i n ... end var V : Word [100] := INIT_WORD i n ... end

The initialization literal is limited to be either a data constant literal such as 0, true, or x01 or a constant name such as INIT_WORD. Its type must matech the array base type.

3.1.11

Bitvectors

Single dimensional arrays of Booleans are called bitvectors. They play a very special role in hardware or low-level software designs. Bitvector literals For bitvector constants, one can use an alternative syntax similar to the unsigned constant syntax presented in Section 3.1.2, replacing the leading 0 by a quote symbol. As for unsigned constants, one can use _ dummy separators. Bit width depends on the basis used, which can be Boolean, octal, or hexadecimal. There is a well-known but unfortunate reversal of endianness w.r.t. the C-like notation:

32

CHAPTER 3. DATA The literal b1_100_001 is a synonym to the array literal {1,0,0,0,0,1,1}. Notice the bit reversal w.r.t. array literals: low-order bits are rst in the C-based array literal notation, last in the bitvector arrray notation. The literal o27 is a compact form for u2bin(0o27, 6), synonym to the array literal {1,1,1,0,1,0}. Notice that the result always has a multiple of 3 bits and that the rst 0s matter: the literal o05 denotes a 6-bits bitvector, not a 3-bit one. The literal x6E is a compact form for u2bin(0x6E, 8), synonym to the array literal {0,1,1,1,0,1,1,0}. Notice that the result always has a multiple of 4 bits and that the rst 0s matter: the literal x05 denotes a 8-bits bitvector.

3.1.12

Generic types

A generic type is declared as follows:


g e n e r i c type T ;

If a data, interface, or module unit contains a generic type declaration, it becomes a generic unit. The generic type can be instantiated as explained in Section 2.4. All generic types must be instantiated for the main module to be executable.

3.2

Constants

Constants can be dened, host, or generic, just as types. A constant has a type, which must be primitive or declared beforehand.

3.2.1

Dened constants

A dened constant is a constant whose value is given at declaration time, after the type and a = symbol.
constant Threshold : unsigned = 223; constant GlobalSize : unsigned = MemorySize * WordSize ;

The value is given by a static expression containing only literals, constants, and primitive operators, with all objects declared beforehand. There is a special tight notation described in Section 3.1.2 and SignedTightConstant to help sizing unsigned and signed constant

3.2.2

Host constants

A host constant is a constant which will be dened in the host language. Its type cannot be generic. A host constant is declared using the host keyword:
host constant Threshold : unsigned <[16] >; host constant Midnight : Time ;

A host constant is known only by its name, nothing being known about the value.

3.3. FUNCTIONS

33

3.2.3

Generic constants

A generic constant is a constant simply declared by its name and its type:
constant C : T ;

The type itself can be dened, host, or generic. If a data, interface, or module unit contains a generic constant declaration, it becomes a generic unit. The generic constant can be instantiated as explained in Section 2.4. All generic constants must be instantiated for the main module to be executable.

3.3

Functions

A function takes typed arguments by value and returns a typed value. A function is supposed to be computed instantaneously and to have no side-eects. The argument types are in a parenthesized list, the result type appearing after a column. Non-predened types must be declared beforehand. They can be arbitrary, including generic. There are predened functions, dened functions, host functions, and generic functions.

3.3.1

Predened functions

The predened functions are as follows: binsize(m) where m is a natural number returns the number of bits it takes to write m in binary. bool2u converts a Boolean to an unsigned number, see Section 3.1.1. bin2u, onehot2u, code2u, u2bin, u2onehot, and u2code2, convert bitvectors to unsigned numbers and conversely, see Section 4.6. bin2s and s2bin convert bitvectors to signed numbers and conversely, see Section 4.6.5. mux chooses between values according to a condition, see Section 7.3. lcat and mcat catenate bitvectors, see Section 4.4.3 reverse reverses bitvectors, see Section 4.4.5,

3.3.2

Host functions

A host function is a function whose body is dened externally in the host language and unknown at Esterel level. The arguments and result types cannot be generic; they must be primitive or declared beforehand. A host function is declared using the host keyword:
host f u n c t i o n IncrementTime ( Time ) : Time ;

The declaration declares the name and type of the host function, its denition remaining unknown at Esterel level.

34

CHAPTER 3. DATA

3.3.3

Generic functions

A generic function is a function simply declared by its name and its argument and result types, which can be generic as well:
f u n c t i o n Fun ( T1 , T2 ) : T ;

The argument and result types can also involve generic constants:
type T ; constant Size : i n t e g e r ; f u n c t i o n Transpose ( T [ Size , Size ]) : T [ Size , Size ];

If a data, interface, or module unit contains a generic function declaration, it becomes a generic unit. The generic function can be instantiated as explained in Section 2.4 provided types match at instantiation time. All generic functions must be instantiated for the main module to be executable.

3.4

Procedures

A procedure has a list of in, out, and inout arguments, with types annotated using the corresponding keyword. The procedure is supposed to be executed instantaneously. When executed, it reads its in and inout arguments and modies (side-eects) its out and inout arguments, which must be variables. There are host procedures and generic procedures. See Section 8.3.2 for the procedure call statement.

3.4.1

Host procedures

A host procedure is a procedure whose body is dened externally in the host language and unknown at Esterel level. The arguments and result types cannot be generic; they must be primitive or declared beforehand. A host procedure is declared using the host keyword:
host procedure UpdateTime ( inout Time , i n i n t e g e r );

3.4.2

Generic procedures

A generic procedure is a procedure simply declared by its name and its argument types and access modes:
g e n e r i c procedure P ( i n T1 , out T2 , inout i n t e g e r );

The types can be generic. If a data, interface, or module unit contains a generic procedure declaration, it becomes a generic unit. The generic procedure can be instantiated as explained in Section 2.4 provided types match at instantiation time. All generic procedures must be instantiated for the main module to be executable. A procedure is called by the call Esterel statement, see Section 8.3.2. There is no provision to dene the code of a procedure in Esterel. Therefore, the code of the procedure will always be written in the host language. Because Esterel is concurrent, side eects other than assigning out or inout parameters are fully under the user control and their eect cannot predicted if procedures sharing user data are called concurrently.

Chapter 4

Arithmetic and Bitvectors


4.1 Boolean operators

Remember that the Boolean literals are false or 0 and true or 1. The Boolean operations are unary not, left-associative and, or, and xor, implication =>, equivalence <=>, exclusion #, plus the generic mux operator. Conjunction and and disjunction or are left-associative, with higher priority for and as usual. Implication is right-associative: x => y => z means x => (y => z). Equivalence <=> is synonym to equality = but makes use of the often clearer standard mathematical notation. It is left associative, just as for =. Exclusion is truly n-ary: x # y # x means that x, y, and z are exclusive, i.e. that no two of them can be true at the same time. Beware, x # y # z is completely dierent from x # (y # z), which means x is exclusive with the result of the exclusivity predicate of y and z. The mux(b, e1 , e2 ) operator takes a Boolean expression b and two expressions e1 and e2 of the same arbitrary type, which can also be an array type. It returns the value of the rst expression if the Boolean evaluates to true, the second expression otherwise. mux(b, e1 , e2 ) = e1 if b = true e2 if b = false

If the expressions are arrays, an array is returned and the expression is considered to be an array expression, see Section 7.2.1. Note that the rst argument is a single Boolean expression, not an array expression. Extension to Boolean arrays as rst arguments requires the [mux] array operator notation presented in Section 7.3. The bool2u function takes a bool and returns an unsigned number of type unsigned<2>, dened as follows:

bool2u(0) = 0 bool2u(1) = 1 Notice the equality bool2u(b) = mux(b, 1, 0). 35

36

CHAPTER 4. ARITHMETIC AND BITVECTORS

4.2

Unsigned arithmetic

The unsigned types support the following internal operations that return signed numbers: addition +, subtraction -, multiplication *, division /, modulo (remainder) mod, power **, binary size binsize, saturation sat<M > and binary truncation trunc<[M ] >, up to any size. The size assertion operation assert<M > described in Section 4.2.10 asserts that a value is in a given unsigned type and provokes an error otherwise. Unsigned numbers support all standard Boolean comparisons =, <>, <, <=, >, and >=. Conversions to and from signed numbers and bitvectors will be studied in Section 4.6 and Section 4.6.5. Note that there is no + and - unary operations on unsigned numbers, since these operations convert unsigned numbers into signed ones. All operations can be applied to unsigned of any size. We use m, n to denote unsigned numbers, M, N to denote type sizes, and em , en to denote unsigned expressions of respective types unsigned<M > and unsigned<N >. Expression typing is denoted em : unsigned<M >. The result type is always computed to be the smallest unsigned type that ts the result, with the rules given below. Therefore, size extension can be viewed as automatic.

4.2.1

Unsigned addition

Unsigned addition + takes two unsigned m, n and returns an unsigned with value m + n. For expression type-checking, the result type is the smallest possible to accomodate the result, the worst case being (M 1) + (N 1): em : unsigned<M >, en : unsigned<N > em + en : unsigned<M + N 1> Note that addition never overows, for any unsigned size. There is no implicit bitdropping. The sizing rule being associative, the size of the result of a n-ary addition is independent of any subexpression calculation order. Therefore, there is no dierence in value and size between (em + en ) + ep and em + (en + ep ).

4.2.2

Unsigned subtraction

Unsigned subtraction - takes two unsigned m, n such that m n and returns an unsigned with value mn. If m is strictly smaller than n, then m - n is undened and the operation raises a run-time error, see Section 12.1.1. The result type is the smallest possible to accomodate the result, i.e. that of the rst expression since the second one can be 0: em : unsigned<M >, en : unsigned<N > em - en : unsigned<M > Unsigned subtraction is left-associative: em - en - ep means (em - en ) - ep .

4.2.3

Unsigned multiplication

Unsigned multiplication takes two unsigned m, n and returns an unsigned with value mn. For the result type size, the worst case is (M 1) * (N 1): em : unsigned<M >, en : unsigned<N > em * en : unsigned<(M 1)(N 1) + 1> As for addition, multiplication never fails and is always exact, without any bit loss. It is fully associative.

4.2. UNSIGNED ARITHMETIC

37

4.2.4

Unsigned power

Unsigned power takes two unsigned m, n and returns an unsigned with value mn . For the result type size, the worst case is (M 1) ** (N 1): em : unsigned<M >, en : unsigned<N > em ** en : unsigned<(M 1)(N 1) + 1> Power associates to the right: em ** en ** ep is em ** (en **ep )

4.2.5

Unsigned division

Unsigned division / takes two unsigned m, n such that n = 0 and returns an unsigned with value the integer quotient m/n. If n has value 0, then m / n is undened and raises a run-time error, see Section 12.1.2. For subexpression typing, the result type is that of the rst expression, since the second one can be 1: em : unsigned<M >, en : unsigned<N > em / en : unsigned<M >

4.2.6

Unsigned modulo

Unsigned modulo mod takes two unsigned m, n such that n = 0 and returns the remainder of the division of m by n. If n has value 0, then m / n is undened and raises a run-time error, see Section 12.1.3. For subexpression typing, since m mod n is strictly less than n and n is strictly less than N , the size of the result type is N 1: em : unsigned<M >, en : unsigned<N > em mod en : unsigned<N 1>

4.2.7

Unsigned binary size

Unsigned binary size binsize(m) takes an unsigned m and returns the number of bits necessary to write m in binary: binsize(m) = run-time-error if m = 0 n if 2n1 m < 2n

The binsize function is mostly used at compile-time to compute the size of the bitvector implementing an unsigned type unsigned<M > in binary, which is binsize(M 1). Notice that binsize(2M 1) = M , which is consistent with the fact that M bits are needed to implement the type unsigned<[M ]> = unsigned<2M >. Of course, the binsize function can also be used for other purposes. The typing rule for type-checking binsize subexpressions itself uses the function binsize: em : unsigned<M > binsize(em ) : binsize(M 1) + 1

38

CHAPTER 4. ARITHMETIC AND BITVECTORS

4.2.8

Unsigned saturation

Unsigned saturation sat<N > takes a positive compile-time constant N and an unsigned m and returns the largest unsigned in unsigned<N > that is less than or equal to m. Therefore, the denition is as follows: sat<N >(m) = The type-checking rule is as follows: em : unsigned<M > sat<N >(em ) : unsigned<N > Unsigned saturation is important for saturated arithmetic, for instance color handling in photonishing. The sat operator can also be used to change the type of an unsigned value into a bigger one. Given an expression e of type unsigned<M> and given N > M, the expression sat<N>(e) has the same value as e but has type unsigned<N> m if m < N N 1 if m N

4.2.9

Unsigned binary truncation

Remember that unsigned<[N ]> is a shorthand for unsigned<2**N >. Binary truncation trunc<[N ]> takes a positive constant N and an unsigned m of any size and returns the remainder of the division of m by 2N , i.e. the value of m mod (2** N ). In the binary representation, one simply drops the bits above N , brutally bringing back m to N binary bits. Truncation is easy to express using bitvector conversions: trunc<[N ]>(m) = m if m < 2N bin2u(u2bin(m)[0..N 1]) if m 2N

The trunc operation is only available for sizes that are powers of 2, and we make the special brackets <[.]> mandatory here to avoid any confusion. The type-checking rule is as follows: em : unsigned<M > trunc<[N ]>(m) : unsigned<[N ]> For an application example, consider addition of two N -bits numbers. The default Esterel calculation is exact: m, n : unsigned<[N ]> m+n : unsigned< 2N +1 1> for instance, if N = 3, one computes 7 + 6 = 13 : unsigned<15>. On the contrary, the usual HDL carry-dropping calculation returns 5 : unsigned<8> if the result is to be put on 3 bits. In Esterel, we must explicitly use trunc for such a truncated addition, writing trunc<[3]>(7+6) = 5. The associated type formula is: m, n : unsigned<[N ]> trunc<[N ]>(m + n) : unsigned<[N ]> Since 7 + 6 = 5 is not very good for program verication, we denitely prefer arithmetic to be exact by default. Therefore, we require carry-dropping to be explicit.

4.2. UNSIGNED ARITHMETIC

39

4.2.10

Unsigned assert

Given two natural numbers M, N and an expression eu of type unsigned<N > of value v, the expression assert<M >(eu ) is of type unsigned<M >. It returns v if v < M and provokes a run-time error otherwise, see Section 12.1.5. Numerical assert<M> is used to shrink down the type of a computation result for which an upper bound is known for dynamic reasons that are outside the scope of static typechecking. Unlike C-like casting, assert<M > is completely safe since it does not change the meaning of its argument and generates a verication condition. For a typical usage instance, consider converting a 4-bits bitvector B that represents the binary writing of an unsigned number into that number m. The conversion expression is u2bin(B), detailed in Section 4.6.1. Its type is unsigned<[4]> = unsigned<16>. Assume now that 0 m < 13 is guaranteed by the design specication. We want the result to be in unsigned<13>. By writing assert<13>(u2bin(B)), we appropriately restrict the type in a safe way. If, because of some bug, B does not respect the specication, we can detect that fact at run-time or verication time. For another usage instance, consider a signal S of type unsigned<M >. One often writes emissions like
emit ? S <= pre (? S )+1

Strictly speaking, these expressions are ill-typed, since the right-hand-side is in the type unsigned<M + 1> while the left-hand-side is in unsigned<M >. With rigid type-checking, the program should be rejected. One should write the following:
emit ? S <= a s s e r t <M -1 >( pre (? S ))+1

In tolerant type-checking, the language tolerates the emission but implicitly encloses the right-hand-side within assert<M >:
emit ? S <= a s s e r t <M >( pre (? S )+1)

which is actually less clever that the form above since the adder must work with one more bit. A similar case is the indexing of an array of sise M with an unsigned expression of a bigger type, that is tolerated in tolerant type-checking and for which compilers should generate verication conditions for simulators or formal veriers.

4.2.11

Unsigned comparisons

The comparison operators are classical: =, <> (not equal), <, <=, >, and >=. They take two unsigned numbers of any size and return a Boolean.

4.2.12

Unsigned size checks

There are ve cases where the value of an unsigned expression exp can be assigned to or passed to an object of unsigned type: variable assignment, signal emission, function call, procedure call, and module run statement:
V := exp emit ? S <= exp ... f (exp, ...)... c a l l P (... , exp, ...) run Sub [ S / I ] // // // // // V unsigned variable S unsigned signal unsigned argument unsigned argument S unsigned signal

40

CHAPTER 4. ARITHMETIC AND BITVECTORS

In practice, it would be too cumbersone to systematically add sat or trunc operations to guarantee that no run-time overow can occur. Therefore, the language allows the user to write an assignment of a value v in unsigned<M> to an object of type unsigned<N> with M > N. Such an assignment is correct only if v < N. Compilers and programming environments may check value assignment correctness in two ways: statically, by checking at compile-time that the value recieved is guaranteed to be in the required range. This is obvious if the value type is smaller that the receiver type; it can also be checked using partial evaluation techniques in other cases. dynamically, by providing run-time checks (at least in simulation mode). This should be done only if static checks are not sucient. Similarly, when indexing a signal or data array as for S[m], the index can be in a type larger than the index range of the array dimension, but it is an out-of-bounds error if its actual value is bigger than or equal to the dimension, see Section 12.1.5. Nevertheless, compilers should always be able to issue warnings for dubious operations.

4.3

Signed arithmetic

The signed types support the following internal operations: unary plus +, unary minus -, addition +, subtraction -, multiplication *, division /, power **, saturation sat<N >, and binary-truncation trunc<[N ]>, up to any size. Notice that there is no signed modulo operation, since this operation has no universally adopted denition yet. Signed types support the assertion assert<M > to assert that a signed value is within a signed type and the assertion assert unsigned<N > to assert that a signed value v satises 0 v < N. Signed numbers support all classical Boolean comparisons. All operations can be applied to signed of any size, with exact signed arithmetic. In practice, one most often uses full binary-encoded signed sets, i.e. types signed<[M ]>. However, ner signed types naturally appear when tightly typing subexpression, and they can also be useful in applications. The relation between signed numbers and unsigned numbers is as follows: the unary operations + and - convert unsigned numbers into signed ones; the abs(x) absolute value function returns the unsigned absolute value of x; the x>=0 test returns the sign of x as a Boolean (one can use mux to convert it to a 0 / 1 sign if needed). Unsigned numbers are automatically converted to signed numbers of the appropriate size by the unary operations + and - and when used together with signed numbers in arithmetic operations. Conversions to and from bitvectors by s2bin and bin2s is studied in Section 4.6.5. We use x, y to denote signed numbers and M, N to denote their type size. We use ex and ey to denote signed subexpressions of respective types unsigned<M > and unsigned<N >, denoting subexpression typing by ex : signed<M >. As for unsigned numbers, the result signed type of an expression is always computed to be the smallest signed type that ts the result, with the rules given below. Therefore, size extension can be viewed as automatic.

4.3.1

Signed unary plus and minus

When applied to a signed number x : signed<M >, unary plus + x simply returns x in the same type; when applied to an unsigned number m : unsigned<M >, unary plus + m

4.3. SIGNED ARITHMETIC

41

returns the signed value m; that value belongs to the type signed<M >, which is the smallest one to contain it. Here are the typing rules: ex : signed<M > = + ex : signed<M > em : unsigned<M > = + em : signed<M > When applied to a signed number x : signed<M >, unary minus - x returns the opposite of x, which is in signed<M + 1> since that type is the smallest one to contain M = (M ). When applied to an unsigned number m : unsigned<M >, unary minus - m returns the opposite of m, which is in signed<M 1> since the worst case is (M 1). Here are the typing rules: ex : signed<M > = - ex : signed<M + 1> m : unsigned<M > = - m : signed<M 1>

4.3.2

Signed addition and subtraction

Signed addition + and subtraction - take two signed numbers x, y and returns their sum x + y and their dierence x y. The result type is signed<M + N > in both cases since the worst cases are respectively (M ) + (N ) and (M 1) - (N ): ex : signed<M >, ey : signed<N > ex + ey : signed<M + N > ex - ey : signed<M + N > Notice that ex + ey has type signed<[N +1]> for ex , ey : signed<[N ]>, and that the same property holds for subtraction, which is always dened. This constrasts signed number vs. unsigned ones, since none of these properties is true for the latter. The sizing rule being associative, the size of the result of a n-ary addition is independent of any subexpression calculation order. Therefore, there is no dierence in value and size between (ex + ey ) + ez and ex +(ey + ez ). As for unsigned addition, signed subtraction is left-associative: ex - ey - ez is (ex - ey ) - ez .

4.3.3

Signed multiplication

Signed multiplication * takes two unsigned x, y and returns their product value x y. The result type is signed<M N + 1> since the worst case is (M ) * (N ) = M N : ex : signed<M >, ey : signed<N > ex * ey : signed<M N + 1> Signed multiplication is left-associative.

4.3.4

Signed division

Signed division / takes two unsigned x, y such that y = 0 and returns their signed integer quotient value x/y. If y has value 0, then x / y is undened and raises a run-time error, see Section 12.1.6. The result type is signed<M + 1> since the worst case is (M ) / (1) = M : ex : signed<M >, ey : signed<N > ex / ey : signed<M + 1> Signed division is left-associative.

42

CHAPTER 4. ARITHMETIC AND BITVECTORS

4.3.5

Signed binary size

The binsize function for unsigned numbers computes how many bits it takes to write an unsigned in binary. We provide no such function for signed numbers. However, one can use unsigned binsize to compute the binary bit number for a signed number x, which is itself an unsigned number. If x > 0, then the number of bits is binsize(abs(x)) + 1. If x < 1, the number of bits is binsize(abs(x + 1)) + 1. In both cases, the trailing +1 corresponds to the sign bit. One can use the following expression to compute signed binsize:
b i n s i z e ( abs (x+ bool2u (x < -1))) + 1

However, notice that this expression provokes a run-time error if x = 0 or x = 1.

4.3.6

Signed saturation

Signed saturation sat<N > takes a positive constant N , a signed x of any size, and returns the largest signed in signed<N > that is less than or equal to x if x > 0 or the smallest signed in signed<N > that is bigger than or equal to x if x < 0. Therefore, the denition is as follows: sat<N >(x) = The type-checking rule is as follows: ex : signed<M > sat<N >(ex ) : signed<N >
x

if N x < N N 1 if x N N if x < N

4.3.7

Signed binary truncation

Remember that signed<[N ]> is a shorthand for signed< 2** (N 1)>. Binary truncation trunc<[N ]> takes a positive constant N , a signed x of any size, and returns a signed obtained by dropping the higher-order bits in the 2s-complement binary representation of x, including the sign bit. It brutally brings back x to N bits. The formal denition is easiest using bitvector conversions: trunc<[N ]>(x) = x if M N bin2s(s2bin(x)[0..N 1]) if M > N

There is no simple arithmetic formula to express numerically what trunc does on signed numbers. For an usage example, consider addition of two signed N -bits expressions. The default Esterel calculation is exact: ex , ey : signed<[N ]> ex +ey : signed<2**N +1> For instance, with N = 3, one has (-8) + (-8) = 16 : signed<[4]>. On the contrary, a 3-bits carry-dropping usual HDL calculation returns 0. In Esterel, such a truncated calculation must involve an explicit trunc truncation: ex , ey : unsigned<[N ]> trunc<[N ]>(ex + ey ) : signed<[N ]>

4.4. BITVECTOR OPERATIONS

43

As for unsigned numbers, We prefer arithmetic to be exact by default since 8 8 = 0 is a formula that does not quite help formal verication. Therefore, we require carry-dropping to be explicit, writing trunc<3>((-8)+(-8)) = 0.

4.3.8

Signed absolute value

The absolute value abs(x) of a signed number x is an unsigned number with value x is x 0 and x if x < 0. Expression type-checking is as follows: ex : signed<M > abs(ex ) : unsigned<M + 1> Here, M + 1 is needed because of the worst case ex = M .

4.3.9

Signed comparisons

The comparison operators are classical: =, <> (not equal), <, <=, >, and >=. They take two signed numbers of any size and return a Boolean.

4.3.10

Signed assert

Signed assertion is similar to unsigned assertion described in Section 4.2.10, and it uses the same syntax. Given two natural numbers M, N and a signed expression ex of type signed<N >, the expression assert<M >(ex ) has type signed<M >. It returns the value v of ex if that value satises M v < M and provokes a run-time error otherwise, see Section 12.1.7.

4.3.11

Signed assert unsigned

Given two natural numbers M, N and a signed expression ex of type signed<N >, the expression assert unsigned<M >(ex ) has type unsigned<M >. It returns the value v of ex if that value satises 0 v < M and provokes a run-time error otherwise, see Section 12.1.7.

4.3.12

Mixed signed / unsigned arithmetic

Unsigned and signed can be combined in operations, as for u + x where u is an unsigned and x is a signed. In this case, the unsigned is converted into a signed, viewing the operation as (+u) + x with the corresponding size computation rules. Conversion is done in a way that respects the operator associativity rules presented in Section 7.2, and leaving intermediate results unsigned as long as possible. For instance, , with u, v unsigned and x signed, u + v + x is evaluated as (+ (u + v)) + x, while u + x + v is evaluated as ((+ u) + x) + (+ v).

4.4

Bitvector operations

Bitvectors are arrays of Booleans. Bitvector literals were presented in Section 3.1.11. We describe here the specic bitvector operations, including the conversion from signed and unsigned numbers to and from bitvectors. A cumbersome issue we cannot escape is that we have to deal with two traditional ways of seeing the same bitvector: least signicant bit (lsb) rst, as for the array notation

44

CHAPTER 4. ARITHMETIC AND BITVECTORS

{0,1,1}, or most signicant (msb) rst, as in b110 which is the same bitvector. In Esterel, we do not identify bitvectors with numbers, but we still have to follow the conventions. We shall be precise about what operators mean in both presentations.

4.4.1

Bitvector equality

Equality = and unequality <> are available for bitvectors. Two bitvectors are equal if and only if they have the same length and the same bits. Equality is not available for general arrays, and in particular for arrays of bitvectors. It needs to be programmed explicitly.

4.4.2

Bitvector shifts

There are four bitvector shift operations: unsigned right shift >>, unsigned left shift <<, signed right shift >>>, and signed left shift <<<. Signed operations preserve the sign if one sees the bitvector as the binary encoding of a signed number. The shift operations take a bitvector expression as left argument and a statically evaluable unsigned expression as right argument, as for X >> k (there is no variable k barrel shifting primitive for the time being, one must use a library module for this operation). The size of the result is equal to the size of the bitvector argument. The shift direction is indicated in the numerical bit order, i.e. with most signicant bit on the left, opposite to the array order. For instance, the same right-shift operation can be written in two ways:
b00110100 >> 1 = b00011010 { 0 , 0 , 1 , 0 , 1 , 1 , 0 , 0} >> 1 = { 0 , 1 , 0 , 1 , 1 , 0 , 0 , 0}

Shift operations are formally dened as follows, assuming that the bitvector expression evaluates to a bitvector B of size n and the shift argument statically evaluates to k: B[i + k] if i < n k 0 if n k i < n 0 if i < k B[i k] if k i < n B[i + k] if i < n k B[n 1] if n k i < n
0

(B >> k)[i] = (B << k)[i] = (B >>> k)[i] =

(B <<< k)[i] =

if i < k B[i k] if k i < n 1 B[n 1] if i = n 1

4.4.3

Bitvector concatenation

The lcat and mcat concatenation operators take n bitvectors and concatenate them. For lcat, which can be read lsb-cat, the lsb bit of the result is that of the rst argument. For mcat, which can be read msb-cat, the msb bit of the result is that of the rst argument. One has the following equality:

lcat(B0 , B1 , . . . , Bn ) = mcat(Bn , . . . , B1 , B0 )

4.4. BITVECTOR OPERATIONS

45

Of course lcat is easiest to understand using the array notation and mcat is easiest using the bitvector notation. Here is an example:
l c a t ({0 ,0} , {1 ,0 ,0} , {0 ,1}} = {0 ,0 ,1 ,0 ,0 ,0 ,1} mcat( b11 , b001 , b 10) = b1100110

The same examples can be written as follows using the other notation:
l c a t ( b00 , b001 , b10 } = b1000100 mcat({1 ,1} , {1 ,0 ,0} , {0 ,1}) = {0 ,1 ,1 ,0 ,0 ,1 ,1}

We formally dened concatenation of two bitvectors B and C of respective lengths k and l, extension to the n-ary case being trivial by associativity:

(lcat(B, C))[i] = (mcat(B, C))[i] =

B[i] if i < k C[i k] if k i < k + l C[i] if i < k B[i k] if k i < k + l

4.4.4

Bitvector extension

There are two extension functions that take a bitvector B of size M and an unsigned statically evaluable expression E of value N with N M : extend and sextend. Here is their denition extend(B, E) is a bitvector C of size N such that C[0..M 1] = B[0..M 1] and C[M..N 1] = 0. sextend(B, E) is a bitvector C of size N such that C[0..M 1] = B[0..M 1] and C[M..N 1]] = B[M 1].

u2bin (extend(B, E)) = u2bin (E) s2bin (sextend(B, E)) = s2bin (E) where u2bin and s2bin are the conversions to unsigned and signed numbers presented in Section 4.6 below.

4.4.5

Bitvector reverse

A bitvector exression of value B and size k is reversed by applying the reverse predened function. The formal denition is as follows:

(reverse(B))[i] =

B[k i 1] for i < k

46

CHAPTER 4. ARITHMETIC AND BITVECTORS

4.5

Bitvector maps

A map is a set of aliases for elds and slices of a named bitvector type. It can be unnamed or named. A map is dened by map keyword, optionally followed by an map name and a colon, followed by the bitvector type name, and by a curly-bracketed comma-separated list of eld names and indexation or slicing directives. Slices and bits can overlap within a map, and one can declare several maps for a single type. Here is an example:
type Word = bool [32]; map Word { Low [0..15] , High [16..31] } // unnamed

map Instruction : Word { Opcode [0..7] , Immediate [8] , // single bit Register [9..15] , Address [16..32] } map SignedNumber : Word { Sign [31] , // single bit Abs [0..30] }

Notice that Sign[31] is a single bit, while Sign[31..31] would be a bitvector of dimension 1. A map name such as Instruction has no semantic meaning. It is only meant to be used by programming and debugging environments, typically to display information in a nicer way. Maps are dened for a given type and do not follow structural type equivalence: a map for Word is not a map for any unsigned<32> nor for any other named type of basetype unsigned<32>. The same eld identier can occur in two maps only if they do not bear on the same type. Since map is a data declaration, in is inherited by data extension. A mapped bit or slice is referred to using the dot-eld notation:
emit { ? W . Immediate , ? W . Opcode <= 42 }

is the same as
emit { ? W [8] <= 1 , ? W [0..7] <= 42 }

Maps can themselves be reindexed or sliced: ?W.Register[1] is the same as ?W[10] and ?W.Register[1..3] is the same as ?W[10..12].

4.6. ENCODERS: FROM UNSIGNED TO BITVECTORS AND BACK

47

4.6

Encoders: from unsigned to bitvectors and back

An unsigned encoding translates an unsigned expression into a bitvector according to a given mathematical encoding. An unsigned decoding performs the converse operation. Esterel v7 provides the user with three predened codes, binary (classical binary writing, least signicant bit 0), onehot (exactly one 1 bit per value, for instance 0 0b001, 1 0b010, 2 0b100), and Gray. The user can also dene its own code. Unsigned encoding and decoding can be applied to any numerical type, not just types dened by powers of 2. However, in dense encodings such as binary and Gray, a number in unsigned<[M ]> is encoded by a bitvector in bool[M ] and conversely, while a number in signed<[M ]> is binary encoded by a bitvector in bool[M + 1] and conversely. This justies the <[.]> notation for signed and unsigned types, which facilitates the handling of powers of 2. For abitrary encodings, the size correspondence may be more complex. For instance, in onehot encoding, unsigned<M > is in bijection with bool[M ], which implies that unsigned<[M ]> is in bijection with bool[2M ]

4.6.1

Binary encoding / decoding

To turn an unsigned expression eu into a binary-encoded bitvector, one can use one of the two forms u2bin(eu ) u2bin(eu , b) Assume that the type of eu is unsigned<M >. In the rst form, the result has type bool[binsize(M 1)]. For instance, one has u2bin(6) = b110 = {0, 1, 1 } with type bool[3]. In the second form, b is the size of the resulting bitvector; it must be statically evaluable and satisfy b binsize(M 1). For instance, to put a value v of type unsigned<M > with M < 232 on a 32-bit bus, one writes
output Bus : bool [32]; ... emit ? Bus <= u2bin (v , 32)

Notice that the second argument is not really needed. One could obtain the same eect with the unary u2bin, by writing u2bin(assert<[32]>(v)). However, this form would be far less readable. According to our exact arithmetic philosophy, there is no implicit number bit-dropping for u2bin: the resulting bitvector must be always big enough to hold the result. If bitdropping is desired, it can be performed either before or after the call to u2bin. For instance, to get the N least signicant bits of v written in binary, one can write one of the two following statements:
emit Y <= u2bin ( v mod 2** N ) emit { X <= u2bin ( v ) , Y <= X [0.. N -1] }

Binary decoding is symmetrical. The unsigned value of a bitvector B read as a binaryencoded number is the result of the following expressions:

48

CHAPTER 4. ARITHMETIC AND BITVECTORS

bin2u(B) bin2u(B, u) In the rst form, the type of the result is unsigned<2M > = unsigned<[M ]> if B is of type bool[M ]. In the second form, the type of the result is unsigned<u>, where u must be statically evaluable. One must have u 2M . Because of incomplete value ranges, one may also want the result to be in a type unsigned<u> with u < 2M . For instance, one may know that only the 13 rst numbers can be encoded in a given 4-bit bitvector, and, therefore, one may want the result in unsigned<13>. This is easy to achieve using assert<>:
input Bits : bool [4]; output Value : unsigned <13 >; emit ? Value <= a s s e r t <13 >( bin2u (? Bits ))

There will be a runtime error if the bits do not satisfy the hypothesis, see Section 12.1.5.

4.6.2

Onehot and Gray encoding

The handling of one-hot and Gray encodings is similar. The following expressions return bitvectors: u2onehot(eu ) u2onehot(eu , b) u2gray(eu ) u2gray(eu , b) Let unsigned<M> be the type of eu . For onehot, the result type is bool[M + 1] in the unary case and bool[b] in the binary case, with the constraint b M + 1. For Gray, things are as for binary: the result type is binsize(M 1) in the unary case and bool[b] in the binary case, with the constraint b binsize(M 1). Conversely, the following expressions return unsigned numbers: onehot2u(B) onehot2u(B, u) gray2u(B) gray2u(B, u) Let bool[M ] be the type of B. For onehot, the result type is unsigned<M 1> in the unary case and unsigned<u> in the binary case, with the contraint u M 1. One must have M 1, and the argument bitvector must have exactly one bit with value 1, otherwise there is a run-time error described in Section 12.1.8. For Gray, the result type is unsigned<2M > in the unary case and unsigned<u> in the binary case, with the constraint u 2M . There is no direct re-encoding from an array to another array. To translate a signal array from binary to Gray, one must write
u2gray ( bin2u ( B ))

4.6. ENCODERS: FROM UNSIGNED TO BITVECTORS AND BACK

49

4.6.3

User-dened encoding

Finally, the user can dene its own encoding, e.g. myencoding. Then, the unsigned and bit lengths must be explicit using the u2code and code2u operations that take the code name as special argument: u2code<myencoding>(eu , u, b) code2u<myencoding>(B, u, b) As before, u is the size of the unsigned type and b the size of the bitvector. The constraints they must satissy depend on the encoding. The precise implementation and host language interface rules may depend on the target language and compiler. Notice that the number of bits needed to encode a number is free. One can for example use 2 bits for binary bit, as for the redundant Avizienis encoding used for carryless constant-speed addition.

4.6.4

Combining encodings

It is often useful to use dierent encodings in a single calculation. For instance, assume we want to compute 2u for a natural number u < M . In Esterel, we can simply write 2u. However, this operation may not be synthesizable by backends. Another way to write it using bitvector intermediates is as follows:
module Pow : g e n e r i c constant SZ : unsigned ; input u : unsigned <M >; input x : unsigned <2**( M -1) + 1 >; emit ? x <= a s s e r t <2**( M -1)+1 >( bin2u (u2onehot (? m ))) end module

The call to u2onehot builds a bitvector of length M with a 1 at index ?u. Calling bin2u on this bitvector computes 2?u , but with type unsigned<2**M>. The call to assert safely shrinks this type to the type <2**(M-1)+1>, which is the best type for the result whose value can reach 2**(M-1).

4.6.5

From signed to bitvectors and back

For signed numbers, we consider only one encoding, 2s-complement binary. The functions that translate signed numbers to bitvectors is s2bin, unary or binary: s2bin(ex ) u2bin(ex , b) As usual, the highest-order bit (msb) of s2bin(ex ) is the sign bit. Assume that the type of ex is signed<M >. Then, in the unary case, the result type is bool[binsize(M ) + 1]. For the binary case, the result type is bool[b], with the constraint b bool[binsize(M )+1]. The reverse conversion from bitvectors to signed numbers is performed by bin2s: bin2s(B) bin2s(B, s) Let bool[M ] be the type of B. Then the result type is signed<[M 1]> in the unary case and signed<s> in the binary case, with the constraint s 2M 1 . As for unsigned

50

CHAPTER 4. ARITHMETIC AND BITVECTORS

numbers, one can use the assert<.> function to narrow the range. For instance, if only numbers from 5 to 4 should be encoded by a bitvector B of width 5, one write
a s s e r t <5 >( bin2s (B ))

A run-time error will be generated if the decoded value is out of range, see Section 12.1.7.

Chapter 5

Signals and Variables


Signals are the main objects dealt with by Esterel v7 30 programs. They are shared scoped objects, broadcasted within their scope in a way that ensures fully safe synchronization of parallel threads. More conventional variables are also available, but they are of restricted use since they cannot be freely shared between concurrent control threads. However, variables are useful as intermediates in computations and necessary as vehicles to communicate with the host language, being passed by reference in procedure calls (see Section 8.3.2). In this chapter, we rst introduce the signal categories in Section 5.1: interface signals, local signals, oracles, and assertions. Then, in Section 5.2.1, we study the main features of signals, the status and the value. Section 5.3 is devoted to standard signals, the most common signals that act instantaneously. Section 5.4 is devoted to registered signals, which act with a delay of one tick and are fundamental for Moore machines and pipelines. Section 5.5 presents temporary signals, which do not hold their value over time. Section 5.7 presents signal arrays. The full signal declaration syntax will be presented in Chapter 6, together with the difference between interface and module features of signals. This chapter mostly concentrates on behavioral aspects.

5.1

Interface and local signals, assertions, and oracles

There are several categories of signals corresponding to dierent uses: Interface signals are declared within interface or module declarations. They establish communication between a module body and its environment. Their scope is a module body. Local signals are declared within a module body or a statement block. This denes their scope. Assertions are special local signals designed for simulation or formal vericationbased validation. Oracles are special local signals used for non-deterministic specications. They are declared locally but input from the environment in a hidden way. Oracles are incomplete and experimental in Esterel v7 30. 51

52

CHAPTER 5. SIGNALS AND VARIABLES

This chapter focuses on the common features of all signals and only uses interface and local signals in examples. Basic example Here is a simple example to illustrate how interface and local signals are declared and what their scopes are:
module Stopwatch : type TimeType ; input Second ; output Time : TimeType ; s i g n a l InternalTime : TimeType i n ... s i g n a l Reset i n ... ... end s i g n a l end s i g n a l end module

The interface signals are Second and Time, with global module scope. The local signals are InternalTime, whose scope is the full module body, and Reset, with scope delimited by the signalend signal keywords pair. The Second signal is a pure signal that conveys a bit-level present / absent status information. Being an input, it is generated by the modules environment, which is the global execution environment for the main module and the the local execution context for a submodule. The Time valued signal is made of the pair of a bit-level status and a value of type TimeType. It is declared output, which means that it is generated by the module itself and output to the modules environment. There are also inputoutput signals, which are bound to master signals in both input and output mode, see Section 9.7.

5.2

Signal status and value

We browse quickly through the main features of signals before describing the precise signal kinds. The rst features we present are the main components of a signal: the status and value. Their combination leads to two dierent kinds of signals: pure and valued, the latter divided into two subkinds, full , and valueonly. The next features concern temporal behavior: signals can be either standard , i.e. instantaneous, or registered. These features are orthogonal to the previous ones. Valued signals can have two additional features, independent of each other and of the temporal ones: being initialized and being combined. Furthermore, for better hardware synthesis, there are temporary signals that do not keep their value over time. They are a bit special and discussed in a specic section. In the last part of this section, we discuss which features are interface features and which are module features.

5.2.1

Signal status

The status of a signal S is the basic tool for control path programming. It is a binary information usually referred to as absent / present, or false / true, and sometimes also called 0 /1, or low / high, or unset / set, which is standard hardware terminology. The

5.2. SIGNAL STATUS AND VALUE

53

status of S is absent by default, and it is set present either by the environment for an input signal or by the program when an emit statement is executed, either directly in the module where the signal is declared or indirectly through a submodule connection. The status of S is tested for presence or awaited to be present by statements such as if S then ..., await S, every 3 S do ... end, etc. In such statements, the value of the Boolean expression S evaluates to false if S is absent and to true if S is present. The status-handling statements are presented in full details in Chapter 8. Here is a simple example of status-based sequential programming:
module Status ; input I ; output O ; await I ; emit O end module

The await I statement terminates at the rst instant I occurs, rst instant excluded. The semicolon ; that follows the await statement is sequencing. Control is immediately passed to emit O that immediately emits O. See Chapter 8 for the full description of control propagation. Dissymmetry of status with absent as default is key to Esterel control path programming: there is no need to execute any statement to keep O absent.

5.2.2

Signal Value

A valued signal is declared using a colon and a type name after the signal name, as in S : T. The value ?S of S is a data object of type T. If the type is an array type, the value is an array value that can be indexed; for instance, with T = bool[8], the value S is a bitvector of width 8 that can be indexed to return a Boolean, as in ?S[3]. As for the status, the value is set either by the environment if S is an input or by emit statements executed by the module body or in a connected submodule. Here is a simple example:
module Value : input A : unsigned <[8] >; output X : unsigned <[9] >; every immediate I do emit ? X <= 2*? A end every end module

Whenever it receives A, the Value module emits twice the value of A as the value of X.

5.2.3

Pure, full, and valueonly signals

The signals I and O of the above Status example are the simplest Esterel objects, called pure signals. They only have a status. The signals A and X of the Value example are called full signals. Their declaration involves a value type after a colon :. They have both a status and a value. The statement emit ?X <= 2*?A performs two simultaneous functions: it sets the status of X present, and it sets its value ?X to that of the right-hand-side 2*?A. Status and value are coordinated: the signal value changes only when the status is present. In

54

CHAPTER 5. SIGNALS AND VARIABLES

hardware terminology, one would say that the status acts as a value enable. The status is tested for presence in control statements such as every immediate I, while the value is used in data expressions such as 2*?A. For instance, for I and O unsigned signals, the statement
every I do emit ? O <= 2*? I end every

emits O with twice the value of I whenever this value is emitted by the rest of the program. There are also value-only signals that have a value but no status. They are declared using a value keyword before the type. Here is an example:
module Converter : input Farenheit : value f l o a t ; output Celsius : f l o a t ; input Second ; every Second do emit ? Celsius <= (? Farenheit -32. f ) * 5. f / 9. f end every end module

Here, the input value ?Farenheit is assumed to be delivered by a thermometer that one can read at any time but that does not send value change information. This is why we use a statusless value-only signal. In Converter, we chose to output the Celsius value as a full signal. We could also output it as a valued signal, using the declaration
output Celsius : value f l o a t ;

In that case, the user would not be warned of value changes on Celsius. Full signals and value-only signals are collectively called valued signals. Therefore, a signal is either pure or valued. For valued signals, one may allow simultaneous multiple emission of values. The values are then combined using a combination (or resolution) function. This will be detailed in Section 5.6.

5.3

Standard signals

A standard signal is declared in the simplest way, by giving its name and possibly its type after a colon, as for input I or output O : Byte. Standard signals status and value are instantaneously updated by signal emission. For valued signals, the value is persistent and it can be read at any time provided it is dened. The pre operators give access to the status and value at previous instant.

5.3.1

Standard signal emission and reception

When a standard signal is emitted, its status and value are made instantaneously available to other active statements. Here is an example:

5.3. STANDARD SIGNALS


module Standard : output O : unsigned ; s i g n a l S : unsigned i n pause ; emit ? S <=1; pause ; pause ; emit ? S <= 2; || await S ; emit ? O <= ? S +1; await S ; emit ? O <= ? S +2 end s i g n a l end module

55

At rst instant, the rst parallel branch initial pause statement waits for one tick while the second parallel branch await statement starts waiting for S. At second instant, the rst branch emits S with value 1, which immediately triggers emission of O with value 2 by the second branch. The rst branch pauses for two ticks, while the second one waits for another occurrence of S. At fourth instant, the rst branch emits S with value 2 and the second branch emits O with value 4. Status and value transmission from S to O through the await S statements and the ?S expressions take no logical time, i.e. are performed combinationally in the same tick as the emission. So are the intermediate additions.

5.3.2

Value persistency and initialization

If a standard signal S is valued, its value ?S is persistent. If it is not emitted nor received from the environment (for an input) in one instant, it remains that of the previous instant. For instance, in Standard above, reading the value ?S at third instant would return 1. By default, before the rst emission, the value is uninitialized and reading it is a runtime error, see Section 12.2.1. For instance, in the Standard module above, the value of S and O are undened at rst instant and reading them at that instant would be an error. One can explicity initialize the signal value at declaration time using the init keyword, as for
output O : unsigned i n i t 0;

In that case, the value is always dened and it can be read at all instants. The initial value is overwritten by the rst signal emission or by the rst reception from the environment for an input. Here is an example involving an initialized local signal:
module InitializedSignal : output O : unsigned ; s i g n a l S : unsigned i n i t 0 i n pause ; emit ? S <= 1; || s u s t a i n ? O <= ? S } end module

56

CHAPTER 5. SIGNALS AND VARIABLES

Here, O is emitted with value 0 at rst instant and value 1 from second instant onwards. Removing the pause statement would make the initial value overwritten at rst instant and thus useless. For arrays, the initial value can be either a full array literal or a simple literal of the base type, see Section 3.1.10:
s i g n a l S : unsigned [5] i n i t {0 ,1 ,2 ,3 ,4} , T : bool [12] i n i t 0 i n ... end s i g n a l

Notice that systematically holding the value over time involves storing it in some memory, which may be too expensive for hardware applications. Thus, we also provide temporary signals that do no store the value, see Section 5.5.

5.3.3

The status pre(S) operator

For a pure or full standard signal S, one can read the status at previous instant using the pre(S) and pre1(S) Boolean expressions, For instance, one can detect a rising edge of S by computing S and not pre(S). The only dierence between pre(S) and pre1(S) is the Boolean value at rst instant of the lifetime of a signal, i.e. at the instant where the signal declaration is executed. At that instant, pre(S) is false while pre1(S) is true. Here is an example:
module Pre : input I , J ; output X , Y ; { sustain { X <= pre ( I ) , Y <= pre1 ( J ) } || pause ; signal S in emit S ; || s u s t a i n Z <= pre1 ( S ) end s i g n a l } end module

Here, X is emitted at any instant that follows an occurrence of I, Y is emitted at rst instant and at any instant that follows an occurrence of J, and Z is emitted at second and third instant. Notice that the expression pre1(S) yields its initial value 1 only at second instant since this is when the lifetime of S starts because of the pause statement. The pre operator extends to status expressions as explained in Section 7.1.2. For instance, pre(S and not T) is the same as pre(S) and not pre1(T) (noticing that pre(not T) is equal to not(pre1(T)) and not to not(pre(T)), because of the initial instant a well-known retiming pitfall). Status pre operators cannot be nested. If needed, nesting can be achieved using auxiliary signals, as in

5.3. STANDARD SIGNALS


s i g n a l preS , prepreS i n sustain { preS <= pre ( S ) , prepreS <= pre ( preS ) } || ... await prepreS ; ... end s i g n a l

57

or auxiliary sliding window arrays, as in


s i g n a l PreS [ N ] i n emit { PreS [N -1] <= S , PreS [0.. N -2] <= pre ( PreS [1.. N -1]) } || ... i f PreS [ i ] then ... end s i g n a l

See Section 5.7 and Section 8.4 for details on status arrays and slices.

5.3.4

The value pre(?S) operator

One can also read the value at previous instant using the pre(?S) data expression. The previous value is dened at the rst instant that follows value denition. If the signal has an initial value, it is also the initial value of pre(?S). There is no extension of data pre to expressions: one cannot write pre(?S+?T), but only pre(?S)+pre(?T). Value pre operations cannot be nested. To implement sliding windows for values, one can use either intermediate signals or sliding windows as for status. Here is an example
s i g n a l PreValS [ N ] : unsigned i n emit { ? PreValS [N -1] <= ? S , ? PreValS [0.. N -2] <= pre (? PreValS [1.. N -1]) } || ... ? PreValS [ i ] + 1 end s i g n a l

See Section 5.7 and Section 8.4 for details on value arrays and slices.

5.3.5

Standard signal submodule connection

Standard signals can be sent to or emitted by submodules through signal connections, detailed in Section 9. Consider the following example:
module Sub : input I : unsigned ; output O : unsigned ; s u s t a i n ? O <= ? I + 1 end module main module Main :

58
input A : unsigned ; output X : unsigned ; emit ? X <= 1; pause ; run Sub [ A / I , X / O ] end module

CHAPTER 5. SIGNALS AND VARIABLES

Here, A in Main is connected to the input I of Sub, while X is connected to the output O of Sub. The status and value of A are tranmitted to the input I of Sub, while the status and value of O in Sub are transmitted to X. In Main, the X signal is emitted in two dierent ways: at rst instant, by the explicit emit ?X statement; after the rst instant, i.e. when Sub runs, through the X / O connection with help of the A / I connection. Assume that Main receives A from its environment with value 2. Then, A is present in Main with that value. After the rst instant, since Sub is running, the connection A / I instantaneously sets I present with value 2 within Sub. This provokes instantaneous emission of O with value 3. The connection X / O makes X present in Main with value 3, and this value is instantaneously returned to the environment. Therefore, an emit in the submodule acts just as an emit in the main module through the signal connection.

5.4

Registered signals

Registered signals act with a delay of one tick for status and value transmission. Pure, full, and value-only signals can be registered. Local and output signals can be registered, but input signals cannot.

5.4.1

Registered signal declaration and emission

The register declaration uses a reg or reg1 keyword after a colon and before the type. Registered signals are emitted using emit next instead of emit, and tested or valueread in the same way as standard signals. A registered signal is present at an instant if it has been emitted at the previous instant; its value is also shifted by one tick. Here is a registered version of Standard above:
module Registered : output O : unsigned ; s i g n a l S : reg unsigned i n pause ; emit next ? S <=1; pause ; pause ; emit next ? S <= 2; || await S ; emit ? O <= ? S +1; await S ; emit ? O <= ? S +2 end s i g n a l end module

5.4. REGISTERED SIGNALS

59

As for Standard, emission of S is performed at second and fourth tick. However, since status and value transmission consume one tick, S is present only at third and fth tick, with respective values 2 and 4. The output O is emitted at these ticks with these values. For a reg1 signal, the status is also set present at the rst instant of the signal lifetime. Replacing reg by reg1 in Registered would make S present at initial instant. Notice that the emit next statement is instantaneous, just as the emit statement: the status and value are instantaneously posted for the next instant and control instantaneously proceeds in sequence.

5.4.2

Value persistency and initialization

The value ?R of a registered signal R is persistent. By default, it is uninitialized until the instant that follow the rst emission of the signal. Therefore, in Registered above, the value of R at rst instant is uninitialized, and reading it would be a run-time error. This is also true for a valued registered signal declared reg1, since the 1 applies only to status and not to value. As for a standard signal, the value of a registered can be initialized using an init keyword. Then, the value at rst signal lifetime instant is the initial value, which implies that no uninitialized signal error can occur. Consider the following variant of Registered:
module RegisteredInit : output O : unsigned ; s i g n a l R : reg unsigned i n i t 0 i n pause ; emit next ? R <=1; pause ; pause ; emit next ? R <= 2; || emit ? O <= ? R await R ; emit ? O <= ? R +1; await R ; emit ? O <= ? R +2 end s i g n a l end module

In RegisteredInit, O is emitted at rst instant with value 0 by the second parallel branch, the subsequent behavior being identical to that of Registered.

5.4.3

The next(R) status operator

For a registered pure or full signal R, the next(R) operator tests whether an emit next R statement is executed in the instant. Here is an example:
module Registered2 : output X : reg , Y ; emit { next X , Y <= next ( X ) }

60

CHAPTER 5. SIGNALS AND VARIABLES

Here, Y is present at the rst instant since emit next X is executed at that instant, while X is only present at the second instant since it is registered. The expression pre(R) is disallowed for a registered signal R.

5.4.4

The next(?R) value operator

For a registered valued signal R, the next(?R) operator returns the posted value if an emit next ?R <= ... statement is executed in the instant; otherwise, it returns the current value of ?R. Of course, in the latter case, there is a run-time error if that value is yet undened. Here is an example:
module Registered3 : output X : reg unsigned , Y : unsigned ; emit { next ? X <= 314 , ? Y <= next (? X ) }

In Registered3, Y is present at rst instant with value 314, while X is undened at rst instant and present at second instant with the same value. The expression pre(?R) is disallowed for a valued registered signal R.

5.4.5

Registered signals submodule connection

Registered signals can be connected to submodule inputs in the same way as local signals. Here is an example similar to that of Section 5.3.5:
module Sub : input I : unsigned ; output Rsub : reg unsigned ; s u s t a i n next ? RSub <= ? I +1 end module module MainReg : output X : unsigned ; output R : reg unsigned ; emit next ? R <= 2; pause ; run Sub [ R / I , X / RSub ] end module

Ar rst instant, Main emits R with value 2 for the next instant. At second instant, R is made present with value 2, and Sub starts. Because of the R / I connection, I is made present in Sub with value 2, which provokes emission of Rsub with value 3 for the next instant. At third instant, because of the X / Rsub connection, X is made present with value 3 and output to the environment. Altogether, there is no dierence between standard and registered connections if one only thinks in terms of current status and value: the status and value considered are those in the instant, and the registers acts only to locally delay status and value setting within Main and Sub Because registers only support emission for the next instant, it is not possible to connect a submodule output signal to a master module registered signal through an output connection.

5.4. REGISTERED SIGNALS

61

However, in the future, it may be useful to introduce connections of the form next R / O advice wanted!

5.4.6

Building data pipelines with registered signals

Registered signals are fundamental for Moore machine design and for hardware data pipelining. Since their current status and value depend only on past actions, they are Moore signals in the usual sense. For an example of data pipelining, consider the following calculation: given four unsigned value-only inputs A, B, C, and D, compute the value (A B) + (C D). The obvious idea is to write
module Calc : input {A , B , C , D } : value unsigned ; output O : unsigned ; s u s t a i n ? O <= (? A *? B )+(? C * D ) end module

But the data calculation may lead to a unacceptable critical path, and one may want to pipeline the design by inserting a register barrier between the multiplications and the addition. This is very easy by Esterel program transformation. First, we introduce two intermediate signals to cut the operation:
module Calc : input {A , B , C , D } : value unsigned ; output O : value unsigned ; s i g n a l { AB , CD } : value unsigned i n sustain { ? AB <= ? A *? B , ? CD <= ? C *? D , ? O <= ? AB + ? CD } end s i g n a l end module

So far, we havent changed anything in the behavior, since the intermediate standard signals AB and CD act in a purely combinational way. However, pipelining is now made easy . We simply declares the local signals AB and CD to be registered, annotating their emission with the next keyword, and we delay the rst emission of O by one tick using the pause statement presented in Section 8.2:
output O : value unsigned ; s i g n a l { AB , CD } : reg value unsigned i n sustain { next ? AB <= ? A *? B , next ? CD <= ? C *? D } || pause ; s u s t a i n ? O <= ? AB + ? CD end s i g n a l

Of course, O is emitted with a delay of one tick, which is the pipeline latency. Since next is used for all equations in the rst sustain statement, we can factor it out at sustain level, as will be explained in Section 8.4:

62

CHAPTER 5. SIGNALS AND VARIABLES


s i g n a l { AB , CD } : reg value unsigned i n s u s t a i n next { ? AB <= ? A *? B , ? CD <= ? C *? D } ... end s i g n a l

In the above design, ?O is the result of an addition. In hardware implementation, this means that ?O is the output of some combinational logic. To improve circuit delay, one often desires output values to be register driven instead of combinational logic driven, i.e. to compose Moore machines instead of Mealy machines. To achieve this, it suces to declare O registered:
output O : value unsigned ; s i g n a l { AB , CD } : reg value unsigned i n s u s t a i n next { ? AB <= ? A *? B , ? CD <= ? C *? D } || pause ; s u s t a i n next ? O <= ? AB + ? CD end s i g n a l

Of course, latency is now 2: the rst value of O is output at third instant.

5.5

Temporary signals

Temporary signals are restricted kinds of valued standard signals with non-persistent values. While the lifetime of the value of a standard or registered signal is the lifetime of its declaration statement, the lifetime of the value of a temporary signal is an instant. Of course, the value pre operator pre(?S) is not available for a temporary signal S, and a temporary signal cannot be registered. Temporaries are used mostly for hardware synthesis, where one does not want to store values in memory unless strictly necessary.

5.5.1

Temporary signal declaration

A temporary signal is declared by the temp keyword before the type name:
output X : temp Byte ;

A temporary signal can be value-only; it is then declared using the temp and value keywords in any order:
output Y : temp value Byte ; s i g n a l S : value temp Byte i n ... end

A temporary signal is internally emitted just as a standard signal, using an emit statement. For a temporary input of a module M, there are two cases: if the signal is full (not value-only), its value is supposed to be received from the module environment only when the signal is present.

5.5. TEMPORARY SIGNALS

63

If the signal is value-only, the value is assumed to be received from the environment at each instant in the lifetime of M. Therefore, one can view a value-only temporary input as a temporary whose status is always present.

5.5.2

Temporary signals submodule connection

A temporary signal S can also be sent to a submodule or received from a submodule. Consider rst the connection of a temporary S to the input I of a submodule Sub, as in run Sub[S / I]: If S is full, at each tick where S is present, I is set present in Sub if it is pure or full, and the value of S is passed to I if I is valued. If S is value-only, then I must also be value-only, and the value of S is passed to I at each tick where Sub is executed. The laws are dual for a temporary S connected to an output O of Sub, as in run Sub[S / O]: If S is full, so must be O; then S is set present and Os value is passed to S at each tick where O is present in Sub. If O is value-only, Os value is passed to S at each tick where Sub is executed. The rules will be made fully precise in Chapter 9.

5.5.3

Temporary signal initialization

The value of a temporary signal can be initialized using the init keyword. Unlike for a standard signal for which initialization is performed only at lifetime start, initialization of a temp signal is performed anew at each instant1 .Therefore, the value of an initialized temp signal is always dened. Here is an example:
module Temp : output O : unsigned ; s i g n a l S : temp unsigned i n i t 0 i n pause ; emit ? S <= 1; || s u s t a i n ? O <= ? S end s i g n a l end module

In Temp, the value of S is initialized to 0 at each instant; since S is emitted at second instant with value 1, it takes that value at that instant, but for that instant only. Therefore, O is emitted at rst instant with value 0, at second instant with value 1, and at each instant with value 0 from then on. The behavior would be the same with S declared value temp. Beware, because initialization is dierent, it may be non-innocuous to turn a standard signal into a temporary one just to improve circuit synthesis. One must check that the value is not accessed at a time where the signal is not emitted, since one would nd the previous value for the standard signal and the initial value for its temp variant.
The initial value could be more accurately called a default value. However, we did not want to add one more keyword just for this case.
1

64

CHAPTER 5. SIGNALS AND VARIABLES

5.5.4

Temporary signal value denition

If a temporary signal is initialized, its value is always dened and can always be read. Otherwise, the value can be undened. There are two cases: For a normal non-value-only temporary signal, the value is dened exactly when the status is present. For a value-only temporary signal, the value is dened when the signal is driven. An input value temp signal is always driven by the environment if it is an input; it is driven by any emit statement executed within the module; nally, when connected to a submodule output, it is driven as long as the submodule is alive, see Section 9.6. Reading the value of an undriven temporary signal is a run-time error, see Section 12.2.2.

5.6

Single vs. combined signals

By default, a valued signal S of any kind supports only one value emission at a time. We say that S is single. It is a run-time error to perform two simultaneous emissions, see Section 12.2.3. Such an error can be detected at run-time, compile-time, or verication time, according to the programming environment. Esterel also provides its user with combined signals that support multiple simultaneous emission. The values are combined using a specied combination (or resolution) operation. Any kind of valued signal (standard, registered, temporary, full, value-only) can be combined. A combined signal is declared by adding the combine keyword followed by a data function name or by an operator name. The function or operator must be of type (T,T):T if the value type is T. It must be associative and commutative. If several values at emitted at a time for S, say v1 , v2 , . . . , vn , and if the function is called f, then the nal value ?S of S is f(v1 , f(v2 , ....f(vn1 , vn ))) Anticipating on signal arrays, here is an easy way to compute the sum of an array of byte with addition as the combination function 2
module ArraySum : input I [16] : unsigned <256 >; output O : unsigned <16*255+1 > combine +; f o r i < 16 do emit ? O <= ? I [ i ] end f o r end module

Notice the result type unsigned<16*255+1>. This is the tightest possible type for the sum, since each array component maximum value is 255. If the signal type is an array type, the combination function must apply on the non-array base type of the signal and combination is done in a pointwise way. Here is an example:
type Word = bool [32]; s i g n a l W : Word combine or ;

For W, or-combination is performed independently for each ?W[i]. Combining arrays values globally requires explicit programming.
Warning: for hardware synthesis, this method may not be optimal in term of adder width, depending on the synthesis system.
2

5.7. SIGNAL ARRAYS

65

5.7

Signal arrays

Signals of any kind can be organized into signal arrays or arrays of arrays. The dimensions are given right after the signal identier. They must be statically evaluable expressions. Here are signal array declarations examples:
input InBus [ Size ]; output Matrix [ N ][ N ] : f l o a t ; s i g n a l S [ N ]: bool [ M ] i n ... end // array of pure signals // square matrix of floats // signal array of bitvectors

Any kind of signal (standard, registered, temporary, full, value-only) can be an array. All properties of elementary signals extend pointwise to arrays.

5.7.1

Signal array status

A pure or full signal array has one status per index. For instance, the Inbus status array above can be indexed as in Inbus[1] or sliced as in Inbus[1..5]. Arrays of arrays such as S[3][8] can be totally indexed, as in S[1][7], partially indexed, as in S[1], or sliced, as in S[1][2..3], S[1..4][0..5], or S[1..4]. In the above example, the status of Matrix is an array of arrays, and the status of S is a simple array. Notice that the value array dimension of S is ignored as far as the status is concerned. For standard signals, the pre operator applies pointwise, and it can be applied to partially or totally indexed arrays. Therefore, one can write the expressions pre(Matrix), which is of type bool[N][N], pre1(Matrix), which is the same but with initial status present at each position, Pre(Matrix[1][2..4]), which is of type bool[3], and Pre(Matrix[1][2]) which is of type bool. The same holds for next expressions for registered arrays. Notice that indexing must be inside pre or next. One cannot write pre(Matrix)[1][2].

5.7.2

Signal array value

A valued signal array A declared of a non-array data type T also has one value of type T per index. Therefore, the expression ?A denotes a data array of the same dimension as the status. This array can be indexed and sliced in the same way. For instance, for Matrix above, ?Matrix is an array of type float[N][N], ?Matrix[1][2] is a oat, and ?Matrix[1..4][2] is an array of type float[4]. If the data type T is itself an array type, the signal and data dimensions are concatenated from left to right, signal dimensions rst. Here is an example:
input Memory [1024] : bool [32];

Here, there is one status per memory 32-bits word. As far as data is concerned, the ?Memory expression has type bool[1024][32]. In the same way as the status expression Memory[100] denotes the status of the 100th word, the data expression ?Memory[100] denoted the value of the 100th word, which is of type bool[32]. Therefore, ?Memory[100][8] denoted the bit of index 8 of the 100th word value. Whether to put the dimensions on status or value is a matter of control granularity. There are three posssible choices for Memory:
input Memory : bool [1024][32]; input Memory [1024] : bool [32]; input Memory [1024][32] : bool ;

66

CHAPTER 5. SIGNALS AND VARIABLES

With the rst choice, there is only one Memory status bit for the whole memory. That bits witnesses any change in the memory. With the second choice, there are 1024 status bits, one per memory word. Only one status bit changes when a word changes. With the third choice, there is one status bit for each memory bit, which gives maximal but expensive control over memory change information. Be careful when the signal is declared using an intermediate dened array type, as in the following example:
type Word = bool [32]; input Memory [1024] : Word ;

The type of Memory is still bool[1024][32], which means that we apply the following implicit operation on dened types:

?Memory

Word[1024]

= (bool[32])[1024] = bool[1024][32] To avoid any confusion, there is no way to write directly a potentially ambiguous type expression of the form (bool[32])[1024] in Esterel, and one should simply remember that indices are always written in the order of dimension declaration. Therefore, ?Memory[i] is a Word, and ?Memory[i][j] is the j-th bit of that word3 . As for the status, the value pre and next operators can be applied to arrays, and they act pointwise. For instance, pre(?Matrix[1..4][2..5] has type float[4][4], while pre(?Memory[1]) has type Word = bool[32]. As for the status, indexing must be done inside the call of pre or next

5.7.3

Combined arrays

Any valued array can be combined, using a pointwise combination function. The combination is done position per position independently. There is no way to use a function that acts globally on data arrays. Here is an example:
s i g n a l CombineMemory [ N ] : bool [ M ] combine or ;

Then, or-combination is performed individually for each position ?Memory[i][j].

5.8

Variables

Besides valued signals, Esterel v7 30 can handle data through more classical typed variables. Variables are in the same namespace as signals and all other names. They are only local to modules, therefore they cannot appear in interfaces.
It turns out that the potential pitfall disappears when one writes the postx form array 1024 of array 32 of bool instead of the prex form bool[1024][32], but we want to keep the simplest and most usual notation for Esterel.
3

5.8. VARIABLES

67

5.8.1

Variable declaration and scope

Variables are declared using the var vardecl in stat end statement which denes their scope. They can be initialized using the := assignment symbol followed by an expression, see Section 8.3.1. They can have array types, but, unlike signals, they cannot be themselves arrays (there is no need to distinguish between variable arrays and variables of array type since variables have no status). Here are examples:
var IterationNumber : unsigned <[8] > , VarArray1 : unsigned <[8] >[5] := {5{0}} , VarArray2 : bool [12] := 0 i n ... end

For arrays, the initialization value can be either a full array literal, as for VarArray1 above, or a single literal of the array base type, as for VarArray2 above. See Section 3.1.10 for details.

5.8.2

Persistent vs. temporary variables

By default, a variable is persistent, i.e. retains its value between instants. If it is initialized, a persistent variable takes its initial value when the var statement that declares it starts; otherwise, it remains uninitialized until its rst value assignment by an assignment or procedure call statement. As for signals, using the temp keyword, one can also declare temporary (or combinational) variables that do not keep their values over time and do not generate hardware registers. Here are examples
var X : temp Byte , Y : temp f l o a t ... end var := 3.14 f i n

As for a signal, an initialized temp variable is reinitialized afresh at each instant. When changing a variable from temp to normal, beware that initialization is performed only once instead of at each tick; this is normal since the variable value has become persistent over time. Variables values are updated either by := assignment statement, see Section 8.3.1, or by procedure calls, see Section 8.3.2. When it exists, the initial value is overwritten. Variable values are read in data expressions by simply mentioning their name, without the ? symbol used for signals. For instance, the expression ?I+X adds the value of the variable X to the current value of the signal I. Reading the value of an undened variable is a run-time error, see Section 12.3.1. Notice that an initialized persistent or temporary variable is never undened.

5.8.3

Variables cannot be shared

Unlike signal emission and reception, variable updating embodies no built-in write / read synchronization mechanism. Therefore, a variable can take several successive values in the same instant. Here is an example:

68
var V : unsigned := 1 i n emit O ( V ); V := V +1; emit O2 ( V ) end var

CHAPTER 5. SIGNALS AND VARIABLES

Here V successively takes the values 1 and 2 in the same instant, O1 is emitted with value 1, and O2 is emitted with value 2. Because of this, we must forbid arbitrary sharing of variables to ensure behavior determinism. The following behavior must be forbidden:
var V : i n t e g e r := 0 i n V := 1; V := 2; || emit O ( V ) end var

Since both assignments and the emission should occur in the same instant, there is no consistent value to emit for O and the program must be rejected. There are two ways of forbidding shared variables: Requiring that no sharing conict ever occurs at run-time. One should then report a run-time error, see Section 12.3.4. Statically enforcing that this will be true, by some compile-time algorithm. Note: In the Esterel Studio we require a variable not to be shared by two concurrent threads: a variable read by one branch of a parallel cannot be read nor written by any other branch of the parallel. In other words, a variable written by a computation thread ought to be local to this thread. This clearly rejects the above statement. More subtle static analysis could be performed by other compilers.

Chapter 6

Interfaces and Module Headers


This chapter presents the declaration of signals and ports in interfaces and modules. We rst present the signal declaration syntax in Section 6.1, detailing all the attributes a signal declaration can have. We establish a distinction between two kinds of attributes: Interface attributes, which concern the way in which one can connect to a module interface signal from outside its module. Module attributes, which are more behavior-related and local to the module that owns the signal. Signal declaration in interfaces can only involve interface attributes, and the other attributes of a signal must be given by renement declarations in modules. We then present hierarchical declarations of signals and ports within interfaces in Section 6.2. We present input and output relations in Section 6.4. We end the chapter by presenting module headers and the renement of interface signals in Section 6.5 Local signal and ports declarations are deferred to Chapter 8, were we study the signal local signal declaration statement in Section 8.14.

6.1
6.1.1

Signal declaration overview


Signal declaration syntax

A signal declaration must occur after one the input, output, inputoutput or signal keywords. The rst three keywords characterize interface signal declarations; they can only occur in interfaces and module headers. The signal keyword starts module signal declarations; it can only occur in module bodies. A signal declaration consists of the name of a signal, optionally followed by a dimension list for a signal array, and optionally followed by a colon and a list of attributes. One can use a single keyword for a comma-separated list of independent signal declarations. Furthermore, when there is an attribute list, a declaration can declare a list of signals or signal arrays sharing the same attributes by enclosing this list within curly brackets. Here are examples:
input I ; output X [4] , {Y , Z [8]} : unsigned <M >[3]; inputoutput IO ;

69

70

CHAPTER 6. INTERFACES AND MODULE HEADERS


s i g n a l S : reg value signed i n i t -2 combine + i n ... end

Local signal declarations can also declare ports, see Section 8.14 for details. We concentrate on interface signals in this chapter.

6.1.2

Signal declaration attributes

The attributes are as follows: The reg attributes declares the signal to be registered. Without it, the signal is standard. The signal must be output or local. The value attribute declares a valued signal to be value-only. Without it, a valued signal is full. The temp attribute declares a valued signal to be temporary. Without it, a valued signal is persistent. The type attribute declares the type of a valued signal. Without it, the signal is pure (and all attributes except reg are disallowed). The combine f declares a valued signal to be combined, with f as the combination function. One can replace f by +, *, and, or, or xor provided the type is adequate. Without the combine attribute, a valued signal is single. The init exp attribute declares the signal to be initialized by the given expression. Without it, a valued signal is uninitialized. The reg attribute is the only one allowed for pure signals. All the other attributes require the signal to be valued, i.e. a type to be declared. The reg and temp attributes are exclusive. For a valued signal declaration, the reg, temp, and value attribute must appear before the type, in any order, while the combine and init attributes must appear after the type, in any order. Here are some examples of valid attribute declarations (in module headers where all attributes are allowed):
output input output input output ... R : reg ; { X1 , X2 } : unsigned ; Y : reg unsigned <[12] > i n i t 0; Z : temp value unsigned i n i t 3 combine +; T : reg value bool combine or i n i t f a l s e ;

And here are examples of invalid declarations:


input input output output output S S R X Y : : : : : reg ; temp; reg value i n i t 0; temp reg unsigned ; unsigned temp; % % % % % no reg for input missing type missing type incompatible attributes wrong order

6.1. SIGNAL DECLARATION OVERVIEW

71

6.1.3

Interface vs. module attributes

Signal declaration in interface units are restricted. They must start with the input, output, or inputoutput keyword, and they can only involve the type, value, and temp attributes. These attributes are called interface attributes, while reg, init, and combine are called module attributes. Interface attributes are exactly what is needed to understand how to connect to the signal from the global environment or from another module. The value attribute tells that the interface signal has a value but no status, while the temp signals declares how the value is driven. On the other hand, module attributes declare internal behavioral properties of signals that should not be known from external users of this signal. For instance, consider a registered output R of a module Sub, which can be directly declared registered in the Sub module header:
module Sub : output R : reg ; ... emit next R ; ... end module

Consider now Sub as a submodule of a module M, with a signal connection of the form run Sub[X / R]. The fact that R registered is important for the behavior of R within Sub, but it irrelevant for the user X of this signal in M. Logically speaking, X simply sees the current status of R as it is produced by Sub, and the same holds for the value if the signals are valued. Changing R in Sub into a standard signal with exactly the same output behavior should be totally equivalent for M, which therefore should not know about the reg feature in Sub. For instance, here are two modules that perform the same, one with a standard output and one with a registered output:
module Sub1 : output O ; pause ; every 2 t i c k do emit O end every end module module Sub2 : output O : reg ; every 2 t i c k do emit next O end every end module

Since they are behaviorally equivalent, the only thing Main should know about them is their common interface, which is simply
i n t e r f a c e SubIntf : output O ; end i n t e r f a c e

Then the same run statement with the same signal renaming will work for both Sub1 and Sub2.

72

CHAPTER 6. INTERFACES AND MODULE HEADERS

Similarly, the combine and init attributes of a signal output by a module should be unknown to its external users: For combine, they should only see the result of the combination. for init, they should see the initial value as the value at initial instant, not dierent from any other value. For a submodule input, we also disallow combine in interface units; it could in principle be allowed by handling multiple connections to a single input, but we feel that this mechanism is much too fancy and requires an explicit protocol which can be easily programmed in Esterel v7. We allow init, but only within the submodule: an init declaration gives the signal an initial value for the body in a way invisible to the external producers of the signal, see Chapter 9. Therefore we disallow init in interface units for input signals, restricting its use to module units. Altogether, we forbid the combine and value attributes in interface units. Specifying these attributes in a module after having imported an interface requires a renement declaration, presented in the next section.

6.1.4

Interface signal renement

Back to the M / Sub1 / Sub2 example above, splitting the interface is obvious for Sub1:
i n t e r f a c e SubIntf : output O ; end i n t e r f a c e module M1 : extends Intf ; pause ; every 2 t i c k do emit O end every end module

Splitting the interface for M2 uses very same interface Intf, but requires the addition of a renement declaration in the module header to recover the missing attributes:
module M2 : extends Intf ; r e f i n e O : reg ; every 2 t i c k do emit next O end every end module

A signal renement declaration can only occur in a module header. It consists of the name of the signal followed by the added attributes. The type and value attributes already declared cannot be mentioned nor changed in the refine declaration. The temp attribute can be rened into mem or reg. With mem, the signal is actually memorized in the module, as if the temp keyword was simply ignored. With reg, the signal is declared registered. In both cases, the temp attribute is canceled. The mem and reg renements are fundamental to allocate signal memories in designs, see Section 6.5.1. Here are examples of correct renements:

6.2. INTERFACES AND PORTS


i n t e r f a c e Intf : input A : temp unsigned <M >; output X ; output Y : signed <N >; output Z : temp bool ; end i n t e r f a c e module Mod : extends Intf ; r e f i n e A : i n i t 0 combine +; r e f i n e X : reg ; r e f i n e Y : mem i n i t -1; r e f i n e Z : combine and; ... end module

73

And here are incorrect renements of the same interface:


module Mod : extends Intf ; r e f i n e A : value i n i t 0; r e f i n e X : reg unsigned ; r e f i n e Y : signed < N +1 >; r e f i n e Z : reg ; end module

% % % %

value forbidden type addition forbidden type change forbidden temp reg forbidden

6.2

Interfaces and Ports

Interface units describe the input / output structure of modules. Besides data declaration and extension already described in Chapter 3, interface declarations consist in signal, port, and relation declarations, plus interface extension. Ports are groups of signals typed by interfaces. They make the interface structure fully hierarchical, in addition to its objectoriented character that follows from extensibility. Relations are simple combinational behavior assertions about input or output signals. In this section, we rst describe simple interfaces, which are the leaves of the hierarchy. We dene the mirror of an interface. Then, we dene ports that lead to general interfaces. We give details about what full or selective extension means for interfaces. Finally, we present input and output relations.

6.2.1

Simple interfaces

A simple interface involves declarations and extension of data and declarations of signals starting with one of the directionality keywords input, output, or inputoutput. The declared signals can be typed, and, in that case, they can bear the additional interface attributes value and temp. The signals must have distinct names that do not conict with other names visible from their declaration point. Here is a simple example:
i n t e r f a c e Intf1 : type Byte = unsigned <[8] >; input I ; output O : Byte ; inputoutput IO : value Byte ;

74
end i n t e r f a c e

CHAPTER 6. INTERFACES AND MODULE HEADERS

A simple interface can also contain relations, whose study is deferred to Section 6.4.

6.2.2

The mirror of a simple interface

The mirror mirror Intf of a simple interface Intf declares exactly the same signals with opposite directionalities: input is changed into output, output is changed into input, while inputoutput stays the same. Input relations and output relations are also swapped, as will be explained in Section 6.4. Mirroring has no eect on data. Of course, mirroring an interface twice yields it back unchanged. Here is an interface that extends the mirror of Intf1 above, followed by its own mirroring:
i n t e r f a c e Intf2 : extends mirror Intf1 ; output X ; end i n t e r f a c e i n t e r f a c e ArrayInft2 : extends mirror Intf2 ; end i n t e r f a c e

In Intf2, I and X are outputs, O is an input, and IO remains an inputoutput. In Intf3, I and X are inputs, O is an output, and IO remains an inputoutput. Notice that the declaration of Intf3 uses the extension mechanism just to give a name to the mirror of Intf2.

6.2.3

Ports

General interfaces can declare ports, where a port is a named groups of signals itself typed by an interface. A port is declared by its name, a colon, and an interface name possibly preceded by the mirror keyword. Here is an example:
i n t e r f a c e Intf1 : type Byte = unsigned <[8] >; input I ; output O : Byte ; inputoutput IO : value Byte ; end i n t e r f a c e i n t e r f a c e Intf2 : input J ; port Direct : Intf1 ; port Mirror : mirror Intf1 ; end i n t e r f a c e

Of course, for the hierarchy to be consistent, dependency of interfaces through ports must have no cycle and port building must develop from simple interfaces. The pointed notation is used to address port elds. In Intf2, Direct.I and Mirror.O are input signals, while Direct.O and Mirror.I are output signals. The signals Direct.IO and Mirror.IO are both inputoutput signals. Using curly brackets to represent interface boundaries, we can picture Intf2 as follows:

6.2. INTERFACES AND PORTS


i n t e r f a c e Intf2 : input J ; port P : { input I ; output O : Byte ; inputoutput IO : value Byte ; }; port Mirror : { output I ; input O : Byte ; inputoutput IO : value Byte ; }; end i n t e r f a c e

75

Warning: this notation is just for explanation; it is not accepted in Esterel. When mirroring a general interface, the port interfaces are recursively mirrored. In the mirror Intf2 mirror interface, Direct.I and Mirror.O become output signals, while Direct.O and Mirror.I become input signals. Signals and declared ports in an interface must have distinct names; however, there is no name conict between components of a port and the objects declared in the interface where the port is declared. An input I added to Intf2 would not conict with Direct.I nor with mirror.I. To ease the declaration of local signals in module bodies, interfaces can be extended and ports can be declared and rened in local signal declarations, see Section 8.14. The open delcaration makes it possible to locally forget about the dot notation by setting I as a synonym for P.I, see Section 8.15.

6.2.4

Port arrays

One can also declare arrays of ports in an interface:


i n t e r f a c e Intf3 : constant Size : i n t e g e r ; input J ; port DirectArray [ Size ] : Intf1 ; port MirrorArray [ Size ] : mirror Intf1 ; end i n t e r f a c e

Port elds addressing uses a mix of dot notation and array indexation, as in DirectArray[3].I. Dimensions add up for signals and values. Consider the declarations
i n t e r f a c e ArIntf : input I [32]; output O : bool [8]; end i n t e r f a c e

i n t e r f a c e ArPortIntf : port P [16] : ArIntf ; end i n t e r f a c e

Then P.I has dimension [16][32], while ?P.O is of type bool[16][8]. However, as said before, indexing is mixx: one writes P[i].I[j] and ?P[i].O[j], not P.I[i][j] and ?P.O[i][j].

76

CHAPTER 6. INTERFACES AND MODULE HEADERS

6.3

General Interfaces

We now present general interfaces, obtained recursively from simple interfaces by extension and port denition. Data extension was described in Chapter 2. Signal extension can be full or selective, i.e. limited to input, output, or inputoutput signals.

6.3.1

Full interface extension and port denition

Full interface extension consists of the extends keyword, optionally followed by mirror, and followed by the interface name. Full extension imports all the signals and ports declared in the base interfaces, their directionalities being mirrored if mirror is specied. Interface extension can be combined with port denition in the declaration of an interface. Here is an example using Intf1 and Intf2 of Section 6.2.3:
i n t e r f a c e Intf1 : type Byte = unsigned <[8] >; input I ; output O : Byte ; inputoutput IO : value Byte ; end i n t e r f a c e i n t e r f a c e Intf2 : input J ; port Direct : Intf1 ; port Mirror : mirror Intf1 ; end i n t e r f a c e i n t e r f a c e Intf3 : extends Intf2 ; port P : Intf2 ; port Q : mirror Intf2 . end i n t e r f a c e

Here, Intf3 directly extends Intf2 and has two additional ports involving Intf2. Using the curly bracket notation, we can picture Intf3 as follows:
i n t e r f a c e Intf3 ; input J ; port Direct : { input I ; output O : Byte ; inputoutput IO : value Byte ; }; port Mirror : { output I ; input O : Byte ; inputoutput IO : value Byte ; }; port P : { input J ; port Direct : { input I ; output O : Byte ; inputoutput IO : value Byte ; }; port Mirror : { output I ; input O : Byte ; inputoutput IO : value Byte ; }; }; port Q : { output J ; port Direct : { output I ;

6.3. GENERAL INTERFACES


input O : Byte ; inputoutput IO : value Byte ; }; port Mirror : { input I ; output O : Byte ; inputoutput IO : value Byte ; }; }; end i n t e r f a c e

77

There is no name conict, since there is no confusion between J, P.J, Q.J, etc. However, extending both Intf2 and mirror Intf2 in the same interface would generate a name conict for all the components of Intf2.

6.3.2

Selective interface extension

Selective interface extension or port denition extends or denes a port with only the inputs or outputs of an interface, descending port interfaces recursively to gather these inputs or outputs. The extends keyword is followed by an optional mirror, by input, output, or inputoutput, and by the interface name. For extends input Intf, all the input signals of Intf are imported, the ports of Intf being imported with interface restricted to their input components. For extends mirror output Intf, all the output signals and port components of Intf are imported mirrored, i.e. as inputs. Extension is symmetrical with output instead of input. Building observers by selective extension A common practical usage is to transform all outputs of an interface into inputs. This is useful to build observers of programs, i.e. additional modules that take all interface signals of a given module as input and compute some properties of the whole set of signals. Here is an example:
module M : input I ; output O ; ... end module module Observer : extends input M ; extends mirror output M ; s u s t a i n a s s e r t O_only_if_preI = O = > pre ( I ) end module

the rst extends directly imports the input I. The second extends imports O as an input, since Ms interface is mirrored before extracting the inputs. The Observer module inputs I and O and checks their temporal dependency. Selective extension examples Let us exemplify further extensions with ports. With Intf1, and Intf2 as above, consider the following selective extensions, which can be seen as restrictions of Intf3:

78

CHAPTER 6. INTERFACES AND MODULE HEADERS


i n t e r f a c e Intf3In : extends input Intf3 ; end i n t e r f a c e i n t e r f a c e Intf3Out : extends output Intf3 ; end i n t e r f a c e

These interfaces are stripped versions of Intf3 reduced to input or output signals. They can be pictured as follows, using curly brackets as interface delimiters:
i n t e r f a c e Intf3In : input J ; port Direct : { input I ; }; port Mirror : { input O : Byte ; }; port P : { input J ; port Direct : { input I ; }; port Mirror : { input O : Byte }}; port Q : { port Direct : { input O : Byte }; port Mirror : { input I ; }}; end i n t e r f a c e i n t e r f a c e Intf3Out : port Direct : { output port Mirror : { output port P : { port Direct port Mirror port Q : { output J ; port Direct port Mirror end i n t e r f a c e

O : Byte ; }; I ; }; : { output O : Byte ; } : { output I }}; : { output I }; : { output O : Byte ; }};

Finally, consider the following interface:


i n t e r f a c e Intf4 : extends input Intf2 ; extends inputoutput Intf2 ; port P : output Intf2 ; port Q : mirror input Intf2 ; end i n t e r f a c e

Then, Intf4 can be pictured as follows.


i n t e r f a c e Intf4 : input J ; inputoutput IO : value Byte ; port Direct : { input I ; }; port Mirror : { output O : Byte ; }; port P : { port Direct : { output O : Byte ; } port Mirror : { output I }}; port Q : { port Direct : { output I ; }; port Mirror : { output O : Byte ; }}; end i n t e r f a c e

6.4. RELATIONS

79

6.3.3

Data renaming for generic interface extension

If an interface has generic components, one can instantiate them at instantiation time using the same syntax as for data extension:
i n t e r f a c e Intf : g e n e r i c constant N : unsigned ; input I [ N ]; output O : unsigned [ N ]; end i n t e r f a c e

i n t e r f a c e Intf32 : extends Intf [ constant 32 / N ]; end i n t e r f a c e

6.3.4
.

Interface extension does not share components

Signal extension diers from data extension on another point. There is no sharing between common base interfaces, cf. Section 2.4.5. For instance
i n t e r f a c e Intf : input I ; end i n t e r f a c e i n t e r f a c e Intf1 : extends Intf ; end i n t e r f a c e i n t e r f a c e BadIntf : extends Intf ; extends Intf1 ; end i n t e r f a c e

Here, it is considered that I is declared twice and the program is rejected. Multiple inheritance for interfaces would be much more complicated than the same for data because of ports.

6.4

Relations

A relation is a possibly named simple instantaneous (combinational) predicate bearing either on statuses of input signals only for an input relation or on statuses of output signals only for an output relation. For reasons explained later on in this section, inputoutput signals cannot appear in relations and there can be no mixed relation involving both inputs and outputs. The predicates involve the signal operators dened in Chapter 7, i.e. the classical and, or, xor, and not connectives plus multiplexor mux, implication =>, equivalence <=>, and exclusion #. When taking the mirror of an interface, an input relation becomes an output relation and conversely.

80

CHAPTER 6. INTERFACES AND MODULE HEADERS

6.4.1

Input relations

An input relation of an interface Intf is a possibly named predicate on input signal statuses. It expresses an assumption over the environment of a module: at any instant, the signals sent by the main or local environment of a module or port of interface Intf are assumed to satisfy all input relations. This can be checked in simulation or assumed in verication and optimization. In verication, a property may be valid only if the input relations are satised. In optimization, input relations can be used as input dont cares [14, 7] to optimize circuits further. Here are examples:
input r e l a t i o n Minute = > Second ; input r e l a t i o n RadioButtons : A # B # C ;

The rst unnamed relation expresses a basic fact about time units. The second named relation expresses that the input button signals A, B, and C are exclusive, i.e. that at most one of them can be present at each instant. Relation names are semantically unimportant, but they can be useful in programming environments, for example to report violated relations in simulations. Port components and explicit array elements can appear in relations, but full arrays cannot. Here is an example of a relation involving ports and arrays:
i n t e r f a c e Intf1 : input I [4] : i n t e g e r ; output O ; end i n t e r f a c e i n t e r f a c e Intf2 : input J ; port P : Intf1 ; port Q : mirror Intf2 ; input r e l a t i o n J = > ( P . I [2] or Q . O ) end i n t e r f a c e

In practice, # exclusion input relations are very useful for optimization and verication, since they drastically restrict the input event space. For example, with n inputs declared exclusive, the number of input events is n + 1 (the empty event and all one-signal events) instead of 2n . One can also use input relations to momentarily stick a signal at present or absent, which is often useful to debug programs. Here is a way to stick A present and B absent:
input r e l a t i o n A and not B ;

6.4.2

Output relations

Dually, an output relation is a predicate on outputs which must hold for any state of a module and any input that satises the input relations:
output r e l a t i o n ( O1 and OA [1]) = > not O2 ;

Ports and arrays are handled as for input relations. Unlike input relations, output relations are not assumed to hold by default for a module or port which extends the interface, since their truth results of the behavior of the module itself. They should be checked at simulation or formal verication time. They can also serve as output dont care conditions for logic synthesis, see [14].

6.4. RELATIONS

81

Output relations of an interface Intf become input relations of the mirror interface mirror Intf. Therefore, they are essential for optimization and verication of modules that extend mirror Intf.

6.4.3

Relation inheritance

Relations are automatically imported by interface extension, and they are mirrored if the extension is mirrored: the mirror of an input relation is an output relation with the same expression and conversely. There is a more subtle extension scheme: a relation declared in an interface Intf is automatically propagated to all ports and port arrays of interface Intf or mirror Intf. Consider the following example;
i n t e r f a c e Intf : input I , J ; output X [2]; input r e l a t i o n I = > J ; output r e l a t i o n X [0] # X [1]; end i n t e r f a c e i n t e r f a c e Intf2 : port P : Intf ; port Q [2] : mirror Intf ; end i n t e r f a c e

Then the following leaf signal relations are automatically inferred for Intf2
input r e l a t i o n P . I = > P . J ; output r e l a t i o n P . X [0] = > P . X [1]; output r e l a t i o n Q [0]. I = > Q [0]. J ; output r e l a t i o n Q [1]. I = > Q [1]. J ; input r e l a t i o n Q [0]. X [0] # Q [0]. X [1]; input r e l a t i o n Q [1]. X [0] # Q [1]. X [1];

6.4.4

Why not allowing more general relations

Esterel v7 interface relations are voluntarily limited in expressive power. One could consider much more elaborate relations, involving simultaneously inputs and outputs and sequential pre operators, but it would not be clear where to stop and intuition might get lost. First, by dening relations between input and outputs, one could fully specify the behavior of a module without the need to give it a body. Here is an and-gate example:
i n t e r f a c e AndIntf : input I , J ; output O ; r e l a t i o n ( I and J ) <= > O ; end i n t e r f a c e

However, notice that the mirror relation does not dene a deterministic behavior and cannot be used as a module specication. Second, using the pre operator, one could state that an input signal I alternates between present and absent:

82

CHAPTER 6. INTERFACES AND MODULE HEADERS


input r e l a t i o n I xor pre ( I );

But where should we stop? Should we also include arbitrary temporal logic predicates, including fairness ones? Our decision is to stick to simple but proven useful combinational input-only and output-only relations within the language. We leave the more elaborate mixed and sequential relations to the programming environment, where fancy temporal assertion can be used to dene complex sequential environment or conditions to check, see for example reference [2].

6.5

Module headers

Module headers contains the declaration of all objects used by the module. They are similar to interface declarations, except that all attributes are allowed in input, output, and inputoutput signal declarations, and that renement declarations can be added to specify module attributes for interface signals imported either by interface extension or by port declarations. We have seen renement examples in Section 6.1.4. Here is another example:
i n t e r f a c e Intf : input I [32] : i n t e g e r ; output O : i n t e g e r ; end i n t e r f a c e module M : extends Intf ; r e f i n e I mem i n i t {32{0}}; r e f i n e O : reg combine +; port P : mirror Intf ; r e f i n e P . I : reg1 ; ... end module

Notice that array dimensions do not appear in renement of signals or port arrays. When considering M as an interface, i.e. in an extends interface M declaration, only the directionalities and interface attributes of signals (type, temp and value) are retained, the other module attributes being discarded.

6.5.1

Memory assignment control by mem and reg renements

In hardware designs, it is frequent to broadcast a valued signal generated by a producer module to a set of consumer modules, with the producer generating the value from a memory and the consumers viewing the value as temporary and not memorizing it. In this case, it is best to declare the signal as temp in a common interface, with a mirror for either the producer or the consumer, and to rene it either mem or reg in the producer. Here is an example where two signals are exchanged in this way between two modules:
i n t e r f a c e Intf : input X : temp unsigned ; output Y : temp unsigned ; end i n t e r f a c e module GenY :

6.5. MODULE HEADERS


// reads X as temp , generates Y from own memory extends Intf ; r e f i n e Y : mem; ... emit ? Y <= ... ... end module module GenX : // reads Y as temp , generates X from own memory extends mirror Intf ; r e f i n e X : reg ; ... emit next ? X <= ... ... end module

83

Here, Y keeps a dened value from its rst emission by GenY on, while X keeps a dened value from the tick that follows its rst emission by GenX on.

84

CHAPTER 6. INTERFACES AND MODULE HEADERS

Chapter 7

Expressions
Expression are built from literal, Boolean signal status tests, signal values, variables, literals, and enumeration values using operators and function calls. They are strongly typed, with limited operator overloading and implicit conversion to avoid ambiguities. They can be freely parenthesized. Expressions are either simple expressions of non-array type or array expressions of array type. An expression of type bool is called a test, an array expression of base type bool is called an array test. Signals and variables can be fully indexed by simple expressions, yielding a simple result, or they can be sliced if they are arrays, returning an array of the same base type and reduced dimension. One can slice several dimensions, as in X[3..5][6..10]. An expression is statically evaluable if it only involves constants operators, and predened functions. A dimension expression is a statically evaluable simple expression of unsigned or signed type, with result bigger than 1 (dimension 0 is disallowed). Assume the following declarations:
input S : unsigned ; input T ; output X [8][10]; input Y [10] var V : unsigned i n ... end

Here are well-typed expression examples illustrating the various kinds of objects:
S and T S and ?S >0 X [1][2] and S X [1] [ or ] Y X [1][2..3] [ xor ] Y [9..10] V + ?S mux(? S >0 , V +1 , bin2u ( Y )) // // // // // // // // status expression status + value test full array indexation , status and pointwise array expression slice expressions addition of a variable and a signal value mux between data expressions , bitvector - to - unsigned conversion

Remark: unlike in the previous version Esterel v7 10, expressions now freely mix signal statuses and values. A signal status is simply wiewed as a Boolean or as an array of Booleans. However, compilers can internally perform very dierent calculations on statuses and values because data and control optimization rely on dierent algorithms. 85

86

CHAPTER 7. EXPRESSIONS

7.1

Expression Leaves

Expression leaves are the terminal elements of expressions. There are literals, already described in Chapter 3, signal status tests and pre tests, and signal values or pre values, and variable values.

7.1.1

Signal status tests

The status of a signal S is tested by the trivial expression S, possibly indexed if the signal is an array. The base type of the result is bool. Assume that S is a signal array such as S[M ][N ]. Then, one can return a Boolean by fully indexing S and a Boolean array by slicing S. A slice is specied by a pair [k..l] of statically evaluable unsigned or positive signed numbers k and l with k l. A slice with k = l such as S[2..2] denes a array of type bool[1], not a bool. A full slice for a dimension [M ] is written [..]. It spans the entire dimension, i.e. is equivalent to [0..M 1]. There can be slices on several dimensions, and full indexations and slices can be mixed. The dimension of a sliced array is obtained by concatenating the elementary slice dimensions from left to right. For instance, X[2..5][3..12] has dimension [4][10]. An indexation is called partial if not all dimensions are indexed or sliced, in which case the missing slices are assumed to be full. Here are legal indexations for S[5][6]:

S[1][3] : bool S[1..3][2] : bool[3] S[1][2..4] : bool[3] S[..][2] : bool[5] S[1] : bool[6] = S[1][..] S[1..3][2..5] : bool[3][4] S[k..l] : bool[l-k+1][6] k and l statically evaluable, k l

Slices are renumbered from 0 for furter indexations. For instance,

(S[3..5][2]) [1] = S[4][2] Notice that the parentheses that isolate S[3..5][2] are mandatory, since the indexation S[3..5][2][1] would be considered a 3-dimensional indexation and therefore ill-typed.

7.1.2

Testing the previous status of a signal

The previous value of a signal is tested using the pre and pre1 operators, with S indexed or sliced as before if it is an array. For pre, the initial value at signal life rst instant is true, while it it false for pre1. The initial value applies elementwise for arrays. Here are legal expressions for S simple and T[5][6] an array:

7.1. EXPRESSION LEAVES


pre ( S ) S and not pre ( S ) not S and pre1 ( S ) pre1 ( T ) pre ( T [2][5..7])

87

// // // //

rising edge of S falling edge of S array pre1 ( T )[5][6] slice of dimension [3]

Consider the second expression above, falling edge: because of the use of pre1, the edge is considered falling at index i, j if T[i][j] is absent at rst instant. Using pre instead of pre1 would make the edge never falling at rst instant. The pre and pre1 operators can be extended to expressions containing only signal status terms, with the following rules:

pre(not E) = not pre1(E) pre1(not E) = not pre(E) pre(E1 or E2) = pre(E1) or pre(E2) pre(E1 and E2) = pre(E1) and pre(E2) pre(E1 xor E2) = pre(E1) xor pre(E2) pre(E1 # E2 # ... # En) = pre(E1) # pre(E2) # ... # pre(En)

Nesting pre is not possible. Therefore, pre(pre(S)) is disallowed. One must use an auxiliary signal to compute it, as in the following example:
s i g n a l preS i n sustain { preS <= pre ( S ) , O <= pre ( preS ) } end s i g n a l

To build a more general pre array, one usually builds a shift register. This is very easy in Esterel:
module ShiftReg : g e n e r i c constant M : unigned ; input S ; output PreArray [ M ]; sustain { PreArray [0] <= S , PreArray [1.. M -1] <= pre ( PreArray [0.. M -2]) } end module

7.1.3

Reading signal values

Given a valued signals S, the expression ?S reads the value of the signal in the current instant. If S is a simple signal of basic type, the expression ?S has the type of S. If S is a signal array or if it has an array type, then ?S is a data array expression with dimension the full dimension of S, i.e. the concatenation of the signal dimensions and the data dimensions of S. Consider the following declarations:

88
input I [12] : bool ; type Word = bool [32]; output O : Word ; s i g n a l S [3] : Word i n ... end

CHAPTER 7. EXPRESSIONS

Then ?I has type bool[12], ?O has type Word = bool[32], and ?S has type bool[3][32]. The distinction between status and value dimensions disappeard when reading the value of a signal. Full indexation and slicing is exactly as for statuses, see Section 7.1.1. Here are slices and their types:

?I[2..4] : bool[3] ?O[12..23] : bool[12] ?S[..][12..23] : bool[3][12] ?S[1..2] : bool[2][32] ?S[1..2][12..23] : bool[2][12]

7.1.4

Reading signal previous values

The previous value of a signal S is read using the expression pre(?S). Value pre applied to a single signal and cannot be extended to expressions, unlike for status. For instance, pre(?S + ?T) is disallowed. As for status, valued pre cannot be nested. If S is a signal array, one use indexing and slicing inside pre, as for pre(?S[2][3..4]). Indexation and slicing works exactly as for standard value read.

7.1.5

Reading variable values

The value of a variable V is read by the trivial expression V, indexed and sliced as for signal status and value if the type is an array type.

7.1.6

Port component access

For ports, one read the status or value of a leaf signal using the dot notations P.S and ?P.Q.S. If the nal signal S is an array, one can index and slice it as any other signal array. If the port is an array, one must index it fully, as in P[12].S or ?P[12][4].Q[k].S[5]. One can index or slice the nal signal if it is an array, as in P[1].S[3..4], or ?P[12].Q[k].S[5..7]. but one cannot slice the port array itself: P[1..2].S[3] is disallowed. The status and value pre operators can be applied to ports, as for pre(p.S[2]) or pre(?P[1].X).

7.2

Operators

Operators apply to basic objects and can be extended to arrays by putting them into brackets, as for [or] and [+]. Note that array extension is not automatic to avoid ambiguities.

7.2. OPERATORS operator symbol type assoc. negation not bool right unary minus numerical right unary plus + numerical right power ** numerical right multiplication * numerical left division / numerical left modulo mod unsigned left addition + numerical left subtraction numerical left left shift << bitvector none signed left shift <<< bitvector none right shift >> bitvector none signed right shift >>> bitvector none less than < numerical none less than or equal to <= numerical none greater than > numerical none greater than or equal to >= numerical none equality = basic, bitvector (non-array) none unequality <> basic, bitvector (non-array) none exclusive or xor bool left conjunction and bool left disjunction or bool left implication => bool right equivalence <=> bool left exclusion # bool truly n-ary Figure 7.1: Data operators, ordered by decreasing precedence.

89

The operators and their precedence table are presented in Figure 7.1. In this gure, left means left-associative, right means right-associative, none means non-associative (i.e. one cannot write X <= Y <= Z), and truly n-ary means that the operator has no associative character and takes all arguments as a single list, as for X # Y # Z. An integral type is dened as a signed or unsigned type, and a numerical type is an integral type, float, or double. When used on mixed types, integral operators promote unsigned to signed, and numerical operators promote unsigned to signed to oat to double. The operator <=> is another way of writing Boolean equality =, often more conventional and more readable. .

7.2.1

Applying operators to arrays

All operators are extended to arrays in a pointwise way, provided they are enclosed in square brackets as for [+]. Extension is not fully implicit to avoid ambiguities. The argument array dimensions must match, and the resulting array has the same dimension.

90 function bin2u binsize extends gray2u lcat mcat mux onehot2u reverse sextends u2bin u2bool u2gray u2onehot

CHAPTER 7. EXPRESSIONS type (bitvector) : unsigned (bitvector, unsigned) : unsigned (unsigned) : unsigned (bitvector) : bitvector (bitvector) : unsigned (bitvector, unsigned) : unsigned (bitvector, . . . , bitvector) : bitvector (bitvector, . . . , bitvector) : bitvector (bool, T, T ) : T (bitvector) : unsigned (bitvector, unsigned) : unsigned (bitvector) : bitvector (bitvector) : bitvector (unsigned) : bitvector (unsigned, unsigned) : bitvector (bool) : unsigned2 (unsigned) : bitvector (unsigned, unsigned) : bitvector (unsigned) : bitvector (unsigned, unsigned) : bitvector

Figure 7.2: PredenedFunctions. Its base type is given by that of the operator when applied to single arguments of array argument base types. Here is a way to add two arrays and to check whether each position is positive, returning an array of Booleans:
( X [+] Y ) [ >] 0

7.3

Function calls

Function calls are performed in the usual way by passing comma-separated arguments to the function. Figure 7.2 gives the list of predened functions. The mux(c, x, y) multiplexor function takes a rst Boolean argument and is type-generic in its second and third arguments. It returns x if c is true, and y if c is false.

7.4

Array indexation

Array indexation or slicing cannot be applied to arbitrary expressions. Here are the cases where they are meaningful: Constant indexation or slicing, for instance C[i] where C is a constant of array type. Variable indexation for a variable of array type, for instance V[1..3][j]].

7.4. ARRAY INDEXATION Signal status indexation for a signal array, for instance S[..][1][2..3].

91

Signal value indexation for a signal array or for a signal or signal array of array type, for instance ?S[2][1]. Remember that signal and value dimensions are concatenated for signal arrays, see Section 5.7. Encoding expressions, as for u2bin(u)[m], u2onehot(u)[2..5], or s2bin(x)[k]. For all other cases, one must use an auxiliary variable.

92

CHAPTER 7. EXPRESSIONS

Chapter 8

Statements
In this chapter, we presents the executable statements that dene the behavior of Esterel v7 programs.

8.1

Statement overview

Without redening it here, we recall the basic Esterel statements behavior which is formally dened in [5]. Statements have two inter-dependent roles: propagating the control and emitting signals. As far as control is concerned, a statement which is executed in an instant has one of three exclusive elementary behaviors: Termination: the statement completes its execution and releases the control. Pausing: the statement holds the control at some point for the next instant, and it restarts from that point at next instant, unless killed or aborted. Trap exit: the statement executes an exit T statement which provokes immediate termination of the corresponding trap statement, killing all concurrent threads in the trap scope. Basic control propagation statements perform elementary control actions, see Section 8.2. Assignment and procedure call deal with variables, see Section 8.3. Signal status and value emission is performed by the emit and sustain statements, which can also check for run-time assertions, see Section 8.4. Sequencing and looping provide basic control propagation, see Section 8.5 and Section 8.6. Control is switched according to Boolean tests by if statements, see Section 8.7. The parallel statements || and for-dopar fork the control into synchronous threads, see Section 8.8. At each instant, they terminate when all the threads are terminated or pause if at least one thread pauses, except if one of the threads exits a trap, in which case the outermost exit is propagated. Temporal statements wait for delays to start, suspend, or kill other statements, see Section 8.10. The trap statement provides structured forward gotos, useful to exit from loops and parallel statements and for many other purposes, see Section 8.11. The finalize statements allows cleanup actions when a statement is terminated or killed, see Section 8.13. 93

94

CHAPTER 8. STATEMENTS

Finally, local signal declarations provide signal scoping, see Section 8.14, open port declarations ease the use of ports, see Section 8.15, and local variable declarations provide variable scoping, see Section 8.16. The language is fully orthogonal: all statements can be freely mixed and in an arbitrary way. One can sequence parallel statements or put sequences in parallel, one can subject any statement to an abortion, etc. There is no limit to statement nesting.

8.1.1

Syntactic matters

All statements except sequencing ; and concurrency || use bracketing keywords: one writes abort ... end abort, etc. Repetition of the initial keyword is optional, so that abort ... end is also correct. To resolve the remaining syntactic ambiguities, any statement can be explicitly bracketed using curly brackets {...}. In the sequel, we do not increment indentation for the parallel bars in parallel statements. Therefore,
signal S in p || q end s i g n a l

is preferred to the more indented form


signal S in p || q end s i g n a l

8.2

Basic control statements

There are three basic pure control statements:


nothing pause halt

Their behavior is as follows: The nothing statement terminates instantaneously when started. The pause statement pauses for one instant. More precisely, it pauses when started, and it terminates in the next instant The halt statement pauses forever and never terminates. Beware, any code that follows a halt statement in sequence will be dead code. Notice that halt is equivalent to loop pause end, see Section 8.6. Also, pause can be written await tick, see Section 8.10.1.

8.3. VARIABLE HANDLING STATEMENTS

95

8.3
8.3.1

Variable handling statements


Assignment

An assignment lhs := e assigns the value of a data expression e to a left-hand side variable cell lhs. For a simple assignment, the left-hand-side is a simple cell designator, i.e. either a non-array variable V or a fully indexed array variable such as A[N+1][?S]. For an array assignment, the lhs can be a full array A or a slice such as A[1..2] or B[..][1..4][2..5]. Then e must be an array expression of matching type and dimension, see Section 7.2.1. Array assignment is performed cell by cell for the given variable indices. Elementary and array assignments are instantaneous. Here are examples:
V := V +1 A [3][3] := 3.14 f A [1..2][2..5] := B [1..2][2..5] [ xor ] X [3..4][8..11]

8.3.2

Procedure call

Procedure calls have the form


c a l l P (e1 , e2 ,. . ., en )

where P is a procedure name and the ei are expressions. The arity and expressions types must match those of the declaration, see Section 3.4. The expression constraints are as follows, distinguishing between simple non-array arguments and array arguments: For in simple arguments, the expressions are arbitrary provided their types match, and they are passed by value. For out or inout simple arguments, the expression must refer to a simple variable cell, as for the left-hand side of a simple assignment, see Section 8.3.1. The cell is passed by reference. For in array arguments, the expression must be a single array identier and the array is passed by value (warning: this can be expensive; however, an implementation can use passing by reference if it is guaranteed equivalent). One cannot pass slices nor partially indexed arrays (because it would be too dicult to generate the code for many host languages). For out or inout array arguments, the expression must be a simple array identier. The array is then passed by reference. One cannot pass slices nor partially indexed arrays. Consider the following procedure declaration:
procedure P ( i n unsigned , inout T ; i n T [5] , out T [5]);

Here is a legal call with X, Y, and Y arrays of base type T:


c a l l P (? I [2] + 1 , X [1] , Y, Z ); // // // // expression for in argument variable cell for simple inout array variable for in array array variable for out array

96

CHAPTER 8. STATEMENTS

8.4

Emit, sustain, and assert

Instantaneous signal emission is realized by the emit statement, while sustained emission is realized by the sustain statement. The emit statement immediately terminates, while the sustain statement never terminates and keeps emitting until preempted. Besides the initial keyword, emit and sustain share the same syntax. The emit and sustain statements can also dene assertions, which represent a standard way of stating properties of programs or hypotheses about the environment one wants to rely on.

8.4.1

Emission overview

The body of an emit or sustain statement is composed of simple emissions, which are equations dealing with the emission of one signal, simple emissions with case lists, where the equation right-hand-side can be decomposed into a case list, concurrent emissions, which group simple emissions in parallel, possibly within for-loops, and conditional emissions which provide the user with conditional if-then-else and if-case structures. Concurrent and conditional constructs can be freely merged to any depth. Adding the seq keyword after emit or sustain makes the emission sequenced: the simple emissions are taken in order instead of concurrently. This makes it possible to dene combinational carry structures.

8.4.2

Simple emission

A simple emission has one of the following four forms:


emit emit emit emit lhs lhs i f t lhs <= rhs lhs <= rhs i f t

A sustained emission has exactly the same form, with sustain replacing emit. In an emission, lhs is the left-hand-side, rhs is the optional right-hand-side, and t is the optional test. The test is always a simple Boolean expression (not an array expression). Signal emission or assertion is said to be conditional if there is a rhs or an if test, unconditional otherwise. There are three kinds of simple lhs : pure emission, valued emission, and assertion. For pure emission, lhs is a status or next status designator such as S, next R[1], P.S, or next Q[2].R[3][?I] where P and Q are ports and R is registered. The next keyword must be used if and only if the signal or port component is registered. There are two pure emission subkinds: For pure simple emission, lhs is fully indexed to designate a terminal non-array status component, and rhs is a Boolean expression. For pure array emission, lhs designates a signal array or slice, and rhs is a Boolean array expression. For valued emission, lhs is a value or next value designator such as ?S, next ?R[1], ?P.S, or next ?Q[2].R[3][?I] where P Q are ports and R is registered. As for

8.4. EMIT, SUSTAIN, AND ASSERT

97

status, the next keyword must be used if and only if the signal or port component is registered. The indices can correspond either to signal dimensions or to value type dimensions. There are two valued emission subkinds: For valued simple emission, lhs designates a simple non-array value cell, and rhs must be a data expression of the same type. For valued array emission, lhs is sliced or not fully indexed, and rhs must be a data array expression of the same type. For assertion, lhs has the form assert A = and it denes an assertion A to be either assumed true or checked true at run-time or at verication time, depending on the need and the programming environment. The assertion name has scope the module in which it is dened. No two assertions in the same module can have the same name. Assertion expressions being sequential, an assertion can express a temporal property of the program. If it exists, the test t is executed rst and the emission is abandoned if the test returns false. For a pure signal, the signal or signal component is emitted if the Boolean rhs evaluates to true. For a valued signal, the signal is emitted with value the result of the evaluation of rhs. We details the dierence cases in the sequel: signal emission from Section 8.4.3 to Section 8.4.6, and assertions in Section 8.4.7. Grouping assertions and signal emissions is very convenient in practice, since assertions are most often about the status or values of signals.

8.4.3

Pure simple emission

A pure simple emission emits or sustains a pure signal. The lhs is a simple status designator, and the rhs is a simple Boolean expression. Here are examples:
emit S s u s t a i n next A [1] emit A [? X ][0] <= I and not J s u s t a i n P . X <= I and (? I > 0) emit P [0]. Y [0] i f not I emit next S <= (? I > 0) if I s u s t a i n S <= pre ( I ) i f ( X = Y ) // // // // // // pure simple signal pure array full indexation pure array full indexation pure simple port component indexed component of indexed port array

If there is no rhs and no test, the signal is emitted. If there is a rhs or a test clause but not both, the rsh or t predicate is evaluated and the signal is emitted only if the predicate is true. If there is both a rhs and a test, then the if t test is evaluated rst. If t is false, the signal is not emitted; otherwise, the rhs expression is evaluated and the signal is emitted if rhs evaluates to true. Notice that the rhs and t predicate are equivalent if they do not appear together. This will not be true any more for array and valued emission. For a sustain statement, evaluation is done in the same way at each tick, with fresh recomputation of rhs and t. Notice that one can use an if clause to protect the evaluation of e from run-time errors. For instance, the following expression provokes a runtime error if X is absent and yet uninitialized:

98
emit S <= X and (? X > 0)

CHAPTER 8. STATEMENTS

The subexpression ?X is evaluated to evaluate rhs, which provokes the error. The following protected statement never provokes an error:;
emit S <= (? X > 0) i f X

8.4.4

Pure array emission

A pure array emission emits or sustains a pure signal status array or array slice. The lhs is a status array designator, and the rhs is a Boolean array expression of the same dimension. Emission is performed as for a pure simple emission for each terminal array component, with matching lhs and rhs indices. Here are examples with A[M][N] a bidimensional signal array:
emit A // full array emission emit A i f (? I > 0) // full array conditional emission s u s t a i n next A [1] // slice emission emit A [..][3..5] <= X [1.. M +1] [and] Y // slicing , array expression sustain P.A // port array component emit P [0..2]. A [3..5] i f not I // complex slice emission

As we shall see in Section 8.4.9, a pure array emission can always be written as a pure simple emission enclosed in explicit fordopar loops, see Section 8.8.2. For instance, the fourth emission above is equivalent to
f o r i < M dopar f o r j < 3 dopar emit A [ i ][ j +3] <= X [ i +1][ j ] [and] Y [ i ][ j ] end f o r end f o r

Or also, as we shall see in Section 8.4.9, to


emit { f o r i < M do f o r j < 3 do A [ i ][ j +3] <= X [ i +1][ j ] [and] Y [ i ][ j ] end f o r end f o r }

8.4.5

Valued simple emission

A valued simple emission emits a simple non-array value. The lhs is a simple signal value designator, the rhs is a data expression of the same type as the signal that denes the emitted value. Here are examples of valued signal emissions:
output X : f l o a t ; output Y [1] : unsigned <256 >; output Z : bool [8]; emit ? X <= 3.14 f s u s t a i n ? Y [1] = 5 i f I emit ? P [3]. B [2] <= true i f (? I > 0)

8.4. EMIT, SUSTAIN, AND ASSERT A simple value emission is performed as follows: If there is no if clause, rhs is evaluated and the signal is emitted with its value.

99

If there is an if t test, the expression t is evaluated and there are two subcases: If t is true, then rhs is evaluated and the signal is emitted with its value. If t is false, then rhs is not evaluated and the signal is not emitted. Notice that this protects rhs from generating run-time errors. Emitting a full signal also sets its status present (for next instant if the signal is registered). For a single valued signal, if an emit or sustain statement is executed, it must be the only one in the instant; for a single or inputoutput single signal, no emit or sustain statement can be executed if the signal is received in the input event. For a combined signal, the emitted value is combined with those emitted by other emit or sustain statements executed in the instant using the signals combination function. For a submodule input or inputoutput combined signal that is received from the caller module and locally emitted in the same instant, the received and emitted values are combined.

8.4.6

Valued array emission

A valued array emission emits a signal value array or array slice. The lhs is a signal array value designator, and the rhs is a data expression of the same type as the signal that denes the emitted value. Here are examples of valued signal emissions:
input I : bool [3][4]; input J [2][3] : bool output O [3] : bool [4]; emit emit ? O [1] <= xA emit ? O [1..2][2..4] = J [and] ? J

Notice that the expression J [and] ?J returns an array of Booleans which are true if the corresponding component of J is present with value true.

8.4.7

Assertions

Assertions are characterized by the assert keyword that starts the left-hand-side. The general syntax is
a s s e r t Ident = e a s s e r t Ident = e i f t

where e, and t are Boolean expressions. For instance:


emit a s s e r t Exclusive = A [0] # A [1] s u s t a i n a s s e r t PositiveIfPresent = ? X > 0 i f X

The scope of the assertion name is the module. An assertion can succeed or fail. When evaluated, an assertion evaluates the test t if it exists. If t is false, the assertion succeeds. Otherwise, the expression e is evaluated and the assertion succeeds if the result is true and fails if the result is false. Therefore, an assertion is like a pure signal that must be present whenever assert is executed.

100

CHAPTER 8. STATEMENTS

One can abbreviate emit assert into assert. An emit assert statement is evaluated once in the instant, while a sustain assert is evaluated at each instant. Beware of a potential pitfall: because of this convention, assert alone is not sustained. Consider the following program fragment:
s i g n a l On , Off i n ... || a s s e r t OnOffExclusive = On # Off end s i g n a l

Here, the assertion checks a very common property, exlusivity of On and Off signals. However, since assert is emit assert, the assertion is tested only once when the signal declaration is entered. To check it as long as the On and Off signals are alive, one must write sustain assert. Note: having assert as a synonym for sustain assert would lead to another pitfall: assert would never terminate, which is obviously also a bad default. Maybe we should only allow the explicit forms.

8.4.8

Simple emissions with case lists in rhs

If is often useful to extend the right-hand-side into a case list. For any type of simple emission, this can be done using the | case separator:
emit { S <= A i f | B if | C, ? T <= ? A | ?B } X Y if A if B

To avoid syntactic conicts with if statements, curly brakets are mandatory after emit. An if clause is mandatory for each case except the last one. The cases are examined in sequence, and the rst true case determines the computed rhs.

8.4.9

Concurrent emission

A concurrent emission statement puts in parallel comma-separeted signal emissions and assertions in a block enclosed within curly brackets { }. Emissions and assertions can also be replicated by a fordo or fordopar statement with explicit indices declared as i < e as i in [e1 ..e2 ] where e, e1 , and e2 are statically evaluable expressions with e1 < e2 . Here are examples:
emit { S, ? X <= ? I [ i ][ j ] , f o r i < N dopar f o r j i n [5..7] dopar Y [ i ][ j ] <= (? A [ i ] + ? B [2* j ]) i f ( A [ i ] and (? B [2* j ] > 0)) , Z [ i ][ j ] <= Y [ i ][ j ] and not pre ( Y [ i ][ j -1]) , a s s e r t YZ_OK = Y [ i ][ j ] = > X [ i ][ j ] end for ,

8.4. EMIT, SUSTAIN, AND ASSERT


next ? Z [1..2][3] <= {2{0}} i f } (? I > 0) ,

101

In the rst emit above, notice that the assertion is replicated by the for loop. Here is a way to swap the values of X and Y from previous tick:
sustain { X <= pre ( Y ) , Y <= pre ( X ) }

The individual emissions are all performed concurrently. There is no order between them, and they can be shued arbitrarily. The following emissions are equivalent
emit { X <= Y , Z <= X } emit { Z <= X , X <= Y }

The result is determined by concurrent evaluation controlled by lhs to rhs and test dependency, not by equation ordering. To simplify long list handling, a trailing comma is permitted but not mandatory after the last emission in a concurrent emission statement or in a dopar emission list. One can factor out the next keyword if all equations are for registered signals. For instance, one can write
emit next { R1 <= I , R2 <= J i f K }

instead of
emit { next R1 <= I , next R2 <= J i f K }

8.4.10

Conditional emissions

Emissions groups can be made globally conditional by using a prex if condition, which is similar to the if-then-else and if-case conditions presented in Section 8.7. This construct makes it possible to factor out conditions for several simple emissions. There are two main forms. The rst form uses then and else clauses, each being optional:
emit { i f S then X <= A , ? Y <= ? A +1

102
else X <= B end i f }

CHAPTER 8. STATEMENTS

emit { i f S then X <= A , ? Y <= ? A +1 end i f }

emit { i f S else X <= B end i f }

If the emit body is composed of a single if clause, the curly brackets {} can be omitted. The second form uses case-do clauses, with an optional default clause:
emit { if case S do X <= A , ? Y <= ? A +1 case T do X <= B d e f a u l t do X <= C end i f }

emit { if case S do X <= A , ? Y <= ? A +1 case T do X <= B end i f }

The cases are taken sequentially in order, the emissions corresponding to the rst true case being the only ones executed. For both forms, the body of a then, else, or do clause can be an arbirary emission body. Therefore, if constructs can be nested to any depth:

8.4. EMIT, SUSTAIN, AND ASSERT


emit { i f S then f o r i < N do if case T [ i ] do X [ i ] <= A , ? Y [ i ] <= i case U do ? Y [ i ] <= i +1 end i f end f o r end i f }

103

8.4.11

Sequenced emission emit seq

Consider the following module:


module OrCarry : g e n e r i c constant M : unsigned ; input I [ M ]; output O [ M ]; emit { O [0] <= I [0] , f o r i < M -1 do O [ i +1] <= O [ i ] or I [ I +1] end f o r } end module

Here, O behaves as an or-carry chain: the output O[i] is present if I[j] is present for some j i. Generating C or HDL code for such a carry chain is easy provided one accepts to expand up to bit-level, i.e. to compute the for loop entirely at compile-time. Clearly, more clever compilers should not perform bit-level expansion that easily blows up, but they should be capable of generating host language for loops. For this, Esterel v7 provides its user with emit seq and sustain seq sequenced emission constructs. With emit seq, one can write
emit seq { O [0] <= I [0] , f o r i < M -1 doup O [ i +1] <= O [ i ] or I [ I +1] end f o r }

In for loops, one must replace do by either doup or dodown. The idea is that the equations are evaluated one by one in the given order instead of being concurrent, and that the for loop indices increase or decrease as indicated by the doup or dodown keyword. One can dene mutually iterative equations as follows:

104
emit seq { X [0] <= I [0] , Y [0] <= J [0] , f o r i < N -1 doup X [ i +1] <= I [ i +1] or Y [ i ] , Y [ i +1] <= J [ i +1] and X [ i +1] end f o r }

CHAPTER 8. STATEMENTS

Notice that X[i+1] depends on Y[i] only while Y[i+1] depends on X[i+1]: the X equation is computed before the Y equation for each loop iteration. The following form would not work:
emit seq { X [0] <= I [0] , Y [0] <= J [0] , f o r i < N -1 doup X [ i +1] <= I [ i +1] or Y [ i ] , end f o r f o r i < N -1 doup Y [ i +1] <= J [ i +1] and X [ i +1] end f o r }

Sequenced emission is also available for valued signals:


module IterativeSum : g e n e r i c constant M : unsigned ; input I : unsigned [ M ]; output O : unsigned [ M ] emit seq { ? O [0] <= ? I [0] , f o r i < M -1 do ? O [ i +1] <= ? O [ i ] + ? I [ I +1] end f o r } end module

We impose the following restrictions on emit seq: Only array elements can be emitted. Input signals cannot be emitted. If an array is emitted in an emit seq statement, it cannot be emitted elsewhere; this either directly by another emit statement, indirectly by a submodule connection, see Chapter 9, or indirectly by reincarnating the emit seq statement,because that would amount to having two emit seq statements, see Section 8.14.3. Semantically speaking, seq, doup, and dodown are irrelevant. They can be seen as implementation pragmas embodied in the language as rst-class citizen since they are very useful. The semantics should be exactly the same as with a normal emit statement, and it is the compiler responsibility to check that the given order is indeed compatible with host language possibilities.

8.4. EMIT, SUSTAIN, AND ASSERT

105

8.4.12

Which case or concurrent structure to choose?

The rich syntax available in emit and sustain bodies makes it possible to write the same thing in dierent ways. These ways actually correspond to dierent styles and tradeos. Prex vs. postx if conditions Prex and postx conditions have the same eect. It is identical to write
emit { i f S then X <= A , Y <= B end i f }

or
emit { X <= A i f S , Y <= B i f S }

The rst form may be preferred because the condition is duplicated in the second form while factored out in the rst form. Rhs cases w.r.t. if-based cases For the emission of one signal, case lists can be written in two ways. Here is an example
emit S <= A i f X | B if Y | C

emit { if case X do S <= A case Y do S <= B default S <= C end i f }

In this case, the rst form is obviously lighter. Assume now that some more complex conditions drive two signals:
emit { S <= A i f X and ? X >0 | B if Y | C ? T <= ? B i f X and ? X >0 | ? C +1 i f Y | 3 }

106

CHAPTER 8. STATEMENTS

Then, factoring out the condition can make code better and especially more maintainable since things are written only once:
emit { if case X and ? X >0 do S <= A , ? T <= case Y do S <= B , ? Y <= ? C +1 default S <= C , ? T <= 3 end i f }

Which way to choose really depend on the application and user. We think one should try to minimize copy-paste of expressions and conditions, and there is no optimal way to do that. Case lists vs. concurrent ifs Case lists as above are very readable, but they can lead to inecient implementation if the cases are always disjoint for reasons extraneous to the statement itself. For example, assume that the signals A, B, and C are always exclusive due to global program behavior. A case-based emission is written as follows:
emit { if case A do < emA > case B do < emB > case C do < emC > end i f }

where < emA >, < emB >, and < emC > are arbitrarily complex emissions. The following parallel emission can be more ecient:
emit if if if } { A then < emA > end, B then < emB > end, C then < emC > end

Of course, the above case-based and parallel emissions are equivalent only if A, B, and C are exclusive. This can be checked by adding assert Excl = A # B # C in the emit statement body. Notice that there is no need to dene a specic parallel case statement: the combination of parallel emissions using , and of if-based tests does the job. Conditional emissions w.r.t. emissions in conditionals There is a subtle but important dierence between placing conditionals within emissions or emissions within conditionals. Consider the following statement:

8.5. SEQUENCING
emit { i f S then X <= A , Y <= B end i f }; p

107

Here, the if test occurs within the emit statement, whose execution is unconditional. Therefore, execution of p is also viewed as unconditional, and it does not depend on testing S. Using a conditional statement, the code would be written as follows:
i f S then emit { X <= A , Y <= B } end i f ; p

In that case, the execution of p is dependent on the test for S, which introduces extra dependencies. In particular, if p is emit S, then the second form generates a causality cycle while the rst form is innocuous (see [5] for an in-depth analysis of dependency issues). Therefore, using an if condition enclosed within an emit statement is much more lightweight than using a full conditional statement with emit in branches. The use of true conditional statemens should be restricted to real branching in the control ow. Concurrent sustain vs. sustain sequences Remember that sustain statements never terminate. A classical mistake is to write:
s u s t a i n X <= A and B ; s u s t a i n Y <= pre ( I or J )

Since the rst sustain never terminates, the second one is never started and is dead code. The right statement is a concurrent emission:
sustain { X <= A and B , Y <= pre ( I or J ) }

8.5

Sequencing

Sequencing is performed by the ; sequence operator:


p; q

the rst statement p is instantaneously started when the sequence is started, and it is executed up to completion or trap exit. If p terminates, q is immediately started and the sequence behaves as q from then on. If p exits some enclosing traps, the exits are immediately propagated and q is dead code since never started, see Section 8.11. For instance, exit T; emit S does not emit S.

108

CHAPTER 8. STATEMENTS

8.6
8.6.1

Looping
Basic loops

A basic loop has the following form:


loop p end loop

The body p is instantaneously restarted afresh upon termination, this forever. If p exits some enclosing traps, the exit is propagated instantaneously and the loop stops. This is the only way to exit a loop from inside. Of course, a loop can also be killed by an external preemption statement, see Section 8.10.2 and Section 8.11. Here is a way to achieve C-like break and continue statements for a loop:
trap Break i n loop trap Continue i n ... e x i t Continue ... e x i t Break ... end trap end loop end trap

This is slightly heavy, but has the advantage that break and continue positions of nested loops can be explicitely named. One always knows which level a break or continue refers to, and, for nested loops, one can break the outermost loop with a single exit.

8.6.2

Instantaneous loops

The body of a loop is not allowed to be able to terminate instantaneously when started. In full generality, this condition can be tested in very precise ways if an implementation has clever ways to detect false paths. Consider for instance the following loop:
loop i f I then i f not J then p end i f else q end i f ; end loop

where p and q are non-instantaneous statements. There is a potential instantaneous path in the loop body corresponding to the case where I and J are both present. However, if I and J are inputs declared incompatible by the input relation I # J, the instantaneous path is a false one since it cannot be taken in any valid input conguration. The same holds if I and J cannot be simultaneous for any kind of dynamic reasons. However, since programs can involve arbitrary data, simultaneity of I and J is undecidable in general.

8.6. LOOPING

109

Implementations can be more stringent and reject programs with static potential false paths such as the one above. In this case, we say that they reject statically instantaneous loops. There is always a simple way to make a loop body non-instantaneous without altering its semantics: adding a pause statement in parallel. Here is the transformation of the previous example:
loop i f I then i f ( not J ) then p end i f else q end i f || pause end loop

8.6.3

Always loops

An always loop is used to transform an instantaneous statement into a permanent one. It is written as follows:
always p end always

The semantics is simply that of loop p each tick, but the statement is much more readable (and familiar to HDL users). Notice that sustain is simply always emit. but again more readable. Note: We used to recommend the always statement in conjunction with decision trees to factor out conditions in emission statements:
always i f I and emit { X <= Y <= } else emit { X <= Y <= end i f end always not pre ( J ) then A or B , A and B

pre ( X ) , A

This form is now subsumed by the even clearer use of if-then-else within sustain statements, see Section 8.4.10:

110
sustain i f I and emit { X <= Y <= } else emit { X <= Y <= end i f }

CHAPTER 8. STATEMENTS

not pre ( J ) then A or B , A and B

pre ( X ) , A

8.6.4

Repeat loops

A repeat loop executes its body for a nite number of times. The body is not allowed to terminate instantaneously, static or dynamic detection of this property being as in Section 8.6. Simple repeat loop The simplest form of repeat loop is:
repeat e times p end repeat

The data expression e must be of unsigned type or must be a positive signed constant, then automatically converted into unsigned. It is evaluated only once at starting time, yielding a result m. The body is then executed m times. It is not executed if m = 0. Repeat counter One can also declare a counter in a repeat loop.:
repeat i := e times p end repeat

he counter goes down from e to 0, performing e iterations. As for simple repeat, e is computed only once, when the repeat statement is entered. If e is a statically evaluable constant with value m, the type of the counter variable is unsigned<m + 1>. Otherwise, the type of the counter is the type of the expression. The counter value can be read within the loop body, but the counter cannot be assigned to: it is read-only. Positive repeat Although its body cannot terminate instantaneously, a repeat statement is considered as possibly statically instantaneous since it can be executed 0 times. Therefore, it cannot be put itself in a loop if not preceded or followed by a delay, this even if its own body is non-instantaneous. For example, the following statement can be rejected as a potentially instantaneous loop, independently of the body p:

8.7. THE IF TEST STATEMENT


loop repeat e times p end repeat ; emit O end loop

111

Esterel compilers are not required to perform static analysis and discover that e is always strictly positive, which is undecidable anyway. To solve this problem, the user may assert that the body will be executed at least once by adding the positive keyword:
loop p o s i t i v e repeat e times ... end repeat end loop

loop p o s i t i v e repeat i := e times ... end repeat end loop

In the positive repeat statement, the test for repetition is performed only after the rst execution of the body. The body is not allowed to be able to terminate instantaneously, and the whole positive repeat statement inherits the same property. The body is executed once if e evaluates to 0.

8.7

The if test statement

The if statement branches according to the instantaneous values of a Boolean expression. Each of the then and else branches can be omitted, but at least one of them must be specied. An omitted branch is implicitly nothing:
if if if if if S then p e l s e q end i f Second and Meter then p end S [2] then q end S and (? S >0) e l s e q end i f pre ( P . S [2]) e l s e q end

See Section 8.4.10 for the dierence between the if-then-else statement and if clauses in emit and sustain.

8.7.1

Case tests

The case form tests several signal expressions in sequence:


if case Meter do emit ? Distance <= pre (? Distance ) +1 case Second do emit ? Speed <= ? Distance end i f

112

CHAPTER 8. STATEMENTS

The tests are taken in order, and the rst true expression starts immediately its do clause. If the do clause is omitted, the if statement simply terminates. If none of the expressions is true, the if statement terminates. One can add an default keyword to be executed in that case:
if case ( Op [0] and Op [1]) do emit Load case ( Op [0] and not Op [1]) do emit Store case ( not Op [0] and Op [1]) // no - op d e f a u l t do e x i t WrongOpCode end i f

Notice that at most one do or default clause is executed in an if-case statement. See Section 8.4.10 for the dierence between the if-then-else statement and if clauses in emit and sustain.

8.8

Concurrency

There are two concurrency constructs: the explicit parallel statement || and the replication construct for...dopar.

8.8.1

The parallel operator

The parallel operator || puts statements in synchronous parallel. The signals emitted by any of its branches or by the rest of the program are instantaneously broadcasted to all branches in each instant. A parallel can be binary, as in p || q, ternary, as in p || q || r, or of any arity. Syntactically, the sequencing operator ; binds tighter than the parallel operator ||. Therefore, p; q || r means { p; q } || r, which is dierent from p; {q || r } where the brackets are mandatory. A parallel statement forks its incoming thread when it starts, starting instantaneously one thread per branch. All threads behave synchronously until termination or trap exit. The parallel terminates when all its branches have terminated, waiting for the last one if some branches terminate earlier. The parallel propagates a trap T as soon as one of its branches exits T, weakly aborting all its branches at that time. See Section 8.11 for the case where several traps are simultaneously exited. There are restrictions for sharing variables among parallel branches, see Section 5.8

8.8.2

The for-dopar replication statement

The for-dopar replication statement can be written in one of two forms:


f o r i < e dopar p end f o r

or

8.8. CONCURRENCY
f o r i i n [e1 ..e2 ] dopar p end f o r

113

where i is an integer variable called the iterator and where e, e1 , and e2 are statically evaluable expressions. In the rst form, replication goes from 0 to e 1 included and e is called the replication count. If e statically evaluates to m, The iterator is implicitely declared of type unsigned<m + 1>. In the second form replication goes from e1 to e2 included, these two expressions being called the replication bounds. If e1 and e2 statically evaluate to m1 and m2 , the iterator is of type unsigned<m2 m1 + 1>. In both cases, the value can be read in the body p, but it is read-only and cannot be assigned to. Assume that there are m replications. Then the construct conceptually builds m parallel copies of p, which act concurrently with each other. The copies have independent control, communicate witch other, and need not terminate at the same time. The whole replication construct behaves exactly as this conceptual parallel statement: it terminates when all the copies of p have terminated; it exits a trap if one of its p copies exits a trap; if several copies of p exit dierent traps at the same time, only the outermost one matters, all other ones being discarded. Beware: parallel replication is very dierent from repeat loop. In a loop, the body is repeated in sequence and it is not allowed to be instantaneous. In a dopar statement, the body is instantiated in parallel and there is no timing restriction for it. Instantaneous statements can be freely replicated.

8.8.3

Replication examples

The classical ABO automaton terminates and emits O when it has received A and B, either simultaneously or in succession, initial instant excluded. Its code is as follows:
module ABO : input A , B ; output O ; { await A || await B }; emit O end module

Here is how to put N copies of ABO in parallel, feeding them with elements of input arrays and sending their outputs to an array, with global termination detection:

114

CHAPTER 8. STATEMENTS
module NABO : g e n e r i c constant N : i n t e g e r ; input A [ N ] , B [ N ]; output O [ N ] , Done ; f o r i < N dopar run ABO [ s i g n a l A [ i ] / A , B [ i ] / B , O [ i ] / O ] end f o r ; emit Done end module

Since a dopar statement terminates when all its branch are terminated, the Done signal is output exactly when all the inputs A[i] and B[i] have been received. Here is an amazing way of computing the N ! factorial for any integer N using a dopar and a combined signal:
module SynchronousFactorial : constant N : i n t e g e r ; output Fact : unsigned combine *; f o r i < N dopar emit Fact ( i +1); end f o r end module

The concurrent emissions of Fact(i+1) are combined by multiplication, yielding the factorial value N ! = 1 2 . . . N for the Fact signal. To illustrate how branches can synchronize in a fancy wasy, here is a computation of (N !)N in one tick
module S y n c h r o n ou s F a c t o r i a l P o w e r : constant N : i n t e g e r ; output Fact : unsigned combine * , PowFact : unsigned combine *; f o r i < N dopar emit Fact ( i +1); emit PowFact (? Fact ) end f o r end module

Within the instant, the N concurrent emissions of PowFact have to wait for the value of Fact to be entirely computed before being able to act. When ?Fact nally evaluates to !N , still in the instant, each emission of PowFact emits !N , yielding the nal result PowFact= (N !)N by *-combination. The necessary waiting and synchronization mechanism is built-in the language semantics.

8.9

Delays

Delay expressions are used to denote occurrence of future events in temporal statements such as abort, await, every, etc.

8.9.1

Simple delays

A simple delay expression is a Boolean test:

8.9. DELAYS
await X abort p when X and ?X >0 every X and Y [2] do p end

115

The delay expression then elapses at the rst instant in the strict future where the test is true.

8.9.2

Immediate delays

An immediate delay consists of a Boolean test preceded by the immediate keyword:


await immediate X abort p when immediate X and ?X >0 every immediate X and Y [2] do p end

The delay expression then denotes the rst instant where the test is true, current instant included. For instance, in the above examples, the await statement immediately terminates if X is present, the body of the abort statement is not started and the abort immediately terminates if X is present with a positive value, and the every statement immediately starts its body if its condition is true.

8.9.3

Count delays

A count delay consists of an integer expression called the count expression, and a simple delay. There are two possible syntactif forms: The count is an arbitrary unsigned expression or a positive constant signed expression. It is followed by the times keyword and a Boolean test. The count is an unsigned constant, a simple identier denoting an unsigned variable or constant, or a signed positive constant or a parenthesized expression of unsigned type. It is immediately followed by the simple delay which must be a single identier, possibly indexed. The second form makes it possible to forget about the times keyword in simple cases. Here are examples:
await await await await every 3 Second ( f (? I )+1) Second 3 times Second 3 times Second or Meter f (? I ) times ( X and Y [1]) do ... end

Remember that the timeskeyword is mandatory if the test is not trivial When the statement that bears the count delay starts, the integer expression is evaluated into a value called the count. Every future instant where the test is true, the count is decremented. When the count becomes less than 1, the delay elapses. Beware: this means that an initial count value strictly less than 1 is equivalent to 1. A statement with a count expression always takes time. This can be misleading if the count starts at 0 ! Notice that the count is evaluated only once, whence the delay is initially started, while the test is re-evaluated independently at each instant. Consider for instance
await ? S times S and ?S >0

116

CHAPTER 8. STATEMENTS

When the await statement starts, the count expression ?S is evaluated to dene the count. It will not be re-evaluated during the delay. On the opposite, the expression ?S in the if part of the delay is re-evaluated at each instant. In Section 11.2, we give the exact denition of a delay by macro-expansion into simpler statements.

8.10
8.10.1

Temporal statements
The await statement

The await statement is the simplest temporal statement. In its basic forms
await d await immediate d

it simply waits for a delay. Here are examples:


await await await await await await await await await Second immediate Second immediate ? I > 0 pre ( Second ) immediate pre ( S [2]) and (? S [1] > 0) 2 Second 2 times P [1]. S [1][2] X +1 times (? S > 2) f ( Y ) times ( Second and not pre ( Meter ))

The delay is started when the await statement is started. The statement pauses until the delay elapses and terminates in that instant. An immediate await statement terminates instantaneously if the signal expression is true in the starting instant. Be careful: the sequence
await immediate Meter ; await immediate Meter

terminates instantaneously if Meter is present in the starting instant (this is why making immediate the default would be misleading). A do clause can be used to start another statement when the delay elapses, with the following syntax:
await d do p end await

This is simply an abbreviation for await d ; p which makes the dependency of p on d more explicit. For instance:
await 2 times Second do emit Beep end await

As for if, one can introduce a case list, where do clauses can be omitted:
await case d1 do p1 case d2 ... case dn do pn end await

8.10. TEMPORAL STATEMENTS

117

An absent do clause is equivalent to do nothing. When the rst delay elapses, the corresponding do clause is started and the whole await statement terminates when the clause terminates. If several delays elapse simultaneously, only the rst one in the case order is considered. Consider for instance:
await case 2 Second do p case immediate Meter case Button do q end await

The above statement immediately terminates if Meter occurs at start time. Otherwise, the rst delay to elapse determines the subsequent behavior: p is started if 2 Second elapses rst, the await statement simply terminates if Meter occurs rst, and q is started if Button occurs rst. If Meter and Button occur simultaneously, then the await statement terminates and q is not started since the rst delay takes priority. Unlike for if, no default clause is allowed for an await statement, which is not supposed to terminate right away. One can use case tick to achieve the same eect.

8.10.2

The abort-when statements

An abortion statement kills its body when a delay elapses. For strong abortion, performed by abort, the body does not receive the control at abortion time. For weak abortion, performed by weak abort, the body receives the control for a last time at abortion time. Simple abortion syntax is as follows, where d is a delay:
abort p when d weak abort p when d

For instance:
abort p when abort p when weak abort p weak abort p 2 Meter 3 times Meter or Second when Meter when immediate Meter

For both constructs, the body p is run until termination or until the delay elapses. If p terminates before the delay elapses, so do the abort and weak abort statements. Otherwise, p is preempted when the delay elapses; in that instant, p is not executed with strong abortion, and it is executed for a last time with weak abortion (p has rights to its last wills). If the delay is immediate and elapses immediately at starting time, the body is not executed at all with strong abortion, and it is executed for one instant with weak abortion For instance, in
abort sustain O when immediate I

the abort statement terminates immediately without emitting O if I is present at starting time. If abort is replaced by weak abort, the whole statement also terminates instantaneously but O is emitted once. As for await, one can add a do clause to execute a statement q in case of delay elapsing:

118
[ weak ] abort p when d do q end abort

CHAPTER 8. STATEMENTS

With both weak and strong abortion, q is executed if and only if p did not terminate strictly before delay elapsing. At abortion time, with strong abortion, p is not executed and q is immediately started. With weak abortion, the rst instant of q is done in sequence after the last instant of p. This behavior is dierent from that of
[ weak ] abort p when d; q

where q is executed wheter p terminates or d elapses. As for await, one can introduce an ordered list of abortion cases:
[ weak ] abort p when case d1 do p1 case d2 ... case dn do pn end await

An absent do clause is equivalent to do nothing. Here, p is immediately strongly or weakly aborted if an immediate delay elapses at start time. Otherwise, p is run for at least one instant. The elapsing of any of the delays strongly or weakly aborts p and the corresponding do clause is immediately started; the whole statement terminates when the clause terminates. If more than one of the delays elapses at abortion time, then the rst one in the list takes priority as for the await statement. If p terminates before any of the delays elapses, then no do clause is executed and the whole construct terminates. Here is an example:
abort p when case Alarm do r case 3 Second do q case immediate Meter end abort

Nesting abort statements automatically builds outside-in priorities. In the statement


abort abort p when I do q end abort when J

8.10. TEMPORAL STATEMENTS

119

the signal J takes priority over I if they occur simultaneously, and q is not started in that case. This is no special rule, but just a consequence of the strong abortion semantics of abort. Finally, notice that await S can be dened as abort halt when S.

8.10.3

The abort-after statements

The abort - after statements use the termination of a statement as the abortion condition. It has the following form:
[ weak ] abort p after q end abort

where p and q are arbitrary statements. The statements are started in parallel, and the whole construct terminates when q terminates. In the abort strong form, p is strongly preempted (not executed) when q terminates. In the weak abort weak form, p is executed at the tick in which q terminates. This new statement is particularly useful to dene equations valid during a statement, as in:
weak abort s u s t a i n RisingS = S and nor pre ( S ) after ... await RisingS ; ... await RisingS ; ... end abort

The weak form is equivalent to the following trap construct:


trap T i n p ; halt || q; e x i t T end trap

The strong form is slightly more complex:


trap T i n signal S in abort p; halt when immediate S || q; emit S ; exit T end s i g n a l end trap

120

CHAPTER 8. STATEMENTS

8.10.4

Temporal loops

Temporal loops are loops over strong abortion statements. The rst form is
loop p each d

where d is a non-immediate delay. At start time, the body p is started right away, and it is strongly aborted and immediately restarted afresh whenever the delay d elapses. If p terminates before d elapses, then one waits for the elapsing of d to restart p. The loop-each statement is simply an abbreviation for
loop abort p; h a l t when d end loop

The delay cannot be immediate, otherwise the loop body would be instantaneous. The second temporal loop has the form
every d do p end every

The dierence is that d is initially waited for before starting the body p. The delay d can be immediate. In that case, in the starting instant, p starts immediately if the delay elapses immediately. The statement
every 3 Second do p end every

abbreviates
await 3 Second ; loop p each 3 Second

The statement
every immediate Centimeter do p end

abbreviates
await immediate Centimeter ; loop p each Centimeter

All temporal loops are innite. The only way to terminate them is by exiting a trap, see Section 8.11 or by the elapsing of an enclosing abortion delay.

8.11. TRAPS

121

8.11

Traps

A trap denes an exit point for its body. The basic syntax is
trap T i n p end trap

The scope of T is the body p and scoping is lexical. A redeclaration of a trap hides the outer declaration. The body p is immediately started when the trap statement starts. Its execution continues up to termination or trap exit, which is provoked by executing the exit T statement. If the body terminates, so does the trap statement. If the body exits the trap T, then the trap statement immediately terminates, weakly aborting p. The weak abort statement can be dened using traps. The construct
weak abort p when S

is an abbreviation for
trap T i n p; exit T || await S ; exit T end trap

8.11.1

Nested traps

When traps are nested, priority is inside-out. Consider for example


trap U i n trap T i n p end trap ; q end trap ; r

If p exits T, then q is immediately started. If p exits U, then q is discarded and r is immediately started. If p exits simultaneously T and U, for example by executing exit T || exit U, then U takes priority and only r is executed. From the point of view of the trap T statement, T is discarded and U is propagated.

8.11.2

Trap Handlers

A handler can be used to handle a trap exit, with the following syntax:
trap T i n p handle T do q end trap

122

CHAPTER 8. STATEMENTS

If p terminates, so does the trap statement. If p exits T, then p is weakly aborted and q is immediately started in sequence.

8.11.3

Concurrent Traps

Several traps can be declared using a single trap keyword:


trap T , U , V i n p handle T do q handle U do r end trap

In this case, the traps are called concurrent traps and they must have distinct names. Concurrent traps are at the same priority level, and any of them can have a handler. If several traps are simultaneously exited, then the corresponding handlers are executed in parallel. Here, q and r are executed in parallel if p exits T and U simultaneously. Since they are concurrent, the q and r handlers cannot share variables, see Section 5.8. The trap statement simply terminates if p exits V that has no handler. Here is the translation of weak abort p when S do q end using concurrent traps:
trap Terminate , WeakAbort i n p; e x i t Terminate || await S ; e x i t WeakAbort handle WeakAbort do q end trap

8.11.4

Valued Traps

Traps can be valued. Valued traps are useful to pass a value to the handler. The value is emitted within parentheses, as in exit T(3). The value is read as the result of the expression ??S, which is allowed only in the handler. Combined traps are allowed. Trap value initialization and the expression pre(?S) are not available for traps; they would make no sense since a statement that exits a trap dies instantaneously.
trap Alarm : unsigned combine + i n ... e x i t Alarm (3) ... ... e x i t Alarm (5) ... handle Alarm do emit Report (?? Alarm ) end trap

Concurrent traps can be valued and combined, but their value cannot be initialized:

8.12. SUSPENSION STATEMENTS


trap T , U : i ntege r , V : i n t e g e r combine + i n p handle T do q handle U and V do emit O (?? U + ?? V ) end trap

123

Here, the second handler starts if and only if U and V are exited in parallel, in which case O is emitted with values the sum of the values of U and V. Note for discussion: valued traps are quite complex. Are they really necessary in Esterel v7? Their usage seems to be fairly rare.

8.12

Suspension statements

Abortion or trap exit violently preempts a statement and kills it, in the same way as C kills a process in Unix. Suspension has a milder action, simply freezing the statement for the instant. There are two suspension statements: For suspend p when t, the body p is frozen and does not react when the test t is true. This is similar to Z in Unix for the current instant only. For weak suspend p when t, the body p acts for the instant but its state change is frozen. For hardware, this is the eect of clock-gating ps registers by t. The name weak suspend comes from the similarity with abort and weak abort: at abortion time, the body is not executed with abort while it is executed with weak abort. The weak suspend statement was rst introduced in [13]. It is fundamental for clock-gating and for multiclock simulation, see Chapter 13. Since its behavior is less intuitive than that of suspend, we present it it great details with examples.

8.12.1

The suspend statement

The syntax of suspend is as follows:


suspend p when t

where t is any test (Boolean expression). When the suspend statement starts, its body p is immediately started. If p terminates or exits a trap at that instant, so does the suspend statement. If p pauses, the behavior is chosen as follows at each subsequent instant: If t is true, then p remains in its current state without being executed. No signal is emitted and no state change is performed. The suspend statement pauses for the instant. If t is false, then p is executed for the instant. If p terminates or exits a trap, so does the suspend statement, and suspension is over. If p pauses, so does the suspend statement, and suspension is re-examined in the next instant.

124 For instance, the statement


suspend abort sustain O when J when I

CHAPTER 8. STATEMENTS

emits O in the rst instant and in all subsequent instants where I is absent, until the rst instant where I is absent and J present. Then the suspend statement terminates, with O is not emitted because of the abort statement. The default suspend statement is delayed, in the sense that the signal expression is not tested for in the rst instant. The immediate form performs that test:
suspend p when immediate t

Here p is not started in the rst instant if t is true. The immediate form can be macroexpanded as follows:
await immediate not t; suspend p when t

8.12.2

The weak suspend statement

When the suspension test of a suspend p when t statement is true, the body p is not activated and it keeps its state. With the weak suspend p when t statement, when t is true, the body p is activated in the instant, but no state change is performed for it. In other words, the combinational part acts, but the state is kept unchanged. In hardware, weak suspension has exactly the eect of clock-gating by t the registers generated by p. Of course, other hardware implementations are possible. One can for example use disabling logic to keep the register state unchanged. Implementation is not studied here, and we concentrate on the behavior of the weak suspend statement, using hardware vocabulary since weak suspend is most useful for hardware. As for suspend, there is a delayed and an immediate form. The delayed form is written
weak suspend p when t

where p is any statement and t is a Boolean test. When the weak suspend statement starts, the body p is immediately started. If p terminates instantaneously, so does the whole weak suspend statement. Otherwise, at any further instant, the test t is evaluated and the behavior is selected as follows: If t is false, p is evaluated, and its termination, pausing, or trap exits are propagated to the whole weak suspend statement. The state change of p is propagated if p pauses, otherwise weak suspension is over.

8.12. SUSPENSION STATEMENTS

125

If t is true, p is also evaluated, but its termination, enclosing trap exits, and state change are ignored. The whole weak suspend statement pauses. It will restart from exactly the same state in the next instant, unless preempted by an enclosing preemptive statement in the current instant. Only the exits of the enclosing traps are ignored when the weak suspension test is true. The exits of the traps declared within the body p are performed normally since they are of a purely combinational nature. See Section 8.12.4 for details and examples. For the immediate form
weak suspend p when immediate t

the test t is also performed at rst instant. If t is true at rst instant, p is executed for the instant but its termination, enclosing trap exits, and state change are ignored. At next instant, the immediate weak suspend statement will restart afresh. At the rst instant where t becomes false, p is executed and its termination and state change are propagated. The immediate weak suspend statement behaves as a standard one from then on. The immediate form can be obtained from the standard form by the following macroexpansion:
trap Done i n loop trap SurfaceOnly i n weak suspend p; when t; e x i t Done ; || i f t then e x i t SurfaceOnly end end trap ; pause ; end loop end trap

The loop ensures fresh restart if t is true at rst instant. If t is false at rst instant, the whole statement behaves as the inner weak suspend statement.

8.12.3

Suspension and weak suspension examples

Eect of suspension on control Consider the following example


suspend pause ; // A emit X ; pause ; // B emit Y when Susp ; emit Z

where S is an input and X, Y, Z are outputs. Here is a sequence of reactions to inputs, with - denoting an empty input or output:

126

CHAPTER 8. STATEMENTS

Figure 8.1: control suspension waveforms


1. 2. 3. 4. 5. 6. Susp Susp Susp X Y Z new state new state new state new state new state done A A B B B

This behavior is pictured in timing diagram form in Figure 8.1. There is no output when Susp is present. Let use replace suspend by weak suspend:
weak suspend pause ; // A emit X ; pause ; // B emit Y when Susp ; emit Z

Then, the outputs are generated at each instant since the body keeps being activated:
1. 2. 3. 4. 5. 6. Susp Susp Susp X X Y Y Y Z new state new state new state new state new state done A A B B B

See also the timing diagram form in Figure 8.2.

Consider steps 5 and 6. At step 5, execution resumes from pause labeled B. The emit Y statement is executed, Y is emitted, and the weak suspend body terminates. Since Susp is present, body termination is discarded, the weak suspend statement pauses, and there

8.12. SUSPENSION STATEMENTS

127

Figure 8.2: control weak suspension waveforms is no control state change. Therefore, step 6 also starts from state B. In that step, emit Y is executed again, Y is emitted, and the weak suspend body terminates. This time, since Susp is absent, termination propagates to the whole weak suspend statement, and emit Z is executed.

Eect of suspension on data To understand how suspension ans weak suspension act on data, consider the following example:
module SuspendCount : input Susp ; output Count : temp unsigned ; suspend var C : unsigned := 0 i n loop emit ? Count <= C ; pause ; C := C + 1 end loop end var when Susp end module

The program counts ticks in the following way: At rst instant, the C variable is initialized to 0 and Count is emitted with that value. At any subsequent instant, if Susp is present, nothing happens and control stays paused at the pause statement; if Susp is absent, control resumes from the pause

128

CHAPTER 8. STATEMENTS

Figure 8.3: count suspension waveforms statements. The assignment to C increments its value, the loop is looped, Count is emitted with the incremented C value, and control pauses again on the pause statement. Here is an execution, with _ denoting a blank input or output, also pictured as waveforms in Figure 8.3:

1. 2. 3. 4. 5. 6. 7. 8.

Susp Susp Susp -

Count (0) Count (1) Count (2) Count (3) Count (4)

Replace suspend by weak suspend:


module WeakSuspendCount : input Susp ; output Count : temp unsigned ; weak suspend var C : unsigned := 0 i n loop emit ? Count <= C ; pause ; C := C + 1 end loop end var when Susp end module

The behavior is exactly the same at rst instant or when Susp is absent. It diers when Susp is present after rst instant. Then, control propagates exactly as when Susp is absent, and Count is emitted with the incremented value of C. However, since C is within the scope of the weak suspend statement, the incremented value is not stored in Cs state

8.12. SUSPENSION STATEMENTS

129

Figure 8.4: count weak suspension waveforms memory, and the next execution will resume from the same value. In hardware terms, Cs register is disabled or clock-gated by Susp. Execution is as follows:

1. 2. 3. 4. 5. 6. 7. 8.

Susp Susp Susp -

Count (0) Count (1) Count (2) Count (2) Count (3) Count (3) Count (3) Count (4)

Waveforms are shown in Figure 8.4. Consider step 2: the value of C before the step is 1, set and stored by step 1. Since Susp is absent, C is incremented and its new value 1 is emitted as the value of Count. Consider step 3: control propagates as usual, incrementing C to 2 and emitting Count with this value. However, the new value 2 is not stored in the state but discarded (in hardware terms, the combinational computation results are discarded when the registers are clock-gated). Step 4 will be performed with C = 1. Relative placement of signal and variable declarations and suspension The relative placement of declarations and suspensions is crucial. In our previous example, things would be dierent if Count was declared outside the weak suspend statement:

130
module BadWeakSuspendCount : input Susp ; output Count : temp unsigned ; var C : unsigned := 0 i n weak suspend loop emit ? Count <= C ; pause ; C := C + 1 end loop when Susp end var end module

CHAPTER 8. STATEMENTS

Then, Cs state (hardware register) is not subject to weak suspension (clock gating) and the new value of S is stored at each cycle, yielding the execution
1. 2. 3. 4. 5. 6. 7. 8. Susp Susp Susp Count (0) Count (1) Count (2) Count (3) Count (4) Count (5) Count (7) Count (6)

Since there is no suspended data state and only one control state bit (generated by the every statement), the weak suspend statement has no eect at all here. Notice that there would be no behavior change for the same declaration/ suspension inversion with suspend instead of weak suspend, since the Count incrementation would not be performed either when S is present. The suspend and weak suspend statements are very dierent in this respect. Notice nally that the intended behavior cannot be acheived without declaring an auxiliary variable (or signal) local to the weak suspend body. The following programs also performs a continuous incrementation instead of a suspended incrementation, since the value of O is declared at top-level and not subject to suspension:
module BadWeakSuspendCount : input Susp ; output O : unsigned i n i t 0; weak suspend loop pause ; emit ? O <= pre (? O ) + 1 end loop when Susp end module

Here also, the weak suspend statement is ineective since there is only one control bit.

8.12.4

Interaction with traps

For WeakSuspendCount above, assume that we want to stop the count when value 3 is reached with Susp absent. We can do this using a trap:

8.12. SUSPENSION STATEMENTS


module W e ak S u sp e n d Co u n tW i t h T r a p : input Susp ; output Count : temp unsigned ; output Done ; trap Done i n weak suspend var C : unsigned := 0 i n loop emit ? Count <= C ; i f ? Count = 3 then e x i t Done end i f ; pause ; C := C + 1; end loop end var when Susp end trap ; emit Done end module

131

The behavior is as follows:


1. 2. 3. 4. 5. 6. 7. Susp Susp Susp Count (0) Count (1) Count (2) Count (2) Count (3) Count (3) Count (3) Done

The exit Done statement is reached at step 5, 6, and 7, but its state-change eect is canceled at step 5 and 6 since it is subject to weak suspension. Actual exit is performed only at step 7 where Susp is absent. Behavior would be identical with trap declared inside the weak suspend and Done emitted outside:
module W e a k S u s p en d C o u n t W i t h T r a p 2 : input Susp ; output Count : temp unsigned ; output Done ; weak suspend trap Done i n var C : unsigned := 0 i n loop emit ? Count <= C ; i f ? Count = 3 then e x i t Done end i f ; pause ; C := C + 1; end loop end var end trap when Susp ; emit Done end module

132

CHAPTER 8. STATEMENTS

Here, the trap is also exited at steps 5, 6, and 7, provoking termination of the weak suspend body. At step 5 and 6, this termination is discarded because of weak suspension, and Done is not emitted. Placing exit Done inside the weak suspend body, i.e. in sequence after end trap, and before when Susp would yield a dierent behavior: Done would be emitted at steps 5 and 6 as well since its emit statement would belong to the combinational path internal to the weak suspend statement.

8.13

The nalize statement

The nalize statement has the following form:


finalize p with q end f i n a l i z e

where the statement q is called the nalizer. The nalizer can contain only instantaneous statements: emit, assignment, call, possibly placed in sequence or in parallel. When the nalize statement is started, it immediately starts p. If one of the following event occurs, the nalizer q is immediately executed and the whole nalize statement immediately terminates: p terminates, in which case q is executed in sequence after p; the finalize construct is strongly aborted by an an enclosing abort statement, in which case q is executed but not p (of course, unless the abort statement is immediate and immediately aborted, in which case the finalize statement is not started). the finalize construct is weakly aborted by an enclosing weak abort statement or by a concurrent exit of an enclosing trap, in which case the execution of q follows the last execution of p in the instant. The nalizer is executed only once even if there are several reasons to execute it. Notice that the nalizer is executed even in the body of a strongly aborted statement, while strongly aborted normal statements are not executed. Finalizers allow the user to perform cleanup when a statement terminates or is aborted for any reason, or to broadcast a message as in the following example:
... finalize p with emit IamDead end ...

Consider the following example of a complex nalization context:

8.13. THE FINALIZE STATEMENT


input I , J , K ; output X : i ntege r , Y : integer ; abort // immediate I trap T i n weak abort // J finalize emit ? X <= 1; await L ; emit ? X <= 2 with emit ? Y <= ? X + 1 end when J || await immediate K ; exit T end trap when immediate I

133

Here are some typical nalization cases: Termination: if I, J, and K do not occur, then X(1) is rst emitted, followed at next tick by the simultaneous emissions of X(2) and Y(3) when L occurs. Strong abortion, with two subcases: If I occurs at rst instant, then the finalize statement is not executed at all and the nalizer is not called. If I does not occur at rst instant but occurs before J, K, and L or at the same time as them, then X(1) is emitted at rst instant and Y(2) is emitted by the nalizer when I occurs. Weak abortion by weak abort: if I and K do not occur but J occurs after the rst instant, then X(1) is emitted at rst instant and there are two termination cases when J occurs: if J occurs without L, then Y(2) is emitted. if J and L occur simultaneously, then X(2) and Y(3) are emitted. Weak abortion by trapexit: if I and J do not occur but K occurs at rst instant or before L, then X(1) and Y(2) are emitted; if K and L occur simultaneously after the rst instant, then X(2) and Y(3) are emitted. In all cases, the whole nalize statement terminates. When nested nalization occur, a cascade of nalizers can be executed. They are executed in inside-out order:

134
var X : i n t e g e r := 1 i n abort finalize finalize p with X := X +1 end; X := X *2 with X := X +3 end when I end var

CHAPTER 8. STATEMENTS

If p terminates before I occurs, the innermost nalizer is executed, then the multiplication by 2, then the outermost nalizer, yielding 7. If I occurs before p terminates, the innermost nalizer is executed before the outermost one and the multiplication is bypassed, yielding X=5. Beware: finalize is a dicult statement to compile, and it can generate nasty cycles in cases a nalizer re-triggers itself in some way. Keep nalizers simple!

8.14
8.14.1

Local signal declaration


Local signal and port declaration and renement

In Chapter 5, we have seen the form of a local signal declaration signal decls in p end that declares a list of signals and ports with all attributes allowed for signals. Here is a example with signals only:
signal S, R : reg1 unsigned <[32] > , A [ M ][ N ] : bool [8] i n i t 0 , B [ M ] : temp value Byte i n i t 0 combine + in , extends Intf , port P : Intf ... end s i g n a l

An interface extension such as extends Intf locally declares all signals and ports dened at toplevel in Intf, ignoring their input / output directionalities. Here is an example:
i n t e r f a c e Intf : input I : unsigned ; output O ; end i n t e r f a c e

8.14. LOCAL SIGNAL DECLARATION


module M : ... s i g n a l extends Intf i n emit ? O <= 1; emit I ; ... end s i g n a l end module

135

The extends Intf declaration is equivalent to the declaration sequence


s i g n a l I : unsigned , O i n ... end

Mirroring Intf in the extension is allowed but ineective since directionalities are ignored. A local port declaration such as port P : Intf declares a local port P of interface Intf, where the interface input / output directionalities are ignored. Here, two signals P.I and P.O are declared. Mirroring Intf is accepted but has no eect. The port can be opened using the open statement of Section 8.15. For local extension and port declaration, since only interface attributes are declared in the interface, one can add refine declarations to specify the remaining module attributes needed for interface or port components. Here is an example, with Intf as in the previous example:
module M : ... signal S, extends Intf , r e f i n e O : reg , port P : Intf , r e f i n e P . I : reg i n i t 0 combine + , // reg OK since I not input any more r e f i n e P . O : reg1 , in ... end s i g n a l end module

Notice that I has been rened reg, which would not be allowed in a module header since I is declared input in the interface. In the signal context, the input declaration is ignored.

8.14.2

Local signals and suspension

Suspension interacts with taking the pre operators for a signal declared within the suspension body, as in
suspend signal S in ... i f pre ( S ) then ... ... end s i g n a l when I

The expression pre(S) refers to the status of S in the previous instant where the signal declaration statement was activated, not to the status of S in the previous absolute instant

136

CHAPTER 8. STATEMENTS

or tick. Instants where I suspends the signal statement do not count for pre. In some sense, the suspend statement steals the tick from its body. This is obvious when expanding the pre operators as explained in Section 11.1.1:
suspend s i g n a l S , preS i n loop i f S then pause ; emit preS else pause end i f end loop || ... i f ( preS ) then ... end ... end s i g n a l when I

The pause statements used to compute preS are indeed suspended by I. On the opposite, the absolute pre is taken if the signal is declared outside the suspend statement:
signal S in suspend ... pre ( S ) ... when I end s i g n a l

Here the expression pre(S) in the suspend body refers to the previous instant of the signal statement, independently of the presence or absence of I.

8.14.3

Local signal reincarnation

Because of instantaneous looping of loops, local signals can have several simultaneous instances that we call reincarnations. They pose no particular problem, but one has to be aware of their existence, in particular to understand causality issues. Here is an example:
loop signal S in i f S then emit O1 e l s e emit O2 end; pause ; emit S end s i g n a l end loop

In the rst instant, the local signal S is declared. It is absent since there is no emitter for it. Therefore, the else branch of the if statement is taken and O2 is emitted. In the second instant, the pause statement terminates and S is emitted and set present. The loop body terminates and it is rest arted afresh right away. The local signal declaration is immediately re-entered. It declares a fresh incarnation of S, distinct from the old one, whose status is lost since the declaration has been exited. The fresh incarnation is absent,

8.15. OPEN PORT DECLARATION

137

unlike the old one. The if statement tests the fresh incarnation and only O2 is emitted. Everything happens as if the loop body was duplicated:
loop signal S in i f S e l s e then emit O1 e l s e emit O2 end; pause ; emit S end s i g n a l ; signal S in i f S then emit O1 e l s e emit O2 end; pause ; emit S end s i g n a l ; end loop

In this obviously equivalent statement, the old and fresh incarnations are split into two syntactically distinct signals that happen to bear the same name S and the if statement is duplicated. In the original form, the single S generates two distinct dynamic incarnations, and the if statement dynamically tests the current incarnation of the signal. The pre and pre(?S) operators always refer to the current incarnation. For example, in
loop signal S in i f pre ( S ) then emit O1 e l s e emit O2 end; pause ; emit S end s i g n a l end loop

the O2 signal is continously emitted. The S emitted at the end of the loop body is not matched by pre, which matches the new incarnation, with pre(S) initially absent, as specied in Chapter 5 and Section 8.14. See [5] and [15] for thorough analyzis of reincarnation.

8.15

Open port declaration

Access to ports is normally done using the dot notation P.I, or P[2].Q.I, etc, see Section 6.2.3. The open declaration makes it possible to make port eld names directly visible, forgetting about the port name. The syntax is
open port-name-list i n p end open

where port-name-list is a list of port names. For each port P in the list, the port eld names N become synonym to the eld P .N . A eld name can hide a signal with the same name that already exists in the scope. Opening is not recursive. If a port Q is a eld of P that itself has a eld S, then open P declares Q as a synonym of P.Q but does not declare S as a synonym of P.Q.S. For this, one can open P and Q in sequence, typing open P, Q in p end. For a simple instance of open consider the following interface and module:

138
i n t e r f a c e Intf : input I ; output O ; end i n t e r f a c e module M : extends Intf ; port P : Intf ; input A ; open P i n emit O <= I and A end open end module

CHAPTER 8. STATEMENTS

Within the open declaration, I is a synonym to P.I and O is a synonym to P.O. Therefore, the emit statement is equivalent to
emit P . O <= P . I

If we add input I to the module header, then the open statement makes I an abbreviation of P.I, thus hiding the input I in the open scope. An important usage of open is to call a submodule that extends an interface with argument a port with that interface. Without open, one must write such a call as follows:
i n t e r f a c e Intf : input I ; output O ; end i n t e r f a c e module Sub : extends Intf ; ... end module module Master : port P : Intf ; ... run Sub [ P . I / I , P . O / O ] ... end module

which is really cumbersome if Intf has many elds. With open, one can use implicit binding:
open P i n run Sub end open

The implicit binding I / I then means P.I / I. See for instance the memory example in Section 2.3.4.

8.16

Local variable declaration

In Section 5.8.1, we have presented local variable declarations. We recall that they start with var followed by a list of individual variable declarations. The variable scope is the

8.16. LOCAL VARIABLE DECLARATION

139

variable declaration body, which is an executable statement that determines the behavior. Here is an example:
var V : unsigned , W : unsigned i n i t 0 , VarArray1 : unsigned [5] := {0 ,1 ,2 ,3 ,4} , VarArray2 : temp bool [32] := 0 i n ... end var

Variables can be initialized at declaration time using the := symbol. If the type is an array type, the initializer can be either an array literal or a simple literal of the array base type, see Section 3.1.10. It the variable is declared temp, the initialization is performed at each cycle before any usage of the variable.

140

CHAPTER 8. STATEMENTS

Chapter 9

The run module instantiation statement


A module can be instantiated within another module using the run executable statement. The instantiated module is called the submodule, generically called Sub, the other one being the master module, generically called Master. The basic conceptual principle is that run executes the code of Sub in Master, after instantiating all generic data parameters of Sub if there is any, and after directly or indirectly binding the signals of the master module to the signals of the submodule. Directly or indirectly recursive run is disallowed. The run statement is a normal statement that can be used anywhere a statement can, without restriction. A run statement starts the submodule body when it starts, terminates when its body terminates, and aborts its body when it is aborted. Therefore, a run statement can be put in sequence or in parallel with other run statements or with any other statement, it can be placed in any abortion context which will abort the submodule execution, etc.

9.1

Basic syntax

The run statement makes it possible to explicitly or implicitly pass data parameters to generic submodule data objects, and to pass reset signals to the submodule. Data parameters, signals, and reset parameters are passed in a bracketed list. Here is an example:
run Sub [ type unsigned <8 > / T1 , bool / T2 ; constant 12 / N ; signal X / I, Y / O; reset R ]

Since signal binding is the most frequent, the signal keyword can be omitted. Therefore, one can write
run Sub [ type unsigned <8 > / T1 , bool / T2 ; constant 12 / N ; X / I, Y / O; reset R ]

which is most useful when there are only signal renamings:


run Sub2 [ X / I , Y / O ]

141

142

CHAPTER 9. THE RUN MODULE INSTANTIATION STATEMENT

For generic submodule data objects, there is a similar implicit substitution mode: if a generic parameter of Sub is not substituted, there must exist a data object of the same kind, name, and type in Master, which then replaces the generic parameter in the submodule interface and body. There is a default mode for signal binding called implicit mode: when a signal argument is not explicitly bound in the run statement, it is assumed that a signal with the same name exists in Master and the binding is done to this master signal. Therefore, an absence of binding for an interface signal S is viewed as a binding S / S. There is also a null binding of the form / S for a submodule interface signal or port S, see Section 9.10 for details. All submodule generic data objects must be explicitly substituted or bound when running the submodule. A run statement does not make the data objects declared in the submodule visible.

9.1.1

Naming a submodule instance

The same submodule can be run several times in a master module. It is not necessary but often convenient to explicitly name the submodule instances, for instance to trace them back from the generated code or to display them in debuggers and browsers. This is done with the following syntax:
module Master : ... run Sub1 / Sub ... run Sub2 / Sub ... end module

This form of instance naming applies to all all forms of run described below, with or without argument lists.

9.2

Argumentless run statement

The simplest form of run statement consists of the run keyword followed by the submodule name.
module Sub : g e n e r i c type T ; input I : T ; output O ; ... end module module Master : type T = unsigned <16 >; type U ; input I : T ; signal O in ... run Sub ...

9.3. DATA SUBSTITUTION


end s i g n a l end module

143

Here, binding is done implicitly: the generic type paramemer T of Sub is bound to the actual type T = unsigned<16> of Master, the input signal I of Sub is bound to the input I of Master, and the output signal O of Sub is bound to the local signal O of Master. The precise behavior of data and signal bindings are presented in Section 9.3 and Section 9.4 below.

9.3

Data substitution

Each generic data object declared in the module header of Sub (directly or by extension) must be substituted at module instantiation time using the syntax already presented in Chapter 2:
run Sub [ type i n t e g e r / T ; constant 0 / Initial , 1 / Increment ; f u n c t i o n MyMult / Mult , F / SubF ; procedure P / SubP ]

Data substitutions lists consist of a data kind keyword followed by an arbitrary number of comma-separated substitution items. There can be any number of substitution lists, themselves separated by semicolons. As for generic data extension, a substitution X / Y is read X is substituted to Y, or X renames Y. The left argument X must be a predened data object (constant, operator) or a data object declared in Master, which can itself be generic or dened generic. The right argument Y must be a generic data object of Sub. Both X and Y must be of the same kind (type, constant, function, or procedure). For constants, the types must agree after type renaming in Sub. For functions and procedures, the argument lists must match after type renaming in Sub. All generic data objects of the submodule must be either explicitly or implicitly bound to data objects of the master module. Therefore, for any submodule generic parameter which is not explicitly bound, there must exist a data object ot the same name and kind in the master module, with matching type for constants, functions, and procedures. The master data object can itself be generic or dened generic. Unlike for generic data extension presented in Section 2.2.1, the data object declared in the submodule data part are not imported in the master module, Therefore, only generic data objects can be substituted in a run statement. Remember that data extension also permits renaming of dened generic data objects, in order to change their name when importing them. This facility is not needed here since there is no data import. Therefore, it is disallowed to rename generic dened data objects in a run statement.

9.4

Signal binding

The signal keyword that starts a signal binding list is optional and can be omitted. Signals are implicitly bound by name or explicitly bound using the same X / Y notation as for data, in which case we say that Y is bound to X or that X renames Y, or using the null binding notation / Y, in which case we say that Y is unbound.

144

CHAPTER 9. THE RUN MODULE INSTANTIATION STATEMENT

Unless null-bound, ll the interface signals of Sub must be implicitly or explicitly bound to signals of Master visible in the scope where the run statement is executed. If an interface signal X of Sub is not explicitly bound in the run statement, then implicit binding by name is used, the binding being assumed to be X / X where X must be dened in Master. There are several variants of signal bindings, described below.

9.4.1

Expression binding

Expression binding has the form


run Sub [ s i g n a l (e) / I ] run Sub [ (e) / I ]

where I is a pure input signal or pure input array of Sub, and where e is a Boolean expression or Boolean array expression valid in the the run context within Master. Parentheses around e are mandatory to avoid ambiguity with other forms of binding. For arrays, dimensions must match. For example, run Sub [ (S.X[1] or T or ?T) / I]. is a correct signal binding for S a port with a pure signal array eld X and for T a signal of type bool. At each instant where Sub is active, the expression binding sets I in Sub present if the the current value of e is true in Master. The expression e is evaluated at each instant.

9.5

Input binding

Input binding has the form


run Sub [ E / I ]

where I is an input signal or signal array of Sub and E has one of the following forms: A simple signal, for instance S. A partially or totally indexed or sliced signal array identier, for instance S[1], S[..][2][1..3]. A port name, possibly indexed or sliced, followed by an expression of the same form, for instance P.S or P[1..2].S[1]. In all the cases above, the master S can be either standard or registered. Of course, dimensions must match for arrays and slices. The submodule input can be declared temp independently of the characteristics of the master signal. It cannot be declared registered. One can forget status or value attributes of the master signal in input binding. If the submodule input is pure, the master signal can be pure or full, in which case its value is forgotten. If the submodule input is value-only, the master signal can be value-only or full with the same type, its status being forgotten in the latter case. If the submodule input is full, then the master signal must also be full.

9.5.1

Full signal input binding

For a pure or full submodule input I, the status of I receives the status of the master argument. If I is an array, each component of the submodule input receives the corresponding component of the master array or slice.

9.5. INPUT BINDING

145

Therefore, the status of the submodule input I signal is set present whenever the module is active and the status of the master signal or signal component is present. The status of I can also be set present by Subs body. At Sub starting instant, pre(I) is absent and pre1(I) of I is absent. Value transmission and initialization for full signal Assume I is a full input signal of Sub. Then, the master signal or array component must also be a full signal. The value transmission laws follows from the general fact that value emission is controlled by the status for a full signal. The general law is as follows: A full input is like a local signal of Sub for which the master module acts as an extra emitter. In the sequel, we detail the consequences of this general rule by an in-depth study of all the derived subcases. Any emission of the master signal or signal component in the master module triggers an emission of I in the submodule with the master value. If I is combined in Sub, then the master value is combined with the values internally emitted in Sub if there are any. Value denition and initialization is as follows: If I is persistent (non-temp) and has no init attribute, its value ?I is undened until the rst reception from the master module or the rst local emission. The value pre(?I) is undened until the rst instant that follows that instant. If I is persistent and has an init E attribute, the values ?I and pre(?I) are initialized to that of E. The initial value of ?I is overwritten by the rst reception or internal emission of I, which may occur at starting instant. If I is declared temp and has no init attribute, its value is dened only when the signal is present, i.e. received from Master or emitted in Sub. Remember that the expression pre(?I) is disallowed for a temp signal. If I is temp and initialized using a init E attribute, its value is always dened. At each instant, it is equal to that of E if I is neither received from Master nor internally emitted, and as the emitted value otherwise.

9.5.2

Value-only input binding

For a value-only input, the laws directly follow form the laws of full signals by considering the master status as always present. Therefore, the value of the master signal is imported at each instant into Sub. For I combined, the master value is combined with the values locally emitted in Sub when Sub is active. If I is not combined, then it cannot be locally emitted in Sub. The value pre(?I) can be used for a persistent (non-temp) signal. It can be initialized using an init E attribute in the declaration of I. Notice that init then acts only on pre(?I) since ?I is input from Master at Sub start time. Since this last point can be confusing, it is advisable for compilers to generate warnings stating that the initial value will never be used if pre(?I) is never used.

146

CHAPTER 9. THE RUN MODULE INSTANTIATION STATEMENT

9.5.3

Input initialization examples

Single input initialization example Consider submodule valued inputs of the form
module Sub : input I : i n t e g e r ; input Iinit : i n t e g e r i n i t 5; input IV : value i n t e g e r ; ... end module

with a binding of the following form in Master:


run Sub [ S / I , S / Iinit , S / IV ]

We use a single master signal S to simplify the explanation; one could of course use three distinct master signals. Assume rst that S is emitted in Master when Sub starts:
emit ? S <= 2; run Sub [ S / I , S / Iinit , S / IV ]

In this case, the value 2 of S is passed from S to I, Iinit, and IV. The value of pre(?Iinit) at that start instant is 5, while the values of pre(?I) and pre(?IV) are undened. Consider now the case where S is not emitted in Master when Sub starts:
emit ? S <= 2; pause ; run Sub [ S / I , S / Iinit , S / IV ]

In this case, at the second instant, I is left undened, Iinit is initialized to its explicitly given initial value 5, and IV takes the value 2 of S. As before, the value of pre(?Iinit) at that instant is 5, while the values of pre(?I) and pre(?IV) are undened. Assume now that we remove the initial emit ?S <= 2 statement in this last example and that S is not initialized in Master when Sub starts. Then, there is a undened signal run-time error for S since one tries to read the value of S in order to initialize that of IV. Combined inputs initialization examples Consider now the case where the submodule inputs are combined:
module Sub : input I : i n t e g e r combine +; input Iinit : i n t e g e r i n i t 5 combine +; input IV : value i n t e g e r combine +; input Combine ; i f Combine then emit { ? I <= 6 , ? Iinit <= 6 , ? IV <= 6 } end i f ; ... end module

9.6. OUTPUT BINDING

147

The behavior is exactly as before if Combine is not emitted by Master module when Sub starts. If Combine is emitted, Then, when Sub starts, initialization is disabled because of internal emission. There are two cases according to the status of S. Consider rst the case where S is emitted.
emit { ? S <= 2 , Combine }; run Sub [ S / I , S / Iinit , S / IV ]

Then, combination of master and internal values is performed for all inputs, which all take value 8. Consider next the case where S is not emitted when Sub starts:
emit ? S <= 2; pause ; emit Combine ; run Sub [ S / I , S / Iinit , S / IV ]

The signal S and Iinit take value 6, while IV takes combined value 8 since a valued signal is always viewed as emitted by the environment.

9.6

Output binding

Output binding has the form


run Sub [ E / O ]

where O is output signal or signal array of Sub and E is a non-registered signal component, array, or slice of the same form as for input binding, see Section 9.5. The submodule output O can be standard or registered. Types must match for typed signals, and array dimensions must match for signal arrays. One can forget signal attributes of O in the binding: if O is full, then E can be a pure or value-only signal or signal array. If E is pure, the value of O is discarded. If E is value-only, the status of O is discarded. One cannot add attributes from O to E: if O is pure, then E must be pure; if O is value-only, then E must be value-only.

9.6.1

Output binding behavior

Output binding behavior is simpler than input binding: As far as Master is concerned, whenever Sub is active, there are two cases: If O is pure or full, presence of O in Sub triggers emission of E in Master provided Sub is alive. This emission of E can be combined with other emissions of E in Master. If O is value-only and Sub is alive, then O triggers an emission of E in Master with the same value at each instant when the run statement is active. This emission is combined with other emissions of E in Master if E is combined.

148

CHAPTER 9. THE RUN MODULE INSTANTIATION STATEMENT As far as Subs body is concerned, O is viewed exactly in the same way as a local signal. Its value is initialized by an init attribute (declared directly or by a refine declaration), or undened until the rst emission, See Section 8.14. Because of the second rule above, only a full signal can be left undened. A value-only output signal must always have a dened value.

Notice that an emission of the submodule output is propagated only when the submodule is alive. Consider the following case:
module Sub : output R : reg ; emit next R ; end module module Main : output X ; signal S in run Sub [ S / R ] || await S ; emit X end s i g n a l end module

Here, Sub is started at rst instant, emits R for next instant, but immediately dies. Since Sub is dead at second instant, the registered emission of R is not propagated to Main and S and X are not emitted.

9.6.2

Output signal initial value transmission

Notice that the initial value of O in Sub is visible to Master in two cases detailed below In the rst case, O is declared reg1. In that case, O must be initialized by an init attribute. Since O is emitted at rst instant in Sub, its initial value is passed to Master. Here is an example:
module Sub : output O : reg1 unsigned i n i t 3; ... end module module Main : output X : unsigned ; run Sub [ X / O ] end module

At rst instant, X is output with value 3. Omitting the init attribute would provoke an undened value run-time error. If, instead, O is declared reg, there is no value transmission at initial intant and no undened value error if there is no init attribute. In the second case of initial value transmission, O is declared value-only. Then, unless it is emitted in the rst instant of Sub, O must have an init attribute, and its initial value is passed to Master at rst instant.

9.7. INPUTOUTPUT BINDING

149

9.7

Inputoutput binding

Inputoutput binding has the form


run Sub [ S / IO ]

where IO is an inputoutput interface signal or array of Sub and S is a signal, signal array, or port component valid in the run context within Master, just as for input and output bindings. The inputoutput signal IO cannot be initialized in Sub. All the attributes of IO must match those of S: type, array dimension, temp or persistent character, combination function if any. Array dimensions must match. The S and IO signals must be both standard or both registered. If any of the signals is valued (resp. value-only), the other must be valued (resp. value-only) and with the same type (there is no forgetting for inputoutputs). Each component of IO is identied to the corresponding component of S. In some sense, inputoutput binding acts as passing by reference in traditional software languages.

9.8

Port binding

Port binding has the form


run Sub [ Q / P ]

where Q is a port or port component of Master (i.e. Q.R, Q[4].R[2], etc.), and P is a port interface signal or array of Sub having the same interface as P. Each signal component of P is bound to the corresponding signal component of Q according to the signal binding rules above.

9.9

Signal binding example

Here is an example of valid signal binding:


i n t e r f a c e Intf : input A ; output B ; end i n t e r f a c e module Sub : constant N : i n t e g e r ; input I , J , K [ N ] , L ; output O1 , O2 [5] , O3 ; inputoutput IO ; port P : Intf ; end module module Master : input X [5]; s i g n a l Y , Z [12] , IO , port Q : Intf i n ... run Sub [ constant 12 / N ; ( Y or Z [1]) / I , Y / J,

150

CHAPTER 9. THE RUN MODULE INSTANTIATION STATEMENT


Z / K, Z [1] / L , Y / O1 , X / O2 , Z [1] / O3 , Q / P ] ... end s i g n a l end module

Here, (Y or Z[1]) / I is an expression binding; Y / J and Z / K are input bindings, the latter binding arrays; Z[1] / L is an array to input binding; Y / O1 and X / O2 are output bindings, the latter binding arrays; Z[1] / O3 is an output to array binding; Q / P is a port binding; nally, there is an implicit inputoutput binding IO / IO. Notice that master signals can appear several times in bindings, and even in both input and output bindings.

9.10

Null binding

Assume that S is an interface signal of Sub subject to a null binding / S in a run statement:
run Sub [ / S , ...]

Then S is not bound to any master module signal and is made local to the instance of Sub generated by the run statement. If S is an input (resp. an output), no status or value is propagated from (resp. to) the master module. Null binding is also available for ports. For an interface port P of Sub, / P recursively means that all components of P are null-bound.

9.11

Value passing rules

We give examples of value passing between a master module and a submodule.

9.11.1

Value passing examples

Consider the following submodule:


module Sub : output O : i n t e g e r ; output O1 : i n t e g e r i n i t 1; pause ; emit { ? O <= 2 , ? O1 <= 3 } end module

and the following signal binding:


run Sub [ X / O , Y / O1 ]

9.12. RESETTING SUBMODULES

151

Assume rst that X and Y are standard signals. Then, values are passed only when the output signals are emitted in Sub. Therefore, when Sub starts, no value is passed from Sub to the master module. The initialization of O1 only applies within Sub; at next instant, value 2 is passed to X and value 3 is passed to Y. If the pause statement is removed, the values are passed at Sub start time, and the initialization of O1 is discarded since the signal is emitted. Valus passing is the same if X and Y are value-only, the status of O and O1 being ignored.

9.11.2

Passing values from registered submodule outputs

Consider the following submodule with registered valued outputs :


module Sub : output R : reg i n t e g e r ; output R1 : reg1 i n t e g e r i n i t 1; emit next { ? R <= 2 , ? R1 <= 3 }; pause end module

and a call for this submodule of the form


run Sub [ X / R , Y / R1 ]

where X and Y are integer signal, full or value-only. The value of R is exported to X when R is present, one tick after being emitted. Since R1 is declared reg1 it is also present when Sub starts, passing value 1 to Y. Remember than the trailing pause in Sub is mandatory, otherwise the values of R and R1 would not be exported since Sub would be dead at second instant.

9.12

Resetting submodules

One can add a reset signal to a run statement. There are two kinds of reset, strong reset, which acts at the beginning of the instant and is the default, and weak reset, which acts at the end of the instant (the terms weak and strong have the same meaning as for abort). In the hardware classical terminology, strong reset corresponds to asynchronous reset, and weak reset corresponds to synchronous reset. We do not use these ambiguous terms, since the semantics is synchronous is both cases.

9.12.1

Strong reset

Strong reset conceptually resets the module at the beginning of the instant. It is written as follows:
run Sub1 / Sub [ r e s e t R ; ...]

The semantics is that of the following statement:


trap End i n loop run Sub1 [...];

152

CHAPTER 9. THE RUN MODULE INSTANTIATION STATEMENT


e x i t End each R end trap

Notice that submodule termination is preserved.

9.12.2

Weak reset

Weak reset conceptually resets the module at the end of the instant. It is written as follows:
run Sub1 / Sub [weak r e s e t R ; ...]

The semantics is that of the following statement:


trap End i n loop weak abort run Sub1 [...]; e x i t End when R ; pause ; end loop end trap

When R occurs, Sub1 is weakly aborted, and it is restarted afresh at next instant. Module termination is preserved.

9.12.3

Why passing reset signals

The interest of passing reset signals shows up in hardware synthesis: one can realize the above behavior in a way that is electrically much simpler and much more ecient than the Esterel expansion.

Chapter 10

Oracles
This chapter deals with oracles, which are special hidden input signals used to model non-deterministic behavior. Warning: oracles are experimental in Esterel v7 20. They are only supported by verication, for experiment purposes. Simulation is not yet available for them. Oracles are special signals assumed not to be on the user control. They are declared with a special signal declaration. They can be valued, but only the interface features temp and value can be used for them. The modules features reg, init, and combine are not available. The status and value of an oracle are supposed to be freely determined by the environment in any execution. Oracles are meant to help modeling non-determinism. Here is how to write a choice between two statements p and q:
o r a c l e Oracle i n i f Oracle then p else q end i f end o r a c l e

Oracles are broadcast as any other signals. Here is a way to perform a coordinated choice in two branches of a parallel:
o r a c l e Oracle i n ... i f Oracle then p1 else q1 end i f ... || ... i f Oracle then p2 else q2 end i f

153

154
... end o r a c l e

CHAPTER 10. ORACLES

If the two concurrent if statements are executed simultaneously, they both take their then branch or their else branch in a coordinated way since they test the same oracle. Coordinated choice can be very useful in modeling non-deterministic applications and is usually not available in asynchronous concurrent formalisms. Valued oracles return any value in the type:
o r a c l e Oracle : i n t e g e r i n emit ? O <= ? Oracle end o r a c l e

The value of O can be any integer. The way the integer is chosen is left to the implementation. Beware: one cannot choose a random integer, because such a thing cannot exist! Randomness makes sense only when a probability distribution is chosen, and no distribution exists for all integers. However, in makes perfect sense to choose randomly a bounded signed or unsigned integer. It is also possible to explicitly emit an oracle using ususal emit or sustain statements, to explicitly gain control over the oracle if needed.

Chapter 11

Macro-expansion of Derived Constructs


11.1
11.1.1

Expansion of signal constructs


Expansion of pre

For a pure signal S, one can encode pre(S) using an auxiliary signal preS. Assume for instance that S is declared in a block such as
signal S in p end s i g n a l

Then, call p0 the statement p where pre(S) is replaced by preS. The following program has the same behavior:
s i g n a l S , preS i n trap Done i n p0; e x i t Done || loop i f S then pause ; emit preS else pause end i f end loop end trap end s i g n a l

For a valued signal, one uses an auxiliary variable to store the value between the instants:
s i g n a l S : T , preS : T i n trap Done i n p0; e x i t Done || var X : T i n

155

156

CHAPTER 11. MACRO-EXPANSION OF DERIVED CONSTRUCTS


loop i f S then pause else X := ? S ; pause ; emit preS ( X ) end i f end loop end var end trap end s i g n a l

11.1.2

Expansion of next

One can code a registered signal S using a standard signal nextS and the pre operator: One replaces emit next S by emit nextS and emit next ?S <= exp by emit ?nextS <= exp. One replaces if next(S) by if nextS and next(?S) by ?nextS. One replaces if S by if pre(nextS) and ?S by pre(?nextS). For an output registered signal, one must also connect pre(nextS) instead of S to the output interface, see Chapter Chapter:Run.

11.2

Expansion of delays

Here is how delays described in Section 8.9.3 are expanded. Consider the following statement:
abort p when c times e

The expansion is as follows:


trap T i n var C := c : i n t e g e r i n loop await e; C := C -1; i f C <= 0 then exit T end i f end loop end var end trap

Chapter 12

Run-time errors
Run-time errors are errors that make the program work incorrectly. In software or hardware generated code, they can create erroneous behaviors. In software, they can also provoke crashes. Therefore, Esterel v7 implementations should make every eort to help the user detecting possible run-time errors before embedding the program. This can be done by using good simulation tools, static analyzers, or verication systems.

12.1
12.1.1

Data-related errors
Unsigned subtraction error

There is an unsigned subtraction run-time error if the second argument of an unsigned - operator is greater than the rst one, see Section 4.2.2.

12.1.2

Unsigned division error

There is an unsigned division run-time error if the second argument of an unsigned / operator evalulates to 0, see Section 4.2.5.

12.1.3

Unsigned modulo error

There is an unsigned modulo run-time error if the second argument of an unsigned mod operator evalulates to 0, see Section 4.2.6.

12.1.4

Unsigned binsize error

There is an unsigned binsize run-time error if the argument of binsize evalulates to 0, see Section 4.2.7.

12.1.5

Unsigned out of range error

A run-time occurs when a variable or a signal is assigned a value outside the range of an unsigned type. This error is raised by an explicit or implicit assertion about the required size. Explicit assertions are calls to the assert<M > function, see Section 4.2.10. An implicit assertion is generated by an assignment to an unsigned variable, a call to a function or procedure with an unsigned argument, an emission of an unsigned signal, and an array indexation. 157

158

CHAPTER 12. RUN-TIME ERRORS

Note that run-time implicit assertions are necessary only if the type of the expression is bigger than the type of the objet the result is used for. Otherwise, type-checking ensures correctness. For instance, let A[M ] be an array and S be a signal of type unsigned<N >. The indexation A[?S] is guaranteed correct at type-checking if N M and it requires a run-time implicit assertion otherwise.

12.1.6

Signed division error

There is a signed division run-time error if the second argument of a signed / operator evalulates to 0, see Section 4.3.4.

12.1.7

Signed out of range error

A run-time occurs when the argument of an assert_unsigned function is not in the required unsigned range, see Section 4.3.11, or when a variable or a signal of a signed type is assigned a value outside the range of the type. As for the unsigned case of Section 12.1.5, this can occur because of an explicit call to assert<M >, see Section 4.3.10, or because of an implicit assertion. An implicit assertion is generated by an assignment to a signed variable, a function or procedure call with a signed argument, or an emission of a signed signal. As for unsigned, there is no need for an implicit run-time assertion if the operation can be shown safe at type-checking time.

12.1.8

Bitvector onehot2u conversion error

A run-time error occurs when a bitvector used as argument of onehot2u has either zero or more than one 1-component, as for b000 or b101. In that case, the bitvector does not represent a 1-hot encoded unsigned number.

12.2
12.2.1

Signal-related errors
Access to uninitialized signal value error

For each incarnation of an uninitialized valued signal S, the following operations are runtime errors: If S is standard valued, reading the signals value ?S before the rst instant where S is emitted. If S is registered valued, reading the next signals value next(?S) before the rst instant where a next emission of S is performed. If S is standard valued, reading the previous signals value pre(?S) before the instant immediately following the one where S is emitted for the rst time. If S is registered valued, reading the signals value ?S before the instant immediately following the one where a next emission of S is performed for the rst time.

12.3. VARIABLE-RELATED ERRORS

159

12.2.2

Bad access to temporary signal value error

It is a run-time error to read the value of a temporary signal if this signal has not been emitted nor received from the environment of from a submodule in the instant.

12.2.3

Multiple emission of single signal error

It is a run-time error to perform two emissions of the same incarnation of a single valued signal. The signal must be declared combined to support multiple emissions, see Section 5.6.

12.2.4

Bad access to a signal array error

It is a run-time error to access a signal array position out of bounds. This error is a particular case of the unsigned out of range error of Section 12.1.5.

12.3

Variable-related errors

Remember that a variable is assigned to by an assignment statement or a procedure call where the variable is passed by reference. For arrays, each position is considered independent of the other ones.

12.3.1

Access to uninitialized variable error

At each instant, it is a run-time error to read the value of an uninitialized variable before this variable has been assigned a value for the rst time in the program execution.

12.3.2

Illegal access to temporary variable error

At each instant, it is a run-time error to read the value of a temporary variable before this variable has been assigned a value for the rst time in the instant. This error cannot occur if the variable is initialized, since the initialization if performed at each instant.

12.3.3

Bad access to a variable array error

It is a run-time error to access a variable array position out of bounds. This error is a particular case of the unsigned out of range error of Section 12.1.5.

12.3.4

Sharing a variable error

It is a run-time error to be in a situation where a variable can be read / write shared between two concurrent control threads: one thread can execute an action that reads the variables, the other threads can execute an action that writes the variable, and the actions are not dynamicaly ordered by a causality path, see Section 5.8.3. Notice that shared variables may be hard to detect. Implementations may put stronger static requirements to ensure non-sharing at compile-time. What is forbidden by the language denition is only to share variables at run-time.

160

CHAPTER 12. RUN-TIME ERRORS

Chapter 13

Introduction to multiclock Esterel design


This chapter informally presents multiclock design with Esterel. Multiclock design is strongly related to hardware issues. Therefore, in this chapter, we shall use a much more hardwarish vocabulary than in the previous chapters. Multiclock design is much trickier than single-clock design and has be much less studied in the previous Esterel literature. Therefore, we also present many more examples than before. We refer to the implicitly single-clocked Esterel language presented in the previous chapters as Classic Esterel. In Section 13.1.1, we rst recall how one can generate single-clocked Register Transfer Level (RTL) circuits from Classic Esterel programs. We discuss the simple relation between timing closure issues and the synchrony hypothesis. In Section 13.2, we discuss the need for multiclock circuits in modern design and we present the Globally Asynchronous Locally Synchronous (GALS) model on which multiclock Esterel is based. We briey discuss timing closure and metastability issues. We informally present multiclock Esterel in Section 13.3 and discuss examples. We discuss how to simulate multiclock designs using weak suspend in classic Esterel, and how to synthesize multiclock circuits or single-cloked emulation from them.

13.1

RTL design

The simplest circuit design paradigm is single-clocked Register Transfer Level or RTL design. Circuits are composed of wires and gates. Gates can be combinational or sequential. Combinational gates continuously compute their output voltage in function of their input voltage. Sequential gates hold state and are called registers. They are driven by clocks, which are electronic devices that generates electrical rising and falling edges. Here, we only consider rising edges as meaningful clock events. A register changes state when a clock rising edge occur: it samples its input voltage and keeps it on its output until the next clock rising edge. An essential requirement of RTL circuits is timing closure. For a state change to be meaningful, the input of a register must be electrically stable when a clock rising edge occurs. Similarly, a circuit primary output must be stable before being used by the circuit external environment. For a given circuit technology and layout, the time it takes to stabilize all register inputs and all primary outputs depends on the longest delay path in the logic gates and wires, which is called the critical path. The critical 161

162

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

Figure 13.1: Circuit and waveform of M1 delay can be statically computed, and it determines the maximal clock frequency for the technology. Computing that frequency is performing the timing closure of the circuit. Below that frequency, there is no dierence between the electrical behavior once stabilized and the logical behavior computed with delay 0 for all gates, wires, and state transitions. Therefore, the Classic Esterel zero-delay abstract model is adequately implemented by the actual circuit. Notice that a clock needs not be regular in time: only its rising edge succession matters, provided that any two rising edges are not closer than required by critical path stabilization. For instance, irregular clocks result from shutting down clocks to save power when a circuit part is unused.

13.1.1

Translating Esterel programs into RTL circuits

The translation from Esterel programs to RTL circuits is explained in [3, 5]. We illustrate it with two examples that we shall later reuse in Section 13.3.1 to study communication in muticlock designs. Consider rst the following inputless module M1:

13.1. RTL DESIGN


module M1 : output O1 ; // Start loop emit O1 ; pause ; // FP pause ; // SP end loop end module

163

The corresponding circuit is pictured in Figure 13.1, together with a waveform denoting its abstracted electrical behavior. Circuit registers implement control states. They are named using the labels of the start condition or pause statement they implement1 . Their reset value is the one written inside the register symbol. A value 1 in a control register means that control is currently paused at that point. The logical behavior is the following sequence of tick executions:
tick 1 : 2 : 3 : 4 : 5 : 6 : ... in - - - - - - out O1 O1 O1 -

where - means that all signals are absent. Circuit execution mimics logical behavior. In Esterel semantics, at tick 1, M1 starts from label Start, executes emit O1, and pauses at pause statement FP . For the circuit, assume reset active level is high. Then, when reset falls, O1 is driven high by the Start register initial value, until clock rising edge 1. At that edge, the Start register changes state to 0 while the FP register changes state to 1. In Esterel semantics, at tick 2, control moves from pause labeled FP to pause labeled SP. In the circuit, at clock rising edge 2, register FP gets new state 0 while register SP gets new state 1. This drives O1 to 1 until rising edge 3, i.e. during the electrical duration of tick 3. At rising edge 3, register SP gets new state 0 while register SP gets state 1. Being driven by SP , O1 falls to 0 until rising edge 4 where SP gets state 1 again and drives O1 to 1. Alternation between these two behaviors follows, which means that O1 is kept high every other clock cycle. In Figure 13.1, waveforms depict the value evolution of signals over continuous time. We use a classical convention: a change in a signal is drawn a little afer the clock edge that provoked it. While logically equivalent to the 0-delay model, this -delay model simplies waveform reading. A stabilized signal value at initial Esterel tick 1 can be read anywhere from the waveform start to value change 1, and, in particular, on the vertical dotted line of clock rising edge 1. A signal value at Esterel tick 2 is read anywhere from rising edge 1 excluded to rising edge 2 included. Generally speaking, signal values at Esterel tick n can be safely read on the vertical dotted line that starts from rising edge n, since all values are stable at that time. With a pure 0-delay waveform, a signal change
In practice a better labeling could be pause@FP, as explained in Section 1.3.3; we do not use it here for readability reasons.
1

164

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

Figure 13.2: Circuit and waveform of M2 would be exactly synchronous with the clock edge, and it would be unclear whether the value to consider is the one right before or the one right after the clock edge. Consider now the following module M2 with input I2:
module M2 input I2 ; output O2 ; // Start loop await pre ( I2 ); // FA await pre ( I2 ); // SA emit O2 end loop end module

The corresponding circuit is pictured in Figure 13.2. The pictured waveform corresponds to the following logical behavior:
1 2 3 4 5 6 7 : : : : : : : I2 I2 I2 I2 O2 O2

// pre ( I2 ) // pre ( I2 ) // pre ( I2 ) // pre ( I2 )

13.2. THE NEED FOR MULTIPLE CLOCKS


8 : -

165

The O2 output is set to 1 at every other edge where pre(I2) is 1. In the circuit, the pre register adds a signal state to the control states. It takes I2 as input and returns pre(I2) as output. We says that the pre register samples I2 at clock rising edge. Tick 1, start to rising edge 1: at start time, the Start register has value 1 and the other registers have value 0. Thus, O2 has value 0. Tick 2: rising edge 1 to 2, At rising edge 1, the FA register changes state to 1, which corresponds to the fact that control in tick 2 is driven by the FA pause statement. The Start register state becomes 0, and the SA register state stays 0. The pre register state keeps value 0 since since I2 is 0 at rising edge 1. The output O2 is 0 since SA is 0. Tick 3, rising edge 2 to 3: At rising edge 2, since the current value of pre is 0, control stays at FA and control register states are unchanged. The pre register state becomes 1 since I2 is 1. The O2 output is still 0 since SA is 0. Tick 4, rising edge 3 to 4: At rising edge 3, since the upper and lower and-gate generated by the rst await statement have respective value 1 and 0, FA becomes 0 and SA becomes 1. The pre register becomes 0 and drives the O2 output to 0. Tick 5, rising edge 4 to 5 : At rising edge 4, FA stays 0 and SA stays 1 because pre is 0. The pre register becomes 1. Since SA and pre are both 1, the O2 output is driven to 1 during the clock period. Tick 6, rising edge 5 to 6 : At rising edge 5, since pre is 1, the FA register becomes 1 and the SA register becomes 0, which corresponds to looping the loop. Thus, O2 falls to 0. The pre register stays 1. Tick 7, rising edge 6 to 7 : At rising edge 6, control moves from FA to SA because of the leftmost upper and-gate; the pre state stays 1 since I2 is 1. The output O2 is 1 since SA and pre are 1. Tick 8, rising edge 7 to 8 : At rising edge 7, control moves back to SA since FA and pre are both 1. The pre register state becomes 0, which drives O2 to 0. In the waveform of Figure 13.2, the I2 input is supposed to be set exactly at state change and to be kept constant during the whole cycle. It is called an early input Because of sampling by the pre register, requiring an early input is actually not necessary: since sampling of I2 occurs right at the clock cycle, only I2s value at that point matters. Figure 13.3 shows that an indentical behavior would be obtained with a more aggressive I2 waveform, which I2 is late but polite enough to have stabilized to the right value just before clock rising edges occur. This intinsic tolerance of sampling w.r.t. late inputs will be fundamental for multiclock design.

13.2

The need for multiple clocks

Modern hardware designs usually have more than one clock and, in addition, can turn clocks on and o. There are several reasons for this. First, it is dicult to distribute a

166

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

Figure 13.3: waveform of M2 with impolite input single clock on a large chip, because of clock skew and power dissipation. Second, Systems on Chip (SoC) designs are usually composed of several IPs (Intellectual Property) modules of dierent nature, which must obey dierent clocking requirements. Third, not all IPs in a circuit are active at a given time, and it is essential to shut down the clock in inactive parts of circuits to save power. Finally, inputs and outputs of a circuit may require being clocked according to external protocols independently of how fast the circuit computes. For instance, a video lter may use an internal clock to compute images while being forced to obey a standardized video frame transmission protocol for I/O driven by an unrelated clock.

13.2.1

Metastability issues

Mutliple clocks can be generated either by a single PLL (Phase-Locked Loops) or by several distinct PLLs. In the second case, clocks are fully asynchronous, i.e. unrelated in time, and there is a danger of metastability. Assume that a signal driven by an emitter clock is the input of a register clocked by an asynchronous receiver clock. Then, if a change in the signal occurs too close from a receiver clock rising edge, the register can take an arbitrary non-Boolean metastable state for an arbitrary amount of time. In practice, noise makes the state randomly stabilize to 0 or 1 after some time, but the stabilized value may not be the intended one and stabilization time is not guaranteed. Therefore, metastability cannot be controlled in a deterministic way. Notice that getting some bits wrong may be acceptable in some applications: a wrong bit in a large picture is usually not a problem, because there are many other physical reasons to generate wrong bits in a photo or video chain. But wrong bits can be unacceptable in more sensitive design, and in particular in control applications. Then, one must either avoid metastability or deal with it. This can be done in two fundamentally dierent ways: Relying over absolute timing to avoid metastability by guaranteeing that signals never change when clock rising edges occurs. This is only possible if there are guaranteed relationships between clocks and clock phase drifts, i.e. if the clock front positions remain comparable over time, and if propagation delays in the actual circuit are well-mastered. We call this design style quasi-synchronous design below. Backend techniques to achieve quasi-synchronous design are outside of the scope of this paper.

13.2. THE NEED FOR MULTIPLE CLOCKS

167

Relying over specic communication devices called synchronizers that cope with metastability, possibly associated with handshake and error-correction protocols. Quasi-synchronous design is direct in multiclock Esterel. Synchronizers are programmable in Esterel, thus they do not need to be language primitives. There is a fairly wide variety of them, see for instance [8]. Here are some: Simple synchronizers use a register or a small chain of registers clocked by the receiver clock to reduce the probability of metastability for the last register; their output is electrically stable with very high probability but can be wrong. Multiple synchronizers transmit the same bits several times, serially or in parallel, and check consistency to detect errors due to metastability issues; Handshake synchronizers implement handshake feedback to acknowledge bit reception. They are often necessary if the clock frequencies are dierent enough. Dual-clock fos are lled on a writer clock and read on a reader clock. They are classical tools to maintain throughput in streaming-based systems. Synchronizers are mostly used for truly asynchronous clocks. However, they can also be used in cases where all clocks are generated by a single PLL and thus related in time, but where one does not want to predict exactly how relative clock rising edges are arranged because that depends on complex routing issues. In this case, one sticks to the worst case, assuming the clocks unrelated as for the multiple PLL case.

13.2.2

Some multiclock design terminology

Unfortunately, there is no well-established and clear terminology in current multi-clock design. Logic and electrical concerns are often mixed up. Since we mostly deal with the logical level in Esterel, we propose a simple terminology sucient for our purpose. A more elaborate one can be found in [12]. Pure synchronous design This is what the classic Esterel v7 language is about. There is a single clock. All register inputs are sampled synchronously by the rising edges of this clock, and, in the actual circuit, all signals are supposed to be stable when the rising edge occurs. Clock-gated synchronous design In synchronous designs, the clock can be gated to avoid sampling register inputs, this either to save power or for purely behavioral reasons. Conceptually, there is no model change w.r.t. pure synchronous design, only implementation improvements. The very same design can be implemented without clock gating, using logic to retain the register value instead of disabling the change. The Esterel suspend and weak suspend statements presented in Section 8.12.1 are the Esterel way to deal with clock gating.

168

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

Quasi-synchronous design In quasi-synchronous design, a multiclock design deals with several clocks whose rising edges have precise relations over time. Generally speaking, they come from a single PLL by clock division or clock gating, but how they are built is inessential here. We distinguish between three subcategories listed below (they may be other): Derived clocks: given a clock C, a clock C is derived from C if all rising edges of C are rising edges of C, see Figure 13.4. In practice, C is essentially obtained from C by clock gating. Derived clocks are used to slow down clocks or to shut them down to save power. Harmonic clocks: two clocks are said to be harmonic if they have a xed frequency and have simultaneous rising edges at some point in time. Then their coincidence point repeats regularly over time. Figure 13.5 shows the case of a ratio 3:4, where the rst clock beats 3 times while the second clock beats 4 times. For harmonic clocks, the distance between any two clocks rising edges is precisely predictable. Harmonic clocks are common in designs where busses or memories have several frequencies values that are multiple of a common base frequency. Phase-shifted clocks: two clocks beat at the same frequency but one is shifted by some delay w.r.t. the other. In this way, the delay between rising edges is predictable. Phase-shifted harmonic clocks combine harmony and phase-shifting, as shown in Figure 13.7. Timing relations become slightly more complex, but are still fully predictable. Of course, other cases are possible. We speak of quasi-synchronous design for any case where timing constraints are mastered enough to avoid the need for resynchronizers to communicate between clock domains. When the clocks are truly unrelated, i.e. generated by distinct analog devices, we say that they are fully asynchronous. Notice that designing with asynchronous clocks is dierent from what is usually called asynchronous designs. This name is used for something totally dierent, i.e. designs that uses no clock at all [9, 11].

13.2.3

Globally Asynchronous Locally Synchronous (GALS) design

For multiclock design, the freedom for complex relative positioning of clock edges leads to much larger state spaces and combinatorial issues than for single-clock design. Therefore, multiclock design is intrinsically much more complex. To keep things manageable, one often adopts the Globally Asynchronous Locally Synchronous (GALS) design paradigm: the design is split into single-clocked purely synchronous islands that only communicate through wires and synchronizers. In this way, sequential computations are only performed by single-clocked modules and communication between clock domains is clearly identied. A signal that goes from a clock domain to another clock domain is called a clock-domain crossing signal or cdc signal.

13.3

Multiclock design in Esterel

For Multiclock Esterel, we adopt the GALS design paradigm together with the usual zero-delay model for combinational computation.

13.3. MULTICLOCK DESIGN IN ESTEREL

169

Figure 13.4: derived clock

Figure 13.5: harmonic clocks

Figure 13.6: phase-shifted clocks

Figure 13.7: phase-shifted harmonic clocks (falling edge coincidence is meaningless since only rising edge smatter)

170

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

13.3.1

Overview

We introduce a new kind of signal called a clock, declared using the clock keyword. Clocks can only be used to clock registers in classic modules. No Boolean gates are available for them, and they cannot be declared reg. New clocks can be derived from existing ones by clock gating or clock muxing, using a specic clock denition statement. We add a new multiclock unit declared using the multiclock keyword. A multiclock unit models a GALS system. It has a header similar to that of a module. In addition, it can declare input and output clocks. The multiclock body can declare local signals and clocks. It is composed of concurrent elements that can be as follows: A classic module clocked by an explicitly given clock, which can be an input clock or a derived clock. A combinational signal computation, which is assumed zero-delay and thus independent from any clock. Recursively, another multiclock unit instantiated with appropriate bindings for data clocks and signals. A clock gater used to derive an output or local clock from another clock and a signal, by masking source clock rising edges when the signal is false. A clock multiplexer that builds a clock from two other clocks using a signal to select between them. Only multiclock units can deal with clocks, while only module units can perform computations, thus achieving the GALS separation of concerns. Multiclock units can declare local signals and clocks. They are data-generic exactly in the same way as module units. Here is an example of a multiclock design based on M1 and M2 presented in Section 13.1.1. Each module is driven by a dierent clock and their intputs and outputs are connected by the multiclock unit MC:
module M1 : output O1 ; loop emit O1 ; pause ; pause ; end loop end module

module M2 : input I2 ; output O2 ; loop await pre ( I2 ); await pre ( I2 ); emit O2 end loop end module

13.3. MULTICLOCK DESIGN IN ESTEREL

171

Figure 13.8: The MC multiclock structure


m ul ti c lock MC : input { C1 , C2 } : c l o c k ; output X ; signal S in run M1 [ c l o c k C1 ; S / O1 ] || run M2 [ c l o c k C2 ; S / I2 , X / O2 ] end s i g n a l end m ulticlock

Here, S is a clock-domain crossing signal since it goes from M1 clocked by C1 to M2 clocked by C2. The MC multiclock unit structure is pictured in Figure 13.8. The local signal S is connected to the output O1 of M1 and to the input I2 of O2. Connection is direct, which means that O2, S, and I2 have exactly the same value over time. Semantically speaking, multiclock units deal with signals by broadcasting them in zero delay, exactly as for modules. Although emission is zero-delay, we must deal with a richer time model to explain how signals are received by other modules. An emitted signal S is kept high for a full clock period of their master clock C, so that this signal can be sampled by modules functioning on other clocks at any time between two successive rising edges of C. In other words, we must promote the continuous time model used for waveforms in Section 13.1.1. Multiclock behavior of MC Even for such a simple multiclock design, behavior needs to be studied with great care. While individual behaviors of M1 and M2 are quite simple, their multiclock combination is not.

172

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

In Figure 13.9, we picture the behavior of MC for the case of 3:4 harmonic clocks described in Section 13.2.2. In Figure 13.10, we picture the behavior of MC for the case of 3:4 phase-shifted harmonic clocks. The clocks do not have a half-cycle duty period and their falling edges shortly follow their rising edge; this is no issue since only the position of rising edges matters. In the waveforms, observe that O1 is emitted every other rising edge of C1, while O2 is emitted every other time I2 is true when sampled on O2 rising edges. An emitted signal is kept high for the whole clock cycle of the module that emits it. Notice that a slight phase shift makes the initial single O2 event of the harmonic case disappear, all O2 events now going by pairs. With these two clocking schemes, timing closure is quite simple to achieve and there is no real risk of metastability: rising edges of C1 and C2 are either simultaneous, which is no problem, or separated by a well-determined minimal amount of time which must simply be bigger than the register output setup time2 . With less constraints clocking schemes, there may be a risk of metastability for the pre(I2) register if O1 is changing during the rising edge of C2. In that case, one must use a more clever synchronizing scheme discussed below.

13.3.2

The importance of input sampling

In MC, the intermediate signals O1 = S = I2 and X = O2 are well-clocked: O1 is clocked by C1, i.e. remains low or high for full clock cycles of C1, while O2 is clocked by C2, i.e. remains low or high for full clock cycles of C2. For O2, this follows from the use of pre(I2) to sample I2 = O1 on rising edges of C2, which acheives reclocking of O1 from C1 to C2. In circuit terms, pre(I2) inserts a sampling register on the input line, as pictured in Figure 13.2. Notice that pre(I2) performs reclocking on C2 because it is written in the body of M2 which is run with clock C2. No explicit mention of the actual clock is needed for the pre operator which is always clocked by its modules implicit clock. It is interesting to see what happens if I2 is directly used in M2 instead of the sampled pre(I2), as in the following code:
module M2_no_sampling : input I2 ; output O2 ; loop await I2 ; await I2 ; emit O2 end loop end module

The new behaviors are presented in Figure 13.11. The output X is not well-clocked any more and becomes much wilder: X = O2 is the conjunction of O1, clocked by C1, and of the output of the SA register, clocked by C2. The combinatorial complexity of the relative placements of C1 and C2 rising edges shows in full force, with a large dierence between the harmonic and phase-shifted harmonic clocking cases. Futhermore, the behavior shown in Figure 13.11 is given in the logical zero-delay framework. Things are much worse in the real world because of electrical delays in the combinational logic. Using X as an output of another design would becomes really problematic.
Ignoring more subtle timing issues that are out of the scope of this manual and well-handled by synthesis systems.
2

13.3. MULTICLOCK DESIGN IN ESTEREL

173

Using samplers is a good design principle that makes signals well-clocked and modules nicely composable. However, this principle is not enforced by the Esterel language.

13.3.3

Synchronizers

Input sampling is so important that it is usually not done by the receiver module M2 but by isolated specic modules called synchronizers. Here is the way in which one would really write MC in practice, here with a simple synchronizer made of one single register:
module M1 : // as before module Synchronizer : input I ; output O : reg ; s u s t a i n next O <= I end module module M3 : input I2 ; // assumes input already reclocked output O2 ; loop await I2 ; await I2 ; emit O2 end loop end module m ul ti c lock MCsync : input { C1 , C2 } : c l o c k ; output X s i g n a l O1 , I2 i n run M1 [ c l o c k C1 ] || run Synchronizer [ c l o c k C2 ; O1 / I , I2 / O ] || run M3 [ c l o c k C2 ] end s i g n a l end m ulticlock

The MCsync multiclock unit structure is pictured in Figure 13.12. The M3 circuit is exactly as theM2 circuit pictured in Figure 13.2 except that the register on the I2 input has been removed. The register is now placed in the synchronizer, which is equivalent but often preferred in practice. The new structure is pictured in Figure 13.12. Notice that we use a registered output instead of a pre operator for Synchronizer. This is equivalent but more readable since registration is more explicit. When clocks schemes are less constrained, one usually places two registers in a row in the synchronizer to decrease the probability of I2 being metastable:

174

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

Figure 13.9: behavior of MC with harmonic clocks

Figure 13.10: behavior of MC with phased-shifted harmonic clocks

Figure 13.11: behavior of MC without input sampling

Figure 13.12: MCsync multiclock structure

13.3. MULTICLOCK DESIGN IN ESTEREL

175

Figure 13.13: peripheral retiming of M2


module Synchronizer2 : input I ; output O : reg ; s i g n a l Aux : reg i n sustain { next Aux <= I , next O <= Aux } end s i g n a l end module

But this is still quite unsafe, and one may have to resort to more complex handshake synchronizers. This is discussed in depth in [8]. Electrically speaking, it is also better to only compose modules whose output signals are all true circuit register outputs to ensure that they are well-clocked and available as early as possible. This is not true for the M2 circuit pictured in Figure 13.2. However, an easy forward retiming can transform the circuit into the equivalent circuit pictured in Figure 13.13, where O2 has become a direct register output. See [10] for details on retiming.

13.3.4

Clock denition

A multiclock unit can derive new clocks from old ones using signals. This is performed by the clock denition mechanism, which has the form of a restricted sustain statement using the clock keyword. Left-hand-sides are clocks names. Righ-hand sides are clock names followed by if conditions that test a single pure signal, or mux clock expressions that select a clock according to the value of a single pure signal. Clock derivation is very useful to shut down parts of a design to save power:
module Compute : output MemoryNeeded ; ... end module

176

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

m ul ti c lock PowerSaver : input c l o c k C ; ... s i g n a l MemoryNeeded , MemoryClock : c l o c k i n run Compute [ c l o c k C ] // computC . E . Leiserson and J . B . Saxe . Retiming synchron es MemoryNeeded || c l o c k MemoryClock <= C i f MemoryNeeded || run Memory [ c l o c k MemoryClock ] end s i g n a l end m ulticlock

Complex tests for clock derivation must be placed in separate sustain statements, that must be combinational, i.e. involving no pre operators. Here is an example of local and output clock derivation using an auxiliary signal:
m ul ti c lock ClockDerivation : input { Cin1 , Cin2 } : c l o c k ; output Cout : c l o c k input I , J ; s i g n a l Cloc : clock , S , IandJ i n run M1 [ c l o c k Cin ; I / In , S / Out ] || s u s t a i n IandJ <= I and J || clock { Cout <= mux( IandJ , Cin1 , Cin2 ) Cloc <= Cout i f S } || run M2 [ c l o c k Cloc ] end m ulticlock

Semantically speaking, if and mux in clock denitions are logical operators specic to clocks. When generating hardware, they are implemented using special cells to ensure clean electrical behavior and avoid clock glitches. In practice, for true hardware multiclock design, it is not recommended to use signals that are outputs of combinational logic as clock gaters or clock multiplexer drivers. It is much better to use clean signals that are primary inputs of the whole design or direct outputs of registers generated in one of the submodules. Timing closure and glitch avoidance is made easier. However, this is not enforced by the Esterel v7 language.

13.3.5

Hierarchical design

Hierarchical multiclock design is pictured in Figure 13.14, without mention of communication signals to make things simpler. The main multiclock unit Multi inputs two clocks C1 and C2. It invokes another multiclock unit MultiSub, passing the clocks to it, and it runs a classic module Mod1 with clock C1. The multiclock unit MultiSub runs a module Mod2 driven by clock C2 and a module Mod3 driven by clock C1. All the registers of Mod1 and Mod3 are clocked by C1, while all the registers of Mod2 are clocked by C2. The multiclock units Multi and MultiSub generate no other registers than those of their leaf modules. Signal renaming is as for modules. Clock renaming is available for multiclock units:

13.3. MULTICLOCK DESIGN IN ESTEREL

177

Figure 13.14: hierarchical multiclock design

178

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN


m ul ti c lock Multi2 : input C1 : Clock ; ... s i g n a l C2 : c l o c k i n Multi [ C / Cin , C2 / C2 / Cout , ...] ... end s i g n a l end m ulticlock

It is a simple name substitution operation, akin to signal renaming. Clock renamings appear writing the signal renaming list. This is very dierent from running a module with a given clock, which determines how the module state changes are performed; this is why the clock keyword does not appear in the renaming. It is impossible to rename a clock for a classic module, which has no knowledge of the existence of clocks, and it is impossible to clock a multiclock unit, which has no executable sequential actions by itself.

13.4

Semantics of multiclock designs in Classic Esterel

The new multiclock language requires no fancy new semantical tool for its semantics to be dened. A multiclock design can be easily translated into a single-clocked one, and that is enough to dene its semantics.

13.4.1

Replacing clocking by weak suspension

The idea is to view clocks events as normal signal status presence events. For this, one introduces a new simulation Classic Esterel toplevel module associating input signals with input clocks. The tick of this module is used as a ctitious simulation tick, fast enough to observe all events in the multiclock design. Remember that the role of clocks is to tell when states should change. The same can be done with Classic Esterel signals using the weak suspend statement described in Section 8.12.1. Here is how to translate the MC design of Section 13.3.1:
module MC_sim : input C1_sig , C2_sig ; // clock signals output X ; signal S in weak suspend run M1 [ S / O1 ] when immediate not C1_sig || weak suspend run M2 [ S / I2 , X / O2 ] when immediate not C2_sig end s i g n a l end m ulticlock

It is easy to check that the behavior of MC_sim exactly mimics that of MC. This simple macro-expansion process is enough to dene the semantics of multiclock designs. It is automatically performed by the Esterel compiler when needed.

13.4. SEMANTICS OF MULTICLOCK DESIGNS IN CLASSIC ESTEREL

179

13.4.2

The mcrun statement

To avoid the manual insertion of weak suspend statements, Esterel provides the user with a new mcrun statement that runs a multiclock unit within a module, passing signals to clocks. Using mcrun, the MC_sim simulation module of Section 13.4.1 is very simply written as follows:
module MC_sim : input C1_sig , C2_sig ; // original clocks changed into signals output X ; mcrun MC [ C1_sig / C1 , C2_sig / C2 ] end m ulticlock

Then MC_sim can be run in any Classic Esterel simulator. Here is a single-clocked execution of MC_sim with harmonic clocks simulating the multiclock execution of Figure 13.9:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: C1 C2 C2 C1 C2 C1 C2 C1 C2 C2 C1 C2 C1 C2 C1 C2 X X X X X X // // // // // // // // // // // // // // // // // // // // // // // // O1 = I2 pre ( I2 ) pre ( I2 ) pre ( I2 ) O1 = I2 O1 = I2 O1 pre ( I2 ) O1 pre ( I2 ) pre ( I2 )

O1 = I2 O1 = I2 O1 = I2 O1 = I2 pre ( I2 ) pre ( I2 ) pre ( I2 )

O1 = I2 O1 = I2 pre ( I2 ) O1 = I2 pre ( I2 ) O1 = I2 pre ( I2 )

This discrete-time single-clock simulation based on the ctitious simulation tick is equivalent to the continuous-time simulation shown in Figure 13.9. The blank events in the above simulation scenario do not provoke output or state change. They could be suppressed, but the reading of clock relative frequencies would be much harder. The relation between a signal and the clock it simulates is simple: There is a clock rising edge for the clock at any simulation tick where the signal is present. Thus, intuitively, the signal acts as a clock gater on the simulation clock to generate the clock of interest. In hardware, mcrun instantiates appropriate clock gaters.

180

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

13.4.3

Simulating clock generation

To study a particular clock pattern, it is often much simpler to generate the clocks in Esterel than to write the testbenches by hand. Here is an easy simulation of MC for the 3:4 phase-shifted harmonic case:
module MC_sim_3_4 : output X ; s i g n a l C1_sig , C2_sig i n loop emit C1_sig each 4 t i c k || pause ; // phase - shift loop emit C2_sig each 3 t i c k || mcrun MC [ C1_sig / C1 , C2_sig / C2 ] end s i g n a l end module

13.4.4

Simulating metastability issues

Although metastability issues are not handled in Esterel, they can be simulated (at logical level only) for verication purposes. One can for example simulate potential wrong sampling by the rst register of a bit synchronizer using an oracle:
module LossyBitSynchronizer : input I ; output O ; signal S in o r a c l e Oracle i n sustain { S <= pre ( I ) xor Oracle , O <= pre ( S ) } end o r a c l e end s i g n a l end module

Then, the Esterel verication engine will try all oracle congurations.

13.5

Hardware synthesis from multiclock designs

There are two ways to synthesize hardware from a multiclock Esterel design: true multiclock synthesis and simulated multiclock synthesis.

13.5.1

True multiclock synthesis

True multiclock synthesis generates a multiclock circuit from a multiclock Esterel design. Synthesis consists in individually synthesizing HDL code for each executable module, and synthesizing a global HDL wrapper to pass the clocks and link the signals as specied in the multiclock unit. No register is generated by this wrapper. Clock gater and clock muxer library modules are needed for clock operations. Synchronizers may require a specic treatment: although they can be programmed as standard

13.5. HARDWARE SYNTHESIS FROM MULTICLOCK DESIGNS

181

Esterel modules that can be directly synthesized, it can be better for circuit performance to replace them by specic library gates that have better electrical characteristics than the synthesized version. This is easy using modular compilation of Esterel programs.

13.5.2

Simulated multiclock synthesis

The other way is to synthesize a single-clock design that mimics operation of the multiclock design. This is very useful for FPGA simulation of multiclock design. The idea is exactly the same as in Section 13.4.1: using a simulation clock, one can directly implement the MC_sim simulation design in hardware or FPGA. Clock-mimicking signals can be generated in Esterel as in Section 13.4.3 or using any clock gating device available in the technology. The advantage of simulated multiclock simulation is that one can simulate or execute the multiclock design on a single-clocked platform without changing a line of code.

13.5.3

Dealing with falling-edge clocks

All our circuit implementation examples were given with rising edge clocks. At Esterel logical level, what matters is only when clock events occur, and whether they are rising or falling edge events is simply a question of HDL implementation. When dealing with a falling-edge clock, simply think of its negation, which is a rising-edge clock that has clock events at the same time. Dealing with designs that react to both rising edges and falling edges of the same clock is more problematic and we do not plan to model it for the time being.

182

CHAPTER 13. INTRODUCTION TO MULTICLOCK ESTEREL DESIGN

Chapter 14

Esterel clocks and multiclock units

14.1
14.1.1

Clocks
Clock declarations

A clock is a signal declared by a specic declaration C : clock, which can appear only in a multiclock unit. A clock can be declared input or output in the multiclock unit header or local in the multiclock unit body. All clocks are pure (i.e. not valued). Clock arrays and inputoutput clocks are not available. Clock declarations can be mixed with other signal declarations. Here are examples:
m ul ti c lock Multi : input I ; input C : c l o c k ; output { C1 , C2 } : c l o c k ; s i g n a l S : unsigned , C3 : c l o c k i n ... end s i g n a l end m ulticlock // // // // // standard pure input input clock output clocks standard valued local signal local clock

Any output or local clock declared in a multiclock unit must be uniquely dened by a clock equation, see Section 14.2.5.

14.1.2

Clock usage

Clocks can only be used in multiclock units. They cannot occur in modules. Their usage is retricted to the following cases: In right-hand sides of clock equations, to derive other clocks. See Section 14.2.5. In module clocked run instantiations of the form run M [clock C; ... ]. Such statements can only appear in multiclock unit bodies. They dene the clock for the modules state change. See Section 14.2.6. In clock renamings in multiclock run instantiation, which can only appear in other multiclock unit bodies. See Section 14.2.7. 183

184

CHAPTER 14. ESTEREL CLOCKS AND MULTICLOCK UNITS In signal-to-clock bindings for mcrun Multi [ S / C ...] statements that run multiclock units inside modules. See Section 14.2.8.

Unlike pure signals, clocks cannot be used in signal expressions in right-hand-sides of signal or clock equations.

14.2

Multiclock units

A multiclock unit starts with multiclock and ends with end multiclock. It is composed of a header and a body that bear many similarity with module headers and bodies.

14.2.1

Multiclock headers

A multiclock header declares data and interface signals and extend data and interface units exactly as for a module, except for restrictions mostly due to the absence of registers generated by a multiclock unit: All declared valued signals are implicitly declared temp. When interfaces are extended, all imported signals are made temp. Signal mem, reg and init declarations and renements are not available. In addition to data and signals, multiclock units can also declare clocks using clock declarations C : clock, see Section 14.1.1. Clock declarations can be mixed with signal declarations. Multiclock units can be data-generic in the same way as module units, and generic data instantiation is done exactly in the same way as for modules. See Section 2.2.2 for details. Here is an example of a multiclock header:
data D : g e n e r i c constant N : unsigned ; end data

i n t e r f a c e Intf : input I : unsigned ; output O : bool [4]; end i n t e r f a c e m ul ti c lock Multi : extends data D ; extends Intf ; input { C1 , C2 } : c l o c k ; output O , C3 : c l o c k ; ... end m ulticlock

// multiclock unit becomes generic // I and O are imported as temp // declaration grouping is as usual // mix of signal and clock declarations

14.2. MULTICLOCK UNITS

185

14.2.2

Multiclock unit extension

As for a module, the declarations of a multiclock unit Multi can be imported in another module or multiclock unit using extends Multi. If extends Multi appears in a multiclock unit Multi2, all declarations of Multi are imported in Multi2, including clock declarations. If extends Multi appears in an interface unit or a module unit, only the data and signals declared in Multi are imported, clock declarations being discarded. If extends Multi appears in a data unit, only the data components of Multi are imported, signals and clock declarations being discarded. To import only the data components of Multi in another unit, including in a multiclock unit, one can use extends data Multi. To import only the data and signal components of Multi in another unit, including in a multiclock unit, one can use extends interface Multi. Notice that there is no multiclock interface unit. This is generally not a problem since clocks are far less numerous than signals. Because of the way multiclock unit extension is dened, multiclock units with body nothing can play the role of multiclock interface units.

14.2.3

Multiclock body structure

The body of a multiclock unit can be either nothing, in which case the unit has no behavior, or composed of the following elements: Local signal declarations, including local clock declarations. Combinational sustain statements, detailed in Section 14.2.4. Clock equations, detailed in Section 14.2.5. Module instantiations, detailed in Section 14.2.6. Multiclock instantiations, detailed in Section 14.2.7. Parallel compositions of the above elements. Here an example of multiclock unit body:
signal C : clock in s u s t a i n X <= I or J || c l o c k C <= Cin i f X || signal S in run Mod [ c l o c k C ] || run Multi2 end s i g n a l end s i g n a l

186

CHAPTER 14. ESTEREL CLOCKS AND MULTICLOCK UNITS

14.2.4

Combinational computations in multiclock units

Combinational computations in multiclock units are simply sustain statements where next is disallowed in the left-hand-side and pre is disallowed in the right-hand-side expressions. Besides these restrictions, all the features of sustain statements are allowed, see Section 8.4. Here are legal multiclock combinational computations:
sustain X || s u s t a i n ? Y <= 1 i f I || sustain { if case A do X <= I case B do ? T <= 1 i f C d e f a u l t do ? Y <= 2 end i f }

Notice that we use sustain, which acts permanently as does hardware combinational logic, instead of emit, which would act once and terminate. There is no control ow in multiclock units, and only lasting operations are allowed. As mentioned in Section 14.1.2, clocks cannot appear in multiclock combinational computation expressions.

14.2.5

Clock equations

Clock equations dene local or output clocks in function of other clocks and signals. Each output or local clock must be dened by exactly one clock equation. A clock equation or equation set starts with the clock keyword and contains a comma-separated list of clock denitions. There are three kinds of clock denitions: Clock synonym: a synonym denition C2 <= C1 denes clock C2 as a synonym for C1. Clock downsampling: a downsampling denition C2 <= C1 if S denes the clock C2 as the subsampling of C1 by the signal S. In the discrete multiclock semantics, C2 has a clock event at any global tick where C1 has a clock event and S is present. Notice that the if test is limited to a single pure signal. If a more complex expression is needed, declare a local signal and use a combinational denition for it. But, in hardware implementation, beware of timing closure issues for this signal. Clock muxing: a muxing denition C3 <= mux(S, C1, C2) denes the clock C3 as the mux of of C1 and C2 according to presence of S. In the discrete multiclock semantics, C3 has a clock event whenever S is present and C1 has a clock event or S is absent and C2 has a clock event. As for clock downsampling, the mux test is limited to a single pure signal. Here are examples of clock equations:

14.2. MULTICLOCK UNITS


c l o c k C2 <= C1 i f I || clock { C3 <= C1 , C4 <= mux (J , C2 , C3 ) }

187

For a clock downsampling or muxing, it is semantically safe for the signal S to change status exactly when the selected clock front occurs: the new status of S is then taken into account as always in the 0-delay model. However, such an operation would almost certainly lead to metastability if actually synthesized to hardware. In practice, controlling the change time of S w.r.t. the edges of the argument clocks is critical for correct electrical behavior.

14.2.6

Running modules in multiclock units

To achieve GALS behavior, a classic module unit can be instantiated within a multiclock unit body using the run statement. This statement is as for module instantiation in other modules, except that a clock must be explicitly passed using a clock C; specication in the substitution list:
run M [ c l o c k C ; constant 2 / N ; X / I, Y / O ] // mandatory first argument // usual data substitution // usual signal binding

The only place where a clock can occur is the clock specication. Refer to Chapter 9 for data and signal substitution, which is unchanged. Implicit substitution by equal names is of course available. The behavior of M is run with clock C to clock Ms state. Signal binding is always done by connection, see Section ??.

14.2.7

Running multiclock units in multiclock units

To achieve hierarchical multiclock design, a multiclock unit body can instantiate another multiclock unit using the run statement. In multiclock run, clocks can be bound to clocks in the same way as signals are bound to signals. Unlike for module run, no clock is passed as mandatory rst argument since a multiclock unit has no sequential computation per se. Here is an example:
run Multi [ constant 3 / N ; C1 / C2 , X / I ] // usual data renaming // clock renaming // usual signal binding

Notice that clock binding can occur anywhere in the signal binding section since it is a normal signal binding.

14.2.8

Running multiclock units in modules

Finally, a multiclock unit can be instantiated in a module body using the mcrun statement, which has the same form as a run statement. All input and output clocks ot the instantiated multiclock unit must be bound to pure signals in the caller. The other data and signal objects are bound as usual. Here is an example:

188

CHAPTER 14. ESTEREL CLOCKS AND MULTICLOCK UNITS


m ul ti c lock Multi : constant N : unsigned ; input CI : c l o c k ; output CO : c l o c k ; input I [ N ]; output O ; ... end m ulticlock

module Mod : output SO ; input I [3]; output X ; s i g n a l SI i n loop emit SI each 2 t i c k || mcrun Multi [ constant 3 / N ; s i g n a l SI / CI , SO / CO , X / O ]

// SI generates events of CI // SO records events of CO

See Section 14.2.8 for the semantics of mcrun and for its used in multiclock design debugging.

Bibliography
[1] System Verilog for design: A guide to using system verilog for hardware design and modeling. [2] Esterel Studio v5.3, user guide and reference manual. Esterel Technologies, 2002. [3] G. Berry. Esterel on hardware. Philosophical Transactions Royal Society of London A, 339:87104, 1992. [4] G. Berry. The Esterel v5 91 Primer. http://wwww.esterel-technologies.com, 1998. [5] G. Berry. The constructive semantics of Pure Esterel. technologies.com, 2000. http://wwww.esterel-

[6] G. Berry. The foundations of Esterel. In G. Plotkin, C. Stirling, and M. Tofte, editors, Proof, Language, and Interaction: Essays in Honour of Robin Milner. MIT Press, 2000. [7] G. Berry and H. Touati. Optimized controller synthesis using esterel. In Proc. Intl. Workshop on Logic Synthesis, Lake Tahoe, USA, 1993. [8] Ran Ginosar. Fourteen ways to fool your synchronizer. page 89, 2003. [9] A. Kondratyev L. Lavagno J. Cortadella, M. Kishinevsky and A. Yakovlev. Logic Synthesis of Asynchronous Controllers and Interfaces. [10] C.E. Leiserson and J.B. Saxe. Retiming synchronous circuitry. Algorithmica, 6(1), 1991. [11] A. R. Taubin M. A. Kishinevsky, A. Y. Kondratyev and V. I. Varshavsky. Concurrent Hardware. The Theory and Practice of Self-Timed Design. [12] David G. Messerschmitt. Synchronization in digital design. 8:14041419, 1990. [13] K. Schneider. Embedding imperative synchronous languages in interactive theorem provers. In Proc. Conference on Application of Concurrency to System Design (ACSD), 2001. [14] E.M. Sentovich, K.J. Singh, L. Lavagno, C. Moon, R. Murgai, A. Saldanha, H. Savoj, P.R. Stephan, R.K. Brayton, and A.L. Sangiovanni-Vincentelli. Sis: A system for sequential circuit synthesis. Technical report, University of California at Berkeley, 1992. Memorandum No. UCB/ERL M92/41. [15] O. Tardieu. Loops in Esterel. PhD. Thesis, Ecole des Mines de Paris and INRIA, 2004. 189

Index
*, 70, 88 signed, 41 unsigned, 36 **, 49, 88 unsigned, 37 +, 36, 40, 41, 70, 88 -, 41, 88 unsigned, 36 error, 157 /, 88 error, 37, 41, 157, 158 signed, 41 unsigned, 37 <, 88 signed, 43 unsigned, 39 <<, 44, 88 <<<, 44, 88 <=, 88 signed, 43 unsigned, 39 <=>, 88 <>, 44, 88 signed, 43 unsigned, 39 =, 44, 88 signed, 43 unsigned, 39 =>, 88 >, 88 signed, 43 unsigned, 39 >=, 88 signed, 43 unsigned, 39 >>, 44, 88 >>>, 44, 88 #, 88 0, 25, 35 1, 25, 35 b, 32 o, 32 x, 32 :=, 95 ;, 108 and ||, 113 ??, 123 || and ;, 113 _, 5, 27, 31 0b, 26 0d, 26 0o, 26 0x, 26 abort, 118, 119, 133 Abortion abort, 118 by trap, 122 run, 141 strong, 118 weak, 118 weak abort, 118 abs, 43 Absent signal, 52 Abstract type, 25 Addition signed, 41 unsigned, 36 always, 110 and, 70, 88 Arithmetic exact, 26 unsigned, 26 Array bitvector, 31 data, 30 expression, 85 190

INDEX index out of bounds, 40 indexation, 90 indirect type, 30 literal, 31 named type, 30 next, 65, 66 of ports, 75 of signals, 65 of variables, 66 operator extension, 89 pre, 65, 66 pre1, 65 slice, 85, 86 test, 85 type equality, 30 unnamed type, 30 assert, 97, 99 scope, 99 assert<>, 48 signed, 43 unsigned, 39 assert_unsigned<>, 43 Assertion, 51 Assignment, 95 Asynchronous, 168 GALS, 168 Attribute interface, 71 module, 71 renement, 72 await, 94, 117, 120 bin2s, 27 bin2u, 26, 49 Binding by name, 144 clock, 187 expression, 144 implicit, 142 input, 144 inputoutput, 149 null, 142, 143, 150 output, 147 port, 149 signal, 143 Binding null, 5 binsize, 25, 90 error, 157 for signed, 42 unsigned, 37 Bitvector, 31 <<, 44 <<<, 44 >>, 44 >>>, 44 concatenation, 44 equality, 44 extend, 45 lcat, 44 literal, 31 map, 46 mcat, 44 reverse, 45 reverse, 45 sextend, 45 shift, 44 onehot2u conversion error, 158 Bitvectors vs. unsigned, 26 bool, 25, 35 false, 25 true, 25 bool2u, 5, 25, 35 Broadcasting, 113 call, 34, 95 case, 102, 112, 119 Clock, 161 asynchronous, 168 binding, 187 denition, 175 derived, 168 downsampling, 186 equation, 186 falling edge, 181 gated, 167 harmonic, 168 mux, 175 muxing, 186 phase-shifted, 168 renaming, 176 rising edge, 161 subsampling, 175 synonym, 186 clock, 170, 175, 183, 184

191

192 Code for unsigned, 47 Combination function, 64 combine, 70, 72 for arrays, 64 Combined signal, 64 Combined signal, 52, 54, 99 Concatenation bitvector, 44 Conditional emit, 102 Connection registered signal, 60 standard, 57 Constant, 32 dened, 32 generic, 33 host, 32 indexation, 90 signed, 27 signed tight, 28 substitution, 143 unsigned tight, 27 constant, 32, 33 Counter, 111, 116 Data array, 30 constant, 32 dened, 9 example, 10 extension, 9 function, 33 generic, 9, 142 host, 9 procedure, 34 type, 25 unit, 9 data, 184 Declaration variable, 66 Decoding, 47 default, 102, 113 Dened constant, 32 type, 25, 28, 29 Delay count, 116 immediate, 116118, 121 non-immediate, 121 simple, 115 Derived clock, 168 Designator status, 96 value, 96 Dimension, 65 expression, 85 full, 87 Division signed, 41 unsigned, 37 do, 100, 102, 112, 117119 dodown, 104 dopar, 114 double, 28 doup, 104

INDEX

each, 121 else, 102, 112 Emission concurrent, 100 simple, 97, 98 emit, 52, 53, 9698 conditional, 5, 9799, 102 unconditional, 9799 valued, 98 valued array, 99 emit next, 58 Encoding, 47 indexation, 91 end, 93 Enum type, 28 enum, 28 Equality bitvector, 44 Error array out of bounds, 40, 159 binsize, 157 division, 37, 41, 157, 158 mod, 37, 157 onehot2u, 158 out of range, 39, 43, 157, 158 run-time, 39, 43, 55, 64, 67, 68, 157 single, 64, 159 subtraction, 157

INDEX temp, 64, 159 uninitialized signal, 55, 158 uninitialized variable, 67, 159 unsigned size, 39 variable sharing, 68, 159 every, 121 Example basic, 10 data instantiation, 11, 13 generic data, 13 genericity, 11, 13 memory, 12 partial instantiation, 20 port, 14 run, 14 substitution, 11, 13 exit, 122 Expression array, 85 binding, 144 dimension, 65, 85 indexation, 90 signal, 124 simple, 85 static, 32 statically evaluable, 85 test, 85 extend, 45 extends, 76, 135, 184 data, 10 interface, 10 extends data, 24 extends interface, 24 Extension multiple, 23 false, 35 finalize, 133 Finalizer, 133 float, 28 for...dopar, 114 for...do, 100 Full signal, 52, 53 Function, 33 combination, 64 generic, 34 host, 33 predened, 33 substitution, 143 function, 33, 34 GALS, 161, 168 Generic constant, 11, 33 dened, 13, 19 free, 13 function, 34 module, 19 procedure, 34 type, 25, 32 generic, 19, 33, 34 Genericity and extension, 11, 20 chaining, 18 halt, 94 handle, 122, 123 Handshake, 167 Harmonic clock, 168 Host constant, 32 function, 33 procedure, 34 type, 25, 29 host, 29, 3234 Identier, 5, 6 if, 5, 100, 102, 112, 137 in emit, 9799 in emit / sustain, 102 in sustain, 9799 immediate, 116118, 121, 125 Incarnation, 137 Indexation Constant, 90 full, 85 partial, 86 port, 88 reindexation, 86 renumbering, 86 Signal, 91 slice, 85, 86 Variable, 90 init, 55, 59, 70, 72 Initial value variable, 67 Initialization

193

194 of temp, 63, 67 signal, 52, 55, 59 variable, 67 Input binding, 144 input, 69, 73, 183 relation, 80 Inputoutput binding, 149 inputoutput, 69, 70, 73 Instantiation implicit, 18 of generic, 20 signal, 15 integer, 27 Interface attribute, 71 denition, 73 example, 10 extends data, 10 extension, 9, 75 generic, 9 mirror, 9, 74 port, 74 relation, 79 signal, 51 simple, 73 unit, 9 interface, 184 Iterator, 114 Keyword, 5, 6 lcat, 44 Lifetime of signal, 56 Literal bitvector, 31 signed, 27 unsigned, 26 Local signal, 51 variable, 66 Loop always, 110 non-instantaneous, 109 loop, 109, 121, 137 lsb, 43 main, 9 Map bitvector, 46 map, 46 mcat, 44 mcrun, 179, 187 mem, 72, 184 Memory, 82 example, 12 Metastability, 166 mirror, 74, 75, 8082 Mirror interface, 74 mod, 88 error, 37, 157 unsigned, 37 Module attribute, 71 body, 9 compact form, 10 example, 10 extends interface, 10 extension, 9 generic, 19 header, 9, 82 main, 9 substitution, 143 unit, 9 Modulo unsigned, 37, 38 msb, 43 Multiclock metastability, 166 need for, 165 synchronizer, 166 multiclock, 170, 184 Multiplication signed, 41 unsigned, 36 mux, 90, 175, 186 Namespace, 22 units, 9 next, 5860, 101 for array, 65, 66 not, 88 nothing, 94, 185 Null binding, 5, 142, 143, 150 Object

INDEX

INDEX scope, 22 visibility, 22 onehot2u conversion error, 158 open, 14, 75, 138 Operator *, 88 **, 88 +, 88 -, 88 /, 88 <, 88 <<, 88 <<<, 88 <=, 88 <=>, 88 <>, 88 =, 88 =>, 88 >, 88 >=, 88 >>, 88 >>>, 88 #, 88 and, 88 array extension, 89 mod, 88 not, 88 or, 88 xor, 88 or, 70, 88 Oracle, 51 Output binding, 147 output, 69, 73, 183 relation, 80 Parallel, 113 and sequence, 113 and variable, 113 termination, 113 thread, 113 trap propagation, 113 weak abortion, 113 pause, 94 Persistent signal value, 54, 70 variable, 67 Phase-shifted clock, 168 Pipeline, 61 Port, 74 open, 14 binding, 149 example, 14 local, 135 no slicing, 88 open, 75, 138 pre, 88 status test, 88 value read, 88 port array, 75 mirror, 75 positive repeat, 112 Power unsigned, 37 pre, 56, 57, 86 and suspend, 136 for array, 65, 66 nesting, 56 no nesting, 87, 88 of port, 88 value, 88 pre1, 56, 86 for array, 65 Predened function, 33 Preemption, 109 Present signal, 52 Primitive type, 25, 27, 28 Priority between traps, 122 by nesting, 119 in case list, 113, 119 Procedure, 34 call, 95 generic, 34 host, 34 substitution, 143 procedure, 34 Pure signal, 52, 53 Quasi-synchronous, 168 refine, 72, 82, 148 memmem, 72

195

196 regreg, 72 reg, 58, 70, 72, 151, 184 connection, 60 from submodule, 60 reg1, 58, 151 Register, 161 metastability, 166 transfer level (RTL), 161 Registered signal, 52, 58 connection, 60 Reincarnation, 137 relation, 79 input, 80 limitations, 81 output, 80 Renaming of dened data, 15 repeat, 111, 114 Reset, 151 strong, 151 weak, 152 Reverse bitvector, 45 reverse, 45 Rising edge, 56 RTL, 161 clock, 161 register, 161 run, 141, 183, 187 abortion, 141 binding, 141 example, 14 instance renaming, 142 null binding, 143 renaming, 141 reset, 151 signal binding, 143 standard connection, 57 strong reset, 151 termination, 141 weak reset, 152 Run-time error, 157 s2bin, 27 sat signed, 42 unsigned, 38 Saturate signed, 42 unsigned, 38 Scope, 22 assertion, 99 of signal, 51 of trap, 122 Sequence, 108 and parallel, 113 sextend, 45 Shift barrel, 44 left, 44 left signed, 44 right, 44 right signed, 44 Signal, 51 absent, 52 array, 65 assert, 51 binding, 142 broadcasting, 113 combination function, 99 combined, 52, 54, 64, 99 concurrent emission, 100 connection, 57, 60 declaration, 69 emit, 97, 98 expression, 124 falling edge, 86 full, 52, 53, 70 incarnation, 137 indexation, 91 init, 55, 59 initialization, 52, 55, 59 input, 73 inputoutput, 73 instantiation, 15 interface, 51, 73 lifetime, 56 local, 51, 75, 135 oracle, 51 output, 73 overview, 51 persistent, 54, 70 pre, 56, 57, 86 pre1, 86 present, 52 pure, 52, 53, 70, 97, 98

INDEX

INDEX registered, 52, 58, 70, 151 reincarnation, 137 rising edge, 56, 86 scope, 51 simple emission, 97, 98 single, 64, 70, 99 single error, 64, 159 standard, 52, 54, 70 status, 52 status test, 86 substitution, 143 temp error, 64, 159 temporary, 52, 56, 62 type, 70 uninitialized, 55, 158 value, 52, 53, 87 value pre, 88 value persistency, 55, 59 value-only, 52, 54 valued, 52, 54, 70, 98, 99 well-clocked, 172 signal, 69, 70, 135, 137, 143 extendsextends, 135 Signal array, 65 next, 65, 66 pre, 65, 66 pre1, 65, 66 value, 65 signal array combine, 64 Signal array status, 65 Signed abs, 43 binary size, 42 comparison, 43 to unsigned, 43 signed, 27 *, 41 +, 40, 41 -, 41 /, 41 assert<>, 43 assert_unsigned<>, 43 constants, 27 Conversion from unsigned, 40 out of range, 43, 158 signed<>, 28 Single signal, 64 Single signal, 99 error, 64, 159 Slice, 85, 86 full, 86 reindexation, 86 renumbering, 86 Standard signal, 52, 54 connection, 57 Statement :=, 95 ;, 108 abort, 118 assignment, 95 await, 117, 120 call, 95 emit, 96 every, 121 exit, 122 halt, 94 if, 112, 137 loop, 109, 121, 137 nothing, 94 parallel, 113 pause, 94 positive repeat, 112 repeat, 111 run, 141 sequence, 108 suspend, 124, 125 trap, 122 weak abort, 118, 122, 123 and trap, 122, 123 weak suspend, 5, 125 Static expression, 32 Status, 52 default, 52 designator, 96 dissymmetry, 53 falling edge, 86 indexation, 91 pre, 86 pre1, 86 rising edge, 86 test, 86 string, 28 Submodule

197

198 register connection, 60 standard connection, 57 Substitution, 143 data, 13 of dened data, 15 Subtraction signed, 41 unsigned, 36 suspend, 124, 125 and pre, 136 suspend immediate, 124 Suspension, 124 delayed, 125 immediate, 125 sustain, 97, 98, 186 conditional, 9799 unconditional, 9799 valued, 98 valued array, 99 Synchronizer, 166 handshake, 167 Synchronous pure, 167 quasi, 168 temp, 62, 70, 184 initialization, 63, 67 input, 144 variable, 67 Temporary signal, 52, 56, 62 value denition, 64 value driving, 64 Temporary variable, 67 Termination run, 141 Test, 85 array, 85 then, 102, 112 Thread, 113 tick, 94 Tight declaration, 27, 28 Timing closure, 161 trap, 122 concurrent, 123 exit, 122 handler, 122, 123 nesting, 122 priority, 122 propagation, 108 scope, 122 type of, 123 valued, 123 true, 35 trunc signed, 42 unsigned, 38 Type, 25 abstract, 25, 29 array, 30 bool, 25 dened, 25, 28, 29 double, 28 enum, 28 float, 28 generic, 25, 32 host, 25 integer, 27 primitive, 25, 27, 28 signed, 27 string, 28 substitution, 143 unsigned, 25 type, 25, 2729, 32 u2bin, 26, 47 indexation, 91 u2code, 47 indexation, 91 u2gray, 47 indexation, 91 u2onehot, 47, 49 indexation, 91 Unary +, 40 -, 41 Unary minus signed, 41 Unary plus of unsigned, 40, 41 signed, 40 Unit data, 9 forgetting components, 24 generic, 11 interface, 9 module, 9

INDEX

INDEX multiclock, 170 name, 9 namespace, 9 reuse, 10 splitting, 10 Unsigned comparison, 39 from signed, 43 unsigned, 25 *, 36 **, 37 +, 36 -, 36 arithmetic, 26 conversion to signed, 40 decoding, 47 encoding, 26, 47 extension using sat, 38 literals, 26 out of range, 39, 157 size check, 39 u2bin, 47 u2code, 47 u2gray, 47 u2onehot, 47 vs. bitvectors, 26 unsigned<>, 27 Value designator, 96 enable, 53 indexation, 91 of signal, 52, 53 signal pre, 88 value signal, 87 value, 54, 70 Value passing from output, 150 from reg output, 151 Value-only signal, 52, 54 Valued signal, 52, 54 var, 67 varvar, 139 Variable, 66, 139 :=, 95 array, 66 assignment, 95 declaration, 66, 67 indexation, 90 initialization, 67 persistent, 67 sharing, 68 sharing error, 68, 159 temporary, 67 uninitialized, 67, 159 unshared, 113 Visibility, 22 Waveform, 163 weak weak, 125 weak abort, 118, 122, 123, 133 weak suspend, 5 when, 118, 119 when immediate, 124, 125 xor, 70, 88

199

You might also like