Initial commit for the firmware. Used the 20090306_ela version as baseline.

It is identical to the popular 20081211, with the doob addition (20090301), a
linux client, and two additional commands for LF analysis. Let me know if
you find issues here!
This commit is contained in:
edouard@lafargue.name 2009-04-09 06:43:20 +00:00
commit 6658905f18
91 changed files with 16661 additions and 0 deletions

239
fpga/fpga.mpf Normal file
View file

@ -0,0 +1,239 @@
;
; Copyright Model Technology, a Mentor Graphics
; Corporation company 2003, - All rights reserved.
;
[Library]
std = $MODEL_TECH/../std
ieee = $MODEL_TECH/../ieee
verilog = $MODEL_TECH/../verilog
vital2000 = $MODEL_TECH/../vital2000
std_developerskit = $MODEL_TECH/../std_developerskit
synopsys = $MODEL_TECH/../synopsys
modelsim_lib = $MODEL_TECH/../modelsim_lib
; VHDL Section
unisim = $MODEL_TECH/../xilinx/vhdl/unisim
simprim = $MODEL_TECH/../xilinx/vhdl/simprim
xilinxcorelib = $MODEL_TECH/../xilinx/vhdl/xilinxcorelib
aim = $MODEL_TECH/../xilinx/vhdl/aim
pls = $MODEL_TECH/../xilinx/vhdl/pls
cpld = $MODEL_TECH/../xilinx/vhdl/cpld
; Verilog Section
unisims_ver = $MODEL_TECH/../xilinx/verilog/unisims_ver
uni9000_ver = $MODEL_TECH/../xilinx/verilog/uni9000_ver
simprims_ver = $MODEL_TECH/../xilinx/verilog/simprims_ver
xilinxcorelib_ver = $MODEL_TECH/../xilinx/verilog/xilinxcorelib_ver
aim_ver = $MODEL_TECH/../xilinx/verilog/aim_ver
cpld_ver = $MODEL_TECH/../xilinx/verilog/cpld_ver
work = work
[vcom]
; Turn on VHDL-1993 as the default. Normally is off.
VHDL93 = 1
; Show source line containing error. Default is off.
; Show_source = 1
; Turn off unbound-component warnings. Default is on.
; Show_Warning1 = 0
; Turn off process-without-a-wait-statement warnings. Default is on.
; Show_Warning2 = 0
; Turn off null-range warnings. Default is on.
; Show_Warning3 = 0
; Turn off no-space-in-time-literal warnings. Default is on.
; Show_Warning4 = 0
; Turn off multiple-drivers-on-unresolved-signal warnings. Default is on.
; Show_Warning5 = 0
; Turn off optimization for IEEE std_logic_1164 package. Default is on.
; Optimize_1164 = 0
; Turn on resolving of ambiguous function overloading in favor of the
; "explicit" function declaration (not the one automatically created by
; the compiler for each type declaration). Default is off.
Explicit = 1
; Turn off VITAL compliance checking. Default is checking on.
; NoVitalCheck = 1
; Ignore VITAL compliance checking errors. Default is to not ignore.
; IgnoreVitalErrors = 1
; Turn off VITAL compliance checking warnings. Default is to show warnings.
; Show_VitalChecksWarnings = false
; Turn off "loading..." messages. Default is messages on.
; Quiet = 1
; Turn on some limited synthesis rule compliance checking. Checks only:
; -- signals used (read) by a process must be in the sensitivity list
; CheckSynthesis = 1
[vlog]
; Turn off "loading..." messages. Default is messages on.
; Quiet = 1
; Turn on Verilog hazard checking (order-dependent accessing of global vars).
; Default is off.
; Hazard = 1
; Turn on converting regular Verilog identifiers to uppercase. Allows case
; insensitivity for module names. Default is no conversion.
; UpCase = 1
; Turns on incremental compilation of modules
; Incremental = 1
[vsim]
; Simulator resolution
; Set to fs, ps, ns, us, ms, or sec with optional prefix of 1, 10, or 100.
Resolution = ps
; User time unit for run commands
; Set to default, fs, ps, ns, us, ms, or sec. The default is to use the
; unit specified for Resolution. For example, if Resolution is 100ps,
; then UserTimeUnit defaults to ps.
UserTimeUnit = default
; Default run length
RunLength = 100
; Maximum iterations that can be run without advancing simulation time
IterationLimit = 5000
; Directive to license manager:
; vhdl Immediately reserve a VHDL license
; vlog Immediately reserve a Verilog license
; plus Immediately reserve a VHDL and Verilog license
; nomgc Do not look for Mentor Graphics Licenses
; nomti Do not look for Model Technology Licenses
; noqueue Do not wait in the license queue when a license isn't available
; License = plus
; Stop the simulator after an assertion message
; 0 = Note 1 = Warning 2 = Error 3 = Failure 4 = Fatal
BreakOnAssertion = 3
; Assertion Message Format
; %S - Severity Level
; %R - Report Message
; %T - Time of assertion
; %D - Delta
; %I - Instance or Region pathname (if available)
; %% - print '%' character
; AssertionFormat = "** %S: %R\n Timf: %T Iteration: %D%I\n"
; Assertion File - alternate file for storing assertion messages
; AssertFile = assert.log
; Default radix for all windows and commands...
; Set to symbolic, ascii, binary, octal, decimal, hex, unsigned
DefaultRadix = symbolic
; VSIM Startup command
; Startup = do startup.do
; File for saving command transcript
TranscriptFile = transcript
; File for saving command history
;CommandHistory = cmdhist.log
; Specify whether paths in simulator commands should be described
; in VHDL or Verilog format. For VHDL, PathSeparator = /
; for Verilog, PathSeparator = .
PathSeparator = /
; Specify the dataset separator for fully rooted contexts.
; The default is ':'. For example, sim:/top
; Must not be the same character as PathSeparator.
DatasetSeparator = :
; Disable assertion messages
; IgnoreNote = 1
; IgnoreWarning = 1
; IgnoreError = 1
; IgnoreFailure = 1
; Default force kind. May be freeze, drive, or deposit
; or in other terms, fixed, wired or charged.
; DefaultForceKind = freeze
; If zero, open files when elaborated
; else open files on first read or write
; DelayFileOpen = 0
; Control VHDL files opened for write
; 0 = Buffered, 1 = Unbuffered
UnbufferedOutput = 0
; Control number of VHDL files open concurrently
; This number should always be less then the
; current ulimit setting for max file descriptors
; 0 = unlimited
ConcurrentFileLimit = 40
; This controls the number of hierarchical regions displayed as
; part of a signal name shown in the waveform window. The default
; value or a value of zero tells VSIM to display the full name.
; WaveSignalNameWidth = 0
; Turn off warnings from the std_logic_arith, std_logic_unsigned
; and std_logic_signed packages.
; StdArithNoWarnings = 1
; Turn off warnings from the IEEE numeric_std and numeric_bit
; packages.
; NumericStdNoWarnings = 1
; Control the format of a generate statement label. Don't quote it.
; GenerateFormat = %s__%d
; Specify whether checkpoint files should be compressed.
; The default is to be compressed.
; CheckpointCompressMode = 0
; List of dynamically loaded objects for Verilog PLI applications
; Veriuser = veriuser.sl
[lmc]
[Project]
Project_Version = 5
Project_DefaultLib = work
Project_SortMethod = unused
Project_Files_Count = 13
Project_File_0 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/fpga_tb.v
Project_File_P_0 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 2 dont_compile 0
Project_File_1 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_simulate.v
Project_File_P_1 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225963633 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 compile_to work vlog_upper 0 vlog_options {} compile_order 6 dont_compile 0
Project_File_2 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_hi_simulate.v
Project_File_P_2 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225964050 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 compile_to work vlog_upper 0 vlog_options {} compile_order 12 dont_compile 0
Project_File_3 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/fpga.v
Project_File_P_3 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1207888760 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 1 dont_compile 0
Project_File_4 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_read_tx.v
Project_File_P_4 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225960972 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 5 dont_compile 0
Project_File_5 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_hi_read_tx.v
Project_File_P_5 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225962515 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 11 dont_compile 0
Project_File_6 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_iso14443a.v
Project_File_P_6 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1207889732 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 3 dont_compile 0
Project_File_7 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/lo_simulate.v
Project_File_P_7 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 8 dont_compile 0
Project_File_8 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/lo_read.v
Project_File_P_8 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225797126 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 7 dont_compile 0
Project_File_9 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/util.v
Project_File_P_9 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 0 dont_compile 0
Project_File_10 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_lo_read.v
Project_File_P_10 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225960239 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 9 dont_compile 0
Project_File_11 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/testbed_lo_simulate.v
Project_File_P_11 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1225960231 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 10 dont_compile 0
Project_File_12 = G:/RFID/Hardware/Proxmark3/Sources/prox_work/fpga/hi_read_rx_xcorr.v
Project_File_P_12 = vlog_protect 0 file_type Verilog group_id 0 vlog_1995compat 0 vlog_nodebug 0 folder {Top Level} vlog_noload 0 last_compile 1179836462 vlog_disableopt 0 vlog_hazard 0 vlog_showsource 0 ood 0 vlog_options {} vlog_upper 0 compile_to work compile_order 4 dont_compile 0
Project_Sim_Count = 0
Project_Folder_Count = 0

