public static class Module {
         private int id = 0;
-        private HashSet<InstantiatedModule> instantiatedModules = 
-            new HashSet<InstantiatedModule>();
         private final String name;
         public String getName() { return name; }
 
-        private final HashSet<Action> actions = new HashSet<Action>();
+        private HashSet<InstantiatedModule> instantiatedModules = new HashSet<InstantiatedModule>();
+        private final ArrayList<Action> actions = new ArrayList<Action>();
 
         // FIXME: always-alphabetical convention?
         private final HashMap<String,Port> ports = new HashMap<String,Port>();
         private final ArrayList<String> portorder = new ArrayList<String>();
         
+        private StringBuffer crap = new StringBuffer();
+        private StringBuffer precrap = new StringBuffer();
+        public void addCrap(String s) { crap.append(s); crap.append('\n'); }
+        public void addPreCrap(String s) { precrap.append(s); precrap.append('\n'); }
+
         public Module(String name) {
             this.name = name;
         }
         }
         private abstract class Port {
             public final String name;
+            public String getName() { return name; }
             public final int width;
             public boolean hasLatch = false;
+            public boolean supress = false;
+            public boolean noAssignData = false;
             public boolean external;
+            public boolean pretendDriven = false;
+            public boolean noreg = false;
             public Port(String name, int width, boolean external) {
                 this.width = width;
                 this.name = name;
         private class InstantiatedModule {
             public final Module module;
             public final int id;
+            private final HashMap<String,Port> ports = new HashMap<String,Port>();
             public String getName() { return module.getName()+"_"+id; }
             public InstantiatedModule(Module module) {
                 this.module = module;
             }
             public Port getPort(String name, int width) {
                 if (module.ports.get(name) instanceof SinkPort)
-                    return getInputPort(name, width);
-                return getOutputPort(name, width, "");
+                    return getOutputPort(name, width, "");
+                return getInputPort(name, width);
             }
             public SinkPort getInputPort(String name, int width) {
-                // FIXME: should not re-instantiate, but so what
-                return new SinkPort(getName()+"_"+name, width, false, "");
+                SinkPort port = (SinkPort)ports.get(name);
+                if (port == null) {
+                    port = new SinkPort(getName()+"_"+name, width, false, "");
+                    ports.put(name, port);
+                }
+                return port;
             }
             public SourcePort getOutputPort(String name, int width, String resetBehavior) {
-                // FIXME: should not re-instantiate, but so what
-                return new SourcePort(getName()+"_"+name, width, false);
+                SourcePort port = (SourcePort)ports.get(name);
+                if (port == null) {
+                    port = new SourcePort(getName()+"_"+name, width, false);
+                    ports.put(name, port);
+                }
+                return port;
             }
         }
 
             }
             public String getAssignments() {
                 StringBuffer sb = new StringBuffer();
-                if (external) {
+                if (external && !pretendDriven) {
                     sb.append("assign " +                 name +"_a_ = " + name + "_a;\n");
                 }
                 if (driven != null) {
                 } else {
                     sb.append("reg "    +                  name +"_r;\n");
                     sb.append("initial " +                 name +"_r = 0;\n");
-                    sb.append("reg    ["+(width-1)+":0]" + name +";\n");
-                    sb.append("initial " +                 name +" = 0;\n");
+                    if (!noreg) {
+                        sb.append("reg    ["+(width-1)+":0]" + name +";\n");
+                        sb.append("initial " +                 name +" = 0;\n");
+                    }
                 }
                 return sb.toString();
             }
             public String getAssignments() {
                 StringBuffer sb = new StringBuffer();
-                if (external) {
+                if (external && !pretendDriven) {
                     sb.append("assign " +                  name +"_r_ = " + name + "_r;\n");
-                    sb.append("assign " +                  name +"_ = " + name + ";\n");
+                    if (!noAssignData) {
+                        sb.append("assign " +                  name +"_ = " + name + ";\n");
+                    }
                 }
                 return sb.toString();
             }
         }
 
         public void dump(PrintWriter pw) {
+            pw.println("`include \"macros.v\"");
             pw.println("module "+name+"(clk");
             for(String name : portorder) {
                 Port p = ports.get(name);
             for(InstantiatedModule m : instantiatedModules) {
                 m.dump(pw);
             }
+            pw.println(precrap);
             pw.println("always @(posedge clk) begin");
-            for(String name : portorder) {
-                Port p = ports.get(name);
+            for(Port p : ports.values()) {
                 if (p instanceof SourcePort) {
                     SourcePort ip = (SourcePort)p;
-                    if (ip.hasLatch)
+                    if (ip.hasLatch && !ip.supress)
                         pw.println("if (!"+ip.getReq()+" && "+ip.getAck()+") "+ip.getAck()+"<=0;");
                 } else {
                     SinkPort op = (SinkPort)p;
-                    if (op.hasLatch)
+                    if (op.hasLatch && !op.supress)
                         pw.println("if ("+op.getReq()+" && "+op.getAck()+") begin "+
                                    op.getReq()+"<=0; "+
                                    op.getResetBehavior()+" end");
             for(Action a : actions) a.dump(pw);
             pw.println(" begin end");
             pw.println("end");
+
+            pw.println(crap);
             pw.println("endmodule");
         }
 
     public static final int WORD_WIDTH = 37;
     public static final int DESTINATION_WIDTH = 11;
     public static final int PACKET_WIDTH = WORD_WIDTH + DESTINATION_WIDTH;
+    public static final int INSTRUCTION_WIDTH = WORD_WIDTH;
 
     public static void main(String[] s) throws Exception {
         String prefix = "src/edu/berkeley/fleet/slipway/";
         PrintWriter pw;
 
-        Module funnel = new Module("funnel");
-        Module.SinkPort out = funnel.getOutputPort("out", PACKET_WIDTH, "");
+        mkfunnel("funnel", prefix);
+        mkhorn(    "horn", prefix, PACKET_WIDTH-1, DESTINATION_WIDTH-1, 0, 0);
+        mkhorn(   "ihorn", prefix, PACKET_WIDTH-1, 34, 24, 0);
+
+        Module fifostage = mkfifo("fifostage", 0, null,      prefix);
+        Module fifo4     = mkfifo("fifo4",     4, fifostage, prefix);
+        Module fifoship  = mkfifo("fifo",      4, fifo4,     prefix);
+        mkoutbox("outbox", false, prefix, fifo4);
+        mkoutbox("inbox", true, prefix, fifo4);
+
+        mkkill("kill", prefix);
+    }
+
+    private static Module mkkill(String name, String prefix) throws Exception {
+        Module killm = new Module(name);
+        Module.SourcePort  instr    = killm.getInputPort("instr",    INSTRUCTION_WIDTH);
+        Module.SinkPort    kill     = killm.getOutputPort("kill",    INSTRUCTION_WIDTH+1, "");
+        Module.SinkPort    notkill  = killm.getOutputPort("notkill", INSTRUCTION_WIDTH,   "");
+        killm.addPreCrap("  reg ifull;");
+        killm.addPreCrap("  initial ifull = 0;");
+        killm.new Action(new Object[] { "!ifull",           instr, notkill              },
+                         new Object[] { "notkill = instr;", instr,         "ifull = 1;" }
+                         );
+        killm.new Action(new Object[] { kill, "ifull",          "`instruction_is_kill(notkill)" },
+                         new Object[] { kill, "ifull=0;",
+                                        "kill = { `instruction_bit_kill_only_standing(notkill), `instruction_count(notkill) };" }
+                         );
+        killm.new Action(new Object[] { notkill, "ifull",          "!`instruction_is_kill(notkill)" },
+                         new Object[] { notkill, "ifull=0;" }
+                         );
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/"+name+".v")));
+        killm.dump(pw);
+        pw.flush();
+        return killm;
+    }
+
+    private static Module mkfunnel(String name, String prefix) throws Exception {
+        Module funnel = new Module(name);
+        Module.SinkPort    out = funnel.getOutputPort("out", PACKET_WIDTH, "");
         Module.SourcePort  in1 = funnel.getInputPort("in1", PACKET_WIDTH);
         Module.SourcePort  in2 = funnel.getInputPort("in2", PACKET_WIDTH);
         funnel.new Action(new Object[] { in1, out }, new Object[] { in1, out, "out  = in1;" });
         funnel.new Action(new Object[] { in2, out }, new Object[] { in2, out, "out  = in2;" });
-        pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/funnel.v")));
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/"+name+".v")));
         funnel.dump(pw);
         pw.flush();
+        return funnel;
+    }
+
+    private static Module mkoutbox(String name, boolean inbox, String prefix, Module fifo) throws Exception {
+        Module box = new Module(name);
+        Module.SourcePort instr         = box.getInputPort("instr",       INSTRUCTION_WIDTH);
+        //instr.hasLatch = true;
+        //instr.supress = true;
+        Module.SourcePort fabric_in     = box.getInputPort("fabric_in",   PACKET_WIDTH);
+        Module.SinkPort   fabric_out    = box.getOutputPort("fabric_out", PACKET_WIDTH, "");
+        fabric_out.hasLatch = true;
+        //fabric_out.supress = true;
 
-        mkhorn( "horn", prefix, PACKET_WIDTH-1, DESTINATION_WIDTH-1, 0, 0);
-        mkhorn("ihorn", prefix, PACKET_WIDTH-1, 34, 24, 0);
+        Module.InstantiatedModule dfifo = box.new InstantiatedModule(fifo);
+        fabric_in.connect(dfifo.getInputPort("in", PACKET_WIDTH));
+        
+        Module.SourcePort dfifo_out = dfifo.getOutputPort("out", PACKET_WIDTH, "");
+        String fabric_in_d0 = dfifo_out.name;
+        String fabric_in_r0 = fabric_in_d0+"_r";
+        String fabric_in_a0 = fabric_in_d0+"_a";
+        dfifo_out.hasLatch = true;
+
+        Module.SourcePort   ship_out    = null;
+        if (!inbox) {
+            ship_out = box.getInputPort("ship",        WORD_WIDTH);
+            ship_out.hasLatch = true;
+        }
+
+        Module.SinkPort   ship_in     = null;
+        if (inbox) {
+            ship_in = box.getOutputPort("ship",        PACKET_WIDTH, "");
+            ship_in.hasLatch = true;
+        }
+
+            box.addPreCrap("  reg[(`INSTRUCTION_WIDTH-1):0] ondeck;");
+            box.addPreCrap("  reg[(`COUNT_WIDTH-1):0] repcount;");
+            box.addPreCrap("  initial ondeck=0;");
+            box.addPreCrap("  reg ondeck_full;    initial ondeck_full=0;");
+            box.addPreCrap("  reg newmayproceed;  initial newmayproceed=1;");
+            box.addPreCrap("  reg dorepeat;       initial dorepeat=0;");
+            box.addPreCrap("  reg dorepeatkill;   initial dorepeatkill=0;");
+            box.addPreCrap("  reg dokill;         initial dokill=0;");
+            box.addPreCrap("  reg clogged;        initial clogged=0;");
+
+            // FIXME: destination
+            Module.SinkPort   token_out = fabric_out;
+            Module.SourcePort token_in  = dfifo_out;
+            Module.SinkPort   data_out  = inbox ? ship_in   : fabric_out;
+            Module.SourcePort data_in   = inbox ? dfifo_out : ship_out;
+
+            Module.InstantiatedModule ififo = box.new InstantiatedModule(fifo);
+            Module.SinkPort ififo_in = ififo.getInputPort("in", PACKET_WIDTH);
+            ififo_in.hasLatch = true;
+            Module.SourcePort ififo_out = ififo.getOutputPort("out", PACKET_WIDTH, "");
+
+            // Clog (must be first)
+            box.new Action(
+                           new Object[] { ififo_out, "newmayproceed==1", "`instruction_is_clog("+ififo_out.getName()+")" },
+                           new Object[] { ififo_out, "clogged <= 1;", "newmayproceed<=0;" }
+                           );
+
+            // UnClog
+            box.new Action(
+                           new Object[] { instr, "clogged==1", "`instruction_is_unclog(instr)" },
+                           new Object[] { instr, "clogged <= 0;", "newmayproceed<=1;"  }
+                           );
+
+            // First Kill
+            box.new Action(
+                           new Object[] { instr, ififo_out, "`instruction_is_kill(instr)", "!`instruction_is_unclog(instr)", "newmayproceed==1" },
+                           new Object[] { instr, ififo_out,
+                                          "if (`instruction_count(instr)!=0)"+
+                                          "  begin repcount <= `instruction_count(instr)-1; newmayproceed <= 0; "+
+                                          "        dorepeatkill <= 1; end else begin newmayproceed<=1; end" }
+                           );
 
-        Module fifostage = new Module("fifostage");
-        Module.SourcePort  in   = fifostage.getInputPort("in", PACKET_WIDTH);
-        Module.SinkPort outf = fifostage.getOutputPort("out", PACKET_WIDTH, "");
-        fifostage.new Action(new Object[] { in, outf }, new Object[] { in, outf, "out  = in;" });
-        pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/fifostage.v")));
-        fifostage.dump(pw);
+            // Kill              
+            box.new Action(
+                           new Object[] { "dokill==1" },
+                           new Object[] { "dokill<=0;",
+                                          "if (`instruction_count(ondeck)!=0)"+
+                                          "  begin repcount <= `instruction_count(ondeck)-1;"+
+                                          "        dorepeatkill <= 1; end else begin newmayproceed<=1; end" }
+                           );
+
+            // RepKill
+            box.new Action(
+                           new Object[] { "dorepeatkill==1", ififo_out },
+                           new Object[] { "dorepeatkill<=0;", ififo_out, "dokill<=1;", "`instruction_count(ondeck)<=repcount;" }
+                           );
+
+            // Enqueue
+            box.new Action(
+                           new Object[] { instr, ififo_in, "!`instruction_is_kill(instr)" },
+                           new Object[] { instr, ififo_in, ififo_in.getName()+"<=instr;"  }
+                           );
+
+            // New
+            box.new Action(
+                           new Object[] { ififo_out, "ondeck_full==0", "newmayproceed==1" },
+                           new Object[] { ififo_out, "ondeck_full<=1;", "newmayproceed<=0;",
+                                          "ondeck<="+ififo_out.getName()+";"  }
+                           );
+
+            // RepeatExecute
+            box.new Action(
+                           new Object[] { "dorepeat==1", },
+                           new Object[] { "dorepeat<=0;", "ondeck_full<=1;", "`instruction_count(ondeck)<=repcount;" }
+                           );
+
+            for(int di=0; di<=1; di++)
+                for(int dout=0; dout<=1; dout++)
+                    for(int tout=0; tout<=1; tout++)
+                    for(int ti=0; ti<=1; ti++) {
+                        box.new Action(
+                                       new Object[] { "ondeck_full==1",
+                                                      data_out,
+                                                      token_out,
+                                                      ififo_in,
+                                                      (di==1 ? "" : "!")+"`instruction_bit_datain(ondeck)",
+                                                      (di==1 ? data_in : "1"),
+                                                      (ti==1 ? "" : "!")+"`instruction_bit_tokenin(ondeck)",
+                                                      (ti==1 ? token_in : "1"),
+                                                      (dout==1 ? "" : "!")+"`instruction_bit_dataout(ondeck)",
+                                                      (tout==1 ? "" : "!")+"`instruction_bit_tokenout(ondeck)"
+                                       },
+                                       new Object[] { "ondeck_full<=0;",
+                                                      "if (`instruction_count(ondeck)==1 || `instruction_bit_recycle(ondeck)) newmayproceed<=1;",
+                                                      "if (`instruction_bit_recycle(ondeck) && `instruction_count(ondeck)!=1) "+
+                                                      "    begin "+ififo_in.getName()+"<=ondeck; "+
+                                                      "          `instruction_count("+ififo_in.getName()+")<=(`instruction_count(ondeck)==0?0:`instruction_count(ondeck)-1);"+
+                                                      "          "+ififo_in.getReq()+"<=1; end",
+                                                      "if (!`instruction_bit_recycle(ondeck) && `instruction_count(ondeck)!=1) "+
+                                                      "    dorepeat <= 1;",
+                                                      "repcount <= (`instruction_count(ondeck)==0 ? 0 : (`instruction_count(ondeck)-1));",
+                                                      (di==1 ? data_in : ""),
+                                                      (dout==1 ? data_out : ""),
+                                                      (ti==1 ? token_in : ""),
+                                                      (tout==1 ? token_out : ""),
+                                                      ("if (`instruction_bit_latch(ondeck)) "+
+                                                       (inbox ? data_out.getName() : "`packet_data("+data_out.getName()+")")+
+                                                       "<="+
+                                                       (inbox ? "`packet_data("+data_in.getName()+")" : data_in.getName())+";"),
+                                                      (tout==1 ? "`packet_dest("+token_out.getName()+")<=`instruction_bit_dest(ondeck);" : ""),
+                                                      (dout==1 && !inbox ? "`packet_dest("+data_out.getName()+")<=`instruction_bit_dest(ondeck);" : "")
+                                       }
+                                       );
+                    }
+
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/"+name+".v")));
+        box.dump(pw);
         pw.flush();
+        return box;
+    }
 
-        Module fifo4 = new Module("fifo4");
+    private static Module mkfifo(String name, int len, Module instance, String prefix) throws Exception {
+        Module fifo4 = new Module(name);
         Module.SourcePort  inx  = fifo4.getInputPort("in", PACKET_WIDTH);
         Module.SinkPort    outx = fifo4.getOutputPort("out", PACKET_WIDTH, "");
-        int len = 4;
         Module.InstantiatedModule[] stages = new Module.InstantiatedModule[len];
-        for(int i=0; i<=len; i++) {
-            if (i<len) stages[i] = fifo4.new InstantiatedModule(fifostage);
+        if (len==0) {
+            fifo4.new Action(new Object[] { inx, outx }, new Object[] { inx, outx, "out  = in;" });
+        } else for(int i=0; i<=len; i++) {
+            if (i<len) stages[i] = fifo4.new InstantiatedModule(instance);
             Module.SourcePort driver = i==0 ? inx : stages[i-1].getOutputPort("out", PACKET_WIDTH, "");
             Module.SinkPort   driven = i==len ? outx : stages[i].getInputPort("in", PACKET_WIDTH);
             driver.connect(driven);
         }
-        pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/fifo4.v")));
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(prefix+"/"+name+".v")));
         fifo4.dump(pw);
         pw.flush();
+        return fifo4;
     }
 
     public static void mkhorn(String name, 
 
+++ /dev/null
-  reg have_instruction;
-  reg need_to_read_token;
-  reg need_to_read_data;
-  reg need_to_send_token;
-  reg need_to_send_data;
-  reg parse_instruction;
-  reg do_recycle;
-  reg fire_ok;
-  reg                            kill_only_standing;
-  reg [(`COUNT_BITS-1):0]        kill_count;
-  reg [(`INSTRUCTION_WIDTH-1):0] instruction;
-
-  wire [(`INSTRUCTION_WIDTH-1):0] instr_d2;
-  wire [(`INSTRUCTION_WIDTH-1):0] instr_d0;
-
-  reg kill_r;
-  reg kill_a;
-  reg [(`COUNT_WIDTH-1+1):0] kill_d;
-
-  reg [(`INSTRUCTION_WIDTH-1):0] kinstruction;
-  reg instr_rx;
-  wire instr_ax;
-  reg ifull;
-
-  always @(posedge clk) begin
-    if (!ifull) begin
-      `onread(instr_r, instr_a)
-        ifull = 1;
-        kinstruction = instr_d;
-      end
-    end else begin
-      if (`instruction_is_kill(kinstruction)) begin
-        kill_d = { `instruction_bit_kill_only_standing(kinstruction), `instruction_count(kinstruction) };
-        `onwrite(kill_r, kill_a)
-          ifull = 0;
-        end
-      end else begin
-        `onwrite(instr_rx, instr_ax)
-          ifull = 0;
-        end
-      end
-    end
-  end
-
-  reg recycle_r;
-  wire recycle_r_;
-  assign recycle_r_ = recycle_r;
-  wire recycle_a;
-  wire [(`INSTRUCTION_WIDTH-1):0] recycle_inst;
-  assign recycle_inst = instruction;
-  
-  wire instr_a2_;
-  reg instr_a2;
-  assign instr_a2_ = instr_a2;
-
-  funnel ifunnel(clk, instr_r0, instr_a0, instr_d0,
-                      instr_rx,  instr_ax,  kinstruction,
-                      recycle_r_, recycle_a, recycle_inst);
-
-  fifo4 ififo(clk, instr_r0, instr_a0, instr_d0,
-                   instr_r2, instr_a2_, instr_d2);
-
-  always @(posedge clk) begin
-
-    if (do_recycle) begin
-      `onwrite(recycle_r, recycle_a)
-        do_recycle = 0;
-        have_instruction = 0;
-        parse_instruction = 0;
-      end
-
-    end else if (fire_ok) begin
-      if (need_to_send_data) begin
-        `onwrite(`data_out_r, `data_out_a)
-          need_to_send_data = 0;
-        end
-
-      end else if (need_to_send_token) begin
-        `onwrite(`token_out_r, `token_out_a)
-          need_to_send_token = 0;
-        end
-
-      end else begin
-         fire_ok = 0;
-         case (`instruction_count(instruction))
-                0: begin end
-                1: have_instruction = 0;
-          default: `instruction_count(instruction) = `instruction_count(instruction) - 1;
-         endcase
-         if (have_instruction && `instruction_bit_recycle(instruction)) begin
-           do_recycle = 1;
-         end else begin
-           parse_instruction = have_instruction;
-         end
-      end
-
-    end else if (!have_instruction && !parse_instruction) begin
-      `onread(instr_r2, instr_a2)
-        instruction                 = instr_d2;
-        parse_instruction           = 1;
-      end
-
-    end else begin
-      // FIXME: actually don't want to kill partway through an instruction
-      `onread(kill_r, kill_a)
-        kill_only_standing = kill_d[`COUNT_WIDTH];
-        kill_count = kill_count + kill_d[(`COUNT_WIDTH-1):0];
-      end
-
-      if ((parse_instruction || have_instruction) && (kill_count > 0) &&
-          (!kill_only_standing || `instruction_count(instruction)==0))
-      begin
-        kill_count = kill_count - 1;
-        have_instruction = 0;
-        parse_instruction = 0;
-      end
-
-      if (parse_instruction) begin
-        need_to_read_token          = `instruction_bit_tokenin(instruction);
-        need_to_read_data           = `instruction_bit_datain(instruction);
-        `packet_dest(`data_out_d)   = `instruction_bit_dest(instruction);
-        `packet_dest(`token_out_d)  = `instruction_bit_dest(instruction);
-        need_to_send_data           = `instruction_bit_dataout(instruction);
-        need_to_send_token          = `instruction_bit_tokenout(instruction);
-        have_instruction            = 1;
-        parse_instruction           = 0;
-      end
-
-      // return to zero
-      if (!`token_in_r && `token_in_a)  `token_in_a  = 0;
-      if (!`data_in_r  && `data_in_a)   `data_in_a   = 0;
-
-      if (have_instruction &&
-          (!need_to_read_token  || (`token_in_r   && !`token_in_a)) &&
-          (!need_to_read_data   || (`data_in_r    && !`data_in_a))
-         ) begin
-        if (need_to_read_token) begin
-          `token_in_a = 1;
-          need_to_read_token = 0;
-        end
-        if (need_to_read_data) begin
-          `data_in_a = 1;
-          need_to_read_data = 0;
-        end
-        if (`instruction_bit_latch(instruction)) begin
-          `packet_data(`data_out_d) = `data_in_d;
-          `extra
-        end
-        fire_ok = 1;
-      end
-
-    end
-  end