//======================================================================
//
// ram.v
// -----
// Module that encapsulates the four SPRAM blocks in the Lattice
// iCE40UP 5K device. This creates a single 32-bit wide,
// 128 kByte large memory.
//
// Author: Joachim Strombergson
// Copyright (C) 2022 - Tillitis AB
// SPDX-License-Identifier: GPL-2.0-only
//
//======================================================================

`default_nettype none

module ram(
	   input   wire          clk,
	   input   wire          reset_n,
           input   wire          cs,
	   input   wire [03 : 0] we,
	   input   wire [14 : 0] address,
	   input   wire [31 : 0] write_data,
	   output  wire [31 : 0] read_data,
	   output  wire          ready
          );


  //----------------------------------------------------------------
  // Registers and wires.
  //----------------------------------------------------------------
  reg ready_reg;

  reg          cs0;
  reg          cs1;
  reg [31 : 0] read_data0;
  reg [31 : 0] read_data1;
  reg [31 : 0] muxed_read_data;


  //----------------------------------------------------------------
  // Concurrent assignment of ports.
  //----------------------------------------------------------------
  assign read_data = muxed_read_data;
  assign ready     = ready_reg;


  //----------------------------------------------------------------
  // SPRAM instances.
  //----------------------------------------------------------------
  SB_SPRAM256KA spram0(
		       .ADDRESS(address[13:0]),
		       .DATAIN(write_data[15:0]),
		       .MASKWREN({we[1], we[1], we[0], we[0]}),
		       .WREN(we[1] | we[0]),
		       .CHIPSELECT(cs0),
		       .CLOCK(clk),
		       .STANDBY(1'b0),
		       .SLEEP(1'b0),
		       .POWEROFF(1'b1),
		       .DATAOUT(read_data0[15:0])
	              );

  SB_SPRAM256KA spram1(
		       .ADDRESS(address[13:0]),
		       .DATAIN(write_data[31:16]),
		       .MASKWREN({we[3], we[3], we[2], we[2]}),
		       .WREN(we[3] | we[2]),
		       .CHIPSELECT(cs0),
		       .CLOCK(clk),
		       .STANDBY(1'b0),
		       .SLEEP(1'b0),
		       .POWEROFF(1'b1),
		       .DATAOUT(read_data0[31:16])
	              );


  SB_SPRAM256KA spram2(
		       .ADDRESS(address[13:0]),
		       .DATAIN(write_data[15:0]),
		       .MASKWREN({we[1], we[1], we[0], we[0]}),
		       .WREN(we[1] | we[0]),
		       .CHIPSELECT(cs1),
		       .CLOCK(clk),
		       .STANDBY(1'b0),
		       .SLEEP(1'b0),
		       .POWEROFF(1'b1),
		       .DATAOUT(read_data1[15:0])
	              );

  SB_SPRAM256KA spram3(
		       .ADDRESS(address[13:0]),
		       .DATAIN(write_data[31:16]),
		       .MASKWREN({we[3], we[3], we[2], we[2]}),
		       .WREN(we[3] | we[2]),
		       .CHIPSELECT(cs1),
		       .CLOCK(clk),
		       .STANDBY(1'b0),
		       .SLEEP(1'b0),
		       .POWEROFF(1'b1),
		       .DATAOUT(read_data1[31:16])
	              );


  //----------------------------------------------------------------
  // reg_update
  //
  // Posedge triggered with synchronous, active low reset.
  // This simply creates a one cycle access latency to match
  // the latency of the spram blocks.
  //----------------------------------------------------------------
  always @(posedge clk)
    begin : reg_update
      if (!reset_n) begin
        ready_reg <= 1'h0;
      end
      else begin
        ready_reg <= cs;
      end
    end


  //----------------------------------------------------------------
  // mem_mux
  //----------------------------------------------------------------
  always @*
    begin : mem_mux
      cs0 = 1'h0;
      cs1 = 1'h0;

      if (address[14]) begin
        cs1             = cs;
        muxed_read_data = read_data1;
      end else begin
        cs0             = cs;
        muxed_read_data = read_data0;
      end
    end

endmodule // ram

//======================================================================
// EOF ram.v
//======================================================================