/*
 Dual-port block RAM for data acquisition and playback. One port is dedicated
 to the local bus, the other is available for high-speed reading or writing.

 Copyright (c) 2006 Dimtel, Inc., All Rights Reserved

 $Id: mem_rp.v,v 1.6 2013/04/02 07:11:27 dim Exp $
 $Date: 2013/04/02 07:11:27 $
 $Author: dim $
 $Revision: 1.6 $
 $Log: mem_rp.v,v $
 Revision 1.6  2013/04/02 07:11:27  dim
 Added instantiated BRAM module with parameterized sizing

 Revision 1.5  2012/09/25 21:02:39  dim
 Added mem_rp1x54 for RTNA

 Revision 1.4  2011/12/28 09:53:26  dim
 Fixed typo in a comment

 Revision 1.3  2010/10/05 16:13:50  dim
 Updates prior to iGp12 release

 Revision 1.2  2009/06/02 20:08:48  dim
 Added mem_rp8A_inst for TI ADC testing

 Revision 1.1.1.1  2009/02/05 07:20:51  dim
 Initial import

 Revision 1.6  2007/12/18 03:55:40  dim
 Added CIC filter, selectable GPIO devices

 Revision 1.5  2007/08/09 17:25:26  dim
 Drive pattern generator, support for FE/BE devices

 Revision 1.4  2007/05/14 17:34:02  dim
 Bug fixes, high-speed acquisition code, conversion to 9.1i

 Revision 1.3  2007/03/14 08:37:48  dim
 Gateware revision 1.01: first release of full iGp functionality

 Revision 1.2  2007/02/09 00:35:50  dim
 Final 16-bit version of the gateware

 Revision 1.1.1.1  2006/11/27 07:48:46  dim
 Initial import

*/

`timescale 1ns/100ps

`include "bus_width.h"

