module sync_bridge(
  input wb_rst_i,

  /* WISHBONE master synchronous to 62.5Mhz SMC clock */
  output wbm_clk_o,
  output wbm_cyc_o,
  output wbm_stb_o,
  output [15:0] wbm_dat_o,
  output [31:0] wbm_adr_o,
  output wbm_we_o,
  input [15:0] wbm_dat_i,
  input wbm_ack_i,
  output [1:0] wbm_sel_o,

  /* WISHBONE master asynchronous (feed external clock) */
  input wbm2_en_i, /* ext. logic uses wbm_adr_o combinationally to enable */
  input wbm2_clk_i,
  output wbm2_cyc_o,
  output wbm2_stb_o,
  output [15:0] wbm2_dat_o,
  output [31:0] wbm2_adr_o,
  output wbm2_we_o,
  input [15:0] wbm2_dat_i,
  input wbm2_ack_i,
  output [1:0] wbm2_sel_o,

  /* Asynchronous WISHBONE slave, receives writes and immediately
   * places them into a FIFO to be picked up by SMC read bursts.  */
  input wbs_clk_i,
  input wbs_cyc_i,
  input wbs_stb_i,
  input wbs_we_i,
  input [31:0] wbs_adr_i,
  input [15:0] wbs_dat_i,
  output [15:0] wbs_dat_o,
  input [2:0] wbs_cti_i,
  input [1:0] wbs_bte_i,
  output wbs_ack_o,

  /* Marvell SMC bus, operating in synchronous mode, fastest timing,
   * and with RDY support enabled */
  input [15:0] smc_dat_i,
  output [15:0] smc_dat_o,
  output smc_dat_oe_o,
  input smc_rdn_i,
  input smc_wrn_i,
  input smc_cs1n_i,
  input smc_be1n_i,
  input smc_be2n_i,
  input smc_advn_i,
  input smc_clk_i,
  //input clk_75mhz_i, XXX
  output smc_waitn_o,

  output wr_fifo_empty_o
);

assign wbm_clk_o = smc_clk_i;
reg [15:0] smc_dat, wbm_dat;
assign wbm_dat_o = wbm_dat;
reg burst_adr, fifo_wr, fifo_rd;
reg smc_rdy;
reg [3:0] burst_cnt;
reg wbm_cyc, wbm_stb, wbm_we;
assign wbm_cyc_o = wbm_cyc && !wbm2_en_i;
assign wbm_stb_o = wbm_stb && !wbm2_en_i;
assign wbm_we_o = wbm_we;
reg [1:0] wbm_sel;
assign wbm_sel_o = wbm_sel;
reg [31:0] wbm_adr;
reg [14:0] ts4710_decodes;
assign wbm_adr_o = {ts4710_decodes, wbm_adr[16:0]};
wire wb_ack, resync_wb_ack;
assign wb_ack = wbm2_en_i ? resync_wb_ack : wbm_ack_i;
wire [15:0] wb_dat, resync_wb_dat;
assign wb_dat = wbm2_en_i ? resync_wb_dat : wbm_dat_i;
assign smc_dat_oe_o = !smc_rdn_i;
assign smc_dat_o = smc_dat;
wire fifo_full, fifo_empty;
reg wbs_ack;
assign wbs_ack_o = (wbs_cyc_i & wbs_stb_i & wbs_we_i & !fifo_full) | wbs_ack;
assign smc_waitn_o = smc_rdy; 

wb_resync resynccore(
  .wb_clk_i(wbm_clk_o),
  .wb_rst_i(wb_rst_i),
  .wbs1_cyc_i(wbm_cyc),
  .wbs1_stb_i(wbm_stb && wbm2_en_i),
  .wbs1_we_i(wbm_we),
  .wbs1_dat_i(wbm_dat),
  .wbs1_dat_o(resync_wb_dat),
  .wbs1_ack_o(resync_wb_ack),
  .wbs1_adr_i(wbm_adr_o),
  .wbs1_sel_i(wbm_sel),

  .wbm_clk_i(wbm2_clk_i),
  .wbm_cyc_o(wbm2_cyc_o),
  .wbm_stb_o(wbm2_stb_o),
  .wbm_we_o(wbm2_we_o),
  .wbm_adr_o(wbm2_adr_o),
  .wbm_dat_o(wbm2_dat_o),
  .wbm_dat_i(wbm2_dat_i),
  .wbm_ack_i(wbm2_ack_i),
  .wbm_sel_o(wbm2_sel_o)
);