41
fpga/fpga.ucf Normal file
View file

@ -0,0 +1,41 @@
# See the schematic for the pin assignment.
NET "adc_d<0>" LOC = "P62" ;
NET "adc_d<1>" LOC = "P60" ;
NET "adc_d<2>" LOC = "P58" ;
NET "adc_d<3>" LOC = "P57" ;
NET "adc_d<4>" LOC = "P56" ;
NET "adc_d<5>" LOC = "P55" ;
NET "adc_d<6>" LOC = "P54" ;
NET "adc_d<7>" LOC = "P53" ;
#NET "cross_hi" LOC = "P88" ;
#NET "miso" LOC = "P40" ;
#PACE: Start of Constraints generated by PACE
#PACE: Start of PACE I/O Pin Assignments
NET "adc_clk" LOC = "P46" ;
NET "adc_noe" LOC = "P47" ;
NET "ck_1356meg" LOC = "P91" ;
NET "ck_1356megb" LOC = "P93" ;
NET "cross_lo" LOC = "P87" ;
NET "dbg" LOC = "P22" ;
NET "mosi" LOC = "P43" ;
NET "ncs" LOC = "P44" ;
NET "pck0" LOC = "P36" ;
NET "pwr_hi" LOC = "P80" ;
NET "pwr_lo" LOC = "P81" ;
NET "pwr_oe1" LOC = "P82" ;
NET "pwr_oe2" LOC = "P83" ;
NET "pwr_oe3" LOC = "P84" ;
NET "pwr_oe4" LOC = "P86" ;
NET "spck" LOC = "P39" ;
NET "ssp_clk" LOC = "P71" ;
NET "ssp_din" LOC = "P32" ;
NET "ssp_dout" LOC = "P34" ;
NET "ssp_frame" LOC = "P31" ;
#PACE: Start of PACE Area Constraints
#PACE: Start of PACE Prohibit Constraints
#PACE: End of Constraints generated by PACE

190
fpga/fpga.v Normal file
View file

