mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-22 15:22:47 +00:00
Merge pull request #3697 from xianglin1998/fpga-merge-and-opensource
FPGA: Merge and opensource
This commit is contained in:
47 files changed
+2257
-353
No files matched your search
@@ -0,0 +1,72 @@
|
||||
name: FPGA PM3 Build
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'fpga/**'
|
||||
- '.github/workflows/fpga-pm3.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'fpga/**'
|
||||
- '.github/workflows/fpga-pm3.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
# The PM3, PM3-ULT and iCopy-X images are Spartan-II and Spartan-3E, and
|
||||
# WebPack ISE 10.1 is the only tool chain that places and routes them. It is a
|
||||
# licensed multi gigabyte install, so the build runs inside this image, the
|
||||
# one doc/fpga_arm_notes.md points at. Pinned by digest rather than by the
|
||||
# tag, which has been re-pushed since it was first published.
|
||||
ISE_IMAGE: nhutton/prox-container@sha256:b42abab7f88df928e59ab97ce3a557b547bc5fc967c19e49f30d7cfb8d3a5a15
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build the PM3 bitstreams
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
# The image is 8 GB unpacked, so keep an eye on the runner's disk.
|
||||
- name: Pull the ISE image
|
||||
run: |
|
||||
df -h /
|
||||
docker pull "$ISE_IMAGE"
|
||||
|
||||
- name: Build the PM3 bitstreams
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker run --rm -v "$PWD:/src" -w /src "$ISE_IMAGE" bash -euc '
|
||||
# The image exports its PATH from ~/.bashrc, which returns early in
|
||||
# a non interactive shell, so name the ISE bin directory here.
|
||||
export PATH="$PATH:/opt/Xilinx/10.1/ISE/bin/lin"
|
||||
# XST cannot create its project temp directory on the mounted
|
||||
# volume, so build a copy inside the container and copy the
|
||||
# bitstreams back out over the checked out ones.
|
||||
rm -rf /build && mkdir /build && cp -r /src/fpga/. /build/
|
||||
cd /build
|
||||
make all -j"$(nproc)"
|
||||
cp *.bit /src/fpga/
|
||||
'
|
||||
|
||||
- name: Check the bitstreams
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# fpga_icopyx_felica, fpga_icopyx_hf_15 and fpga_icopyx_lf have no
|
||||
# target of their own and stay empty; the rest must have content
|
||||
for t in fpga_pm3_lf fpga_pm3_hf fpga_pm3_hf_15 fpga_pm3_felica \
|
||||
fpga_pm3_ult_lf fpga_pm3_ult_hf fpga_pm3_ult_hf_15 \
|
||||
fpga_pm3_ult_felica fpga_icopyx_hf; do
|
||||
size=$(stat -c%s "fpga/$t.bit")
|
||||
echo "fpga/$t.bit ${size} bytes"
|
||||
test "$size" -gt 1000
|
||||
done
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fpga-pm3-bitstreams
|
||||
path: fpga/*.bit
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,89 @@
|
||||
name: FPGA PM5 Build
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'fpga/**'
|
||||
- '.github/workflows/fpga-pm5.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'fpga/**'
|
||||
- '.github/workflows/fpga-pm5.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# oss-cad-suite is released daily; bump the date to move to a newer one.
|
||||
OSS_CAD_SUITE_VERSION: '2026-10-11'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
pm5:
|
||||
name: Build PM5 bitstream
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Install oss-cad-suite
|
||||
run: |
|
||||
set -euo pipefail
|
||||
url="https://github.com/YosysHQ/oss-cad-suite-build/releases/download/${OSS_CAD_SUITE_VERSION}/oss-cad-suite-linux-x64-${OSS_CAD_SUITE_VERSION//-/}.tgz"
|
||||
echo "Fetching $url"
|
||||
curl -sSL "$url" | tar -xz -C "$RUNNER_TEMP"
|
||||
# the makefile puts the suite's bin/, lib/ and bundled python3 in
|
||||
# front of PATH itself, so nothing else needs to be exported
|
||||
echo "OSS_CAD_SUITE=$RUNNER_TEMP/oss-cad-suite" >> "$GITHUB_ENV"
|
||||
|
||||
# Runs the real tools through the same environment the build uses, so a
|
||||
# suite that cannot run fails here instead of half way through the build.
|
||||
- name: Check the toolchain
|
||||
run: make -C fpga -f proxmark5_fpga.mk smoke
|
||||
|
||||
# Place and route fails the job on a timing violation, so this does not
|
||||
# only prove the design still elaborates.
|
||||
- name: Build the PM5 bitstream
|
||||
run: make -C fpga pm5
|
||||
|
||||
- name: Check the bitstream
|
||||
run: |
|
||||
set -euo pipefail
|
||||
bin_size=$(stat -c%s fpga/fpga_pm5.bin)
|
||||
fs_size=$(stat -c%s fpga/fpga_pm5.fs)
|
||||
echo "fpga_pm5.bin ${bin_size} bytes"
|
||||
echo "fpga_pm5.fs ${fs_size} bytes"
|
||||
# the .bin is the bitstream the ARM JTAG loader shifts out, the .fs is
|
||||
# its ASCII form: 8 characters plus a newline per byte
|
||||
test "$bin_size" -gt 100000
|
||||
test "$fs_size" -gt $((bin_size * 6))
|
||||
test "$fs_size" -lt $((bin_size * 10))
|
||||
|
||||
# The device only accepts a bitstream naming its own IDCODE and
|
||||
# otherwise stays unconfigured, with nothing driving the antennas.
|
||||
# apycula's GW1N-4 database writes the plain GW1N-4, so the build
|
||||
# stamps the part's in (fpga/common/gowin_devid.py) - keep it that way.
|
||||
# The IDCODE is the four bytes at 0x1C of the header.
|
||||
idcode=$(od -An -tx1 -j 28 -N 4 fpga/fpga_pm5.bin | tr -d ' \n')
|
||||
echo "device idcode in the bitstream: 0x${idcode}"
|
||||
test "$idcode" = "1100381b"
|
||||
|
||||
# A rebuild from scratch has to produce the same bytes; the tools carry no
|
||||
# timestamp, so a difference means something started leaking one in, and
|
||||
# a released bitstream could no longer be checked against the sources.
|
||||
- name: Check the build is reproducible
|
||||
run: |
|
||||
set -euo pipefail
|
||||
md5sum fpga/fpga_pm5.fs fpga/fpga_pm5.bin > "$RUNNER_TEMP/bitstream.md5"
|
||||
make -C fpga -f proxmark5_fpga.mk clean
|
||||
make -C fpga pm5
|
||||
md5sum -c "$RUNNER_TEMP/bitstream.md5"
|
||||
|
||||
- name: Upload the bitstream
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: fpga-pm5-bitstream
|
||||
path: |
|
||||
fpga/fpga_pm5.fs
|
||||
fpga/fpga_pm5.bin
|
||||
if-no-files-found: error
|
||||
@@ -100,6 +100,8 @@ tools/mfulc_des_brute/mfulc_des_brute
|
||||
!tools/lena.bmp
|
||||
|
||||
fpga/__build*
|
||||
# the ASCII form of the PM5 bitstream, regenerated by the build like its .bin
|
||||
fpga/*.fs
|
||||
|
||||
# offcial dumps folder
|
||||
dumps/*
|
||||
|
||||
@@ -39,6 +39,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||
- Fixed `hf 14a info` now correctly handles NAK when receiving RATS (@iceman1001)
|
||||
- Fixed `hf mfu restore` to support Ultralight AES (@iceman1001)
|
||||
- Fixed `hf mfu info` to print UL-AES config pages again (@iceman1001)
|
||||
- Added FPGA opensource (@xianglin1998)
|
||||
|
||||
## [Frosty Lemon.4.23346][2026-09-18]
|
||||
- Fixed `hf mfdes sim` - the ATS now advertises 106 kbit/s only, PPS to a higher rate is refused, a selected card ignores REQA/WUPA, and ISO-DEP block recovery now follows the card sequence (@mistial-dev)
|
||||
|
||||
+45
-236
@@ -1,259 +1,68 @@
|
||||
#
|
||||
# FPGA Makefile for all targets
|
||||
# FPGA build dispatcher.
|
||||
#
|
||||
# The top part of this Makefile is used to define custom options for a number of compilation targets
|
||||
# To define an additional target simply look at the other defined targets and add a new TARGET entry with a unique number and the custom options required
|
||||
# Each requested target is routed to the implementation of the platform that
|
||||
# owns it:
|
||||
# proxmark3_fpga.mk Xilinx ISE - PM3, PM3-ULT, iCopy-X
|
||||
# proxmark5_fpga.mk yosys/nextpnr - PM5
|
||||
#
|
||||
# make - every PM3 image
|
||||
# make pm3 - same
|
||||
# make pm5 - the PM5 bitstream
|
||||
# make <target> - routed by name, e.g. fpga_pm3_hf, fpga_pm3_ult_lf, fpga_pm5
|
||||
# make clean - both platforms
|
||||
# make help - both target lists
|
||||
#
|
||||
# Both platform files are also usable directly, which is how the recursive
|
||||
# builds call them:
|
||||
# make -C fpga -f proxmark5_fpga.mk all
|
||||
|
||||
XILINX_TOOLS_PREFIX=
|
||||
include common_src.mk
|
||||
|
||||
# Copy update (only when destination is older or missing)
|
||||
CP = cp -u
|
||||
PM3_MK = proxmark3_fpga.mk
|
||||
PM5_MK = proxmark5_fpga.mk
|
||||
|
||||
# Make directory, no error if already existing
|
||||
MKDIR = mkdir -p
|
||||
PLATFORMS = pm3 pm5
|
||||
|
||||
# Remove recursive, force
|
||||
RMDIR = rm -rf
|
||||
# Everything that is not a dispatcher builtin is forwarded to a platform file.
|
||||
GOALS = $(filter-out $(PLATFORMS) all clean help,$(MAKECMDGOALS))
|
||||
|
||||
# Path to make
|
||||
MAKE = make
|
||||
# PM5 owns the fpga_pm5* targets, everything else is PM3.
|
||||
mk_for = $(if $(filter fpga_pm5%,$1),$(PM5_MK),$(PM3_MK))
|
||||
|
||||
# Custom prefix for build directories, each target is built into its own separate directory name formed by combining the PREFIX and TARGET names.
|
||||
# This way the source is not polluted with build files and the build directories are left behind after compilation so logs and reports can be
|
||||
# examined or can be easily deleted with "make clean"
|
||||
PREFIX = __
|
||||
.PHONY: all pm3 pm5 clean help $(GOALS)
|
||||
|
||||
# Options to be passed to XST
|
||||
XST_OPTS_BASE = run
|
||||
XST_OPTS_BASE += -ifn xst.prj
|
||||
XST_OPTS_BASE += -ifmt mixed
|
||||
XST_OPTS_BASE += -ofmt NGC
|
||||
XST_OPTS_BASE += -lso xst.lso
|
||||
XST_OPTS_BASE += -top fpga_top
|
||||
XST_OPTS_BASE += -resource_sharing yes
|
||||
ifneq ($(GOALS),)
|
||||
|
||||
# Optimizations for speed (default)
|
||||
XST_OPTS_SPEED = -opt_mode Speed
|
||||
XST_OPTS_SPEED += -opt_level 1
|
||||
XST_OPTS_SPEED += -fsm_style lut
|
||||
XST_OPTS_SPEED += -fsm_encoding auto
|
||||
$(GOALS):
|
||||
$(Q)$(MAKE) --no-print-directory -f $(call mk_for,$@) $@
|
||||
|
||||
# Optimization for reduced space
|
||||
XST_OPTS_AREA = -opt_mode area
|
||||
XST_OPTS_AREA += -opt_level 2
|
||||
XST_OPTS_AREA += -fsm_style bram
|
||||
XST_OPTS_AREA += -fsm_encoding compact
|
||||
all pm3 pm5 clean help: ;
|
||||
|
||||
# par specific option (set determistic seed)
|
||||
PAR_OPTIONS = -t 2
|
||||
else
|
||||
|
||||
# Types of selective module compilation:
|
||||
# WITH_LF Enables selection of LF modules (and disables all HF)
|
||||
all: pm3
|
||||
|
||||
# To enable these modules WITH_LF _MUST_ be defined
|
||||
# WITH_LF0 enable LF reader (generic)
|
||||
# WITH_LF1 enable LF edge detect (generic)
|
||||
# WITH_LF2 enable LF passthrough
|
||||
# WITH_LF3 enable LF ADC (read/write)
|
||||
pm3:
|
||||
$(Q)$(MAKE) --no-print-directory -f $(PM3_MK) all
|
||||
|
||||
# To enable these modules WITH_LF _MUST_NOT_ be defined
|
||||
# WITH_HF0 enable HF reader (see also WITH_HF_15 below)
|
||||
# WITH_HF_15 select "iso15 2sc mode" extensions instead of original
|
||||
# WITH_HF1 enable HF simulated tag
|
||||
# WITH_HF2 enable HF ISO14443-A
|
||||
# WITH_HF3 enable sniff
|
||||
# WITH_HF4 enable HF ISO18092 FeliCa
|
||||
# WITH_HF5 enable HF get trace
|
||||
pm5:
|
||||
$(Q)$(MAKE) --no-print-directory -f $(PM5_MK) all
|
||||
|
||||
# RDV40/Generic - Enable LF and all the LF modules
|
||||
TARGET1_OPTIONS = -define \{WITH_LF WITH_LF0 WITH_LF1 WITH_LF2 WITH_LF3\}
|
||||
# RDV40/Generic - Enable all HF modules except Felica
|
||||
TARGET2_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF2 WITH_HF3 WITH_HF5\}
|
||||
# RDV40/Generic - Enable all HF modules except Felica and ISO14443, select HF_15 instead of HF
|
||||
TARGET3_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF5 WITH_HF_15 WITH_HF_15_LOWSIGNAL\}
|
||||
# RDV40/Generic - Enable all HF modules except ISO14443
|
||||
TARGET4_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF4 WITH_HF5 WITH_FELICA_PROBE\}
|
||||
# ICOPYX
|
||||
TARGET5_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET6_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET7_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET8_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET9_OPTIONS = $(TARGET1_OPTIONS)
|
||||
|
||||
# Here we list the target names
|
||||
TARGET1_NAME = fpga_pm3_lf
|
||||
TARGET2_NAME = fpga_pm3_hf
|
||||
TARGET3_NAME = fpga_pm3_hf_15
|
||||
TARGET4_NAME = fpga_pm3_felica
|
||||
TARGET5_NAME = fpga_icopyx_hf
|
||||
TARGET6_NAME = fpga_pm3_ult_lf
|
||||
TARGET7_NAME = fpga_pm3_ult_hf
|
||||
TARGET8_NAME = fpga_pm3_ult_hf_15
|
||||
TARGET9_NAME = fpga_pm3_ult_felica
|
||||
|
||||
# Targets can be compiled for different FPGA flavours
|
||||
TARGET1_FPGA = xc2s30-5-vq100
|
||||
TARGET2_FPGA = $(TARGET1_FPGA)
|
||||
TARGET3_FPGA = $(TARGET1_FPGA)
|
||||
TARGET4_FPGA = $(TARGET1_FPGA)
|
||||
TARGET5_FPGA = xc3s100e-4-vq100
|
||||
TARGET6_FPGA = xc2s50-5-tq144
|
||||
TARGET7_FPGA = $(TARGET6_FPGA)
|
||||
TARGET8_FPGA = $(TARGET6_FPGA)
|
||||
TARGET9_FPGA = $(TARGET6_FPGA)
|
||||
|
||||
# Assemble the final XST options for each target
|
||||
TARGET1_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET1_FPGA) -ofn $(TARGET1_NAME) $(TARGET1_OPTIONS)
|
||||
TARGET2_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET2_FPGA) -ofn $(TARGET2_NAME) $(TARGET2_OPTIONS)
|
||||
TARGET3_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET3_FPGA) -ofn $(TARGET3_NAME) $(TARGET3_OPTIONS)
|
||||
TARGET4_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET4_FPGA) -ofn $(TARGET4_NAME) $(TARGET4_OPTIONS)
|
||||
TARGET5_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET5_FPGA) -ofn $(TARGET5_NAME) $(TARGET5_OPTIONS)
|
||||
TARGET6_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET6_FPGA) -ofn $(TARGET6_NAME) $(TARGET6_OPTIONS)
|
||||
TARGET7_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET7_FPGA) -ofn $(TARGET7_NAME) $(TARGET7_OPTIONS)
|
||||
TARGET8_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET8_FPGA) -ofn $(TARGET8_NAME) $(TARGET8_OPTIONS)
|
||||
TARGET9_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET9_FPGA) -ofn $(TARGET9_NAME) $(TARGET9_OPTIONS)
|
||||
|
||||
# these files are common for all targets
|
||||
TARGET_COMMON_FILES = define.v
|
||||
TARGET_COMMON_FILES += mux8.v
|
||||
TARGET_COMMON_FILES += clk_divider.v
|
||||
TARGET_COMMON_FILES += lp20khz_1MSa_iir_filter.v
|
||||
TARGET_COMMON_FILES += min_max_tracker.v
|
||||
TARGET_COMMON_FILES += hi_flite.v
|
||||
TARGET_COMMON_FILES += hi_get_trace.v
|
||||
TARGET_COMMON_FILES += hi_iso14443a.v
|
||||
TARGET_COMMON_FILES += hi_reader.v
|
||||
TARGET_COMMON_FILES += hi_simulate.v
|
||||
TARGET_COMMON_FILES += hi_sniffer.v
|
||||
TARGET_COMMON_FILES += lf_edge_detect.v
|
||||
TARGET_COMMON_FILES += lo_adc.v
|
||||
TARGET_COMMON_FILES += lo_edge_detect.v
|
||||
TARGET_COMMON_FILES += lo_passthru.v
|
||||
TARGET_COMMON_FILES += lo_read.v
|
||||
|
||||
# Add the files that are unique per target and all the common files
|
||||
TARGET1_FILES = $(TARGET_COMMON_FILES) fpga_pm3_top.v
|
||||
TARGET2_FILES = $(TARGET1_FILES)
|
||||
TARGET3_FILES = $(TARGET1_FILES)
|
||||
TARGET4_FILES = $(TARGET1_FILES)
|
||||
TARGET5_FILES = $(TARGET_COMMON_FILES) mux2_onein.v mux2_oneout.v fpga_icopyx_hf.v fpga_icopyx_lf.v fpga_icopyx_top.v
|
||||
TARGET6_FILES = $(TARGET1_FILES)
|
||||
TARGET7_FILES = $(TARGET1_FILES)
|
||||
TARGET8_FILES = $(TARGET1_FILES)
|
||||
TARGET9_FILES = $(TARGET1_FILES)
|
||||
|
||||
# List of all valid target FPGA images to build
|
||||
TARGETS = $(TARGET1_NAME) $(TARGET2_NAME) $(TARGET3_NAME) $(TARGET4_NAME) $(TARGET5_NAME) $(TARGET6_NAME) $(TARGET7_NAME) $(TARGET8_NAME) $(TARGET9_NAME)
|
||||
|
||||
# Verbosity type for ISE tools ise|xflow|silent
|
||||
VERBOSITY = -intstyle silent
|
||||
# Echo (Q=) or not echo (Q=@) build commands to the terminal
|
||||
Q=
|
||||
|
||||
# Pass the custom variables to the lower make rules
|
||||
$(TARGET1_NAME).bit: TARGET_FPGA = $(TARGET1_FPGA)
|
||||
$(TARGET1_NAME).bit: TARGET_FILES = $(TARGET1_FILES)
|
||||
$(TARGET1_NAME).bit: TARGET_XST_OPTS = $(TARGET1_XST_OPTS)
|
||||
|
||||
$(TARGET2_NAME).bit: TARGET_FPGA = $(TARGET2_FPGA)
|
||||
$(TARGET2_NAME).bit: TARGET_FILES = $(TARGET2_FILES)
|
||||
$(TARGET2_NAME).bit: TARGET_XST_OPTS = $(TARGET2_XST_OPTS)
|
||||
|
||||
$(TARGET3_NAME).bit: TARGET_FPGA = $(TARGET3_FPGA)
|
||||
$(TARGET3_NAME).bit: TARGET_FILES = $(TARGET3_FILES)
|
||||
$(TARGET3_NAME).bit: TARGET_XST_OPTS = $(TARGET3_XST_OPTS)
|
||||
|
||||
$(TARGET4_NAME).bit: TARGET_FPGA = $(TARGET4_FPGA)
|
||||
$(TARGET4_NAME).bit: TARGET_FILES = $(TARGET4_FILES)
|
||||
$(TARGET4_NAME).bit: TARGET_XST_OPTS = $(TARGET4_XST_OPTS)
|
||||
|
||||
$(TARGET5_NAME).bit: TARGET_FPGA = $(TARGET5_FPGA)
|
||||
$(TARGET5_NAME).bit: TARGET_FILES = $(TARGET5_FILES)
|
||||
$(TARGET5_NAME).bit: TARGET_XST_OPTS = $(TARGET5_XST_OPTS)
|
||||
|
||||
$(TARGET6_NAME).bit: TARGET_FPGA = $(TARGET6_FPGA)
|
||||
$(TARGET6_NAME).bit: TARGET_FILES = $(TARGET6_FILES)
|
||||
$(TARGET6_NAME).bit: TARGET_XST_OPTS = $(TARGET6_XST_OPTS)
|
||||
|
||||
$(TARGET7_NAME).bit: TARGET_FPGA = $(TARGET7_FPGA)
|
||||
$(TARGET7_NAME).bit: TARGET_FILES = $(TARGET7_FILES)
|
||||
$(TARGET7_NAME).bit: TARGET_XST_OPTS = $(TARGET7_XST_OPTS)
|
||||
|
||||
$(TARGET8_NAME).bit: TARGET_FPGA = $(TARGET8_FPGA)
|
||||
$(TARGET8_NAME).bit: TARGET_FILES = $(TARGET8_FILES)
|
||||
$(TARGET8_NAME).bit: TARGET_XST_OPTS = $(TARGET8_XST_OPTS)
|
||||
|
||||
$(TARGET9_NAME).bit: TARGET_FPGA = $(TARGET9_FPGA)
|
||||
$(TARGET9_NAME).bit: TARGET_FILES = $(TARGET9_FILES)
|
||||
$(TARGET9_NAME).bit: TARGET_XST_OPTS = $(TARGET9_XST_OPTS)
|
||||
|
||||
$(TARGETS):
|
||||
$(Q)$(MKDIR) $(PREFIX)build_$@
|
||||
$(Q)$(MAKE) -C $(PREFIX)build_$@ -f ../Makefile $(notdir $@).bit
|
||||
|
||||
work:
|
||||
$(Q)$(RM) xst.prj
|
||||
$(Q)for item in $(TARGET_FILES); do echo verilog work ../$$item>>xst.prj; done
|
||||
$(Q)echo work> xst.lso
|
||||
|
||||
%.xst: work
|
||||
$(Q)$(RM) $@
|
||||
$(Q)echo $(TARGET_XST_OPTS)> $@
|
||||
|
||||
%.ngc: %.xst
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] XST $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)xst $(VERBOSITY) -ifn $<
|
||||
|
||||
%.ngd: %.ngc
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] NGD $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)ngdbuild $(VERBOSITY) -quiet -p $(TARGET_FPGA) -nt timestamp -uc ../$(TARGET_FPGA).ucf $< $@
|
||||
|
||||
%_map.ncd: %.ngd
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] MAP $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)map $(VERBOSITY) -p $(TARGET_FPGA) -o $*_map $*
|
||||
|
||||
%.ncd: %_map.ncd
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] PAR $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)par $(PAR_OPTIONS) $(VERBOSITY) -w $< $@
|
||||
|
||||
%.bit: %.ncd
|
||||
$(Q)$(RM) $@ $*.drc $*.rbt
|
||||
$(info [=] BITGEN $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)bitgen $(VERBOSITY) -w $* $@
|
||||
$(Q)python3 ../strip_date_time_from_binary.py $@ || true
|
||||
$(Q)$(CP) $@ ..
|
||||
|
||||
# Build all targets
|
||||
all: $(TARGETS)
|
||||
|
||||
# ALWAYS have some hardcoded text after $(PREFIX) to avoid rm -rf * or rm -rf /* situations if PREFIX is incorrectly set to empty "" or just "/"
|
||||
clean:
|
||||
$(Q)$(RMDIR) $(PREFIX)build_*
|
||||
$(info [-] Build files deleted)
|
||||
|
||||
.DEFAULT:
|
||||
@if [ "$@" != "all" ] && [ ! "$(filter $@,$(TARGETS))" ]; then \
|
||||
make help; \
|
||||
else \
|
||||
make all; \
|
||||
fi
|
||||
|
||||
.PHONY: all help clean
|
||||
$(Q)$(MAKE) --no-print-directory -f $(PM3_MK) clean
|
||||
$(Q)$(MAKE) --no-print-directory -f $(PM5_MK) clean
|
||||
|
||||
help:
|
||||
@echo "################################################################"
|
||||
@echo "# FPGA build dispatcher"
|
||||
@echo "#"
|
||||
@echo "# <target> - Builds only one of the above listed targets"
|
||||
@echo "# all - Builds the FPGA bitstreams for all targets"
|
||||
@echo "# clean - Keeps .bit files but cleans intermediate build files for all targets"
|
||||
@echo "#"
|
||||
@echo "#"
|
||||
@echo "# Valid targets are:"
|
||||
@echo "# $(TARGETS)"
|
||||
@echo "#"
|
||||
@echo "# make - every PM3 image (Xilinx ISE)"
|
||||
@echo "# make pm5 - the PM5 bitstream (yosys/nextpnr-himbaechel)"
|
||||
@echo "# make <target> - routed by name, e.g. fpga_pm3_hf, fpga_pm5"
|
||||
@echo "# make clean - both platforms"
|
||||
@echo "################################################################"
|
||||
$(Q)$(MAKE) --no-print-directory -f $(PM3_MK) help
|
||||
$(Q)$(MAKE) --no-print-directory -f $(PM5_MK) help
|
||||
|
||||
endif
|
||||
@@ -32,7 +32,7 @@ module clk_divider(
|
||||
div_cnt_ <= 8'd0;
|
||||
div_clk_ = !div_clk_;
|
||||
end else
|
||||
div_cnt_ <= div_cnt_ + 1;
|
||||
div_cnt_ <= div_cnt_ + 1'b1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -99,6 +99,10 @@
|
||||
`define FPGA_CMD_SET_DIVISOR 2
|
||||
`define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD 3
|
||||
`define FPGA_CMD_TRACE_ENABLE 2
|
||||
// Definitions for the FPGA commands that are only available on PM5
|
||||
`ifdef PM5
|
||||
`define FPGA_CMD_SET_PWR_PWM_LOW_COUNT 4
|
||||
`endif
|
||||
|
||||
// Major modes
|
||||
`define FPGA_MAJOR_MODE_LF_READER 0
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Pack an ASCII Gowin bitstream (.fs from apycula's gowin_pack) into the binary
|
||||
# form the ARM JTAG loader shifts out for `hw fpga config`.
|
||||
# Every byte in the .fs is 8 ASCII '0'/'1' characters, most significant bit
|
||||
# first; the inverse of apycula's bslib.bytearr(), which is reused here so the
|
||||
# two cannot drift apart.
|
||||
#
|
||||
# usage: fs2bin.py <in.fs> <out.bin>
|
||||
|
||||
import sys
|
||||
|
||||
from apycula.bslib import bytearr
|
||||
|
||||
|
||||
def main(infile, outfile):
|
||||
data = bytearray()
|
||||
with open(infile) as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith("//"):
|
||||
continue
|
||||
if len(line) % 8:
|
||||
sys.exit("{}: line of {} characters is not a multiple of 8".format(infile, len(line)))
|
||||
data += bytearr(line)
|
||||
|
||||
with open(outfile, "wb") as f:
|
||||
f.write(data)
|
||||
|
||||
print("[=] PACK {} ({} bytes)".format(outfile, len(data)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("usage: fs2bin.py <in.fs> <out.bin>")
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Stamp the real device IDCODE into a Gowin bitstream (.fs from apycula's
|
||||
# gowin_pack), in place, and repair the CRC that depends on it.
|
||||
#
|
||||
# apycula's GW1N-4 database writes 0x0100381B, the plain GW1N-4, but the
|
||||
# GW1N-4B/4D silicon on a PM5 reports 0x1100381B. The device compares that
|
||||
# field with its own IDCODE and refuses an image that disagrees: the FPGA stays
|
||||
# unconfigured (Gowin status register done_final = 0), so nothing drives the
|
||||
# antenna and `hw tune` reads a couple of volts instead of tens. `hw fpga
|
||||
# config` prints the value to use here as "gowin_jtag OK: idcode = ...".
|
||||
#
|
||||
# Only the IDCODE changes. The running CRC that apycula's write_bitstream()
|
||||
# carries into the first row covers the header entries, so that one CRC is
|
||||
# recomputed. Every other row is verified rather than rewritten, so a change to
|
||||
# the apycula bitstream layout is reported instead of silently producing an
|
||||
# image that will not configure.
|
||||
#
|
||||
# usage: gowin_devid.py <file.fs> <idcode>
|
||||
|
||||
import sys
|
||||
|
||||
PREAMBLE = 3 # write_bitstream() starts folding header entries into the CRC after these
|
||||
SKIP = 0xd2 # ... and leaves the SPI address entry out
|
||||
|
||||
|
||||
def crc16_arc(data):
|
||||
"""CRC-16/ARC: poly 0x8005 reflected, init 0, xorout 0, as apycula computes it."""
|
||||
crc = 0
|
||||
for byte in data:
|
||||
crc ^= byte
|
||||
for _ in range(8):
|
||||
crc = (crc >> 1) ^ 0xA001 if crc & 1 else crc >> 1
|
||||
return crc
|
||||
|
||||
|
||||
def to_bytes(line):
|
||||
"""8 ASCII '0'/'1' characters per byte, most significant bit first"""
|
||||
return bytearray(int(line[i:i + 8], 2) for i in range(0, len(line), 8))
|
||||
|
||||
|
||||
def to_line(data):
|
||||
return "".join("{:08b}".format(b) for b in data)
|
||||
|
||||
|
||||
def split(blocks):
|
||||
"""header, rows, footer: the rows are the repeated long lines in the middle"""
|
||||
lengths = [len(b) for b in blocks]
|
||||
row_len = max(set(lengths), key=lengths.count)
|
||||
first = next(n for n, b in enumerate(blocks) if len(b) == row_len)
|
||||
last = len(blocks) - 1 - next(n for n, b in enumerate(reversed(blocks)) if len(b) == row_len)
|
||||
head, rows, foot = blocks[:first], blocks[first:last + 1], blocks[last + 1:]
|
||||
if len(head) <= PREAMBLE or not rows or not foot:
|
||||
sys.exit("unexpected .fs layout: {} header, {} row(s), {} footer entries".format(
|
||||
len(head), len(rows), len(foot)))
|
||||
for row in rows:
|
||||
if row[-6:] != b"\xff" * 6:
|
||||
sys.exit("unexpected .fs row: not terminated by {} CRC + 6 padding bytes".format(2))
|
||||
return head, rows, foot
|
||||
|
||||
|
||||
def main(path, idcode):
|
||||
blocks = [to_bytes(l) for l in open(path).read().splitlines() if l]
|
||||
head, rows, foot = split(blocks)
|
||||
|
||||
# the first entry write_bitstream() folds into the CRC, right after the preamble
|
||||
chip = head[PREAMBLE]
|
||||
old = int.from_bytes(chip[-4:], "big")
|
||||
if old == idcode:
|
||||
print("[=] DEVID {} already 0x{:08X}".format(path, idcode))
|
||||
return
|
||||
chip[-4:] = idcode.to_bytes(4, "big")
|
||||
|
||||
# what write_bitstream() has accumulated once it reaches the first row
|
||||
seed = b"".join(bytes(entry) for entry in head[PREAMBLE:] if entry[0] != SKIP)
|
||||
|
||||
for n, row in enumerate(rows):
|
||||
data = bytes(row[:-8])
|
||||
want = crc16_arc(seed + data if n == 0 else b"\xff" * 6 + data)
|
||||
if n == 0:
|
||||
row[-8:-6] = want.to_bytes(2, "little")
|
||||
elif row[-8:-6] != want.to_bytes(2, "little"):
|
||||
sys.exit("row {} CRC mismatch: apycula changed the bitstream layout".format(n))
|
||||
|
||||
with open(path, "w") as f:
|
||||
for block in head + rows + foot:
|
||||
f.write(to_line(block) + "\n")
|
||||
|
||||
print("[=] DEVID {} 0x{:08X} -> 0x{:08X}".format(path, old, idcode))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("usage: gowin_devid.py <file.fs> <idcode>")
|
||||
main(sys.argv[1], int(sys.argv[2], 16))
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Rewrite a Gowin IDE timing constraint file into the subset of SDC that
|
||||
# nextpnr-himbaechel understands: create_clock and set_false_path, both taking
|
||||
# [get_ports {...}]. The // comments, the create_clock -waveform arguments and
|
||||
# the closing report_timing are dropped, and each set_clock_groups
|
||||
# -asynchronous is expanded into pairwise false paths between the ports the
|
||||
# clocks were declared on, since nextpnr has no notion of clock names.
|
||||
#
|
||||
# usage: gowin_sdc_clean.py <in.sdc> <out.sdc>
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
WAVEFORM = re.compile(r"-waveform\s*\{[^}]*\}")
|
||||
CLOCK = re.compile(r"create_clock\s+(?:.*?-name\s+(\S+))?.*?\[get_ports\s*\{([^}]*)\}\]")
|
||||
CLOCK_GROUP = re.compile(r"\[get_clocks\s*\{([^}]*)\}\]")
|
||||
|
||||
|
||||
def read_clocks(lines):
|
||||
""""clock name -> port it is declared on" for the create_clock entries"""
|
||||
ports = {}
|
||||
for line in lines:
|
||||
m = CLOCK.match(line.strip())
|
||||
if m and m.group(1):
|
||||
ports[m.group(1)] = m.group(2)
|
||||
return ports
|
||||
|
||||
|
||||
def clean(line, clocks):
|
||||
if line.startswith("//") or not line.strip():
|
||||
return []
|
||||
if re.match(r"\s*report_timing\b", line):
|
||||
return []
|
||||
if re.match(r"\s*set_clock_groups\b", line):
|
||||
if "-asynchronous" not in line:
|
||||
return [line]
|
||||
# every pair of groups is asynchronous, not just the neighbours
|
||||
ports = [clocks.get(name, name) for name in CLOCK_GROUP.findall(line)]
|
||||
return ["set_false_path -from [get_ports {%s}] -to [get_ports {%s}]" % (a, b)
|
||||
for i, a in enumerate(ports) for b in ports[i + 1:]]
|
||||
return [WAVEFORM.sub("", line).rstrip()]
|
||||
|
||||
|
||||
def main(infile, outfile):
|
||||
with open(infile) as f:
|
||||
lines = f.read().splitlines()
|
||||
clocks = read_clocks(lines)
|
||||
rules = [out for line in lines for out in clean(line, clocks)]
|
||||
with open(outfile, "w") as f:
|
||||
f.write("\n".join(rules) + "\n")
|
||||
|
||||
print("[=] SDC {} ({} rules, {} clocks)".format(outfile, len(rules), len(clocks)))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
sys.exit("usage: gowin_sdc_clean.py <in.sdc> <out.sdc>")
|
||||
main(sys.argv[1], sys.argv[2])
|
||||
@@ -200,7 +200,7 @@ reg zero = 1'b0; // Manchester first halfbit low second high corresponds to this
|
||||
reg [8:0] ssp_cnt = 9'd0;
|
||||
|
||||
always @(posedge adc_clk)
|
||||
ssp_cnt <= (ssp_cnt + 1);
|
||||
ssp_cnt <= (ssp_cnt + 1'b1);
|
||||
|
||||
always @(negedge adc_clk)
|
||||
if (ssp_cnt[5:0] == 6'd32)
|
||||
@@ -242,9 +242,26 @@ end
|
||||
//maybe change it so that ARM sends preamble as well.
|
||||
//then: ready bits sent to ARM, 8 bits sent from ARM (all ones), then preamble (all zeros, presumably) - which starts modulation
|
||||
|
||||
// SSP(FRAME_H) ~speed -> 9'd31 speed -> 8'd15
|
||||
// SSP(FRAME_L) ~speed -> 9'b1011111(d95 OF 512) speed -> 8'b101111(d47 OF 256)
|
||||
// TIMODE(FRAME_H) ~speed -> 9'd(512-64) speed -> 8'd(256-32)
|
||||
// TIMODE(FRAME_L) ~speed -> 9'b0 speed -> 8'b0
|
||||
`ifdef WITH_TIMODE
|
||||
`define HI_FLITE_FRAME_SET_L 9'd448
|
||||
`define HI_FLITE_FRAME_SET_H 8'd224
|
||||
`define HI_FLITE_FRAME_CLR_L 9'd0
|
||||
`define HI_FLITE_FRAME_CLR_H 8'd0
|
||||
`else
|
||||
`define HI_FLITE_FRAME_SET_L 9'd31
|
||||
`define HI_FLITE_FRAME_SET_H 8'd15
|
||||
`define HI_FLITE_FRAME_CLR_L 9'b1011111 // 95 = 96 - 1
|
||||
`define HI_FLITE_FRAME_CLR_H 8'b101111 // 47 = 48 - 1
|
||||
`endif
|
||||
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
//count fc/64 - transfer bits to ARM at the rate they are received
|
||||
//At speed 0, clk rises at 0 or 64; at speed 1, clk rises at 0 or 32.
|
||||
if( ((~speed) && (ssp_cnt[5:0] == 6'b000000) ) || (speed && (ssp_cnt[4:0] == 5'b00000)) )
|
||||
begin
|
||||
ssp_clk <= 1'b1;
|
||||
@@ -270,15 +287,16 @@ begin
|
||||
ssp_din <= curbit;
|
||||
end
|
||||
end
|
||||
// At speed 0, clk falls at 32; at speed 1, clk falls at 16.
|
||||
if( ( (~speed) && (ssp_cnt[5:0] == 6'b100000)) ||(speed && ssp_cnt[4:0] == 5'b10000))
|
||||
ssp_clk <= 1'b0;
|
||||
//create frame pulses. TBH, I still don't know what they do exactly, but they are crucial for ARM->FPGA transfer. If the frame is in the beginning of the byte, transfer slows to a crawl for some reason
|
||||
// took me a day to figure THAT out.
|
||||
if(( (~speed) && (ssp_cnt[8:0] == 9'd31)) || (speed && ssp_cnt[7:0] == 8'd15))
|
||||
if(( (~speed) && (ssp_cnt[8:0] == `HI_FLITE_FRAME_SET_L)) || (speed && ssp_cnt[7:0] == `HI_FLITE_FRAME_SET_H))
|
||||
begin
|
||||
ssp_frame <= 1'b1;
|
||||
end
|
||||
if(( (~speed) && (ssp_cnt[8:0] == 9'b1011111)) || (speed &&ssp_cnt[7:0] == 8'b101111) )
|
||||
if(( (~speed) && (ssp_cnt[8:0] == `HI_FLITE_FRAME_CLR_L)) || (speed && ssp_cnt[7:0] == `HI_FLITE_FRAME_CLR_H))
|
||||
begin
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
@@ -315,7 +333,7 @@ begin
|
||||
// Watchdog clock, see `stalebit. It runs first so that the three places
|
||||
// that clear it below - a clean desync, a successful Manchester lock, and
|
||||
// the watchdog firing itself - all override it.
|
||||
stale <= stale + 1;
|
||||
stale <= stale + 1'b1;
|
||||
|
||||
//envelope follow code...
|
||||
////////////
|
||||
@@ -338,7 +356,7 @@ begin
|
||||
if (power)
|
||||
bit_counts <= 0;
|
||||
else
|
||||
bit_counts <= bit_counts + 1;
|
||||
bit_counts <= bit_counts + 1'b1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
@@ -348,7 +366,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
fccount <= fccount + 1;
|
||||
fccount <= fccount + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -453,7 +471,7 @@ begin
|
||||
acc <= 0;
|
||||
end
|
||||
else
|
||||
tsinceedge <= (tsinceedge + 1);
|
||||
tsinceedge <= (tsinceedge + 1'b1);
|
||||
end
|
||||
end
|
||||
|
||||
@@ -541,7 +559,11 @@ always @(ck_1356meg or ssp_dout or power or disabl or mod)
|
||||
begin
|
||||
if (power)
|
||||
begin
|
||||
`ifdef PM5 // pwr_hi is used to control the 1356 clk enable on PM5, not clk output.
|
||||
pwr_hi <= 1'b1; // enable 1356 clk output
|
||||
`else
|
||||
pwr_hi <= ck_1356meg;
|
||||
`endif
|
||||
pwr_lo <= 1'b0;
|
||||
pwr_oe1 <= 1'b0;//mod;
|
||||
pwr_oe2 <= 1'b0;//mod;
|
||||
@@ -29,7 +29,7 @@ module hi_get_trace(
|
||||
reg [6:0] clock_cnt;
|
||||
always @(negedge ck_1356megb)
|
||||
begin
|
||||
clock_cnt <= clock_cnt + 1;
|
||||
clock_cnt <= clock_cnt + 1'b1;
|
||||
end
|
||||
|
||||
// sample at 13,56MHz / 8. The highest signal frequency (subcarrier) is 848,5kHz, i.e. in this case we oversample by a factor of 2
|
||||
@@ -39,7 +39,7 @@ begin
|
||||
if (sample_clock == 3'd7)
|
||||
sample_clock <= 3'd0;
|
||||
else
|
||||
sample_clock <= sample_clock + 1;
|
||||
sample_clock <= sample_clock + 1'b1;
|
||||
end
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ begin
|
||||
if (addr == 12'd3071)
|
||||
addr <= 12'd0;
|
||||
else
|
||||
addr <= addr + 1;
|
||||
addr <= addr + 1'b1;
|
||||
end
|
||||
end
|
||||
else if (major_mode != `FPGA_MAJOR_MODE_OFF)
|
||||
@@ -89,7 +89,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
addr <= addr + 1;
|
||||
addr <= addr + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -156,10 +156,21 @@ begin
|
||||
|
||||
ssp_clk <= ~clock_cnt[3]; // ssp_clk frequency = 13,56MHz / 16 = 847,5 kHz
|
||||
|
||||
`ifdef WITH_TIMODE
|
||||
// Toggle once every 8 ssp_clk cycles
|
||||
// TI mode 7D112: starts with the TI frame start signal; the line is driven high at the (128-16)th ssp_clk cycle.
|
||||
if(clock_cnt[6:0] == 7'd112)
|
||||
ssp_frame <= 1'b1;
|
||||
|
||||
// Clear the frame signal at the new frame start (0th ssp_clk cycle)
|
||||
if(clock_cnt[6:0] == 7'd0)
|
||||
ssp_frame <= 1'b0;
|
||||
`else
|
||||
if (clock_cnt[6:4] == 3'b000) // set ssp_frame for 0...31
|
||||
ssp_frame <= 1'b1;
|
||||
else
|
||||
ssp_frame <= 1'b0;
|
||||
`endif
|
||||
|
||||
end
|
||||
|
||||
@@ -36,7 +36,12 @@ module hi_iso14443a(
|
||||
output debug
|
||||
);
|
||||
|
||||
// The PM5's ADC is inverting (for HF).
|
||||
`ifdef PM5
|
||||
assign adc_clk = ~ck_1356meg;
|
||||
`else
|
||||
assign adc_clk = ck_1356meg;
|
||||
`endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reader -> PM3:
|
||||
@@ -47,8 +52,16 @@ reg [11:0] has_been_low_for;
|
||||
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
if(adc_d >= 16) after_hysteresis <= 1'b1; // U >= 1,14V -> after_hysteresis = 1
|
||||
else if(adc_d < 8) after_hysteresis <= 1'b0; // U < 1,04V -> after_hysteresis = 0
|
||||
|
||||
// The PM5's ADC is inverting (for HF). Therefore, the trigger values are inverted as well.
|
||||
`ifdef PM5
|
||||
if(adc_d <= 240) after_hysteresis <= 1'b1; // U >= 1,14V -> after_hysteresis = 1
|
||||
else if(adc_d > 248) after_hysteresis <= 1'b0; // U < 1,04V -> after_hysteresis = 0
|
||||
`else
|
||||
if(adc_d >= 16) after_hysteresis <= 1'b1; // U >= 1,14V -> after_hysteresis = 1
|
||||
else if(adc_d < 8) after_hysteresis <= 1'b0; // U < 1,04V -> after_hysteresis = 0
|
||||
`endif
|
||||
|
||||
// Note: was >= 3,53V and <= 1,19V. The new trigger values allow more reliable detection of the first bit
|
||||
// (it might not reach 3,53V due to the high time constant of the high pass filter in the analogue RF part).
|
||||
// In addition, the new values are more in line with ISO14443-2: "The PICC shall detect the ”End of Pause” after the field exceeds
|
||||
@@ -57,7 +70,11 @@ begin
|
||||
|
||||
// detecting a loss of reader's field (adc_d < 192 for 4096 clock cycles). If this is the case,
|
||||
// set the detected reader signal (after_hysteresis) to '1' (unmodulated)
|
||||
`ifdef PM5
|
||||
if(adc_d <= 32)
|
||||
`else
|
||||
if(adc_d >= 192)
|
||||
`endif
|
||||
begin
|
||||
has_been_low_for <= 12'd0;
|
||||
end
|
||||
@@ -70,7 +87,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
has_been_low_for <= has_been_low_for + 1;
|
||||
has_been_low_for <= has_been_low_for + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -86,7 +103,11 @@ reg [8:0] saw_deep_modulation;
|
||||
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
`ifdef PM5
|
||||
if(& adc_d[7:0]) // if adc_d == 255 (ADC Value of PM5 is inverted)
|
||||
`else
|
||||
if(~(| adc_d[7:0])) // if adc_d == 0 (U <= 0,94V)
|
||||
`endif
|
||||
begin
|
||||
if(deep_counter == 3'd7) // adc_d == 0 for 8 adc_clk ticks -> deep_modulation (by reader)
|
||||
begin
|
||||
@@ -94,7 +115,7 @@ begin
|
||||
saw_deep_modulation <= 8'd0;
|
||||
end
|
||||
else
|
||||
deep_counter <= deep_counter + 1;
|
||||
deep_counter <= deep_counter + 1'b1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
@@ -102,7 +123,7 @@ begin
|
||||
if(saw_deep_modulation == 8'd255) // adc_d != 0 for 256 adc_clk ticks -> deep_modulation is over, probably waiting for tag's response
|
||||
deep_modulation <= 1'b0;
|
||||
else
|
||||
saw_deep_modulation <= saw_deep_modulation + 1;
|
||||
saw_deep_modulation <= saw_deep_modulation + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -153,7 +174,7 @@ begin
|
||||
begin
|
||||
if (reader_falling_edge_time == 4'd1) // reader signal changes right after sampling. Better sample earlier next time.
|
||||
begin
|
||||
negedge_cnt <= negedge_cnt + 2; // time warp
|
||||
negedge_cnt <= negedge_cnt + 2'd2; // time warp
|
||||
end
|
||||
else if (reader_falling_edge_time == 4'd0) // reader signal changes right before sampling. Better sample later next time.
|
||||
begin
|
||||
@@ -161,7 +182,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
negedge_cnt <= negedge_cnt + 1; // Continue as usual
|
||||
negedge_cnt <= negedge_cnt + 1'b1; // Continue as usual
|
||||
end
|
||||
reader_falling_edge_time[3:0] <= 4'd8; // adjust only once per detected edge
|
||||
end
|
||||
@@ -171,7 +192,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
negedge_cnt <= negedge_cnt + 1;
|
||||
negedge_cnt <= negedge_cnt + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -221,7 +242,7 @@ begin
|
||||
if (mod_type == `FPGA_HF_ISO14443A_SNIFFER)
|
||||
begin
|
||||
// detect modulation signal: if modulating, there must have been a falling AND a rising edge
|
||||
if ((rx_mod_falling_edge_max > edge_detect_threshold_high) && (rx_mod_rising_edge_max < -edge_detect_threshold_high))
|
||||
if ((rx_mod_falling_edge_max > $signed(edge_detect_threshold_high)) && (rx_mod_rising_edge_max < -$signed(edge_detect_threshold_high)))
|
||||
curbit <= 1'b1; // modulation
|
||||
else
|
||||
curbit <= 1'b0; // no modulation
|
||||
@@ -229,7 +250,7 @@ begin
|
||||
else
|
||||
begin
|
||||
// detect modulation signal: if modulating, there must have been a falling AND a rising edge
|
||||
if ((rx_mod_falling_edge_max > edge_detect_threshold) && (rx_mod_rising_edge_max < -edge_detect_threshold))
|
||||
if ((rx_mod_falling_edge_max > $signed(edge_detect_threshold)) && (rx_mod_rising_edge_max < -$signed(edge_detect_threshold)))
|
||||
curbit <= 1'b1; // modulation
|
||||
else
|
||||
curbit <= 1'b0; // no modulation
|
||||
@@ -341,12 +362,12 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
sub_carrier_cnt <= sub_carrier_cnt + 1;
|
||||
sub_carrier_cnt <= sub_carrier_cnt + 1'b1;
|
||||
end
|
||||
end
|
||||
else
|
||||
begin
|
||||
fdt_counter <= fdt_counter + 1;
|
||||
fdt_counter <= fdt_counter + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -407,7 +428,7 @@ begin
|
||||
if (mod_sig_ptr == 5'd31)
|
||||
mod_sig_ptr <= 5'd0; // buffer overflow - data loss.
|
||||
else
|
||||
mod_sig_ptr <= mod_sig_ptr + 1; // increase buffer (= increase delay by 16 adc_clk ticks). mod_sig_ptr always points ahead of first 1.
|
||||
mod_sig_ptr <= mod_sig_ptr + 1'b1; // increase buffer (= increase delay by 16 adc_clk ticks). mod_sig_ptr always points ahead of first 1.
|
||||
else if(fdt_elapsed && ~temp_buffer_reset)
|
||||
begin
|
||||
// wait for the next 1 after fdt_elapsed before fixing the delay and starting modulation. This ensures that the response can only happen
|
||||
@@ -417,7 +438,7 @@ begin
|
||||
if(mod_sig_ptr == 5'd1)
|
||||
mod_sig_ptr <= 5'd8; // still nothing received, need to go for the next interval
|
||||
else
|
||||
mod_sig_ptr <= mod_sig_ptr - 1; // decrease buffer.
|
||||
mod_sig_ptr <= mod_sig_ptr - 1'b1; // decrease buffer.
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -472,32 +493,47 @@ end
|
||||
// FPGA <-> ARM communication:
|
||||
// generate a ssp clock and ssp frame signal for the synchronous transfer from/to the ARM
|
||||
|
||||
`ifdef WITH_TIMODE
|
||||
`define HI_ISO14443A_FRAME_SET_8B 6'd56
|
||||
`define HI_ISO14443A_FRAME_CLR_8B 6'd0
|
||||
`define HI_ISO14443A_FRAME_SET_16B 7'd112
|
||||
`define HI_ISO14443A_FRAME_CLR_16B 7'd0
|
||||
`else
|
||||
`define HI_ISO14443A_FRAME_SET_8B 6'd0
|
||||
`define HI_ISO14443A_FRAME_CLR_8B 6'd8
|
||||
`define HI_ISO14443A_FRAME_SET_16B 7'd7
|
||||
`define HI_ISO14443A_FRAME_CLR_16B 7'd23
|
||||
`endif
|
||||
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
if(mod_type == `FPGA_HF_ISO14443A_SNIFFER)
|
||||
// FPGA_HF_ISO14443A_SNIFFER mode (ssp_clk = adc_clk / 8, ssp_frame clock = adc_clk / 64)):
|
||||
begin
|
||||
if(negedge_cnt[2:0] == 3'd0)
|
||||
if(negedge_cnt[2:0] == 3'd0) // High level for 4 cycles
|
||||
ssp_clk <= 1'b1;
|
||||
if(negedge_cnt[2:0] == 3'd4)
|
||||
if(negedge_cnt[2:0] == 3'd4)// Low level for 4 cycles
|
||||
ssp_clk <= 1'b0;
|
||||
|
||||
if(negedge_cnt[5:0] == 6'd0) // ssp_frame rising edge indicates start of frame
|
||||
//ssp模式6D0,TI模式6D56,TI帧开始信号开始:最后一个ssp_clk拉高(64-8)的时候就拉高
|
||||
// ssp_frame rising edge indicates start of frame
|
||||
if(negedge_cnt[5:0] == `HI_ISO14443A_FRAME_SET_8B)
|
||||
ssp_frame <= 1'b1;
|
||||
if(negedge_cnt[5:0] == 6'd8)
|
||||
//ssp模式6D8,TI模式6D0,TI帧开始信号结束:在第一次ssp_clk拉高的时候就拉低(清除上一个周期拉高)
|
||||
if(negedge_cnt[5:0] == `HI_ISO14443A_FRAME_CLR_8B)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
else
|
||||
// all other modes (ssp_clk = adc_clk / 16, ssp_frame clock = adc_clk / 128):
|
||||
begin
|
||||
if(negedge_cnt[3:0] == 4'd0)
|
||||
if(negedge_cnt[3:0] == 4'd0) // High level for 8 cycles (rising edge at cycle 0 or 16)
|
||||
ssp_clk <= 1'b1;
|
||||
if(negedge_cnt[3:0] == 4'd8)
|
||||
if(negedge_cnt[3:0] == 4'd8) // Low level for 8 cycles (falling edge at cycle 8)
|
||||
ssp_clk <= 1'b0;
|
||||
|
||||
if(negedge_cnt[6:0] == 7'd7) // ssp_frame rising edge indicates start of frame, sampled on falling edge of ssp_clk
|
||||
if(negedge_cnt[6:0] == `HI_ISO14443A_FRAME_SET_16B) // ssp_frame rising edge indicates start of frame, sampled on falling edge of ssp_clk
|
||||
ssp_frame <= 1'b1;
|
||||
if(negedge_cnt[6:0] == 7'd23)
|
||||
if(negedge_cnt[6:0] == `HI_ISO14443A_FRAME_CLR_16B)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
end
|
||||
@@ -544,7 +580,12 @@ assign sub_carrier = ~sub_carrier_cnt[3];
|
||||
|
||||
// in FPGA_HF_ISO14443A_READER_MOD: drop carrier for mod_sig_coil == 1 (pause);
|
||||
// in FPGA_HF_ISO14443A_READER_LISTEN: carrier always on; in other modes: carrier always off
|
||||
|
||||
`ifdef PM5
|
||||
assign pwr_hi = (((mod_type == `FPGA_HF_ISO14443A_READER_MOD) & ~mod_sig_coil) || (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN));
|
||||
`else
|
||||
assign pwr_hi = (ck_1356meg & (((mod_type == `FPGA_HF_ISO14443A_READER_MOD) & ~mod_sig_coil) || (mod_type == `FPGA_HF_ISO14443A_READER_LISTEN)));
|
||||
`endif
|
||||
|
||||
// Enable HF antenna drivers:
|
||||
assign pwr_oe1 = 1'b0;
|
||||
@@ -42,15 +42,31 @@ assign adc_clk = ck_1356meg; // sample frequency is 13,56 MHz
|
||||
// using AM. Do this the same way that we do it for the simulated tag.
|
||||
reg after_hysteresis, after_hysteresis_prev, after_hysteresis_prev_prev;
|
||||
reg [11:0] has_been_low_for;
|
||||
|
||||
// Reader-carrier detection thresholds, selected at runtime by the configured mode.
|
||||
// The lower thresholds (240 / 63 instead of 255 / 0) allow the reader signal to still be recovered
|
||||
// when the received level is weak. They are meant for ISO15693: the 2-subcarrier modes, plus the
|
||||
// amplitude sniffer that "hf 15 sniff" uses -- including the plain SNIFF_AMPLITUDE that its jam
|
||||
// sequence switches to and keeps for the rest of the command.
|
||||
// This module is shared with other protocols (ISO14443B sniff reads the very same
|
||||
// after_hysteresis bits) and must keep the rail-to-rail thresholds there, so the selection is done
|
||||
// at runtime instead of with a compile-time switch.
|
||||
wire low_signal_mode = (subcarrier_frequency == `FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ)
|
||||
|| (minor_mode == `FPGA_HF_READER_MODE_SNIFF_AMPLITUDE);
|
||||
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
`ifdef WITH_HF_15_LOWSIGNAL
|
||||
if (& adc_d[7:4]) after_hysteresis <= 1'b1;
|
||||
else if (~(| adc_d[7:6])) after_hysteresis <= 1'b0;
|
||||
`else
|
||||
if (& adc_d[7:0]) after_hysteresis <= 1'b1;
|
||||
else if (~(| adc_d[7:0])) after_hysteresis <= 1'b0;
|
||||
`endif
|
||||
// TODO DXL - 阈值是绝对值,这是否会在PM5上导致问题?需要考虑判定的adc值是否和场的信号同步,待测
|
||||
if (low_signal_mode)
|
||||
begin
|
||||
if (& adc_d[7:4]) after_hysteresis <= 1'b1;
|
||||
else if (~(| adc_d[7:6])) after_hysteresis <= 1'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (& adc_d[7:0]) after_hysteresis <= 1'b1;
|
||||
else if (~(| adc_d[7:0])) after_hysteresis <= 1'b0;
|
||||
end
|
||||
|
||||
if (after_hysteresis)
|
||||
begin
|
||||
@@ -64,7 +80,7 @@ begin
|
||||
after_hysteresis <= 1'b1;
|
||||
end
|
||||
else
|
||||
has_been_low_for <= has_been_low_for + 1;
|
||||
has_been_low_for <= has_been_low_for + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -75,7 +91,7 @@ end
|
||||
// We need a 6-bit counter for the timing.
|
||||
reg [5:0] corr_i_cnt;
|
||||
always @(negedge adc_clk)
|
||||
corr_i_cnt <= corr_i_cnt + 1;
|
||||
corr_i_cnt <= corr_i_cnt + 1'b1;
|
||||
|
||||
`ifdef WITH_HF_15
|
||||
reg [1:0] fskout = 2'd0;
|
||||
@@ -147,8 +163,8 @@ begin
|
||||
begin
|
||||
if (match28 < match32)
|
||||
begin
|
||||
diff28 = match32 - match28;
|
||||
diff16 = match16 - match28;
|
||||
diff28 = (match32 - match28) & 8'hFF;
|
||||
diff16 = (match16 - match28) & 8'hFF;
|
||||
if (diff28*2 > diff16)
|
||||
fskout = 2'b01;
|
||||
else if (!last0)
|
||||
@@ -158,8 +174,8 @@ begin
|
||||
end
|
||||
else //if (match32 <= match28)
|
||||
begin
|
||||
diff32 = match28 - match32;
|
||||
diff16 = match16 - match32;
|
||||
diff32 = (match28 - match32) & 8'hFF;
|
||||
diff16 = (match16 - match32) & 8'hFF;
|
||||
if (diff32*2 > diff16)
|
||||
fskout = 2'b10;
|
||||
else if (!last0)
|
||||
@@ -368,7 +384,15 @@ begin
|
||||
|
||||
end
|
||||
|
||||
// ssp clock and frame signal for communication to and from ARM
|
||||
`ifdef WITH_TIMODE // In TI mode, the frame signal goes high during the last `ssp_clk` pulse (at the 60th clock cycle, i.e., 64-4).
|
||||
`define HI_READER_FRAME_SET 6'd60
|
||||
`define HI_READER_FRAME_CLR 6'd0
|
||||
`else
|
||||
`define HI_READER_FRAME_SET 6'd1
|
||||
`define HI_READER_FRAME_CLR 6'd3
|
||||
`endif
|
||||
|
||||
// ssp clock and frame signal for communication to and from ARM, not SPI TI-MODE
|
||||
// _____ _____ _____ _
|
||||
// ssp_clk | |_____| |_____| |_____|
|
||||
// _____
|
||||
@@ -380,18 +404,18 @@ end
|
||||
//
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
if (corr_i_cnt[1:0] == 2'b00)
|
||||
if (corr_i_cnt[1:0] == 2'b00) // High level for 2 cycles (rising edge at cycle 0 or 4)
|
||||
ssp_clk <= 1'b1;
|
||||
|
||||
if (corr_i_cnt[1:0] == 2'b10)
|
||||
if (corr_i_cnt[1:0] == 2'b10) // Low level for 2 cycles (falling edge at the second cycle)
|
||||
ssp_clk <= 1'b0;
|
||||
|
||||
// set ssp_frame signal for corr_i_cnt = 1..3
|
||||
// (send one frame with 16 Bits)
|
||||
if (corr_i_cnt == 6'd1)
|
||||
if (corr_i_cnt == `HI_READER_FRAME_SET)
|
||||
ssp_frame <= 1'b1;
|
||||
|
||||
if (corr_i_cnt == 6'd3)
|
||||
if (corr_i_cnt == `HI_READER_FRAME_CLR)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
|
||||
@@ -405,11 +429,15 @@ always @(negedge adc_clk)
|
||||
begin
|
||||
if (corr_i_cnt == 6'd0)
|
||||
begin
|
||||
jam_counter <= jam_counter + 1;
|
||||
jam_counter <= jam_counter + 1'b1;
|
||||
jam_signal <= jam_counter[1] ^ jam_counter[3];
|
||||
end
|
||||
end
|
||||
|
||||
// ---
|
||||
// pwr_hi is used to control the 1356 clk enable on PM5, not clk output.
|
||||
// ---
|
||||
|
||||
always @(*)
|
||||
begin
|
||||
pwr_oe1 = 1'b0;
|
||||
@@ -417,22 +445,38 @@ begin
|
||||
|
||||
if (minor_mode == `FPGA_HF_READER_MODE_SEND_SHALLOW_MOD)
|
||||
begin
|
||||
`ifdef PM5
|
||||
pwr_hi = 1'b1;
|
||||
`else
|
||||
pwr_hi = ck_1356meg;
|
||||
`endif
|
||||
pwr_oe4 = ssp_dout;
|
||||
end
|
||||
else if (minor_mode == `FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4)
|
||||
begin
|
||||
`ifdef PM5
|
||||
pwr_hi = 1'b1;
|
||||
`else
|
||||
pwr_hi = ck_1356meg;
|
||||
`endif
|
||||
pwr_oe1 = ssp_dout;
|
||||
end
|
||||
else if (minor_mode == `FPGA_HF_READER_MODE_SEND_FULL_MOD)
|
||||
begin
|
||||
`ifdef PM5
|
||||
pwr_hi = ~ssp_dout;
|
||||
`else
|
||||
pwr_hi = ck_1356meg & ~ssp_dout;
|
||||
`endif
|
||||
pwr_oe4 = 1'b0;
|
||||
end
|
||||
else if (minor_mode == `FPGA_HF_READER_MODE_SEND_JAM)
|
||||
begin
|
||||
`ifdef PM5
|
||||
pwr_hi = jam_signal;
|
||||
`else
|
||||
pwr_hi = ck_1356meg & jam_signal;
|
||||
`endif
|
||||
pwr_oe4 = 1'b0;
|
||||
end
|
||||
else if (minor_mode == `FPGA_HF_READER_MODE_SNIFF_IQ
|
||||
@@ -444,7 +488,11 @@ begin
|
||||
end
|
||||
else // receiving from tag
|
||||
begin
|
||||
`ifdef PM5
|
||||
pwr_hi = 1'b1;
|
||||
`else
|
||||
pwr_hi = ck_1356meg;
|
||||
`endif
|
||||
pwr_oe4 = 1'b0;
|
||||
end
|
||||
end
|
||||
@@ -83,7 +83,7 @@ begin
|
||||
end
|
||||
else
|
||||
begin
|
||||
has_been_low_for <= has_been_low_for + 1;
|
||||
has_been_low_for <= has_been_low_for + 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -93,7 +93,7 @@ end
|
||||
reg [8:0] ssp_clk_divider;
|
||||
|
||||
always @(negedge adc_clk)
|
||||
ssp_clk_divider <= (ssp_clk_divider + 1);
|
||||
ssp_clk_divider <= (ssp_clk_divider + 1'b1);
|
||||
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
@@ -108,22 +108,33 @@ begin
|
||||
ssp_clk <= ~ssp_clk_divider[4];
|
||||
end
|
||||
|
||||
// Since the 212k clock is generated by ssp_clk_divider[5], a full cycle here involves 16 bits—effectively 8 clock periods.
|
||||
// This means an 8-bit transfer; in SSP mode, the frame signal is driven high at bit 1 (clock rising edge) and low at bit 5 (clock falling edge).
|
||||
// To switch to TI mode, this must be changed to the rising edge of the final cycle: driving high at bit 14 (rising edge) and low at bit 0 (falling edge).
|
||||
`ifdef WITH_TIMODE
|
||||
`define HI_SIMULATE_FRAME_SET_212K 4'd14
|
||||
`define HI_SIMULATE_FRAME_CLR_212K 4'd0
|
||||
`else
|
||||
`define HI_SIMULATE_FRAME_SET_212K 4'd1
|
||||
`define HI_SIMULATE_FRAME_CLR_212K 4'd5
|
||||
`endif
|
||||
|
||||
// Produce the byte framing signal; the phase of this signal
|
||||
// is arbitrary, because it's just a bit stream in this module.
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K)
|
||||
begin
|
||||
if (ssp_clk_divider[8:5] == 4'd1)
|
||||
if (ssp_clk_divider[8:5] == `HI_SIMULATE_FRAME_SET_212K)
|
||||
ssp_frame <= 1'b1;
|
||||
if (ssp_clk_divider[8:5] == 4'd5)
|
||||
if (ssp_clk_divider[8:5] == `HI_SIMULATE_FRAME_CLR_212K)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (ssp_clk_divider[7:4] == 4'd1)
|
||||
if (ssp_clk_divider[7:4] == `HI_SIMULATE_FRAME_SET_212K)
|
||||
ssp_frame <= 1'b1;
|
||||
if (ssp_clk_divider[7:4] == 4'd5)
|
||||
if (ssp_clk_divider[7:4] == `HI_SIMULATE_FRAME_CLR_212K)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
end
|
||||
@@ -48,18 +48,36 @@ assign ssp_din = adc_d_out[0];
|
||||
|
||||
always @(posedge ssp_clk)
|
||||
begin
|
||||
ssp_cnt <= ssp_cnt + 1;
|
||||
ssp_cnt <= ssp_cnt + 1'b1;
|
||||
|
||||
`ifdef WITH_TIMODE
|
||||
if(ssp_cnt[2:0] == 3'b111) // set frame pin to high for TI mode on the last bit of the frame
|
||||
begin
|
||||
ssp_frame <= 1'b1;
|
||||
end
|
||||
`endif
|
||||
|
||||
if(ssp_cnt[2:0] == 3'b000) // set frame length
|
||||
begin
|
||||
adc_d_out[7:0] <= adc_d;
|
||||
ssp_frame <= 1'b1;
|
||||
|
||||
`ifdef WITH_TIMODE
|
||||
ssp_frame <= 1'b0; // In TI mode, pull the frame signal low here—at the rising edge of the first bit (marking the end of the last bit).
|
||||
`else
|
||||
ssp_frame <= 1'b1; // In SSP mode, pull the frame signal high here.
|
||||
`endif
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
begin
|
||||
// shift value right one bit
|
||||
adc_d_out[7:0] <= {1'b0, adc_d_out[7:1]};
|
||||
ssp_frame <= 1'b0;
|
||||
|
||||
`ifndef WITH_TIMODE
|
||||
ssp_frame <= 1'b0; // In SSP mode, pull the frame signal low here.
|
||||
`endif
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
File renamed without changes.
@@ -30,7 +30,7 @@ module lo_adc(
|
||||
input ssp_dout,
|
||||
|
||||
output ssp_din,
|
||||
output ssp_frame,
|
||||
output reg ssp_frame,
|
||||
output ssp_clk,
|
||||
output adc_clk,
|
||||
output pwr_lo,
|
||||
@@ -66,7 +66,14 @@ assign pwr_oe4 = tag_modulation & lf_weak_load; // 10k Ohms
|
||||
assign debug = adc_clk;
|
||||
|
||||
// ADC clock out of phase with antenna driver
|
||||
`ifdef PM5
|
||||
// Since the PM5's ADC sampling occurs on the rising edge,
|
||||
// whereas the falling edge of the field signal causes a rapid voltage rise—making it unsuitable for sampling
|
||||
// the phase was adjusted to align them.
|
||||
assign adc_clk = clk_state;
|
||||
`else
|
||||
assign adc_clk = ~clk_state;
|
||||
`endif
|
||||
|
||||
// serialized SSP data is gated by clk_state to suppress unwanted signal
|
||||
assign ssp_din = to_arm_shiftreg[7] && !clk_state;
|
||||
@@ -74,20 +81,40 @@ assign ssp_din = to_arm_shiftreg[7] && !clk_state;
|
||||
// SSP clock always runs at 24MHz
|
||||
assign ssp_clk = pck0;
|
||||
|
||||
// SSP frame is gated by clk_state and goes high when pck_divider=8..15
|
||||
assign ssp_frame = (pck_divider[7:3] == 5'd1) && !clk_state;
|
||||
// ssp_frame window: TI = 1-cycle pulse (at divider 7), SSP = spans the entire data window (dividers 8–15).
|
||||
// Note: The 'always' block below reads the value of pck_divider *before* it toggles for the current cycle,
|
||||
// so SET is triggered at the window start minus 1, and CLR is triggered at the window end.
|
||||
`ifdef WITH_TIMODE
|
||||
`define LO_ADC_FRAME_SET 8'd6
|
||||
`define LO_ADC_FRAME_CLR 8'd7
|
||||
`else
|
||||
`define LO_ADC_FRAME_SET 8'd7
|
||||
`define LO_ADC_FRAME_CLR 8'd15
|
||||
`endif
|
||||
|
||||
// Frame signal generation.
|
||||
always @(posedge pck0)
|
||||
begin
|
||||
if ((pck_divider == `LO_ADC_FRAME_SET) && !clk_state)
|
||||
ssp_frame <= 1'b1;
|
||||
if ((pck_divider == `LO_ADC_FRAME_CLR) && !clk_state)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
|
||||
// divide 24mhz down to 3mhz
|
||||
always @(posedge pck0)
|
||||
always @(posedge pck0) // SSPCLK rising edge
|
||||
begin
|
||||
if (pck_divider == divisor[7:0])
|
||||
begin
|
||||
// Resets to zero upon reaching the frequency division factor; effectively, the value 95 corresponds to 125 kHz, meaning it executes 96 times (counting from 0 to 95) and resets on the 96th iteration.
|
||||
// After resetting, the cycle count becomes 0 and does not increment.
|
||||
pck_divider <= 8'd0;
|
||||
// Toggle clk upon reaching the frequency division factor; the value 95 (corresponding to 125 kHz) actually implies operation at a frequency of 250 kHz.
|
||||
clk_state = !clk_state;
|
||||
end
|
||||
else
|
||||
begin
|
||||
pck_divider <= pck_divider + 1;
|
||||
pck_divider <= pck_divider + 1'd1; // Increments by 1 on each rising edge of sspclk.
|
||||
end
|
||||
end
|
||||
|
||||
@@ -95,6 +122,15 @@ end
|
||||
// the ADC output which is then clocked into the ARM SSP.
|
||||
always @(posedge pck0)
|
||||
begin
|
||||
|
||||
// For PM5: Since adc_clk = ~clk_state, the ADC receives a rising edge when clk_state is 0.
|
||||
// Note: Because the PM5 ADC samples on the rising edge, this causes hardware sampling to occur the moment the field is turned off (when voltage rises rapidly); therefore, a modification was made here.
|
||||
// The adc_clk is now set to be in-phase with clk_state (since pwrlo is based on clk_state, this aligns the phases).
|
||||
// Consequently, when clk_state is 0, the ADC receives a falling edge; sampling the output of the 9280 on the falling edge is safe, so this approach was adopted.
|
||||
// ADC sampling completes when pck_divider == 8'd7; one cycle later, the 'else' condition takes effect.
|
||||
// Shift transmission begins at 8'd8, but since the data payload is only 8 bits, zeros are transmitted continuously after the actual data is sent—a point easily misunderstood.
|
||||
// In reality, the active sequence spans pck_divider values from 0 to 7 (sampling), to 8 (transmission start), and finally to 16 (transmission end).
|
||||
|
||||
if ((pck_divider == 8'd7) && !clk_state)
|
||||
to_arm_shiftreg <= adc_d;
|
||||
else
|
||||
@@ -131,6 +131,13 @@ wire tracker_freeze = lf_ed_hold_tracker & (hold_cnt != 0);
|
||||
// filter the ADC values
|
||||
wire data_rdy;
|
||||
wire [7:0] adc_filtered;
|
||||
|
||||
// Regarding PM5, here is why the ADC sampling points remain in-phase (unlike other cases where sampling occurs at the same frequency as the signal):
|
||||
// 1. The ADC operates at 125 kHz (derived from a 24 MHz oversampling clock); sampling points are distributed throughout the entire cycle. High dV/dt events near the signal edges will inevitably be captured regardless of phase inversion,
|
||||
// and are subsequently suppressed by the 20 kHz low-pass filter, making phase alignment irrelevant;
|
||||
// 2. Phase inversion (adc_clk = ~pck0) causes the ADC to output data on the pck0 falling edge while downstream components (adc_filter/lf_edge_detect) read it on the pck0 rising edge.
|
||||
// This leaves a margin of only half a clock cycle (20.8 ns); given the ADC's maximum tOD of approximately 25 ns, the timing margin for reading is insufficient, creating a risk of capturing unstable or metastable data.
|
||||
|
||||
assign adc_clk = pck0;
|
||||
|
||||
lp20khz_1MSa_iir_filter adc_filter(
|
||||
File renamed without changes.
@@ -29,7 +29,7 @@ module lo_read(
|
||||
input lf_field,
|
||||
|
||||
output ssp_din,
|
||||
output ssp_frame,
|
||||
output reg ssp_frame,
|
||||
output ssp_clk,
|
||||
output adc_clk,
|
||||
output pwr_lo,
|
||||
@@ -74,8 +74,27 @@ end
|
||||
assign ssp_din = to_arm_shiftreg[7] && !pck_divclk;
|
||||
// SSP clock always runs at 24MHz
|
||||
assign ssp_clk = pck0;
|
||||
// SSP frame is gated by pck_divclk and goes high when pck_cnt=8..15
|
||||
assign ssp_frame = (pck_cnt[7:3] == 5'd1) && !pck_divclk;
|
||||
|
||||
// ssp_frame window: TI = 1-cycle pulse (at counter 7), SSP = spans the entire data window (counts 8-15).
|
||||
// Note: The 'always' block below reads the value of pck_cnt *before* it toggles for the current cycle,
|
||||
// so SET is triggered at the window start minus 1, and CLR is triggered at the window end.
|
||||
`ifdef WITH_TIMODE
|
||||
`define LO_READ_FRAME_SET 8'd6
|
||||
`define LO_READ_FRAME_CLR 8'd7
|
||||
`else
|
||||
`define LO_READ_FRAME_SET 8'd7
|
||||
`define LO_READ_FRAME_CLR 8'd15
|
||||
`endif
|
||||
|
||||
// Frame signal generation.
|
||||
always @(posedge pck0)
|
||||
begin
|
||||
if ((pck_cnt == `LO_READ_FRAME_SET) && !pck_divclk)
|
||||
ssp_frame <= 1'b1;
|
||||
if ((pck_cnt == `LO_READ_FRAME_CLR) && !pck_divclk)
|
||||
ssp_frame <= 1'b0;
|
||||
end
|
||||
|
||||
// unused signals tied low
|
||||
assign pwr_hi = 1'b0;
|
||||
// always on outputs, unused
|
||||
@@ -85,8 +104,17 @@ assign pwr_oe3 = 1'b0;
|
||||
assign pwr_oe4 = 1'b0;
|
||||
// this is the antenna driver signal
|
||||
assign pwr_lo = lf_field & pck_divclk;
|
||||
|
||||
// ADC clock out of phase with antenna driver
|
||||
`ifdef PM5
|
||||
// Since the PM5's ADC sampling occurs on the rising edge,
|
||||
// whereas the falling edge of the field signal causes a rapid voltage rise, making it unsuitable for sampling,
|
||||
// the phase was adjusted to align them.
|
||||
assign adc_clk = pck_divclk;
|
||||
`else
|
||||
assign adc_clk = ~pck_divclk;
|
||||
`endif
|
||||
|
||||
// ADC clock also routed to debug pin
|
||||
assign debug = adc_clk;
|
||||
endmodule
|
||||
File renamed without changes.
@@ -72,7 +72,7 @@ module lp20khz_1MSa_iir_filter(
|
||||
// We could use 9bit x registers for that, but that would be
|
||||
// a waste, let's just add the constant during the computation
|
||||
// (x0+109) + 2*(x1+109) + (x2+109) = x0 + 2*x1 + x2 + 436
|
||||
x0 + {x1, 1'b0} + adc_d + 436
|
||||
x0 + {x1, 1'b0} + adc_d + 14'd436
|
||||
// we want "- y0 * 0xd6 / 0x100" using only shift and add
|
||||
// 0xd6 == 0b11010110
|
||||
// so *0xd6/0x100 is equivalent to
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Shared definitions for the FPGA build files.
|
||||
#
|
||||
# Source layout:
|
||||
# common/ HDL shared by every platform
|
||||
# pm3/ Xilinx PM3 / PM3-ULT top level and pin constraints
|
||||
# icopyx/ Xilinx iCopy-X top level and pin constraints
|
||||
# pm5/ Gowin PM5 top level, PLL, yosys script and pin constraints
|
||||
#
|
||||
# Included by proxmark3_fpga.mk and proxmark5_fpga.mk.
|
||||
|
||||
COMMON_DIR = common
|
||||
PM3_DIR = pm3
|
||||
ICOPYX_DIR = icopyx
|
||||
PM5_DIR = pm5
|
||||
|
||||
# Prefix of the per target build directories, gitignored as fpga/__build*
|
||||
PREFIX = __
|
||||
|
||||
# Copy update (only when destination is older or missing)
|
||||
CP = cp -u
|
||||
|
||||
# Make directory, no error if already existing
|
||||
MKDIR = mkdir -p
|
||||
|
||||
# Remove recursive, force
|
||||
RMDIR = rm -rf
|
||||
|
||||
# Path to make
|
||||
MAKE = make
|
||||
|
||||
# Echo (Q=) or not echo (Q=@) build commands to the terminal
|
||||
Q =
|
||||
|
||||
# HDL shared by every target, in synthesis order
|
||||
COMMON_HDL = $(COMMON_DIR)/define.v
|
||||
COMMON_HDL += $(COMMON_DIR)/mux8.v
|
||||
COMMON_HDL += $(COMMON_DIR)/clk_divider.v
|
||||
COMMON_HDL += $(COMMON_DIR)/lp20khz_1MSa_iir_filter.v
|
||||
COMMON_HDL += $(COMMON_DIR)/min_max_tracker.v
|
||||
COMMON_HDL += $(COMMON_DIR)/hi_flite.v
|
||||
COMMON_HDL += $(COMMON_DIR)/hi_get_trace.v
|
||||
COMMON_HDL += $(COMMON_DIR)/hi_iso14443a.v
|
||||
COMMON_HDL += $(COMMON_DIR)/hi_reader.v
|
||||
COMMON_HDL += $(COMMON_DIR)/hi_simulate.v
|
||||
COMMON_HDL += $(COMMON_DIR)/hi_sniffer.v
|
||||
COMMON_HDL += $(COMMON_DIR)/lf_edge_detect.v
|
||||
COMMON_HDL += $(COMMON_DIR)/lo_adc.v
|
||||
COMMON_HDL += $(COMMON_DIR)/lo_edge_detect.v
|
||||
COMMON_HDL += $(COMMON_DIR)/lo_passthru.v
|
||||
COMMON_HDL += $(COMMON_DIR)/lo_read.v
|
||||
|
||||
# HDL of the PM5 (Gowin) bitstream, in synthesis order. One image covers both
|
||||
# halves; the board selects LF or HF with the module_sel pin.
|
||||
PM5_HDL = $(COMMON_HDL)
|
||||
PM5_HDL += $(PM5_DIR)/gowin_rpll.v
|
||||
PM5_HDL += $(PM5_DIR)/fpga_pm5_hf.v
|
||||
PM5_HDL += $(PM5_DIR)/fpga_pm5_lf.v
|
||||
PM5_HDL += $(PM5_DIR)/fpga_pm5_top.v
|
||||
|
||||
# Strips the build date/time out of a generated Xilinx bitstream, so that
|
||||
# rebuilding without a source change produces a byte identical file.
|
||||
# Xilinx specific: not applicable to the Gowin bitstream.
|
||||
STRIP_DATE_TIME = $(COMMON_DIR)/strip_date_time_from_binary.py
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,45 +1,45 @@
|
||||
# See the schematic for the pin assignment.
|
||||
|
||||
NET "adc_d<0>" LOC = "P54" ;
|
||||
NET "adc_d<1>" LOC = "P57" ;
|
||||
NET "adc_d<2>" LOC = "P59" ;
|
||||
NET "adc_d<3>" LOC = "P60" ;
|
||||
NET "adc_d<4>" LOC = "P62" ;
|
||||
NET "adc_d<5>" LOC = "P63" ;
|
||||
NET "adc_d<6>" LOC = "P65" ;
|
||||
NET "adc_d<7>" LOC = "P67" ;
|
||||
#NET "cross_hi" LOC = "P88" ;
|
||||
#NET "miso" LOC = "P40" ;
|
||||
NET "adc_clk" LOC = "P75" ;
|
||||
NET "adc_noe" LOC = "P74" ;
|
||||
NET "ck_1356meg" LOC = "P15" ;
|
||||
NET "ck_1356megb" LOC = "P12" ;
|
||||
NET "cross_lo" LOC = "P19" ;
|
||||
NET "dbg" LOC = "P112" ;
|
||||
NET "mosi" LOC = "P80" ;
|
||||
NET "ncs" LOC = "P79" ;
|
||||
NET "pck0" LOC = "P91" ;
|
||||
NET "pwr_hi" LOC = "P31" ;
|
||||
NET "pwr_lo" LOC = "P30" ;
|
||||
NET "pwr_oe1" LOC = "P28" ;
|
||||
NET "pwr_oe2" LOC = "P27" ;
|
||||
NET "pwr_oe3" LOC = "P26" ;
|
||||
NET "pwr_oe4" LOC = "P21" ;
|
||||
NET "spck" LOC = "P88" ;
|
||||
NET "ssp_clk" LOC = "P43" ;
|
||||
NET "ssp_din" LOC = "P99" ;
|
||||
NET "ssp_dout" LOC = "P94" ;
|
||||
NET "ssp_frame" LOC = "P100" ;
|
||||
|
||||
# definition of Clock nets:
|
||||
NET "ck_1356meg" TNM_NET = "clk_net_1356" ;
|
||||
NET "ck_1356megb" TNM_NET = "clk_net_1356b";
|
||||
NET "pck0" TNM_NET = "clk_net_pck0" ;
|
||||
NET "spck" TNM_NET = "clk_net_spck" ;
|
||||
|
||||
# Timing specs of clock nets:
|
||||
TIMEGRP "clk_net_1356_all" = "clk_net_1356" "clk_net_1356b" ;
|
||||
TIMESPEC "TS_1356MHz" = PERIOD "clk_net_1356_all" 74 ns HIGH 37 ns ;
|
||||
TIMESPEC "TS_24MHz" = PERIOD "clk_net_pck0" 42 ns HIGH 21 ns ;
|
||||
TIMESPEC "TS_4MHz" = PERIOD "clk_net_spck" 250 ns HIGH 125 ns ;
|
||||
|
||||
# See the schematic for the pin assignment.
|
||||
|
||||
NET "adc_d<0>" LOC = "P54" ;
|
||||
NET "adc_d<1>" LOC = "P57" ;
|
||||
NET "adc_d<2>" LOC = "P59" ;
|
||||
NET "adc_d<3>" LOC = "P60" ;
|
||||
NET "adc_d<4>" LOC = "P62" ;
|
||||
NET "adc_d<5>" LOC = "P63" ;
|
||||
NET "adc_d<6>" LOC = "P65" ;
|
||||
NET "adc_d<7>" LOC = "P67" ;
|
||||
#NET "cross_hi" LOC = "P88" ;
|
||||
#NET "miso" LOC = "P40" ;
|
||||
NET "adc_clk" LOC = "P75" ;
|
||||
NET "adc_noe" LOC = "P74" ;
|
||||
NET "ck_1356meg" LOC = "P15" ;
|
||||
NET "ck_1356megb" LOC = "P12" ;
|
||||
NET "cross_lo" LOC = "P19" ;
|
||||
NET "dbg" LOC = "P112" ;
|
||||
NET "mosi" LOC = "P80" ;
|
||||
NET "ncs" LOC = "P79" ;
|
||||
NET "pck0" LOC = "P91" ;
|
||||
NET "pwr_hi" LOC = "P31" ;
|
||||
NET "pwr_lo" LOC = "P30" ;
|
||||
NET "pwr_oe1" LOC = "P28" ;
|
||||
NET "pwr_oe2" LOC = "P27" ;
|
||||
NET "pwr_oe3" LOC = "P26" ;
|
||||
NET "pwr_oe4" LOC = "P21" ;
|
||||
NET "spck" LOC = "P88" ;
|
||||
NET "ssp_clk" LOC = "P43" ;
|
||||
NET "ssp_din" LOC = "P99" ;
|
||||
NET "ssp_dout" LOC = "P94" ;
|
||||
NET "ssp_frame" LOC = "P100" ;
|
||||
|
||||
# definition of Clock nets:
|
||||
NET "ck_1356meg" TNM_NET = "clk_net_1356" ;
|
||||
NET "ck_1356megb" TNM_NET = "clk_net_1356b";
|
||||
NET "pck0" TNM_NET = "clk_net_pck0" ;
|
||||
NET "spck" TNM_NET = "clk_net_spck" ;
|
||||
|
||||
# Timing specs of clock nets:
|
||||
TIMEGRP "clk_net_1356_all" = "clk_net_1356" "clk_net_1356b" ;
|
||||
TIMESPEC "TS_1356MHz" = PERIOD "clk_net_1356_all" 74 ns HIGH 37 ns ;
|
||||
TIMESPEC "TS_24MHz" = PERIOD "clk_net_pck0" 42 ns HIGH 21 ns ;
|
||||
TIMESPEC "TS_4MHz" = PERIOD "clk_net_spck" 250 ns HIGH 125 ns ;
|
||||
|
||||
@@ -0,0 +1,382 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// See LICENSE.txt for the text of the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
module fpga_hf(
|
||||
input ck_1356meg,
|
||||
input spck,
|
||||
//input pck0,
|
||||
input ncs,
|
||||
input [7:0] adc_d,
|
||||
//input cross_hi,
|
||||
//input cross_lo,
|
||||
input mosi,
|
||||
input ssp_dout,
|
||||
input ce, // 复位输入,常态高
|
||||
|
||||
output ssp_din,
|
||||
output ssp_frame,
|
||||
output ssp_clk,
|
||||
output adc_clk,
|
||||
//output adc_noe,
|
||||
output miso,
|
||||
output pwr_lo,
|
||||
output pwr_hi,
|
||||
output pwr_oe1,
|
||||
output pwr_oe2,
|
||||
//output pwr_oe3,
|
||||
output pwr_oe4,
|
||||
output dbg,
|
||||
output reg [11:0] pwm_duty = 12'h800
|
||||
);
|
||||
|
||||
|
||||
// In all modes, let the ADC's outputs be enabled.
|
||||
//assign adc_noe = 1'b0;
|
||||
assign miso = 1'bZ;
|
||||
wire cross_hi = 1'b0;
|
||||
|
||||
wire [2:0] major_mode;
|
||||
assign major_mode = ce ? conf_word[8:6] : 3'b111;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Receive 16bits of data from ARM here.
|
||||
reg [15:0] shift_reg;
|
||||
|
||||
//always @(posedge spck) if (~ncs) shift_reg <= {shift_reg[14:0], mosi};
|
||||
always @(posedge spck) begin
|
||||
if(~ncs & ce)
|
||||
shift_reg <= {shift_reg[14:0], mosi};
|
||||
end
|
||||
|
||||
reg trace_enable;
|
||||
|
||||
reg [5:0] hf_edge_detect_threshold = 6'd7;
|
||||
reg [5:0] hf_edge_detect_threshold_high = 6'd20;
|
||||
|
||||
reg [8:0] conf_word;
|
||||
|
||||
initial
|
||||
begin
|
||||
hf_edge_detect_threshold <= 7;
|
||||
hf_edge_detect_threshold_high <= 20;
|
||||
end
|
||||
|
||||
// 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
|
||||
if(ce) begin
|
||||
// 4 bit command
|
||||
case (shift_reg[15:12])
|
||||
`FPGA_CMD_SET_CONFREG: conf_word <= shift_reg[8:0];
|
||||
`FPGA_CMD_TRACE_ENABLE: trace_enable <= shift_reg[0];
|
||||
`FPGA_CMD_SET_EDGE_DETECT_THRESHOLD:
|
||||
begin
|
||||
hf_edge_detect_threshold <= shift_reg[5:0];
|
||||
hf_edge_detect_threshold_high <= shift_reg[11:6];
|
||||
end
|
||||
`FPGA_CMD_SET_PWR_PWM_LOW_COUNT:pwm_duty <= shift_reg[11:0];
|
||||
endcase
|
||||
end
|
||||
end
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// HF reader
|
||||
hi_reader hr(
|
||||
.ck_1356meg (ck_1356meg),
|
||||
.adc_d (adc_d),
|
||||
.subcarrier_frequency (conf_word[5:4]),
|
||||
.minor_mode (conf_word[3:0]),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_din (mux0_ssp_din),
|
||||
.ssp_frame (mux0_ssp_frame),
|
||||
.ssp_clk (mux0_ssp_clk),
|
||||
.adc_clk (mux0_adc_clk),
|
||||
.pwr_lo (mux0_pwr_lo),
|
||||
.pwr_hi (mux0_pwr_hi),
|
||||
.pwr_oe1 (mux0_pwr_oe1),
|
||||
.pwr_oe2 (mux0_pwr_oe2),
|
||||
.pwr_oe3 (mux0_pwr_oe3),
|
||||
.pwr_oe4 (mux0_pwr_oe4),
|
||||
.debug (mux0_debug)
|
||||
);
|
||||
|
||||
// HF simulated tag
|
||||
hi_simulate hs(
|
||||
.ck_1356meg (ck_1356meg),
|
||||
.adc_d (adc_d),
|
||||
.mod_type (conf_word[3:0]),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_din (mux1_ssp_din),
|
||||
.ssp_frame (mux1_ssp_frame),
|
||||
.ssp_clk (mux1_ssp_clk),
|
||||
.adc_clk (mux1_adc_clk),
|
||||
.pwr_lo (mux1_pwr_lo),
|
||||
.pwr_hi (mux1_pwr_hi),
|
||||
.pwr_oe1 (mux1_pwr_oe1),
|
||||
.pwr_oe2 (mux1_pwr_oe2),
|
||||
.pwr_oe3 (mux1_pwr_oe3),
|
||||
.pwr_oe4 (mux1_pwr_oe4),
|
||||
.debug (mux1_debug)
|
||||
);
|
||||
|
||||
// HF ISO14443-A
|
||||
hi_iso14443a hisn(
|
||||
.ck_1356meg (ck_1356meg),
|
||||
.adc_d (adc_d),
|
||||
.mod_type (conf_word[3:0]),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_din (mux2_ssp_din),
|
||||
.ssp_frame (mux2_ssp_frame),
|
||||
.ssp_clk (mux2_ssp_clk),
|
||||
.adc_clk (mux2_adc_clk),
|
||||
.pwr_lo (mux2_pwr_lo),
|
||||
.pwr_hi (mux2_pwr_hi),
|
||||
.pwr_oe1 (mux2_pwr_oe1),
|
||||
.pwr_oe2 (mux2_pwr_oe2),
|
||||
.pwr_oe3 (mux2_pwr_oe3),
|
||||
.pwr_oe4 (mux2_pwr_oe4),
|
||||
.debug (mux2_debug),
|
||||
.edge_detect_threshold ({5'b00000, hf_edge_detect_threshold}),
|
||||
.edge_detect_threshold_high ({5'b00000, hf_edge_detect_threshold_high})
|
||||
);
|
||||
|
||||
// HF sniff
|
||||
hi_sniffer he(
|
||||
.ck_1356meg (ck_1356meg),
|
||||
.adc_d (adc_d),
|
||||
|
||||
.ssp_din (mux3_ssp_din),
|
||||
.ssp_frame (mux3_ssp_frame),
|
||||
.ssp_clk (mux3_ssp_clk),
|
||||
.adc_clk (mux3_adc_clk),
|
||||
.pwr_lo (mux3_pwr_lo),
|
||||
.pwr_hi (mux3_pwr_hi),
|
||||
.pwr_oe1 (mux3_pwr_oe1),
|
||||
.pwr_oe2 (mux3_pwr_oe2),
|
||||
.pwr_oe3 (mux3_pwr_oe3),
|
||||
.pwr_oe4 (mux3_pwr_oe4)
|
||||
);
|
||||
|
||||
// HF ISO18092 FeliCa
|
||||
hi_flite hfl(
|
||||
.ck_1356meg (ck_1356meg),
|
||||
.adc_d (adc_d),
|
||||
.mod_type (conf_word[3:0]),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_din (mux4_ssp_din),
|
||||
.ssp_frame (mux4_ssp_frame),
|
||||
.ssp_clk (mux4_ssp_clk),
|
||||
.adc_clk (mux4_adc_clk),
|
||||
.pwr_lo (mux4_pwr_lo),
|
||||
.pwr_hi (mux4_pwr_hi),
|
||||
.pwr_oe1 (mux4_pwr_oe1),
|
||||
.pwr_oe2 (mux4_pwr_oe2),
|
||||
.pwr_oe3 (mux4_pwr_oe3),
|
||||
.pwr_oe4 (mux4_pwr_oe4),
|
||||
.debug (mux4_debug)
|
||||
);
|
||||
|
||||
// HF get trace
|
||||
|
||||
hi_get_trace gt(
|
||||
.ck_1356megb (ck_1356meg),
|
||||
.adc_d (adc_d),
|
||||
.trace_enable (trace_enable),
|
||||
.major_mode (major_mode),
|
||||
.ssp_din (mux5_ssp_din),
|
||||
.ssp_frame (mux5_ssp_frame),
|
||||
.ssp_clk (mux5_ssp_clk)
|
||||
);
|
||||
|
||||
// These assignments must agree with the defines in fpgaloader.h
|
||||
|
||||
// Major modes High Frequency
|
||||
// mux0 = HF reader
|
||||
// mux1 = HF simulated tag
|
||||
// mux2 = HF ISO14443-A
|
||||
// mux3 = HF sniff
|
||||
// mux4 = HF ISO18092 FeliCa
|
||||
// mux5 = HF get trace
|
||||
// mux6 = unused
|
||||
// mux7 = FPGA_MAJOR_MODE_OFF
|
||||
|
||||
mux8 mux_ssp_clk (
|
||||
.sel (major_mode),
|
||||
.y (ssp_clk ),
|
||||
.x0 (mux0_ssp_clk ),
|
||||
.x1 (mux1_ssp_clk ),
|
||||
.x2 (mux2_ssp_clk ),
|
||||
.x3 (mux3_ssp_clk ),
|
||||
.x4 (mux4_ssp_clk ),
|
||||
.x5 (mux5_ssp_clk ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_ssp_din (
|
||||
.sel (major_mode),
|
||||
.y (ssp_din ),
|
||||
.x0 (mux0_ssp_din ),
|
||||
.x1 (mux1_ssp_din ),
|
||||
.x2 (mux2_ssp_din ),
|
||||
.x3 (mux3_ssp_din ),
|
||||
.x4 (mux4_ssp_din ),
|
||||
.x5 (mux5_ssp_din ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_ssp_frame (
|
||||
.sel (major_mode),
|
||||
.y (ssp_frame ),
|
||||
.x0 (mux0_ssp_frame ),
|
||||
.x1 (mux1_ssp_frame ),
|
||||
.x2 (mux2_ssp_frame ),
|
||||
.x3 (mux3_ssp_frame ),
|
||||
.x4 (mux4_ssp_frame ),
|
||||
.x5 (mux5_ssp_frame ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe1 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe1 ),
|
||||
.x0 (mux0_pwr_oe1 ),
|
||||
.x1 (mux1_pwr_oe1 ),
|
||||
.x2 (mux2_pwr_oe1 ),
|
||||
.x3 (mux3_pwr_oe1 ),
|
||||
.x4 (mux4_pwr_oe1 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe2 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe2 ),
|
||||
.x0 (mux0_pwr_oe2 ),
|
||||
.x1 (mux1_pwr_oe2 ),
|
||||
.x2 (mux2_pwr_oe2 ),
|
||||
.x3 (mux3_pwr_oe2 ),
|
||||
.x4 (mux4_pwr_oe2 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe3 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe3 ),
|
||||
.x0 (mux0_pwr_oe3 ),
|
||||
.x1 (mux1_pwr_oe3 ),
|
||||
.x2 (mux2_pwr_oe3 ),
|
||||
.x3 (mux3_pwr_oe3 ),
|
||||
.x4 (mux4_pwr_oe3 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe4 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe4 ),
|
||||
.x0 (mux0_pwr_oe4 ),
|
||||
.x1 (mux1_pwr_oe4 ),
|
||||
.x2 (mux2_pwr_oe4 ),
|
||||
.x3 (mux3_pwr_oe4 ),
|
||||
.x4 (mux4_pwr_oe4 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_lo (
|
||||
.sel (major_mode),
|
||||
.y (pwr_lo ),
|
||||
.x0 (mux0_pwr_lo ),
|
||||
.x1 (mux1_pwr_lo ),
|
||||
.x2 (mux2_pwr_lo ),
|
||||
.x3 (mux3_pwr_lo ),
|
||||
.x4 (mux4_pwr_lo ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_hi (
|
||||
.sel (major_mode),
|
||||
.y (pwr_hi ),
|
||||
.x0 (mux0_pwr_hi ),
|
||||
.x1 (mux1_pwr_hi ),
|
||||
.x2 (mux2_pwr_hi ),
|
||||
.x3 (mux3_pwr_hi ),
|
||||
.x4 (mux4_pwr_hi ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_adc_clk (
|
||||
.sel (major_mode),
|
||||
.y (adc_clk ),
|
||||
.x0 (mux0_adc_clk ),
|
||||
.x1 (mux1_adc_clk ),
|
||||
.x2 (mux2_adc_clk ),
|
||||
.x3 (mux3_adc_clk ),
|
||||
.x4 (mux4_adc_clk ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_dbg (
|
||||
.sel (major_mode),
|
||||
.y (dbg ),
|
||||
.x0 (mux0_debug ),
|
||||
.x1 (mux1_debug ),
|
||||
.x2 (mux2_debug ),
|
||||
.x3 (1'b0 ),
|
||||
.x4 (mux4_debug ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
endmodule
|
||||
@@ -0,0 +1,340 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// See LICENSE.txt for the text of the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
module fpga_lf(
|
||||
//input ck_1356meg,
|
||||
input spck,
|
||||
input pck0,
|
||||
input ncs,
|
||||
input [7:0] adc_d,
|
||||
input cross_lo,
|
||||
input mosi,
|
||||
input ssp_dout,
|
||||
input ce, // 复位输入,常态高
|
||||
|
||||
output ssp_din,
|
||||
output ssp_frame,
|
||||
output ssp_clk,
|
||||
output adc_clk,
|
||||
output miso,
|
||||
output pwr_lo,
|
||||
output pwr_oe1,
|
||||
output pwr_oe2,
|
||||
output pwr_oe4,
|
||||
output dbg,
|
||||
output reg [11:0] pwm_duty = 12'h500
|
||||
);
|
||||
|
||||
// In all modes, let the ADC's outputs be enabled.
|
||||
//assign adc_noe = 1'b0;
|
||||
assign miso = 1'bZ;
|
||||
|
||||
wire [2:0] major_mode;
|
||||
assign major_mode = ce ? conf_word[8:6] : 3'b111;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Receive 16bits of data from ARM here.
|
||||
reg [15:0] shift_reg;
|
||||
|
||||
always @(posedge spck) begin
|
||||
if(~ncs & ce)
|
||||
shift_reg <= {shift_reg[14:0], mosi};
|
||||
end
|
||||
|
||||
reg [7:0] lf_ed_threshold;
|
||||
|
||||
// adjustable frequency clock
|
||||
wire [7:0] pck_cnt;
|
||||
wire pck_divclk;
|
||||
reg [7:0] divisor;
|
||||
clk_divider div_clk(pck0, divisor, pck_cnt, pck_divclk);
|
||||
|
||||
reg [11: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
|
||||
if(ce) begin
|
||||
// 4 bit command
|
||||
case (shift_reg[15:12])
|
||||
`FPGA_CMD_SET_CONFREG:
|
||||
begin
|
||||
// 12 bit data
|
||||
conf_word <= shift_reg[11:0];
|
||||
if (shift_reg[8:6] == `FPGA_MAJOR_MODE_LF_EDGE_DETECT) lf_ed_threshold <= 127; // default threshold
|
||||
end
|
||||
|
||||
`FPGA_CMD_SET_DIVISOR:
|
||||
divisor <= shift_reg[7:0]; // 8bits
|
||||
|
||||
`FPGA_CMD_SET_EDGE_DETECT_THRESHOLD:
|
||||
lf_ed_threshold <= shift_reg[7:0]; // 8 bits
|
||||
`FPGA_CMD_SET_PWR_PWM_LOW_COUNT:pwm_duty <= shift_reg[11:0];
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// ############################################################################
|
||||
|
||||
// LF reader (generic)
|
||||
lo_read lr(
|
||||
.pck0 (pck0),
|
||||
.pck_divclk (pck_divclk),
|
||||
.pck_cnt (pck_cnt),
|
||||
.adc_d (adc_d),
|
||||
.lf_field (conf_word[0]),
|
||||
|
||||
.ssp_din (mux0_ssp_din),
|
||||
.ssp_frame (mux0_ssp_frame),
|
||||
.ssp_clk (mux0_ssp_clk),
|
||||
.adc_clk (mux0_adc_clk),
|
||||
.pwr_lo (mux0_pwr_lo),
|
||||
.pwr_oe1 (mux0_pwr_oe1),
|
||||
.pwr_oe2 (mux0_pwr_oe2),
|
||||
.pwr_oe3 (mux0_pwr_oe3),
|
||||
.pwr_oe4 (mux0_pwr_oe4),
|
||||
.debug (mux0_debug)
|
||||
);
|
||||
|
||||
// LF edge detect (generic)
|
||||
lo_edge_detect le(
|
||||
.pck0 (pck0),
|
||||
.pck_divclk (pck_divclk),
|
||||
.adc_d (adc_d),
|
||||
.cross_lo (cross_lo),
|
||||
.lf_field (conf_word[0]),
|
||||
.lf_ed_toggle_mode (conf_word[1]),
|
||||
.lf_weak_load (conf_word[2]),
|
||||
.lf_ed_hold_tracker (conf_word[3]),
|
||||
.lf_ed_sensitive (conf_word[4]),
|
||||
.lf_ed_slope (conf_word[5]),
|
||||
.lf_ed_threshold (lf_ed_threshold),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_frame (mux1_ssp_frame),
|
||||
.ssp_clk (mux1_ssp_clk),
|
||||
.adc_clk (mux1_adc_clk),
|
||||
.pwr_lo (mux1_pwr_lo),
|
||||
.pwr_oe1 (mux1_pwr_oe1),
|
||||
.pwr_oe2 (mux1_pwr_oe2),
|
||||
.pwr_oe3 (mux1_pwr_oe3),
|
||||
.pwr_oe4 (mux1_pwr_oe4),
|
||||
.debug (mux1_debug)
|
||||
);
|
||||
|
||||
// LF passthrough
|
||||
lo_passthru lp(
|
||||
.pck_divclk (pck_divclk),
|
||||
.cross_lo (cross_lo),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_din (mux2_ssp_din),
|
||||
.adc_clk (mux2_adc_clk),
|
||||
.pwr_lo (mux2_pwr_lo),
|
||||
.pwr_oe1 (mux2_pwr_oe1),
|
||||
.pwr_oe2 (mux2_pwr_oe2),
|
||||
.pwr_oe3 (mux2_pwr_oe3),
|
||||
.pwr_oe4 (mux2_pwr_oe4),
|
||||
.debug (mux2_debug)
|
||||
);
|
||||
|
||||
// LF ADC (read/write)
|
||||
lo_adc la(
|
||||
.pck0 (pck0),
|
||||
.adc_d (adc_d),
|
||||
.divisor (divisor),
|
||||
.lf_field (conf_word[0]),
|
||||
.lf_weak_load (conf_word[2]),
|
||||
.ssp_dout (ssp_dout),
|
||||
|
||||
.ssp_din (mux3_ssp_din),
|
||||
.ssp_frame (mux3_ssp_frame),
|
||||
.ssp_clk (mux3_ssp_clk),
|
||||
.adc_clk (mux3_adc_clk),
|
||||
.pwr_lo (mux3_pwr_lo ),
|
||||
.pwr_oe1 (mux3_pwr_oe1),
|
||||
.pwr_oe2 (mux3_pwr_oe2),
|
||||
.pwr_oe3 (mux3_pwr_oe3),
|
||||
.pwr_oe4 (mux3_pwr_oe4),
|
||||
.debug (mux3_debug)
|
||||
);
|
||||
|
||||
assign mux6_pwr_lo = 1'b1;
|
||||
// 7 -- SPARE
|
||||
|
||||
// These assignments must agree with the defines in fpgaloader.h
|
||||
// Major modes Low Frequency
|
||||
// mux0 = LF reader (generic)
|
||||
// mux1 = LF edge detect (generic)
|
||||
// mux2 = LF passthrough
|
||||
// mux3 = LF ADC (read/write)
|
||||
// mux4 = SPARE
|
||||
// mux5 = SPARE
|
||||
// mux6 = SPARE
|
||||
// mux7 = FPGA_MAJOR_MODE_OFF
|
||||
|
||||
mux8 mux_ssp_clk (
|
||||
.sel (major_mode),
|
||||
.y (ssp_clk ),
|
||||
.x0 (mux0_ssp_clk ),
|
||||
.x1 (mux1_ssp_clk ),
|
||||
.x2 (1'b0 ),
|
||||
.x3 (mux3_ssp_clk ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_ssp_din (
|
||||
.sel (major_mode),
|
||||
.y (ssp_din ),
|
||||
.x0 (mux0_ssp_din ),
|
||||
.x1 (1'b0 ),
|
||||
.x2 (mux2_ssp_din ),
|
||||
.x3 (mux3_ssp_din ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_ssp_frame (
|
||||
.sel (major_mode),
|
||||
.y (ssp_frame ),
|
||||
.x0 (mux0_ssp_frame ),
|
||||
.x1 (mux1_ssp_frame ),
|
||||
.x2 (1'b0 ),
|
||||
.x3 (mux3_ssp_frame ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe1 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe1 ),
|
||||
.x0 (mux0_pwr_oe1 ),
|
||||
.x1 (mux1_pwr_oe1 ),
|
||||
.x2 (mux2_pwr_oe1 ),
|
||||
.x3 (mux3_pwr_oe1 ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe2 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe2 ),
|
||||
.x0 (mux0_pwr_oe2 ),
|
||||
.x1 (mux1_pwr_oe2 ),
|
||||
.x2 (mux2_pwr_oe2 ),
|
||||
.x3 (mux3_pwr_oe2 ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe3 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe3 ),
|
||||
.x0 (mux0_pwr_oe3 ),
|
||||
.x1 (mux1_pwr_oe3 ),
|
||||
.x2 (mux2_pwr_oe3 ),
|
||||
.x3 (mux3_pwr_oe3 ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_oe4 (
|
||||
.sel (major_mode),
|
||||
.y (pwr_oe4 ),
|
||||
.x0 (mux0_pwr_oe4 ),
|
||||
.x1 (mux1_pwr_oe4 ),
|
||||
.x2 (mux2_pwr_oe4 ),
|
||||
.x3 (mux3_pwr_oe4 ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_pwr_lo (
|
||||
.sel (major_mode),
|
||||
.y (pwr_lo ),
|
||||
.x0 (mux0_pwr_lo ),
|
||||
.x1 (mux1_pwr_lo ),
|
||||
.x2 (mux2_pwr_lo ),
|
||||
.x3 (mux3_pwr_lo ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (mux6_pwr_lo ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_adc_clk (
|
||||
.sel (major_mode),
|
||||
.y (adc_clk ),
|
||||
.x0 (mux0_adc_clk ),
|
||||
.x1 (mux1_adc_clk ),
|
||||
.x2 (mux2_adc_clk ),
|
||||
.x3 (mux3_adc_clk ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
|
||||
mux8 mux_dbg (
|
||||
.sel (major_mode),
|
||||
.y (dbg ),
|
||||
.x0 (mux0_debug ),
|
||||
.x1 (mux1_debug ),
|
||||
.x2 (mux2_debug ),
|
||||
.x3 (mux3_debug ),
|
||||
.x4 (1'b0 ),
|
||||
.x5 (1'b0 ),
|
||||
.x6 (1'b0 ),
|
||||
.x7 (1'b0 )
|
||||
);
|
||||
endmodule
|
||||
@@ -0,0 +1,132 @@
|
||||
module fpga_top (
|
||||
input ck_1356meg,
|
||||
input ck_1356meg_b, // B is for close range wiring
|
||||
input spi_sck,
|
||||
input pck0,
|
||||
input spi_ncs,
|
||||
input [9:0] adc_d,
|
||||
input cross_lo,
|
||||
input spi_mosi,
|
||||
input ssp_dout,
|
||||
input module_sel, // 选择模块,1=HF,0=LF
|
||||
|
||||
output ssp_din,
|
||||
output ssp_frame,
|
||||
output ssp_clk,
|
||||
output adc_clk,
|
||||
output spi_miso,
|
||||
output pwr_lo,
|
||||
output pwr_hi,
|
||||
output pwr_oe1,
|
||||
output pwr_oe2,
|
||||
output pwr_oe4,
|
||||
output dbg1,
|
||||
output dbg2,
|
||||
output dbgled,
|
||||
|
||||
output clk_gate, //门控输出,控制切断外部13.56m
|
||||
output pwr_pwm, //升压器输出,pwm控制升压电压
|
||||
output pan_en, //模拟供电使能
|
||||
output pdp_en //升压器使能
|
||||
);
|
||||
|
||||
wire ce_lf = ~module_sel; //高启用,低重置
|
||||
wire ce_hf = module_sel; //高启用,低重置
|
||||
//-----------------------------------------GATE-------------------------
|
||||
//门控使能信号同步到输入时钟域,避免亚稳态
|
||||
reg gate_en_sync1, gate_en_sync2;
|
||||
always @(posedge ck_1356meg) begin
|
||||
gate_en_sync1 <= gate_en;
|
||||
gate_en_sync2 <= gate_en_sync1;
|
||||
end
|
||||
|
||||
assign clk_gate = ~gate_en_sync2;//这里取反是因为外部门控是低有效的
|
||||
//-----------------------------------------PWM--------------------------
|
||||
|
||||
Gowin_rPLL pwr_pwm_pll(
|
||||
.clkout(pwr_pwm_pll_clko), //output clkout
|
||||
.clkin(ck_1356meg) //input clkin
|
||||
);
|
||||
|
||||
wire [11:0] pwm_duty_lf; // LF 模块的 PWM 占空比输出
|
||||
wire [11:0] pwm_duty_hf; // HF 模块的 PWM 占空比输出
|
||||
|
||||
wire [11:0] pwm_duty; // PWM占空比,实际上是高电平长度,和4096做比例
|
||||
reg [11:0] pwm_cnt= 12'h0;
|
||||
|
||||
always @(posedge pwr_pwm_pll_clko) pwm_cnt <= pwm_cnt + 1'b1; //计数器
|
||||
assign pwr_pwm = (pwm_cnt > pwm_duty); //pwm输出
|
||||
|
||||
assign pan_en = 1'b1;
|
||||
assign pdp_en = 1'b1;
|
||||
|
||||
assign pwr_hi = 1'bZ; //使用外部门控时禁用PWRHI,由于是并联的,需要高阻
|
||||
assign dbgled = 1'b1;
|
||||
assign dbg2 = 1'bZ; // 未使用
|
||||
|
||||
fpga_lf uut_lf (
|
||||
.pck0(pck0),
|
||||
|
||||
.spck(spi_sck),
|
||||
.ncs(spi_ncs),
|
||||
|
||||
.adc_d(adc_d[9:2]),
|
||||
.cross_lo(cross_lo),
|
||||
.mosi(spi_mosi),
|
||||
.ssp_dout(ssp_dout),
|
||||
.ce(ce_lf),
|
||||
|
||||
.ssp_din(ssp_din_lf),
|
||||
.ssp_frame(ssp_frame_lf),
|
||||
.ssp_clk(ssp_clk_lf),
|
||||
.adc_clk(adc_clk_lf),
|
||||
.miso(miso_lf),
|
||||
.pwr_lo(pwr_lo_lf),
|
||||
.pwr_oe1(pwr_oe1_lf),
|
||||
.pwr_oe2(pwr_oe2_lf),
|
||||
.pwr_oe4(pwr_oe4_lf),
|
||||
.dbg(dbg_lf),
|
||||
|
||||
.pwm_duty(pwm_duty_lf)
|
||||
);
|
||||
|
||||
fpga_hf uut_hf (
|
||||
.ck_1356meg(ck_1356meg),
|
||||
|
||||
.spck(spi_sck),
|
||||
.ncs(spi_ncs),
|
||||
|
||||
.adc_d(adc_d[9:2]),
|
||||
.mosi(spi_mosi),
|
||||
.ssp_dout(ssp_dout),
|
||||
.ce(ce_hf),
|
||||
|
||||
.ssp_din(ssp_din_hf),
|
||||
.ssp_frame(ssp_frame_hf),
|
||||
.ssp_clk(ssp_clk_hf),
|
||||
.adc_clk(adc_clk_hf),
|
||||
.miso(miso_hf),
|
||||
.pwr_lo(pwr_lo_hf),
|
||||
.pwr_hi(gate_en),
|
||||
.pwr_oe1(pwr_oe1_hf),
|
||||
.pwr_oe2(pwr_oe2_hf),
|
||||
.pwr_oe4(pwr_oe4_hf),
|
||||
.dbg(dbg_hf),
|
||||
|
||||
.pwm_duty(pwm_duty_hf)
|
||||
);
|
||||
|
||||
assign ssp_din = ~module_sel ? ssp_din_lf : ssp_din_hf;
|
||||
assign ssp_frame = ~module_sel ? ssp_frame_lf : ssp_frame_hf;
|
||||
assign ssp_clk = ~module_sel ? ssp_clk_lf : ssp_clk_hf;
|
||||
assign adc_clk = ~module_sel ? adc_clk_lf : adc_clk_hf;
|
||||
assign spi_miso = ~module_sel ? miso_lf : miso_hf;
|
||||
assign pwr_lo = ~module_sel ? pwr_lo_lf : pwr_lo_hf;
|
||||
//assign pwr_hi = ~module_sel ? pwr_hi_lf : pwr_hi_hf;
|
||||
assign pwr_oe1 = ~module_sel ? pwr_oe1_lf : pwr_oe1_hf;
|
||||
assign pwr_oe2 = ~module_sel ? pwr_oe2_lf : pwr_oe2_hf;
|
||||
assign pwr_oe4 = ~module_sel ? pwr_oe4_lf : pwr_oe4_hf;
|
||||
assign dbg1 = ~module_sel ? dbg_lf : dbg_hf;
|
||||
assign pwm_duty = ~module_sel ? pwm_duty_lf : pwm_duty_hf;
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,81 @@
|
||||
//Copyright (C)2014-2023 Gowin Semiconductor Corporation.
|
||||
//All rights reserved.
|
||||
//File Title: Physical Constraints file
|
||||
//Tool Version: V1.9.9 (64-bit)
|
||||
//Part Number: GW1N-UV4QN48C6/I5
|
||||
//Device: GW1N-4
|
||||
//Device Version: D
|
||||
//Created Time: Thu 06 04 12:54:44 2026
|
||||
|
||||
IO_LOC "pdp_en" 42;
|
||||
IO_PORT "pdp_en" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pan_en" 24;
|
||||
IO_PORT "pan_en" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pwr_pwm" 38;
|
||||
IO_PORT "pwr_pwm" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "clk_gate" 29;
|
||||
IO_PORT "clk_gate" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "dbgled" 41;
|
||||
IO_PORT "dbgled" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "dbg2" 39;
|
||||
IO_PORT "dbg2" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "dbg1" 40;
|
||||
IO_PORT "dbg1" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pwr_oe4" 31;
|
||||
IO_PORT "pwr_oe4" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pwr_oe2" 34;
|
||||
IO_PORT "pwr_oe2" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pwr_oe1" 32;
|
||||
IO_PORT "pwr_oe1" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pwr_lo" 33;
|
||||
IO_PORT "pwr_lo" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_clk" 13;
|
||||
IO_PORT "adc_clk" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "ssp_clk" 47;
|
||||
IO_PORT "ssp_clk" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "ssp_frame" 44;
|
||||
IO_PORT "ssp_frame" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "ssp_din" 45;
|
||||
IO_PORT "ssp_din" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "spi_miso" 7;
|
||||
IO_PORT "spi_miso" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "pwr_hi" 27;
|
||||
IO_PORT "pwr_hi" IO_TYPE=LVTTL33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
||||
IO_LOC "module_sel" 11;
|
||||
IO_PORT "module_sel" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "ssp_dout" 46;
|
||||
IO_PORT "ssp_dout" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "spi_mosi" 6;
|
||||
IO_PORT "spi_mosi" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "cross_lo" 35;
|
||||
IO_PORT "cross_lo" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[9]" 14;
|
||||
IO_PORT "adc_d[9]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[8]" 15;
|
||||
IO_PORT "adc_d[8]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[7]" 16;
|
||||
IO_PORT "adc_d[7]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[6]" 17;
|
||||
IO_PORT "adc_d[6]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[5]" 18;
|
||||
IO_PORT "adc_d[5]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[4]" 19;
|
||||
IO_PORT "adc_d[4]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[3]" 20;
|
||||
IO_PORT "adc_d[3]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[2]" 21;
|
||||
IO_PORT "adc_d[2]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "spi_ncs" 4;
|
||||
IO_PORT "spi_ncs" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "pck0" 30;
|
||||
IO_PORT "pck0" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "spi_sck" 5;
|
||||
IO_PORT "spi_sck" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "ck_1356meg" 10;
|
||||
IO_PORT "ck_1356meg" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[1]" 22;
|
||||
IO_PORT "adc_d[1]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "adc_d[0]" 23;
|
||||
IO_PORT "adc_d[0]" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
IO_LOC "ck_1356meg_b" 28;
|
||||
IO_PORT "ck_1356meg_b" IO_TYPE=LVTTL33 PULL_MODE=NONE BANK_VCCIO=3.3;
|
||||
@@ -0,0 +1,11 @@
|
||||
//Copyright (C)2014-2026 GOWIN Semiconductor Corporation.
|
||||
//All rights reserved.
|
||||
//File Title: Timing Constraints file
|
||||
//Tool Version: V1.9.9 (64-bit)
|
||||
//Created Time: 2026-04-20 17:50:38
|
||||
create_clock -name CLK_SPI -period 250 -waveform {0 125} [get_ports {spi_sck}]
|
||||
create_clock -name CLK_NCS -period 1000 -waveform {0 500} [get_ports {spi_ncs}]
|
||||
create_clock -name clk24 -period 41.667 -waveform {0 20.834} [get_ports {pck0}]
|
||||
create_clock -name clk1356 -period 73.746 -waveform {0 36.873} [get_ports {ck_1356meg}]
|
||||
set_clock_groups -asynchronous -group [get_clocks {clk1356}] -group [get_clocks {clk24}] -group [get_clocks {CLK_SPI}] -group [get_clocks {CLK_NCS}]
|
||||
report_timing -setup -from_clock [get_clocks {clk1356}] -to [get_ports {pwr_hi}]
|
||||
@@ -0,0 +1,64 @@
|
||||
//Copyright (C)2014-2023 Gowin Semiconductor Corporation.
|
||||
//All rights reserved.
|
||||
//File Title: IP file
|
||||
//Tool Version: V1.9.9 (64-bit)
|
||||
//Part Number: GW1N-UV4QN48C6/I5
|
||||
//Device: GW1N-4
|
||||
//Device Version: D
|
||||
//Created Time: Mon Jul 6 18:07:08 2026
|
||||
|
||||
module Gowin_rPLL (clkout, clkin);
|
||||
|
||||
output clkout;
|
||||
input clkin;
|
||||
|
||||
wire lock_o;
|
||||
wire clkoutp_o;
|
||||
wire clkoutd_o;
|
||||
wire clkoutd3_o;
|
||||
wire gw_gnd;
|
||||
|
||||
assign gw_gnd = 1'b0;
|
||||
|
||||
rPLL rpll_inst (
|
||||
.CLKOUT(clkout),
|
||||
.LOCK(lock_o),
|
||||
.CLKOUTP(clkoutp_o),
|
||||
.CLKOUTD(clkoutd_o),
|
||||
.CLKOUTD3(clkoutd3_o),
|
||||
.RESET(gw_gnd),
|
||||
.RESET_P(gw_gnd),
|
||||
.CLKIN(clkin),
|
||||
.CLKFB(gw_gnd),
|
||||
.FBDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
||||
.IDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
||||
.ODSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
||||
.PSDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
||||
.DUTYDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
||||
.FDLY({gw_gnd,gw_gnd,gw_gnd,gw_gnd})
|
||||
);
|
||||
|
||||
defparam rpll_inst.FCLKIN = "13.56";
|
||||
defparam rpll_inst.DYN_IDIV_SEL = "false";
|
||||
defparam rpll_inst.IDIV_SEL = 1;
|
||||
defparam rpll_inst.DYN_FBDIV_SEL = "false";
|
||||
defparam rpll_inst.FBDIV_SEL = 58;
|
||||
defparam rpll_inst.DYN_ODIV_SEL = "false";
|
||||
defparam rpll_inst.ODIV_SEL = 2;
|
||||
defparam rpll_inst.PSDA_SEL = "0000";
|
||||
defparam rpll_inst.DYN_DA_EN = "false";
|
||||
defparam rpll_inst.DUTYDA_SEL = "1000";
|
||||
defparam rpll_inst.CLKOUT_FT_DIR = 1'b1;
|
||||
defparam rpll_inst.CLKOUTP_FT_DIR = 1'b1;
|
||||
defparam rpll_inst.CLKOUT_DLY_STEP = 0;
|
||||
defparam rpll_inst.CLKOUTP_DLY_STEP = 0;
|
||||
defparam rpll_inst.CLKFB_SEL = "internal";
|
||||
defparam rpll_inst.CLKOUT_BYPASS = "false";
|
||||
defparam rpll_inst.CLKOUTP_BYPASS = "false";
|
||||
defparam rpll_inst.CLKOUTD_BYPASS = "false";
|
||||
defparam rpll_inst.DYN_SDIV_SEL = 2;
|
||||
defparam rpll_inst.CLKOUTD_SRC = "CLKOUT";
|
||||
defparam rpll_inst.CLKOUTD3_SRC = "CLKOUT";
|
||||
defparam rpll_inst.DEVICE = "GW1N-4D";
|
||||
|
||||
endmodule //Gowin_rPLL
|
||||
@@ -0,0 +1,11 @@
|
||||
# 手动综合脚本,等价于 proxmark5_fpga.mk 自动生成的步骤。
|
||||
# 在 fpga 目录下运行:yosys -s pm5/run.ys (构建时请用 make pm5)
|
||||
|
||||
# 1. 读取源代码和宏定义
|
||||
read_verilog -D PM5 -D WITH_TIMODE -D WITH_HF_15 -D WITH_FELICA_PROBE common/define.v common/mux8.v common/clk_divider.v common/lp20khz_1MSa_iir_filter.v common/min_max_tracker.v common/hi_flite.v common/hi_get_trace.v common/hi_iso14443a.v common/hi_reader.v common/hi_simulate.v common/hi_sniffer.v common/lf_edge_detect.v common/lo_adc.v common/lo_edge_detect.v common/lo_passthru.v common/lo_read.v pm5/gowin_rpll.v pm5/fpga_pm5_hf.v pm5/fpga_pm5_lf.v pm5/fpga_pm5_top.v
|
||||
|
||||
# 2. 检查并强制指定顶层
|
||||
hierarchy -top fpga_top
|
||||
|
||||
# 3. 进行综合
|
||||
synth_gowin -nolutram -json output.json
|
||||
@@ -0,0 +1,255 @@
|
||||
#
|
||||
# Proxmark3 / Proxmark3-ULT / iCopy-X FPGA build (Xilinx ISE toolchain)
|
||||
#
|
||||
# Invoked by the fpga/Makefile dispatcher, or directly:
|
||||
# make -C fpga -f proxmark3_fpga.mk [<target>|all|clean]
|
||||
#
|
||||
# The top part of this file is used to define custom options for a number of compilation targets
|
||||
# To define an additional target simply look at the other defined targets and add a new TARGET entry with a unique number and the custom options required
|
||||
|
||||
include $(dir $(lastword $(MAKEFILE_LIST)))common_src.mk
|
||||
|
||||
XILINX_TOOLS_PREFIX=
|
||||
|
||||
# Each target is built into its own separate directory name formed by combining the PREFIX and TARGET names.
|
||||
# This way the source is not polluted with build files and the build directories are left behind after compilation so logs and reports can be
|
||||
# examined or can be easily deleted with "make clean"
|
||||
|
||||
# Options to be passed to XST
|
||||
XST_OPTS_BASE = run
|
||||
XST_OPTS_BASE += -ifn xst.prj
|
||||
XST_OPTS_BASE += -ifmt mixed
|
||||
XST_OPTS_BASE += -ofmt NGC
|
||||
XST_OPTS_BASE += -lso xst.lso
|
||||
XST_OPTS_BASE += -top fpga_top
|
||||
XST_OPTS_BASE += -resource_sharing yes
|
||||
|
||||
# Optimizations for speed (default)
|
||||
XST_OPTS_SPEED = -opt_mode Speed
|
||||
XST_OPTS_SPEED += -opt_level 1
|
||||
XST_OPTS_SPEED += -fsm_style lut
|
||||
XST_OPTS_SPEED += -fsm_encoding auto
|
||||
|
||||
# Optimization for reduced space
|
||||
XST_OPTS_AREA = -opt_mode area
|
||||
XST_OPTS_AREA += -opt_level 2
|
||||
XST_OPTS_AREA += -fsm_style bram
|
||||
XST_OPTS_AREA += -fsm_encoding compact
|
||||
|
||||
# par specific option (set determistic seed)
|
||||
PAR_OPTIONS = -t 2
|
||||
|
||||
# Types of selective module compilation:
|
||||
# WITH_LF Enables selection of LF modules (and disables all HF)
|
||||
|
||||
# To enable these modules WITH_LF _MUST_ be defined
|
||||
# WITH_LF0 enable LF reader (generic)
|
||||
# WITH_LF1 enable LF edge detect (generic)
|
||||
# WITH_LF2 enable LF passthrough
|
||||
# WITH_LF3 enable LF ADC (read/write)
|
||||
|
||||
# To enable these modules WITH_LF _MUST_NOT_ be defined
|
||||
# WITH_HF0 enable HF reader (see also WITH_HF_15 below)
|
||||
# WITH_HF_15 select "iso15 2sc mode" extensions instead of original
|
||||
# (the iso15 low-signal reader detection thresholds are selected at runtime
|
||||
# in hi_reader.v, based on subcarrier/minor mode -- no separate define)
|
||||
# WITH_HF1 enable HF simulated tag
|
||||
# WITH_HF2 enable HF ISO14443-A
|
||||
# WITH_HF3 enable sniff
|
||||
# WITH_HF4 enable HF ISO18092 FeliCa
|
||||
# WITH_HF5 enable HF get trace
|
||||
|
||||
# WITH_TIMODE enable TI mode (for SPI_SSC. PM5 only currently)
|
||||
|
||||
# RDV40/Generic - Enable LF and all the LF modules
|
||||
TARGET1_OPTIONS = -define \{WITH_LF WITH_LF0 WITH_LF1 WITH_LF2 WITH_LF3\}
|
||||
# RDV40/Generic - Enable all HF modules except Felica
|
||||
TARGET2_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF2 WITH_HF3 WITH_HF5\}
|
||||
# RDV40/Generic - Enable all HF modules except Felica and ISO14443, select HF_15 instead of HF
|
||||
TARGET3_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF5 WITH_HF_15\}
|
||||
# RDV40/Generic - Enable all HF modules except ISO14443
|
||||
TARGET4_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF4 WITH_HF5 WITH_FELICA_PROBE\}
|
||||
# ICOPYX
|
||||
TARGET5_OPTIONS = $(TARGET1_OPTIONS)
|
||||
# PM3-ULT (xc2s50): same four images as the RDV4 targets
|
||||
TARGET6_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET7_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET8_OPTIONS = $(TARGET1_OPTIONS)
|
||||
TARGET9_OPTIONS = $(TARGET1_OPTIONS)
|
||||
|
||||
# Here we list the target names
|
||||
TARGET1_NAME = fpga_pm3_lf
|
||||
TARGET2_NAME = fpga_pm3_hf
|
||||
TARGET3_NAME = fpga_pm3_hf_15
|
||||
TARGET4_NAME = fpga_pm3_felica
|
||||
TARGET5_NAME = fpga_icopyx_hf
|
||||
TARGET6_NAME = fpga_pm3_ult_lf
|
||||
TARGET7_NAME = fpga_pm3_ult_hf
|
||||
TARGET8_NAME = fpga_pm3_ult_hf_15
|
||||
TARGET9_NAME = fpga_pm3_ult_felica
|
||||
|
||||
# Targets can be compiled for different FPGA flavours
|
||||
TARGET1_FPGA = xc2s30-5-vq100
|
||||
TARGET2_FPGA = $(TARGET1_FPGA)
|
||||
TARGET3_FPGA = $(TARGET1_FPGA)
|
||||
TARGET4_FPGA = $(TARGET1_FPGA)
|
||||
TARGET5_FPGA = xc3s100e-4-vq100
|
||||
TARGET6_FPGA = xc2s50-5-tq144
|
||||
TARGET7_FPGA = $(TARGET6_FPGA)
|
||||
TARGET8_FPGA = $(TARGET6_FPGA)
|
||||
TARGET9_FPGA = $(TARGET6_FPGA)
|
||||
|
||||
# Pin constraints, one file per FPGA flavour
|
||||
TARGET1_UCF = $(PM3_DIR)/xc2s30-5-vq100.ucf
|
||||
TARGET2_UCF = $(TARGET1_UCF)
|
||||
TARGET3_UCF = $(TARGET1_UCF)
|
||||
TARGET4_UCF = $(TARGET1_UCF)
|
||||
TARGET5_UCF = $(ICOPYX_DIR)/xc3s100e-4-vq100.ucf
|
||||
TARGET6_UCF = $(PM3_DIR)/xc2s50-5-tq144.ucf
|
||||
TARGET7_UCF = $(TARGET6_UCF)
|
||||
TARGET8_UCF = $(TARGET6_UCF)
|
||||
TARGET9_UCF = $(TARGET6_UCF)
|
||||
|
||||
# Assemble the final XST options for each target
|
||||
TARGET1_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET1_FPGA) -ofn $(TARGET1_NAME) $(TARGET1_OPTIONS)
|
||||
TARGET2_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET2_FPGA) -ofn $(TARGET2_NAME) $(TARGET2_OPTIONS)
|
||||
TARGET3_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET3_FPGA) -ofn $(TARGET3_NAME) $(TARGET3_OPTIONS)
|
||||
TARGET4_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET4_FPGA) -ofn $(TARGET4_NAME) $(TARGET4_OPTIONS)
|
||||
TARGET5_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET5_FPGA) -ofn $(TARGET5_NAME) $(TARGET5_OPTIONS)
|
||||
TARGET6_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET6_FPGA) -ofn $(TARGET6_NAME) $(TARGET6_OPTIONS)
|
||||
TARGET7_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET7_FPGA) -ofn $(TARGET7_NAME) $(TARGET7_OPTIONS)
|
||||
TARGET8_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET8_FPGA) -ofn $(TARGET8_NAME) $(TARGET8_OPTIONS)
|
||||
TARGET9_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET9_FPGA) -ofn $(TARGET9_NAME) $(TARGET9_OPTIONS)
|
||||
|
||||
# Add the files that are unique per target and all the common files
|
||||
TARGET1_FILES = $(COMMON_HDL) $(PM3_DIR)/fpga_pm3_top.v
|
||||
TARGET2_FILES = $(TARGET1_FILES)
|
||||
TARGET3_FILES = $(TARGET1_FILES)
|
||||
TARGET4_FILES = $(TARGET1_FILES)
|
||||
TARGET5_FILES = $(COMMON_HDL) $(COMMON_DIR)/mux2_onein.v $(COMMON_DIR)/mux2_oneout.v $(ICOPYX_DIR)/fpga_icopyx_hf.v $(ICOPYX_DIR)/fpga_icopyx_lf.v $(ICOPYX_DIR)/fpga_icopyx_top.v
|
||||
TARGET6_FILES = $(TARGET1_FILES)
|
||||
TARGET7_FILES = $(TARGET1_FILES)
|
||||
TARGET8_FILES = $(TARGET1_FILES)
|
||||
TARGET9_FILES = $(TARGET1_FILES)
|
||||
|
||||
# List of all valid target FPGA images to build
|
||||
TARGETS = $(TARGET1_NAME) $(TARGET2_NAME) $(TARGET3_NAME) $(TARGET4_NAME) $(TARGET5_NAME) $(TARGET6_NAME) $(TARGET7_NAME) $(TARGET8_NAME) $(TARGET9_NAME)
|
||||
|
||||
# Verbosity type for ISE tools ise|xflow|silent
|
||||
VERBOSITY = -intstyle silent
|
||||
|
||||
# Pass the custom variables to the lower make rules
|
||||
$(TARGET1_NAME).bit: TARGET_FPGA = $(TARGET1_FPGA)
|
||||
$(TARGET1_NAME).bit: TARGET_UCF = $(TARGET1_UCF)
|
||||
$(TARGET1_NAME).bit: TARGET_FILES = $(TARGET1_FILES)
|
||||
$(TARGET1_NAME).bit: TARGET_XST_OPTS = $(TARGET1_XST_OPTS)
|
||||
|
||||
$(TARGET2_NAME).bit: TARGET_FPGA = $(TARGET2_FPGA)
|
||||
$(TARGET2_NAME).bit: TARGET_UCF = $(TARGET2_UCF)
|
||||
$(TARGET2_NAME).bit: TARGET_FILES = $(TARGET2_FILES)
|
||||
$(TARGET2_NAME).bit: TARGET_XST_OPTS = $(TARGET2_XST_OPTS)
|
||||
|
||||
$(TARGET3_NAME).bit: TARGET_FPGA = $(TARGET3_FPGA)
|
||||
$(TARGET3_NAME).bit: TARGET_UCF = $(TARGET3_UCF)
|
||||
$(TARGET3_NAME).bit: TARGET_FILES = $(TARGET3_FILES)
|
||||
$(TARGET3_NAME).bit: TARGET_XST_OPTS = $(TARGET3_XST_OPTS)
|
||||
|
||||
$(TARGET4_NAME).bit: TARGET_FPGA = $(TARGET4_FPGA)
|
||||
$(TARGET4_NAME).bit: TARGET_UCF = $(TARGET4_UCF)
|
||||
$(TARGET4_NAME).bit: TARGET_FILES = $(TARGET4_FILES)
|
||||
$(TARGET4_NAME).bit: TARGET_XST_OPTS = $(TARGET4_XST_OPTS)
|
||||
|
||||
$(TARGET5_NAME).bit: TARGET_FPGA = $(TARGET5_FPGA)
|
||||
$(TARGET5_NAME).bit: TARGET_UCF = $(TARGET5_UCF)
|
||||
$(TARGET5_NAME).bit: TARGET_FILES = $(TARGET5_FILES)
|
||||
$(TARGET5_NAME).bit: TARGET_XST_OPTS = $(TARGET5_XST_OPTS)
|
||||
|
||||
$(TARGET6_NAME).bit: TARGET_FPGA = $(TARGET6_FPGA)
|
||||
$(TARGET6_NAME).bit: TARGET_UCF = $(TARGET6_UCF)
|
||||
$(TARGET6_NAME).bit: TARGET_FILES = $(TARGET6_FILES)
|
||||
$(TARGET6_NAME).bit: TARGET_XST_OPTS = $(TARGET6_XST_OPTS)
|
||||
|
||||
$(TARGET7_NAME).bit: TARGET_FPGA = $(TARGET7_FPGA)
|
||||
$(TARGET7_NAME).bit: TARGET_UCF = $(TARGET7_UCF)
|
||||
$(TARGET7_NAME).bit: TARGET_FILES = $(TARGET7_FILES)
|
||||
$(TARGET7_NAME).bit: TARGET_XST_OPTS = $(TARGET7_XST_OPTS)
|
||||
|
||||
$(TARGET8_NAME).bit: TARGET_FPGA = $(TARGET8_FPGA)
|
||||
$(TARGET8_NAME).bit: TARGET_UCF = $(TARGET8_UCF)
|
||||
$(TARGET8_NAME).bit: TARGET_FILES = $(TARGET8_FILES)
|
||||
$(TARGET8_NAME).bit: TARGET_XST_OPTS = $(TARGET8_XST_OPTS)
|
||||
|
||||
$(TARGET9_NAME).bit: TARGET_FPGA = $(TARGET9_FPGA)
|
||||
$(TARGET9_NAME).bit: TARGET_UCF = $(TARGET9_UCF)
|
||||
$(TARGET9_NAME).bit: TARGET_FILES = $(TARGET9_FILES)
|
||||
$(TARGET9_NAME).bit: TARGET_XST_OPTS = $(TARGET9_XST_OPTS)
|
||||
|
||||
$(TARGETS):
|
||||
$(Q)$(MKDIR) $(PREFIX)build_$@
|
||||
$(Q)$(MAKE) -C $(PREFIX)build_$@ -f ../proxmark3_fpga.mk $(notdir $@).bit
|
||||
|
||||
work:
|
||||
$(Q)$(RM) xst.prj
|
||||
$(Q)for item in $(TARGET_FILES); do echo verilog work ../$$item>>xst.prj; done
|
||||
$(Q)echo work> xst.lso
|
||||
|
||||
%.xst: work
|
||||
$(Q)$(RM) $@
|
||||
$(Q)echo $(TARGET_XST_OPTS)> $@
|
||||
|
||||
%.ngc: %.xst
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] XST $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)xst $(VERBOSITY) -ifn $<
|
||||
|
||||
%.ngd: %.ngc
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] NGD $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)ngdbuild $(VERBOSITY) -quiet -p $(TARGET_FPGA) -nt timestamp -uc ../$(TARGET_UCF) $< $@
|
||||
|
||||
%_map.ncd: %.ngd
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] MAP $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)map $(VERBOSITY) -p $(TARGET_FPGA) -o $*_map $*
|
||||
|
||||
%.ncd: %_map.ncd
|
||||
$(Q)$(RM) $@
|
||||
$(info [-] PAR $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)par $(PAR_OPTIONS) $(VERBOSITY) -w $< $@
|
||||
|
||||
%.bit: %.ncd
|
||||
$(Q)$(RM) $@ $*.drc $*.rbt
|
||||
$(info [=] BITGEN $@)
|
||||
$(Q)$(XILINX_TOOLS_PREFIX)bitgen $(VERBOSITY) -w $* $@
|
||||
$(Q)python3 ../$(STRIP_DATE_TIME) $@ || true
|
||||
$(Q)$(CP) $@ ..
|
||||
|
||||
# Build all targets
|
||||
all: $(TARGETS)
|
||||
|
||||
# ALWAYS have some hardcoded text after $(PREFIX) to avoid rm -rf * or rm -rf /* situations if PREFIX is incorrectly set to empty "" or just "/"
|
||||
clean:
|
||||
$(Q)$(RMDIR) $(PREFIX)build_*
|
||||
$(info [-] Build files deleted)
|
||||
|
||||
.DEFAULT:
|
||||
@if [ "$@" != "all" ] && [ ! "$(filter $@,$(TARGETS))" ]; then \
|
||||
make help; \
|
||||
else \
|
||||
make all; \
|
||||
fi
|
||||
|
||||
.PHONY: all help clean
|
||||
|
||||
help:
|
||||
@echo "################################################################"
|
||||
@echo "#"
|
||||
@echo "# <target> - Builds only one of the above listed targets"
|
||||
@echo "# all - Builds the FPGA bitstreams for all targets"
|
||||
@echo "# clean - Keeps .bit files but cleans intermediate build files for all targets"
|
||||
@echo "#"
|
||||
@echo "#"
|
||||
@echo "# Valid targets are:"
|
||||
@echo "# $(TARGETS)"
|
||||
@echo "#"
|
||||
@echo "################################################################"
|
||||
@@ -0,0 +1,171 @@
|
||||
#
|
||||
# Proxmark5 FPGA build (open source toolchain: yosys + nextpnr-himbaechel + apycula)
|
||||
#
|
||||
# Invoked by the fpga/Makefile dispatcher, or directly:
|
||||
# make -C fpga -f proxmark5_fpga.mk [all|clean]
|
||||
#
|
||||
# Overridable variables:
|
||||
# OSS_CAD_SUITE oss-cad-suite root, holds yosys/nextpnr-himbaechel/gowin_pack
|
||||
# PM5_FAMILY Gowin family, selects the chip database in nextpnr
|
||||
# PM5_PART exact part, must match the board
|
||||
# PM5_IDCODE IDCODE the bitstream declares, must match the silicon
|
||||
# PM5_CST pin constraints, generated by the Gowin IDE
|
||||
# PM5_SDC timing constraints, generated by the Gowin IDE
|
||||
# PM5_FREQ fallback clock constraint, used when PM5_SDC is empty
|
||||
# PM5_DEFINES HDL defines, one image covers both the LF and HF halves
|
||||
|
||||
include $(dir $(lastword $(MAKEFILE_LIST)))common_src.mk
|
||||
|
||||
# oss-cad-suite is looked up next to the checkout and under the usual Desktop
|
||||
# locations. Leave OSS_CAD_SUITE empty to use tools that are already on PATH
|
||||
# (i.e. when make runs inside the suite's own shell).
|
||||
OSS_CAD_SUITE ?= $(firstword $(wildcard \
|
||||
$(abspath $(CURDIR)/../../oss-cad-suite) \
|
||||
$(HOME)/Desktop/oss-cad-suite \
|
||||
$(USERPROFILE)/Desktop/oss-cad-suite))
|
||||
|
||||
|
||||
PM5_FAMILY ?= GW1N-4
|
||||
PM5_PART ?= GW1N-UV4QN48C6/I5
|
||||
PM5_IDCODE ?= 0x1100381B
|
||||
PM5_CST ?= $(PM5_DIR)/gowin.cst
|
||||
PM5_SDC ?= $(PM5_DIR)/gowin.sdc
|
||||
PM5_FREQ ?= 24
|
||||
PM5_DEFINES ?= PM5 WITH_TIMODE WITH_HF_15 WITH_FELICA_PROBE
|
||||
|
||||
# The Gowin IDE generated SDC covers every clock, but nextpnr only takes a
|
||||
# subset of SDC; it is rewritten into the build directory, leaving the original
|
||||
# file untouched for the next regeneration from the IDE. See the script for
|
||||
# what it drops. PM5_FREQ is the fallback for the unconstrained case.
|
||||
PM5_TIMING = $(if $(PM5_SDC),--sdc $(PM5_NAME).sdc,--freq $(PM5_FREQ))
|
||||
PM5_SDC_CLEAN = $(OC_ENV) $(OC_PYTHON) $(COMMON_DIR)/gowin_sdc_clean.py
|
||||
|
||||
PM5_NAME = fpga_pm5
|
||||
PM5_OUT = $(PREFIX)build_$(PM5_NAME)
|
||||
|
||||
# The oss-cad-suite launchers need their root, and both bin/ and lib/ on PATH
|
||||
# (lib/ carries the shared libraries), otherwise they exit without a message.
|
||||
# The suite is laid out per platform, and each of its own environment scripts
|
||||
# says what to put where. On Windows the shared libraries sit in lib/ and are
|
||||
# found through PATH, as environment.bat does. Everywhere else they are found by
|
||||
# the tools themselves, and adding lib/ to PATH or LD_LIBRARY_PATH makes yosys
|
||||
# load the suite's libstdc++ instead of the system one and segfault at startup.
|
||||
# With OSS_CAD_SUITE unset the tools come from PATH, which is what sourcing the
|
||||
# suite's environment gives you.
|
||||
ifneq ($(OSS_CAD_SUITE),)
|
||||
ifneq ($(wildcard $(OSS_CAD_SUITE)/lib/python3.exe),)
|
||||
OC_PATH = $(OSS_CAD_SUITE)/bin:$(OSS_CAD_SUITE)/lib
|
||||
OC_PYTHON = $(OSS_CAD_SUITE)/lib/python3.exe
|
||||
else
|
||||
OC_PATH = $(OSS_CAD_SUITE)/bin:$(OSS_CAD_SUITE)/py3bin
|
||||
OC_PYTHON = $(firstword $(wildcard $(OSS_CAD_SUITE)/py3bin/python3 $(OSS_CAD_SUITE)/bin/python3) python3)
|
||||
endif
|
||||
OC_ENV = YOSYSHQ_ROOT="$(OSS_CAD_SUITE)/" PATH="$(OC_PATH):$$PATH" PYTHON_EXECUTABLE="$(OC_PYTHON)"
|
||||
else
|
||||
OC_ENV =
|
||||
OC_PYTHON = python3
|
||||
endif
|
||||
|
||||
PM5_BUILD_HDL = $(addprefix ../,$(PM5_HDL))
|
||||
PM5_DEFS = $(foreach d,$(PM5_DEFINES),-D $(d))
|
||||
|
||||
.PHONY: all clean help check-tools smoke $(PM5_NAME)
|
||||
|
||||
all: $(PM5_NAME)
|
||||
|
||||
$(PM5_NAME): $(PM5_NAME).fs $(PM5_NAME).bin
|
||||
|
||||
# Only the bitstream is copied next to the Xilinx ones, everything else stays
|
||||
# in the build directory. Unlike the PM3 targets these recipes run in the fpga
|
||||
# directory itself, so the destination is this directory, not its parent.
|
||||
$(PM5_NAME).fs: $(PM5_OUT)/$(PM5_NAME).fs
|
||||
$(Q)$(CP) $< .
|
||||
|
||||
$(PM5_NAME).bin: $(PM5_OUT)/$(PM5_NAME).bin
|
||||
$(Q)$(CP) $< .
|
||||
|
||||
# The pin constraints are board specific and are not part of the repository.
|
||||
$(PM5_CST):
|
||||
$(error [!] missing $@ -- fill in the PM5 pin constraints, then rerun)
|
||||
|
||||
# The makefiles are prerequisites of the generated files so that editing the
|
||||
# defines or the source lists regenerates them.
|
||||
$(PM5_OUT)/$(PM5_NAME).json: $(PM5_HDL) $(MAKEFILE_LIST) | check-tools $(PM5_OUT)
|
||||
$(info [-] YOSYS $@)
|
||||
$(Q)$(RM) $@
|
||||
$(Q)printf '%s\n' \
|
||||
"read_verilog $(PM5_DEFS) $(PM5_BUILD_HDL)" \
|
||||
"hierarchy -top fpga_top" \
|
||||
"synth_gowin -nolutram -json $(PM5_NAME).json" > $(PM5_OUT)/synth.ys
|
||||
$(Q)cd $(PM5_OUT) && $(OC_ENV) yosys -q -s synth.ys
|
||||
|
||||
$(PM5_OUT)/$(PM5_NAME).sdc: $(PM5_SDC) $(COMMON_DIR)/gowin_sdc_clean.py $(MAKEFILE_LIST) | $(PM5_OUT)
|
||||
$(Q)$(PM5_SDC_CLEAN) $< $@
|
||||
|
||||
$(PM5_OUT)/$(PM5_NAME)_pnr.json: $(PM5_OUT)/$(PM5_NAME).json $(PM5_CST) $(if $(PM5_SDC),$(PM5_OUT)/$(PM5_NAME).sdc)
|
||||
$(info [=] NEXTPNR $@)
|
||||
$(Q)cd $(PM5_OUT) && $(OC_ENV) nextpnr-himbaechel \
|
||||
--device "$(PM5_PART)" \
|
||||
--vopt family=$(PM5_FAMILY) \
|
||||
--vopt cst=../$(PM5_CST) \
|
||||
$(PM5_TIMING) \
|
||||
--json $(PM5_NAME).json \
|
||||
--write $(PM5_NAME)_pnr.json
|
||||
|
||||
# TODO: workaround, drop once apycula can target GW1N-4D. The GW1N-4 database
|
||||
# apycula ships stamps the plain GW1N-4 IDCODE (0x0100381B), which the silicon
|
||||
# on a PM5 (GW1N-4B/4D, 0x1100381B) rejects: done_final stays 0 and nothing
|
||||
# drives the antennas. gowin_devid.py rewrites that field to PM5_IDCODE here so
|
||||
# the image is accepted at all. Remove this step, PM5_IDCODE and the workflow's
|
||||
# IDCODE check once apycula gains a GW1N-4B/4D target or stops discarding the
|
||||
# C6/I5 device version from the part number. Until then, suspect the fuse map
|
||||
# first when a bitstream misbehaves: it comes from the non-D database entry.
|
||||
$(PM5_OUT)/$(PM5_NAME).fs: $(PM5_OUT)/$(PM5_NAME)_pnr.json $(COMMON_DIR)/gowin_devid.py
|
||||
$(info [=] GOWIN_PACK $@)
|
||||
$(Q)cd $(PM5_OUT) && $(OC_ENV) gowin_pack -d $(PM5_FAMILY) -o $(PM5_NAME).fs $(PM5_NAME)_pnr.json
|
||||
$(Q)$(OC_ENV) $(OC_PYTHON) $(COMMON_DIR)/gowin_devid.py $(PM5_OUT)/$(PM5_NAME).fs $(PM5_IDCODE)
|
||||
|
||||
$(PM5_OUT)/$(PM5_NAME).bin: $(PM5_OUT)/$(PM5_NAME).fs
|
||||
$(info [=] FS2BIN $@)
|
||||
$(Q)cd $(PM5_OUT) && $(OC_ENV) $(OC_PYTHON) ../$(COMMON_DIR)/fs2bin.py $(PM5_NAME).fs $(PM5_NAME).bin
|
||||
|
||||
$(PM5_OUT):
|
||||
$(Q)$(MKDIR) $@
|
||||
|
||||
check-tools:
|
||||
$(Q)missing=""; \
|
||||
for t in yosys nextpnr-himbaechel gowin_pack; do \
|
||||
$(OC_ENV) command -v $$t >/dev/null 2>&1 || missing="$$missing $$t"; \
|
||||
done; \
|
||||
if [ -n "$$missing" ]; then \
|
||||
echo "[!] not found:$$missing"; \
|
||||
echo "[!] point OSS_CAD_SUITE at oss-cad-suite, or add its bin/ to PATH"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Compile a two line design through the same toolchain and environment the
|
||||
# build uses, so a suite that cannot run fails here with a clear message
|
||||
# instead of half way through the build.
|
||||
smoke: | check-tools $(PM5_OUT)
|
||||
$(Q)$(OC_ENV) yosys -V
|
||||
$(Q)printf '%s\n' "module smoke(input a, output b);" " assign b = ~a;" "endmodule" > $(PM5_OUT)/smoke.v
|
||||
$(Q)cd $(PM5_OUT) && $(OC_ENV) yosys -q -p "read_verilog smoke.v; synth_gowin -json smoke.json"
|
||||
$(Q)test -s $(PM5_OUT)/smoke.json && echo "[=] SMOKE ok"
|
||||
|
||||
# ALWAYS have some hardcoded text after $(PREFIX) to avoid rm -rf * or rm -rf /* situations if PREFIX is incorrectly set to empty "" or just "/"
|
||||
clean:
|
||||
$(Q)$(RMDIR) $(PREFIX)build_$(PM5_NAME)
|
||||
$(info [-] Build files deleted)
|
||||
|
||||
help:
|
||||
@echo "################################################################"
|
||||
@echo "#"
|
||||
@echo "# $(PM5_NAME) - Builds the PM5 bitstream into $(PM5_NAME).fs/.bin"
|
||||
@echo "# all - same as $(PM5_NAME)"
|
||||
@echo "# clean - Keeps the bitstream but cleans intermediate build files"
|
||||
@echo "#"
|
||||
@echo "# Device: $(PM5_PART) (family $(PM5_FAMILY))"
|
||||
@echo "# Pins : $(PM5_CST)"
|
||||
@echo "# Toolchain: $(OSS_CAD_SUITE)"
|
||||
@echo "#"
|
||||
@echo "################################################################"
|
||||
+5
-1
@@ -8,6 +8,10 @@
|
||||
MKDIR = mkdir -p
|
||||
TEST_OUTDIR = tb_tmp
|
||||
|
||||
# iverilog ships with oss-cad-suite; point at it when it is not on PATH, e.g.
|
||||
# make IVERILOG=$(HOME)/Desktop/oss-cad-suite/bin/iverilog
|
||||
IVERILOG ?= iverilog
|
||||
|
||||
TB_SOURCES = \
|
||||
tb_lp20khz_1MSa_iir_filter.v \
|
||||
tb_min_max_tracker.v \
|
||||
@@ -22,7 +26,7 @@ TB_DATA = \
|
||||
all: $(TBS) tests
|
||||
|
||||
%.vvp: %.v
|
||||
iverilog -I .. -o $@ $<
|
||||
$(IVERILOG) -I ../common -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf *.vvp $(TEST_OUTDIR)
|
||||
|
||||
Reference in new issue
Block a user