first attempt at serdes lowlevel fsm

first attempt at serdes lowlevel fsm

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import Clocks :: *;
import XilinxCells::*;
import XbsvXilinxCells::*;

interface Iserdesbvi;
    method Action           align_start(Bit#(1) v);
    method Bit#(1)          align_busy();
    method Bit#(1)          aligned();
    method Bit#(3)          samplein();
    method Bit#(3)          iodelay_reset_inc_ce();
    method Bit#(1)          bitslip();
    method Action           autoalign(Bit#(1) v);
    method Action           training(Bit#(10) v);
    method Action           manual_tap(Bit#(10) v);
    method Action           delay_wren(Bit#(1) v);
    method Action           fifo_wren(Bit#(1) v);
    method Action           reset(Bit#(1) v);
    method Bit#(1)          fifo_wren_sync();
    method Action           dataout(Bit#(10) v);
endinterface: Iserdesbvi

import "BVI" iserdes_datadeser =
module mkIserdesbvi#(Clock clkdiv)(Iserdesbvi);
    input_clock clkdiv (CLKDIV) = clkdiv;
    default_clock clock(CLOCK);
    method                  align_start(ALIGN_START) enable((*inhigh*) en1) clocked_by (clock);
    method ALIGN_BUSY       align_busy() clocked_by (clock);
    method ALIGNED          aligned() clocked_by (clock);
    method ISERDES_BITSLIP  bitslip() clocked_by (clkdiv);
    method SAMPLEIN samplein() clocked_by (clock);
    method IODELAY_RESET_INC_CE iodelay_reset_inc_ce() clocked_by (clkdiv);
    method                  autoalign(AUTOALIGN) enable((*inhigh*) en7) clocked_by (clock);
    method                  training(TRAINING) enable((*inhigh*) en8) clocked_by (clock);
    method                  manual_tap(MANUAL_TAP) enable((*inhigh*) en9) clocked_by (clock);
    method                  fifo_wren(FIFO_WREN) enable((*inhigh*) en10) clocked_by (clkdiv);
    method FIFO_WREN_SYNC   fifo_wren_sync() clocked_by (clock);
    method                  delay_wren(DELAY_WREN) enable((*inhigh*) en11) clocked_by (clkdiv);
    method                  reset(RESET) enable((*inhigh*) en17) clocked_by (clkdiv);
    method                  dataout(ISERDES_DATA) enable((*inhigh*) en18) clocked_by(clkdiv);
    schedule (fifo_wren, reset, delay_wren, dataout, iodelay_reset_inc_ce, bitslip, align_busy, aligned, samplein, fifo_wren_sync, align_start, autoalign, training, manual_tap, bitslip)
         CF (fifo_wren, reset, delay_wren, dataout, iodelay_reset_inc_ce, bitslip, align_busy, aligned, samplein, fifo_wren_sync, align_start, autoalign, training, manual_tap, bitslip);
endmodule: mkIserdesbvi

interface IserdesDatadeser;
    method Action           ibufdso(Bit#(1) v);
    method Bit#(1)          align_busy();
    method Bit#(1)          aligned();
    method Bit#(1)          sampleinfirstbit();
    method Bit#(1)          sampleinlastbit();
    method Bit#(1)          sampleinotherbit();
    method Action           delay_wren(Bit#(1) v);
    method Action           fifo_wren(Bit#(1) v);
    method Action           reset(Bit#(1) v);
    method Bit#(1)          empty();
    method Bit#(10)         dataout();
endinterface: IserdesDatadeser

interface FIFO18;
   method Action            di(Bit#(16) v);
   method Action            rden(Bit#(1) v);
   method Action            wren(Bit#(1) v);
   method Action            reset(Bit#(1) v);
   method Bit#(16)          dataout();
   method Bit#(1)           empty();
endinterface: FIFO18

import "BVI" FIFO18 =
module mkFIFO18#(Clock clkdiv)(FIFO18);
    parameter ALMOST_FULL_OFFSET = 'h80;
    parameter ALMOST_EMPTY_OFFSET = 'h80;
    parameter DATA_WIDTH = 18;
    parameter DO_REG = 1;
    parameter EN_SYN = 0;
    parameter FIRST_WORD_FALL_THROUGH = 0;
    parameter SIM_MODE = "SAFE";

    default_clock clock(RDCLK);
    input_clock clkdiv (WRCLK) = clkdiv;
    no_reset;
    port DIP = 0;

    method          reset(RST) enable((*inhigh*) en9) clocked_by (clkdiv);
    method          di(DI) enable((*inhigh*) en0) clocked_by (clkdiv);
    method          rden(RDEN) enable((*inhigh*) en2) clocked_by (clock);
    method          wren(WREN) enable((*inhigh*) en3) clocked_by (clock);
    method DO       dataout() clocked_by(clock);
    method EMPTY    empty() clocked_by(clock);
    schedule (reset, di, rden, wren, dataout, empty) CF (reset, di, rden, wren, dataout, empty);
endmodule: mkFIFO18

typedef enum { DIdle, DValid, DLow} DState deriving (Bits,Eq);

module mkIserdesDatadeser#(Clock serdes_clock, Reset serdes_reset, Clock serdest, Bit#(1) align_start,
    Bit#(1) autoalign, Bit#(10) training, Bit#(10) manual_tap, Bit#(1) rden)(IserdesDatadeser);

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    Iserdesbvi serbvi <- mkIserdesbvi(serdes_clock);
    FIFO18 dfifo <- mkFIFO18(serdes_clock);
    IdelayE2 delaye2 <- mkIDELAYE2(IDELAYE2_Config {
        cinvctrl_sel: "FALSE", delay_src: "IDATAIN",
        high_performance_mode: "TRUE",
        idelay_type: "VARIABLE", idelay_value: 0,
        pipe_sel: "FALSE", refclk_frequency: 200, signal_pattern: "DATA"},
        defaultClock, clocked_by serdes_clock);
    ClockDividerIfc serdest_inverted <- mkClockInverter(clocked_by serdest);
    IserdesE2 master_data <- mkISERDESE2( ISERDESE2_Config{
        data_rate: "DDR", data_width: 10,
        dyn_clk_inv_en: "FALSE", dyn_clkdiv_inv_en: "FALSE",
        interface_type: "NETWORKING", num_ce: 2, ofb_used: "FALSE",
        init_q1: 0, init_q2: 0, init_q3: 0, init_q4: 0,
        srval_q1: 0, srval_q2: 0, srval_q3: 0, srval_q4: 0,
        serdes_mode: "MASTER", iobdelay: "IFD"},
        serdest, serdest_inverted.slowClock, clocked_by serdes_clock);
    IserdesE2 slave_data <- mkISERDESE2( ISERDESE2_Config{
        data_rate: "DDR", data_width: 10,
        dyn_clk_inv_en: "FALSE", dyn_clkdiv_inv_en: "FALSE",
        interface_type: "NETWORKING", num_ce: 2, ofb_used: "FALSE",
        init_q1: 0, init_q2: 0, init_q3: 0, init_q4: 0,
        srval_q1: 0, srval_q2: 0, srval_q3: 0, srval_q4: 0,
        serdes_mode: "SLAVE", iobdelay: "NONE"},
        serdest, serdest_inverted.slowClock, clocked_by serdes_clock);
    Reg#(Bit#(1)) bvi_delay_wren_wire <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) bvi_reset_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(3)) dcounter <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(DState)  dstate <- mkReg(DIdle, clocked_by serdes_clock, reset_by serdes_reset);
//
    Reg#(Bit#(3)) ctrl_ric <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) ctrl_fifo_reset <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(3)) ctrl_SAMPLEIN <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) tempfifo_wren <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) iserdes_data <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) ctrl_bitslip <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
//
    Reg#(Bit#(1)) itemreq <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) dreqpipe0 <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) dreqpipe1 <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(3)) sync_ric <- mkReg(3'b100, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) sync_bitslip <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) fifo_reset <- mkReg(1, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) fifo_wren_sync <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) dfifo_wren_r <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) dfifo_reset_r <- mkReg(1, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) dackint <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(3)) ireset_inc_ce <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) iserdes_bitslip <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) sync_data <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);

    rule clkdiv_rule if (bvi_reset_reg != 0);
        let ds = dstate;
        let dc = dcounter;
        let da = dackint;
        let fwsync = 0;

        dc = dc - 1;
        dreqpipe0 <= itemreq;
        dreqpipe1 <= dreqpipe0;
        dfifo_reset_r <= ctrl_fifo_reset;
        fifo_reset <= dfifo_reset_r;
        dfifo_wren_r <= tempfifo_wren;
        if (bvi_delay_wren_wire == 1 && ctrl_SAMPLEIN[2] == 1)
            fwsync = dfifo_wren_r;
        else
            fwsync = tempfifo_wren;
        ireset_inc_ce <= sync_ric;
        iserdes_bitslip <= sync_bitslip;
        sync_data <= iserdes_data;
        if (dstate == DIdle && dreqpipe1 == 1)
            begin
            sync_ric <= ctrl_ric;
            sync_bitslip <= ctrl_bitslip;
            dc = 3'b011;
            ds = DLow;
            end
        if (dstate == DValid && dcounter >= 3'b100)
            begin
            da = 1;
            ds = DLow;
            end
        if (dstate == DLow && dreqpipe1 == 0)
            begin
            da = 0;
            ds = DIdle;
            end
        dstate <= ds;
        dcounter <= dc;
        dackint <= da;
        fifo_wren_sync <= fwsync;
    endrule

    rule setrule;
        dfifo.reset(serbvi.iodelay_reset_inc_ce()[2]);
        delaye2.reset(serbvi.iodelay_reset_inc_ce()[2]);
        delaye2.cinvctrl(0);
        delaye2.cntvaluein(0);
        delaye2.ld(0);
        delaye2.ldpipeen(0);
        delaye2.datain(0);
        delaye2.inc(serbvi.iodelay_reset_inc_ce()[1] == 1);
        delaye2.ce(serbvi.iodelay_reset_inc_ce()[0]);
    endrule

    rule serdesdata_rule;
        let dout = 10'b0;
        master_data.d(0);
        master_data.bitslip(serbvi.bitslip());
        master_data.ce1(1);
        master_data.ce2(1);
        master_data.ddly(delaye2.dataout());
        master_data.ofb(0);
        master_data.dynclkdivsel(0);
        master_data.dynclksel(0);
        master_data.shiftin1(0);
        master_data.shiftin2(0);
        master_data.oclk(0);
        master_data.oclkb(0);
        master_data.reset(serbvi.iodelay_reset_inc_ce()[2]);
        slave_data.d(0);
        slave_data.bitslip(serbvi.bitslip());
        slave_data.ce1(1);
        slave_data.ce2(1);
        slave_data.ddly(0);
        slave_data.ofb(0);
        slave_data.dynclkdivsel(0);
        slave_data.dynclksel(0);
        slave_data.shiftin1(master_data.shiftout1());
        slave_data.shiftin2(master_data.shiftout2());
        dout = {slave_data.q4(), slave_data.q3(), master_data.q8(),
           master_data.q7(), master_data.q6(), master_data.q5(),
           master_data.q4(), master_data.q3(), master_data.q2(), master_data.q1()};
        slave_data.oclk(0);
        slave_data.oclkb(0);
        slave_data.reset(serbvi.iodelay_reset_inc_ce()[2]);
        serbvi.dataout(dout);
        dfifo.di({6'b0,dout});
    endrule

    rule wren_dfifo_rule;
        dfifo.wren(serbvi.fifo_wren_sync());
    endrule

    rule serdesrule;
        serbvi.align_start(align_start);
        serbvi.autoalign(autoalign);
        serbvi.training(training);
        serbvi.manual_tap(manual_tap);
        dfifo.rden(rden);
    endrule

    method Action ibufdso(Bit#(1) v);
        delaye2.idatain(v);
    endmethod
    method Bit#(1)                align_busy();
        return serbvi.align_busy();
    endmethod
    method Bit#(1)                aligned();
        return serbvi.aligned();
    endmethod
    method Bit#(1)                sampleinfirstbit();
        return serbvi.samplein()[2];
    endmethod
    method Bit#(1)                sampleinlastbit();
        return serbvi.samplein()[1];
    endmethod
    method Bit#(1)                sampleinotherbit();
        return serbvi.samplein()[0];
    endmethod
    method Action                 delay_wren(Bit#(1) v);
        bvi_delay_wren_wire <= v;
        serbvi.delay_wren(v);
    endmethod
    method Action                 fifo_wren(Bit#(1) v);
        serbvi.fifo_wren(v);
    endmethod
    method Action                 reset(Bit#(1) v);
        bvi_reset_reg <= v;
        serbvi.reset(v);
    endmethod
    method Bit#(1)                empty();
        return dfifo.empty();
    endmethod
    method Bit#(10)               dataout();
        return dfifo.dataout()[9:0];
    endmethod
endmodule: mkIserdesDatadeser

(* always_enabled *)
interface ImageonSerdesPins;
    method Action io_vita_sync_p(Bit#(1) v);
    method Action io_vita_sync_n(Bit#(1) v);
    method Action io_vita_data_p(Bit#(4) v);
    method Action io_vita_data_n(Bit#(4) v);
    method Action io_vita_clk_p(Bit#(1) v);
    method Action io_vita_clk_n(Bit#(1) v);
endinterface

interface ImageonSerdesControl;
    method Action set_decoder_control(Bit#(32) v);
    method Action set_iserdes_control(Bit#(32) v);
    method Action set_serdes_manual_tap(Bit#(10) v);
    method Action set_serdes_training(Bit#(10) v);
    method Bit#(32) get_iserdes_control();
endinterface

interface SerdesData;
    method Wire#(Bit#(1)) reset();
    method Bit#(1) raw_empty();
    method Bit#(50) raw_data();
endinterface

interface ISerdes;
    interface ImageonSerdesControl control;
    interface ImageonSerdesPins pins;
    interface SerdesData data;
endinterface

module mkISerdes#(Clock axi_clock, Reset axi_reset)(ISerdes);
    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();

    Wire#(Bit#(1)) vita_clk_p <- mkDWire(0);
    Wire#(Bit#(1)) vita_clk_n <- mkDWire(0);
    Clock ibufds_clk <- mkClockIBUFDS(vita_clk_p, vita_clk_n);
    ClockGenIfc serdes_clk <- mkBUFR5(ibufds_clk);
    Clock serdes_clock = serdes_clk.gen_clk;
    Reset serdes_reset <- mkAsyncReset(2, defaultReset, serdes_clock);

    Vector#(5, Wire#(Bit#(1))) vita_data_p <- replicateM(mkDWire(0));
    Vector#(5, Wire#(Bit#(1))) vita_data_n <- replicateM(mkDWire(0));
    Reg#(Bit#(1)) decoder_enable_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_auto_align_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_align_start_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_fifo_enable_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    ReadOnly#(Bit#(1)) serdes_fifo_enable_null <- mkNullCrossingWire(serdes_clock, serdes_fifo_enable_reg);
    Reg#(Bit#(10)) serdes_manual_tap_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(10)) serdes_training_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_reset_reg <- mkSyncReg(1, axi_clock, axi_reset, defaultClock);
    ReadOnly#(Bit#(1)) serdes_reset_null <- mkNullCrossingWire(serdes_clock, serdes_reset_reg);

    Wire#(Bit#(50)) raw_data_wire <- mkDWire(0);

    Wire#(Bit#(1)) empty_wire <- mkDWire(0);
    Wire#(Bit#(1)) bittest_wire <- mkDWire(0);
    Reg#(Bit#(1)) delay_wren_r_reg <-mkReg(0);
    Reg#(Bit#(1)) delay_wren_r2_reg <- mkSyncReg(0, defaultClock, defaultReset, serdes_clock);
    Reg#(Bit#(1)) delay_wren_c_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) fifo_wren_r2_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) fifo_wren_c_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);

    ClockGenIfc serdest_clk <- mkBUFIO(ibufds_clk);
    Vector#(5, ReadOnly#(Bit#(1))) ibufds_v;
    for (Integer i = 0; i < 5; i = i + 1)
        ibufds_v[i] <- mkIBUFDS(vita_data_p[i], vita_data_n[i]);
    Reg#(Bit#(1)) serdes_align_busy_temp <- mkReg(0);
    Reg#(Bit#(1)) serdes_align_busy_reg <- mkSyncReg(0, defaultClock, defaultReset, axi_clock);
    Reg#(Bit#(1)) serdes_aligned_temp <- mkReg(0);
    Reg#(Bit#(1)) serdes_aligned_reg <- mkSyncReg(0, defaultClock, defaultReset, axi_clock);
    Wire#(Bit#(1)) new_raw_empty_wire <- mkDWire(0);
    Vector#(5, IserdesDatadeser) serdes_v <- replicateM(mkIserdesDatadeser(serdes_clock, serdes_reset, serdest_clk.gen_clk,
	  serdes_align_start_reg, serdes_auto_align_reg, serdes_training_reg,
	  serdes_manual_tap_reg, decoder_enable_reg));

    rule serdes_copybits;
        serdes_aligned_reg <= serdes_aligned_temp;
        serdes_align_busy_reg <= serdes_align_busy_temp;
    endrule

    rule sendup_imageon_clock;
       Bit#(5) alignbusyw = 0;
       Bit#(5) alignedw = 0;
       Bit#(5) firstw = 0;
       Bit#(5) lastw = 0;
       Bit#(5) otherw = 0;
       Bit#(5) emptyw = 0;
       Bit#(50) rawdataw = 0;
       for (Bit#(8) i = 0; i < 5; i = i+1) begin
	  serdes_v[i].ibufdso(ibufds_v[i]);
	  alignbusyw[i] = serdes_v[i].align_busy();
	  alignedw[i] = serdes_v[i].aligned();
	  firstw[i] = serdes_v[i].sampleinfirstbit();
	  lastw[i] = serdes_v[i].sampleinlastbit();
	  otherw[i] = serdes_v[i].sampleinotherbit();
	  emptyw[i] = serdes_v[i].empty();
	  rawdataw[(i+1)*10-1: i*10] = serdes_v[i].dataout();
       end
       serdes_align_busy_temp <= pack(alignbusyw != 0);
       serdes_aligned_temp <= pack(alignedw == 5'b11111);
       bittest_wire <= pack(otherw == 0 && firstw != 0 && lastw != 0);
       empty_wire <= pack(emptyw != 0);
       raw_data_wire <= rawdataw;
    endrule

    rule sendup_sdes_clock;
    for (Bit#(8) i = 0; i < 5; i = i+1) begin
       serdes_v[i].reset(serdes_reset_null);
       serdes_v[i].delay_wren(delay_wren_c_reg);
       serdes_v[i].fifo_wren(serdes_fifo_enable_null);
    end
    endrule

    rule serdes_reset_rule if (serdes_reset_reg == 0);
        new_raw_empty_wire <= 0;
        delay_wren_r_reg <= 0;
        delay_wren_r2_reg <= 0;
    endrule

    rule serdes_resetc_rule if (serdes_reset_null == 0);
        delay_wren_c_reg <= 0;
        fifo_wren_r2_reg <= 0;
        fifo_wren_c_reg <= 0;
    endrule

    rule serdes_calc2 if (serdes_reset_reg == 1);
        new_raw_empty_wire <= empty_wire;
        delay_wren_r_reg <= bittest_wire;
        delay_wren_r2_reg <= delay_wren_r_reg;
    endrule

    rule serdes_calc2c if (serdes_reset_null == 1);
        delay_wren_c_reg <= delay_wren_r2_reg;
        fifo_wren_r2_reg <= serdes_fifo_enable_null;
        fifo_wren_c_reg <= fifo_wren_r2_reg;
    endrule

    interface ImageonSerdesControl control;
	method Action set_serdes_manual_tap(Bit#(10) v);
	    serdes_manual_tap_reg <= v;
	endmethod
	method Action set_serdes_training(Bit#(10) v);
	    serdes_training_reg <= v;
	endmethod
	method Action set_iserdes_control(Bit#(32) v);
	    serdes_reset_reg <= ~v[0];
	    serdes_auto_align_reg <= v[1];
	    serdes_align_start_reg <= v[2];
	    serdes_fifo_enable_reg <= v[3];
	endmethod
	method Bit#(32) get_iserdes_control();
	    let v = 0;
	    v[8] = 1;
	    v[9] = serdes_align_busy_reg;
	    v[10] = serdes_aligned_reg;
	    return v;
	endmethod
	method Action set_decoder_control(Bit#(32) v);
	    decoder_enable_reg <= v[1];
	endmethod
    endinterface

    interface ImageonSerdesPins pins;
        method Action io_vita_sync_p(Bit#(1) v);
            vita_data_p[0] <= v;
        endmethod
        method Action io_vita_sync_n(Bit#(1) v);
            vita_data_n[0] <= v;
        endmethod
        method Action io_vita_data_p(Bit#(4) v);
            for (Integer i = 0; i < 4; i = i + 1)
                vita_data_p[i+1] <= v[i];
        endmethod
        method Action io_vita_data_n(Bit#(4) v);
            for (Integer i = 0; i < 4; i = i + 1)
                vita_data_n[i+1] <= v[i];
        endmethod
        method Action io_vita_clk_p(Bit#(1) v);
            vita_clk_p <= v;
        endmethod
        method Action io_vita_clk_n(Bit#(1) v);
            vita_clk_n <= v;
        endmethod
    endinterface
    interface SerdesData data;
        method Wire#(Bit#(1)) reset();
            return serdes_reset_reg;
        endmethod
        method Bit#(1) raw_empty();
            return new_raw_empty_wire;
        endmethod
        method Bit#(50) raw_data();
            return raw_data_wire;
	endmethod
    endinterface
endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import Clocks :: *;
import XilinxCells::*;
import XbsvXilinxCells::*;

interface Iserdesbvi;
   method Action                 align_start(Bit#(1) v);
   method Bit#(1)                align_busy();
   method Bit#(1)                aligned();
   method Bit#(3)                samplein();
   method Bit#(3)                iodelay_reset_inc_ce();
   method Bit#(1)                bitslip();
   method Action                 autoalign(Bit#(1) v);
   method Action                 training(Bit#(10) v);
   method Action                 manual_tap(Bit#(10) v);
   method Action                 delay_wren(Bit#(1) v);
   method Action                 fifo_wren(Bit#(1) v);
   method Action                 reset(Bit#(1) v);
   method Bit#(1)                fifo_wren_sync();
   method Bit#(1)                fifo_reset();
   method Action                 dataout(Bit#(10) v);
endinterface: Iserdesbvi

import "BVI" iserdes_datadeser =
module mkIserdesbvi#(Clock clkdiv)(Iserdesbvi);
   input_clock clkdiv (CLKDIV) = clkdiv;
   default_clock clock(CLOCK);
      method                  align_start(ALIGN_START) enable((*inhigh*) en1) clocked_by (clock);
      method ALIGN_BUSY       align_busy() clocked_by (clock);
      method ALIGNED          aligned() clocked_by (clock);
      method ISERDES_BITSLIP  bitslip() clocked_by (clkdiv);
      method SAMPLEIN samplein() clocked_by (clock);
      method IODELAY_RESET_INC_CE iodelay_reset_inc_ce() clocked_by (clkdiv);
      method                  autoalign(AUTOALIGN) enable((*inhigh*) en7) clocked_by (clock);
      method                  training(TRAINING) enable((*inhigh*) en8) clocked_by (clock);
      method                  manual_tap(MANUAL_TAP) enable((*inhigh*) en9) clocked_by (clock);
      method                  fifo_wren(FIFO_WREN) enable((*inhigh*) en10) clocked_by (clkdiv);
      method FIFO_WREN_SYNC   fifo_wren_sync() clocked_by (clock);
      method FIFO_RESET       fifo_reset() clocked_by(clkdiv);
      method                  delay_wren(DELAY_WREN) enable((*inhigh*) en11) clocked_by (clkdiv);
      method                  reset(RESET) enable((*inhigh*) en17) clocked_by (clkdiv);
      method                  dataout(ISERDES_DATA) enable((*inhigh*) en18) clocked_by(clkdiv);
   schedule (fifo_reset, fifo_wren, reset, delay_wren, dataout, iodelay_reset_inc_ce, bitslip, align_busy, aligned, samplein, fifo_wren_sync, align_start, autoalign, training, manual_tap, bitslip)
         CF (fifo_reset, fifo_wren, reset, delay_wren, dataout, iodelay_reset_inc_ce, bitslip, align_busy, aligned, samplein, fifo_wren_sync, align_start, autoalign, training, manual_tap, bitslip);
endmodule: mkIserdesbvi

interface IserdesDatadeser;
   method Action                 ibufdso(Bit#(1) v);
   method Bit#(1)                align_busy();
   method Bit#(1)                aligned();
   method Bit#(1)                sampleinfirstbit();
   method Bit#(1)                sampleinlastbit();
   method Bit#(1)                sampleinotherbit();
   method Action                 delay_wren(Bit#(1) v);
   method Action                 fifo_wren(Bit#(1) v);
   method Action                 reset(Bit#(1) v);
   method Bit#(1)                empty();
   method Bit#(10)               dataout();
endinterface: IserdesDatadeser

interface FIFO18;
   method Action   di(Bit#(16) v);
   method Action   rden(Bit#(1) v);
   method Action   wren(Bit#(1) v);
   method Action   reset(Bit#(1) v);
   method Bit#(16) dataout();
   method Bit#(1)  empty();
endinterface: FIFO18

import "BVI" FIFO18 =
module mkFIFO18#(Clock clkdiv)(FIFO18);
    parameter ALMOST_FULL_OFFSET = 'h80;
    parameter ALMOST_EMPTY_OFFSET = 'h80;
    parameter DATA_WIDTH = 18;
    parameter DO_REG = 1;
    parameter EN_SYN = 0;
    parameter FIRST_WORD_FALL_THROUGH = 0;
    parameter SIM_MODE = "SAFE";

    default_clock clock(RDCLK);
    input_clock clkdiv (WRCLK) = clkdiv;
    no_reset;
    port DIP = 0;

    method          reset(RST) enable((*inhigh*) en9) clocked_by (clkdiv);
    method          di(DI) enable((*inhigh*) en0) clocked_by (clkdiv);
    method          rden(RDEN) enable((*inhigh*) en2) clocked_by (clock);
    method          wren(WREN) enable((*inhigh*) en3) clocked_by (clock);
    method DO       dataout() clocked_by(clock);
    method EMPTY    empty() clocked_by(clock);
   schedule (reset, di, rden, wren, dataout, empty) CF (reset, di, rden, wren, dataout, empty);
endmodule: mkFIFO18

module mkIserdesDatadeser#(Clock serdes_clock, Reset serdes_reset, Clock serdest, Bit#(1) align_start,
    Bit#(1) autoalign, Bit#(10) training, Bit#(10) manual_tap, Bit#(1) rden)(IserdesDatadeser);

    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();
    Iserdesbvi serbvi <- mkIserdesbvi(serdes_clock);
    FIFO18 dfifo <- mkFIFO18(serdes_clock);
    IdelayE2 delaye2 <- mkIDELAYE2(IDELAYE2_Config {
        cinvctrl_sel: "FALSE", delay_src: "IDATAIN",
        high_performance_mode: "TRUE",
        idelay_type: "VARIABLE", idelay_value: 0,
        pipe_sel: "FALSE", refclk_frequency: 200, signal_pattern: "DATA"},
        defaultClock, clocked_by serdes_clock);
    ClockDividerIfc serdest_inverted <- mkClockInverter(clocked_by serdest);
    IserdesE2 master_data <- mkISERDESE2( ISERDESE2_Config{
        data_rate: "DDR", data_width: 10,
        dyn_clk_inv_en: "FALSE", dyn_clkdiv_inv_en: "FALSE",
        interface_type: "NETWORKING", num_ce: 2, ofb_used: "FALSE",
        init_q1: 0, init_q2: 0, init_q3: 0, init_q4: 0,
        srval_q1: 0, srval_q2: 0, srval_q3: 0, srval_q4: 0,
        serdes_mode: "MASTER", iobdelay: "IFD"},
        serdest, serdest_inverted.slowClock, clocked_by serdes_clock);
    IserdesE2 slave_data <- mkISERDESE2( ISERDESE2_Config{
        data_rate: "DDR", data_width: 10,
        dyn_clk_inv_en: "FALSE", dyn_clkdiv_inv_en: "FALSE",
        interface_type: "NETWORKING", num_ce: 2, ofb_used: "FALSE",
        init_q1: 0, init_q2: 0, init_q3: 0, init_q4: 0,
        srval_q1: 0, srval_q2: 0, srval_q3: 0, srval_q4: 0,
        serdes_mode: "SLAVE", iobdelay: "NONE"},
        serdest, serdest_inverted.slowClock, clocked_by serdes_clock);

    rule setrule;
        dfifo.reset(serbvi.iodelay_reset_inc_ce()[2]);
        delaye2.reset(serbvi.iodelay_reset_inc_ce()[2]);
        delaye2.cinvctrl(0);
        delaye2.cntvaluein(0);
        delaye2.ld(0);
        delaye2.ldpipeen(0);
        delaye2.datain(0);
        delaye2.inc(serbvi.iodelay_reset_inc_ce()[1] == 1);
        delaye2.ce(serbvi.iodelay_reset_inc_ce()[0]);
    endrule

    rule serdesdata_rule;
        let dout = 10'b0;
        master_data.d(0);
        master_data.bitslip(serbvi.bitslip());
        master_data.ce1(1);
        master_data.ce2(1);
        master_data.ddly(delaye2.dataout());
        master_data.ofb(0);
        master_data.dynclkdivsel(0);
        master_data.dynclksel(0);
        master_data.shiftin1(0);
        master_data.shiftin2(0);
        master_data.oclk(0);
        master_data.oclkb(0);
        master_data.reset(serbvi.iodelay_reset_inc_ce()[2]);
        slave_data.d(0);
        slave_data.bitslip(serbvi.bitslip());
        slave_data.ce1(1);
        slave_data.ce2(1);
        slave_data.ddly(0);
        slave_data.ofb(0);
        slave_data.dynclkdivsel(0);
        slave_data.dynclksel(0);
        slave_data.shiftin1(master_data.shiftout1());
        slave_data.shiftin2(master_data.shiftout2());
        dout = {slave_data.q4(), slave_data.q3(), master_data.q8(),
           master_data.q7(), master_data.q6(), master_data.q5(),
           master_data.q4(), master_data.q3(), master_data.q2(), master_data.q1()};
        slave_data.oclk(0);
        slave_data.oclkb(0);
        slave_data.reset(serbvi.iodelay_reset_inc_ce()[2]);
        serbvi.dataout(dout);
        dfifo.di({6'b0,dout});
    endrule

    rule wren_dfifo_rule;
        dfifo.wren(serbvi.fifo_wren_sync());
    endrule

    rule serdesrule;
        serbvi.align_start(align_start);
        serbvi.autoalign(autoalign);
        serbvi.training(training);
        serbvi.manual_tap(manual_tap);
        dfifo.rden(rden);
    endrule

    method Action ibufdso(Bit#(1) v);
        delaye2.idatain(v);
    endmethod
    method Bit#(1)                align_busy();
        return serbvi.align_busy();
    endmethod
    method Bit#(1)                aligned();
        return serbvi.aligned();
    endmethod
    method Bit#(1)                sampleinfirstbit();
        return serbvi.samplein()[2];
    endmethod
    method Bit#(1)                sampleinlastbit();
        return serbvi.samplein()[1];
    endmethod
    method Bit#(1)                sampleinotherbit();
        return serbvi.samplein()[0];
    endmethod
    method Action                 delay_wren(Bit#(1) v);
        serbvi.delay_wren(v);
    endmethod
    method Action                 fifo_wren(Bit#(1) v);
        serbvi.fifo_wren(v);
    endmethod
    method Action                 reset(Bit#(1) v);
        serbvi.reset(v);
    endmethod
    method Bit#(1)                empty();
        return dfifo.empty();
    endmethod
    method Bit#(10)               dataout();
        return dfifo.dataout()[9:0];
    endmethod
endmodule: mkIserdesDatadeser

(* always_enabled *)
interface ImageonSerdesPins;
    method Action io_vita_sync_p(Bit#(1) v);
    method Action io_vita_sync_n(Bit#(1) v);
    method Action io_vita_data_p(Bit#(4) v);
    method Action io_vita_data_n(Bit#(4) v);
    method Action io_vita_clk_p(Bit#(1) v);
    method Action io_vita_clk_n(Bit#(1) v);
endinterface

interface ImageonSerdesControl;
    method Action set_decoder_control(Bit#(32) v);
    method Action set_iserdes_control(Bit#(32) v);
    method Action set_serdes_manual_tap(Bit#(10) v);
    method Action set_serdes_training(Bit#(10) v);
    method Bit#(32) get_iserdes_control();
endinterface

interface SerdesData;
    method Wire#(Bit#(1)) reset();
    method Bit#(1) raw_empty();
    method Bit#(50) raw_data();
endinterface

interface ISerdes;
    interface ImageonSerdesControl control;
    interface ImageonSerdesPins pins;
    interface SerdesData data;
endinterface

module mkISerdes#(Clock axi_clock, Reset axi_reset)(ISerdes);
    Clock defaultClock <- exposeCurrentClock();
    Reset defaultReset <- exposeCurrentReset();

    Wire#(Bit#(1)) vita_clk_p <- mkDWire(0);
    Wire#(Bit#(1)) vita_clk_n <- mkDWire(0);
    Clock ibufds_clk <- mkClockIBUFDS(vita_clk_p, vita_clk_n);
    ClockGenIfc serdes_clk <- mkBUFR5(ibufds_clk);
    Clock serdes_clock = serdes_clk.gen_clk;
    Reset serdes_reset <- mkAsyncReset(2, defaultReset, serdes_clock);

    Vector#(5, Wire#(Bit#(1))) vita_data_p <- replicateM(mkDWire(0));
    Vector#(5, Wire#(Bit#(1))) vita_data_n <- replicateM(mkDWire(0));
    Reg#(Bit#(1)) decoder_enable_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_auto_align_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_align_start_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_fifo_enable_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    ReadOnly#(Bit#(1)) serdes_fifo_enable_null <- mkNullCrossingWire(serdes_clock, serdes_fifo_enable_reg);
    Reg#(Bit#(10)) serdes_manual_tap_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(10)) serdes_training_reg <- mkSyncReg(0, axi_clock, axi_reset, defaultClock);
    Reg#(Bit#(1)) serdes_reset_reg <- mkSyncReg(1, axi_clock, axi_reset, defaultClock);
    ReadOnly#(Bit#(1)) serdes_reset_null <- mkNullCrossingWire(serdes_clock, serdes_reset_reg);

    Wire#(Bit#(50)) raw_data_wire <- mkDWire(0);

    Wire#(Bit#(1)) empty_wire <- mkDWire(0);
    Wire#(Bit#(1)) bittest_wire <- mkDWire(0);
    Reg#(Bit#(1)) delay_wren_r_reg <-mkReg(0);
    Reg#(Bit#(1)) delay_wren_r2_reg <- mkSyncReg(0, defaultClock, defaultReset, serdes_clock);
    Reg#(Bit#(1)) delay_wren_c_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) fifo_wren_r2_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);
    Reg#(Bit#(1)) fifo_wren_c_reg <- mkReg(0, clocked_by serdes_clock, reset_by serdes_reset);

    ClockGenIfc serdest_clk <- mkBUFIO(ibufds_clk);
    Vector#(5, ReadOnly#(Bit#(1))) ibufds_v;
    for (Integer i = 0; i < 5; i = i + 1)
        ibufds_v[i] <- mkIBUFDS(vita_data_p[i], vita_data_n[i]);
    Reg#(Bit#(1)) serdes_align_busy_temp <- mkReg(0);
    Reg#(Bit#(1)) serdes_align_busy_reg <- mkSyncReg(0, defaultClock, defaultReset, axi_clock);
    Reg#(Bit#(1)) serdes_aligned_temp <- mkReg(0);
    Reg#(Bit#(1)) serdes_aligned_reg <- mkSyncReg(0, defaultClock, defaultReset, axi_clock);
    Wire#(Bit#(1)) new_raw_empty_wire <- mkDWire(0);
    Vector#(5, IserdesDatadeser) serdes_v <- replicateM(mkIserdesDatadeser(serdes_clock, serdes_reset, serdest_clk.gen_clk,
	  serdes_align_start_reg, serdes_auto_align_reg, serdes_training_reg,
	  serdes_manual_tap_reg, decoder_enable_reg));

    rule serdes_copybits;
        serdes_aligned_reg <= serdes_aligned_temp;
        serdes_align_busy_reg <= serdes_align_busy_temp;
    endrule

    rule sendup_imageon_clock;
       Bit#(5) alignbusyw = 0;
       Bit#(5) alignedw = 0;
       Bit#(5) firstw = 0;
       Bit#(5) lastw = 0;
       Bit#(5) otherw = 0;
       Bit#(5) emptyw = 0;
       Bit#(50) rawdataw = 0;
       for (Bit#(8) i = 0; i < 5; i = i+1) begin
	  serdes_v[i].ibufdso(ibufds_v[i]);
	  alignbusyw[i] = serdes_v[i].align_busy();
	  alignedw[i] = serdes_v[i].aligned();
	  firstw[i] = serdes_v[i].sampleinfirstbit();
	  lastw[i] = serdes_v[i].sampleinlastbit();
	  otherw[i] = serdes_v[i].sampleinotherbit();
	  emptyw[i] = serdes_v[i].empty();
	  rawdataw[(i+1)*10-1: i*10] = serdes_v[i].dataout();
       end
       serdes_align_busy_temp <= pack(alignbusyw != 0);
       serdes_aligned_temp <= pack(alignedw == 5'b11111);
       bittest_wire <= pack(otherw == 0 && firstw != 0 && lastw != 0);
       empty_wire <= pack(emptyw != 0);
       raw_data_wire <= rawdataw;
    endrule

    rule sendup_sdes_clock;
    for (Bit#(8) i = 0; i < 5; i = i+1) begin
       serdes_v[i].reset(serdes_reset_null);
       serdes_v[i].delay_wren(delay_wren_c_reg);
       serdes_v[i].fifo_wren(serdes_fifo_enable_null);
    end
    endrule

    rule serdes_reset_rule if (serdes_reset_reg == 0);
        new_raw_empty_wire <= 0;
        delay_wren_r_reg <= 0;
        delay_wren_r2_reg <= 0;
    endrule

    rule serdes_resetc_rule if (serdes_reset_null == 0);
        delay_wren_c_reg <= 0;
        fifo_wren_r2_reg <= 0;
        fifo_wren_c_reg <= 0;
    endrule

    rule serdes_calc2 if (serdes_reset_reg == 1);
        new_raw_empty_wire <= empty_wire;
        delay_wren_r_reg <= bittest_wire;
        delay_wren_r2_reg <= delay_wren_r_reg;
    endrule

    rule serdes_calc2c if (serdes_reset_null == 1);
        delay_wren_c_reg <= delay_wren_r2_reg;
        fifo_wren_r2_reg <= serdes_fifo_enable_null;
        fifo_wren_c_reg <= fifo_wren_r2_reg;
    endrule

    interface ImageonSerdesControl control;
	method Action set_serdes_manual_tap(Bit#(10) v);
	    serdes_manual_tap_reg <= v;
	endmethod
	method Action set_serdes_training(Bit#(10) v);
	    serdes_training_reg <= v;
	endmethod
	method Action set_iserdes_control(Bit#(32) v);
	    serdes_reset_reg <= ~v[0];
	    serdes_auto_align_reg <= v[1];
	    serdes_align_start_reg <= v[2];
	    serdes_fifo_enable_reg <= v[3];
	endmethod
	method Bit#(32) get_iserdes_control();
	    let v = 0;
	    v[8] = 1;
	    v[9] = serdes_align_busy_reg;
	    v[10] = serdes_aligned_reg;
	    return v;
	endmethod
	method Action set_decoder_control(Bit#(32) v);
	    decoder_enable_reg <= v[1];
	endmethod
    endinterface

    interface ImageonSerdesPins pins;
        method Action io_vita_sync_p(Bit#(1) v);
            vita_data_p[0] <= v;
        endmethod
        method Action io_vita_sync_n(Bit#(1) v);
            vita_data_n[0] <= v;
        endmethod
        method Action io_vita_data_p(Bit#(4) v);
            for (Integer i = 0; i < 4; i = i + 1)
                vita_data_p[i+1] <= v[i];
        endmethod
        method Action io_vita_data_n(Bit#(4) v);
            for (Integer i = 0; i < 4; i = i + 1)
                vita_data_n[i+1] <= v[i];
        endmethod
        method Action io_vita_clk_p(Bit#(1) v);
            vita_clk_p <= v;
        endmethod
        method Action io_vita_clk_n(Bit#(1) v);
            vita_clk_n <= v;
        endmethod
    endinterface
    interface SerdesData data;
        method Wire#(Bit#(1)) reset();
            return serdes_reset_reg;
        endmethod
        method Bit#(1) raw_empty();
            return new_raw_empty_wire;
        endmethod
        method Bit#(50) raw_data();
            return raw_data_wire;
	endmethod
    endinterface
endmodule


fix missing tag to suppress warning during bluespec compilation

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;

import ConnectalMemory::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;

module mkMemreadEngine(MemreadEngineV#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(TLog#(numServers)), MemTypes::MemengineCmd), TMin#(2,TLog#(numServers)))
	    ,Pipe::FunnelPipesPipelined#(1, numServers, Tuple2#(Bit#(dataWidth), Bool),TMin#(2, TLog#(numServers)))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(d__, TLog#(numServers), 6)
	    );
   let rv <- mkMemreadEngineBuff(256);
   return rv;
endmodule

module mkMemreadEngineBuff#(Integer bufferSizeBytes) (MemreadEngineV#(dataWidth, cmdQDepth, numServers))
   provisos (Div#(dataWidth,8,dataWidthBytes),
	     Mul#(dataWidthBytes,8,dataWidth),
	     Log#(dataWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Mul#(cmdQDepth,numServers,cmdBuffSz),
	     Log#(cmdBuffSz, cmdBuffAddrSz),
	     Log#(numServers, serverIdxSz),
	     Add#(1,logCmdQDepth, outCntSz),
	     Add#(1, c__, numServers),
	     Add#(b__, TLog#(numServers), cmdBuffAddrSz),
	     Add#(e__, TLog#(numServers), TAdd#(1, cmdBuffAddrSz)),
	     Add#(a__, serverIdxSz, cmdBuffAddrSz),
	     Min#(2,TLog#(numServers),bpc),
	     FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(serverIdxSz),MemengineCmd),bpc),
	     FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(dataWidth),Bool),bpc),
	     Add#(d__, TLog#(numServers), TAdd#(1, serverIdxSz)),
	     Add#(f__, serverIdxSz, 6));

   let verbose = False;

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs1 <- replicateM(mkReg(0));
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs0 <- replicateM(mkReg(0));
   Vector#(numServers, ConfigCounter#(16))     buffCap <- replicateM(mkConfigCounter(fromInteger(bufferSizeBeats)));
   UGBramFifos#(numServers,cmdQDepth,MemengineCmd) cmdBuf <- mkUGBramFifos;

   Reg#(Bool) load_in_progress <- mkReg(False);
   FIFO#(Tuple3#(MemengineCmd,Bool,Bool))         loadf_b <- mkSizedFIFO(1);
   FIFO#(Tuple2#(Bit#(serverIdxSz),MemengineCmd)) loadf_c <- mkSizedFIFO(1);
   FIFO#(Tuple3#(Bit#(8),Bit#(serverIdxSz),Bool))   workf <- mkSizedFIFO(32); // isthis the right size?

   Vector#(numServers, FIFO#(void))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(Tuple2#(Bit#(serverIdxSz), MemengineCmd))) cmds_in <- replicateM(mkSizedFIFOF(1));
   FunnelPipe#(1, numServers, Tuple2#(Bit#(serverIdxSz), MemengineCmd),bpc) cmds_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,cmds_in));

   FIFOF#(Tuple2#(Bit#(TLog#(numServers)), Tuple2#(Bit#(dataWidth),Bool))) read_data <- mkFIFOF;
   UnFunnelPipe#(1, numServers, Tuple2#(Bit#(dataWidth),Bool),bpc) read_data_unfunnel <- mkUnFunnelPipesPipelined(cons(toPipeOut(read_data),nil));
   Vector#(numServers, FIFOF#(Tuple2#(Bit#(dataWidth),Bool)))  read_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));
   Vector#(numServers, PipeIn#(Tuple2#(Bit#(dataWidth),Bool))) foo = map(toPipeIn, read_data_buffs);
   zipWithM(mkConnection, read_data_unfunnel, foo);
   function PipeOut#(Bit#(dataWidth)) check_out(PipeOut#(Tuple2#(Bit#(dataWidth),Bool)) x, Integer i) =
      (interface PipeOut;
	  method Bit#(dataWidth) first;
	     return tpl_1(x.first);
	  endmethod
	  method Action deq;
	     x.deq;
	     buffCap[i].increment(1);
	     if (tpl_2(x.first))
		outfs[i].enq(?);
	  endmethod
	  method Bool notEmpty = x.notEmpty;
       endinterface);
   Vector#(numServers, PipeOut#(Bit#(dataWidth))) read_data_pipes = zipWith(check_out, map(toPipeOut,read_data_buffs), genVector);

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(TAdd#(1,serverIdxSz))) loadIdx <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   let cmd_q_depth = fromInteger(valueOf(cmdQDepth));

   function Action incr_loadIdx =
      (action
       if(loadIdx+1 >= fromInteger(valueOf(numServers)))
	  loadIdx <= 0;
       else
	  loadIdx <= loadIdx+1;
       endaction);

   rule store_cmd;
      match {.idx, .cmd} <- toGet(cmds_in_funnel[0]).get;
      outs1[idx] <= outs1[idx]+1;
      cmdBuf.enq(idx,cmd);
      if (verbose) $display("mkMemreadEngineBuff::store_cmd %d %d", idx, buffCap[idx].read);
   endrule

   rule load_ctxt_a (!load_in_progress);
      if (outs1[loadIdx] > 0) begin
	 load_in_progress <= True;
	 cmdBuf.first_req(truncate(loadIdx));
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_a %d", loadIdx);
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b;
      let cmd <- cmdBuf.first_resp;
      let cond0 = buffCap[loadIdx].read() >= unpack(extend(cmd.burstLen>>beat_shift));
      let cond1 = cmd.len <= extend(cmd.burstLen);
      loadf_b.enq(tuple3(cmd,cond0,cond1));
      if (verbose) $display("mkMemreadEngineBuff::load_ctxt_b %d %d", buffCap[loadIdx].read(), cmd.burstLen>>beat_shift);
   endrule

   rule load_ctxt_c;
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_c %d %d", cmd.len, loadIdx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 buffCap[loadIdx].decrement(unpack(extend(x>>beat_shift)));
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 if (cond1) begin
	    //$display("load_ctxt_b cond1");
	    outs1[loadIdx] <= outs1[loadIdx]-1;
	    cmdBuf.deq(truncate(loadIdx));
	 end
	 else begin
	    let new_cmd = MemengineCmd{sglId:cmd.sglId, base:cmd.base+extend(cmd.burstLen),
				       burstLen:cmd.burstLen, len:cmd.len-extend(cmd.burstLen), tag: ?};
	    cmdBuf.upd_head(truncate(loadIdx),new_cmd);
	 end
      end
   endrule

   function MemreadServer#(dataWidth) toMemreadServer(Server#(MemengineCmd,Bool) cs, PipeOut#(Bit#(dataWidth)) p) =
      (interface MemreadServer;
	  interface cmdServer = cs;
	  interface dataPipe  = p;
       endinterface);

   Vector#(numServers, Server#(MemengineCmd,Bool)) rs;
   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      rs[i] = (interface Server#(MemengineCmd,Bool);
		  interface Put request;
		     method Action put(MemengineCmd c) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			let mdw = ((c.len)/dw)*dw != c.len;
			let bbl = extend(c.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported burstLen %d %d", bsb, c.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported len %d", c.len);
			end
			else begin
`endif
	 		   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(tuple2(fromInteger(i),c));
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			outfs[i].deq;
	 		outs0[i] <= outs0[i]-1;
			return True;
		     endmethod
		  endinterface
	       endinterface);
   interface readServers = rs;
   interface MemReadClient dmaClient;
      interface Get readReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    let last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    workf.enq(tuple3(truncate(bl>>beat_shift), idx, last));
	    //$display("readReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: (cmd.tag << valueOf(serverIdxSz)) | extend(idx)};
	 endmethod
      endinterface
      interface Put readData;
	 method Action put(MemData#(dataWidth) d);
	    match {.rc, .idx, .last} = workf.first;
	    let new_respCnt = respCnt+1;
	    let l = False;
	    //$display("%h %d", d.data, idx);
	    if (new_respCnt == rc) begin
	       respCnt <= 0;
	       workf.deq;
	       //$display("eob %d", idx);
	       l = last;
	    end
	    else begin
	       respCnt <= new_respCnt;
	    end
	    read_data.enq(tuple2(idx,tuple2(d.data,l)));
	 endmethod
      endinterface
   endinterface
   interface dataPipes = read_data_pipes;
   interface read_servers = zipWith(toMemreadServer, rs, read_data_pipes);
endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Vector::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;

import ConnectalMemory::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;

module mkMemreadEngine(MemreadEngineV#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(TLog#(numServers)), MemTypes::MemengineCmd), TMin#(2,TLog#(numServers)))
	    ,Pipe::FunnelPipesPipelined#(1, numServers, Tuple2#(Bit#(dataWidth), Bool),TMin#(2, TLog#(numServers)))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(d__, TLog#(numServers), 6)
	    );
   let rv <- mkMemreadEngineBuff(256);
   return rv;
endmodule

module mkMemreadEngineBuff#(Integer bufferSizeBytes) (MemreadEngineV#(dataWidth, cmdQDepth, numServers))
   provisos (Div#(dataWidth,8,dataWidthBytes),
	     Mul#(dataWidthBytes,8,dataWidth),
	     Log#(dataWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Mul#(cmdQDepth,numServers,cmdBuffSz),
	     Log#(cmdBuffSz, cmdBuffAddrSz),
	     Log#(numServers, serverIdxSz),
	     Add#(1,logCmdQDepth, outCntSz),
	     Add#(1, c__, numServers),
	     Add#(b__, TLog#(numServers), cmdBuffAddrSz),
	     Add#(e__, TLog#(numServers), TAdd#(1, cmdBuffAddrSz)),
	     Add#(a__, serverIdxSz, cmdBuffAddrSz),
	     Min#(2,TLog#(numServers),bpc),
	     FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(serverIdxSz),MemengineCmd),bpc),
	     FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(dataWidth),Bool),bpc),
	     Add#(d__, TLog#(numServers), TAdd#(1, serverIdxSz)),
	     Add#(f__, serverIdxSz, 6));

   let verbose = False;

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs1 <- replicateM(mkReg(0));
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs0 <- replicateM(mkReg(0));
   Vector#(numServers, ConfigCounter#(16))     buffCap <- replicateM(mkConfigCounter(fromInteger(bufferSizeBeats)));
   UGBramFifos#(numServers,cmdQDepth,MemengineCmd) cmdBuf <- mkUGBramFifos;

   Reg#(Bool) load_in_progress <- mkReg(False);
   FIFO#(Tuple3#(MemengineCmd,Bool,Bool))         loadf_b <- mkSizedFIFO(1);
   FIFO#(Tuple2#(Bit#(serverIdxSz),MemengineCmd)) loadf_c <- mkSizedFIFO(1);
   FIFO#(Tuple3#(Bit#(8),Bit#(serverIdxSz),Bool))   workf <- mkSizedFIFO(32); // isthis the right size?

   Vector#(numServers, FIFO#(void))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(Tuple2#(Bit#(serverIdxSz), MemengineCmd))) cmds_in <- replicateM(mkSizedFIFOF(1));
   FunnelPipe#(1, numServers, Tuple2#(Bit#(serverIdxSz), MemengineCmd),bpc) cmds_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,cmds_in));

   FIFOF#(Tuple2#(Bit#(TLog#(numServers)), Tuple2#(Bit#(dataWidth),Bool))) read_data <- mkFIFOF;
   UnFunnelPipe#(1, numServers, Tuple2#(Bit#(dataWidth),Bool),bpc) read_data_unfunnel <- mkUnFunnelPipesPipelined(cons(toPipeOut(read_data),nil));
   Vector#(numServers, FIFOF#(Tuple2#(Bit#(dataWidth),Bool)))  read_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));
   Vector#(numServers, PipeIn#(Tuple2#(Bit#(dataWidth),Bool))) foo = map(toPipeIn, read_data_buffs);
   zipWithM(mkConnection, read_data_unfunnel, foo);
   function PipeOut#(Bit#(dataWidth)) check_out(PipeOut#(Tuple2#(Bit#(dataWidth),Bool)) x, Integer i) =
      (interface PipeOut;
	  method Bit#(dataWidth) first;
	     return tpl_1(x.first);
	  endmethod
	  method Action deq;
	     x.deq;
	     buffCap[i].increment(1);
	     if (tpl_2(x.first))
		outfs[i].enq(?);
	  endmethod
	  method Bool notEmpty = x.notEmpty;
       endinterface);
   Vector#(numServers, PipeOut#(Bit#(dataWidth))) read_data_pipes = zipWith(check_out, map(toPipeOut,read_data_buffs), genVector);

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(TAdd#(1,serverIdxSz))) loadIdx <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   let cmd_q_depth = fromInteger(valueOf(cmdQDepth));

   function Action incr_loadIdx =
      (action
       if(loadIdx+1 >= fromInteger(valueOf(numServers)))
	  loadIdx <= 0;
       else
	  loadIdx <= loadIdx+1;
       endaction);

   rule store_cmd;
      match {.idx, .cmd} <- toGet(cmds_in_funnel[0]).get;
      outs1[idx] <= outs1[idx]+1;
      cmdBuf.enq(idx,cmd);
      if (verbose) $display("mkMemreadEngineBuff::store_cmd %d %d", idx, buffCap[idx].read);
   endrule

   rule load_ctxt_a (!load_in_progress);
      if (outs1[loadIdx] > 0) begin
	 load_in_progress <= True;
	 cmdBuf.first_req(truncate(loadIdx));
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_a %d", loadIdx);
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b;
      let cmd <- cmdBuf.first_resp;
      let cond0 = buffCap[loadIdx].read() >= unpack(extend(cmd.burstLen>>beat_shift));
      let cond1 = cmd.len <= extend(cmd.burstLen);
      loadf_b.enq(tuple3(cmd,cond0,cond1));
      if (verbose) $display("mkMemreadEngineBuff::load_ctxt_b %d %d", buffCap[loadIdx].read(), cmd.burstLen>>beat_shift);
   endrule

   rule load_ctxt_c;
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemreadEngineBuff::load_ctxt_c %d %d", cmd.len, loadIdx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 buffCap[loadIdx].decrement(unpack(extend(x>>beat_shift)));
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 if (cond1) begin
	    //$display("load_ctxt_b cond1");
	    outs1[loadIdx] <= outs1[loadIdx]-1;
	    cmdBuf.deq(truncate(loadIdx));
	 end
	 else begin
	    let new_cmd = MemengineCmd{sglId:cmd.sglId, base:cmd.base+extend(cmd.burstLen),
				       burstLen:cmd.burstLen, len:cmd.len-extend(cmd.burstLen)};
	    cmdBuf.upd_head(truncate(loadIdx),new_cmd);
	 end
      end
   endrule

   function MemreadServer#(dataWidth) toMemreadServer(Server#(MemengineCmd,Bool) cs, PipeOut#(Bit#(dataWidth)) p) =
      (interface MemreadServer;
	  interface cmdServer = cs;
	  interface dataPipe  = p;
       endinterface);

   Vector#(numServers, Server#(MemengineCmd,Bool)) rs;
   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      rs[i] = (interface Server#(MemengineCmd,Bool);
		  interface Put request;
		     method Action put(MemengineCmd c) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			let mdw = ((c.len)/dw)*dw != c.len;
			let bbl = extend(c.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported burstLen %d %d", bsb, c.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemreadEngineBuff::unsupported len %d", c.len);
			end
			else begin
`endif
	 		   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(tuple2(fromInteger(i),c));
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			outfs[i].deq;
	 		outs0[i] <= outs0[i]-1;
			return True;
		     endmethod
		  endinterface
	       endinterface);
   interface readServers = rs;
   interface MemReadClient dmaClient;
      interface Get readReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    let last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    workf.enq(tuple3(truncate(bl>>beat_shift), idx, last));
	    //$display("readReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: (cmd.tag << valueOf(serverIdxSz)) | extend(idx)};
	 endmethod
      endinterface
      interface Put readData;
	 method Action put(MemData#(dataWidth) d);
	    match {.rc, .idx, .last} = workf.first;
	    let new_respCnt = respCnt+1;
	    let l = False;
	    //$display("%h %d", d.data, idx);
	    if (new_respCnt == rc) begin
	       respCnt <= 0;
	       workf.deq;
	       //$display("eob %d", idx);
	       l = last;
	    end
	    else begin
	       respCnt <= new_respCnt;
	    end
	    read_data.enq(tuple2(idx,tuple2(d.data,l)));
	 endmethod
      endinterface
   endinterface
   interface dataPipes = read_data_pipes;
   interface read_servers = zipWith(toMemreadServer, rs, read_data_pipes);
endmodule


fixed pins interface

fixed pins interface

// Copyright (c) 2015 Cornell University.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Arith ::*;
import BuildVector::*;
import ClientServer::*;
import ConfigCounter::*;
import Connectable::*;
import DefaultValue::*;
import FIFO ::*;
import FIFOF ::*;
import GetPut ::*;
import Gearbox ::*;
import Pipe ::*;
import SpecialFIFOs ::*;
import Vector ::*;

import Ethernet::*;
import HostInterface::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import PacketBuffer::*;

interface SonicPins;
   method Action osc_50(Bit#(1) b3d, Bit#(1) b4a, Bit#(1) b4d, Bit#(1) b7a, Bit#(1) b7d, Bit#(1) b8a, Bit#(1) b8d);
   (* prefix="" *)
   method Action user_reset_n(Bit#(1) user_reset_n);
   interface Clock deleteme_unused_clock;
   interface Reset deleteme_unused_reset;
endinterface

typedef 512    TxCredTotal;
typedef 32     TxCredThres;
typedef 100000 TxCredTimeout;

typedef 512    RxCredTotal;
typedef 32     RxCredThres;
typedef 100000 RxCredTimeout;

typedef TDiv#(DataBusWidth,32) DataBusWords;
typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} TxDesc deriving (Eq, Bits);

typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} RxDesc deriving (Eq, Bits);

typedef 16 RxMetadataLen;
typedef struct {
   Bit#(32) filled;
   Bit#(32) len;
   Bit#(64) reserved;
} RxMeta deriving (Eq, Bits);

interface SonicTopRequest;
   method Action sonic_read_version();
   method Action startRead(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numBytes, Bit#(32) burstLen);
   method Action startWrite(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numWords, Bit#(32) burstLen);
   method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
   method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
   method Action resetWrite(Bit#(32) qid);
endinterface

interface SonicTopIndication;
   method Action sonic_read_version_resp(Bit#(32) version);
   method Action readDone(Bit#(32) mismatchCnt);
   method Action writeDone(Bit#(32) v);
   method Action writeTxCred(UInt#(32) v);
endinterface

interface SonicTop;
   interface SonicTopRequest request;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) dmaWriteClient;
   interface Vector#(1, MemReadClient#(DataBusWidth)) dmaReadClient;
   interface SonicPins pins;
endinterface

typedef 12 NumOutstandingRequests;
typedef TMul#(NumOutstandingRequests, TMul#(32, 4)) BufferSizeBytes;
module mkSonicTop#(Clock derivedClock, Reset derivedReset, SonicTopIndication indication)(SonicTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   let verbose = True;

   // Tx Path
   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(TxDesc) newTxDesc <- mkReg(unpack(0));
   FIFOF#(TxDesc) txDescQueue <- mkSizedFIFOF(valueof(TxCredTotal));
   ConfigCounter#(32) txCredFreed <- mkConfigCounter(0);
   FIFOF#(UInt#(32)) txCredCf <- mkSizedFIFOF(8);

   FIFOF#(void) xmitInProgress <- mkSizedFIFOF(1);
   Gearbox#(2, 1, PacketData#(64)) fifoTxData <- mkNto1Gearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   MemreadEngine#(DataBusWidth,NumOutstandingRequests,1) re <- mkMemreadEngineBuff(valueOf(BufferSizeBytes));
   PacketBuffer txPktBuff <- mkPacketBuffer();

   rule everyCycle;
      cycle <= cycle + 1;
   endrule

   rule enqTxDesc (txDescQueue.notFull && newTxDesc.nDesc > 0);
      if (verbose) $display("Test: Enqueue TxDesc %x %d", newTxDesc.offset, newTxDesc.len);
      txDescQueue.enq(newTxDesc);
      newTxDesc.nDesc <= newTxDesc.nDesc-1;
   endrule

   // Credit Writeback is triggered by either timeout or threshold.
   rule txCreditWritebackTimeout ((cycle % fromInteger(valueOf(TxCredTimeout)) == 0) && !txCredCf.notEmpty);
      if (txCredFreed.read != 0) begin
         indication.writeTxCred(txCredFreed.read);
         txCredFreed.decrement(txCredFreed.read);
      end
   endrule

   rule txCreditWritebackThreshold;
      let v <- toGet(txCredCf).get;
      indication.writeTxCred(v);
      if (verbose) $display("Write back cred %d", v);
   endrule

   rule dmaRead;
      let v <- toGet(txDescQueue).get;
      //FIXME: burstlen
      re.readServers[0].request.put(MemengineCmd{tag:0, sglId:v.sglId, base:v.offset, len:v.len,
                                               burstLen:truncate(v.len)});
   endrule

   rule readData;
      if (re.readServers[0].memDataPipe.notEmpty()) begin
         let v <- toGet(re.readServers[0].memDataPipe).get;
         txPktBuff.writeServer.writeData.put(EtherData{sop: v.first, eop: v.last, data:v.data});
      end
   endrule

   rule txMacStart;
      let pktLen <- txPktBuff.readServer.readLen.get;
      txPktBuff.readServer.readReq.put(EtherReq{len: pktLen});
      xmitInProgress.enq(?);
   endrule

   //FIXME::
   function Vector#(2, PacketData#(64)) split(EtherData in);
      Vector#(2, PacketData#(64)) v = defaultValue;
      v[0].sop = in.sop;
      v[0].eop = False;
      v[0].err = 0;
      v[0].empty = 0;
      v[0].data = in.data[63:0];
      v[0].valid = True;
      v[1].sop = False;
      v[1].eop = in.eop;
      v[1].err = 0;
      v[1].empty = 0;
      v[1].data = in.data[127:64];
      v[1].valid = True;
      return v;
   endfunction
   rule txMacInProgress if (xmitInProgress.notEmpty);
      let v <- txPktBuff.readServer.readData.get;
      //if (verbose) $display("Send: data=%x sop=%x eop=%x", v.data, v.sop, v.eop);
      // rule to read 128 bit packet from buffer, and enqueue 64 bit twice to txfifo
      Vector#(2, PacketData#(64)) txdata = split(v);
      fifoTxData.enq(txdata);

      if (v.eop) begin
         txCredFreed.increment(1);
         // tigger writeback is freed txcred is more than threshold
         if ((txCredFreed.read>=fromInteger(valueOf(TxCredThres))) && txCredCf.notFull) begin
            txCredCf.enq(txCredFreed.read);
            txCredFreed.decrement(txCredFreed.read);
         end
         xmitInProgress.deq;
      end
   endrule

 //  rule txMacClockCrossing if (fifoTxData.notEmpty);
 //     let data = fifoTxData.first;
 //     let temp = head(data);
 //     //if(verbose) $display("Send %d data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
 //     fifoTxData.deq;
 //  endrule

   //SyncFIFOIfc#(Bit#(64)) txMacSyncFifo <- mkSyncFIFO(8, clk_156_25, rst_156_n, host.portalClock);
   //PipeOut#(PacketData) txMacSyncPipeOut = toPipeOut(txMacSyncFifo);
   //PipeIn#(PacketData) txMacSyncPipeIn = toPipeIn(txMacSyncFifo);
   //mkConnection(toPipeOut(txMacFifo), txMacSyncPipeIn);
   //mkConnection(txMacSyncPipeOut, );

   // Rx Path
   FIFOF#(RxDesc)            rxDescQueue <- mkSizedFIFOF(valueof(RxCredTotal));
   FIFOF#(RxMeta)         currRxMetadata <- mkSizedFIFOF(1);
   FIFOF#(Bit#(EtherLen)) recvInProgress <- mkSizedFIFOF(1);
   FIFOF#(Bool)               finishFifo <- mkFIFOF;
   Reg#(RxDesc)                newRxDesc <- mkReg(unpack(0));
   Reg#(Bit#(32))                totalRx <- mkReg(0);
   Reg#(Bit#(EtherLen))     currDmaWrLen <- mkReg(0);
   PacketBuffer                rxPktBuff <- mkPacketBuffer();
   MemwriteEngine#(DataBusWidth,2,1) we <- mkMemwriteEngine;
   Gearbox#(1, 2, PacketData#(64)) fifoRxData <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   rule enqRxDesc(rxDescQueue.notFull && newRxDesc.nDesc>0);
      rxDescQueue.enq(newRxDesc);
      newRxDesc.nDesc <= newRxDesc.nDesc-1;
      totalRx <= totalRx+1;
   endrule
   rule dmaWriteStart;
      let rxDesc <- toGet(rxDescQueue).get;
      let pktLen <- rxPktBuff.readServer.readLen.get;
      // write packet metadata
      we.writeServers[0].cmdServer.request.put(MemengineCmd{tag:0, sglId:rxDesc.sglId,
                                                base:extend(rxDesc.offset - fromInteger(valueOf(RxMetadataLen))),
                                                len:extend(pktLen + fromInteger(valueOf(RxMetadataLen))),
                                                burstLen:truncate(pktLen + fromInteger(valueOf(RxMetadataLen)))});
      $display("SonicTop::dmaWriteStart offset=%x pktlen=%d", rxDesc.offset - fromInteger(valueof(RxMetadataLen)), pktLen);
      currRxMetadata.enq(RxMeta{filled: 1, len: extend(pktLen), reserved: 0});
   endrule
   rule dmaWriteMeta;
      let meta <- toGet(currRxMetadata).get;
      $display("SonicTop::dmaWriteMeta %d: filled=%x, len=%x", cycle, meta.filled, meta.len);
      we.writeServers[0].dataPipe.enq(pack(meta));
      rxPktBuff.readServer.readReq.put(EtherReq{len: truncate(meta.len)});
      recvInProgress.enq(truncate(meta.len));
   endrule
   rule dmaWriteInProgress if (recvInProgress.notEmpty);
      let v <- rxPktBuff.readServer.readData.get;
      $display("SonicTop::dmaWriteInProgress %d: data=%x sop=%x eop=%x", cycle, v.data, v.sop, v.eop);
      we.writeServers[0].dataPipe.enq(extend(v.data));
      if (v.eop) begin
         recvInProgress.deq;
      end
   endrule
   rule dmaWriteFinish;
      let rv <- we.writeServers[0].cmdServer.response.get;
      $display("SonicTop::dmaWriteFinish");
   endrule
   // rule to clock crossing from 156.25MHz to 250MHz with 64bit
   function EtherData combine(Vector#(2, PacketData#(64)) in);
      return EtherData {sop: in[0].sop, eop: in[1].eop, data: {in[1].data, in[0].data}};
   endfunction

   rule rxWriteData;
      // rule to pack 2x64 read from MAC interface to 128bit
      let data = fifoRxData.first; fifoRxData.deq;
      let temp = combine(data);
      $display("Recv %d: data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
      rxPktBuff.writeServer.writeData.put(temp);
   endrule

//   rule rxMacClockCrossing;
//      fifoRxData.enq(data);
//   endrule

   // Network MAC and PHY

   // Loopback
   mkConnection(toPipeOut(fifoTxData), toPipeIn(fifoRxData));

   Reg#(Bit#(32)) tx_cnt <- mkReg(0);
   interface dmaWriteClient = vec(we.dmaClient);
   interface dmaReadClient = vec(re.dmaClient);
   interface SonicTopRequest request;
      method Action sonic_read_version();
         let v = `SonicVersion; //Defined in Makefile as time of compilation.
         indication.sonic_read_version_resp(v);
      endmethod
      method Action startRead(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         $display("rp=%x offset=%x len=%x burstLen=%x, tx_cnt=%d", rp, off, nb, bl, tx_cnt);
         newTxDesc <= TxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
         tx_cnt <= tx_cnt + 1;
      endmethod
      method Action startWrite(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         //$display("NewRxDesc: rp=%x offset=%x len=%x burstLen=%x", rp, off, nb, bl);
         newRxDesc <= RxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
      endmethod
      method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
         let d = EtherData{data: {upper, lower}, sop: unpack(sop), eop: unpack(eop)};
         rxPktBuff.writeServer.writeData.put(d);
      endmethod
      method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
         PacketData#(64) v = defaultValue;
         v.data = data;
         v.sop = unpack(sop);
         v.eop = unpack(eop);
         fifoRxData.enq(vec(v));
      endmethod
      method Action resetWrite(Bit#(32) qid);
         rxDescQueue.clear();
         currRxMetadata.clear();
         recvInProgress.clear();
         finishFifo.clear();
      endmethod
   endinterface
   interface SonicPins pins;
      // Clocks
      interface deleteme_unused_clock = defaultClock;
      interface deleteme_unused_reset = defaultReset;
      // Resets
      // SFP+
   endinterface
endmodule

// Copyright (c) 2015 Cornell University.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Arith ::*;
import BuildVector::*;
import ClientServer::*;
import ConfigCounter::*;
import Connectable::*;
import DefaultValue::*;
import FIFO ::*;
import FIFOF ::*;
import GetPut ::*;
import Gearbox ::*;
import Pipe ::*;
import SpecialFIFOs ::*;
import Vector ::*;

import Ethernet::*;
import HostInterface::*;
import MemTypes::*;
import MemreadEngine::*;
import MemwriteEngine::*;
import PacketBuffer::*;

interface SonicPins;
   method Action osc_50(Bit#(1) b3b, Bit#(1) b3d, Bit#(1) b4a, Bit#(1) b4d, Bit#(1) b7a, Bit#(1) b7d, Bit#(1) b8a, Bit#(1) b8d);
   (* prefix="" *)
   method Action pcie_perst_n(Bit#(1) pcie_perst_n);
   method Action user_reset_n(Bit#(1) user_reset_n);
endinterface

typedef 512    TxCredTotal;
typedef 32     TxCredThres;
typedef 100000 TxCredTimeout;

typedef 512    RxCredTotal;
typedef 32     RxCredThres;
typedef 100000 RxCredTimeout;

typedef TDiv#(DataBusWidth,32) DataBusWords;
typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} TxDesc deriving (Eq, Bits);

typedef struct {
   SGLId sglId;
   Bit#(MemOffsetSize) offset;
   Bit#(32) len;
   Bit#(BurstLenSize) burstLen;
   Bit#(32) nDesc;
} RxDesc deriving (Eq, Bits);

typedef 16 RxMetadataLen;
typedef struct {
   Bit#(32) filled;
   Bit#(32) len;
   Bit#(64) reserved;
} RxMeta deriving (Eq, Bits);

interface SonicTopRequest;
   method Action sonic_read_version();
   method Action startRead(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numBytes, Bit#(32) burstLen);
   method Action startWrite(Bit#(32) pointer, Bit#(32) offset, Bit#(32) numWords, Bit#(32) burstLen);
   method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
   method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
   method Action resetWrite(Bit#(32) qid);
endinterface

interface SonicTopIndication;
   method Action sonic_read_version_resp(Bit#(32) version);
   method Action readDone(Bit#(32) mismatchCnt);
   method Action writeDone(Bit#(32) v);
   method Action writeTxCred(UInt#(32) v);
endinterface

interface SonicTop;
   interface SonicTopRequest request;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) dmaWriteClient;
   interface Vector#(1, MemReadClient#(DataBusWidth)) dmaReadClient;
   interface SonicPins pins;
endinterface

typedef 12 NumOutstandingRequests;
typedef TMul#(NumOutstandingRequests, TMul#(32, 4)) BufferSizeBytes;
module mkSonicTop#(Clock derivedClock, Reset derivedReset, SonicTopIndication indication)(SonicTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   let verbose = True;

   // Tx Path
   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(TxDesc) newTxDesc <- mkReg(unpack(0));
   FIFOF#(TxDesc) txDescQueue <- mkSizedFIFOF(valueof(TxCredTotal));
   ConfigCounter#(32) txCredFreed <- mkConfigCounter(0);
   FIFOF#(UInt#(32)) txCredCf <- mkSizedFIFOF(8);

   FIFOF#(void) xmitInProgress <- mkSizedFIFOF(1);
   Gearbox#(2, 1, PacketData#(64)) fifoTxData <- mkNto1Gearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   MemreadEngine#(DataBusWidth,NumOutstandingRequests,1) re <- mkMemreadEngineBuff(valueOf(BufferSizeBytes));
   PacketBuffer txPktBuff <- mkPacketBuffer();

   rule everyCycle;
      cycle <= cycle + 1;
   endrule

   rule enqTxDesc (txDescQueue.notFull && newTxDesc.nDesc > 0);
      if (verbose) $display("Test: Enqueue TxDesc %x %d", newTxDesc.offset, newTxDesc.len);
      txDescQueue.enq(newTxDesc);
      newTxDesc.nDesc <= newTxDesc.nDesc-1;
   endrule

   // Credit Writeback is triggered by either timeout or threshold.
   rule txCreditWritebackTimeout ((cycle % fromInteger(valueOf(TxCredTimeout)) == 0) && !txCredCf.notEmpty);
      if (txCredFreed.read != 0) begin
         indication.writeTxCred(txCredFreed.read);
         txCredFreed.decrement(txCredFreed.read);
      end
   endrule

   rule txCreditWritebackThreshold;
      let v <- toGet(txCredCf).get;
      indication.writeTxCred(v);
      if (verbose) $display("Write back cred %d", v);
   endrule

   rule dmaRead;
      let v <- toGet(txDescQueue).get;
      //FIXME: burstlen
      re.readServers[0].request.put(MemengineCmd{tag:0, sglId:v.sglId, base:v.offset, len:v.len,
                                               burstLen:truncate(v.len)});
   endrule

   rule readData;
      if (re.readServers[0].memDataPipe.notEmpty()) begin
         let v <- toGet(re.readServers[0].memDataPipe).get;
         txPktBuff.writeServer.writeData.put(EtherData{sop: v.first, eop: v.last, data:v.data});
      end
   endrule

   rule txMacStart;
      let pktLen <- txPktBuff.readServer.readLen.get;
      txPktBuff.readServer.readReq.put(EtherReq{len: pktLen});
      xmitInProgress.enq(?);
   endrule

   //FIXME::
   function Vector#(2, PacketData#(64)) split(EtherData in);
      Vector#(2, PacketData#(64)) v = defaultValue;
      v[0].sop = in.sop;
      v[0].eop = False;
      v[0].err = 0;
      v[0].empty = 0;
      v[0].data = in.data[63:0];
      v[0].valid = True;
      v[1].sop = False;
      v[1].eop = in.eop;
      v[1].err = 0;
      v[1].empty = 0;
      v[1].data = in.data[127:64];
      v[1].valid = True;
      return v;
   endfunction
   rule txMacInProgress if (xmitInProgress.notEmpty);
      let v <- txPktBuff.readServer.readData.get;
      //if (verbose) $display("Send: data=%x sop=%x eop=%x", v.data, v.sop, v.eop);
      // rule to read 128 bit packet from buffer, and enqueue 64 bit twice to txfifo
      Vector#(2, PacketData#(64)) txdata = split(v);
      fifoTxData.enq(txdata);

      if (v.eop) begin
         txCredFreed.increment(1);
         // tigger writeback is freed txcred is more than threshold
         if ((txCredFreed.read>=fromInteger(valueOf(TxCredThres))) && txCredCf.notFull) begin
            txCredCf.enq(txCredFreed.read);
            txCredFreed.decrement(txCredFreed.read);
         end
         xmitInProgress.deq;
      end
   endrule

 //  rule txMacClockCrossing if (fifoTxData.notEmpty);
 //     let data = fifoTxData.first;
 //     let temp = head(data);
 //     //if(verbose) $display("Send %d data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
 //     fifoTxData.deq;
 //  endrule

   //SyncFIFOIfc#(Bit#(64)) txMacSyncFifo <- mkSyncFIFO(8, clk_156_25, rst_156_n, host.portalClock);
   //PipeOut#(PacketData) txMacSyncPipeOut = toPipeOut(txMacSyncFifo);
   //PipeIn#(PacketData) txMacSyncPipeIn = toPipeIn(txMacSyncFifo);
   //mkConnection(toPipeOut(txMacFifo), txMacSyncPipeIn);
   //mkConnection(txMacSyncPipeOut, );

   // Rx Path
   FIFOF#(RxDesc)            rxDescQueue <- mkSizedFIFOF(valueof(RxCredTotal));
   FIFOF#(RxMeta)         currRxMetadata <- mkSizedFIFOF(1);
   FIFOF#(Bit#(EtherLen)) recvInProgress <- mkSizedFIFOF(1);
   FIFOF#(Bool)               finishFifo <- mkFIFOF;
   Reg#(RxDesc)                newRxDesc <- mkReg(unpack(0));
   Reg#(Bit#(32))                totalRx <- mkReg(0);
   Reg#(Bit#(EtherLen))     currDmaWrLen <- mkReg(0);
   PacketBuffer                rxPktBuff <- mkPacketBuffer();
   MemwriteEngine#(DataBusWidth,2,1) we <- mkMemwriteEngine;
   Gearbox#(1, 2, PacketData#(64)) fifoRxData <- mk1toNGearbox(defaultClock, defaultReset, defaultClock, defaultReset);

   rule enqRxDesc(rxDescQueue.notFull && newRxDesc.nDesc>0);
      rxDescQueue.enq(newRxDesc);
      newRxDesc.nDesc <= newRxDesc.nDesc-1;
      totalRx <= totalRx+1;
   endrule
   rule dmaWriteStart;
      let rxDesc <- toGet(rxDescQueue).get;
      let pktLen <- rxPktBuff.readServer.readLen.get;
      // write packet metadata
      we.writeServers[0].cmdServer.request.put(MemengineCmd{tag:0, sglId:rxDesc.sglId,
                                                base:extend(rxDesc.offset - fromInteger(valueOf(RxMetadataLen))),
                                                len:extend(pktLen + fromInteger(valueOf(RxMetadataLen))),
                                                burstLen:truncate(pktLen + fromInteger(valueOf(RxMetadataLen)))});
      $display("SonicTop::dmaWriteStart offset=%x pktlen=%d", rxDesc.offset - fromInteger(valueof(RxMetadataLen)), pktLen);
      currRxMetadata.enq(RxMeta{filled: 1, len: extend(pktLen), reserved: 0});
   endrule
   rule dmaWriteMeta;
      let meta <- toGet(currRxMetadata).get;
      $display("SonicTop::dmaWriteMeta %d: filled=%x, len=%x", cycle, meta.filled, meta.len);
      we.writeServers[0].dataPipe.enq(pack(meta));
      rxPktBuff.readServer.readReq.put(EtherReq{len: truncate(meta.len)});
      recvInProgress.enq(truncate(meta.len));
   endrule
   rule dmaWriteInProgress if (recvInProgress.notEmpty);
      let v <- rxPktBuff.readServer.readData.get;
      $display("SonicTop::dmaWriteInProgress %d: data=%x sop=%x eop=%x", cycle, v.data, v.sop, v.eop);
      we.writeServers[0].dataPipe.enq(extend(v.data));
      if (v.eop) begin
         recvInProgress.deq;
      end
   endrule
   rule dmaWriteFinish;
      let rv <- we.writeServers[0].cmdServer.response.get;
      $display("SonicTop::dmaWriteFinish");
   endrule
   // rule to clock crossing from 156.25MHz to 250MHz with 64bit
   function EtherData combine(Vector#(2, PacketData#(64)) in);
      return EtherData {sop: in[0].sop, eop: in[1].eop, data: {in[1].data, in[0].data}};
   endfunction

   rule rxWriteData;
      // rule to pack 2x64 read from MAC interface to 128bit
      let data = fifoRxData.first; fifoRxData.deq;
      let temp = combine(data);
      $display("Recv %d: data=%x sop=%x eop=%x", cycle, temp.data, temp.sop, temp.eop);
      rxPktBuff.writeServer.writeData.put(temp);
   endrule

//   rule rxMacClockCrossing;
//      fifoRxData.enq(data);
//   endrule

   // Network MAC and PHY

   // Loopback
   mkConnection(toPipeOut(fifoTxData), toPipeIn(fifoRxData));

   Reg#(Bit#(32)) tx_cnt <- mkReg(0);
   interface pins = (interface SonicPins;
      // Clocks

      // Resets
      // SFP+
   endinterface);
   interface dmaWriteClient = vec(we.dmaClient);
   interface dmaReadClient = vec(re.dmaClient);
   interface SonicTopRequest request;
      method Action sonic_read_version();
         let v = `SonicVersion; //Defined in Makefile as time of compilation.
         indication.sonic_read_version_resp(v);
      endmethod
      method Action startRead(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         $display("rp=%x offset=%x len=%x burstLen=%x, tx_cnt=%d", rp, off, nb, bl, tx_cnt);
         newTxDesc <= TxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
         tx_cnt <= tx_cnt + 1;
      endmethod
      method Action startWrite(Bit#(32) rp, Bit#(32) off, Bit#(32) nb, Bit#(32) bl);
         //$display("NewRxDesc: rp=%x offset=%x len=%x burstLen=%x", rp, off, nb, bl);
         newRxDesc <= RxDesc{sglId:rp, offset:extend(off), len:nb, burstLen:truncate(bl), nDesc:1};
      endmethod
      method Action writePacketData(Bit#(64) upper, Bit#(64) lower, Bit#(1) sop, Bit#(1) eop);
         let d = EtherData{data: {upper, lower}, sop: unpack(sop), eop: unpack(eop)};
         rxPktBuff.writeServer.writeData.put(d);
      endmethod
      method Action writeMacData(Bit#(64) data, Bit#(1) sop, Bit#(1) eop);
         PacketData#(64) v = defaultValue;
         v.data = data;
         v.sop = unpack(sop);
         v.eop = unpack(eop);
         fifoRxData.enq(vec(v));
      endmethod
      method Action resetWrite(Bit#(32) qid);
         rxDescQueue.clear();
         currRxMetadata.clear();
         recvInProgress.clear();
         finishFifo.clear();
      endmethod
   endinterface
endmodule