`default_nettype none

// Read-Only Memory Adapter
//
// This core is used to interface an SPI Flash ROM to the
// TileLink TL/UL interconnect.
//
// This core is not in any way, shape, or form intended to be a particularly
// fast core.  EVERY memory access to the ROM will result in a 96 clock cycle
// latency, for will always fetch a complete 64-bit word.  The lowest 3 bits
// of the address provided on the A-channel are ignored and assumed to be
// 3'b000.  Thus, byte, half-word, word, and double-word accesses are all
// equally accommodated without any need for decoding a_mask and a_size
// fields.


`include "tilelink.vh"


module roma(
	i_clk,
	i_reset,

	// Slave Link
	//
	//   Channel A
	i_a_opcode,
	i_a_address,
	i_a_source,
	i_a_valid,
	o_a_ready,

	//   Channel D
	o_d_opcode,
	o_d_data,
	o_d_size,
	o_d_source,
	o_d_valid,
	i_d_ready,

	// SPI Link
	o_spi_mosi,
	i_spi_miso,
	o_spi_clk,
	o_spi_cs
);
	input i_clk;
	input i_reset;

	input [2:0] i_a_opcode;
	input [20:0] i_a_address;
	input [1:0] i_a_source;
	input i_a_valid;
	output reg o_a_ready;

	output reg [2:0] o_d_opcode;
	output reg [63:0] o_d_data;
	output reg [1:0] o_d_size;
	output reg [1:0] o_d_source;
	output reg o_d_valid;
	input i_d_ready;

	output reg o_spi_mosi;
	input i_spi_miso;
	output reg o_spi_clk;
	output reg o_spi_cs;

	reg [32:0] send_message;
	reg [6:0] bits_so_far;
	reg getting_data;

	wire is_read_request = (i_a_valid && o_a_ready && (i_a_opcode === `A_OPC_GET));
	wire can_start_receiving_data = (bits_so_far == 7'd32);
	wire finished_receiving_data = (bits_so_far == 7'd96);
	wire d_channel_handoff = (o_d_valid && i_d_ready);

	assign o_d_size = `D_SIZ_DWORD;

	assign o_spi_clk = i_clk & o_spi_cs;

	always @(posedge i_clk) begin
		o_a_ready <= o_a_ready;

		if(i_reset) begin
			o_a_ready <= 1;
		end
		else if(is_read_request) begin
			o_a_ready <= 0;
		end
		else if(d_channel_handoff) begin
			o_a_ready <= 1;
		end
	end

	always @(posedge i_clk) begin
		o_d_valid <= o_d_valid;
		o_d_opcode <= o_d_opcode;
		o_d_source <= o_d_source;

		if(i_reset) begin
			o_d_valid <= 0;
			o_d_opcode <= 0;
		end
		else if(finished_receiving_data) begin
			o_d_valid <= 1;
			o_d_opcode <= `D_OPC_GET_DATA;
			o_d_source <= i_a_source;
		end
		else if(d_channel_handoff) begin
			o_d_valid <= 0;
			o_d_opcode <= 0;
		end
	end

	always @(posedge i_clk) begin
		o_spi_cs <= o_spi_cs;

		if(i_reset) begin
			o_spi_cs <= 0;
		end
		else if(is_read_request) begin
			o_spi_cs <= 1;
		end
		else if(finished_receiving_data) begin
			o_spi_cs <= 0;
		end
	end

	always @(posedge i_clk) begin
		send_message <= {send_message[31:0], 1'b0};

		if(is_read_request) begin
			send_message <= {1'b0, 8'h03, 3'h0, i_a_address[20:3], 3'b000};
		end
	end

	assign o_spi_mosi = send_message[32];

	always @(posedge i_clk) begin
		bits_so_far <= bits_so_far;
		getting_data <= getting_data;

		if(i_reset) begin
			bits_so_far <= 97;
			getting_data <= 0;
		end
		else if(is_read_request) begin
			bits_so_far <= 0;
			getting_data <= 0;
		end
		else if(o_spi_cs) begin
			// -1 bias because of up-counter action below.
			if(can_start_receiving_data) begin
				getting_data <= 1;
			end
			else if(finished_receiving_data) begin
				getting_data <= 0;
			end
			bits_so_far <= bits_so_far + 1;
		end
	end

	reg [63:0] rxd;
	always @(posedge i_clk) begin
		rxd <= rxd;

		if(getting_data) begin
			rxd <= {rxd[62:0], i_spi_miso};
		end
	end
	assign o_d_data = {
		rxd[7:0], rxd[15:8], rxd[23:16], rxd[31:24],
		rxd[39:32], rxd[47:40], rxd[55:48], rxd[63:56]
	};
endmodule
