aboutsummaryrefslogtreecommitdiff
`default_nettype none
`timescale 1ns/1ns

`include "messages.vh"

`ifndef SIMULATION
 `error_SIMULATION_not_defined
; /* Cause syntax error */
`endif

module W25Q16BV_flash
  #(
    parameter BYTES_TO_INITIALIZE = 0,
    parameter INITIAL_CONTENTS_FILE = "some_file.mem"
    )
   (
    /* wires we're not using are omitted */
    input wire 	sdo,
    output wire sdi,
    input wire 	sck,
    input wire 	ss_n
    );

   reg 		outputting;
   initial
     outputting <= 0;
   reg 		outputted_bit;
   assign sdi = (outputting && !ss_n) ? outputted_bit : 1'bz;

   reg 		powered_up;
   initial
     powered_up <= 0;

   reg 		powering_up_in_progress;
   initial
     powering_up_in_progress <= 0;

   parameter power_up_wait_time = 3000; /* ns */
   integer 	power_up_time;

   parameter idle = 0;
   parameter error = 1;
   parameter receiving_instruction = 2;
   parameter releasing_power_down = 3;
   parameter receiving_fast_read_address = 4;
   parameter receiving_fast_read_dummy_byte = 5;
   parameter responding_fast_read = 6;
   integer 	state;
   initial
     state <= idle;

   parameter release_power_down_instruction = 8'hAB;
   parameter fast_read_instruction = 8'h0B;

   reg [7:0] 	instruction;
   integer 	instruction_bits_received;

   reg [23:0] 	address;
   integer 	address_bits_received;
   reg [2:0] 	bit_in_byte;

   integer 	dummy_bits_received;

   parameter memory_size = 1024 * 1024 * 2; /* 2 megabytes */
   reg [7:0] 	memory [memory_size - 1 : 0];

   generate
      if (BYTES_TO_INITIALIZE) begin
	 initial
	   $readmemh(INITIAL_CONTENTS_FILE, memory, 0, BYTES_TO_INITIALIZE - 1);
      end
   endgenerate

   always @ (posedge sck) begin
      if (!powered_up && power_up_time + power_up_wait_time < $time) begin
	 powered_up <= 1;
	 if (state == releasing_power_down)
	   state = idle;
      end

      case (state)
	idle : begin
	   if (!ss_n) begin
	      state <= receiving_instruction;
	      instruction_bits_received <= 1;
	      instruction[7] <= sdo;
	   end
	end
	error : begin
	   if (ss_n) begin
	      state <= idle;
	   end
	end
	receiving_instruction : begin
	   if (ss_n) begin
	      state <= idle;
	      `MSG(("SPI: error: operation aborted after only %0d instruction bits received",
		    instruction_bits_received));
	   end else begin
	      instruction[7 - instruction_bits_received] <= sdo;
	      instruction_bits_received <= instruction_bits_received + 1;

	      if (instruction_bits_received + 1 == 8) begin
		 if (!powered_up && ({instruction[7:1], sdo} !=
				     release_power_down_instruction)) begin
		    state <= error;
		    `MSG(("SPI: error: attempted instruction 0x%x while in power down mode",
			  {instruction[7:1], sdo}));
		 end else begin
		    case ({instruction[7:1], sdo})
		      release_power_down_instruction : begin
			 state <= releasing_power_down;
			 power_up_time <= $time;
			 `DBG(("SPI: release power down issued"));
		      end
		      fast_read_instruction : begin
			 state <= receiving_fast_read_address;
			 address_bits_received <= 0;
			 `DBG(("SPI: fast read issued"));
		      end
		      default : begin
			 state <= error;
			 `MSG(("SPI: error: unknown instruction: 0x%x",
			       {instruction[7:1], sdo}));
		      end
		    endcase // case ({instruction[7:1], sdo})
		 end // else: !if(!powered_up && ({instruction[7:1], sdo} !=...
	      end // if (instruction_bits_received + 1 == 8)
	   end // else: !if(ss_n)
	end // case: receiving_instruction
	releasing_power_down : begin
	   if (!ss_n) begin
	      state <= receiving_instruction;
	      instruction_bits_received <= 1;
	      instruction[7] <= sdo;

	      if (power_up_time + power_up_wait_time >= $time) begin
		 `MSG(("SPI: error: release power down interrupted %0d ns before finishing",
		       power_up_time + power_up_wait_time - $time));
	      end
	   end // if (!ss_n)
	end // case: releasing_power_down
	receiving_fast_read_address : begin
	   if (ss_n) begin
	      state <= idle;
	      `MSG(("SPI: error: fast read instruction aborted after %0d bits of address were sent",
		    address_bits_received));
	   end else begin
	      address[23 - address_bits_received] <= sdo;
	      address_bits_received <= address_bits_received + 1;

	      if (address_bits_received + 1 == 24) begin
		 state <= receiving_fast_read_dummy_byte;
		 dummy_bits_received <= 0;
	      end
	   end
	end
	receiving_fast_read_dummy_byte : begin
	   if (ss_n) begin
	      state <= idle;
	      `MSG(("SPI: error: fast read instruction aborted after %0d bits of dummy byte were sent",
		    dummy_bits_received));
	   end else begin
	      dummy_bits_received <= dummy_bits_received + 1;

	      if (dummy_bits_received + 1 == 8) begin
		 bit_in_byte <= 7;

		 if (address >= memory_size) begin
		    state <= error;
		    `MSG(("SPI: error: memory address too high (0x%x)",
			  address));
		 end else begin
		    state <= responding_fast_read;
		 end
	      end
	   end // else: !if(ss_n)
	end // case: receiving_fast_read_dummy_byte
	responding_fast_read : begin
	   if (ss_n) begin
	      state <= idle;
	   end else begin
	      bit_in_byte <= bit_in_byte - 1;

	      if (bit_in_byte == 0) begin
		 address <= address + 1;

		 if (address + 1 >= memory_size) begin
		    state <= error;
		    `MSG(("SPI: error: memory address too high (0x%x)",
			  address));
		 end
	      end
	   end // else: !if(ss_n)
	end // case: responding_fast_read
      endcase // case (state)
   end // always @ (posedge sck)

   always @ (negedge sck) begin
      if (ss_n) begin
	 outputting <= 0;
      end else if (state == responding_fast_read) begin
	 outputting <= 1;
	 outputted_bit <= memory[address][bit_in_byte];
      end else if (state == error) begin
	 outputted_bit <= 1'bx;
      end else begin
	 outputting <= 0;
      end
   end // always @ (negedge sck)
endmodule // W25Q16BV_flash