diff options
Diffstat (limited to 'models')
-rw-r--r-- | models/flash_memory.v | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/models/flash_memory.v b/models/flash_memory.v new file mode 100644 index 0000000..3b6c659 --- /dev/null +++ b/models/flash_memory.v @@ -0,0 +1,206 @@ +`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]; + initial + $readmemh(INITIAL_CONTENTS_FILE, memory, 0, BYTES_TO_INITIALIZE - 1); + + 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 |