// *************************************************************************** //
// file     : HBFdch.v
// desp     : data channel
// author   : duweitao@cuc.edu.cn
// date     : July.23th.2007
// version  : 0.1
// modified : 
// backup memo: before modify GetBk01ZR() 
// *************************************************************************** // 
`include "timescale.v"
// module dchannel(), data channel 
module dchnnel(
  CLK       ,   // clock , posedge 
  RST       ,   // high level reset asyn
  DINEN     ,   // data input enable
  DIN       ,   // data input  
  PECY      ,   // pe cycle counter
  COEF      ,   // data input enable
  DOUT      ,   // data output  
  DOUTEN    ,   // data output enable
  OUTIDX    ,   // data output index
  CLKOUT    );  // output clock

// external set param 
parameter INPUTWL   = 0;    //  input word length
parameter OUTPUTWL  = 0;    // output word length
parameter COEFWL    = 0;    // coefficent word length
parameter K1        = 0;    
parameter K2        = 0;
parameter K1LOG2    = 0;    
parameter K2LOG2    = 0;
parameter C2Q       = 0;    // clock to Q time in ns
parameter PCYWL     = 0;    // pe cycle word length
parameter OCYWL     = 0;    // output cycle word length
parameter PCYMAX    = 0;    // pe cycle max value
parameter PCYIDL    = 0;    // pe counter IDLE value
parameter ADDRWL    = 0;    // dmem2bk address word length

  input CLK, RST, DINEN; 
  input [COEFWL-1 :0]   COEF; 
  input [INPUTWL-1:0]   DIN;
  input [PCYWL-1:0]     PECY;
  output[OUTPUTWL-1:0]  DOUT;  
  output                DOUTEN;
  output[1:0]           OUTIDX;
  output                CLKOUT;

  wire  [ADDRWL-1:0] dwa0W, dwa1W, dra0W, dra1W;
  wire  dwe0W, dwe1W;
  wire  [INPUTWL-1:0] dwd0W, dwd1W, drd0W, drd1W;
dpath U_dpath(
  .CLK      (CLK    ),
  .RST      (RST    ),
  .DINEN    (DINEN  ),
  .DIN      (DIN    ),
  .PECY     (PECY   ),
  .COEF     (COEF   ),
  .DRD0     (drd0W  ),
  .DRD1     (drd1W  ),
  .DWA0     (dwa0W  ),
  .DWA1     (dwa1W  ),
  .DWE0     (dwe0W  ),
  .DWE1     (dwe1W  ),
  .DWD0     (dwd0W  ),
  .DWD1     (dwd1W  ),
  .DRA0     (dra0W  ),
  .DRA1     (dra1W  ),
  .DOUT     (DOUT   ), 
  .DOUTEN   (DOUTEN ),
  .OUTIDX   (OUTIDX ), 
  .CLKOUT   (CLKOUT));

defparam U_dpath.INPUTWL   = INPUTWL ;    // input data word length
defparam U_dpath.OUTPUTWL  = OUTPUTWL;    // output data word length 
defparam U_dpath.COEFWL    = COEFWL  ;    // coefficent word length
defparam U_dpath.K1        = K1      ;    
defparam U_dpath.K1LOG2    = K1LOG2  ;    
defparam U_dpath.K2        = K2      ;
defparam U_dpath.K2LOG2    = K2LOG2  ;
defparam U_dpath.C2Q       = C2Q     ;    // clock to Q delay in ns
defparam U_dpath.PCYWL     = PCYWL   ;    // pe cycle word length
defparam U_dpath.PCYMAX    = PCYMAX  ;    // pe counter max value
defparam U_dpath.PCYIDL    = PCYIDL  ;    // pe cycle IDLE value
defparam U_dpath.OCYWL     = OCYWL   ;    // output cycle word length
defparam U_dpath.ADDRWL    = ADDRWL  ;    // data ram address word length
defparam U_dpath.DATAWL    = INPUTWL ;    // data ram data word length

dmem2bk U_dmem2bk(
  .CLK   (CLK   ),   // clock , posedge 
  .DWA0  (dwa0W ),   // data0 write address 
  .DWA1  (dwa1W ),   // data1 write address 
  .DWE0  (dwe0W ),   // data0 write enable
  .DWE1  (dwe1W ),   // data1 write enable
  .DWD0  (dwd0W ),   // data0 write data
  .DWD1  (dwd1W ),   // data1 write data
  .DRA0  (dra0W ),   // data0 read address
  .DRA1  (dra1W ),   // data1 read address
  .DRD0  (drd0W ),   // data read data 0 
  .DRD1  (drd1W ));  // data read data 1 
defparam U_dmem2bk.DATAWL    = INPUTWL  ;    // data word length
defparam U_dmem2bk.ADDRWL    = ADDRWL   ;    // address word length
defparam U_dmem2bk.C2Q       = C2Q      ;    // clock to Q time in ns

endmodule   // module dchannel()
///////////////////////////////////////////////////////////////////////////////////////
//module dpath()
module dpath(
  CLK       ,    // clock
  RST       ,    // reset
  DINEN     ,    // data input enable
  DIN       ,    // input data
  PECY      ,    // pe cycle counter
  DRD0      ,    // data read from ram bank 0
  DRD1      ,    // data read from ram bank 0
  COEF      ,    // coefficent

  DWA0      ,    // data write address for ram bank 0
  DWA1      ,    // data write address for ram bank 1
  DWE0      ,    // data write enable for ram bank 0
  DWE1      ,    // data write enable for ram bank 1
  DWD0      ,    // data write for ram bank 0
  DWD1      ,    // data write for ram bank 1
  DRA0      ,    // data read address for ram bank 0
  DRA1      ,    // data read address for ram bank 1
  DOUT      ,    // output data
  DOUTEN    ,    // output data enable
  OUTIDX    ,    // output data index
  CLKOUT    );   // output clock
// external set param
parameter INPUTWL   = 0;    // input data word length
parameter OUTPUTWL  = 0;    // output data word length 
parameter COEFWL    = 0;    // coefficent word length
parameter K1        = 0;    
parameter K1LOG2    = 0;    
parameter K2        = 0;
parameter K2LOG2    = 0;
parameter C2Q       = 0;    // clock to Q delay in ns
parameter PCYWL     = 0;    // pe cycle word length
parameter PCYMAX    = 0;    // pe counter max value
parameter PCYIDL    = 0;    // pe cycle IDLE value
parameter OCYWL     = 0;    // output cycle word length
parameter ADDRWL    = 0;    // data ram address word length
parameter DATAWL    = 0;    // data ram data word length

// local set param
parameter WRY1PCY   = 19;    // the pecy value that write the y1(peout) to ram 

  input                     CLK  , RST  , DINEN;
  input     [PCYWL-1:0]     PECY;
  input     [INPUTWL-1:0]   DIN, DRD0, DRD1;
  input     [COEFWL-1:0]    COEF;

  output    [ADDRWL-1:0]    DWA0, DWA1, DRA0, DRA1;
  output                    DWE0, DWE1;
  output    [DATAWL-1:0]    DWD0, DWD1;
  output    [OUTPUTWL-1:0]  DOUT;
  output                    DOUTEN;
  output    [1:0]           OUTIDX;
  output                    CLKOUT;

  wire      [DATAWL-1:0]    drd0W, drd1W, data0W, data1W; 
  wire      [DATAWL-1:0]    COEF;  
  wire                      peenW, peoeW, sumclrW ; 
  wire      [DATAWL-1:0]    peoutW;
    
  wire      [K1LOG2:0]      x1posW;
  wire      [K2LOG2-1:0]    y1posW;


// -----------------------------------------------------------------------------------
dwrctl U_dwrctl(
  .CLK   (CLK       ),   // clock
  .RST   (RST       ),   // reset
  .DINEN (DINEN     ),   // data input enable
  .DIN   (DIN       ),   // data input
  .PECY  (PECY      ),   // pe cycle
  .PEOUT (peoutW    ),   // pe output

  .X1POS (x1posW    ),   // x1 position
  .Y1POS (y1posW    ),   // y1 position
  .WA0   (DWA0      ),   // bank 0 write address
  .WA1   (DWA1      ),   // bank 1 write address
  .WE0   (DWE0      ),   // bank 0 write enable 
  .WE1   (DWE1      ),   // bank 1 write enable
  .WD0   (DWD0      ),   // bank 0 write data
  .WD1   (DWD1      ));  // bank 1 write data 

defparam U_dwrctl.C2Q       = C2Q    ;
defparam U_dwrctl.ADDRWL    = ADDRWL ;
defparam U_dwrctl.DATAWL    = DATAWL ;
defparam U_dwrctl.K1        = K1     ;
defparam U_dwrctl.K1LOG2    = K1LOG2 ;
defparam U_dwrctl.K2        = K2     ;
defparam U_dwrctl.K2LOG2    = K2LOG2 ;
defparam U_dwrctl.WRY1PCY   = WRY1PCY;    // the pecy value that write the y1(peout) to ram 
defparam U_dwrctl.PCYWL     = PCYWL  ;    // pe cycle word length
defparam U_dwrctl.PCYMAX    = PCYMAX ;    // pe counter max value
defparam U_dwrctl.PCYIDL    = PCYIDL ;    // pe cycle IDLE value

// -----------------------------------------------------------------------------------
pe U_pe(
  .CLK       (CLK       ),       // clock
  .RST       (RST       ),       // reset
  .SUMCLR    (sumclrW   ),       // sum clear 
  .DATA0     (data0W    ),       // input data0
  .DATA1     (data1W    ),       // input data1
  .COEF      (COEF      ),       // input coefficent
  .PEEN      (peenW     ),       // pe enable
  .PEOUT     (peoutW    ),       // PE OUTPUT
  .PEOE      (peoeW     ));      // PE OUTPUT enable
defparam U_pe.DATAWL = DATAWL;
defparam U_pe.C2Q    = C2Q;
// -----------------------------------------------------------------------------------
drdctl U_drdctl(
  .CLK       (CLK       ),       // input clock    
  .RST       (RST       ),       // input reset 
  .DIN       (DIN       ),       // input input data
  .DINEN     (DINEN     ),       // input input data enable
  .PECY      (PECY      ),       // input pe cycle counter
  .DRD0      (DRD0      ),       // input data read from bank 0
  .DRD1      (DRD1      ),       // input data read from bank 1
  .X1POS     (x1posW    ),       // input x1 position
  .Y1POS     (y1posW    ),       // input y1 position

  .SUMCLR    (sumclrW   ),       // output sum clear 
  .PEEN      (peenW     ),       // output pe enable
  .DRA0      (DRA0      ),       // output data read address 0
  .DRA1      (DRA1      ),       // output data read address 1
  .DRE0      (DRE0      ),       // output data read enable 0
  .DRE1      (DRE0      ),       // output data read enable 1
  .DATA0     (data0W    ),       // output pe data0
  .DATA1     (data1W    ));      // output pe data1
defparam U_drdctl.C2Q       = C2Q   ;
defparam U_drdctl.PCYIDL    = PCYIDL;
defparam U_drdctl.PCYMAX    = PCYMAX;
defparam U_drdctl.PCYWL     = PCYWL ;
defparam U_drdctl.K1        = K1    ;
defparam U_drdctl.K1LOG2    = K1LOG2;
defparam U_drdctl.K2        = K2    ;
defparam U_drdctl.K2LOG2    = K2LOG2;
defparam U_drdctl.DATAWL    = DATAWL;
defparam U_drdctl.ADDRWL    = ADDRWL;
defparam U_drdctl.WRY1PCY   = WRY1PCY;    // the pecy value that write the y1(peout) to ram 
// -----------------------------------------------------------------------------------
outbuf U_outbuf(
  .CLK       (CLK       ),   // clock   
  .RST       (RST       ),   // asyn high reset
  .XNBK01    (x1posW[0] ),   // bank of Xn data
  .DATA0     (data0W    ),   // PE data0
  .DATA1     (data1W    ),   // PE data1
  .PEOUT     (peoutW    ),   // PE output data
  .PEOE      (peoeW     ),   // PE output enable
  .PECY      (PECY      ),   // pe cycle counter
  .DOUT      (DOUT      ),   // data output
  .DOUTEN    (DOUTEN    ),   // data output enable
  .OUTIDX    (OUTIDX    ),   // output data index
  .CLKOUT    (CLKOUT    ));  // output clock

defparam U_outbuf.DATAWL = DATAWL;
defparam U_outbuf.C2Q    = C2Q   ;
defparam U_outbuf.PCYWL  = PCYWL ;
defparam U_outbuf.K1     = K1;
defparam U_outbuf.K2     = K2;

// -----------------------------------------------------------------------------------

endmodule   // module dpath
///////////////////////////////////////////////////////////////////////////////////////
// module dwrctl()
module dwrctl(
  CLK   ,   // clock
  RST   ,   // reset
  DINEN ,   // data input enable
  DIN   ,   // data input
  PECY  ,   // pe cycle
  PEOUT ,   // pe output

  X1POS ,   // x1 position
  Y1POS ,   // y1 position
  WA0   ,   // bank 0 write address
  WA1   ,   // bank 1 write address
  WE0   ,   // bank 0 write enable 
  WE1   ,   // bank 1 write enable
  WD0   ,   // bank 0 write data
  WD1   );  // bank 1 write data 

parameter C2Q       = 0;
parameter ADDRWL    = 0;
parameter DATAWL    = 0;
parameter K1        = 0;
parameter K1LOG2    = 0;
parameter K2        = 0;
parameter K2LOG2    = 0;
parameter WRY1PCY   = 0;    // the pecy value that write the y1(peout) to ram 
parameter PCYWL     = 0;    // pe cycle word length
parameter PCYMAX    = 0;    // pe counter max value
parameter PCYIDL    = 0;    // pe cycle IDLE value

  input                   CLK, RST, DINEN;
  input [DATAWL-1:0]      DIN; 
  input [PCYWL-1:0]       PECY;
  input [DATAWL-1:0]      PEOUT;
  
  output                  WE0, WE1;
  output[ADDRWL-1:0]      WA0, WA1;
  output[DATAWL-1:0]      WD0, WD1;
  output[K1LOG2:0]        X1POS;
  output[K2LOG2-1:0]      Y1POS;
 
  // the bit width of x1,y1 address reg is important, for the overflow wrap add 
  // property is used for these address bit width
  reg [K1LOG2:0]      x1LastPosR; // reg for last x1 position, data width is K1LOG2+1
  reg [K2LOG2-1:0]    y1LastPosR; // reg for last x1 position, data width is K2LOG2
  reg [K1LOG2:0]      x1WrAddr;   // x1 write address 
  reg [K2LOG2-1:0]    y1WrAddr;   // y1 write address
  reg [K2LOG2-1:0]    Y1POS;
   
  reg                     WE0, WE1;
  reg   [ADDRWL-1:0]      WA0, WA1;
  reg   [DATAWL-1:0]      WD0, WD1;


  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      x1LastPosR <= #C2Q (1<<(K1LOG2+1))-1;
      y1LastPosR <= #C2Q (1<<K1LOG2)-1;
    end // if(RST) begin
    else begin
      if(DINEN) begin
        x1LastPosR <= #C2Q x1LastPosR + 1'b1;
      end   // if(DINEN)
      else begin
        if(PECY == WRY1PCY) begin 
          y1LastPosR <= #C2Q y1LastPosR + 1'b1;
        end // if(PECY)
        else begin
          x1LastPosR <= #C2Q x1LastPosR ;
          y1LastPosR <= #C2Q y1LastPosR ;
        end // else(PECY)
      end   // else(DIEN)
    end // else (RST) begin
  end // always @ (posedge CLK or posedge RST) begin

  always@(*) begin
    if(DINEN) begin
      x1WrAddr = x1LastPosR + 1'b1;
      y1WrAddr = 0;
      if(x1WrAddr[0] == 1'b0) begin// the LSB decide the bank number
        WE0 = 1'b1;
        WA0 = x1WrAddr[K1LOG2:1];
        WD0 = DIN;
        WE1 = 0; WA1 = 0; WD1 = 0; 
      end
      else begin
        WE1 = 1'b1;
        WA1 = x1WrAddr[K1LOG2:1];
        WD1 = DIN;
        WE0 = 0; WA0 = 0; WD0 = 0; 
      end
    end // if(DINEN) begin
    else begin  // (DINEN == 0)
      x1WrAddr = 0;
      if(PECY == WRY1PCY) begin   // time to write y1, write to both banks
        y1WrAddr = y1LastPosR + 1'b1;
        WE0 = 1'b1; 
        WE1 = 1'b1; 
        WA0 = y1WrAddr+ K1; 
        WA1 = y1WrAddr+ K1; 
        WD0 = PEOUT;
        WD1 = PEOUT;
      end
      else begin
        y1WrAddr = 0;
        WE0 = 0; WA0 = 0; WD0 = 0; WE1 = 0; WA1 = 0; WD1 = 0; 
      end     // if(PECY) 
    end // else(DINEN) begin

  end   // always@(*) begin

  // update X1POS, Y1POS
  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      Y1POS <= #C2Q 0;
    end
    else begin
      if(DINEN == 1'b1) begin
        Y1POS <= #C2Q y1LastPosR + 1'b1;
      end
      else begin
        Y1POS <= #C2Q Y1POS;
      end   // else (DINEN)
    end     // else (RST)
  end       // always(CLK RST)
  assign X1POS = x1LastPosR;

endmodule   // module dwrctl()
///////////////////////////////////////////////////////////////////////////////////////
// module pe
module pe(
  CLK       ,       // clock
  RST       ,       // reset
  SUMCLR    ,       // clear the sum
  DATA0     ,       // input data0
  DATA1     ,       // input data1
  COEF      ,       // input coefficent
  PEEN      ,       // pe enable
  PEOUT     ,       // PE OUTPUT
  PEOE      );      // PE OUTPUT enable

// external set param
parameter  C2Q      = 0;
parameter  DATAWL   = 0;
// internal set param
parameter  ADDERWL  = DATAWL + 1;
parameter  MULTWL   = DATAWL+DATAWL;   
parameter  SUMWL    = DATAWL+ 5;

  input         CLK, RST, PEEN, SUMCLR;
  input signed  [DATAWL-1:0] DATA0, DATA1;
  input signed  [DATAWL-1:0] COEF;
  output signed [DATAWL-1:0] PEOUT;
  output                     PEOE;
  
  // DFF reg
  reg           peenR1, peenR2, PEOE, sumclrR1, sumclrR2;
  reg signed    [DATAWL-1:0]   adderR;
  reg signed    [DATAWL-1:0]   multR;
  reg signed    [DATAWL-1:0]   coefR;
  reg           [SUMWL-1:0]    sumR;
  // combo reg
  reg signed    [ADDERWL-1:0]   adderOut;
  reg signed    [MULTWL-1:0]    multOut;
  reg signed    [SUMWL-1:0]     sumOut;

  assign PEOUT = sumR[DATAWL-1:0];

  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      peenR1    <= #C2Q 0;
      peenR2    <= #C2Q 0;
      sumclrR1  <= #C2Q 0;
      sumclrR2  <= #C2Q 0;
      PEOE      <= #C2Q 0;
      adderR    <= #C2Q 0;
      coefR     <= #C2Q 0;
      multR     <= #C2Q 0;
      sumR      <= #C2Q 0;
    end     // if(RST)
    else begin
      peenR1    <= #C2Q PEEN;
      peenR2    <= #C2Q peenR1;
      sumclrR1  <= #C2Q SUMCLR;
      sumclrR2  <= #C2Q sumclrR1;
      PEOE      <= #C2Q peenR2;
      coefR     <= #C2Q COEF;
      adderR    <= #C2Q adderOut[ADDERWL-1:ADDERWL-DATAWL];   
      multR     <= #C2Q multOut[MULTWL-2:MULTWL-DATAWL-1];    // discard MSB 
      sumR      <= #C2Q sumOut;
    end     // else(RST)
  end   // always(RST, CLK)

  // combo circuit
  always @ (*) begin
    if(PEEN) begin
      adderOut = DATA0 + DATA1;
    end
    else begin
      adderOut = 0;
    end
    if(peenR1) begin
      multOut = adderR * coefR;
    end 
    else begin
      multOut = 0;
    end
    if(peenR2) begin
      if(sumclrR2) begin
        sumOut = multR;
      end
      else begin
        sumOut = sumR  + {{(SUMWL-DATAWL){multR[DATAWL-1]}},multR};  // sign extension 
      end
    end
    else begin
        sumOut = 0;
    end
  end
endmodule   // module pe
///////////////////////////////////////////////////////////////////////////////////////
// module drdctl(), data read control
module drdctl(
  CLK       ,       // input clock    
  RST       ,       // input reset 
  DIN       ,       // input input data
  DINEN     ,       // input input data enable
  PECY      ,       // input pe cycle counter
  DRD0      ,       // input data read from bank 0
  DRD1      ,       // input data read from bank 1
  X1POS     ,       // input x1 position
  Y1POS     ,       // input y1 position

  SUMCLR    ,       // output sum clear 
  PEEN      ,       // output pe enable
  DRA0      ,       // output data read address 0
  DRA1      ,       // output data read address 1
  DRE0      ,       // output data read enable 0
  DRE1      ,       // output data read enable 1
  DATA0     ,       // output pe data0
  DATA1     );      // output pe data1

parameter C2Q       = 0;
parameter PCYIDL    = 0;
parameter PCYMAX    = 0;
parameter PCYWL     = 0;
parameter K1        = 0;
parameter K1LOG2    = 0;
parameter K2        = 0;
parameter K2LOG2    = 0;
parameter DATAWL    = 0;
parameter ADDRWL    = 0;
parameter WRY1PCY   = 0;

  input                 CLK, RST; 
  input [DATAWL-1:0]    DIN, DRD0, DRD1; 
  input                 DINEN; 
  input [PCYWL-1:0]     PECY; 
  input [K1LOG2:0]      X1POS;
  input [K2LOG2-1:0]    Y1POS;
  
  output                SUMCLR, PEEN;     
  output[ADDRWL-1:0]    DRA0, DRA1;
  output                DRE0, DRE1; 
  output[DATAWL-1:0]    DATA0, DATA1; 


  reg                   SUMCLR, PEEN;     
  reg   [ADDRWL-1:0]    DRA0, DRA1;
  reg                   DRE0, DRE1; 
  reg   [DATAWL-1:0]    DATA0, DATA1; 

  // DFF reg
  reg   [K1LOG2+1 :0]   x1StuffR;       // stuff index of x1 in data RAM
  reg   [K2LOG2+1 :0]   y1StuffR;       // stuff index of y1 in data RAM

  reg   [PCYWL-1:0]     pecyd1R;        // PECY 1 clk delay
  reg                   bk0ZR, bk1ZR;   // replace bank0/1 read data with 0
  reg   [1:0]           bk01ZRin;
  // combo reg
  reg   [K1LOG2  : 0]   x1Idx0, x1Idx1, x1Idx, r1; // the x1 index 
  reg   [K2LOG2-1: 0]   y1Idx, r2;          // the y1Idx
  // update pecy delay DFF 
  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      pecyd1R <= # C2Q 0; 
    end
    else begin
      pecyd1R <= # C2Q PECY;
    end
  end
  // update x1Idx0, x1Idx0, x1Idx, y1Idx, r1, r2
  always @ (*) begin
    if((PECY>=0) && (PECY <= K1-1)) begin   // y1(n,0)
      r1        = PECY[K1LOG2-1:0];
      x1Idx0    = X1POS-r1; 
      x1Idx1    = X1POS-2*K1+1+r1;
      r2    = 0;
      y1Idx = 0;
      x1Idx = 0;
    end     // if(PECY[0,K1-1])
    else if ((PECY>=K1)&&(PECY<=K1+K2-1)) begin         // y2(n,0)
      r2      = K1+K2-1-PECY[K1LOG2:0];
      if((PECY>=K1)&&(PECY<=K1+(K2>>1)-1)) begin        // PECY in [K1~K1+K2/2-1]
        y1Idx = Y1POS + r2 - 3*(K2>>1) + 1;
        x1Idx = X1POS - r2 + (K2>>1) - K1;
      end
      else begin
        y1Idx = Y1POS - r2;
        x1Idx = X1POS + r2 - K2 + 1 - K1;
      end
      x1Idx0 = 0;
      x1Idx1 = 1;
      r1     = 0;
    end     // else if(PECY[K1,K1+K2-1])
    else if ((PECY>=K1+K2)&&(PECY<=K1+K2*2-1))  begin    // y2(n,2)
      r2      = PECY[K1LOG2:0] - K1 - K2;
      if((PECY>=K1+K2)&&(PECY<=K1+3*(K2>>1)-1)) begin
        y1Idx = Y1POS+r2-K2+1;
        x1Idx = X1POS-r2+1-K1;
      end
      else      begin
        y1Idx = Y1POS-r2+(K2>>1);
        x1Idx = X1POS+r2-3*(K2>>1)+2-K1;
      end
      x1Idx0 = 0;
      x1Idx1 = 1;
      r1     = 0;
    end     // else if(PECY[K1+K2, K1+2K2-1]
    else begin
      r1     = 0;
      r2     = 0;
      x1Idx0 = 0;
      x1Idx1 = 0;
      x1Idx  = 0;
      y1Idx  = 0;
    end     // else(PECY)
  end       // always(x1Idx0, x1Idx1, x1Idx, y1Idx, r1, r2)
  
  // update DRA0, DRA1, DRE0, DRE1
  always @ (x1Idx0 or x1Idx1 or x1Idx or y1Idx or PECY) begin
    if((PECY>=0) && (PECY<=K1-1)) begin
      {DRA0,DRA1} = x1Idx01ToAddr({x1Idx0,x1Idx1});
      DRE0 = 1'b1; DRE1 = 1'b1;
    end
    else if((PECY>=K1)&&(PECY<=K1+2*K2-1)) begin
      {DRA0,DRA1} = x1y1IdxToAddr({x1Idx,y1Idx});
      DRE0 = 1'b1; DRE1 = 1'b1;
    end
    else begin
      DRA0 = 0; DRA1 = 0; DRE0 = 0; DRE1 = 0;
    end     // else PECY
  end

 // update x1StuffR 
  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      x1StuffR <= # C2Q (1<<(K1LOG2+2))-1;
    end
    else begin  // if (RST)
      if(DINEN) begin
        if(x1StuffR != (2*K1-1)) begin
          x1StuffR <= # C2Q x1StuffR +1;
        end
        else begin
          x1StuffR <= # C2Q x1StuffR;
        end
      end   // if(DINEN)
      else begin
        x1StuffR <= # C2Q x1StuffR; 
      end   // else (DINEN)
    end     // else (RST)
  end       // always(CLK, RST)

  // update y1StuffR 
  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      y1StuffR <= # C2Q (1<<(K2LOG2+2))-1;
    end
    else begin  // if (RST)
      if(PECY == K1-1) begin
        if(y1StuffR == K2) begin
          y1StuffR <= #C2Q y1StuffR;
        end
        else begin
          y1StuffR <= #C2Q y1StuffR + 1'b1;
        end     // else (y1StuffR)
      end
      else begin
      end       // else(PECY)
    end         // else (RST)
  end           // always(CLK, RST)
 // update bk0ZR, bk1ZR;
  always @ (*) begin
    bk01ZRin = GetBk01ZR(x1Idx0, x1Idx1, x1Idx, y1Idx, PECY, x1StuffR, y1StuffR);
  end   // always(*)

  always @ (posedge CLK or posedge RST) begin
    if(RST) begin
      bk0ZR <= # C2Q 0; 
      bk1ZR <= # C2Q 0; 
    end
    else begin
      {bk0ZR,bk1ZR} <= # C2Q bk01ZRin;
    end
  end


  // update DATA0, DATA1
  always @ (*) begin
    if((pecyd1R>=0)&&(pecyd1R < K1)) begin
      if((bk0ZR == 1'b0))
        DATA0 = DRD0;
      else
        DATA0 = 0;
      if((bk1ZR == 1'b0))
        DATA1 = DRD1;
      else
        DATA1 = 0;
    end
    else if((pecyd1R>=K1)&&(pecyd1R<K1+2*K2-1)) begin
      if((bk0ZR == 1'b0))
        if(x1Idx[0] == 0)
          DATA0 = (DRD0>>>1);
        else
          DATA0 = DRD0;
      else
        DATA0 = 0;
      if((bk1ZR == 1'b0))
        if(x1Idx[0] == 1)
          DATA1 = (DRD1>>>1);
        else
          DATA1 = DRD1;
      else
        DATA1 = 0;
    end
    else begin
      DATA0 = 0; DATA1 = 0;
    end
  end
  
  // update SUMCLR, PEEN
  always @ (*) begin
    if((pecyd1R >= 0) && (pecyd1R < K1+2*K2-1)) begin
      PEEN = 1'b1;
      if((pecyd1R == 0)) 
        SUMCLR = 1'b1;
      else if((pecyd1R == K1))
        SUMCLR = 1'b1;
      else if((pecyd1R == K1+K2))
        SUMCLR = 1'b1;
      else
        SUMCLR = 1'b0;
    end     // if(pecyd1R)
    else begin
      PEEN      = 1'b0;
      SUMCLR    = 1'b0;
    end     // else(pecyd1R)
  end

  // -----------------------------------------------------------------------------------------------
  // function x1Idx01ToAddr(), conver x1Idx0/1  to ram bank 0/1 address 
  // -----------------------------------------------------------------------------------------------
  function [ADDRWL*2-1:0] x1Idx01ToAddr;
  input [2*K1LOG2+1:0] x1Idx01;
  reg[ADDRWL-1:0] addr0, addr1;
  reg[K1LOG2:0] idx0, idx1;
  begin
    {idx0,idx1} = x1Idx01;
    case({idx0[0],idx1[0]})
      2'b01: begin
        addr0 = idx0[K1LOG2:1];
        addr1 = idx1[K1LOG2:1];
      end
      2'b10: begin
        addr1 = idx0[K1LOG2:1];
        addr0 = idx1[K1LOG2:1];
      end
      default begin
        // synopsys translate_off
        // $display("ERROR, module drdctl().x1Idx01ToAddr(), @%0d, idx0 = %d,idx1 = %d", $time, idx0, idx1); 
        // synopsys translate_on
        addr1 = 0;
        addr0 = 0;
      end
    endcase
    x1Idx01ToAddr = {addr0, addr1};
  end
  endfunction       // function x1Idx01ToAddr
  // -----------------------------------------------------------------------------------------------
  // function x1y1IdxToAddr(), conver x1Idx y1Idx  to ram bank 0/1 address 
  // -----------------------------------------------------------------------------------------------
  function [ADDRWL*2-1:0] x1y1IdxToAddr;
  input [K1LOG2+1 + K2LOG2 - 1:0] x1Idx_y1Idx;
  reg[ADDRWL-1:0]   addr0, addr1;
  reg[K1LOG2:0]     x1Idx;
  reg[K2LOG2-1:0]   y1Idx;
  begin
    {x1Idx,y1Idx} = x1Idx_y1Idx;
    if(x1Idx[0] == 1'b0) begin
      addr0 = x1Idx[K1LOG2:1];
      addr1 = y1Idx[K2LOG2-1:0] + K1;
    end
    else begin
      addr1 = x1Idx[K1LOG2:1];
      addr0 = y1Idx[K2LOG2-1:0] + K1;
    end
    x1y1IdxToAddr = {addr0, addr1};
  end
  endfunction       // function x1y1IdxToAddr()
  // -----------------------------------------------------------------------------------------------
  // function GetBk01ZR(), generate ram bank Zero replace Reg
  // -----------------------------------------------------------------------------------------------
  function [1:0] GetBk01ZR;
  input [K1LOG2   :0]   x1Idx0, x1Idx1, x1Idx;
  input [K2LOG2-1 :0]   y1Idx;
  input [PCYWL-1  :0]   PECY;
  input [K1LOG2+1 :0]   x1StuffR;       
  input [K2LOG2+1 :0]   y1StuffR;
  reg bk0ZR, bk1ZR;
  begin
    if((PECY >= 0) && (PECY <= K1-1)) begin
      if((x1Idx0 > x1StuffR) && (x1Idx1 <= x1StuffR))begin
        if(x1Idx0[0] == 1'b0)           begin
          bk0ZR = 1'b1; bk1ZR = 1'b0;   end
        else                            begin 
          bk0ZR = 1'b0; bk1ZR = 1'b1;   end 
      end   // if( > && <= )
      else if((x1Idx0 <= x1StuffR) && (x1Idx1 > x1StuffR)) begin
        if(x1Idx1[0] == 1'b0)           begin
          bk0ZR = 1'b1; bk1ZR = 1'b0;   end
        else bk1ZR = 1'b1;              begin
          bk0ZR = 1'b0; bk1ZR = 1'b1;   end 
      end   // else if( <= && > )
      else if((x1Idx0 > x1StuffR) && (x1Idx1 >  x1StuffR)) begin
        bk0ZR = 1'b1; bk1ZR = 1'b1;
      end
      else begin
        bk0ZR = 1'b0; bk1ZR = 1'b0;
      end
//sysnopsys translate_off
`ifdef DBG_GETBK01ZR
      $display("PECY%d x1Idx0=%d, x1Idx1=%d, bk0ZR=%d, bk1ZR=%d, @%d", PECY, x1Idx0, x1Idx1, bk0ZR, bk1ZR, $time);
