module arc4 (
	input logic clk,
	input logic rst_n,
	input logic en,
	output logic rdy,
	input logic [23:0] key,

	output logic [7:0] ct_addr,
	input logic [7:0] ct_rddata,

	output logic [7:0] pt_addr,
	input logic [7:0] pt_rddata,
	output logic [7:0] pt_wrdata,
	output logic pt_wren
);

	logic s_wren;
	logic [7:0] s_addr;
	logic [7:0] s_wrdata, s_rddata;

	logic i_en, i_rdy, i_wren;
	logic [7:0] i_addr;
	logic [7:0] i_wrdata;

	logic k_en, k_rdy, k_wren;
	logic [7:0] k_addr;
	logic [7:0] k_wrdata;

	logic p_en, p_rdy, p_s_wren, p_pt_wren;
	logic [7:0] p_s_addr, p_ct_addr, p_pt_addr;
	logic [7:0] p_s_wrdata, p_pt_wrdata;

	enum logic [2:0] {
		IDLE,
		INIT_BEGIN,
		INIT,
		KSA_BEGIN,
		KSA,
		PRGA_BEGIN,
		PRGA,
		DONE
	} state;

	s_core_mem s (
		.address(s_addr),
		.clock(clk),
		.data(s_wrdata),
		.wren(s_wren),
		.q(s_rddata)
	);

	init i (
		.clk,
		.rst_n,
		.en(i_en),
		.rdy(i_rdy),
		.addr(i_addr),
		.wrdata(i_wrdata),
		.wren(i_wren)
	);

	ksa k (
		.clk,
		.rst_n,
		.en(k_en),
		.rdy(k_rdy),
		.key,
		.addr(k_addr),
		.rddata(s_rddata),
		.wrdata(k_wrdata),
		.wren(k_wren)
	);

	prga p (
		.clk,
		.rst_n,
		.en(p_en),
		.rdy(p_rdy),
		.key,
		.s_addr(p_s_addr),
		.s_rddata,
		.s_wrdata(p_s_wrdata),
		.s_wren(p_s_wren),
		.ct_addr(p_ct_addr),
		.ct_rddata,
		.pt_addr(p_pt_addr),
		.pt_rddata,
		.pt_wrdata(p_pt_wrdata),
		.pt_wren(p_pt_wren)
	);

	always_ff @(posedge clk) begin: fsm
		if (~rst_n) begin
			state <= IDLE;
		end else unique case (state)
			IDLE: if (en) state <= INIT_BEGIN;
			INIT_BEGIN: if (i_rdy) state <= INIT;
			INIT: if (i_rdy) state <= KSA_BEGIN;
			KSA_BEGIN: if (k_rdy) state <= KSA;
			KSA: if (k_rdy) state <= PRGA_BEGIN;
			PRGA_BEGIN: if (p_rdy) state <= PRGA;
			PRGA: if (p_rdy) state <= DONE;
			DONE: state <= DONE;
			default: state <= IDLE;
		endcase
	end: fsm

	always_comb begin: controller
		rdy = '0;

		i_en = '0;
		k_en = '0;
		p_en = '0;

		s_wren = '0;
		s_addr = '0;
		s_wrdata = '0;

		ct_addr = '0;

		pt_wren = '0;
		pt_addr = '0;
		pt_wrdata = '0;

		unique case (state)
			IDLE: rdy = '1;
			INIT_BEGIN: i_en = i_rdy;
			INIT: begin
				s_wren = i_wren;
				s_addr = i_addr;
				s_wrdata = i_wrdata;
			end
			KSA_BEGIN: k_en = k_rdy;
			KSA: begin
				s_wren = k_wren;
				s_addr = k_addr;
				s_wrdata = k_wrdata;
			end
			PRGA_BEGIN: p_en = p_rdy;
			PRGA: begin
				s_wren = p_s_wren;
				s_addr = p_s_addr;
				s_wrdata = p_s_wrdata;
				ct_addr = p_ct_addr;
				pt_wren = p_pt_wren;
				pt_addr = p_pt_addr;
				pt_wrdata = p_pt_wrdata;
			end
			DONE: rdy = '1;
			default: begin end
		endcase
	end: controller

endmodule: arc4