@ -0,0 +1,190 @@
//-----------------------------------------------------------------------------
// The FPGA is responsible for interfacing between the A/D, the coil drivers,
// and the ARM. In the low-frequency modes it passes the data straight
// through, so that the ARM gets raw A/D samples over the SSP. In the high-
// frequency modes, the FPGA might perform some demodulation first, to
// reduce the amount of data that we must send to the ARM.
//
// I am not really an FPGA/ASIC designer, so I am sure that a lot of this
// could be improved.
//
// Jonathan Westhues, March 2006
// Added ISO14443-A support by Gerhard de Koning Gans, April 2008
//-----------------------------------------------------------------------------
`include "lo_read.v"
`include "lo_simulate.v"
`include "hi_read_tx.v"
`include "hi_read_rx_xcorr.v"
`include "hi_simulate.v"
`include "hi_iso14443a.v"
`include "util.v"
module fpga(
spck, miso, mosi, ncs,
pck0i, ck_1356meg, ck_1356megb,
pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4,
adc_d, adc_clk, adc_noe,
ssp_frame, ssp_din, ssp_dout, ssp_clk,
cross_hi, cross_lo,
dbg
);
input spck, mosi, ncs;
output miso;
input pck0i, 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, adc_noe;
input ssp_dout;
output ssp_frame, ssp_din, ssp_clk;
input cross_hi, cross_lo;
output dbg;
IBUFG #(.IOSTANDARD("DEFAULT") ) pck0b(
.O(pck0),
.I(pck0i)
);
//assign pck0 = pck0i;
//-----------------------------------------------------------------------------
// The SPI receiver. This sets up the configuration word, which the rest of
// the logic looks at to determine how to connect the A/D and the coil
// drivers (i.e., which section gets it). Also assign some symbolic names
// to the configuration bits, for use below.
//-----------------------------------------------------------------------------
reg [7:0] conf_word_shift;
reg [7:0] conf_word;
// We switch modes between transmitting to the 13.56 MHz tag and receiving
// from it, which means that we must make sure that we can do so without
// glitching, or else we will glitch the transmitted carrier.
always @(posedge ncs)
begin
conf_word <= conf_word_shift;
end
always @(posedge spck)
begin
if(~ncs)
begin
conf_word_shift[7:1] <= conf_word_shift[6:0];
conf_word_shift[0] <= mosi;
end
end
wire [2:0] major_mode;
assign major_mode = conf_word[7:5];
// For the low-frequency configuration:
wire lo_is_125khz;
assign lo_is_125khz = conf_word[3];
// For the high-frequency transmit configuration: modulation depth, either
// 100% (just quite driving antenna, steady LOW), or shallower (tri-state
// some fraction of the buffers)
wire hi_read_tx_shallow_modulation;
assign hi_read_tx_shallow_modulation = conf_word[0];
// For the high-frequency receive correlator: frequency against which to
// correlate.
wire hi_read_rx_xcorr_848;
assign hi_read_rx_xcorr_848 = conf_word[0];
// and whether to drive the coil (reader) or just short it (snooper)
wire hi_read_rx_xcorr_snoop;
assign hi_read_rx_xcorr_snoop = conf_word[1];
// For the high-frequency simulated tag: what kind of modulation to use.
wire [2:0] hi_simulate_mod_type;
assign hi_simulate_mod_type = conf_word[2:0];
//-----------------------------------------------------------------------------
// And then we instantiate the modules corresponding to each of the FPGA's
// major modes, and use muxes to connect the outputs of the active mode to
// the output pins.
//-----------------------------------------------------------------------------
lo_read lr(
pck0, ck_1356meg, ck_1356megb,
lr_pwr_lo, lr_pwr_hi, lr_pwr_oe1, lr_pwr_oe2, lr_pwr_oe3, lr_pwr_oe4,
adc_d, lr_adc_clk,
lr_ssp_frame, lr_ssp_din, ssp_dout, lr_ssp_clk,
cross_hi, cross_lo,
lr_dbg,
lo_is_125khz
);
lo_simulate ls(
pck0, ck_1356meg, ck_1356megb,
ls_pwr_lo, ls_pwr_hi, ls_pwr_oe1, ls_pwr_oe2, ls_pwr_oe3, ls_pwr_oe4,
adc_d, ls_adc_clk,
ls_ssp_frame, ls_ssp_din, ssp_dout, ls_ssp_clk,
cross_hi, cross_lo,
ls_dbg
);
hi_read_tx ht(
pck0, ck_1356meg, ck_1356megb,
ht_pwr_lo, ht_pwr_hi, ht_pwr_oe1, ht_pwr_oe2, ht_pwr_oe3, ht_pwr_oe4,
adc_d, ht_adc_clk,
ht_ssp_frame, ht_ssp_din, ssp_dout, ht_ssp_clk,
cross_hi, cross_lo,
ht_dbg,
hi_read_tx_shallow_modulation
);
hi_read_rx_xcorr hrxc(
pck0, ck_1356meg, ck_1356megb,
hrxc_pwr_lo, hrxc_pwr_hi, hrxc_pwr_oe1, hrxc_pwr_oe2, hrxc_pwr_oe3, hrxc_pwr_oe4,
adc_d, hrxc_adc_clk,
hrxc_ssp_frame, hrxc_ssp_din, ssp_dout, hrxc_ssp_clk,
cross_hi, cross_lo,
hrxc_dbg,
hi_read_rx_xcorr_848, hi_read_rx_xcorr_snoop
);
hi_simulate hs(
pck0, ck_1356meg, ck_1356megb,
hs_pwr_lo, hs_pwr_hi, hs_pwr_oe1, hs_pwr_oe2, hs_pwr_oe3, hs_pwr_oe4,
adc_d, hs_adc_clk,
hs_ssp_frame, hs_ssp_din, ssp_dout, hs_ssp_clk,
cross_hi, cross_lo,
hs_dbg,
hi_simulate_mod_type
);
hi_iso14443a hisn(
pck0, ck_1356meg, ck_1356megb,
hisn_pwr_lo, hisn_pwr_hi, hisn_pwr_oe1, hisn_pwr_oe2, hisn_pwr_oe3, hisn_pwr_oe4,
adc_d, hisn_adc_clk,
hisn_ssp_frame, hisn_ssp_din, ssp_dout, hisn_ssp_clk,
cross_hi, cross_lo,
hisn_dbg,
hi_simulate_mod_type
);
// Major modes:
// 000 -- LF reader (generic)
// 001 -- LF simulated tag (generic)
// 010 -- HF reader, transmitting to tag; modulation depth selectable
// 011 -- HF reader, receiving from tag, correlating as it goes; frequency selectable
// 100 -- HF simulated tag
// 101 -- HF ISO14443-A
// 110 -- unused
// 111 -- everything off
mux8 mux_ssp_clk (major_mode, ssp_clk, lr_ssp_clk, ls_ssp_clk, ht_ssp_clk, hrxc_ssp_clk, hs_ssp_clk, hisn_ssp_clk, 1'b0, 1'b0);
mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, ls_ssp_din, ht_ssp_din, hrxc_ssp_din, hs_ssp_din, hisn_ssp_din, 1'b0, 1'b0);
mux8 mux_ssp_frame (major_mode, ssp_frame, lr_ssp_frame, ls_ssp_frame, ht_ssp_frame, hrxc_ssp_frame, hs_ssp_frame, hisn_ssp_frame, 1'b0, 1'b0);
mux8 mux_pwr_oe1 (major_mode, pwr_oe1, lr_pwr_oe1, ls_pwr_oe1, ht_pwr_oe1, hrxc_pwr_oe1, hs_pwr_oe1, hisn_pwr_oe1, 1'b0, 1'b0);
mux8 mux_pwr_oe2 (major_mode, pwr_oe2, lr_pwr_oe2, ls_pwr_oe2, ht_pwr_oe2, hrxc_pwr_oe2, hs_pwr_oe2, hisn_pwr_oe2, 1'b0, 1'b0);
mux8 mux_pwr_oe3 (major_mode, pwr_oe3, lr_pwr_oe3, ls_pwr_oe3, ht_pwr_oe3, hrxc_pwr_oe3, hs_pwr_oe3, hisn_pwr_oe3, 1'b0, 1'b0);
mux8 mux_pwr_oe4 (major_mode, pwr_oe4, lr_pwr_oe4, ls_pwr_oe4, ht_pwr_oe4, hrxc_pwr_oe4, hs_pwr_oe4, hisn_pwr_oe4, 1'b0, 1'b0);
mux8 mux_pwr_lo (major_mode, pwr_lo, lr_pwr_lo, ls_pwr_lo, ht_pwr_lo, hrxc_pwr_lo, hs_pwr_lo, hisn_pwr_lo, 1'b0, 1'b0);
mux8 mux_pwr_hi (major_mode, pwr_hi, lr_pwr_hi, ls_pwr_hi, ht_pwr_hi, hrxc_pwr_hi, hs_pwr_hi, hisn_pwr_hi, 1'b0, 1'b0);
mux8 mux_adc_clk (major_mode, adc_clk, lr_adc_clk, ls_adc_clk, ht_adc_clk, hrxc_adc_clk, hs_adc_clk, hisn_adc_clk, 1'b0, 1'b0);
mux8 mux_dbg (major_mode, dbg, lr_dbg, ls_dbg, ht_dbg, hrxc_dbg, hs_dbg, hisn_dbg, 1'b0, 1'b0);
// In all modes, let the ADC's outputs be enabled.
assign adc_noe = 1'b0;
endmodule

38
fpga/go.bat Normal file
View file

@ -0,0 +1,38 @@
@echo off
rmdir/s/q xst
del fpga.ngc
xst -ifn xst.scr
if errorlevel 0 goto ok1
goto done
:ok1
del fpga.ngd
ngdbuild -aul -p xc2s30-6vq100 -nt timestamp -uc fpga.ucf fpga.ngc fpga.ngd
if errorlevel 0 goto ok2
goto done
:ok2
del fpga.ncd
map -p xc2s30-6vq100 fpga.ngd
if errorlevel 0 goto ok3
goto done
:ok3
del fpga-placed.ncd
par fpga.ncd fpga-placed.ncd
if errorlevel 0 goto ok4
goto done
:ok4
del fpga.bit fpga.drc fpga.rbt
bitgen -b fpga-placed.ncd fpga.bit
if errorlevel 0 goto ok5
goto done
:ok5
echo okay
perl ..\tools\rbt2c.pl fpga.rbt > ..\armsrc\fpgaimg.c
:done

360
fpga/hi_iso14443a.v Normal file
View file

@ -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

165
fpga/hi_read_rx_xcorr.v Normal file
View file

@ -0,0 +1,165 @@
//-----------------------------------------------------------------------------
//
// Jonathan Westhues, April 2006
//-----------------------------------------------------------------------------
module hi_read_rx_xcorr(
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,
xcorr_is_848, snoop
);
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 xcorr_is_848, snoop;
// Carrier is steady on through this, unless we're snooping.
assign pwr_hi = ck_1356megb & (~snoop);
assign pwr_oe1 = 1'b0;
assign pwr_oe2 = 1'b0;
assign pwr_oe3 = 1'b0;
assign pwr_oe4 = 1'b0;
reg ssp_clk;
reg ssp_frame;
reg fc_div_2;
always @(posedge ck_1356meg)
fc_div_2 = ~fc_div_2;
reg adc_clk;
always @(xcorr_is_848 or fc_div_2 or ck_1356meg)
if(xcorr_is_848)
// The subcarrier frequency is fc/16; we will sample at fc, so that
// means the subcarrier is 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 ...
adc_clk <= ck_1356meg;
else
// The subcarrier frequency is fc/32; we will sample at fc/2, and
// the subcarrier will look identical.
adc_clk <= fc_div_2;
// When we're a reader, we just need to do the BPSK demod; but when we're an
// eavesdropper, we also need to pick out the commands sent by the reader,
// using AM. Do this the same way that we do it for the simulated tag.
reg after_hysteresis, after_hysteresis_prev;
reg [11:0] has_been_low_for;
always @(negedge adc_clk)
begin
if(& adc_d[7:0]) after_hysteresis <= 1'b1;
else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0;
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
// Let us report a correlation every 4 subcarrier cycles, or 4*16 samples,
// so we need a 6-bit counter.
reg [5:0] corr_i_cnt;
reg [5:0] corr_q_cnt;
// And a couple of registers in which to accumulate the correlations.
reg signed [15:0] corr_i_accum;
reg signed [15:0] corr_q_accum;
reg signed [7:0] corr_i_out;
reg signed [7:0] corr_q_out;
// ADC data appears on the rising edge, so sample it on the falling edge
always @(negedge adc_clk)
begin
// These are the correlators: we correlate against in-phase and quadrature
// versions of our reference signal, and keep the (signed) result to
// send out later over the SSP.
if(corr_i_cnt == 7'd63)
begin
if(snoop)
begin
corr_i_out <= {corr_i_accum[12:6], after_hysteresis_prev};
corr_q_out <= {corr_q_accum[12:6], after_hysteresis};
end
else
begin
// Only correlations need to be delivered.
corr_i_out <= corr_i_accum[13:6];
corr_q_out <= corr_q_accum[13:6];
end
corr_i_accum <= adc_d;
corr_q_accum <= adc_d;
corr_q_cnt <= 4;
corr_i_cnt <= 0;
end
else
begin
if(corr_i_cnt[3])
corr_i_accum <= corr_i_accum - adc_d;
else
corr_i_accum <= corr_i_accum + adc_d;
if(corr_q_cnt[3])
corr_q_accum <= corr_q_accum - adc_d;
else
corr_q_accum <= corr_q_accum + adc_d;
corr_i_cnt <= corr_i_cnt + 1;
corr_q_cnt <= corr_q_cnt + 1;
end
// The logic in hi_simulate.v reports 4 samples per bit. We report two
// (I, Q) pairs per bit, so we should do 2 samples per pair.
if(corr_i_cnt == 6'd31)
after_hysteresis_prev <= after_hysteresis;
// Then the result from last time is serialized and send out to the ARM.
// We get one report each cycle, and each report is 16 bits, so the
// ssp_clk should be the adc_clk divided by 64/16 = 4.
if(corr_i_cnt[1:0] == 2'b10)
ssp_clk <= 1'b0;
if(corr_i_cnt[1:0] == 2'b00)
begin
ssp_clk <= 1'b1;
// Don't shift if we just loaded new data, obviously.
if(corr_i_cnt != 7'd0)
begin
corr_i_out[7:0] <= {corr_i_out[6:0], corr_q_out[7]};
corr_q_out[7:1] <= corr_q_out[6:0];
end
end
if(corr_i_cnt[5:2] == 4'b000 || corr_i_cnt[5:2] == 4'b1000)
ssp_frame = 1'b1;
else
ssp_frame = 1'b0;
end
assign ssp_din = corr_i_out[7];
assign dbg = corr_i_cnt[3];
// Unused.
assign pwr_lo = 1'b0;
endmodule

76
fpga/hi_read_tx.v Normal file
View file

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// The way that we connect things when transmitting a command to an ISO
// 15693 tag, using 100% modulation only for now.
//
// Jonathan Westhues, April 2006
//-----------------------------------------------------------------------------
module hi_read_tx(
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,
shallow_modulation
);
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 shallow_modulation;
// The high-frequency stuff. For now, for testing, just bring out the carrier,
// and allow the ARM to modulate it over the SSP.
reg pwr_hi;
reg pwr_oe1;
reg pwr_oe2;
reg pwr_oe3;
reg pwr_oe4;
always @(ck_1356megb or ssp_dout or shallow_modulation)
begin
if(shallow_modulation)
begin
pwr_hi <= ck_1356megb;
pwr_oe1 <= ~ssp_dout;
pwr_oe2 <= ~ssp_dout;
pwr_oe3 <= ~ssp_dout;
pwr_oe4 <= 1'b0;
end
else
begin
pwr_hi <= ck_1356megb & ssp_dout;
pwr_oe1 <= 1'b0;
pwr_oe2 <= 1'b0;
pwr_oe3 <= 1'b0;
pwr_oe4 <= 1'b0;
end
end
// Then just divide the 13.56 MHz clock down to produce appropriate clocks
// for the synchronous serial port.
reg [6:0] hi_div_by_128;
always @(posedge ck_1356meg)
hi_div_by_128 <= hi_div_by_128 + 1;
assign ssp_clk = hi_div_by_128[6];
reg [2:0] hi_byte_div;
always @(negedge ssp_clk)
hi_byte_div <= hi_byte_div + 1;
assign ssp_frame = (hi_byte_div == 3'b000);
assign ssp_din = 1'b0;
assign pwr_lo = 1'b0;
assign dbg = ssp_frame;
endmodule

106
fpga/hi_simulate.v Normal file
View file

@ -0,0 +1,106 @@
//-----------------------------------------------------------------------------
// Pretend to be an ISO 14443 tag. We will do this by alternately short-
// circuiting and open-circuiting the antenna coil, with the tri-state
// pins.
//
// We communicate over the SSP, as a bitstream (i.e., might as well be
// unframed, though we still generate the word sync signal). The output
// (ARM -> FPGA) tells us whether to modulate or not. The input (FPGA
// -> ARM) is us using the A/D as a fancy comparator; this is with
// (software-added) hysteresis, to undo the high-pass filter.
//
// At this point only Type A is implemented. This means that we are using a
// bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make
// things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s)
//
// Jonathan Westhues, October 2006
//-----------------------------------------------------------------------------
module hi_simulate(
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;
// Power amp goes between LOW and tri-state, so pwr_hi (and pwr_lo) can
// always be low.
assign pwr_hi = 1'b0;
assign pwr_lo = 1'b0;
// The comparator with hysteresis on the output from the peak detector.
reg after_hysteresis;
assign adc_clk = ck_1356meg;
always @(negedge adc_clk)
begin
if(& adc_d[7:5]) after_hysteresis = 1'b1;
else if(~(| adc_d[7:5])) after_hysteresis = 1'b0;
end
// Divide 13.56 MHz by 32 to produce the SSP_CLK
reg [4:0] ssp_clk_divider;
always @(posedge adc_clk)
ssp_clk_divider <= (ssp_clk_divider + 1);
assign ssp_clk = ssp_clk_divider[4];
// Divide SSP_CLK by 8 to produce the byte framing signal; the phase of
// this is arbitrary, because it's just a bitstream.
// One nasty issue, though: I can't make it work with both rx and tx at
// once. The phase wrt ssp_clk must be changed. TODO to find out why
// that is and make a better fix.
reg [2:0] ssp_frame_divider_to_arm;
always @(posedge ssp_clk)
ssp_frame_divider_to_arm <= (ssp_frame_divider_to_arm + 1);
reg [2:0] ssp_frame_divider_from_arm;
always @(negedge ssp_clk)
ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1);
reg ssp_frame;
always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type)
if(mod_type == 3'b000) // not modulating, so listening, to ARM
ssp_frame = (ssp_frame_divider_to_arm == 3'b000);
else
ssp_frame = (ssp_frame_divider_from_arm == 3'b000);
// Synchronize up the after-hysteresis signal, to produce DIN.
reg ssp_din;
always @(posedge ssp_clk)
ssp_din = after_hysteresis;
// Modulating carrier frequency is fc/16, reuse ssp_clk divider for that
reg modulating_carrier;
always @(mod_type or ssp_clk or ssp_dout)
if(mod_type == 3'b000)
modulating_carrier <= 1'b0; // no modulation
else if(mod_type == 3'b001)
modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK
else
modulating_carrier <= 1'b0; // yet unused
// This one is all LF, so doesn't matter
assign pwr_oe2 = modulating_carrier;
// 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_oe4 = modulating_carrier;
// This one is always on, so that we can watch the carrier.
assign pwr_oe3 = 1'b0;
assign dbg = after_hysteresis;
endmodule

102
fpga/lo_read.v Normal file
View file

@ -0,0 +1,102 @@
//-----------------------------------------------------------------------------
// The way that we connect things in low-frequency read mode. In this case
// we are generating the 134 kHz or 125 kHz carrier, and running the
// unmodulated carrier at that frequency. The A/D samples at that same rate,
// and the result is serialized.
//
// Jonathan Westhues, April 2006
//-----------------------------------------------------------------------------
module lo_read(
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,
lo_is_125khz
);
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 lo_is_125khz;
// The low-frequency RFID stuff. This is relatively simple, because most
// of the work happens on the ARM, and we just pass samples through. The
// PCK0 must be divided down to generate the A/D clock, and from there by
// a factor of 8 to generate the carrier (that we apply to the coil drivers).
//
// This is also where we decode the received synchronous serial port words,
// to determine how to drive the output enables.
// PCK0 will run at (PLL clock) / 4, or 24 MHz. That means that we can do
// 125 kHz by dividing by a further factor of (8*12*2), or ~134 kHz by
// dividing by a factor of (8*11*2) (for 136 kHz, ~2% error, tolerable).
reg [3:0] pck_divider;
reg clk_lo;
always @(posedge pck0)
begin
if(lo_is_125khz)
begin
if(pck_divider == 4'd11)
begin
pck_divider <= 4'd0;
clk_lo = !clk_lo;
end
else
pck_divider <= pck_divider + 1;
end
else
begin
if(pck_divider == 4'd10)
begin
pck_divider <= 4'd0;
clk_lo = !clk_lo;
end
else
pck_divider <= pck_divider + 1;
end
end
reg [2:0] carrier_divider_lo;
always @(posedge clk_lo)
begin
carrier_divider_lo <= carrier_divider_lo + 1;
end
assign pwr_lo = carrier_divider_lo[2];
// This serializes the values returned from the A/D, and sends them out
// over the SSP.
reg [7:0] to_arm_shiftreg;
always @(posedge clk_lo)
begin
if(carrier_divider_lo == 3'b000)
to_arm_shiftreg <= adc_d;
else
to_arm_shiftreg[7:1] <= to_arm_shiftreg[6:0];
end
assign ssp_clk = clk_lo;
assign ssp_frame = (carrier_divider_lo == 3'b001);
assign ssp_din = to_arm_shiftreg[7];
// The ADC converts on the falling edge, and our serializer loads when
// carrier_divider_lo == 3'b000.
assign adc_clk = ~carrier_divider_lo[2];
assign pwr_hi = 1'b0;
assign dbg = adc_clk;
endmodule

37
fpga/lo_simulate.v Normal file
View file

@ -0,0 +1,37 @@
//-----------------------------------------------------------------------------
// The way that we connect things in low-frequency simulation mode. In this
// case just pass everything through to the ARM, which can bit-bang this
// (because it is so slow).
//
// Jonathan Westhues, April 2006
//-----------------------------------------------------------------------------
module lo_simulate(
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
);
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;
// No logic, straight through.
assign pwr_oe3 = 1'b0;
assign pwr_oe1 = ssp_dout;
assign pwr_oe2 = ssp_dout;
assign pwr_oe4 = ssp_dout;
assign ssp_clk = cross_lo;
assign pwr_lo = 1'b0;
assign adc_clk = 1'b0;
assign pwr_hi = 1'b0;
assign dbg = cross_lo;
endmodule

27
fpga/sim.tcl Normal file
View file

@ -0,0 +1,27 @@
#------------------------------------------------------------------------------
# Run the simulation testbench in ModelSim: recompile both Verilog source
# files, then start the simulation, add a lot of signals to the waveform
# viewer, and run. I should (TODO) fix the absolute paths at some point.
#
# Jonathan Westhues, Mar 2006
#------------------------------------------------------------------------------
vlog -work work -O0 C:/depot/proximity/mark3/fpga/fpga.v
vlog -work work -O0 C:/depot/proximity/mark3/fpga/fpga_tb.v
vsim work.fpga_tb
add wave sim:/fpga_tb/adc_clk
add wave sim:/fpga_tb/adc_d
add wave sim:/fpga_tb/pwr_lo
add wave sim:/fpga_tb/ssp_clk
add wave sim:/fpga_tb/ssp_frame
add wave sim:/fpga_tb/ssp_din
add wave sim:/fpga_tb/ssp_dout
add wave sim:/fpga_tb/dut/clk_lo
add wave sim:/fpga_tb/dut/pck_divider
add wave sim:/fpga_tb/dut/carrier_divider_lo
add wave sim:/fpga_tb/dut/conf_word
run 30000

50
fpga/testbed_fpga.v Normal file
View file

@ -0,0 +1,50 @@
`include "fpga.v"
module testbed_fpga;
reg spck, mosi, ncs;
wire miso;
reg pck0i, ck_1356meg, ck_1356megb;
wire pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4;
reg [7:0] adc_d;
wire adc_clk, adc_noe;
reg ssp_dout;
wire ssp_frame, ssp_din, ssp_clk;
fpga dut(
spck, miso, mosi, ncs,
pck0i, ck_1356meg, ck_1356megb,
pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4,
adc_d, adc_clk, adc_noe,
ssp_frame, ssp_din, ssp_dout, ssp_clk
);
integer i;
initial begin
// init inputs
#5 ncs=1;
#5 spck = 1;
#5 mosi = 1;
#50 ncs=0;
for (i = 0 ; i < 8 ; i = i + 1) begin
#5 mosi = $random;
#5 spck = 0;
#5 spck = 1;
end
#5 ncs=1;
#50 ncs=0;
for (i = 0 ; i < 8 ; i = i + 1) begin
#5 mosi = $random;
#5 spck = 0;
#5 spck = 1;
end
#5 ncs=1;
#50 mosi=1;
$finish;
end
endmodule // main

