anchor.diff:     interface PipeOut pipe = dotpipe;
      interface Reg numElts = numEltsReg;
   endmodule : mkDotProdServer
  +
  +interface SharedDotProdServer#(numeric type k, numeric type n);
  +   interface Reg#(UInt#(20)) numElts;
  +   interface Put#(Vector#(n,Float)) aInput;
  +   interface Vector#(k, Put#(Vector#(n,Float))) bInput;
  +   interface Vector#(k, PipeOut#(Float)) pipes;
  +endinterface
  +
  +(* synthesize *)
  +module [Module] mkSharedDotProdServer#(UInt#(TLog#(TMul#(J,K))) label)(SharedDotProdServer#(K,N));
  +
  +   let n = valueOf(N);
  +   let add_depth = valueOf(FP_ADD_DEPTH);
  +   let mul_depth = valueOf(FP_MUL_DEPTH);
  +   Bool verbose = False; //label==0;
  +
  +   Reg#(Bit#(TAdd#(TLog#(FP_ADD_DEPTH),1))) initCnt <- mkReg(0);
  +   FIFO#(void) initCtrl <- mkSizedFIFO(1);
  +
  +   Reg#(UInt#(20)) numEltsReg <- mkReg(0);
  +   Reg#(UInt#(20)) countInReg <- mkReg(0);
  +
  +   FloatAlu#(FP_MUL_DEPTH) mul   <- mkFloatMultiplier(defaultValue);
  +   FloatAlu#(FP_ADD_DEPTH) adder <- mkFloatAdder(defaultValue);
  +   Vector#(K,FloatAlu#(FP_MUL_DEPTH)) muls   <- replicateM(mkFloatMultiplier(defaultValue));
  +   Vector#(K,FloatAlu#(FP_ADD_DEPTH)) adders <- replicateM(mkFloatAdder(defaultValue));
  +
  +   // This could be done in fewer steps
  +   FIFOF#(Vector#(N,Float)) afifo <- mkFIFOF();
  +   Vector#(K, PipeOut#(Vector#(N,Float))) aPipesN <- mkForkVector(toPipeOut(afifo));
  +   Vector#(K, PipeOut#(Vector#(1,Float))) aPipes  = aPipesN; //<- mapM(mkFunnel, aPipesN);
  +   PipeOut#(Vector#(K,Float))             aPipe   <- mkJoinVector(id, map(unvectorPipeOut, aPipes));
  +   PipeOut#(Vector#(1,Float))             aFunnelV <- mkFunnel(aPipe);
  +   PipeOut#(Float)                        aFunnel = unvectorPipeOut(aFunnelV);
  +
  +   Vector#(K, FIFOF#(Vector#(N,Float)))   bfifos <- replicateM(mkFIFOF());
  +   Vector#(K, PipeOut#(Vector#(N,Float))) bPipesN = map(toPipeOut,bfifos);
  +   Vector#(K, PipeOut#(Vector#(1,Float))) bPipes = bPipesN; //<- mapM(mkFunnel, bPipesN);
  +   PipeOut#(Vector#(K,Float))             bPipe  <- mkJoinVector(id, map(unvectorPipeOut, bPipes));
  +   PipeOut#(Vector#(1,Float))             bFunnelV <- mkFunnel(bPipe);
  +   PipeOut#(Float)                        bFunnel = unvectorPipeOut(bFunnelV);
  +
  +   Vector#(K,FIFOF#(Bool)) firstFifos <- replicateM(mkSizedFIFOF(valueOf(K)));
  +   Vector#(K,FIFOF#(Bool)) lastFifos <- replicateM(mkSizedFIFOF(valueOf(K)));
  +
  +   Vector#(N, Reg#(Bit#(TAdd#(TLog#(FP_ADD_DEPTH),1)))) drainCnts <- replicateM(mkReg(0));
  +   Vector#(N, Reg#(Bool)) drained <- replicateM(mkReg(False));
  +
  +   Vector#(K, FIFOF#(Float))  accumFifos <- replicateM(mkFIFOF);
  +   Vector#(K,FIFOF#(Float)) dotfifos <- replicateM(mkFIFOF);
  +
  +   Reg#(Bit#(TLog#(K))) chanReg <- mkReg(0);
  +   Vector#(2,FIFO#(Bit#(TLog#(K)))) chanFifos <- replicateM(mkSizedFIFO(valueOf(K)));
  +
  +   function Bit#(TLog#(N)) i_v(Integer i) = fromInteger(i);
  +
  +   Reg#(Bit#(32)) cycles <- mkReg(0);
  +   rule countCycles;
  +      cycles <= cycles + 1;
  +   endrule
  +
  +   Vector#(K, Reg#(Bit#(32))) lastMulin <- replicateM(mkReg(0));
  +   Vector#(12, Reg#(Bool))   latencyReported <- replicateM(mkReg(False));
  +
  +   (* descending_urgency = "mulin,accout" *)
  +   rule mulin;
  +      let chan = chanReg;
  +      begin
  +	 //$display("%08d label=%d mulin chan=%d chanReg=%d", cycles, label, chan, chanReg);
  +	 let latency = cycles-lastMulin[chan];
  +	 if ((lastMulin[chan] - cycles) < 6)
  +	    $display("%08d label=%d mulin chan=%d latency", cycles, label, chan, latency);
  +	 if (latency > 11) latency = 11;
  +	 if (!latencyReported[latency]) begin
  +	    $display("%08d label=%d mulin chan=%d latency", cycles, label, chan, latency);
  +	    latencyReported[latency] <= True;
  +	 end
  +	 lastMulin[chan] <= cycles;
  +      end
  +
  +      for (Integer i = 0; i < 2; i=i+1)
  +	 chanFifos[i].enq(chan);
  +      chanReg <= (chan + 1);
  +      let a <- toGet(aFunnel).get();
  +      let b <- toGet(bFunnel).get();
  +      let first <- toGet(firstFifos[chan]).get();
  +      if (first)
  +	 accumFifos[chan].enq(unpack(0));
  +      //mul.request.put(tuple2(a, b));
  +      muls[chan].request.put(tuple2(a, b));
  +   endrule
  +
  +   rule mulout;
  +      let chan <- toGet(chanFifos[0]).get();
  +      //$display("label=%d mulout chan=%d chanReg=%d", label, chan, chanReg);
  +      match {.resp,.*} <- muls[chan].response.get();
  +      let acc <- toGet(accumFifos[chan]).get();
  +      //adder.request.put(tuple2(resp,acc));
  +      adders[chan].request.put(tuple2(resp,acc));
  +   endrule
  +
  +   rule accout;
  +      let chan <- toGet(chanFifos[1]).get();
  +      //$display("label=%d accout chan=%d chanReg=%d", label, chan, chanReg);
  +      let last <- toGet(lastFifos[chan]).get;
  +      match {.acc,.*} <- adders[chan].response.get();
  +      if (last)
  +	 dotfifos[chan].enq(acc);
  +      else
  +	 accumFifos[chan].enq(acc);
  +   endrule
  +
  +   Vector#(K,PipeOut#(Float)) dotpipes = map(toPipeOut, dotfifos);
  +
  +   interface Put aInput;
  +      method Action put(Vector#(N,Float) avec);
  +
  +   	 afifo.enq(avec);
  +
  +	 let c = countInReg+fromInteger(n);
  +	 Bool isFirst = (countInReg == 0);
  +	 Bool isLast = (c >= numEltsReg);
  +	 if (isLast) begin
  +	    c = 0;
  +	 end
  +	 countInReg <= c;
  +	 for (Integer k = 0; k < valueOf(K); k = k + 1) begin
  +	    lastFifos[k].enq(isLast);
  +	    firstFifos[k].enq(isFirst);
  +	 end
  +      endmethod
  +   endinterface
  +   interface Vector bInput = map(toPut, bfifos);
  +   interface Vector pipes = dotpipes;
  +   interface Reg numElts = numEltsReg;
  +endmodule : mkSharedDotProdServer
   
   function Vector#(TMul#(j,k), etype) flattenMatrix(Vector#(j, Vector#(k, etype)) mat);
      function etype flatten(Integer i); return mat[i/valueOf(k)][i%valueOf(k)]; endfunction
  
positive.code: 	 sinks[j].vector.start(descriptorC.pointer, pack(extend(startC>>nshift)), fromInteger(kk/n));
  	 if (verbose || verbose1) $display($format(fshow(cycles)+fshow("      sinks[")+fshow(jint)+fshow("].start")+fshow(startC)));
  
  	 // this one counts by one rather than 1/n because the source values are unfunneled before mul/add
  	 if (j == 0)
  	    firstLastPipe.start(truncate(descriptorA.numColumns));
        endrule
  
        rule finishSourceA;
  
negative.code:       for (Integer j = 0; j < jj; j = j + 1) begin
  	 startAOffset[j] <= fromInteger(j)*numColumnsA;
  	 startCOffset[j] <= fromInteger(j)*numRowsB;
  	 fxdotprods[j].numElts <= truncate(numColumnsA);
        end
        for (Integer k = 0; k < kk; k = k + 1) begin
  	 startBOffset[k] <= fromInteger(k)*numColumnsB;
        end
     endmethod
  