module DRAM_MOD ( MCLK, RST_N, IC_ACC, IDRAM_ADR, DC_ACC, DC_WR, DRAM_ADR, DRAM_DI,
				  IC_MDONE, DC_MDONE, ENWR, IC_INHIBIT, DC_INHIBIT, MEM_Q );

	input			MCLK;
	input			RST_N;
	input			IC_ACC;
	input	[28:0]	IDRAM_ADR;
	input			DC_ACC;	
	input			DC_WR;
	input	[28:0]	DRAM_ADR;
	input	[35:0]	DRAM_DI;

	output	reg		IC_MDONE;	
	output	reg		DC_MDONE;	
	output			ENWR;
	output	reg		IC_INHIBIT;
	output	reg  	DC_INHIBIT;

	output	reg	[127:0]	MEM_Q;

// +++++++++++++++++++ Memories ++++++++++++++++++++

    parameter addr_msb = 16;    // total memory is 128 kB

    reg      [7:0]  EDRAM_F [0:2**(addr_msb-3)-1];  // Byte-wide RAM blocks
    reg      [7:0]  EDRAM_E [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_D [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_C [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_B [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_A [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_9 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_8 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_7 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_6 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_5 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_4 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_3 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_2 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_1 [0:2**(addr_msb-3)-1];
    reg      [7:0]  EDRAM_0 [0:2**(addr_msb-3)-1];

    wire    [15:0]  wrbyte;
    wire [addr_msb:4]   addr;
    wire            dc_active;

// +++++++++++++++++++++++++  Datapath  +++++++++++++++++++

    assign dc_active = DC_WR | (DC_ACC & ~DC_MDONE);

    assign addr = dc_active ? DRAM_ADR[addr_msb:4] : IDRAM_ADR[addr_msb:4];

    always @(posedge MCLK)
        begin
            MEM_Q[127:120] <= EDRAM_F[addr];    // READ on rising edge
            MEM_Q[119:112] <= EDRAM_E[addr];
            MEM_Q[111:104] <= EDRAM_D[addr];
            MEM_Q[103:96]  <= EDRAM_C[addr];
            MEM_Q[95:88]   <= EDRAM_B[addr];
            MEM_Q[87:80]   <= EDRAM_A[addr];
            MEM_Q[79:72]   <= EDRAM_9[addr];
            MEM_Q[71:64]   <= EDRAM_8[addr];
            MEM_Q[63:56]   <= EDRAM_7[addr];
            MEM_Q[55:48]   <= EDRAM_6[addr];
            MEM_Q[47:40]   <= EDRAM_5[addr];
            MEM_Q[39:32]   <= EDRAM_4[addr];
            MEM_Q[31:24]   <= EDRAM_3[addr];
            MEM_Q[23:16]   <= EDRAM_2[addr];
            MEM_Q[15:8]    <= EDRAM_1[addr];
            MEM_Q[7:0]     <= EDRAM_0[addr];
        end

    assign wrbyte[0]  = DC_WR & DRAM_DI[32] & (DRAM_ADR[3:2] == 2'b00);
    assign wrbyte[1]  = DC_WR & DRAM_DI[33] & (DRAM_ADR[3:2] == 2'b00);
    assign wrbyte[2]  = DC_WR & DRAM_DI[34] & (DRAM_ADR[3:2] == 2'b00);
    assign wrbyte[3]  = DC_WR & DRAM_DI[35] & (DRAM_ADR[3:2] == 2'b00);
    assign wrbyte[4]  = DC_WR & DRAM_DI[32] & (DRAM_ADR[3:2] == 2'b01);
    assign wrbyte[5]  = DC_WR & DRAM_DI[33] & (DRAM_ADR[3:2] == 2'b01);
    assign wrbyte[6]  = DC_WR & DRAM_DI[34] & (DRAM_ADR[3:2] == 2'b01);
    assign wrbyte[7]  = DC_WR & DRAM_DI[35] & (DRAM_ADR[3:2] == 2'b01);
    assign wrbyte[8]  = DC_WR & DRAM_DI[32] & (DRAM_ADR[3:2] == 2'b10);
    assign wrbyte[9]  = DC_WR & DRAM_DI[33] & (DRAM_ADR[3:2] == 2'b10);
    assign wrbyte[10] = DC_WR & DRAM_DI[34] & (DRAM_ADR[3:2] == 2'b10);
    assign wrbyte[11] = DC_WR & DRAM_DI[35] & (DRAM_ADR[3:2] == 2'b10);
    assign wrbyte[12] = DC_WR & DRAM_DI[32] & (DRAM_ADR[3:2] == 2'b11);
    assign wrbyte[13] = DC_WR & DRAM_DI[33] & (DRAM_ADR[3:2] == 2'b11);
    assign wrbyte[14] = DC_WR & DRAM_DI[34] & (DRAM_ADR[3:2] == 2'b11);
    assign wrbyte[15] = DC_WR & DRAM_DI[35] & (DRAM_ADR[3:2] == 2'b11);

    always @(posedge MCLK)
        begin
            if (wrbyte[15]) EDRAM_F[addr] <= DRAM_DI[31:24];    // WRITE on rising edge
            if (wrbyte[14]) EDRAM_E[addr] <= DRAM_DI[23:16];
            if (wrbyte[13]) EDRAM_D[addr] <= DRAM_DI[15:8];
            if (wrbyte[12]) EDRAM_C[addr] <= DRAM_DI[7:0];
            if (wrbyte[11]) EDRAM_B[addr] <= DRAM_DI[31:24];    // WRITE on rising edge
            if (wrbyte[10]) EDRAM_A[addr] <= DRAM_DI[23:16];
            if (wrbyte[9])  EDRAM_9[addr] <= DRAM_DI[15:8];
            if (wrbyte[8])  EDRAM_8[addr] <= DRAM_DI[7:0];
            if (wrbyte[7])  EDRAM_7[addr] <= DRAM_DI[31:24];    // WRITE on rising edge
            if (wrbyte[6])  EDRAM_6[addr] <= DRAM_DI[23:16];
            if (wrbyte[5])  EDRAM_5[addr] <= DRAM_DI[15:8];
            if (wrbyte[4])  EDRAM_4[addr] <= DRAM_DI[7:0];
            if (wrbyte[3])  EDRAM_3[addr] <= DRAM_DI[31:24];    // WRITE on rising edge
            if (wrbyte[2])  EDRAM_2[addr] <= DRAM_DI[23:16];
            if (wrbyte[1])  EDRAM_1[addr] <= DRAM_DI[15:8];
            if (wrbyte[0])  EDRAM_0[addr] <= DRAM_DI[7:0];
        end

// +++++++++++++++++++++++++  Controllogic  +++++++++++++++++++

	// each access takes one clock cycle inside the DRAM model. Due to the timing requirement of xx_MDONE the read
	// access is pipelined: it will take two clock cycles to serve a cache read request
	
	always @(posedge MCLK) DC_MDONE <= DC_ACC & ~DC_MDONE & ~DC_WR;
	
	always @(posedge MCLK) IC_MDONE <= IC_ACC & ~IC_MDONE & ~dc_active;
	
	always @(posedge MCLK) DC_INHIBIT <= ~DRAM_ADR[1];	// ~USE_CA => INHIBIT
	
	always @(posedge MCLK) IC_INHIBIT <= ~IDRAM_ADR[1];	// ~USE_CA => INHIBIT

	assign ENWR = 1'b1;	// always active
	
endmodule