109
fpga/testbed_hi_read_tx.v Normal file
View file

@ -0,0 +1,109 @@
`include "hi_read_tx.v"
/*
pck0 - input main 24Mhz clock (PLL / 4)
[7:0] adc_d - input data from A/D converter
shallow_modulation - modulation type
pwr_lo - output to coil drivers (ssp_clk / 8)
adc_clk - output A/D clock signal
ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted)
ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first)
ssp_clk - output SSP clock signal
ck_1356meg - input unused
ck_1356megb - input unused
ssp_dout - input unused
cross_hi - input unused
cross_lo - input unused
pwr_hi - output unused, tied low
pwr_oe1 - output unused, undefined
pwr_oe2 - output unused, undefined
pwr_oe3 - output unused, undefined
pwr_oe4 - output unused, undefined
dbg - output alias for adc_clk
*/
module testbed_hi_read_tx;
reg pck0;
reg [7:0] adc_d;
reg shallow_modulation;
wire pwr_lo;
wire adc_clk;
reg ck_1356meg;
reg ck_1356megb;
wire ssp_frame;
wire ssp_din;
wire ssp_clk;
reg ssp_dout;
wire pwr_hi;
wire pwr_oe1;
wire pwr_oe2;
wire pwr_oe3;
wire pwr_oe4;
wire cross_lo;
wire cross_hi;
wire dbg;
hi_read_tx #(5,200) dut(
.pck0(pck0),
.ck_1356meg(ck_1356meg),
.ck_1356megb(ck_1356megb),
.pwr_lo(pwr_lo),
.pwr_hi(pwr_hi),
.pwr_oe1(pwr_oe1),
.pwr_oe2(pwr_oe2),
.pwr_oe3(pwr_oe3),
.pwr_oe4(pwr_oe4),
.adc_d(adc_d),
.adc_clk(adc_clk),
.ssp_frame(ssp_frame),
.ssp_din(ssp_din),
.ssp_dout(ssp_dout),
.ssp_clk(ssp_clk),
.cross_hi(cross_hi),
.cross_lo(cross_lo),
.dbg(dbg),
.shallow_modulation(shallow_modulation)
);
integer idx, i;
// main clock
always #5 begin
ck_1356megb = !ck_1356megb;
ck_1356meg = ck_1356megb;
end
//crank DUT
task crank_dut;
begin
@(posedge ssp_clk) ;
ssp_dout = $random;
end
endtask
initial begin
// init inputs
ck_1356megb = 0;
adc_d = 0;
ssp_dout=0;
// shallow modulation off
shallow_modulation=0;
for (i = 0 ; i < 16 ; i = i + 1) begin
crank_dut;
end
// shallow modulation on
shallow_modulation=1;
for (i = 0 ; i < 16 ; i = i + 1) begin
crank_dut;
end
$finish;
end
endmodule // main

116
fpga/testbed_hi_simulate.v Normal file
View file

@ -0,0 +1,116 @@
`include "hi_simulate.v"
/*
pck0 - input main 24Mhz clock (PLL / 4)
[7:0] adc_d - input data from A/D converter
mod_type - modulation type
pwr_lo - output to coil drivers (ssp_clk / 8)
adc_clk - output A/D clock signal
ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted)
ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first)
ssp_clk - output SSP clock signal
ck_1356meg - input unused
ck_1356megb - input unused
ssp_dout - input unused
cross_hi - input unused
cross_lo - input unused
pwr_hi - output unused, tied low
pwr_oe1 - output unused, undefined
pwr_oe2 - output unused, undefined
pwr_oe3 - output unused, undefined
pwr_oe4 - output unused, undefined
dbg - output alias for adc_clk
*/
module testbed_hi_simulate;
reg pck0;
reg [7:0] adc_d;
reg mod_type;
wire pwr_lo;
wire adc_clk;
reg ck_1356meg;
reg ck_1356megb;
wire ssp_frame;
wire ssp_din;
wire ssp_clk;
reg ssp_dout;
wire pwr_hi;
wire pwr_oe1;
wire pwr_oe2;
wire pwr_oe3;
wire pwr_oe4;
wire cross_lo;
wire cross_hi;
wire dbg;
hi_simulate #(5,200) dut(
.pck0(pck0),
.ck_1356meg(ck_1356meg),
.ck_1356megb(ck_1356megb),
.pwr_lo(pwr_lo),
.pwr_hi(pwr_hi),
.pwr_oe1(pwr_oe1),
.pwr_oe2(pwr_oe2),
.pwr_oe3(pwr_oe3),
.pwr_oe4(pwr_oe4),
.adc_d(adc_d),
.adc_clk(adc_clk),
.ssp_frame(ssp_frame),
.ssp_din(ssp_din),
.ssp_dout(ssp_dout),
.ssp_clk(ssp_clk),
.cross_hi(cross_hi),
.cross_lo(cross_lo),
.dbg(dbg),
.mod_type(mod_type)
);
integer idx, i;
// main clock
always #5 begin
ck_1356megb = !ck_1356megb;
ck_1356meg = ck_1356megb;
end
always begin
@(negedge adc_clk) ;
adc_d = $random;
end
//crank DUT
task crank_dut;
begin
@(negedge ssp_clk) ;
ssp_dout = $random;
end
endtask
initial begin
// init inputs
ck_1356megb = 0;
// random values
adc_d = 0;
ssp_dout=1;
// shallow modulation off
mod_type=0;
for (i = 0 ; i < 16 ; i = i + 1) begin
crank_dut;
end
// shallow modulation on
mod_type=1;
for (i = 0 ; i < 16 ; i = i + 1) begin
crank_dut;
end
$finish;
end
endmodule // main