module mem_rp32(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [14:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [14:0]		addr2,
  input [`IFACE-1:0]	din2
);
  reg [14:0] read_addr = 15'b0;
  reg [`IFACE-1:0] ram[32767:0];

  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    read_addr <= addr;
    if (strobe & ce & ~ack) begin
      if (~rd)
        ram[addr] <= din;
      ack <= 1'b1;
    end
    if (ack) ack <= 1'b0;
  end

  always @(posedge clk2)
    if (ce2) ram[addr2] <= din2;

  assign dout = ram[read_addr]; 
endmodule // mem_rp32

module mem_rp16(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [13:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [13:0]		addr2,
  input [`IFACE-1:0]	din2
);
  reg [13:0] read_addr = 14'b0;
  reg [`IFACE-1:0] ram[16383:0];

  initial begin 
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    read_addr <= addr;
    if (strobe & ce & ~ack) begin
	ack <= 1'b1;
    end
    if (ack) ack <= 1'b0;
  end

  always @(posedge clk2)
    if (ce2) ram[addr2] <= din2;

  assign dout = ram[read_addr]; 
  
endmodule // mem_rp16

module mem_rp32_inst(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [14:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [14:0]		addr2,
  input [`IFACE-1:0]	din2
);
  wire [`IFACE-1:0] doutH, doutL;
  wire ena = strobe & ce & ~ack;
  
  initial begin
    ack <= 1'b0;
  end

  assign dout = addr[14] ? doutH : doutL;

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < `IFACE; i = i + 1) begin: blk_
    RAMB16_S1_S1  ramL(.DOA(doutL[i]),		.DOB(), 
                       .ADDRA(addr[13:0]),	.ADDRB(addr2[13:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[i]),		.DIB(din2[i]),
                       .ENA(ena & ~addr[14]),	.ENB(ce2 & ~addr2[14]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
    RAMB16_S1_S1  ramH(.DOA(doutH[i]),		.DOB(), 
                       .ADDRA(addr[13:0]),	.ADDRB(addr2[13:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[i]),		.DIB(din2[i]),
                       .ENA(ena & addr[14]),	.ENB(ce2 & addr2[14]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
  end
  endgenerate
  
endmodule // mem_rp32_inst

module mem_rp64_inst(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [15:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [15:0]		addr2,
  input [`IFACE-1:0]	din2
);
  wire [`IFACE-1:0] doutH, doutL;
  wire ena = strobe & ce & ~ack;
  
  initial begin
    ack <= 1'b0;
  end

  assign dout = addr[15] ? doutH : doutL;

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < `IFACE; i = i + 1) begin: blk_
    RAMB36 #(.READ_WIDTH_A(1), .READ_WIDTH_B(1),
             .WRITE_WIDTH_A(1), .WRITE_WIDTH_B(1))
                  ramL(.DOA(doutL[i]),		.DOB(), 
                       .ADDRA(addr[14:0]),	.ADDRB(addr2[14:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[i]),		.DIB(din2[i]),
                       .ENA(ena & ~addr[15]),	.ENB(ce2 & ~addr2[15]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{~rd}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(1), .READ_WIDTH_B(1),
             .WRITE_WIDTH_A(1), .WRITE_WIDTH_B(1))
                  ramH(.DOA(doutH[i]),		.DOB(), 
                       .ADDRA(addr[14:0]),	.ADDRB(addr2[15:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[i]),		.DIB(din2[i]),
                       .ENA(ena & addr[15]),	.ENB(ce2 & addr2[15]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{~rd}}),		.WEB(4'b1111));
  end
  endgenerate
  
endmodule // mem_rp64_inst

module mem_rp64_inf(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [15:0]		addr,
  input [`IFACE-1:0]	din,
  output reg [`IFACE-1:0] dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [15:0]		addr2,
  input [`IFACE-1:0]	din2
);
  wire ena = strobe & ce & ~ack;
  reg [31:0] ram [65535:0];
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) begin
      if (~rd)
        ram[addr] <= din;
      dout <= ram[addr];
    end
  end

  always @(posedge clk2)
    if (ce2)
      ram[addr2] <= din2;
endmodule // mem_rp64_inf

module mem_rp64w_inf(
  // Local bus port
  input 		clk,
  input			ce,
  input			we,
  input [15:0]		addr,
  input [35:0]		din,
  output reg [35:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [15:0]		addr2,
  input [35:0]		din2
);
  reg [35:0] ram [65535:0];
  
  always @(posedge clk) begin
    if (ce) begin
      if (we)
        ram[addr] <= din;
    end
    dout <= ram[addr];
  end

  always @(posedge clk2)
    if (ce2)
      ram[addr2] <= din2;
endmodule // mem_rp64w_inf

module mem_rp64w_inst(
  // Local bus port
  input 		clk,
  input			ce,
  input			we,
  input [15:0]		addr,
  input [35:0]		din,
  output [35:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [15:0]		addr2,
  input [35:0]		din2
);
  wire [35:0] busX[15:0];
  wire [15:0] decce, decce2;
  
  reg [3:0] addr_reg;
  
  assign dout = busX[addr_reg];
    
  always @(posedge clk)
    addr_reg <= addr[15:12];
  
  generate
  genvar i;
  for (i = 0; i < 16; i = i + 1) begin: blk_
    assign decce[i] = ce & (addr[15:12] == i);
    assign decce2[i] = ce2 & (addr2[15:12] == i);
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram0(.DOA(busX[i][7:0]),	.DOB(),
                       .DOPA(busX[i][8]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[7:0]),		.DIB(din2[7:0]),
                       .DIPA(din[8]),		.DIPB(din2[8]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram1(.DOA(busX[i][16:9]),	.DOB(),
                       .DOPA(busX[i][17]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[16:9]),		.DIB(din2[16:9]),
                       .DIPA(din[17]),		.DIPB(din2[17]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram2(.DOA(busX[i][25:18]),	.DOB(),
                       .DOPA(busX[i][26]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[25:18]),	.DIB(din2[25:18]),
                       .DIPA(din[26]),		.DIPB(din2[26]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram3(.DOA(busX[i][34:27]),	.DOB(),
                       .DOPA(busX[i][35]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[34:27]),	.DIB(din2[34:27]),
                       .DIPA(din[35]),		.DIPB(din2[35]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
  end
  endgenerate
  
endmodule // mem_rp64w_inst

// Parameterized BlockRAM module, 36 bits wide on each port
module mem_recw_inst #(parameter memsize=65536, awidth=16) (
  // Local bus port
  input		clk,
  input		ce,
  input		we,
  input [awidth-1:0] addr,
  input [35:0]	din,
  output [35:0] dout,
  
  // Second port
  input		clk2,
  input		ce2,
  input [awidth-1:0] addr2,
  input [35:0]	din2
);
  parameter blocks  = memsize/4096;
  
  wire [35:0] busX[blocks-1:0];
  wire [blocks-1:0] decce, decce2;
  wire [awidth-1:12] blocksel = addr[awidth-1:12];
  wire [awidth-1:12] blocksel2 = addr2[awidth-1:12];
  
  reg [awidth-1:12] addr_reg;
  
  assign dout = busX[addr_reg];
    
  always @(posedge clk)
    addr_reg <= blocksel;
  
  generate
  genvar i;
  for (i = 0; i < blocks; i = i + 1) begin: blk_
    assign decce[i] = ce & (blocksel == i);
    assign decce2[i] = ce2 & (blocksel2 == i);
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram0(.DOA(busX[i][7:0]),	.DOB(),
                       .DOPA(busX[i][8]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[7:0]),		.DIB(din2[7:0]),
                       .DIPA(din[8]),		.DIPB(din2[8]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram1(.DOA(busX[i][16:9]),	.DOB(),
                       .DOPA(busX[i][17]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[16:9]),		.DIB(din2[16:9]),
                       .DIPA(din[17]),		.DIPB(din2[17]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram2(.DOA(busX[i][25:18]),	.DOB(),
                       .DOPA(busX[i][26]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[25:18]),	.DIB(din2[25:18]),
                       .DIPA(din[26]),		.DIPB(din2[26]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram3(.DOA(busX[i][34:27]),	.DOB(),
                       .DOPA(busX[i][35]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2[11:0], 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[34:27]),	.DIB(din2[34:27]),
                       .DIPA(din[35]),		.DIPB(din2[35]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
  end
  endgenerate
  
endmodule // mem_recw_inst

// Parameterized BlockRAM module, 36 bits wide on each port
// ce2 and addr2 is pipelined to make timing easier
module mem_recwp_inst #(parameter memsize=65536, awidth=16) (
  // Local bus port
  input		clk,
  input		ce,
  input		we,
  input [awidth-1:0] addr,
  input [35:0]	din,
  output [35:0] dout,
  
  // Second port
  input		clk2,
  input		ce2,
  input [awidth-1:0] addr2,
  input [35:0]	din2
);
  parameter blocks  = memsize/4096;
  
  wire [35:0] busX[blocks-1:0];
  wire [blocks-1:0] decce;
  reg [blocks-1:0] decce2;
  wire [awidth-1:12] blocksel = addr[awidth-1:12];
  wire [awidth-1:12] blocksel2 = addr2[awidth-1:12];
  
  reg [awidth-1:12] addr_reg;
  reg [11:0] addr2p;
  
  assign dout = busX[addr_reg];
    
  always @(posedge clk)
    addr_reg <= blocksel;

  always @(posedge clk2)
    addr2p <= addr2[11:0];
  
  generate
  genvar i;
  for (i = 0; i < blocks; i = i + 1) begin: blk_
    assign decce[i] = ce & (blocksel == i);
    always @(posedge clk2)
      decce2[i] <= ce2 & (blocksel2 == i); // Pipeline decoding!!!
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram0(.DOA(busX[i][7:0]),	.DOB(),
                       .DOPA(busX[i][8]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2p, 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[7:0]),		.DIB(din2[7:0]),
                       .DIPA(din[8]),		.DIPB(din2[8]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram1(.DOA(busX[i][16:9]),	.DOB(),
                       .DOPA(busX[i][17]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2p, 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[16:9]),		.DIB(din2[16:9]),
                       .DIPA(din[17]),		.DIPB(din2[17]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram2(.DOA(busX[i][25:18]),	.DOB(),
                       .DOPA(busX[i][26]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2p, 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[25:18]),	.DIB(din2[25:18]),
                       .DIPA(din[26]),		.DIPB(din2[26]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
    RAMB36 #(.READ_WIDTH_A(9), .READ_WIDTH_B(9),
             .WRITE_WIDTH_A(9), .WRITE_WIDTH_B(9))
                  ram3(.DOA(busX[i][34:27]),	.DOB(),
                       .DOPA(busX[i][35]),	.DOPB(),
                       .ADDRA({1'b0, addr[11:0], 3'b0}),.ADDRB({1'b0, addr2p, 3'b0}),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[34:27]),	.DIB(din2[34:27]),
                       .DIPA(din[35]),		.DIPB(din2[35]),
                       .ENA(decce[i]),		.ENB(decce2[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA({4{we}}),		.WEB(4'b1111));
  end
  endgenerate
  
endmodule // mem_recwp_inst

module mem_rp32_inf(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [14:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [14:0]		addr2,
  input [`IFACE-1:0]	din2
);
  reg [14:0] read_addr;
  reg [15:0] ram1 [32767:0];
  reg [15:0] ram2 [32767:0];
  wire ena = strobe & ce & ~ack;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  always @(posedge clk2)
    if (ce2) begin
      ram1[addr2] <= din2[15:0];
      ram2[addr2] <= din2[31:16];
    end

  always @(posedge clk)
    read_addr <= addr;

  assign dout = {ram2[read_addr], ram1[read_addr]};

//  dpram memblk(.clka(clk), 	.clkb(clk2), 
//               .ena(ena),  	.enb(ce2),
//               .wea(~rd),	.web(1'b1),
//               .addra(addr),	.addrb(addr2),
//               .dia(din),	.dib(din2),
//               .doa(dout),	.dob());
  
endmodule // mem_rp32_inf

module dpram(
  input clka,
  input clkb,
  input ena,
  input enb,
  input wea,
  input web,
  input [14:0] addra,
  input [14:0] addrb,
  input [31:0] dia,
  input [31:0] dib,
  output reg [31:0] doa,
  output reg [31:0] dob
);
  reg [31:0] ram [32767:0];

  always @(posedge clka) begin
    if (ena) begin
      if (wea)
        ram[addra] <= dia;
      doa <= ram[addra];
    end
  end

  always @(posedge clkb) begin
    if (enb) begin
      if (web)
        ram[addrb] <= dib;
      dob <= ram[addrb];
    end
  end

endmodule // dpram

module mem_rp16_inst(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [13:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [13:0]		addr2,
  input [`IFACE-1:0]	din2
);
  wire ena = strobe & ce & ~ack;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < `IFACE; i = i + 1) begin: blk_
    RAMB16_S1_S1 ram(.DOA(dout[i]),		.DOB(), 
                     .ADDRA(addr),		.ADDRB(addr2),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i]),		.DIB(din2[i]),
                     .ENA(ena),			.ENB(ce2),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
  end
  endgenerate
  
endmodule // mem_rp16_inst

module mem_rp8_inst(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [12:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [12:0]		addr2,
  input [`IFACE-1:0]	din2
);
  wire ena = strobe & ce & ~ack;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < `IFACE; i = i + 2) begin: blk_
    RAMB16_S2_S2 ram(.DOA(dout[i+1:i]),		.DOB(), 
                     .ADDRA(addr),		.ADDRB(addr2),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+1:i]),		.DIB(din2[i+1:i]),
                     .ENA(ena),			.ENB(ce2),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
  end
  endgenerate
  
endmodule // mem_rp8_inst

/* Two blocks of RAM, each is 24 bits wide, using 18 2048x8 RAMS */
module mem_rp8A_inst(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [13:0]		addr,
  input [23:0]		din,
  output reg [23:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [12:0]		addr2,
  input [47:0]		din2
);
  wire en = strobe & ce & ~ack;
  wire [5:0] ena;
  wire [2:0] ce2a;
  wire [23:0] dout0, dout1, dout2, dout3, dout4, dout5;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  assign ena[0] = en & (addr[13:11] == 3'b000);
  assign ena[1] = en & (addr[13:11] == 3'b001);
  assign ena[2] = en & (addr[13:11] == 3'b010);
  assign ena[3] = en & (addr[13:11] == 3'b011);
  assign ena[4] = en & (addr[13:11] == 3'b100);
  assign ena[5] = en & (addr[13:11] == 3'b101);

  assign ce2a[0] = ce2 & (addr2[12:11] == 2'b00);
  assign ce2a[1] = ce2 & (addr2[12:11] == 2'b01);
  assign ce2a[2] = ce2 & (addr2[12:11] == 2'b10);

  // Output data mux
  always @(dout or dout0 or dout1 or dout2 or dout3 or dout4 or dout5 or addr[13:11])
    case (addr[13:11])
      3'b000: dout <= dout0;
      5'b001: dout <= dout1;
      5'b010: dout <= dout2;
      5'b011: dout <= dout3;
      5'b100: dout <= dout4;
      default: dout <= dout5;
    endcase
    
  generate
  genvar i;
  for (i = 0; i < 24; i = i + 8) begin: blk_
    RAMB16_S9_S9 ramLL(.DOA(dout0[i+7:i]),	.DOB(), 
                     .ADDRA(addr[10:0]),	.ADDRB(addr2[10:0]),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+7:i]),		.DIB(din2[i+7:i]),
                     .DIPA(1'b0),		.DIPB(1'b0),
                     .ENA(ena[0]),		.ENB(ce2a[0]),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
    RAMB16_S9_S9 ramLM(.DOA(dout1[i+7:i]),	.DOB(), 
                     .ADDRA(addr[10:0]),	.ADDRB(addr2[10:0]),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+7:i]),		.DIB(din2[i+7:i]),
                     .DIPA(1'b0),		.DIPB(1'b0),
                     .ENA(ena[1]),		.ENB(ce2a[1]),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
    RAMB16_S9_S9 ramLH(.DOA(dout2[i+7:i]),	.DOB(), 
                     .ADDRA(addr[10:0]),	.ADDRB(addr2[10:0]),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+7:i]),		.DIB(din2[i+7:i]),
                     .DIPA(1'b0),		.DIPB(1'b0),
                     .ENA(ena[2]),		.ENB(ce2a[2]),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
    RAMB16_S9_S9 ramHL(.DOA(dout3[i+7:i]),	.DOB(), 
                     .ADDRA(addr[10:0]),	.ADDRB(addr2[10:0]),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+7:i]),		.DIB(din2[i+31:i+24]),
                     .DIPA(1'b0),		.DIPB(1'b0),
                     .ENA(ena[3]),		.ENB(ce2a[0]),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
    RAMB16_S9_S9 ramHM(.DOA(dout4[i+7:i]),	.DOB(), 
                     .ADDRA(addr[10:0]),	.ADDRB(addr2[10:0]),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+7:i]),		.DIB(din2[i+31:i+24]),
                     .DIPA(1'b0),		.DIPB(1'b0),
                     .ENA(ena[4]),		.ENB(ce2a[1]),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
    RAMB16_S9_S9 ramHH(.DOA(dout5[i+7:i]),	.DOB(), 
                     .ADDRA(addr[10:0]),	.ADDRB(addr2[10:0]),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+7:i]),		.DIB(din2[i+31:i+24]),
                     .DIPA(1'b0),		.DIPB(1'b0),
                     .ENA(ena[5]),		.ENB(ce2a[2]),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
  end
  endgenerate
  
endmodule // mem_rp8A_inst

module mem_rp4_inst(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [11:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [11:0]		addr2,
  input [`IFACE-1:0]	din2
);
  wire ena = strobe & ce & ~ack;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < `IFACE; i = i + 4) begin: blk_
    RAMB16_S4_S4 ram(.DOA(dout[i+3:i]),		.DOB(), 
                     .ADDRA(addr),		.ADDRB(addr2),
                     .CLKA(clk),		.CLKB(clk2),
                     .DIA(din[i+3:i]),		.DIB(din2[i+3:i]),
                     .ENA(ena),			.ENB(ce2),
                     .SSRA(1'b0),		.SSRB(1'b0),
                     .WEA(~rd),			.WEB(1'b1));
  end
  endgenerate
  
endmodule // mem_rp4_inst

// A weird memory: x18 on the local bus, x36 on the fast side.
// 24 blockrams for a total of 48 kSamples
// synthesis attribute shreg_extract of mem_play48 is false
module mem_play48(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [14:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0]   dout,
  
  // Second port
  input			clk2,
  input [13:0]		addr2,
  output reg [35:0]	play
);
  wire [17:0] dout0, dout1, dout2, dout3, dout4, dout5;
  wire [17:0] dout6, dout7, dout8, dout9, doutA, doutB;
  wire [17:0] doutC, doutD, doutE, doutF, doutG, doutH;
  wire [17:0] doutI, doutJ, doutK, doutL, doutM, doutN;

  wire [35:0] dout20, dout21, dout22, dout23, dout24, dout25;
  wire [35:0] dout26, dout27, dout28, dout29, dout2A, dout2B;
  wire [35:0] dout2C, dout2D, dout2E, dout2F, dout2G, dout2H;
  wire [35:0] dout2I, dout2J, dout2K, dout2L, dout2M, dout2N;

  reg [35:0] dout20x, dout21x, dout22x, dout23x, dout24x, dout25x;
  reg [35:0] dout26x, dout27x, dout28x, dout29x, dout2Ax, dout2Bx;
  reg [35:0] dout2Cx, dout2Dx, dout2Ex, dout2Fx, dout2Gx, dout2Hx;
  reg [35:0] dout2Ix, dout2Jx, dout2Kx, dout2Lx, dout2Mx, dout2Nx;
  reg [4:0] addr2x, addr2y;
  
  // Bit-swapped vectors for proper bunch shaping
  wire [17:0] dinX;
  reg [17:0] doutX;
  
  wire [23:0] sel;

  wire ena = strobe & ce & ~ack;
  
  reg [35:0] play0;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end
  
  // Block selects
  assign sel[0]  = addr[14:10] == 5'b00000;
  assign sel[1]  = addr[14:10] == 5'b00001;
  assign sel[2]  = addr[14:10] == 5'b00010;
  assign sel[3]  = addr[14:10] == 5'b00011;
  assign sel[4]  = addr[14:10] == 5'b00100;
  assign sel[5]  = addr[14:10] == 5'b00101;
  assign sel[6]  = addr[14:10] == 5'b00110;
  assign sel[7] =  addr[14:10] == 5'b00111;
  assign sel[8]  = addr[14:10] == 5'b01000;
  assign sel[9]  = addr[14:10] == 5'b01001;
  assign sel[10] = addr[14:10] == 5'b01010;
  assign sel[11] = addr[14:10] == 5'b01011;
  assign sel[12] = addr[14:10] == 5'b01100;
  assign sel[13] = addr[14:10] == 5'b01101;
  assign sel[14] = addr[14:10] == 5'b01110;
  assign sel[15] = addr[14:10] == 5'b01111;
  assign sel[16] = addr[14:10] == 5'b10000;
  assign sel[17] = addr[14:10] == 5'b10001;
  assign sel[18] = addr[14:10] == 5'b10010;
  assign sel[19] = addr[14:10] == 5'b10011;
  assign sel[20] = addr[14:10] == 5'b10100;
  assign sel[21] = addr[14:10] == 5'b10101;
  assign sel[22] = addr[14:10] == 5'b10110;
  assign sel[23] = addr[14:10] == 5'b10111;
  
  always @(posedge clk2) begin
    dout20x <= dout20; dout21x <= dout21; dout22x <= dout22;
    dout23x <= dout23; dout24x <= dout24; dout25x <= dout25;
    dout26x <= dout26; dout27x <= dout27; dout28x <= dout28;
    dout29x <= dout29; dout2Ax <= dout2A; dout2Bx <= dout2B;
    dout2Cx <= dout2C; dout2Dx <= dout2D; dout2Ex <= dout2E;
    dout2Fx <= dout2F; dout2Gx <= dout2G; dout2Hx <= dout2H;
    dout2Ix <= dout2I; dout2Jx <= dout2J; dout2Kx <= dout2K;
    dout2Lx <= dout2L; dout2Mx <= dout2M; dout2Nx <= dout2N;
    addr2x <= addr2[13:9];
    addr2y <= addr2x;
    case (addr2y)
      5'b00000: play0 <= dout20x;
      5'b00001: play0 <= dout21x;
      5'b00010: play0 <= dout22x;
      5'b00011: play0 <= dout23x;
      5'b00100: play0 <= dout24x;
      5'b00101: play0 <= dout25x;
      5'b00110: play0 <= dout26x;
      5'b00111: play0 <= dout27x;
      5'b01000: play0 <= dout28x;
      5'b01001: play0 <= dout29x;
      5'b01010: play0 <= dout2Ax;
      5'b01011: play0 <= dout2Bx;
      5'b01100: play0 <= dout2Cx;
      5'b01101: play0 <= dout2Dx;
      5'b01110: play0 <= dout2Ex;
      5'b01111: play0 <= dout2Fx;
      5'b10000: play0 <= dout2Gx;
      5'b10001: play0 <= dout2Hx;
      5'b10010: play0 <= dout2Ix;
      5'b10011: play0 <= dout2Jx;
      5'b10100: play0 <= dout2Kx;
      5'b10101: play0 <= dout2Lx;
      5'b10110: play0 <= dout2Mx;
      5'b10111: play0 <= dout2Nx;
    endcase
    play <= {play0[35], play0[31:24], play0[34], play0[23:16], play0[33],
             play0[15:8], play0[32], play0[7:0]};
  end
    
  always @(addr[14:10] or dout0 or dout1 or dout2 or dout3 or dout4 or dout5
                       or dout6 or dout7 or dout8 or dout9 or doutA or doutB
                       or doutC or doutD or doutE or doutF or doutG or doutH                       or dout6 or dout7 or dout8 or dout9 or doutA or doutB                       or dout6 or dout7 or dout8 or dout9 or doutA or doutB         
                       or doutI or doutJ or doutK or doutL or doutM or doutN)
    case (addr[14:10])
      5'b00000: doutX <= dout0;
      5'b00001: doutX <= dout1;
      5'b00010: doutX <= dout2;
      5'b00011: doutX <= dout3;
      5'b00100: doutX <= dout4;
      5'b00101: doutX <= dout5;
      5'b00110: doutX <= dout6;
      5'b00111: doutX <= dout7;
      5'b01000: doutX <= dout8;
      5'b01001: doutX <= dout9;
      5'b01010: doutX <= doutA;
      5'b01011: doutX <= doutB;
      5'b01100: doutX <= doutC;
      5'b01101: doutX <= doutD;
      5'b01110: doutX <= doutE;
      5'b01111: doutX <= doutF;
      5'b10000: doutX <= doutG;
      5'b10001: doutX <= doutH;
      5'b10010: doutX <= doutI;
      5'b10011: doutX <= doutJ;
      5'b10100: doutX <= doutK;
      5'b10101: doutX <= doutL;
      5'b10110: doutX <= doutM;
      5'b10111: doutX <= doutN;
      default: doutX <= `IFACE'b0;
    endcase

  // This swapping helps keep the memory layout, as seen
  // from the local bus, sane.
  assign dout = {doutX[17], doutX[15:8], 3'b0, doutX[16], doutX[7:0]};
  assign dinX = {din[20], din[8], din[19:12], din[7:0]};                                
         
  RAMB16_S18_S36 ram0(.DOA(dout0[15:0]),	.DOB(dout20[31:0]),
                      .DOPA(dout0[17:16]),	.DOPB(dout20[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[0]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram1(.DOA(dout1[15:0]),	.DOB(dout21[31:0]),
                      .DOPA(dout1[17:16]),	.DOPB(dout21[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[1]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram2(.DOA(dout2[15:0]),	.DOB(dout22[31:0]),
                      .DOPA(dout2[17:16]),	.DOPB(dout22[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[2]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram3(.DOA(dout3[15:0]),	.DOB(dout23[31:0]),
                      .DOPA(dout3[17:16]),	.DOPB(dout23[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[3]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram4(.DOA(dout4[15:0]),	.DOB(dout24[31:0]),
                      .DOPA(dout4[17:16]),	.DOPB(dout24[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[4]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram5(.DOA(dout5[15:0]),	.DOB(dout25[31:0]),
                      .DOPA(dout5[17:16]),	.DOPB(dout25[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[5]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram6(.DOA(dout6[15:0]),	.DOB(dout26[31:0]),
                      .DOPA(dout6[17:16]),	.DOPB(dout26[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[6]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram7(.DOA(dout7[15:0]),	.DOB(dout27[31:0]),
                      .DOPA(dout7[17:16]),	.DOPB(dout27[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[7]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram8(.DOA(dout8[15:0]),	.DOB(dout28[31:0]),
                      .DOPA(dout8[17:16]),	.DOPB(dout28[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[8]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram9(.DOA(dout9[15:0]),	.DOB(dout29[31:0]),
                      .DOPA(dout9[17:16]),	.DOPB(dout29[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[9]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramA(.DOA(doutA[15:0]),	.DOB(dout2A[31:0]),
                      .DOPA(doutA[17:16]),	.DOPB(dout2A[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[10]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramB(.DOA(doutB[15:0]),	.DOB(dout2B[31:0]),
                      .DOPA(doutB[17:16]),	.DOPB(dout2B[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[11]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramC(.DOA(doutC[15:0]),	.DOB(dout2C[31:0]),
                      .DOPA(doutC[17:16]),	.DOPB(dout2C[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[12]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramD(.DOA(doutD[15:0]),	.DOB(dout2D[31:0]),
                      .DOPA(doutD[17:16]),	.DOPB(dout2D[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[13]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramE(.DOA(doutE[15:0]),	.DOB(dout2E[31:0]),
                      .DOPA(doutE[17:16]),	.DOPB(dout2E[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[14]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramF(.DOA(doutF[15:0]),	.DOB(dout2F[31:0]),
                      .DOPA(doutF[17:16]),	.DOPB(dout2F[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[15]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramG(.DOA(doutG[15:0]),	.DOB(dout2G[31:0]),
                      .DOPA(doutG[17:16]),	.DOPB(dout2G[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[16]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramH(.DOA(doutH[15:0]),	.DOB(dout2H[31:0]),
                      .DOPA(doutH[17:16]),	.DOPB(dout2H[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[17]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramI(.DOA(doutI[15:0]),	.DOB(dout2I[31:0]),
                      .DOPA(doutI[17:16]),	.DOPB(dout2I[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[18]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramJ(.DOA(doutJ[15:0]),	.DOB(dout2J[31:0]),
                      .DOPA(doutJ[17:16]),	.DOPB(dout2J[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[19]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramK(.DOA(doutK[15:0]),	.DOB(dout2K[31:0]),
                      .DOPA(doutK[17:16]),	.DOPB(dout2K[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[20]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramL(.DOA(doutL[15:0]),	.DOB(dout2L[31:0]),
                      .DOPA(doutL[17:16]),	.DOPB(dout2L[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[21]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramM(.DOA(doutM[15:0]),	.DOB(dout2M[31:0]),
                      .DOPA(doutM[17:16]),	.DOPB(dout2M[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[22]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramN(.DOA(doutN[15:0]),	.DOB(dout2N[31:0]),
                      .DOPA(doutN[17:16]),	.DOPB(dout2N[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[23]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  
endmodule // mem_play48

// A weird memory: x18 on the local bus, x36 on the fast side.
// 12 blockrams for a total of 24 kSamples
// synthesis attribute shreg_extract of mem_play24 is false
module mem_play24(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [14:0]		addr,
  input [`IFACE-1:0]	din,
  output [`IFACE-1:0]   dout,
  
  // Second port
  input			clk2,
  input [13:0]		addr2,
  output reg [35:0]	play
);
  wire [17:0] dout0, dout1, dout2, dout3, dout4, dout5;
  wire [17:0] dout6, dout7, dout8, dout9, doutA, doutB;

  wire [35:0] dout20, dout21, dout22, dout23, dout24, dout25;
  wire [35:0] dout26, dout27, dout28, dout29, dout2A, dout2B;

  reg [35:0] dout20x, dout21x, dout22x, dout23x, dout24x, dout25x;
  reg [35:0] dout26x, dout27x, dout28x, dout29x, dout2Ax, dout2Bx;
  reg [4:0] addr2x, addr2y;
  
  // Bit-swapped vectors for proper bunch shaping
  wire [17:0] dinX;
  reg [17:0] doutX;
  
  wire [11:0] sel;

  wire ena = strobe & ce & ~ack;
  
  reg [35:0] play0;
  
  initial begin
    ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end
  
  // Block selects
  assign sel[0]  = addr[14:10] == 5'b00000;
  assign sel[1]  = addr[14:10] == 5'b00001;
  assign sel[2]  = addr[14:10] == 5'b00010;
  assign sel[3]  = addr[14:10] == 5'b00011;
  assign sel[4]  = addr[14:10] == 5'b00100;
  assign sel[5]  = addr[14:10] == 5'b00101;
  assign sel[6]  = addr[14:10] == 5'b00110;
  assign sel[7] =  addr[14:10] == 5'b00111;
  assign sel[8]  = addr[14:10] == 5'b01000;
  assign sel[9]  = addr[14:10] == 5'b01001;
  assign sel[10] = addr[14:10] == 5'b01010;
  assign sel[11] = addr[14:10] == 5'b01011;
  
  always @(posedge clk2) begin
    dout20x <= dout20; dout21x <= dout21; dout22x <= dout22;
    dout23x <= dout23; dout24x <= dout24; dout25x <= dout25;
    dout26x <= dout26; dout27x <= dout27; dout28x <= dout28;
    dout29x <= dout29; dout2Ax <= dout2A; dout2Bx <= dout2B;
    addr2x <= addr2[13:9];
    addr2y <= addr2x;
    case (addr2y)
      5'b00000: play0 <= dout20x;
      5'b00001: play0 <= dout21x;
      5'b00010: play0 <= dout22x;
      5'b00011: play0 <= dout23x;
      5'b00100: play0 <= dout24x;
      5'b00101: play0 <= dout25x;
      5'b00110: play0 <= dout26x;
      5'b00111: play0 <= dout27x;
      5'b01000: play0 <= dout28x;
      5'b01001: play0 <= dout29x;
      5'b01010: play0 <= dout2Ax;
      5'b01011: play0 <= dout2Bx;
    endcase
    play <= {play0[35], play0[31:24], play0[34], play0[23:16], play0[33],
             play0[15:8], play0[32], play0[7:0]};
  end
    
  always @(addr[14:10] or dout0 or dout1 or dout2 or dout3 or dout4 or dout5
                       or dout6 or dout7 or dout8 or dout9 or doutA or doutB)
    case (addr[14:10])
      5'b00000: doutX <= dout0;
      5'b00001: doutX <= dout1;
      5'b00010: doutX <= dout2;
      5'b00011: doutX <= dout3;
      5'b00100: doutX <= dout4;
      5'b00101: doutX <= dout5;
      5'b00110: doutX <= dout6;
      5'b00111: doutX <= dout7;
      5'b01000: doutX <= dout8;
      5'b01001: doutX <= dout9;
      5'b01010: doutX <= doutA;
      5'b01011: doutX <= doutB;
      default: doutX <= `IFACE'b0;
    endcase

  // This swapping helps keep the memory layout, as seen
  // from the local bus, sane.
  assign dout = {doutX[17], doutX[15:8], 3'b0, doutX[16], doutX[7:0]};
  assign dinX = {din[20], din[8], din[19:12], din[7:0]};                                
         
  RAMB16_S18_S36 ram0(.DOA(dout0[15:0]),	.DOB(dout20[31:0]),
                      .DOPA(dout0[17:16]),	.DOPB(dout20[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[0]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram1(.DOA(dout1[15:0]),	.DOB(dout21[31:0]),
                      .DOPA(dout1[17:16]),	.DOPB(dout21[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[1]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram2(.DOA(dout2[15:0]),	.DOB(dout22[31:0]),
                      .DOPA(dout2[17:16]),	.DOPB(dout22[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[2]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram3(.DOA(dout3[15:0]),	.DOB(dout23[31:0]),
                      .DOPA(dout3[17:16]),	.DOPB(dout23[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[3]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram4(.DOA(dout4[15:0]),	.DOB(dout24[31:0]),
                      .DOPA(dout4[17:16]),	.DOPB(dout24[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[4]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram5(.DOA(dout5[15:0]),	.DOB(dout25[31:0]),
                      .DOPA(dout5[17:16]),	.DOPB(dout25[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[5]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram6(.DOA(dout6[15:0]),	.DOB(dout26[31:0]),
                      .DOPA(dout6[17:16]),	.DOPB(dout26[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[6]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram7(.DOA(dout7[15:0]),	.DOB(dout27[31:0]),
                      .DOPA(dout7[17:16]),	.DOPB(dout27[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[7]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram8(.DOA(dout8[15:0]),	.DOB(dout28[31:0]),
                      .DOPA(dout8[17:16]),	.DOPB(dout28[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[8]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ram9(.DOA(dout9[15:0]),	.DOB(dout29[31:0]),
                      .DOPA(dout9[17:16]),	.DOPB(dout29[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[9]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramA(.DOA(doutA[15:0]),	.DOB(dout2A[31:0]),
                      .DOPA(doutA[17:16]),	.DOPB(dout2A[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[10]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  RAMB16_S18_S36 ramB(.DOA(doutB[15:0]),	.DOB(dout2B[31:0]),
                      .DOPA(doutB[17:16]),	.DOPB(dout2B[35:32]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2[8:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(dinX[15:0]),	.DIB(),
                       .DIPA(dinX[17:16]),	.DIPB(),
                       .ENA(ena & sel[11]),	.ENB(1'b1),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b0));
  
endmodule // mem_play24

module mem_play2 (
  input 	clk,
  input 	strobe,
  input 	ce,
  input 	rd,
  output reg	ack,
  input [9:0]	addr,
  input [`IFACE-1:0] din,
  output [`IFACE-1:0] dout,
                                                                
  // Second read port - use to drive stuff out
  input 	clk2,
  input	[9:0]	addr2,
  output [31:0]	play
);
  reg [31:0] ram [1023:0];
  reg [9:0] read_addr, read_addr2;

  assign wea = strobe & ce & ~ack & ~rd;

  always @(posedge clk) begin
    if (strobe & ce & ~ack)
        ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  always @(posedge clk) begin
    if (wea)
      ram[addr] <= din;
    read_addr <= addr;
  end
  
  always @(posedge clk2)
    read_addr2 <= addr2;
                                                                                                                                                                                      
  assign dout = ram[read_addr];
  assign play = ram[read_addr2];
endmodule // mem_play2

module mem_rp20x18(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [14:0]		addr,
  input [17:0]		din,
  output signed [31:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [14:0]		addr2,
  input [17:0]		din2
);
  parameter rams = 24;

  initial begin
    ack <= 1'b0;
  end

  wire en = strobe & ce & ~ack;
  wire [rams-1:0] ena;
  reg [rams-1:0] ce2w = {rams{1'b0}};
  reg [17:0] din2_pipe = 18'b0;
  reg [9:0] addr2_pipe;
  wire signed [17:0] doutw[rams-1:0];
  
  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < rams; i = i + 1) begin: blk_
    assign ena[i]  = en & (addr[14:10] == i);
    always @(posedge clk2)
      ce2w[i]    <= ce2 & (addr2[14:10] == i);
    RAMB16_S18_S18 ram3(.DOA(doutw[i][15:0]),	.DOB(),
                      .DOPA(doutw[i][17:16]),	.DOPB(),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2_pipe),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[15:0]),		.DIB(din2_pipe[15:0]),
                       .DIPA(din[17:16]),	.DIPB(din2_pipe[17:16]),
                       .ENA(ena[i]),		.ENB(ce2w[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
  end
  endgenerate
  
  always @(posedge clk2) begin
    din2_pipe  <= din2;
    addr2_pipe <= addr2[9:0];
  end

  
  assign dout = doutw[addr[14:10]];
endmodule // mem_rp20x18

// 3kx58 on the fast side, 9kx18 +4kx4 on the local bus
module mem_rp3x58(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [13:0]		addr,
  input [21:0]		din,
  output [21:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [11:0]		addr2,
  input [57:0]		din2
);
  initial begin
    ack <= 1'b0;
  end

  wire en = strobe & ce & ~ack;
  wire [9:0] ena;
  reg ce2_pipe;
  reg [2:0] ce2w = 3'b0;
  reg [57:0] din2_pipe = 58'b0;
  reg [11:0] addr2_pipe;
  wire [17:0] doutw[8:0];
  
  always @(posedge clk) begin
    if (en) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < 3; i = i + 1) begin: blk_
    assign ena[3*i]    = en & (addr[13:10] == 3*i);
    assign ena[3*i+1]  = en & (addr[13:10] == 3*i+1);
    assign ena[3*i+2]  = en & (addr[13:10] == 3*i+2);
    always @(posedge clk2)
      ce2w[i]    <= ce2 & (addr2[11:10] == i);
    RAMB16_S18_S18 ram0(.DOA(doutw[3*i][15:0]),	.DOB(),
                      .DOPA(doutw[3*i][17:16]),	.DOPB(),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2_pipe[9:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[15:0]),		.DIB(din2_pipe[15:0]),
                       .DIPA(din[17:16]),	.DIPB(din2_pipe[17:16]),
                       .ENA(ena[3*i]),		.ENB(ce2w[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
    RAMB16_S18_S18 ram1(.DOA(doutw[3*i+1][15:0]), .DOB(),
                      .DOPA(doutw[3*i+1][17:16]), .DOPB(),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2_pipe[9:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[15:0]),		.DIB(din2_pipe[33:18]),
                       .DIPA(din[17:16]),	.DIPB(din2_pipe[35:34]),
                       .ENA(ena[3*i+1]),	.ENB(ce2w[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
    RAMB16_S18_S18 ram2(.DOA(doutw[3*i+2][15:0]), .DOB(),
                      .DOPA(doutw[3*i+2][17:16]), .DOPB(),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2_pipe[9:0]),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[15:0]),		.DIB(din2_pipe[51:36]),
                       .DIPA(din[17:16]),	.DIPB(din2_pipe[53:52]),
                       .ENA(ena[3*i+2]),	.ENB(ce2w[i]),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
  end
  endgenerate
  
  RAMB16_S4_S4 ramx(.DOA(dout[21:18]),		.DOB(),
                       .ADDRA(addr[11:0]),	.ADDRB(addr2_pipe),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[21:18]),	.DIB(din2_pipe[57:54]),
                       .ENA(en & (addr[13:12]==2'b0)), .ENB(ce2_pipe),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(1'b1));
  
  always @(posedge clk2) begin
    din2_pipe  <= din2;
    addr2_pipe <= addr2;
    ce2_pipe   <= ce2;
  end

  
  assign dout[17:0] = doutw[addr[13:10]];
endmodule // mem_rp3x58

// 2kx90 on the fast side, 6kx27 + 2kx9 on the local bus
module mem_rp2x90(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [12:0]		addr,
  input [26:0]		din,
  output [26:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input [10:0]		addr2,
  input [89:0]		din2
);
  initial begin
    ack <= 1'b0;
  end

  wire en = strobe & ce & ~ack;
  wire [9:0] ena;
  reg ce2_pipe;
  reg [89:0] din2_pipe = 90'b0;
  reg [10:0] addr2_pipe;
  wire [26:0] doutw[3:0];
  
  always @(posedge clk) begin
    if (en) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < 3; i = i + 1) begin: blk_
    assign ena[i]    = en & (addr[12:11] == i);
    RAMB16_S9_S9 ram0(.DOA(doutw[i][7:0]),	.DOB(),
                      .DOPA(doutw[i][8]),	.DOPB(),
                      .ADDRA(addr[10:0]),	.ADDRB(addr2_pipe),
                      .CLKA(clk),		.CLKB(clk2),
                      .DIA(din[7:0]),		.DIB(din2_pipe[27*i+7:27*i]),
                      .DIPA(din[8]),		.DIPB(din2_pipe[27*i+8]),
                      .ENA(ena[i]),		.ENB(ce2_pipe),
                      .SSRA(1'b0),		.SSRB(1'b0),
                      .WEA(~rd),		.WEB(1'b1));
    RAMB16_S9_S9 ram1(.DOA(doutw[i][16:9]),	.DOB(),
                      .DOPA(doutw[i][17]),	.DOPB(),
                      .ADDRA(addr[10:0]),	.ADDRB(addr2_pipe),
                      .CLKA(clk),		.CLKB(clk2),
                      .DIA(din[16:9]),		.DIB(din2_pipe[27*i+16:27*i+9]),
                      .DIPA(din[17]),		.DIPB(din2_pipe[27*i+17]),
                      .ENA(ena[i]),		.ENB(ce2_pipe),
                      .SSRA(1'b0),		.SSRB(1'b0),
                      .WEA(~rd),		.WEB(1'b1));
    RAMB16_S9_S9 ram2(.DOA(doutw[i][25:18]),	.DOB(),
                      .DOPA(doutw[i][26]),	.DOPB(),
                      .ADDRA(addr[10:0]),	.ADDRB(addr2_pipe),
                      .CLKA(clk),		.CLKB(clk2),
                      .DIA(din[25:18]),		.DIB(din2_pipe[27*i+25:27*i+18]),
                      .DIPA(din[26]),		.DIPB(din2_pipe[27*i+26]),
                      .ENA(ena[i]),		.ENB(ce2_pipe),
                      .SSRA(1'b0),		.SSRB(1'b0),
                      .WEA(~rd),		.WEB(1'b1));
  end
  endgenerate

  // RAM #10, outside generate
  assign doutw[3][26:9] = 18'b0;
  RAMB16_S9_S9 ram10(.DOA(doutw[3][7:0]),	.DOB(),
                    .DOPA(doutw[3][8]),		.DOPB(),
                    .ADDRA(addr[10:0]),		.ADDRB(addr2_pipe),
                    .CLKA(clk),			.CLKB(clk2),
                    .DIA(din[7:0]),		.DIB(din2_pipe[88:81]),
                    .DIPA(din[8]),		.DIPB(din2_pipe[89]),
                    .ENA(en & addr[12] & addr[11]), .ENB(ce2_pipe),
                    .SSRA(1'b0),		.SSRB(1'b0),
                    .WEA(~rd),			.WEB(1'b1));
  
  always @(posedge clk2) begin
    din2_pipe  <= din2;
    addr2_pipe <= addr2;
    ce2_pipe   <= ce2;
  end

  
  assign dout = doutw[addr[12:11]];
endmodule // mem_rp2x90

// Memory: 1kx54 on the fast side, 3kx18 on the local bus
module mem_rp1x54(
  // Local bus port
  input 		clk,
  input			strobe,
  input			ce,
  input			rd,
  output reg		ack,
  input [11:0]		addr,
  input [17:0]		din,
  output [17:0] 	dout,
  
  // Second port
  input			clk2,
  input			ce2,
  input			rd2,
  input [9:0]		addr2,
  input [53:0]		din2,
  output [53:0]		dout2
);
  parameter rams = 3;

  initial begin
    ack <= 1'b0;
  end

  wire en = strobe & ce & ~ack;
  wire [rams-1:0] ena;
  wire signed [17:0] doutw[rams-1:0];
  
  always @(posedge clk) begin
    if (ena) ack <= 1'b1;
    if (ack) ack <= 1'b0;
  end

  generate
  genvar i;
  for (i = 0; i < rams; i = i + 1) begin: blk_
    assign ena[i]  = en & (addr[11:10] == i);
    RAMB16_S18_S18 ram3(.DOA(doutw[i][15:0]),	.DOB(dout2[i*18+15:i*18]),
                      .DOPA(doutw[i][17:16]),	.DOPB(dout2[i*18+17:i*18+16]),
                       .ADDRA(addr[9:0]),	.ADDRB(addr2),
                       .CLKA(clk),		.CLKB(clk2),
                       .DIA(din[15:0]),		.DIB(din2[i*18+15:i*18]),
                       .DIPA(din[17:16]),	.DIPB(din2[i*18+17:i*18+16]),
                       .ENA(ena[i]),		.ENB(ce2),
                       .SSRA(1'b0),		.SSRB(1'b0),
                       .WEA(~rd),		.WEB(~rd2));
  end
  endgenerate
  
  assign dout = doutw[addr[11:10]];
endmodule // mem_rp1x54
