aboutsummaryrefslogtreecommitdiff
path: root/models/flash_memory.v
blob: 43d6c996122e929853300b7d376cfad1ad10a284 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
`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