`endif
//sysnopsys translate_on
    end     // if PECY in [0, K1-1] 
    else if((PECY>=K1) && (PECY <= K1+2*K2-1)) begin
      if((x1Idx > x1StuffR)&&(y1Idx <= y1StuffR)) begin
        if(x1Idx[0] == 1'b0)            begin
          bk0ZR = 1'b1; bk1ZR = 1'b0;   end
        else                            begin 
          bk0ZR = 1'b0; bk1ZR = 1'b1;   end
      end   // if( > && <= )
      else if((x1Idx <= x1StuffR)&&(y1Idx > y1StuffR)) begin
        if(x1Idx[0] == 1'b0)            begin
          bk0ZR = 1'b0; bk1ZR = 1'b1;   end
        else                            begin 
          bk0ZR = 1'b1; bk1ZR = 1'b0;   end
      end   // else if( <= && > )
      else if((x1Idx > x1StuffR) && (y1Idx > y1StuffR)) begin
        bk0ZR = 1'b1; bk1ZR = 1'b1;
      end
      else begin
        bk0ZR = 1'b0; bk1ZR = 1'b0;
      end
    end     // if PECY in [K1, K1+2*K2-1]
    else begin
      bk0ZR = 1'b0; bk1ZR = 1'b0;
    end     // else (PECY)
    GetBk01ZR = {bk0ZR, bk1ZR};
  end       // function begin
  endfunction   // function GetBk01ZR()


endmodule       // module drdctl()
///////////////////////////////////////////////////////////////////////////////////////
module outbuf(
  CLK       ,   // clock   
  RST       ,   // asyn high reset
  XNBK01    ,   // bank of Xn data
  DATA0     ,   // PE data0
  DATA1     ,   // PE data1
  PEOUT     ,   // PE output data
  PEOE      ,   // PE output enable
  PECY      ,   // pe cycle counter
  DOUT      ,   // data output
  DOUTEN    ,   // data output enable
  OUTIDX    ,   // output data index
  CLKOUT    );  // output clock 

parameter DATAWL = 0;
parameter C2Q    = 0;
parameter PCYWL  = 0;
parameter K1 = 0;
parameter K2 = 0;

input               CLK, RST;
input               XNBK01;
input [DATAWL-1:0]  DATA0, DATA1, PEOUT;
input               PEOE;
input [PCYWL-1:0]   PECY;

output[DATAWL-1:0]  DOUT;
output              DOUTEN;
output[1:0]         OUTIDX;
output              CLKOUT;

reg [DATAWL-1:0]    data01[1:0];
reg [DATAWL-1:0]    outBufR0, outBufR1, outBufR2, outBufR3;
reg [PCYWL-1:0]     pecyd1R, pecyd2R, pecyd3R, pecyd4R;

reg [PCYWL-1:0]     outCyR; // output cycle counter
reg [DATAWL-1:0]    DOUT;
reg                 DOUTEN;
reg [1:0]           OUTIDX;

assign CLKOUT = CLK;
always @ (*) begin
  data01[0] = DATA0;
  data01[1] = DATA1;
end

// update pecydnR
always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    pecyd1R <= #C2Q 0;
    pecyd2R <= #C2Q 0;
    pecyd3R <= #C2Q 0;
    pecyd4R <= #C2Q 0;
  end       // if(RST)
  else begin
    pecyd1R <= #C2Q PECY   ;
    pecyd2R <= #C2Q pecyd1R;
    pecyd3R <= #C2Q pecyd2R;
    pecyd4R <= #C2Q pecyd3R;
  end       // else(RST)
end         // always(CLK, RST)
// update outBufRn
always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    outBufR0 <= # C2Q 0; outBufR1 <= # C2Q 0; outBufR2 <= # C2Q 0; outBufR3 <= # C2Q 0;
  end       // if(RST)
  else begin
    // outBufR0
    if(PECY == K1+K2+3) begin
      outBufR0 <= #C2Q PEOUT;
    end
    else begin
      outBufR0 <= #C2Q outBufR0;
    end
    // outBufR1
    if(PECY == K1+K2) begin
      outBufR1 <= #C2Q data01[~XNBK01];
    end
    else begin
      outBufR1 <= #C2Q outBufR1;
    end
    // outBufR2
    if(pecyd4R == 31) begin
      outBufR2 <= #C2Q PEOUT;
    end
    else begin
      outBufR2 <= #C2Q outBufR2;
    end
    // outBufR3
    if(pecyd1R == 31) begin
      outBufR3 <= #C2Q data01[XNBK01];
    end
    else begin
      outBufR3 <= #C2Q outBufR3;
    end
  end       // else(RST)
end         // always(CLK, RST)
// update outCyR 
always @ (posedge CLK or posedge RST) begin
  if(RST) begin
    outCyR <= #(1<<PCYWL)-1;    // reset value
  end   // if(RST)
  else begin
    if(PECY == 27) begin
      outCyR <= #C2Q 0;
    end
    else if (outCyR<31)begin
      outCyR <= #C2Q outCyR + 1'b1;
    end
    else begin
      outCyR <= #C2Q outCyR;
    end

  end   // else(RST)
end         // always(CLK, RST)

// update DOUT, DOUTEN, OUTIDX
always @ (*) begin
  case(outCyR[4:3]) 
    0: DOUT = outBufR0; 
    1: DOUT = outBufR1; 
    2: DOUT = outBufR2; 
    3: DOUT = outBufR3; 
    default: DOUT = 0;
  endcase
  case(outCyR) 
    0   : DOUTEN = 1;
    8   : DOUTEN = 1;
    16  : DOUTEN = 1;
    24  : DOUTEN = 1;
    default: DOUTEN = 0;
  endcase
  OUTIDX = outCyR[4:3];
end

endmodule   // module outbuf
///////////////////////////////////////////////////////////////////////////////////////
