// DeCSS.V

module DeCSS(Clk, Resetn, 
		Keyin, 
		Secin,
		Start,
		Secout,
		Dvalid); 
input Clk, Resetn;
input [7:0] Keyin;
input [7:0] Secin;
input Start;
output [7:0] Secout;
output Dvalid;

// register the outputs
reg    [7:0] Secout;
reg    Dvalid;

// internal state
reg [8:0] t1,t5;
reg [7:0] t2, t4, t6; 
reg [31:0] t3;

reg [15:0] ctr; 
reg [7:0] state; // FSM state



// wire up the CSTable ROMs to appropriate registers
wire [7:0] tab1out, tab2out, tab3out, tab4out, tab5out; 
CSTable_1 tab1(Clk, Resetn, Secin, tab1out); 
CSTable_2 tab2(Clk, Resetn, t2, tab2out); 
CSTable_3 tab3(Clk, Resetn, t1, tab3out); 
CSTable_4 tab4(Clk, Resetn, t6, tab4out); 
CSTable_5 tab5(Clk, Resetn, t4, tab5out); 

parameter STATE_IDLE=0;
parameter STATE_INIT1=1;
parameter STATE_INIT2=2;
parameter STATE_INIT3=3;
parameter STATE_INIT4=4;
parameter STATE_INIT5=5;
parameter STATE_INIT6=6;
parameter STATE_INIT7=7;

parameter STATE_MIXSTATE=8;
parameter STATE_MIX1=13;
parameter STATE_MIX2=9;
parameter STATE_MIX3=10;
parameter STATE_MIX4=11;

parameter STATE_GRIND=12;
parameter STATE_WAITPADDING=14;


parameter MAX_COUNT= 32'h 800; 



always @(posedge Clk or negedge Resetn) begin

if (!Resetn) begin: reset
	t1<=0; 	t2<=0; t3<=0; t4<=0; t5<=0; t6<=0;
	ctr<=0; state<=0; 
	Dvalid<=0; Secout<=0; 	
	end // reset
else casex (state)
	STATE_IDLE: begin
		if (Start) begin 
			t2<=0; t3<=0; t4<=0; t5<=0; t6<=0;
			t1<= Keyin^Secin | 32'h 100;	// Secin is Sec[0x54], Keyin is Key[0]
			ctr<=0; 
			Dvalid<=0; Secout<=0; 	
			state<=STATE_INIT1; 
			$display("starting"); 
			$display("time=", $time); 
			end
		end

	STATE_INIT1: begin 
			t2<= Keyin^Secin;
			state<=STATE_INIT2;
		end 

	STATE_INIT2: begin
			t3[7:0]<= Keyin^Secin;	// start of int access
			t4[3:0]<= Keyin^Secin;	// t4=t3&7;
			state<=STATE_INIT3;
		end 
	STATE_INIT3: begin
			t3[15:8]<= Keyin^Secin;
			state<=STATE_INIT4;
		end 
	STATE_INIT4: begin
			t3[23:16]<= Keyin^Secin;
			state<=STATE_INIT5;
		end 
	STATE_INIT5: begin
			t3[31:24]<= Keyin^Secin;
			state<=STATE_INIT6;
		end 
	STATE_INIT6: begin
			$display("t3=%x", t3);
			t3<= (t3<<1) + 8 - t4; 
			state<=STATE_MIX1; 	// WAITPADDING; 
		end 

	STATE_WAITPADDING: begin: waiting
		if (ctr==19) begin 
				state<=STATE_MIX1; // STATE; 
				ctr <=0; 
				// $display("End of init: %x %x %x %x %x %x\n", t1,t2,t3,t4,t5,t6);	// ok
				end
		else ctr <= ctr+1; 
		$display("waiting...%d", ctr); 
		end


	STATE_MIX1: begin
		// $display("End of init: %x %x %x %x %x %x", t1,t2,t3,t4,t5,t6);	// ok

		t4<= tab2out^tab3out; // driven by t2, t1 respectively
		// $display("t4= %x ^ %x", tab2out, tab3out); 

		t2<= t1>>1;
		t6<= (((((((t3>>3)^t3)>>1)^t3)>>8)^t3)>>5)& 32'h ff;
		state <= STATE_MIX2; 
		Dvalid <=0; 
		end 

	STATE_MIX2: begin
		// $display("t6=%x t3=%x",t6,t3); 

		t1 <= ((t1&1)<<8)^t4;
		t3 <= (t3<<8)|t6;
		state <= STATE_MIX3; 
		end
	
	STATE_MIX3: begin
		t4 <= tab5out; // driven by t4 which settles in prev step

		t6 <= tab4out; 	// driven by t6

		// $display("new t6=%x", t6); 				$display("t4=%x", tab5out); 

		state <= STATE_MIX4; 
		end

	STATE_MIX4: begin
		t5 <= t5 + t6 + t4; 
		state <= STATE_GRIND; 
		end 


	STATE_GRIND: begin
		// $display("grind: in=%x %x %x %x %x %x %x", Secin, t1,t2,t3,t4,t5,t6);
		// $display("time=%d", $time); 
		Secout <= tab1out ^ t5; 
		Dvalid <=1; 
		t5 <= t5>>8; 
		state <= STATE_MIX1; 
		// $display("secin=%d\n", Secin); 
		if (ctr >= MAX_COUNT) begin
			$display("ctr=%d", ctr); 
			state<= STATE_IDLE; 
			end
		else ctr <= ctr+1; 
		end

	
	endcase // main
end // always
endmodule



// eof