//-----------------------------------------------------------------------------
// ISO14443-A support for the Proxmark III
// Gerhard de Koning Gans, April 2008
//-----------------------------------------------------------------------------

module hi_iso14443a(
    pck0, ck_1356meg, ck_1356megb,
    pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4,
    adc_d, adc_clk,
    ssp_frame, ssp_din, ssp_dout, ssp_clk,
    cross_hi, cross_lo,
    dbg,
    mod_type
);
    input pck0, ck_1356meg, ck_1356megb;
    output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4;
    input [7:0] adc_d;
    output adc_clk;
    input ssp_dout;
    output ssp_frame, ssp_din, ssp_clk;
    input cross_hi, cross_lo;
    output dbg;
    input [2:0] mod_type;

reg ssp_clk;
reg ssp_frame;

reg fc_div_2;
always @(posedge ck_1356meg)
    fc_div_2 = ~fc_div_2;

wire adc_clk;
assign adc_clk = ck_1356meg;

reg after_hysteresis, after_hysteresis_prev1, after_hysteresis_prev2, after_hysteresis_prev3;
reg [11:0] has_been_low_for;
reg [8:0] saw_deep_modulation;
reg [2:0] deep_counter;
reg deep_modulation;
always @(negedge adc_clk)
begin
	if(& adc_d[7:6]) after_hysteresis <= 1'b1;
    else if(~(| adc_d[7:4])) after_hysteresis <= 1'b0;
	
	if(~(| adc_d[7:0]))
	begin
		if(deep_counter == 3'd7)
		begin
			deep_modulation <= 1'b1;
			saw_deep_modulation <= 8'd0;
		end
		else
			deep_counter <= deep_counter + 1;
	end
	else
	begin
		deep_counter <= 3'd0;
		if(saw_deep_modulation == 8'd255)
			deep_modulation <= 1'b0;
		else
			saw_deep_modulation <= saw_deep_modulation + 1;
	end
	
	if(after_hysteresis)
    begin
        has_been_low_for <= 7'b0;
    end
    else
    begin
        if(has_been_low_for == 12'd4095)
        begin
            has_been_low_for <= 12'd0;
            after_hysteresis <= 1'b1;
        end
        else
            has_been_low_for <= has_been_low_for + 1;
    end
end

// Report every 4 subcarrier cycles
// 64 periods of carrier frequency => 6-bit counter [negedge_cnt]
reg [5:0] negedge_cnt;
reg bit1, bit2, bit3;
reg [3:0] count_ones;
reg [3:0] count_zeros;
wire [7:0] avg;
reg [7:0] lavg;
reg signed [12:0] step1;
reg signed [12:0] step2;
reg [7:0] stepsize;
reg curbit;
reg [12:0] average;
wire signed [9:0] dif;

// A register to send the results to the arm
reg signed [7:0] to_arm;

assign avg[7:0] = average[11:4];
assign dif = lavg - avg;

reg bit_to_arm;
reg fdt_indicator, fdt_elapsed;
reg [10:0] fdt_counter;
reg [47:0] mod_sig_buf;
wire mod_sig_buf_empty;
reg [5:0] mod_sig_ptr;
reg [3:0] mod_sig_flip;
reg mod_sig, mod_sig_coil;
reg temp_buffer_reset;
reg sendbit;

assign mod_sig_buf_empty = ~(|mod_sig_buf[47:0]);
reg [2:0] ssp_frame_counter;

// ADC data appears on the rising edge, so sample it on the falling edge
always @(negedge adc_clk)
begin

	// last bit = 0 then fdt = 1172, in case of 0x26 (7-bit command, LSB first!)
	// last bit = 1 then fdt = 1236, in case of 0x52 (7-bit command, LSB first!)
	if(fdt_counter == 11'd740) fdt_indicator = 1'b1;
	
	if(fdt_counter == 11'd1148)
	begin
		if(fdt_elapsed)
		begin
			if(negedge_cnt[3:0] == mod_sig_flip[3:0]) mod_sig_coil <= mod_sig;
		end
		else
		begin
			mod_sig_flip[3:0] <= negedge_cnt[3:0];
			mod_sig_coil <= mod_sig;
			fdt_elapsed = 1'b1;
			fdt_indicator = 1'b0;

			if(~(| mod_sig_ptr[5:0])) mod_sig_ptr <= 6'b001001;
			else temp_buffer_reset = 1'b1; // fix position of the buffer pointer
		end
	end
	else
	begin
		fdt_counter <= fdt_counter + 1;
	end
	
	if(& negedge_cnt[3:0])
	begin
		// When there is a dip in the signal and not in reader mode
		if(~after_hysteresis && mod_sig_buf_empty && ~((mod_type == 3'b100) || (mod_type == 3'b011) || (mod_type == 3'b010))) // last condition to prevent reset
		begin
			fdt_counter <= 11'd0;
			fdt_elapsed = 1'b0;
			fdt_indicator = 1'b0;
			temp_buffer_reset = 1'b0;
			mod_sig_ptr <= 6'b000000;
		end
		
		lavg <= avg;
		
		if(stepsize<16) stepsize = 8'd16;

		if(dif>0)
		begin
			step1 = dif*3;
			step2 = stepsize*2; // 3:2
			if(step1>step2)
			begin
				curbit = 1'b0;
				stepsize = dif;
			end
		end
		else
		begin
			step1 = dif*3;
			step1 = -step1;
			step2 = stepsize*2;
			if(step1>step2)
			begin
				curbit = 1'b1;
				stepsize = -dif;
			end
		end
		
		if(curbit)
		begin
			count_zeros <= 4'd0;
			if(& count_ones[3:2])
			begin
				curbit = 1'b0; // suppressed signal
				stepsize = 8'd24; // just a fine number
			end
			else
			begin
				count_ones <= count_ones + 1;
			end
		end
		else
		begin
			count_ones <= 4'd0;
			if(& count_zeros[3:0])
			begin
				stepsize = 8'd24;
			end
			else
			begin
				count_zeros <= count_zeros + 1;
			end
		end
		
		// What do we communicate to the ARM
		if(mod_type == 3'b001) sendbit = after_hysteresis;
		else if(mod_type == 3'b010)
		begin
			if(fdt_counter > 11'd772) sendbit = mod_sig_coil;
			else sendbit = fdt_indicator;
		end
		else if(mod_type == 3'b011) sendbit = curbit;
		else sendbit = 1'b0;

	end

	if(~(| negedge_cnt[3:0])) average <= adc_d;
	else average <= average + adc_d;

	if(negedge_cnt == 7'd63)
    begin
		if(deep_modulation)
		begin
			to_arm <= {after_hysteresis_prev1,after_hysteresis_prev2,after_hysteresis_prev3,after_hysteresis,1'b0,1'b0,1'b0,1'b0};
		end
		else
		begin
			to_arm <= {after_hysteresis_prev1,after_hysteresis_prev2,after_hysteresis_prev3,after_hysteresis,bit1,bit2,bit3,curbit};
		end

        negedge_cnt <= 0;
	
		end
    else
    begin
        negedge_cnt <= negedge_cnt + 1;
    end

    if(negedge_cnt == 6'd15)
	begin
        after_hysteresis_prev1 <= after_hysteresis;
		bit1 <= curbit;
	end
    if(negedge_cnt == 6'd31)
	begin
        after_hysteresis_prev2 <= after_hysteresis;
		bit2 <= curbit;
	end
    if(negedge_cnt == 6'd47)
	begin
        after_hysteresis_prev3 <= after_hysteresis;
		bit3 <= curbit;
	end
	

	if(mod_type != 3'b000)
	begin
		if(negedge_cnt[3:0] == 4'b1000)
		begin
			// The modulation signal of the tag
			mod_sig_buf[47:0] <= {mod_sig_buf[46:1], ssp_dout, 1'b0};
			if((ssp_dout || (| mod_sig_ptr[5:0])) && ~fdt_elapsed)
				if(mod_sig_ptr == 6'b101110)
				begin
					mod_sig_ptr <= 6'b000000;
				end
				else mod_sig_ptr <= mod_sig_ptr + 1;
			else if(fdt_elapsed && ~temp_buffer_reset)
			begin
				if(ssp_dout) temp_buffer_reset = 1'b1;
				if(mod_sig_ptr == 6'b000010) mod_sig_ptr <= 6'b001001;
				else mod_sig_ptr <= mod_sig_ptr - 1;
			end
			else
			begin
				// side effect: when ptr = 1 it will cancel the first 1 of every block of ones
				if(~mod_sig_buf[mod_sig_ptr-1] && ~mod_sig_buf[mod_sig_ptr+1]) mod_sig = 1'b0;
				else mod_sig = mod_sig_buf[mod_sig_ptr] & fdt_elapsed; // & fdt_elapsed  was for direct relay to oe4
			end
		end
	end
	
	// SSP Clock and data
	if(mod_type == 3'b000)
	begin
		if(negedge_cnt[2:0] == 3'b100)
			ssp_clk <= 1'b0;
			
		if(negedge_cnt[2:0] == 3'b000)
		begin
			ssp_clk <= 1'b1;
			// Don't shift if we just loaded new data, obviously.
			if(negedge_cnt != 7'd0)
			begin
				to_arm[7:1] <= to_arm[6:0];
			end
		end

		if(negedge_cnt[5:4] == 2'b00)
			ssp_frame = 1'b1;
		else
			ssp_frame = 1'b0;
		
		bit_to_arm = to_arm[7];
	end
	else
	begin
		if(negedge_cnt[3:0] == 4'b1000) ssp_clk <= 1'b0;

		if(negedge_cnt[3:0] == 4'b0111)
		begin
			if(ssp_frame_counter == 3'd7) ssp_frame_counter <= 3'd0;
			else ssp_frame_counter <= ssp_frame_counter + 1;
		end

		if(negedge_cnt[3:0] == 4'b0000)
		begin
			ssp_clk <= 1'b1;
		end
		
		ssp_frame = (ssp_frame_counter == 3'd7);
	
		bit_to_arm = sendbit;
	end
	
end

assign ssp_din = bit_to_arm;

// Modulating carrier frequency is fc/16
wire modulating_carrier;
assign modulating_carrier = (mod_sig_coil & negedge_cnt[3] & (mod_type == 3'b010));
assign pwr_hi = (ck_1356megb & (((mod_type == 3'b100) & ~mod_sig_coil) || (mod_type == 3'b011)));

// This one is all LF, so doesn't matter
//assign pwr_oe2 = modulating_carrier;
assign pwr_oe2 = 1'b0;

// Toggle only one of these, since we are already producing much deeper
// modulation than a real tag would.
//assign pwr_oe1 = modulating_carrier;
assign pwr_oe1 = 1'b0;
assign pwr_oe4 = modulating_carrier;
//assign pwr_oe4 = 1'b0;

// This one is always on, so that we can watch the carrier.
//assign pwr_oe3 = modulating_carrier;
assign pwr_oe3 = 1'b0;


assign dbg = negedge_cnt[3];

// Unused.
assign pwr_lo = 1'b0;

endmodule