module mtx_io #(parameter IOW    = 64,          // IO width
	        parameter TARGET = "GENERIC"  // target selector
	       )
  (// reset, clk, cfg
   input 	    nreset, // async active low reset
   input 	    io_clk, // clock from divider
   input 	    ddr_mode, // send data as ddr
   input [1:0] 	    iowidth, // iowidth *8,16,32,64  
   // chip IO interface
   output [IOW-1:0] tx_packet, // data for IO
   output reg 	    tx_access, // access signal for IO
   input 	    tx_wait, // IO wait signals
   // core side 
   input [7:0] 	    io_valid, // per byte valid indicator
   input [IOW-1:0]  io_packet, // packet
   output 	    io_wait // pushback to serializer in sdr mode   
   );

   //local wires
   reg [63:0] 	    shiftreg;
   reg [2:0] 	    tx_state;
   reg [IOW-1:0]    tx_packet_sdr;
   reg [7:0] 	    io_valid_reg;
   wire [IOW/2-1:0] tx_packet_ddr;
   wire 	    tx_wait_sync;
   wire 	    transfer_active;
   wire [7:0] 	    io_valid_next;
   wire [IOW/2-1:0] ddr_data_even;
   wire [IOW/2-1:0] ddr_data_odd;
   wire 	    dmode8;
   wire 	    dmode16;
   wire 	    dmode32;
   wire 	    dmode64;
   wire 	    io_nreset;
   wire 	    reload;
   
   //########################################
   //# STATE MACHINE
   //########################################   

   assign dmode8   = (iowidth[1:0]==2'b00);   
   assign dmode16  = ((iowidth[1:0]==2'b01) & ~ddr_mode) |
                     (iowidth[1:0]==2'b00) & ddr_mode;   
   assign dmode32  = ((iowidth[1:0]==2'b10) & ~ddr_mode) |
                     (iowidth[1:0]==2'b01) & ddr_mode;   
   assign dmode64  = ((iowidth[1:0]==2'b11) & ~ddr_mode) |
                     (iowidth[1:0]==2'b10) & ddr_mode;   

   assign io_valid_next[7:0] = dmode8  ? {1'b0,io_valid_reg[7:1]} :
			       dmode16 ? {2'b0,io_valid_reg[7:2]} :
			       dmode32 ? {4'b0,io_valid_reg[7:4]} :
			                  8'b0;
   
   assign reload = ~transfer_active | dmode64 | (io_valid_next[7:0]==8'b0);
  
   always @ (posedge io_clk or negedge io_nreset)
     if(!io_nreset)
       io_valid_reg[7:0] <= 'b0;
     else if(reload)
       io_valid_reg[7:0] <= io_valid[7:0];
     else
       io_valid_reg[7:0] <= io_valid_next[7:0];
       
   assign transfer_active = |io_valid_reg[7:0];
   
   //pipeline access signal
   always @ (posedge io_clk or negedge io_nreset)
     if(!io_nreset)
       tx_access <= 1'b0;   
     else
       tx_access <= transfer_active;

   assign io_wait = tx_wait_sync | ~reload;
   
   //########################################
   //# SHIFT REGISTER  (SHIFT DOWN)
   //########################################

   always @ (posedge io_clk)
     if(reload)
       shiftreg[63:0] <= io_packet[IOW-1:0];
     else if(dmode8)//8 bit
       shiftreg[63:0] <= {8'b0,shiftreg[IOW-1:8]};   
     else if(dmode16)//16 bit
       shiftreg[63:0] <= {16'b0,shiftreg[IOW-1:16]};
     else if(dmode32)//32 bit
       shiftreg[63:0] <= {32'b0,shiftreg[IOW-1:32]};   
   
   //########################################
   //# DDR OUTPUT
   //########################################

   // pipeline sdr to compensate for ddr
   always @ (posedge io_clk)
     tx_packet_sdr[IOW-1:0] <= shiftreg[IOW-1:0];
      
   // ddr circuit (one stage pipeline delay!)
   assign ddr_data_even[IOW/2-1:0] = shiftreg[IOW/2-1:0];

   assign ddr_data_odd[IOW/2-1:0] = (iowidth[1:0]==2'b00) ? shiftreg[7:4]   : //byte
				    (iowidth[1:0]==2'b01) ? shiftreg[15:8]  : //short
    				    (iowidth[1:0]==2'b10) ? shiftreg[31:16] : //word
				                            shiftreg[63:32];  //double
   
   oh_oddr#(.DW(IOW/2))
   data_oddr (.out	(tx_packet_ddr[IOW/2-1:0]),
              .clk	(io_clk),
	      .din1	(ddr_data_even[IOW/2-1:0]),
	      .din2	(ddr_data_odd[IOW/2-1:0]));

   //select between ddr/sdr
   assign tx_packet[IOW-1:0] = ddr_mode ? {{(IOW/2){1'b0}},tx_packet_ddr[IOW/2-1:0]}:
		                                           tx_packet_sdr[IOW-1:0];
   
   //########################################
   //# Synchronizers
   //########################################  

   // synchronize reset to io clock
   oh_rsync sync_reset(.nrst_out (io_nreset),
		       .clk	 (io_clk),
		       .nrst_in	 (nreset));
   
   //synchronize wait
   oh_dsync sync_wait(.nreset	(io_nreset),
		      .clk	(io_clk),
		      .din      (tx_wait),
		      .dout     (tx_wait_sync));
   
endmodule // mtx_io