/* Copyright 2012. Unpublished Work of Technologic Systems
 * All Rights Reserved.
 *
 * THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,
 * PROPRIETARY AND TRADE SECRET INFORMATION OF TECHNOLOGIC SYSTEMS.
 * ACCESS TO THIS WORK IS RESTRICTED TO (I) TECHNOLOGIC SYSTEMS 
 * EMPLOYEES WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE
 * OF THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN TECHNOLOGIC
 * SYSTEMS WHO HAVE ENTERED INTO APPROPRIATE LICENSE AGREEMENTS.  NO
 * PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED, COPIED, 
 * DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED, CONDENSED, 
 * EXPANDED, COLLECTED, COMPILED, LINKED, RECAST, TRANSFORMED, ADAPTED
 * IN ANY FORM OR BY ANY MEANS, MANUAL, MECHANICAL, CHEMICAL, 
 * ELECTRICAL, ELECTRONIC, OPTICAL, BIOLOGICAL, OR OTHERWISE WITHOUT
 * THE PRIOR WRITTEN PERMISSION AND CONSENT OF TECHNOLOGIC SYSTEMS.
 * ANY USE OR EXPLOITATION OF THIS WORK WITHOUT THE PRIOR WRITTEN
 * CONSENT OF TECHNOLOGIC SYSTEMS COULD SUBJECT THE PERPETRATOR TO
 * CRIMINAL AND CIVIL LIABILITY.
 */

/* This is refactored version of wb_spi.v intended for interfacing with an
 * SPI flash on a Xilinx Spartan 6.  The old memory map is retained for 
 * software compatibility.  The following changes are made:
 * - no hispeed mode is supported
 * - supports 4 bit wide operation
 * - the latching of input bits is delayed
 * - no "read data from last write" is supported (spiflashctl doesn't use it)
 * - compared to the old core, bytes are swapped within each 16 bit word
 * - only one LUN is supported
 * - CS and SCK cannot be controlled manually
 *
 * Coupled with a TS PCIe core DMA channel, this core can achieve 25MB/s
 * throughput on an SPI flash read.
 *
 * Output bits are updated with the falling edge of SCK.  Input bits are 
 * latched one clock after the rising edge of SCK.  At 50MHz (speed=1) this
 * means that the MSB of input is latched on the 2nd falling edge.  If it turns
 * out that this is too late, we will add an input delay.  On the N25Q064, it
 * should never be too late, based on the timing diagram on page 143 of the
 * datasheet.
 *
 * The rationale for byte-swapping within 16 bit words is as follows.
 * If the SPI flash is only ever accessed by this core, then it doesn't matter.
 * In our case, the flash is also accessed by the FPGA when it wants to read a
 * bitstream, so it does matter.  SPI flash is byte oriented.  On a flash
 * transaction, byte 0 is first, MSB first.  On a normal 16 bit SPI transaction
 * byte 1 MSB is first.  As a result, if we don't swap bytes here, we have to
 * do it in software on bitstreams.  Doing it here is easy and then this issue
 * goes away.
 */

module wb_spi2 (
  wb_clk_i,
  wb_rst_i,

  wb1_cyc_i,
  wb1_stb_i,
  wb1_we_i,
  wb1_adr_i,
  wb1_dat_i,
  wb1_sel_i,
  wb1_dat_o,
  wb1_ack_o,

  wb2_cyc_i,
  wb2_stb_i,
  wb2_we_i,
  wb2_adr_i,
  wb2_dat_i,
  wb2_sel_i,
  wb2_dat_o,
  wb2_ack_o,

  wb3_cyc_i,
  wb3_stb_i,
  wb3_we_i,
  wb3_adr_i,
  wb3_dat_i,
  wb3_sel_i,
  wb3_dat_o,
  wb3_ack_o,

  wb4_cyc_i,
  wb4_stb_i,
  wb4_we_i,
  wb4_adr_i,
  wb4_dat_i,
  wb4_sel_i,
  wb4_dat_o,
  wb4_ack_o,

  sck_o,
  d_o,
  d_oe_o,
  d_i,
  csn_o
);

input wb_clk_i, wb_rst_i;

input wb1_cyc_i, wb1_stb_i, wb1_we_i;
input [3:0] wb1_sel_i;
input [31:0] wb1_adr_i, wb1_dat_i;
output [31:0] wb1_dat_o;
output wb1_ack_o;

input wb2_cyc_i, wb2_stb_i, wb2_we_i;
input [3:0] wb2_sel_i;
input [31:0] wb2_adr_i, wb2_dat_i;
output [31:0] wb2_dat_o;
output wb2_ack_o;

