blob: cd33c11b34ce4c88390e5c4a703506e6b41fa166 [file] [log] [blame]
`default_nettype none
/*
* SPDX-FileCopyrightText: 2022 <Dinesh Annayya>
*
* Riscdunio
*
* Copyright (C) 2022 Dinesh Annayya <dinesha.opencore.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* SPDX-License-Identifier: ISC
*/
`timescale 1 ns / 1 ps
//
// Simple SPI Ram simulation model for 128Kx8 LOW VOLTAGE, FAST SERIAL SRAM
// (IS62/65WVS1288GALL)
//
// This model samples io input signals 1ns before the SPI clock edge and
// updates output signals 1ns after the SPI clock edge.
//
// Supported commands:
// 0x03, 0x02, 0x3B , 0x38, 0xFF, 0x05 , 0x01
// Instruction Hex Description
// READ 0x03 Read data from memory array beginning at selected address
// WRITE 0x02 Write data to memory array beginning at selected address
// ESDI 0x3B Enter SDI mode
// ESQI 0x38 Enter SQI mode
// RSTDQI 0xFF Reset SDI/SQI mode
// RDMR 0x05 Read Mode Register
// WRMR 0x01 Write Mode Register
//
module spiram #(
parameter mem_file_name = "firmware.hex"
)(
input csb,
input clk,
inout io0, // MOSI
inout io1, // MISO
inout io2,
inout io3
);
localparam verbose = 0;
localparam integer latency = 8;
reg [7:0] buffer;
reg [3:0] reset_count = 0;
reg [3:0] reset_monitor = 0;
integer bitcount = 0;
integer bytecount = 0;
integer dummycount = 0;
reg [7:0] spi_cmd;
reg [23:0] spi_addr;
reg [7:0] spi_in;
reg [7:0] spi_out;
reg spi_io_vld;
localparam [1:0] sspi = 1;
localparam [1:0] dspi = 2;
localparam [1:0] qspi = 3;
localparam [3:0] mode_sspi_rd = 1;
localparam [3:0] mode_sspi_wr = 2;
localparam [3:0] mode_dspi_rd = 3;
localparam [3:0] mode_dspi_wr = 4;
localparam [3:0] mode_qspi_rd = 5;
localparam [3:0] mode_qspi_wr = 6;
reg [3:0] spi_phase = mode_sspi_rd;
reg [3:0] spi_data_phase = 0;
reg [3:0] spi_mode = sspi;
reg io0_oe = 0;
reg io1_oe = 0;
reg io2_oe = 0;
reg io3_oe = 0;
reg io0_dout = 0;
reg io1_dout = 0;
reg io2_dout = 0;
reg io3_dout = 0;
assign #1 io0 = io0_oe ? io0_dout : 1'bz;
assign #1 io1 = io1_oe ? io1_dout : 1'bz;
assign #1 io2 = io2_oe ? io2_dout : 1'bz;
assign #1 io3 = io3_oe ? io3_dout : 1'bz;
wire io0_delayed;
wire io1_delayed;
wire io2_delayed;
wire io3_delayed;
assign #1 io0_delayed = io0;
assign #1 io1_delayed = io1;
assign #1 io2_delayed = io2;
assign #1 io3_delayed = io3;
// 128KB RAM
reg [7:0] memory [0:128*1024-1];
initial begin
if (!(mem_file_name == "none"))
$readmemh(mem_file_name,memory);
end
task spi_action;
begin
spi_in = buffer;
if (bytecount == 1) begin
spi_cmd = buffer;
if (spi_cmd == 8'h 3b) begin
spi_mode = dspi;
end
if (spi_cmd == 8'h 38) begin
spi_mode = qspi;
end
if (spi_cmd == 8'h ff) begin
spi_mode = sspi;
end
// spi read
if (spi_cmd == 8'h 03 && spi_mode == sspi)
spi_phase = mode_sspi_rd;
// spi write
if (spi_cmd == 8'h 02 && spi_mode == sspi)
spi_phase = mode_sspi_wr;
// dual spi read
if (spi_cmd == 8'h 03 && spi_mode == dspi)
spi_phase = mode_dspi_rd;
// dual spi write
if (spi_cmd == 8'h 02 && spi_mode == dspi)
spi_phase = mode_dspi_wr;
// quad spi read
if (spi_cmd == 8'h 03 && spi_mode == qspi)
spi_phase = mode_qspi_rd;
// quad spi write
if (spi_cmd == 8'h 02 && spi_mode == qspi)
spi_phase = mode_qspi_wr;
end
if (spi_cmd == 'h 03 || (spi_cmd == 'h 02)) begin
if (bytecount == 2)
spi_addr[23:16] = buffer;
if (bytecount == 3)
spi_addr[15:8] = buffer;
if (bytecount == 4) begin
spi_addr[7:0] = buffer;
spi_data_phase = spi_phase;
end
// Dummy by selection at end of address phase for read
// mode only
if (bytecount == 4 && spi_mode == sspi && spi_cmd ==8'h03 )
dummycount = 8;
if (bytecount == 4 && spi_mode == dspi && spi_cmd ==8'h03)
dummycount = 4;
if (bytecount == 4 && spi_mode == qspi && spi_cmd ==8'h03)
dummycount = 2;
if (bytecount >= 4 && spi_cmd ==8'h03) begin // Data Read Phase
buffer = memory[spi_addr];
//$display("%m: Read Memory Address: %x Data: %x",spi_addr,buffer);
spi_addr = spi_addr + 1;
end
if (bytecount > 4 && spi_cmd ==8'h02) begin // Data Write Phase
memory[spi_addr] = buffer;
//$display("%m: Write Memory Address: %x Data: %x",spi_addr,buffer);
spi_addr = spi_addr + 1;
end
end
spi_out = buffer;
spi_io_vld = 1;
if (verbose) begin
if (bytecount == 1)
$write("<SPI-START>");
$write("<SPI:%02x:%02x>", spi_in, spi_out);
end
end
endtask
always @(csb) begin
if (csb) begin
if (verbose) begin
$display("");
$fflush;
end
buffer = 0;
bitcount = 0;
bytecount = 0;
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
spi_data_phase = 0;
end
end
always @(csb, clk) begin
spi_io_vld = 0;
if (!csb && !clk) begin
if (dummycount > 0) begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end else
case (spi_data_phase)
mode_sspi_rd: begin
io0_oe = 0;
io1_oe = 1;
io2_oe = 0;
io3_oe = 0;
io1_dout = buffer[7];
end
mode_sspi_wr: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
mode_dspi_wr: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
mode_dspi_rd: begin
io0_oe = 1;
io1_oe = 1;
io2_oe = 0;
io3_oe = 0;
io0_dout = buffer[6];
io1_dout = buffer[7];
end
mode_qspi_wr: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
mode_qspi_rd: begin
io0_oe = 1;
io1_oe = 1;
io2_oe = 1;
io3_oe = 1;
io0_dout = buffer[4];
io1_dout = buffer[5];
io2_dout = buffer[6];
io3_dout = buffer[7];
end
default: begin
io0_oe = 0;
io1_oe = 0;
io2_oe = 0;
io3_oe = 0;
end
endcase
end
end
always @(posedge clk) begin
if (!csb) begin
if (dummycount > 0) begin
dummycount = dummycount - 1;
end else
case (spi_mode)
sspi: begin
buffer = {buffer, io0};
bitcount = bitcount + 1;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
dspi: begin
buffer = {buffer, io1, io0};
bitcount = bitcount + 2;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
qspi: begin
buffer = {buffer, io3, io2, io1, io0};
bitcount = bitcount + 4;
if (bitcount == 8) begin
bitcount = 0;
bytecount = bytecount + 1;
spi_action;
end
end
endcase
end
end
endmodule