module KOLDETECT ( BCLK, BRESET, DRAM_WR, CVALID, ADDR, TAG0, TAG1 , CFG , C_VALID, READ_I, ACC_OK, HOLD, KDET, INVAL_A, ENA_HK,
				   NEWCVAL, KOLLISION, STOP_ICRD, RUN_ICRD, KILL, KILLADR, ICTODC, STOP_CINV );

	input			BCLK;
	input			BRESET;
	input			DRAM_WR;
	input	[23:0]	CVALID;		// Data from master Valid RAM
	input	[28:4]	ADDR;
	input  [28:12]	TAG0,TAG1;
	input	 [1:0]	CFG;
	input	[23:0]	C_VALID;	// Data from secondary Valid RAM
	input			READ_I;
	input			ACC_OK;
	input			HOLD;		// active low
	input			KDET;
	input			INVAL_A;	// Cache Invalidate All
	input			ENA_HK;		// Enable HOLD and Kohaerenz
	
	output	[23:0]	NEWCVAL;
	output			KOLLISION;
	output			STOP_ICRD;
	output			RUN_ICRD;
	output			KILL;
	output	[11:7]	KILLADR;
	output	 [2:0]	ICTODC;
	output			STOP_CINV;
	
	reg		[28:4]	addr_r;
	reg		 [7:0]	clear;
	reg				do_koll;
	reg		 [2:0]	counter;
	reg		 [1:0]	wpointer,rpointer;
	reg		[35:0]	adrfifo;
	reg		 [8:0]	fifo_q,fifo_c;
	reg		 [1:0]	state;
	reg				pipe;
	reg				do_kill;
	reg				dma;

	wire	[7:0]	set_0,set_1;
	wire			match_0,match_1;
	wire			valid_0,valid_1;
	wire			found_0,found_1;
	wire			kolli,dma_kolli;
	wire			last_match;
	wire			wr_entry;
	wire	[23:0]	cdaten;
	wire	 [8:0]	kaddr;
	wire	 [7:0]	new_0,new_1;
	wire			dma_mode,ic_dma;
	wire			free,ende;
	wire			init_b;

	always @(posedge BCLK) do_koll <= DRAM_WR & CFG[0];	// one cycle pulse, without Cache Enable no collision
	always @(posedge BCLK) addr_r <= ADDR;
	
	// similar logic like in CA_MATCH
	
	assign set_0 = C_VALID[7:0];
	assign set_1 = C_VALID[15:8];
		
	assign valid_0 = set_0[addr_r[6:4]];
	assign valid_1 = set_1[addr_r[6:4]];
	
	assign match_0 = ( TAG0 == addr_r[28:12] );	// 4KB
	assign match_1 = ( TAG1 == addr_r[28:12] );	// 4KB
	
	assign found_0 = valid_0 & match_0;
	assign found_1 = valid_1 & match_1;
	
	assign kolli = (found_0 | found_1) & ~CFG[1] & do_koll;	// Action only if ICACHE is not locked

	assign KOLLISION = (found_0 | found_1) & do_koll;	// to Statistik Modul, Register there

	assign dma_kolli = (found_0 | found_1) & ~CFG[1] & CFG[0];
	
	// the FIFO with 4 entries :
	assign init_b = CFG[0] & ~INVAL_A;	// initialise if CINV A too
	
	always @(posedge BCLK)
		if (!init_b) wpointer <= 2'b00;
			else
				wpointer <= wpointer + {1'b0,wr_entry};
				
	always @(posedge BCLK)
		if (!init_b) rpointer <= 2'b00;
			else
				rpointer <= rpointer + {1'b0,do_kill};
	
	always @(posedge BCLK)
	  begin
		if (wr_entry && (wpointer == 2'b00)) adrfifo[8:0]   <= {addr_r[11:4],found_1};
		if (wr_entry && (wpointer == 2'b01)) adrfifo[17:9]  <= {addr_r[11:4],found_1};
		if (wr_entry && (wpointer == 2'b10)) adrfifo[26:18] <= {addr_r[11:4],found_1};
		if (wr_entry && (wpointer == 2'b11)) adrfifo[35:27] <= {addr_r[11:4],found_1};
	  end
		
	always @(adrfifo or rpointer)
		case (rpointer)
		  2'b00 : fifo_q = adrfifo[8:0];
		  2'b01 : fifo_q = adrfifo[17:9];
		  2'b10 : fifo_q = adrfifo[26:18];
		  2'b11 : fifo_q = adrfifo[35:27];
		endcase
		
	always @(adrfifo or wpointer)	// for Match of last entry use wpointer
		case (wpointer)
		  2'b01 : fifo_c = adrfifo[8:0];
		  2'b10 : fifo_c = adrfifo[17:9];
		  2'b11 : fifo_c = adrfifo[26:18];
		  2'b00 : fifo_c = adrfifo[35:27];
		endcase
		
	// Control
	
	assign last_match = counter[2] & (fifo_c == {addr_r[11:4],found_1});	// if Match with last Entry no new Entry

	assign wr_entry = kolli & ~last_match;
	
	always @(posedge BCLK)
		casex ({init_b,wr_entry,do_kill,counter})
		  6'b0_xx_xxx : counter <= 3'b000;
		  6'b1_00_xxx : counter <= counter;
		  6'b1_11_xxx : counter <= counter;
		  6'b1_10_000 : counter <= 3'b100;
		  6'b1_10_1xx : counter <= (counter[1:0] == 2'b11) ? 3'b111 : {counter[2],(counter[1:0] + 2'b01)};	// Overflow avoid
		  6'b1_01_1xx : counter <= (counter[1:0] == 2'b00) ? 3'b000 : {counter[2],(counter[1:0] + 2'b11)};
		  default	  : counter <= counter;
		endcase

	// DMA Access
	always @(posedge BCLK) dma <= ~HOLD;	// there is only one FF for this , from here to DCACHE
	
	// Controlling of ICACHE
	
	assign free = (~READ_I | ACC_OK) & ENA_HK;	// switch off if CINV
	
	always @(posedge BCLK)					//  state[1]  state[0]
		casex ({BRESET,dma,counter[2],free,ende,STOP_ICRD,dma_mode})
		  7'b0_xx_xx_xx : state <= 2'b00;
		  7'b1_00_xx_00 : state <= 2'b00;
		  7'b1_01_1x_00 : state <= 2'b10;	// Start of DCACHE Kohaerenz
		  7'b1_1x_1x_00 : state <= 2'b11;	// Start of DMA
	//
		  7'b1_xx_x0_10 : state <= 2'b10;	// without "ende" it stays as is
		  7'b1_0x_x1_10 : state <= 2'b00;	// DMA is not active
		  7'b1_1x_x1_10 : state <= 2'b11;	// to DMA !
	//
		  7'b1_00_xx_11 : state <= 2'b00;
		  7'b1_01_xx_11 : state <= 2'b10;
		  7'b1_1x_xx_11 : state <= 2'b11;
		  default		: state <= 2'b00;
		endcase
	
	assign STOP_ICRD = state[1];	// used for Multiplexer
	assign dma_mode  = state[0];	// internal Multiplexer

	assign STOP_CINV = state[1] & ~ENA_HK;	// stops CINV if DMA access or Kohaerenz access
	
	assign ende = (counter[1:0] == 2'b00) & do_kill;
	
	assign ic_dma = STOP_ICRD & dma_mode;	// Signal to DCACHE that ICACHE has stoped
	
	always @(posedge BCLK) pipe <= STOP_ICRD;
		
	assign RUN_ICRD = ~(STOP_ICRD | pipe);	// Release for IC_READ
	
	always @(posedge BCLK) do_kill <= STOP_ICRD & ~dma_mode & ~do_kill;	// Write pulse in Cache Valid RAM, 1 cycle on, 1 cycle off
	
	assign KILL = do_kill | (KDET & dma_kolli);
	
	// Valid Daten prepare : different sources for DMA and DCACHE Kohaerenz
	
	assign cdaten = dma_mode ? C_VALID : CVALID;
	assign kaddr  = dma_mode ? {addr_r[11:4],found_1} : fifo_q;
	
	assign KILLADR = kaddr[8:4];
	
	always @(kaddr)
		case (kaddr[3:1])
		  3'h0 : clear = 8'hFE;
		  3'h1 : clear = 8'hFD;
		  3'h2 : clear = 8'hFB;
		  3'h3 : clear = 8'hF7;
		  3'h4 : clear = 8'hEF;
		  3'h5 : clear = 8'hDF;
		  3'h6 : clear = 8'hBF;
		  3'h7 : clear = 8'h7F;
		endcase
	
	assign new_0 = kaddr[0] ? cdaten[7:0] : (cdaten[7:0] & clear);
	assign new_1 = kaddr[0] ? (cdaten[15:8] & clear) : cdaten[15:8];

	assign NEWCVAL = {cdaten[23:16],new_1,new_0};
	
	// multiple signals are needed in DCACHE :
	assign ICTODC = {dma,ic_dma,~(counter[2:1] == 2'b11)};
	
endmodule