wire [15:0] fifo_q;
wire wr_fifo_empty, wr_fifo_full;
assign wr_fifo_empty_o = wr_fifo_empty;

xilinx_fifo_async_1kb read_fifo (
  .rst(wb_rst_i),
  .wr_clk(wbs_clk_i),
  .rd_clk(wbm_clk_o),
  .din(wbs_dat_i),
  .wr_en(wbs_cyc_i && wbs_stb_i && wbs_we_i && !fifo_full),
  .rd_en(fifo_rd),
  .dout(fifo_q),
  .full(fifo_full),
  .empty(fifo_empty)
);

xilinx_fifo_async_1kb write_fifo (
  .rst(wb_rst_i),
  .wr_clk(wbm_clk_o),
  .rd_clk(wbs_clk_i),
  .din(smc_dat_i),
  .wr_en(fifo_wr),
  .rd_en(wbs_cyc_i && wbs_stb_i && !wbs_we_i && !wr_fifo_empty && !wbs_ack),
  .dout(wbs_dat_o),
  .full(wr_fifo_full),
  .empty(wr_fifo_empty)
);

always @(posedge wbs_clk_i) 
  wbs_ack <= wbs_cyc_i && wbs_stb_i && !wbs_we_i && !wr_fifo_empty && !wbs_ack;

reg [3:0] state;
wire [3:0] idle =         4'd0;
wire [3:0] wait1 =        4'd1;
wire [3:0] wait2 =        4'd8;
wire [3:0] burst_wait =   4'd2;
wire [3:0] burst_wr =     4'd3;
wire [3:0] burst_rd =     4'd4;
wire [3:0] wb_rd =        4'd5;
wire [3:0] wb_wr =        4'd6;
wire [3:0] wb_wr2 =       4'd7;

`undef scope8 // XXX
`ifdef scope8
wire [71:0] scope_dat = {
  /*
  8'd0,
  state, // 4
  smc_be2n_i,
  smc_be1n_i,
  wbm_ack_i,
  wbm_we_o,
  wbm_cyc_o,
  wbm_stb_o,
  wbm_adr_o[31],
  wbm_dat_o, // 16
  wbm_adr_o[15:0], // 16
  smc_advn_i,
  smc_wrn_i,
  smc_rdn_i,
  smc_rdy,
  burst_adr,
  smc_dat_i // 16
  */
  //wbm2_en_i,
  wb_ack,
  wbm_we,
  wbm_stb,
  wbm_sel, // 2
  smc_cs1n_i,
  smc_be2n_i,
  smc_be1n_i,
  smc_wrn_i,
  smc_rdn_i,
  smc_rdy,
  smc_advn_i,
  ts4710_decodes[11:4], // 8
  state, // 4
  smc_dat_i, // 16
  wb_dat, // 16
  wbm_dat // 16
};

wire [7:0] scope_trig = {ts4710_decodes[10:7], 
  smc_advn_i, wbm_we, smc_rdn_i, smc_wrn_i};

reg [71:0] scope_dat_q;
reg [7:0] scope_trig_q;
always @(posedge wbm_clk_o) begin
  scope_dat_q <= scope_dat;
  scope_trig_q <= scope_trig;
end

wire [35:0] scope_ctl;
chipscope_icon iconcore (
    .CONTROL0(scope_ctl) // INOUT BUS [35:0]
);

