/*
 * Multi-port ZBT SRAM WISHBONE controller
 * Copyright (C) 2008 Sebastien Bourdeauducq - http://lekernel.net
 * This file is part of Milkymist.
 *
 * Milkymist is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Library General Public License as published
 * by the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */

module wb_zbt(
	input               clk,
	input               rst,
	// Wishbone bus A, highest priority, with prefetch
	input      [31:0]   wbA_adr_i,
	input      [31:0]   wbA_dat_i,
	output     [31:0]   wbA_dat_o,
	input      [ 3:0]   wbA_sel_i,
	input               wbA_cyc_i,
	input               wbA_stb_i,
	output              wbA_ack_o,
	input               wbA_we_i,
	// Wishbone bus B, lower priority
	input      [31:0]   wbB_adr_i,
	input      [31:0]   wbB_dat_i,
	output     [31:0]   wbB_dat_o,
	input      [ 3:0]   wbB_sel_i,
	input               wbB_cyc_i,
	input               wbB_stb_i,
	output              wbB_ack_o,
	input               wbB_we_i,
	// Memory connection
	output              sram_clk,
	output     [17:0]   sram_a,
	inout      [31:0]   sram_d,
	output              sram_we,
	output     [ 3:0]   sram_bw,
	output              sram_adv,
	output              sram_ce,
	output              sram_oe,
	output              sram_mode,
	output              sram_zz
);

assign sram_clk = clk;
assign sram_oe = 1'b0;
assign sram_ce = 1'b0;
assign sram_adv = 1'b0;
assign sram_mode = 1'b0;
assign sram_zz = 1'b0;

/* Wishbone decoding */

wire busA_active;
wire busB_active;

assign busA_active = wbA_cyc_i & wbA_stb_i;
assign busB_active = wbB_cyc_i & wbB_stb_i;

/* Those are used to represent the state of the SRAM pipeline
 * Bit 0 = Write Enable
 * Bits 18..1 = Address
 */
wire [18:0] pipeline_in;
reg [18:0] pipeline_internal;
reg [18:0] pipeline_out;

always @(posedge clk or posedge rst) begin
	if(rst) begin
		pipeline_internal <= 0;
		pipeline_out <= 0;
	end else begin
		pipeline_internal <= pipeline_in;
		pipeline_out <= pipeline_internal;
	end
end

/* Pipeline contents decode */

wire inprogressA;
wire inprogressB;

assign inprogressA = (pipeline_internal[18:1] == wbA_adr_i[19:2]) & (pipeline_internal[0] == wbA_we_i);
assign inprogressB = (pipeline_internal[18:1] == wbB_adr_i[19:2]) & (pipeline_internal[0] == wbB_we_i);

wire hitA;
wire hitB;

assign hitA = (pipeline_out[18:1] == wbA_adr_i[19:2]) & (pipeline_out[0] == wbA_we_i);
assign hitB = (pipeline_out[18:1] == wbB_adr_i[19:2]) & (pipeline_out[0] == wbB_we_i);

/* Access arbitration */

wire [1:0] bus_request;

assign bus_request[0] = busA_active & ~inprogressA & ~hitA;
assign bus_request[1] = busB_active & ~inprogressB & ~hitB;

wire prefetch_enable;
reg [17:0] prefetch_address;

assign prefetch_enable = ~bus_request[0] & ~bus_request[1];
always @(posedge clk) begin
	if(prefetch_enable)
		prefetch_address <= wbA_adr_i[19:2] + 2;
	else
		prefetch_address <= wbA_adr_i[19:2] + 1;
end

wire [1:0] bus_selected;

assign bus_selected[0] = bus_request[0];
assign bus_selected[1] = bus_request[1] & ~bus_request[0];

assign pipeline_in[18:1] = ({18{bus_selected[0]}} & wbA_adr_i[19:2])
	| ({18{bus_selected[1]}} & wbB_adr_i[19:2])
	| ({18{prefetch_enable}} & prefetch_address);
assign pipeline_in[0] = (bus_selected[0] & wbA_we_i)|(bus_selected[1] & wbB_we_i);

/* SRAM control */

assign sram_a = pipeline_in[18:1];
assign sram_bw = ~(({4{bus_selected[0]}} & wbA_sel_i)|({4{bus_selected[1]}} & wbB_sel_i));
assign sram_we = ~pipeline_in[0];

/* SRAM data */

wire [31:0] bus_wdata;

assign wbA_ack_o = busA_active & hitA;
assign wbB_ack_o = busB_active & hitB;

assign bus_wdata = ({32{hitA}} & wbA_dat_i)|({32{hitB}} & wbB_dat_i);
assign sram_d = pipeline_out[0] ? bus_wdata : 32'hzzzzzzzz;
assign wbA_dat_o = sram_d;
assign wbB_dat_o = sram_d;

endmodule