module ksa(clk, rst_n, en, rdy, key, addr, rddata, wrdata, wren);

	input logic clk, rst_n, en;
	input logic [7:0] rddata;
	input logic [23:0] key;
	output logic rdy, wren;
	output logic [7:0] addr, wrdata;

	logic started, wren_next;
	logic [2:0] state, state_next;
	logic [7:0] i, j, i_next, j_next, wrdata_next, addr_next, si, si_next;

	always_comb begin: ksa_logic
		state_next = state + 1;
		i_next = i;
		j_next = j;
		addr_next = i;
		wrdata_next = 8'b0;
		wren_next = 1'b0;
		si_next = si;
		case (state)
			/* Fetch s[i]. */
			3'd0: begin end
			/* s[i] is now in rddata. */
			3'd1: begin end
			/* Compute j; fetch s[j]; latch s[i] from rddata. */
			3'd2: begin
				if (i % 3 == 2'd0)
					j_next = j + rddata + key[23:16];
				else if (i % 3 == 2'd1)
					j_next = j + rddata + key[15:8];
				else
					j_next = j + rddata + key[7:0];
				addr_next = j_next;
				si_next = rddata;
			end
			/* s[j] is now in rddata. */
			3'd3: begin end
			/* Store rddata (s[j]) to address i. */
			3'd4: begin
				wrdata_next = rddata;
				wren_next = 1'b1;
			end
			/* Store si (s[i]) to address j; increment i; reset. */
			3'd5: begin
				state_next = 3'd0;
				i_next = i + 1;
				addr_next = j;
				wrdata_next = si;
				wren_next = 1'b1;
			end
		endcase
	end: ksa_logic

	always_ff @(posedge clk) begin: ksa_state_machine
		if (~rst_n) begin
			rdy <= 1'b1;
			started <= 1'b0;
			state <= 3'd0;
			i <= 8'b0;
			j <= 8'b0;
			addr <= 8'b0;
		end else if (rdy && en) begin
			rdy <= 1'b0;
			started <= 1'b1;
			state <= 3'd0;
			i <= 8'b0;
			j <= 8'b0;
			addr <= 8'b0;
			wren <= 1'b0;
		end else if (started) begin
			state <= state_next;
			i <= i_next;
			j <= j_next;
			addr <= addr_next;
			wrdata <= wrdata_next;
			wren <= wren_next;
			si <= si_next;
			if ((i == 8'd255) && (state == 3'd5)) begin
				rdy <= 1'b1;
				started <= 1'b0;
			end
		end
	end: ksa_state_machine

endmodule: ksa
