New LF edge detection algorithm + lowpass filter

This is a new LF edge detection algorithm for the FPGA.

- It uses a low-pass IIR filter to clean the signal
(see https://fail0verflow.com/blog/2014/proxmark3-fpga-iir-filter.html)
- The algorithm is able to detect consecutive peaks in the same
  direction
- It uses an envelope follower to dynamically adjust the peak thresholds
- The main threshold used in the envelope follower can be set from the ARM side

fpga/lf_edge_detect.v,
fpga/lp20khz_1MSa_iir_filter.v,
fpga/min_max_tracker.v: New file.

fpga/lo_edge_detect.v, fpga/fpga_lf.v: Modify accordingly.

armsrc/apps.h (FPGA_CMD_SET_USER_BYTE1,
FPGA_CMD_SET_EDGE_DETECT_THRESHOLD): New FPGA command.
fpga/fpga_lf.v: Modify accordingly/Add a 8bit user register.

fpga/fpga_lf.bit: Update accordingly.

fpga/tests: New directory for testbenches

fpga/tests/Makefile: New file. It compiles the testbenches
and runs all the tests by default (comparing with the golden output)

fpga/tests/tb_lp20khz_1MSa_iir_filter.v,
fpga/tests/tb_min_max_tracker.v,
fpga/tests/tb_lf_edge_detect.v: New testbenches

fpga/tests/plot_edgedetect.py: New script to plot the results from
the edge detection tests.

fpga/tests/tb_data: New directory for data and golden outputs
This commit is contained in:
iZsh 2014-06-22 00:26:38 +02:00
commit 3b2fee43ea
36 changed files with 685 additions and 57 deletions

87
fpga/tests/Makefile Normal file
View file

@ -0,0 +1,87 @@
#-----------------------------------------------------------------------------
# Copyright (C) 2014 iZsh <izsh at fail0verflow.com>
#
# This code is licensed to you under the terms of the GNU GPL, version 2 or,
# at your option, any later version. See the LICENSE.txt file for the text of
# the license.
#-----------------------------------------------------------------------------
TEST_OUTDIR = tb_tmp
TB_SOURCES = \
tb_lp20khz_1MSa_iir_filter.v \
tb_min_max_tracker.v \
tb_lf_edge_detect.v
TBS = $(TB_SOURCES:.v=.vvp)
TB_DATA = \
pcf7931_write1byte_1MSA_data \
pcf7931_read_1MSA_data
all: $(TBS) tests
%.vvp: %.v
iverilog -I .. -o $@ $<
clean:
rm -rf *.vvp $(TEST_OUTDIR)
tests: tb_lp20khz_1MSa_iir_filter tb_min_max_tracker tb_lf_edge_detect
tb_lp20khz_1MSa_iir_filter: tb_lp20khz_1MSa_iir_filter.vvp | test_dir
@printf "Testing $@\n"
@for d in $(TB_DATA); do \
$(call run_test,$@.vvp,$$d,in); \
$(call check_golden,$$d,filtered); \
done; \
rm -f $(TEST_OUTDIR)/data.*
tb_min_max_tracker: tb_min_max_tracker.vvp | test_dir
@printf "Testing $@\n"
@for d in $(TB_DATA); do \
$(call run_test,$@.vvp,$$d,in filtered.gold); \
$(call check_golden,$$d,min); \
$(call check_golden,$$d,max); \
done; \
rm -f $(TEST_OUTDIR)/data.*
tb_lf_edge_detect: tb_lf_edge_detect.vvp | test_dir
@printf "Testing $@\n"
@for d in $(TB_DATA); do \
$(call run_test,$@.vvp,$$d,in filtered.gold); \
$(call check_golden,$$d,min); \
$(call check_golden,$$d,max); \
$(call check_golden,$$d,state); \
$(call check_golden,$$d,toggle); \
$(call check_golden,$$d,high); \
$(call check_golden,$$d,highz); \
$(call check_golden,$$d,lowz); \
$(call check_golden,$$d,low); \
done; \
rm -f $(TEST_OUTDIR)/data.*
test_dir:
@if [ ! -d $(TEST_OUTDIR) ] ; then mkdir $(TEST_OUTDIR) ; fi
.PHONY: all clean
# $(1) = basename
# $(2) = extension to check
check_golden = \
printf " Checking $(1).$(2)... "; \
mv $(TEST_OUTDIR)/data.$(2) $(TEST_OUTDIR)/$(1).$(2); \
if cmp -s tb_data/$(1).$(2).gold $(TEST_OUTDIR)/$(1).$(2); then \
printf "OK\n"; \
else \
printf "ERROR\n"; \
fi
# $(1) = vvp file
# $(2) = data basename
# $(3) = data extensions to copy
run_test = \
env echo " With $(2)... "; \
cp tb_data/$(2).time $(TEST_OUTDIR); \
for e in $(3); do cp tb_data/$(2).$$e $(TEST_OUTDIR)/data.$$e; done; \
./$(1)

58
fpga/tests/plot_edgedetect.py Executable file
View file

@ -0,0 +1,58 @@
#!/usr/bin/env python
#-----------------------------------------------------------------------------
# Copyright (C) 2014 iZsh <izsh at fail0verflow.com>
#
# This code is licensed to you under the terms of the GNU GPL, version 2 or,
# at your option, any later version. See the LICENSE.txt file for the text of
# the license.
#-----------------------------------------------------------------------------
import numpy
import matplotlib.pyplot as plt
import sys
if len(sys.argv) != 2:
print "Usage: %s <basename>" % sys.argv[0]
sys.exit(1)
BASENAME = sys.argv[1]
nx = numpy.fromfile(BASENAME + ".time")
def plot_time(dat1):
plt.plot(nx, dat1)
sig = open(BASENAME + ".filtered").read()
sig = map(lambda x: ord(x), sig)
min_vals = open(BASENAME + ".min").read()
min_vals = map(lambda x: ord(x), min_vals)
max_vals = open(BASENAME + ".max").read()
max_vals = map(lambda x: ord(x), max_vals)
states = open(BASENAME + ".state").read()
states = map(lambda x: ord(x) * 10 + 65, states)
toggles = open(BASENAME+ ".toggle").read()
toggles = map(lambda x: ord(x) * 10 + 80, toggles)
high = open(BASENAME + ".high").read()
high = map(lambda x: ord(x), high)
highz = open(BASENAME + ".highz").read()
highz = map(lambda x: ord(x), highz)
lowz = open(BASENAME + ".lowz").read()
lowz = map(lambda x: ord(x), lowz)
low = open(BASENAME + ".low").read()
low = map(lambda x: ord(x), low)
plot_time(sig)
plot_time(min_vals)
plot_time(max_vals)
plot_time(states)
plot_time(toggles)
plot_time(high)
plot_time(highz)
plot_time(lowz)
plot_time(low)
plt.show()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2014 iZsh <izsh at fail0verflow.com>
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// testbench for lf_edge_detect
`include "lf_edge_detect.v"
`define FIN "tb_tmp/data.filtered.gold"
`define FOUT_MIN "tb_tmp/data.min"
`define FOUT_MAX "tb_tmp/data.max"
`define FOUT_STATE "tb_tmp/data.state"
`define FOUT_TOGGLE "tb_tmp/data.toggle"
`define FOUT_HIGH "tb_tmp/data.high"
`define FOUT_HIGHZ "tb_tmp/data.highz"
`define FOUT_LOWZ "tb_tmp/data.lowz"
`define FOUT_LOW "tb_tmp/data.low"
module lf_edge_detect_tb;
integer fin, fout_state, fout_toggle;
integer fout_high, fout_highz, fout_lowz, fout_low, fout_min, fout_max;
integer r;
reg clk = 0;
reg [7:0] adc_d;
wire adc_clk;
wire data_rdy;
wire edge_state;
wire edge_toggle;
wire [7:0] high_threshold;
wire [7:0] highz_threshold;
wire [7:0] lowz_threshold;
wire [7:0] low_threshold;
wire [7:0] max;
wire [7:0] min;
initial
begin
clk = 0;
fin = $fopen(`FIN, "r");
if (!fin) begin
$display("ERROR: can't open the data file");
$finish;
end
fout_min = $fopen(`FOUT_MIN, "w+");
fout_max = $fopen(`FOUT_MAX, "w+");
fout_state = $fopen(`FOUT_STATE, "w+");
fout_toggle = $fopen(`FOUT_TOGGLE, "w+");
fout_high = $fopen(`FOUT_HIGH, "w+");
fout_highz = $fopen(`FOUT_HIGHZ, "w+");
fout_lowz = $fopen(`FOUT_LOWZ, "w+");
fout_low = $fopen(`FOUT_LOW, "w+");
if (!$feof(fin))
adc_d = $fgetc(fin); // read the first value
end
always
# 1 clk = !clk;
// input
initial
begin
while (!$feof(fin)) begin
@(negedge clk) adc_d <= $fgetc(fin);
end
if ($feof(fin))
begin
# 3 $fclose(fin);
$fclose(fout_state);
$fclose(fout_toggle);
$fclose(fout_high);
$fclose(fout_highz);
$fclose(fout_lowz);
$fclose(fout_low);
$fclose(fout_min);
$fclose(fout_max);
$finish;
end
end
initial
begin
// $monitor("%d\t S: %b, E: %b", $time, edge_state, edge_toggle);
end
// output
always @(negedge clk)
if ($time > 2) begin
r = $fputc(min, fout_min);
r = $fputc(max, fout_max);
r = $fputc(edge_state, fout_state);
r = $fputc(edge_toggle, fout_toggle);
r = $fputc(high_threshold, fout_high);
r = $fputc(highz_threshold, fout_highz);
r = $fputc(lowz_threshold, fout_lowz);
r = $fputc(low_threshold, fout_low);
end
// module to test
lf_edge_detect detect(clk, adc_d, 8'd127,
max, min,
high_threshold, highz_threshold,
lowz_threshold, low_threshold,
edge_state, edge_toggle);
endmodule

View file

@ -0,0 +1,55 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2014 iZsh <izsh at fail0verflow.com>
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// testbench for lp20khz_1MSa_iir_filter
`include "lp20khz_1MSa_iir_filter.v"
`define FIN "tb_tmp/data.in"
`define FOUT "tb_tmp/data.filtered"
module lp20khz_1MSa_iir_filter_tb;
integer fin, fout, r;
reg clk;
reg [7:0] adc_d;
wire data_rdy;
wire [7:0] adc_filtered;
initial
begin
clk = 0;
fin = $fopen(`FIN, "r");
if (!fin) begin
$display("ERROR: can't open the data file");
$finish;
end
fout = $fopen(`FOUT, "w+");
if (!$feof(fin))
adc_d = $fgetc(fin); // read the first value
end
always
# 1 clk = !clk;
always @(posedge clk)
if (data_rdy) begin
if ($time > 1)
r = $fputc(adc_filtered, fout);
if (!$feof(fin))
adc_d <= $fgetc(fin);
else begin
$fclose(fin);
$fclose(fout);
$finish;
end
end
// module to test
lp20khz_1MSa_iir_filter filter(clk, adc_d, data_rdy, adc_filtered);
endmodule

View file

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2014 iZsh <izsh at fail0verflow.com>
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// testbench for min_max_tracker
`include "min_max_tracker.v"
`define FIN "tb_tmp/data.filtered.gold"
`define FOUT_MIN "tb_tmp/data.min"
`define FOUT_MAX "tb_tmp/data.max"
module min_max_tracker_tb;
integer fin;
integer fout_min, fout_max;
integer r;
reg clk;
reg [7:0] adc_d;
wire [7:0] min;
wire [7:0] max;
initial
begin
clk = 0;
fin = $fopen(`FIN, "r");
if (!fin) begin
$display("ERROR: can't open the data file");
$finish;
end
fout_min = $fopen(`FOUT_MIN, "w+");
fout_max = $fopen(`FOUT_MAX, "w+");
if (!$feof(fin))
adc_d = $fgetc(fin); // read the first value
end
always
# 1 clk = !clk;
// input
initial
begin
while (!$feof(fin)) begin
@(negedge clk) adc_d <= $fgetc(fin);
end
if ($feof(fin))
begin
# 3 $fclose(fin);
$fclose(fout_min);
$fclose(fout_max);
$finish;
end
end
initial
begin
// $monitor("%d\t min: %x, max: %x", $time, min, max);
end
// output
always @(negedge clk)
if ($time > 2) begin
r = $fputc(min, fout_min);
r = $fputc(max, fout_max);
end
// module to test
min_max_tracker tracker(clk, adc_d, 8'd127, min, max);
endmodule