105
fpga/testbed_lo_read.v Normal file
View file

@ -0,0 +1,105 @@
`include "lo_read.v"
/*
pck0 - input main 24Mhz clock (PLL / 4)
[7:0] adc_d - input data from A/D converter
lo_is_125khz - input freq selector (1=125Khz, 0=136Khz)
pwr_lo - output to coil drivers (ssp_clk / 8)
adc_clk - output A/D clock signal
ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted)
ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first)
ssp_clk - output SSP clock signal 1Mhz/1.09Mhz (pck0 / 2*(11+lo_is_125khz) )
ck_1356meg - input unused
ck_1356megb - input unused
ssp_dout - input unused
cross_hi - input unused
cross_lo - input unused
pwr_hi - output unused, tied low
pwr_oe1 - output unused, undefined
pwr_oe2 - output unused, undefined
pwr_oe3 - output unused, undefined
pwr_oe4 - output unused, undefined
dbg - output alias for adc_clk
*/
module testbed_lo_read;
reg pck0;
reg [7:0] adc_d;
reg lo_is_125khz;
wire pwr_lo;
wire adc_clk;
wire ck_1356meg;
wire ck_1356megb;
wire ssp_frame;
wire ssp_din;
wire ssp_clk;
wire ssp_dout;
wire pwr_hi;
wire pwr_oe1;
wire pwr_oe2;
wire pwr_oe3;
wire pwr_oe4;
wire cross_lo;
wire cross_hi;
wire dbg;
lo_read #(5,200) dut(
.pck0(pck0),
.ck_1356meg(ck_1356meg),
.ck_1356megb(ck_1356megb),
.pwr_lo(pwr_lo),
.pwr_hi(pwr_hi),
.pwr_oe1(pwr_oe1),
.pwr_oe2(pwr_oe2),
.pwr_oe3(pwr_oe3),
.pwr_oe4(pwr_oe4),
.adc_d(adc_d),
.adc_clk(adc_clk),
.ssp_frame(ssp_frame),
.ssp_din(ssp_din),
.ssp_dout(ssp_dout),
.ssp_clk(ssp_clk),
.cross_hi(cross_hi),
.cross_lo(cross_lo),
.dbg(dbg),
.lo_is_125khz(lo_is_125khz)
);
integer idx, i;
// main clock
always #5 pck0 = !pck0;
//new A/D value available from ADC on positive edge
task crank_dut;
begin
@(posedge adc_clk) ;
adc_d = $random;
end
endtask
initial begin
// init inputs
pck0 = 0;
adc_d = 0;
// simulate 4 A/D cycles at 134Khz
lo_is_125khz=0;
for (i = 0 ; i < 4 ; i = i + 1) begin
crank_dut;
end
// simulate 4 A/D cycles at 125Khz
lo_is_125khz=1;
for (i = 0 ; i < 4 ; i = i + 1) begin
crank_dut;
end
$finish;
end
endmodule // main

101
fpga/testbed_lo_simulate.v Normal file
View file

@ -0,0 +1,101 @@
`include "lo_simulate.v"
/*
pck0 - input main 24Mhz clock (PLL / 4)
[7:0] adc_d - input data from A/D converter
pwr_lo - output to coil drivers (ssp_clk / 8)
adc_clk - output A/D clock signal
ssp_frame - output SSS frame indicator (goes high while the 8 bits are shifted)
ssp_din - output SSP data to ARM (shifts 8 bit A/D value serially to ARM MSB first)
ssp_clk - output SSP clock signal
ck_1356meg - input unused
ck_1356megb - input unused
ssp_dout - input unused
cross_hi - input unused
cross_lo - input unused
pwr_hi - output unused, tied low
pwr_oe1 - output unused, undefined
pwr_oe2 - output unused, undefined
pwr_oe3 - output unused, undefined
pwr_oe4 - output unused, undefined
dbg - output alias for adc_clk
*/
module testbed_lo_simulate;
reg pck0;
reg [7:0] adc_d;
wire pwr_lo;
wire adc_clk;
wire ck_1356meg;
wire ck_1356megb;
wire ssp_frame;
wire ssp_din;
wire ssp_clk;
reg ssp_dout;
wire pwr_hi;
wire pwr_oe1;
wire pwr_oe2;
wire pwr_oe3;
wire pwr_oe4;
reg cross_lo;
wire cross_hi;
wire dbg;
lo_simulate #(5,200) dut(
.pck0(pck0),
.ck_1356meg(ck_1356meg),
.ck_1356megb(ck_1356megb),
.pwr_lo(pwr_lo),
.pwr_hi(pwr_hi),
.pwr_oe1(pwr_oe1),
.pwr_oe2(pwr_oe2),
.pwr_oe3(pwr_oe3),
.pwr_oe4(pwr_oe4),
.adc_d(adc_d),
.adc_clk(adc_clk),
.ssp_frame(ssp_frame),
.ssp_din(ssp_din),
.ssp_dout(ssp_dout),
.ssp_clk(ssp_clk),
.cross_hi(cross_hi),
.cross_lo(cross_lo),
.dbg(dbg)
);
integer i, counter=0;
// main clock
always #5 pck0 = !pck0;
//cross_lo is not really synced to pck0 but it's roughly pck0/192 (24Mhz/192=125Khz)
task crank_dut;
begin
@(posedge pck0) ;
counter = counter + 1;
if (counter == 192) begin
counter = 0;
ssp_dout = $random;
cross_lo = 1;
end else begin
cross_lo = 0;
end
end
endtask
initial begin
pck0 = 0;
for (i = 0 ; i < 4096 ; i = i + 1) begin
crank_dut;
end
$finish;
end
endmodule // main

27
fpga/util.v Normal file
View file

@ -0,0 +1,27 @@
//-----------------------------------------------------------------------------
// General-purpose miscellany.
//
// Jonathan Westhues, April 2006.
//-----------------------------------------------------------------------------
module mux8(sel, y, x0, x1, x2, x3, x4, x5, x6, x7);
input [2:0] sel;
input x0, x1, x2, x3, x4, x5, x6, x7;
output y;
reg y;
always @(x0 or x1 or x2 or x3 or x4 or x5 or x6 or x7 or sel)
begin
case (sel)
3'b000: y = x0;
3'b001: y = x1;
3'b010: y = x2;
3'b011: y = x3;
3'b100: y = x4;
3'b101: y = x5;
3'b110: y = x6;
3'b111: y = x7;
endcase
end
endmodule

1
fpga/xst.scr Normal file
View file

@ -0,0 +1 @@
run -ifn fpga.v -ifmt Verilog -ofn fpga.ngc -ofmt NGC -p xc2s30-6vq100 -opt_mode Speed -opt_level 1 -ent fpga