X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/b811cc51f9a21324dc7589d1254e767374488a20..6658905f18a1eebc148836f26c731dea9c1377dc:/fpga/hi_iso14443a.v diff --git a/fpga/hi_iso14443a.v b/fpga/hi_iso14443a.v new file mode 100644 index 00000000..eb03fa23 --- /dev/null +++ b/fpga/hi_iso14443a.v @@ -0,0 +1,360 @@ +//----------------------------------------------------------------------------- +// 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