chipscope_ila chiscopecore (
    .CONTROL(scope_ctl), // INOUT BUS [35:0]
    .CLK(wbm_clk_o), // IN
    .DATA(scope_dat_q), // IN BUS [71:0]
    .TRIG0(scope_trig_q) // IN BUS [7:0]
);
`endif


always @(posedge wbm_clk_o or posedge wb_rst_i) begin
  if (wb_rst_i) begin
    smc_dat <= 16'd0;
    wbm_dat <= 16'd0;
    smc_rdy <= 1'b1;
    wbm_cyc <= 1'b0;
    wbm_stb <= 1'b0;
    wbm_we <= 1'b0;
    wbm_sel <= 2'd0;
    wbm_adr <= 32'd0;
    burst_adr <= 1'b0;
    burst_cnt <= 4'd0;
    fifo_rd <= 1'b0;
    fifo_wr <= 1'b0;
    state <= idle;
  end else begin
    case (state)
      idle: begin
	smc_rdy <= 1'b1; // XXX
        if (!smc_advn_i) begin
	  smc_rdy <= 1'b0;
	  if (!smc_cs1n_i) wbm_adr <= {16'd0, smc_dat_i[15:1], 1'b0};
	  else wbm_adr <= {15'd0, smc_dat_i, 1'b0};
	  if (!smc_cs1n_i) wbm_sel <= smc_dat_i[0] ? 2'b10 : 2'b01;  
	  else wbm_sel <= 2'b11;
          if ({smc_dat_i, 1'b0} == 17'h4e20) state <= burst_wait;
	  else state <= wait1;
          if ({smc_dat_i, 1'b0} == 17'h4e20) burst_adr <= 1'b1;
	  else burst_adr <= 1'b0;
	end
      end
      wait1: begin
        if (!smc_rdn_i) begin
	  wbm_cyc <= 1'b1;
	  wbm_stb <= 1'b1;
	  wbm_we <= 1'b0;
	  state <= wb_rd;
	end
        if (!smc_wrn_i) begin
	  smc_rdy <= 1'b1;
	  wbm_we <= 1'b1;
	  if (smc_rdy) state <= wb_wr;
	  //state <= wait2;
	end
      end
      wait2: state <= wb_wr;
      wb_rd: begin
        if (wb_ack) begin
          smc_dat <= wb_dat;
	  if (!smc_cs1n_i && wbm_sel[1]) smc_dat[7:0] <= wb_dat[15:8];
	  // XXX smc_rdy <= 1'b1;
	  wbm_cyc <= 1'b0;
	  wbm_stb <= 1'b0;
	  state <= idle;
	end
      end
      wb_wr: begin
	wbm_dat <= smc_dat_i;
	if (!smc_cs1n_i) wbm_dat <= {smc_dat_i[7:0], smc_dat_i[7:0]};
	if (smc_cs1n_i) wbm_sel <= {!smc_be2n_i, !smc_be1n_i};
	wbm_cyc <= 1'b1;
	wbm_stb <= 1'b1;
	state <= wb_wr2;
      end
      wb_wr2: begin
        if (wb_ack) begin
	  wbm_cyc <= 1'b0;
	  wbm_stb <= 1'b0;
	  state <= idle;
	end
      end
      burst_wait: begin
        burst_cnt <= 4'h0;
        if (!smc_rdn_i) begin
	  state <= burst_rd;
          smc_rdy <= 1'b1;
          smc_dat <= fifo_q;
	  fifo_rd <= 1'b1;
	end
        if (!smc_wrn_i) begin
	  state <= burst_wr;
          smc_rdy <= 1'b1;
	  fifo_wr <= 1'b1;
	end
      end
      burst_rd: begin
	burst_cnt <= burst_cnt + 1'b1;
        smc_dat <= fifo_q;
	if (burst_cnt == 4'hf) begin
	  fifo_rd <= 1'b0;
          state <= idle;
	end
      end
      burst_wr: begin
	burst_cnt <= burst_cnt + 1'b1;
	if (burst_cnt == 4'hf) begin
	  fifo_wr <= 1'b0;
          state <= idle;
	end
      end
    endcase
    /*
    if (smc_cs1n && !smc_advn) begin
      wbm_adr <= { 15'd0, smc_dat, 1'b0 };
      if ({smc_dat, 1'b0} == 17'h4e20) burst_adr <= 1'b1; else burst_adr <= 1'b0;
      update_sel <= 1'b1;
      //wbm_adr <= { 15'd4, 1'b0, smc_dat[15:1], 1'b0 }; // XXX
    end else if (!smc_cs1n && !smc_advn) begin
      wbm_adr <= { 15'd4, 1'b0, smc_dat[15:1], 1'b0 };
      burst_adr <= 1'b0;
      wbm_sel <= smc_dat[0] ? 2'b10 : 2'b01;  
      update_sel <= 1'b0;
    end
  
    smc_rdy[2] <= smc_rdy[1];
    smc_rdy[1] <= smc_rdy[0];
    if (!smc_advn) smc_rdy[2:0] <= 3'd0;
    if (!smc_wrn && flushack) smc_rdy[0] <= 1'b1;

    if (!smc_rdn && !wbm_cyc) begin
      wbm_cyc <= 1'b1;
      if (!burst_adr) wbm_stb <= 1'b1;
      wbm_we <= 1'b0;
      if (update_sel) wbm_sel <= 2'b11;
    end else if (!smc_wrn && (burst_adr ? smc_rdy[0] : smc_rdy[1]) && !wbm_cyc) begin
    //end else if (!smc_wrn && smc_rdy[0] && !wbm_cyc) begin
      if (burst_adr) burst_wr <= 1'b1;
      wbm_stb <= 1'b1;
      wbm_cyc <= 1'b1;
      wbm_we <= 1'b1;
      if (update_sel) wbm_sel <= {!smc_be2n, !smc_be1n};
    end

    fifo_rd <= !smc_rdn && burst_adr;
    if (!smc_rdn && burst_adr) begin
      smc_rdy[0] <= wbm_cyc;
      burst_cnt <= burst_cnt + 1'b1;
      if (burst_cnt == 4'hf) burst_adr <= 1'b0;
    end
    if (fifo_rd) smc_d_out <= fifo_q;

    if (wb_ack && wbm_stb && wbm_cyc) begin
      smc_d_out <= wb_dat[15:0];
      if (!burst_wr) wbm_stb <= 1'b0;
      else begin
        burst_cnt <= burst_cnt + 1'b1;
        if (burst_cnt == 4'hf) begin
          burst_wr <= 1'b0;
          wbm_stb <= 1'b0;
        end
      end
      smc_rdy[0] <= 1'b1;
    end 

    if (smc_rdn && smc_wrn) begin
      wbm_cyc <= 1'b0;
      if (wbm_we) smc_sync_switch <= 1'b1;
    end

    if (wbm_cyc && wbm_stb && wb_ack) first_cyc <= 1'b0;
    else if (!wbm_cyc) first_cyc <= 1'b1;
    */
  end
end


/* The SMC bus has 16 address lines that address 16 bit quantities, so it
 * is actually 128KB of space.  I am now modifying it to use all 128KB.
 */

wire [16:0] add = wbm_adr[16:0];
always @(posedge wbm_clk_o or posedge wb_rst_i) begin
  if (wb_rst_i) begin
    ts4710_decodes <= 15'h000;
  end else begin
    if (add < 17'h4000) begin
      ts4710_decodes <= 15'h001; // blockram

    end else if (add[16]) begin
      ts4710_decodes <= 15'h800; // LM32 program memory

    end else if (add >= 17'h8000) begin
      ts4710_decodes <= 15'h002; // external bus

    end else if (add >= 17'h5400) begin
      ts4710_decodes <= 15'h004; // XUART IO registers

    end else if (add >= 17'h5000) begin
      ts4710_decodes <= 15'h008; // touchscreen

    end else if (add >= 17'h4f00) begin
      ts4710_decodes <= 15'h400; // CAN core #2

    end else if (add >= 17'h4e00) begin
      ts4710_decodes <= 15'h200; // DMA core

    end else if (add >= 17'h4d00) begin
      ts4710_decodes <= 15'h100; // SD core

    end else if (add >= 17'h4C00) begin
      ts4710_decodes <= 15'h010; // CAN core

    end else if (add >= 17'h4800) begin
      ts4710_decodes <= 15'h020; // SPI core

    end else if (add >= 17'h4400) begin
      ts4710_decodes <= 15'h040; // ADC core

    end else begin
      ts4710_decodes <= 15'h080; // Syscon
    end

    if (burst_adr) ts4710_decodes <= 15'h4000;
  end
end

endmodule
