39c9035949213558237853ce64fa818541424d11
[fleet.git] / src / edu / berkeley / fleet / fpga / verilog / Verilog.java
1 package edu.berkeley.fleet.fpga.verilog;
2 import edu.berkeley.fleet.api.*;
3 import edu.berkeley.fleet.two.*;
4 import edu.berkeley.fleet.*;
5 import java.lang.reflect.*;
6 import edu.berkeley.sbp.chr.*;
7 import edu.berkeley.sbp.misc.*;
8 import edu.berkeley.sbp.meta.*;
9 import edu.berkeley.sbp.util.*;
10 import java.util.*;
11 import java.io.*;
12 import static edu.berkeley.fleet.two.FleetTwoFleet.*;
13
14 public class Verilog {
15
16     public static class SimpleValue implements Value {
17         public final String s;
18         public SimpleValue(String s) { this.s = s; }
19         public SimpleValue(String s, int high, int low) { this.s = s+"["+high+":"+low+"]"; }
20         public Value getBits(int high, int low) { return new SimpleValue(s, high, low); }
21         public Assignable getAssignableBits(int high, int low) { return new SimpleValue(s, high, low); }
22         public String getVerilogName() { return s; }
23         public String toString() { return s; }
24     }
25
26     public static class CatValue implements Value {
27         private final Value[] values;
28         public CatValue(Value[] values) { this.values = values; }
29         public Value getBits(int high, int low) {
30             throw new RuntimeException();
31         }
32         public Assignable getAssignableBits(int high, int low) {
33             throw new RuntimeException();
34         }
35         public String toString() { return getVerilogName(); }
36         public String getVerilogName() {
37             StringBuffer sb = new StringBuffer();
38             sb.append("{ ");
39             boolean first = true;
40             for(int i=0; i<values.length; i++) {
41                 if (values[i]==null) continue;
42                 if (!first) sb.append(", ");
43                 sb.append(values[i].getVerilogName());
44                 first = false;
45             }
46             sb.append(" }");
47             return sb.toString();
48         }
49     }
50
51     public static interface Action {
52         public String getVerilogAction();
53     }
54
55     public static interface Trigger {
56         public String getVerilogTrigger();
57     }
58
59     public static interface Assignable {
60         public String getVerilogName();
61         public Assignable getAssignableBits(int high, int low);
62     }
63
64     public static interface Value extends Assignable {
65         public String getVerilogName();
66         public Value getBits(int high, int low);
67     }
68
69     public static class ConditionalAction implements Action {
70         public String condition;
71         public Action action;
72         public ConditionalAction(String condition, Action action) {
73             this.condition = condition;
74             this.action = action;
75         }
76         public String toString() { return getVerilogAction(); }
77         public String getVerilogAction() { return "if ("+condition+") begin "+action.getVerilogAction()+" end"; }
78     }
79
80     public static class ConditionalTrigger implements Trigger {
81         public String condition;
82         public Trigger trigger;
83         public ConditionalTrigger(String condition, Trigger trigger) {
84             this.condition = condition;
85             this.trigger = trigger;
86             if (trigger instanceof Module.Port)
87                 ((Module.Port)trigger).hasLatch = true;
88         }
89         public String getVerilogTrigger() {
90             return "&& (("+condition+") ? (1 " + trigger.getVerilogTrigger() + ") : 1)";
91         }
92     }
93
94     public static class SimpleAssignable implements Assignable {
95         public final String s;
96         public SimpleAssignable(String s) { this.s = s; }
97         public String getVerilogName() { return s; }
98         public Assignable getAssignableBits(int high, int low) { return new SimpleValue(s, high, low); }
99     }
100
101     public static class AssignAction implements Action {
102         public String left;
103         public String right;
104         public AssignAction(Assignable left, Value right) {
105             this.left = left.getVerilogName();
106             this.right = right.getVerilogName().toString();
107         }
108         public AssignAction(Assignable left, String right) {
109             this.left = left.getVerilogName();
110             this.right = right;
111         }
112         public String getVerilogAction() { return left + "<=" + right + ";"; }
113         public String toString() { return getVerilogAction(); }
114     }
115     
116     public static class SimpleAction implements Action {
117         public final String verilog;
118         public SimpleAction(String verilog) { this.verilog = verilog; }
119         public String getVerilogAction() { return verilog; }
120         public String toString() { return verilog; }
121     }
122
123     public static class Module {
124         public void dump(String prefix) throws IOException {
125             PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/"+name+".v")));
126             dump(pw, true);
127             pw.flush();
128             for(InstantiatedModule m : instantiatedModules)
129                 m.module.dump(prefix);
130         }
131         public int id = 0;
132         public final String name;
133         public String getName() { return name; }
134         public Port getPort(String name) { return ports.get(name); }
135
136         public HashSet<InstantiatedModule> instantiatedModules = new HashSet<InstantiatedModule>();
137         public final ArrayList<Event> events = new ArrayList<Event>();
138
139         // FIXME: always-alphabetical convention?
140         public final HashMap<String,Port> ports = new HashMap<String,Port>();
141         public final ArrayList<String> portorder = new ArrayList<String>();
142         public final HashMap<String,StateWire> statewires = new HashMap<String,StateWire>();
143         public final HashMap<String,Latch> latches = new HashMap<String,Latch>();
144         
145         public StringBuffer crap = new StringBuffer();
146         public StringBuffer precrap = new StringBuffer();
147         //public void addCrap(String s) { crap.append(s); crap.append('\n'); }
148         public void addPreCrap(String s) { precrap.append(s); precrap.append('\n'); }
149         public void addPreCrap0(String s) { precrap.append(s); }
150
151         public Module(String name) {
152             this.name = name;
153         }
154
155         public SourcePort createInputPort(String name, int width) {
156             if (ports.get(name)!=null) throw new RuntimeException();
157             return new SourcePort(name, width, true);
158         }
159         public SourcePort getInputPort(String name) {
160             SourcePort ret = (SourcePort)ports.get(name);
161             if (ret==null) throw new RuntimeException();
162             return ret;
163         }
164         public SinkPort createOutputPort(String name, int width, String resetBehavior) {
165             if (ports.get(name)!=null) throw new RuntimeException();
166             return new SinkPort(name, width, true, resetBehavior);
167         }
168         public SinkPort createWirePort(String name, int width) {
169             if (ports.get(name)!=null) throw new RuntimeException();
170             return new SinkPort(name, width, false, "");
171         }
172         public SourcePort createWireSourcePort(String name, int width) {
173             if (ports.get(name)!=null) throw new RuntimeException();
174             return new SourcePort(name, width, false);
175         }
176         public SinkPort getOutputPort(String name) {
177             SinkPort ret = (SinkPort)ports.get(name);
178             if (ret==null) throw new RuntimeException();
179             return ret;
180         }
181
182         public class StateWire {
183             public final String name;
184             public final boolean initiallyFull;
185             public String getName() { return name; }
186             public Action isFull()  { return new SimpleAction(name+"==1"); }
187             public Action isEmpty() { return new SimpleAction(name+"==0"); }
188             public Action doFill()  { return new SimpleAction(name+"<=1;"); }
189             public Action doDrain() { return new SimpleAction(name+"<=0;"); }
190             public String doReset() { return name+"<="+(initiallyFull?"1":"0")+";"; }
191             public StateWire(String name) { this(name, false); }
192             public StateWire(String name, boolean initiallyFull) {
193                 this.name = name;
194                 this.initiallyFull = initiallyFull;
195                 statewires.put(name, this);
196             }
197             public void dump(PrintWriter pw) {
198                 pw.println("  reg "+name+";");
199                 pw.println("  initial "+name+"="+(initiallyFull?"1":"0")+";");
200             }
201         }
202
203         public class Latch implements Assignable, Value {
204             public final String name;
205             public final int width;
206             public final long initial;
207             public Latch(String name, int width) { this(name, width, 0); }
208             public Latch(String name, int width, long initial) {
209                 this.width = width;
210                 this.name = name;
211                 this.initial = initial;
212                 latches.put(name, this);
213             }
214             public String getVerilogName() { return name; }
215             public Value getBits(int high, int low) { return new SimpleValue(getVerilogName(), high, low); }
216             public Assignable getAssignableBits(int high, int low) { return new SimpleValue(getVerilogName(), high, low); }
217             public String doReset() { return name+"<="+initial+";"; }
218             public void dump(PrintWriter pw) {
219                 pw.println("  reg ["+(width-1)+":0] "+name+";");
220                 pw.println("  initial "+name+"="+initial+";");
221             }
222             public void connect(SinkPort driven) {
223                 driven.latchDriver = this;
224             }
225         }
226
227         public abstract class Port implements Action, Assignable, Trigger {
228             public abstract String doReset();
229             public final String name;
230             public String getName() { return name; }
231             public final int width;
232             public int getWidth() { return width; }
233             public boolean hasLatch = false;
234             public boolean external;
235             public Port(String name, int width, boolean external) {
236                 this.width = width;
237                 this.name = name;
238                 this.external = external;
239                 ports.put(name, this);
240                 if (external)
241                     portorder.add(name);
242             }
243             public String getVerilogName() { return name; }
244             String getAck() { return name+"_a"; }
245             String getReq() { return name+"_r"; }
246             public String isFull() { return "("+name+"_r"+" && !"+name+"_a)"; }
247             public abstract String getInterface();
248             public abstract String getSimpleInterface();
249             public abstract String getDeclaration();
250             public abstract String getAssignments();
251             public abstract void   connect(SinkPort driven);
252         }
253
254         public static class InstantiatedModule {
255             public final Module module;
256             public final Module thisModule;
257             public final int id;
258             public final HashMap<String,Port> ports = new HashMap<String,Port>();
259             public String getName() { return module.getName()+"_"+id; }
260             public InstantiatedModule(Module thisModule, Module module) {
261                 this.thisModule = thisModule;
262                 this.module = module;
263                 this.id = thisModule.id++;
264                 thisModule.instantiatedModules.add(this);
265             }
266             public void dump(PrintWriter pw) {
267                 pw.println("  " + module.getName() + " " + getName() + "(clk, rst ");
268                 for(String s : module.portorder)
269                     pw.println(", " + getPort(s).getSimpleInterface());
270                 if (module.name.equals("dram")) {
271                     pw.println("    , dram_addr");
272                     pw.println("    , dram_addr_r");
273                     pw.println("    , dram_addr_a");
274                     pw.println("    , dram_isread");
275                     pw.println("    , dram_write_data");
276                     pw.println("    , dram_write_data_push");
277                     pw.println("    , dram_write_data_full");
278                     pw.println("    , dram_read_data");
279                     pw.println("    , dram_read_data_pop");
280                     pw.println("    , dram_read_data_empty");
281                     pw.println("    , dram_read_data_latency");
282                 }
283                 if (module.name.equals("ddr2")) {
284                     pw.println("    , ddr2_addr");
285                     pw.println("    , ddr2_addr_r");
286                     pw.println("    , ddr2_addr_a");
287                     pw.println("    , ddr2_isread");
288                     pw.println("    , ddr2_write_data");
289                     pw.println("    , ddr2_write_data_push");
290                     pw.println("    , ddr2_write_data_full");
291                     pw.println("    , ddr2_read_data");
292                     pw.println("    , ddr2_read_data_pop");
293                     pw.println("    , ddr2_read_data_empty");
294                     pw.println("    , ddr2_read_data_latency");
295                 }
296                 if (module.name.equals("video")) {
297                     pw.println("    , vga_clk");
298                     pw.println("    , vga_psave");
299                     pw.println("    , vga_hsync");
300                     pw.println("    , vga_vsync");
301                     pw.println("    , vga_sync");
302                     pw.println("    , vga_blank");
303                     pw.println("    , vga_r");
304                     pw.println("    , vga_g");
305                     pw.println("    , vga_b");
306                     pw.println("    , vga_clkout");
307                 }
308                 pw.println("   );");
309             }
310             public Port getPort(String name) {
311                 return (module.ports.get(name) instanceof SinkPort) ? getOutputPort(name) : getInputPort(name);
312             }
313             public SinkPort getInputPort(String name) {
314                 int width = module.getPort(name).getWidth();
315                 SinkPort port = (SinkPort)ports.get(name);
316                 if (port == null) {
317                     port = thisModule.new SinkPort(getName()+"_"+name, width, false, "");
318                     ports.put(name, port);
319                 }
320                 return port;
321             }
322             public SourcePort getOutputPort(String name) {
323                 int width = module.getPort(name).getWidth();
324                 SourcePort port = (SourcePort)ports.get(name);
325                 if (port == null) {
326                     port = thisModule.new SourcePort(getName()+"_"+name, width, false);
327                     ports.put(name, port);
328                 }
329                 return port;
330             }
331         }
332
333         public class SourcePort extends Port implements Value {
334             public SourcePort(String name, int width, boolean external) { 
335                 super(name, width, external); }
336             public Value getBits(int high, int low) { return new SimpleValue(getVerilogName(), high, low); }
337             public Assignable getAssignableBits(int high, int low) { return new SimpleValue(getVerilogName(), high, low); }
338             public String getVerilogTrigger() { return " && " + getReq() + " && !"+getAck(); }
339             public String getVerilogAction() { return getAck() + " <= 1;"; }
340             public String getInterface() { return getReq()+", "+getAck()+"_, "+name+""; }
341             public String getSimpleInterface() { return getReq()+", "+getAck()+", "+name+""; }
342             public String testBit(int index, boolean value) {
343                 return "("+name+"["+index+"]=="+(value?1:0)+")";
344             }
345             public String getDeclaration() {
346                 StringBuffer sb = new StringBuffer();
347                 if (external) {
348                     sb.append("input "  +                 name +"_r;\n");
349                     sb.append("output " +                 name +"_a_;\n");
350                     if (width>0)
351                         sb.append("input ["+(width-1)+":0]" + name +";\n");
352                     else
353                         sb.append("input [0:0]" + name +";\n"); // waste a bit, I guess
354                 } else {
355                     sb.append("wire "  +                 name +"_r;\n");
356                     if (width>0)
357                         sb.append("wire ["+(width-1)+":0]" + name +";\n");
358                 }
359                 if (!hasLatch) {
360                     sb.append("wire "    +                 name +"_a;\n");
361                 } else {
362                     sb.append("reg "    +                 name +"_a;\n");
363                     sb.append("initial " +                name +"_a = 0;\n");
364                 }
365                 return sb.toString();
366             }
367             public String doReset() { return hasLatch ? name+"_a<=1;" : ""; }
368             public String getAssignments() {
369                 StringBuffer sb = new StringBuffer();
370                 if (external)
371                     sb.append("assign " +                 name +"_a_ = " + name + "_a;\n");
372                 return sb.toString();
373             }
374             public void connect(SinkPort driven) {
375                 driven.driver = this;
376             }
377         }
378         public class SinkPort extends Port {
379             public SourcePort driver = null;
380             public boolean forceNoLatch = false;
381             public SinkPort driven = null;
382             public Latch latchDriver = null;
383             public boolean noDriveLatches = false;
384             public final String resetBehavior;
385             public Assignable getAssignableBits(int high, int low) { return new SimpleValue(getVerilogName(), high, low); }
386             public String getVerilogAction() { return getReq() + " <= 1;"; }
387             public String getVerilogTrigger() { return " && !" + getReq() + " && !"+getAck(); }
388             public SinkPort(String name, int width, boolean external, String resetBehavior) {
389                 super(name, width, external); this.resetBehavior=resetBehavior; }
390             public String getResetBehavior() { return resetBehavior; }
391             public String getInterface() { return name+"_r_, "+name+"_a, "+name+"_"; }
392             public String getSimpleInterface() { return name+"_r, "+name+"_a, "+name; }
393             public String getDeclaration() {
394                 StringBuffer sb = new StringBuffer();
395                 if (external) {
396                     sb.append("output "  +                 name +"_r_;\n");
397                     sb.append("input "   +                 name +"_a;\n");
398                     if (width>0)
399                         sb.append("output ["+(width-1)+":0]" + name +"_;\n");
400                     else
401                         sb.append("output [0:0]" + name +"_;\n"); // waste a bit, I guess
402                 } else {
403                     sb.append("wire "   +                 name +"_a;\n");
404                 }
405
406                 if (forceNoLatch ||  latchDriver!=null) {
407                     sb.append("reg "    +                  name +"_r;\n");
408                     sb.append("initial " +                 name +"_r = 0;\n");
409                     if (width>0)
410                         sb.append("wire   ["+(width-1)+":0]" + name +";\n");
411                 } else if (!hasLatch) {
412                     sb.append("wire "    +                 name +"_r;\n");
413                     if (width>0)
414                         sb.append("wire   ["+(width-1)+":0]" + name +";\n");
415                 } else {
416                     sb.append("reg "    +                  name +"_r;\n");
417                     sb.append("initial " +                 name +"_r = 0;\n");
418                     if (width>0) {
419                         sb.append("reg    ["+(width-1)+":0]" + name +";\n");
420                         if (!"/*NORESET*/".equals(resetBehavior))
421                             sb.append("initial " +                 name +" = 0;\n");
422                     }
423                 }
424                 return sb.toString();
425             }
426             public String doReset() {
427                 return (forceNoLatch||latchDriver!=null||width==0)
428                     ? name+"_r<=0;"
429                     : hasLatch
430                     ? (name+"_r<=0; "+("/*NORESET*/".equals(resetBehavior) ? "" : (name+"<=0;")))
431                     : "";
432             }
433             public String getAssignments() {
434                 StringBuffer sb = new StringBuffer();
435                 if (external) {
436                     sb.append("assign " +                  name +"_r_ = " + name + "_r;\n");
437                     if (width>0)
438                         sb.append("assign " +                  name +"_ = " + name + ";\n");
439                 }
440                 if (driven != null) {
441                     sb.append("assign " + driven.name +"_r = " + name + "_r;\n");
442                     sb.append("assign " + name +"_a = " + driven.name + "_a;\n");
443                     if (width>0)
444                         sb.append("assign " + driven.name +"   = " + name + ";\n");
445                 }
446                 if (driver != null) {
447                     sb.append("assign " + name +"_r = " + driver.name + "_r;\n");
448                     sb.append("assign " + driver.name +"_a = " + name + "_a;\n");
449                     if (width>0 && !noDriveLatches)
450                         sb.append("assign " + name +"   = " + driver.name + ";\n");
451                 }
452                 if (latchDriver != null) {
453                     if (width>0)
454                         sb.append("assign " + name +"   = " + latchDriver.name + ";\n");
455                 }
456                 return sb.toString();
457             }
458             public void connect(SinkPort driven) {
459                 this.driven = driven;
460                 throw new RuntimeException();
461             }
462         }
463
464         public void dump(PrintWriter pw, boolean fix) {
465             pw.println("module "+name+"(clk, rst ");
466             for(String name : portorder) {
467                 Port p = ports.get(name);
468                 pw.println("    , " + p.getInterface());
469             }
470             if (this.name.equals("root")) {
471                 pw.println("    , dram_addr");
472                 pw.println("    , dram_addr_r");
473                 pw.println("    , dram_addr_a");
474                 pw.println("    , dram_isread");
475                 pw.println("    , dram_write_data");
476                 pw.println("    , dram_write_data_push");
477                 pw.println("    , dram_write_data_full");
478                 pw.println("    , dram_read_data");
479                 pw.println("    , dram_read_data_pop");
480                 pw.println("    , dram_read_data_empty");
481                 pw.println("    , dram_read_data_latency");
482                 pw.println("    , vga_clk");
483                 pw.println("    , vga_psave");
484                 pw.println("    , vga_hsync");
485                 pw.println("    , vga_vsync");
486                 pw.println("    , vga_sync");
487                 pw.println("    , vga_blank");
488                 pw.println("    , vga_r");
489                 pw.println("    , vga_g");
490                 pw.println("    , vga_b");
491                 pw.println("    , vga_clkout");
492                 pw.println("    , ddr2_addr");
493                 pw.println("    , ddr2_addr_r");
494                 pw.println("    , ddr2_addr_a");
495                 pw.println("    , ddr2_isread");
496                 pw.println("    , ddr2_write_data");
497                 pw.println("    , ddr2_write_data_push");
498                 pw.println("    , ddr2_write_data_full");
499                 pw.println("    , ddr2_read_data");
500                 pw.println("    , ddr2_read_data_pop");
501                 pw.println("    , ddr2_read_data_empty");
502                 pw.println("    , ddr2_read_data_latency");
503             }
504             pw.println("   );");
505             pw.println();
506             pw.println("    input clk;");
507             pw.println("    input rst;");
508             if (this.name.equals("root")) {
509                 pw.println("output  [31:0] dram_addr;");
510                 pw.println("output         dram_addr_r;");
511                 pw.println("input          dram_addr_a;");
512                 pw.println("output         dram_isread;");
513                 pw.println("output  [63:0] dram_write_data;");
514                 pw.println("output         dram_write_data_push;");
515                 pw.println("input          dram_write_data_full;");
516                 pw.println("input   [63:0] dram_read_data;");
517                 pw.println("output         dram_read_data_pop;");
518                 pw.println("input          dram_read_data_empty;");
519                 pw.println("input   [1:0]  dram_read_data_latency;");
520                 pw.println("output  [31:0] ddr2_addr;");
521                 pw.println("output         ddr2_addr_r;");
522                 pw.println("input          ddr2_addr_a;");
523                 pw.println("output         ddr2_isread;");
524                 pw.println("output  [63:0] ddr2_write_data;");
525                 pw.println("output         ddr2_write_data_push;");
526                 pw.println("input          ddr2_write_data_full;");
527                 pw.println("input   [63:0] ddr2_read_data;");
528                 pw.println("output         ddr2_read_data_pop;");
529                 pw.println("input          ddr2_read_data_empty;");
530                 pw.println("input   [1:0]  ddr2_read_data_latency;");
531                 pw.println("input          vga_clk;");
532                 pw.println("output         vga_psave;");
533                 pw.println("output         vga_hsync;");
534                 pw.println("output         vga_vsync;");
535                 pw.println("output         vga_sync;");
536                 pw.println("output         vga_blank;");
537                 pw.println("output   [7:0] vga_r;");
538                 pw.println("output   [7:0] vga_g;");
539                 pw.println("output   [7:0] vga_b;");
540                 pw.println("output         vga_clkout;");
541             }
542             for(String name : ports.keySet()) {
543                 Port p = ports.get(name);
544                 pw.println("    " + p.getDeclaration());
545             }
546             for(StateWire sw : statewires.values())
547                 sw.dump(pw);
548             for(Latch l : latches.values())
549                 l.dump(pw);
550             for(String name : ports.keySet()) {
551                 Port p = ports.get(name);
552                 pw.println("    " + p.getAssignments());
553             }
554             for(InstantiatedModule m : instantiatedModules) {
555                 m.dump(pw);
556             }
557             pw.println(precrap);
558             pw.println("always @(posedge clk) begin");
559             pw.println("  if (!rst) begin");
560             for(Latch l : latches.values())
561                 pw.println(l.doReset());
562             for(StateWire sw : statewires.values())
563                 pw.println(sw.doReset());
564             for(Port p : ports.values())
565                 pw.println(p.doReset());
566             pw.println("  end else begin");
567             for(Port p : ports.values()) {
568                 if (p instanceof SourcePort) {
569                     SourcePort ip = (SourcePort)p;
570                     if (ip.hasLatch)
571                         pw.println("if (!"+ip.getReq()+" && "+ip.getAck()+") "+ip.getAck()+"<=0;");
572                 } else {
573                     SinkPort op = (SinkPort)p;
574                     if (op.hasLatch)
575                         pw.println("if ("+op.getReq()+" && "+op.getAck()+") begin "+
576                                    op.getReq()+"<=0; "+
577                                    op.getResetBehavior()+" end");
578                 }
579             }
580             for(Event a : events) a.dump(pw, fix);
581             pw.println("    begin end");
582             pw.println("    end");
583             pw.println("  end");
584
585             pw.println(crap);
586             pw.println("endmodule");
587         }
588
589         public class Event {
590             public Object[]  triggers;
591             public Object[]  actions;
592             public Event(Object[] triggers, Object action) { this(triggers, new Object[] { action }); }
593             public Event(Object[] triggers, Object[] actions) {
594                 Module.this.events.add(this);
595                 this.triggers = triggers;
596                 this.actions = actions;
597                 for(int i=0; i<triggers.length; i++)
598                     if (triggers[i] instanceof Port)
599                         ((Port)triggers[i]).hasLatch = true;
600             }
601             public void dump(PrintWriter pw, boolean fix) {
602                 pw.print("if (1");
603                 for(Object o : triggers) {
604                     if (o instanceof Trigger) pw.print(((Trigger)o).getVerilogTrigger());
605                     else                      pw.print(" && " + o);
606                 }
607                 pw.println(") begin ");
608                 for(Object a : actions) if (a!=null) pw.println(((Action)a).getVerilogAction());
609                 if (fix) pw.println("end /*else*/ ");
610                 else     pw.println("end else ");
611             }
612         }
613     }
614 }