input wb3_cyc_i, wb3_stb_i, wb3_we_i;
input [3:0] wb3_sel_i;
input [31:0] wb3_adr_i, wb3_dat_i;
output [31:0] wb3_dat_o;
output wb3_ack_o;

input wb4_cyc_i, wb4_stb_i, wb4_we_i;
input [3:0] wb4_sel_i;
input [31:0] wb4_adr_i, wb4_dat_i;
output [31:0] wb4_dat_o;
output wb4_ack_o;

output sck_o;
output csn_o;

output [3:0] d_o;
output [3:0] d_oe_o;
input [3:0] d_i;

wire [31:0] wb_adr_i;
wire [31:0] wb_dat_i;
wire [3:0] wb_sel_i;
wire [31:0] wb_dat_o;
wire wb_ack_o, wb_cyc_i, wb_stb_i, wb_we_i;
wb_arbiter arbitercore(
  .wb_clk_i(wb_clk_i),

  .wbowner_dat_o(wb_dat_i),
  .wbowner_dat_i(wb_dat_o),
  .wbowner_adr_o(wb_adr_i),
  .wbowner_cyc_o(wb_cyc_i),
  .wbowner_stb_o(wb_stb_i),
  .wbowner_we_o(wb_we_i),
  .wbowner_ack_i(wb_ack_o),
  .wbowner_sel_o(wb_sel_i),

  .wb1_adr_i(wb1_adr_i),
  .wb1_dat_i(wb1_dat_i),
  .wb1_dat_o(wb1_dat_o),
  .wb1_cyc_i(wb1_cyc_i),
  .wb1_stb_i(wb1_stb_i),
  .wb1_we_i(wb1_we_i),
  .wb1_ack_o(wb1_ack_o),
  .wb1_sel_i(wb1_sel_i),

  .wb2_adr_i(wb2_adr_i),
  .wb2_dat_i(wb2_dat_i),
  .wb2_dat_o(wb2_dat_o),
  .wb2_cyc_i(wb2_cyc_i),
  .wb2_stb_i(wb2_stb_i),
  .wb2_we_i(wb2_we_i),
  .wb2_ack_o(wb2_ack_o),
  .wb2_sel_i(wb2_sel_i),

  .wb3_adr_i(wb3_adr_i),
  .wb3_dat_i(wb3_dat_i),
  .wb3_dat_o(wb3_dat_o),
  .wb3_cyc_i(wb3_cyc_i),
  .wb3_stb_i(wb3_stb_i),
  .wb3_we_i(wb3_we_i),
  .wb3_ack_o(wb3_ack_o),
  .wb3_sel_i(wb3_sel_i),

  .wb4_adr_i(wb4_adr_i),
  .wb4_dat_i(wb4_dat_i),
  .wb4_dat_o(wb4_dat_o),
  .wb4_cyc_i(wb4_cyc_i),
  .wb4_stb_i(wb4_stb_i),
  .wb4_we_i(wb4_we_i),
  .wb4_ack_o(wb4_ack_o),
  .wb4_sel_i(wb4_sel_i)
);

// States:
reg [1:0] state;

wire [1:0] idle = 2'd0;
wire [1:0] between_bytes = 2'd1;
wire [1:0] running = 2'd2;
wire [1:0] stalled = 2'd3;

reg [4:0] speed, cntdown;
reg [3:0] bits;
reg x4, cs, sck, eot, rd, latch;
reg [1:0] inter;
reg [3:0] d, oe;
reg [15:0] wdat, rdat;
reg wb_ack;
assign wb_ack_o = wb_ack;
assign wb_dat_o = rdat; // XXX needs byte swap

assign sck_o = sck;
assign d_o = d;
assign d_oe_o = oe;
assign csn_o = !cs;

`undef scope5
`ifdef scope5
wire [71:0] scope_dat = {
  //oe, // 71:68
  3'd0,
  eot, // 68
  wb_adr_i[3:0], // 67:64
  bits, // 63:60
  state, // 59:58
  x4, // 57
  rd, // 56
  wb_cyc_i, // 55
  wb_stb_i, // 54
  wb_we_i, // 53
  wb_ack, // 52
  wb_dat_i[15:0], // 51:36
  rdat, // 35:20
  wdat, // 19:4
  cs, // 3
  sck, // 2
  d[0], // 1
  d_i[1] // 0
};

