module simple_spi_top(
  // 8bit WISHBONE bus slave interface
  input  wire       clk_i,         // clock
  input  wire       rst_i,         // reset (asynchronous active low)
  input  wire       cyc_i,         // cycle
  input  wire       stb_i,         // strobe
  input  wire [1:0] adr_i,         // address
  input  wire       we_i,          // write enable
  input  wire [7:0] dat_i,         // data input
  output reg  [7:0] dat_o,         // data output
  output reg        ack_o,         // normal bus termination
  output reg        inta_o,        // interrupt output

  // SPI port
  output reg        sck_o,         // serial clock output
  output wire       mosi_o,        // MasterOut SlaveIN
  input  wire       miso_i         // MasterIn SlaveOut
);

  //
  // Module body
  //
  reg  [7:0] spcr;       // Serial Peripheral Control Register ('HC11 naming)
  wire [7:0] spsr;       // Serial Peripheral Status register ('HC11 naming)
  reg  [7:0] sper;       // Serial Peripheral Extension register
  reg  [7:0] treg, rreg; // Transmit/Receive register

  // fifo signals
  wire [7:0] rfdout;
  reg        wfre, rfwe;
  wire       rfre, rffull, rfempty;
  wire [7:0] wfdout;
  wire       wfwe, wffull, wfempty;

  // misc signals
  wire      tirq;     // transfer interrupt (selected number of transfers done)
  wire      wfov;     // write fifo overrun (writing while fifo full)
  reg [1:0] state;    // statemachine state
  reg [2:0] bcnt;

  //
  // Wishbone interface
  wire wb_acc = cyc_i & stb_i;       // WISHBONE access
  wire wb_wr  = wb_acc & we_i;       // WISHBONE write access

  // dat_i
  always @(posedge clk_i or negedge rst_i)
    if (~rst_i)
      begin
          spcr <= #1 8'h10;  // set master bit
          sper <= #1 8'h00;
      end
    else if (wb_wr)
      begin
        if (adr_i == 2'b00)
          spcr <= #1 dat_i | 8'h10; // always set master bit

        if (adr_i == 2'b11)
          sper <= #1 dat_i;
      end

  // write fifo
  assign wfwe = wb_acc & (adr_i == 2'b10) & ack_o &  we_i;
  assign wfov = wfwe & wffull;

  // dat_o
  always @(posedge clk_i)
    case(adr_i) // synopsys full_case parallel_case
      2'b00: dat_o <= #1 spcr;
      2'b01: dat_o <= #1 spsr;
      2'b10: dat_o <= #1 rfdout;
      2'b11: dat_o <= #1 sper;
    endcase

  // read fifo
  assign rfre = wb_acc & (adr_i == 2'b10) & ack_o & ~we_i;

  // ack_o
  always @(posedge clk_i or negedge rst_i)
    if (~rst_i)
      ack_o <= #1 1'b0;
    else
      ack_o <= #1 wb_acc & !ack_o;

  // decode Serial Peripheral Control Register
  wire       spie = spcr[7];   // Interrupt enable bit
  wire       spe  = spcr[6];   // System Enable bit
  wire       dwom = spcr[5];   // Port D Wired-OR Mode Bit
  wire       mstr = spcr[4];   // Master Mode Select Bit
  wire       cpol = spcr[3];   // Clock Polarity Bit
  wire       cpha = spcr[2];   // Clock Phase Bit
  wire [1:0] spr  = spcr[1:0]; // Clock Rate Select Bits

  // decode Serial Peripheral Extension Register
  wire [1:0] icnt = sper[7:6]; // interrupt on transfer count
  wire [1:0] spre = sper[1:0]; // extended clock rate select

  wire [3:0] espr = {spre, spr};

  // generate status register
  wire wr_spsr = wb_wr & (adr_i == 2'b01);

  reg spif;
  always @(posedge clk_i)
    if (~spe)
      spif <= #1 1'b0;
    else
      spif <= #1 (tirq | spif) & ~(wr_spsr & dat_i[7]);

  reg wcol;
  always @(posedge clk_i)
    if (~spe)
      wcol <= #1 1'b0;
    else
      wcol <= #1 (wfov | wcol) & ~(wr_spsr & dat_i[6]);

  assign spsr[7]   = spif;
  assign spsr[6]   = wcol;
  assign spsr[5:4] = 2'b00;
  assign spsr[3]   = wffull;
  assign spsr[2]   = wfempty;
  assign spsr[1]   = rffull;
  assign spsr[0]   = rfempty;
  

  // generate IRQ output (inta_o)
  always @(posedge clk_i)
    inta_o <= #1 spif & spie;

  //
  // hookup read/write buffer fifo
  fifo4 #(8)
  rfifo(
	.clk   ( clk_i   ),
	.rst   ( rst_i   ),
	.clr   ( ~spe    ),
	.din   ( treg    ),
	.we    ( rfwe    ),
	.dout  ( rfdout  ),
	.re    ( rfre    ),
	.full  ( rffull  ),
	.empty ( rfempty )
  ),
  wfifo(
	.clk   ( clk_i   ),
	.rst   ( rst_i   ),
	.clr   ( ~spe    ),
	.din   ( dat_i   ),
	.we    ( wfwe    ),
	.dout  ( wfdout  ),
	.re    ( wfre    ),
	.full  ( wffull  ),
	.empty ( wfempty )
  );

  //
  // generate clk divider
  reg [11:0] clkcnt;
  always @(posedge clk_i)
    if(spe & (|clkcnt & |state))
      clkcnt <= #1 clkcnt - 11'h1;
    else
      case (espr) // synopsys full_case parallel_case
        4'b0000: clkcnt <= #1 12'h0;   // 2   -- original M68HC11 coding
        4'b0001: clkcnt <= #1 12'h1;   // 4   -- original M68HC11 coding
        4'b0010: clkcnt <= #1 12'h3;   // 16  -- original M68HC11 coding
        4'b0011: clkcnt <= #1 12'hf;   // 32  -- original M68HC11 coding
        4'b0100: clkcnt <= #1 12'h1f;  // 8
        4'b0101: clkcnt <= #1 12'h7;   // 64
        4'b0110: clkcnt <= #1 12'h3f;  // 128
        4'b0111: clkcnt <= #1 12'h7f;  // 256
        4'b1000: clkcnt <= #1 12'hff;  // 512
        4'b1001: clkcnt <= #1 12'h1ff; // 1024
        4'b1010: clkcnt <= #1 12'h3ff; // 2048
        4'b1011: clkcnt <= #1 12'h7ff; // 4096
      endcase

  // generate clock enable signal
  wire ena = ~|clkcnt;

  // transfer statemachine
  always @(posedge clk_i)
    if (~spe)
      begin
          state <= #1 2'b00; // idle
          bcnt  <= #1 3'h0;
          treg  <= #1 8'h00;
          wfre  <= #1 1'b0;
          rfwe  <= #1 1'b0;
          sck_o <= #1 1'b0;
      end
    else
      begin
         wfre <= #1 1'b0;
         rfwe <= #1 1'b0;

         case (state) //synopsys full_case parallel_case
           2'b00: // idle state
              begin
                  bcnt  <= #1 3'h7;   // set transfer counter
                  treg  <= #1 wfdout; // load transfer register
                  sck_o <= #1 cpol;   // set sck

                  if (~wfempty) begin
                    wfre  <= #1 1'b1;
                    state <= #1 2'b01;
                    if (cpha) sck_o <= #1 ~sck_o;
                  end
              end

           2'b01: // clock-phase2, next data
              if (ena) begin
                sck_o   <= #1 ~sck_o;
                state   <= #1 2'b11;
              end

           2'b11: // clock phase1
              if (ena) begin
                treg <= #1 {treg[6:0], miso_i};
                bcnt <= #1 bcnt -3'h1;

                if (~|bcnt) begin
                  state <= #1 2'b00;
                  sck_o <= #1 cpol;
                  rfwe  <= #1 1'b1;
                end else begin
                  state <= #1 2'b01;
                  sck_o <= #1 ~sck_o;
                end
              end

           2'b10: state <= #1 2'b00;
         endcase
      end

  assign mosi_o = treg[7];


  // count number of transfers (for interrupt generation)
  reg [1:0] tcnt; // transfer count
  always @(posedge clk_i)
    if (~spe)
      tcnt <= #1 icnt;
    else if (rfwe) // rfwe gets asserted when all bits have been transfered
      if (|tcnt)
        tcnt <= #1 tcnt - 2'h1;
      else
        tcnt <= #1 icnt;

  assign tirq = ~|tcnt & rfwe;

endmodule