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
parent e17437f985
commit 3b2fee43ea
36 changed files with 685 additions and 57 deletions

1
.gitignore vendored
View file

@ -19,6 +19,7 @@ lua
luac
fpga/*
!fpga/tests
!fpga/fpga_lf.bit
!fpga/fpga_hf.bit
!fpga/*.v

View file

@ -81,6 +81,7 @@ void SetAdcMuxFor(uint32_t whichGpio);
// Definitions for the FPGA commands.
#define FPGA_CMD_SET_CONFREG (1<<12)
#define FPGA_CMD_SET_DIVISOR (2<<12)
#define FPGA_CMD_SET_USER_BYTE1 (3<<12)
// Definitions for the FPGA configuration word.
// LF
#define FPGA_MAJOR_MODE_LF_ADC (0<<5)
@ -96,7 +97,9 @@ void SetAdcMuxFor(uint32_t whichGpio);
// Options for LF_ADC
#define FPGA_LF_ADC_READER_FIELD (1<<0)
// Options for LF_EDGE_DETECT
#define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD FPGA_CMD_SET_USER_BYTE1
#define FPGA_LF_EDGE_DETECT_READER_FIELD (1<<0)
#define FPGA_LF_EDGE_DETECT_TOGGLE_MODE (1<<1)
// Options for the HF reader, tx to tag
#define FPGA_HF_READER_TX_SHALLOW_MOD (1<<0)
// Options for the HF reader, correlating against rx from tag

View file

@ -9,7 +9,7 @@ fpga_hf.ngc: fpga_hf.v fpga.ucf xst_hf.scr util.v hi_simulate.v hi_read_tx.v hi_
$(DELETE) $@
$(XILINX_TOOLS_PREFIX)xst -ifn xst_hf.scr
fpga_lf.ngc: fpga_lf.v fpga.ucf xst_lf.scr util.v clk_divider.v lo_edge_detect.v lo_read.v lo_passthru.v
fpga_lf.ngc: fpga_lf.v fpga.ucf xst_lf.scr util.v clk_divider.v lo_edge_detect.v lo_read.v lo_passthru.v lp20khz_1MSa_iir_filter.v min_max_tracker.v lf_edge_detect.v
$(DELETE) $@
$(XILINX_TOOLS_PREFIX)xst -ifn xst_lf.scr

Binary file not shown.

View file

@ -1,13 +1,4 @@
//-----------------------------------------------------------------------------
// The FPGA is responsible for interfacing between the A/D, the coil drivers,
// and the ARM. In the low-frequency modes it passes the data straight
// through, so that the ARM gets raw A/D samples over the SSP. In the high-
// frequency modes, the FPGA might perform some demodulation first, to
// reduce the amount of data that we must send to the ARM.
//
// I am not really an FPGA/ASIC designer, so I am sure that a lot of this
// could be improved.
//
// Jonathan Westhues, March 2006
// iZsh <izsh at fail0verflow.com>, June 2014
//-----------------------------------------------------------------------------
@ -39,15 +30,20 @@ module fpga_lf(
reg [15:0] shift_reg;
reg [7:0] divisor;
reg [7:0] conf_word;
reg [7:0] user_byte1;
// 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
case(shift_reg[15:12])
4'b0001: conf_word <= shift_reg[7:0]; // FPGA_CMD_SET_CONFREG
4'b0010: divisor <= shift_reg[7:0]; // FPGA_CMD_SET_DIVISOR
4'b0001:
begin
conf_word <= shift_reg[7:0];
if (shift_reg[7:0] == 8'b00000001) begin // LF edge detect
user_byte1 <= 127; // default threshold
end
end
4'b0010: divisor <= shift_reg[7:0]; // FPGA_CMD_SET_DIVISOR
4'b0011: user_byte1 <= shift_reg[7:0]; // FPGA_CMD_SET_USER_BYTE1
endcase
end
@ -60,11 +56,12 @@ begin
end
end
wire [2:0] major_mode;
assign major_mode = conf_word[7:5];
wire [2:0] major_mode = conf_word[7:5];
// For the low-frequency configuration:
wire lf_field = conf_word[0];
wire lf_ed_toggle_mode = conf_word[1]; // for lo_edge_detect
wire [7:0] lf_ed_threshold = user_byte1;
//-----------------------------------------------------------------------------
// And then we instantiate the modules corresponding to each of the FPGA's
@ -93,13 +90,14 @@ lo_passthru lp(
);
lo_edge_detect le(
pck0, pck_cnt, pck_divclk,
pck0, pck_divclk,
le_pwr_lo, le_pwr_hi, le_pwr_oe1, le_pwr_oe2, le_pwr_oe3, le_pwr_oe4,
adc_d, le_adc_clk,
le_ssp_frame, ssp_dout, le_ssp_clk,
cross_lo,
le_dbg,
lf_field
lf_field,
lf_ed_toggle_mode, lf_ed_threshold
);
// Major modes:
@ -108,7 +106,7 @@ lo_edge_detect le(
// 010 -- LF passthrough
mux8 mux_ssp_clk (major_mode, ssp_clk, lr_ssp_clk, le_ssp_clk, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0);
mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0);
mux8 mux_ssp_din (major_mode, ssp_din, lr_ssp_din, 1'b0, lp_ssp_din, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0);
mux8 mux_ssp_frame (major_mode, ssp_frame, lr_ssp_frame, le_ssp_frame, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0);
mux8 mux_pwr_oe1 (major_mode, pwr_oe1, lr_pwr_oe1, le_pwr_oe1, lp_pwr_oe1, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0);
mux8 mux_pwr_oe2 (major_mode, pwr_oe2, lr_pwr_oe2, le_pwr_oe2, lp_pwr_oe2, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0);

77
fpga/lf_edge_detect.v Normal file
View file

@ -0,0 +1,77 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// input clk is 24Mhz
`include "min_max_tracker.v"
module lf_edge_detect(input clk, input [7:0] adc_d, input [7:0] lf_ed_threshold,
output [7:0] max, output [7:0] min,
output [7:0] high_threshold, output [7:0] highz_threshold,
output [7:0] lowz_threshold, output [7:0] low_threshold,
output edge_state, output edge_toggle);
min_max_tracker tracker(clk, adc_d, lf_ed_threshold, min, max);
// auto-tune
assign high_threshold = (max + min) / 2 + (max - min) / 4;
assign highz_threshold = (max + min) / 2 + (max - min) / 8;
assign lowz_threshold = (max + min) / 2 - (max - min) / 8;
assign low_threshold = (max + min) / 2 - (max - min) / 4;
// heuristic to see if it makes sense to try to detect an edge
wire enabled =
(high_threshold > highz_threshold)
& (highz_threshold > lowz_threshold)
& (lowz_threshold > low_threshold)
& ((high_threshold - highz_threshold) > 8)
& ((highz_threshold - lowz_threshold) > 16)
& ((lowz_threshold - low_threshold) > 8);
// Toggle the output with hysteresis
// Set to high if the ADC value is above the threshold
// Set to low if the ADC value is below the threshold
reg is_high = 0;
reg is_low = 0;
reg is_zero = 0;
reg trigger_enabled = 1;
reg output_edge = 0;
reg output_state;
always @(posedge clk)
begin
is_high <= (adc_d >= high_threshold);
is_low <= (adc_d <= low_threshold);
is_zero <= ((adc_d > lowz_threshold) & (adc_d < highz_threshold));
end
// all edges detection
always @(posedge clk)
if (enabled) begin
// To enable detecting two consecutive peaks at the same level
// (low or high) we check whether or not we went back near 0 in-between.
// This extra check is necessary to prevent from noise artifacts
// around the threshold values.
if (trigger_enabled & (is_high | is_low)) begin
output_edge <= ~output_edge;
trigger_enabled <= 0;
end else
trigger_enabled <= trigger_enabled | is_zero;
end
// edge states
always @(posedge clk)
if (enabled) begin
if (is_high)
output_state <= 1'd1;
else if (is_low)
output_state <= 1'd0;
end
assign edge_state = output_state;
assign edge_toggle = output_edge;
endmodule

View file

@ -1,21 +1,34 @@
//-----------------------------------------------------------------------------
// The way that we connect things in low-frequency simulation mode. In this
// case just pass everything through to the ARM, which can bit-bang this
// (because it is so slow).
// Copyright (C) 2014 iZsh <izsh at fail0verflow.com>
//
// Jonathan Westhues, April 2006
// iZsh <izsh at fail0verflow.com>, June 2014
// 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.
//-----------------------------------------------------------------------------
//
// There are two modes:
// - lf_ed_toggle_mode == 0: the output is set low (resp. high) when a low
// (resp. high) edge/peak is detected, with hysteresis
// - lf_ed_toggle_mode == 1: the output is toggling whenever an edge/peak
// is detected.
// That way you can detect two consecutive edges/peaks at the same level (L/H)
//
// Output:
// - ssp_frame (wired to TIOA1 on the arm) for the edge detection/state
// - ssp_clk: cross_lo
`include "lp20khz_1MSa_iir_filter.v"
`include "lf_edge_detect.v"
module lo_edge_detect(
input pck0, input [7:0] pck_cnt, input pck_divclk,
output pwr_lo, output pwr_hi,
output pwr_oe1, output pwr_oe2, output pwr_oe3, output pwr_oe4,
input [7:0] adc_d, output adc_clk,
output ssp_frame, input ssp_dout, output ssp_clk,
input cross_lo,
output dbg,
input lf_field
input pck0, input pck_divclk,
output pwr_lo, output pwr_hi,
output pwr_oe1, output pwr_oe2, output pwr_oe3, output pwr_oe4,
input [7:0] adc_d, output adc_clk,
output ssp_frame, input ssp_dout, output ssp_clk,
input cross_lo,
output dbg,
input lf_field,
input lf_ed_toggle_mode, input [7:0] lf_ed_threshold
);
wire tag_modulation = ssp_dout & !lf_field;
@ -29,34 +42,25 @@ assign pwr_oe4 = tag_modulation;
assign ssp_clk = cross_lo;
assign pwr_lo = reader_modulation;
assign pwr_hi = 1'b0;
assign dbg = ssp_frame;
assign adc_clk = ~pck_divclk;
// filter the ADC values
wire data_rdy;
wire [7:0] adc_filtered;
assign adc_clk = pck0;
lp20khz_1MSa_iir_filter adc_filter(pck0, adc_d, data_rdy, adc_filtered);
// Toggle the output with hysteresis
// Set to high if the ADC value is above 200
// Set to low if the ADC value is below 64
reg is_high;
reg is_low;
reg output_state;
// detect edges
wire [7:0] high_threshold, highz_threshold, lowz_threshold, low_threshold;
wire [7:0] max, min;
wire edge_state, edge_toggle;
lf_edge_detect lf_ed(pck0, adc_filtered, lf_ed_threshold,
max, min,
high_threshold, highz_threshold, lowz_threshold, low_threshold,
edge_state, edge_toggle);
always @(posedge pck0)
begin
if((pck_cnt == 8'd7) && !pck_divclk) begin
is_high = (adc_d >= 8'd190);
is_low = (adc_d <= 8'd70);
end
end
assign dbg = lf_ed_toggle_mode ? edge_toggle : edge_state;
always @(posedge is_high or posedge is_low)
begin
if(is_high)
output_state <= 1'd1;
else if(is_low)
output_state <= 1'd0;
end
assign ssp_frame = output_state;
assign ssp_frame = lf_ed_toggle_mode ? edge_toggle : edge_state;
endmodule

View file

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// Butterworth low pass IIR filter
// input: 8bit ADC signal, 1MS/s
// output: 8bit value, Fc=20khz
//
// coef: (using http://www-users.cs.york.ac.uk/~fisher/mkfilter/trad.html)
// Recurrence relation:
// y[n] = ( 1 * x[n- 2])
// + ( 2 * x[n- 1])
// + ( 1 * x[n- 0])
// + ( -0.8371816513 * y[n- 2])
// + ( 1.8226949252 * y[n- 1])
//
// therefore:
// a = [1,2,1]
// b = [-0.8371816513, 1.8226949252]
// b is approximated to b = [-0xd6/0x100, 0x1d3 / 0x100] (for optimization)
// gain = 2.761139367e2
//
// See details about its design see
// https://fail0verflow.com/blog/2014/proxmark3-fpga-iir-filter.html
module lp20khz_1MSa_iir_filter(input clk, input [7:0] adc_d, output rdy, output [7:0] out);
// clk is 24Mhz, the IIR filter is designed for 1MS/s
// hence we need to divide it by 24
// using a shift register takes less area than a counter
reg [23:0] cnt = 1;
assign rdy = cnt[0];
always @(posedge clk)
cnt <= {cnt[22:0], cnt[23]};
reg [7:0] x0 = 0;
reg [7:0] x1 = 0;
reg [16:0] y0 = 0;
reg [16:0] y1 = 0;
always @(posedge clk)
begin
if (rdy)
begin
x0 <= x1;
x1 <= adc_d;
y0 <= y1;
y1 <=
// center the signal:
// input range is [0; 255]
// We want "128" to be at the center of the 17bit register
// (128+z)*gain = 17bit center
// z = (1<<16)/gain - 128 = 109
// 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
// we want "- y0 * 0xd6 / 0x100" using only shift and add
// 0xd6 == 0b11010110
// so *0xd6/0x100 is equivalent to
// ((x << 1) + (x << 2) + (x << 4) + (x << 6) + (x << 7)) >> 8
// which is also equivalent to
// (x >> 7) + (x >> 6) + (x >> 4) + (x >> 2) + (x >> 1)
- ((y0 >> 7) + (y0 >> 6) + (y0 >> 4) + (y0 >> 2) + (y0 >> 1)) // - y0 * 0xd6 / 0x100
// we want "+ y1 * 0x1d3 / 0x100"
// 0x1d3 == 0b111010011
// so this is equivalent to
// ((x << 0) + (x << 1) + (x << 4) + (x << 6) + (x << 7) + (x << 8)) >> 8
// which is also equivalent to
// (x >> 8) + (x >> 7) + (x >> 4) + (x >> 2) + (x >> 1) + (x >> 0)
+ ((y1 >> 8) + (y1 >> 7) + (y1 >> 4) + (y1 >> 2) + (y1 >> 1) + y1);
end
end
// output: reduce to 8bit
assign out = y1[16:9];
endmodule

65
fpga/min_max_tracker.v Normal file
View file

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
// track min and max peak values (envelope follower)
//
// NB: the min value (resp. max value) is updated only when the next high peak
// (resp. low peak) is reached/detected, since you can't know it isn't a
// local minima (resp. maxima) until then.
// This also means the peaks are detected with an unpredictable delay.
// This algorithm can't therefore be used directly for realtime peak detections,
// but it can be used as a simple envelope follower.
module min_max_tracker(input clk, input [7:0] adc_d, input [7:0] threshold,
output [7:0] min, output [7:0] max);
reg [7:0] min_val = 255;
reg [7:0] max_val = 0;
reg [7:0] cur_min_val = 255;
reg [7:0] cur_max_val = 0;
reg [1:0] state = 0;
always @(posedge clk)
begin
case (state)
0:
begin
if (cur_max_val >= ({1'b0, adc_d} + threshold))
state <= 2;
else if (adc_d >= ({1'b0, cur_min_val} + threshold))
state <= 1;
if (cur_max_val <= adc_d)
cur_max_val <= adc_d;
else if (adc_d <= cur_min_val)
cur_min_val <= adc_d;
end
1:
begin
if (cur_max_val <= adc_d)
cur_max_val <= adc_d;
else if (({1'b0, adc_d} + threshold) <= cur_max_val) begin
state <= 2;
cur_min_val <= adc_d;
max_val <= cur_max_val;
end
end
2:
begin
if (adc_d <= cur_min_val)
cur_min_val <= adc_d;
else if (adc_d >= ({1'b0, cur_min_val} + threshold)) begin
state <= 1;
cur_max_val <= adc_d;
min_val <= cur_min_val;
end
end
endcase
end
assign min = min_val;
assign max = max_val;
endmodule

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