wire [7:0] scope_trig = {eot, 2'd0, d, cs};

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

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

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

task start_word;
  begin
    state <= running;
    sck <= 1'b0;
    eot <= wb_adr_i[2];
    rd <= !wb_we_i;
    oe <= 4'b1101;
    if (x4) oe <= 4'b0000;
    if (x4 && wb_we_i) oe <= 4'b1111;
    d[3:2] <= 2'b11;
    // XXX need to modify this with byte swapping which will simplify it
    if (wb_sel_i[1]) begin
      wdat <= wb_dat_i;
      d[0] <= wb_dat_i[15];
      bits <= 4'd15;
      if (x4) d <= wb_dat_i[15:12];
      if (x4) bits <= 4'd3;
    end else begin
      wdat[15:8] <= wb_dat_i[7:0];
      d[0] <= wb_dat_i[7];
      bits <= 4'd7;
      if (x4) d <= wb_dat_i[7:4];
      if (x4) bits <= 4'd1;
    end
  end
endtask

wire dat_cycle = wb_stb_i && wb_cyc_i && wb_adr_i[3];

always @(posedge wb_clk_i or posedge wb_rst_i) begin
  if (wb_rst_i) begin
    state <= idle;
    wb_ack <= 1'b0;
    sck <= 1'b1;
    x4 <= 1'b0;
    bits <= 4'd0;
    cs <= 1'b0;
    inter <= 2'd0;
    oe <= 4'b1100;
    d <= 4'b1111;
    eot <= 1'b0;
    rd <= 1'b0;
    latch <= 1'b0;
    rdat <= 16'd0;
    wdat <= 16'd0;
    speed <= 5'd1;
    cntdown <= 5'd0;
  end else begin
    cntdown <= cntdown - 1'b1;
    wb_ack <= 1'b0;
    latch <= 1'b0;

    if (latch) begin
      rdat <= {rdat[14:0], d_i[1]};
      if (x4) rdat <= {rdat[11:0], d_i};
    end

    case (state)

      idle: begin
        cs <= 1'b0;
	inter <= 2'd0;
        eot <= 1'b0;
        oe <= 4'b1100;
        d[3:2] <= 2'b11;
        cntdown <= speed;
        if (wb_cyc_i && wb_stb_i && !wb_ack) begin
          if (wb_adr_i[3]) begin
            state <= between_bytes;
            cs <= 1'b1;
          end else begin
            wb_ack <= 1'b1;
            if (wb_we_i) begin
              x4 <= wb_dat_i[1];
              speed[3:0] <= wb_dat_i[13:10];
              speed[4] <= wb_dat_i[0];
            end
            rdat <= 16'd0;
            rdat[13:10] <= speed[3:0];
            rdat[0] <= speed[4];
            rdat[1] <= x4;
          end
        end
      end

      between_bytes: begin
        if (cntdown == 5'd1) begin
          cntdown <= speed;
          if (eot) cs <= 1'b0;
	  // inter is for inteval required between transactions
	  if (!cs) inter <= inter + 1'b1;
	  else inter <= 2'd0;
	  if (inter == 2'd2) state <= idle;
          if (!eot && wb_cyc_i && wb_stb_i && !wb_ack) begin
            wb_ack <= wb_we_i;
            start_word();
          end
        end
      end

      running: begin
        if (cntdown == 5'd1) begin
          cntdown <= speed;
          if (sck) begin // falling edge
            sck <= 1'b0;
            bits <= bits - 1'b1;
            wdat <= {wdat[14:0], 1'b0};
            if (x4) wdat <= {wdat[11:0], 4'd0};
            d[0] <= wdat[14];
            if (x4) d <= wdat[11:8];
            if (bits == 4'd0) begin
              sck <= 1'b1;
              state <= between_bytes;
	      if (dat_cycle && !eot) eot <= wb_adr_i[2];
              // if a new write is coming in, start it
              if (!rd && wb_we_i && dat_cycle && !eot) start_word();
              // if a streaming read is occurring, start the next read
              if (!wb_we_i && dat_cycle && wb_adr_i[1]) start_word();
              // transition from writing to reading: start a read
              if (!rd && !wb_we_i && dat_cycle && !eot) start_word();
              // if reading and no wishbone cycle is coming in, we stall
              if (rd && !dat_cycle) state <= stalled;
              // any read or write gets an ack
              if (dat_cycle) wb_ack <= 1'b1;
              // except on a write to read transition
              if (!rd && !wb_we_i && dat_cycle) wb_ack <= 1'b0;
	      // or a cycle following an end of transmit write
	      if (!rd && eot) wb_ack <= 1'b0;
	      // XXX eot is not getting set right on final read
            end
          end else begin // rising edge
            sck <= 1'b1;
            latch <= 1'b1;
          end
        end
      end

      stalled: begin
        cntdown <= speed;
        if (wb_cyc_i && wb_stb_i) begin
          wb_ack <= 1'b1;
          if (wb_adr_i[1]) start_word;
          else begin
            state <= between_bytes;
            eot <= 1'b1;
          end
        end
      end
    
    endcase
  end
end

endmodule


