summaryrefslogtreecommitdiff
path: root/digital
diff options
context:
space:
mode:
authorNicolas Schodet2007-06-30 17:45:54 +0200
committerNicolas Schodet2007-06-30 17:45:54 +0200
commitd578aab00d511d4254ec200558bf2f17db481b73 (patch)
treef907950499429da592e8d5e2137cc7bf6454b382 /digital
parent7b17b553ad370dd3a1a957fed8c6b30de410d971 (diff)
Added hdlcounter for CPLD.
Verilog source files and test cases.
Diffstat (limited to 'digital')
-rw-r--r--digital/asserv/src/hdlcounter/Makefile25
-rw-r--r--digital/asserv/src/hdlcounter/README30
-rw-r--r--digital/asserv/src/hdlcounter/common.v85
-rw-r--r--digital/asserv/src/hdlcounter/counter_top.v58
-rw-r--r--digital/asserv/src/hdlcounter/input_latch.v46
-rw-r--r--digital/asserv/src/hdlcounter/noise_filter.v56
-rw-r--r--digital/asserv/src/hdlcounter/quad_decoder_div4.v75
-rw-r--r--digital/asserv/src/hdlcounter/quad_decoder_full.v71
-rw-r--r--digital/asserv/src/hdlcounter/test_counter_top.v147
-rw-r--r--digital/asserv/src/hdlcounter/test_counter_top.wave18
-rw-r--r--digital/asserv/src/hdlcounter/test_input_latch.v100
-rw-r--r--digital/asserv/src/hdlcounter/test_input_latch.wave14
-rw-r--r--digital/asserv/src/hdlcounter/test_noise_filter.v111
-rw-r--r--digital/asserv/src/hdlcounter/test_noise_filter.wave10
-rw-r--r--digital/asserv/src/hdlcounter/test_quad_decoder.v95
-rw-r--r--digital/asserv/src/hdlcounter/test_quad_decoder.wave17
-rw-r--r--digital/asserv/src/hdlcounter/test_updown_counter.v82
-rw-r--r--digital/asserv/src/hdlcounter/updown_counter.v55
18 files changed, 1095 insertions, 0 deletions
diff --git a/digital/asserv/src/hdlcounter/Makefile b/digital/asserv/src/hdlcounter/Makefile
new file mode 100644
index 00000000..13de43bb
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/Makefile
@@ -0,0 +1,25 @@
+EXEC = test_input_latch test_noise_filter test_updown_counter \
+ test_quad_decoder test_counter_top
+
+test_input_latch_SOURCES = test_input_latch.v input_latch.v
+test_noise_filter_SOURCES = test_noise_filter.v noise_filter.v
+test_updown_counter_SOURCES = test_updown_counter.v updown_counter.v
+test_quad_decoder_SOURCES = test_quad_decoder.v quad_decoder_div4.v \
+ quad_decoder_full.v
+test_counter_top_SOURCES = test_counter_top.v counter_top.v input_latch.v \
+ noise_filter.v quad_decoder_div4.v \
+ quad_decoder_full.v
+
+all: $(EXEC:%=%.vcd)
+
+%.vcd: %
+ vvp $<
+
+define EXEC_TEMPLATE
+$1: $$($1_SOURCES) common.v
+ iverilog -Wall -o $$@ $$($1_SOURCES)
+endef
+$(foreach exec,$(EXEC),$(eval $(call EXEC_TEMPLATE,$(exec))))
+
+clean:
+ rm -f $(EXEC) $(EXEC:%=%.vcd)
diff --git a/digital/asserv/src/hdlcounter/README b/digital/asserv/src/hdlcounter/README
new file mode 100644
index 00000000..b91a1eca
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/README
@@ -0,0 +1,30 @@
+hdlcounter - Incremental encoder counter on programmable logic.
+
+Microcontrolers often have counter inputs, but they do not have any dedicated
+inputs for incremental encoders which are therefore tendious to decode without
+any external logic.
+
+The hdlcounter implements this decoder in a CPLD or FPGA chip, allowing
+integration of several counter in one chip depending on the number of flip
+flop available.
+
+
+Copyright (C) 2007 Nicolas Schodet
+
+Robot APB Team 2008.
+ Web: http://apbteam.org/
+ Email: team AT apbteam DOT org
+
+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 2 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.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
diff --git a/digital/asserv/src/hdlcounter/common.v b/digital/asserv/src/hdlcounter/common.v
new file mode 100644
index 00000000..bfa6087c
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/common.v
@@ -0,0 +1,85 @@
+// common.v - Common useful test utilities.
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+
+ initial begin
+ $timeformat (-9, 0, "", 3);
+ end
+
+ task assert;
+ input a, b;
+ begin
+ if (a != b) begin
+ $display ("%t: assertion failled ", $time, a, " != ", b);
+ if (!debug)
+ $finish;
+ end
+ else if (debug)
+ $display ("%t: assertion ok ", $time, a, " == ", b);
+ end
+ endtask
+
+ // How to make this works for any size?
+ task assertv7;
+ parameter size = 7;
+ input [size-1:0] a, b;
+ begin
+ if (a != b) begin
+ $display ("%t: assertion failled ", $time, a, " != ", b);
+ if (!debug)
+ $finish;
+ end
+ else if (debug)
+ $display ("%t: assertion ok ", $time, a, " == ", b);
+ end
+ endtask
+
+ task assertv8;
+ parameter size = 8;
+ input [size-1:0] a, b;
+ begin
+ if (a != b) begin
+ $display ("%t: assertion failled ", $time, a, " != ", b);
+ if (!debug)
+ $finish;
+ end
+ else if (debug)
+ $display ("%t: assertion ok ", $time, a, " == ", b);
+ end
+ endtask
+
+ task assertv12;
+ parameter size = 12;
+ input [size-1:0] a, b;
+ begin
+ if (a != b) begin
+ $display ("%t: assertion failled ", $time, a, " != ", b);
+ if (!debug)
+ $finish;
+ end
+ else if (debug)
+ $display ("%t: assertion ok ", $time, a, " == ", b);
+ end
+ endtask
+
diff --git a/digital/asserv/src/hdlcounter/counter_top.v b/digital/asserv/src/hdlcounter/counter_top.v
new file mode 100644
index 00000000..fa1b737c
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/counter_top.v
@@ -0,0 +1,58 @@
+// counter_top.v
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module counter_top(clk, rst, q0, q1, q2, q3, oe, sel, count);
+ parameter size = 8;
+ input clk;
+ input rst;
+ input [1:0] q0, q1, q2, q3;
+ input oe;
+ input [1:0] sel;
+ output [size-1:0] count;
+
+ wire [1:0] qf0, qf1, qf2, qf3;
+ wire [size-1:0] count0, count1, count2, count3;
+
+ noise_filter f0[1:0] (clk, rst, q0, qf0);
+ quad_decoder_div4 qd0 (clk, rst, qf0, count0);
+
+ noise_filter f1[1:0] (clk, rst, q1, qf1);
+ quad_decoder_div4 qd1 (clk, rst, qf1, count1);
+
+ input_latch f2[1:0] (clk, rst, q2, qf2);
+ quad_decoder_full qd2 (clk, rst, qf2, count2);
+
+ input_latch f3[1:0] (clk, rst, q3, qf3);
+ quad_decoder_full qd3 (clk, rst, qf3, count3);
+
+ assign count =
+ !oe ? 8'bz :
+ sel == 0 ? count0 :
+ sel == 1 ? count1 :
+ sel == 2 ? count2 :
+ count3;
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/input_latch.v b/digital/asserv/src/hdlcounter/input_latch.v
new file mode 100644
index 00000000..5eb059d4
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/input_latch.v
@@ -0,0 +1,46 @@
+// input_latch.v - Input latch to protect from input change near clock edge.
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+// Input should be latched at clock rising edge in one and only one flip-flop.
+// If this latch is not done, two flip-flops connected to the input might
+// receive a different value if the input switch near the clock rising edge.
+
+module input_latch(clk, rst, q, ql);
+ input clk;
+ input rst;
+ input q;
+ output ql;
+
+ reg ql;
+
+ always @(posedge clk or negedge rst) begin
+ if (!rst)
+ ql <= 0;
+ else
+ ql <= q;
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/noise_filter.v b/digital/asserv/src/hdlcounter/noise_filter.v
new file mode 100644
index 00000000..7de58ab5
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/noise_filter.v
@@ -0,0 +1,56 @@
+// noise_filter.v - Low pass noise filter.
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+// To switch output, input should be stable for a number of cycle. This will
+// filter small spikes. As input is latched in only one flip-flop, this
+// module can be used directly on an input pin. See input_latch.v for more
+// details.
+
+module noise_filter(clk, rst, q, qf);
+ parameter size = 3;
+ input clk;
+ input rst;
+ input q;
+ output qf;
+
+ reg qf;
+ reg [size-1:0] hist;
+
+ always @(posedge clk or negedge rst) begin
+ if (!rst) begin
+ qf <= 0;
+ hist <= 0;
+ end
+ else begin
+ if (&hist)
+ qf <= 1;
+ else if (~|hist)
+ qf <= 0;
+ hist <= { hist[size-2:0], q };
+ end
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/quad_decoder_div4.v b/digital/asserv/src/hdlcounter/quad_decoder_div4.v
new file mode 100644
index 00000000..5021141b
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/quad_decoder_div4.v
@@ -0,0 +1,75 @@
+// quad_decoder_div4.v - One out of four quadrature signal decoder.
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+// Here is the signal:
+//
+// q[0]: _____------______------______----
+// q[1]: --______------______------______-
+// ^ ^ ^
+//
+// Counting is always done at the same point in order not to count false
+// steps. When the encoder signal is going forward (to the right on the
+// drawing), counting is done on the rising edge of q[1]. When the encoder is
+// going backward, counting is done on the falling edge of q[1]. Actually,
+// this is the same edge, but seen from an different point of view (forward or
+// backward). Counting is only done when q[0] is low.
+//
+// Timing constraints: one state duration should not be shorter than one clock
+// period, except when changing direction. In actual device, of course
+// a security factor should be added, beware of optical encoders tolerance.
+
+module quad_decoder_div4(clk, rst, q, count);
+ parameter bits = 8;
+ input clk;
+ input rst;
+ input [1:0] q;
+ output [bits-1:0] count;
+
+ reg [bits-1:0] count;
+ // Old input, only one channel to remember.
+ reg zq1;
+
+ always @(posedge clk or negedge rst) begin
+ if (!rst) begin
+ count <= 0;
+ zq1 <= 0;
+ end
+ else begin
+ case ({ q[0], zq1, q[1] })
+ // 1 to 0 transition on q[1], when q[0] is 0.
+ 2'b0_1_0:
+ count <= count + 1;
+ // 0 to 1 transition on q[1], when q[0] is 0.
+ 2'b0_0_1:
+ count <= count - 1;
+ default:
+ count <= count;
+ endcase
+ zq1 <= q[1];
+ end
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/quad_decoder_full.v b/digital/asserv/src/hdlcounter/quad_decoder_full.v
new file mode 100644
index 00000000..cdad0161
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/quad_decoder_full.v
@@ -0,0 +1,71 @@
+// quad_decoder_full.v - Full quadrature signal decoder.
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+// Here is the signal:
+//
+// q[0]: _____------______------______----
+// q[1]: --______------______------______-
+// ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
+//
+// Counting is done on any rising or falling edge. The encoder can be seen as
+// a absolute grey code encoder with two bits and four position. When one bit
+// change, it is easy to know what is the direction of the encoder knowing the
+// old state.
+//
+// Timing constraints: one state duration should not be shorter than one clock
+// period, except when changing direction. In actual device, of course
+// a security factor should be added, beware of optical encoders tolerance.
+
+module quad_decoder_full(clk, rst, q, count);
+ parameter bits = 8;
+ input clk;
+ input rst;
+ input [1:0] q;
+ output [bits-1:0] count;
+
+ reg [bits-1:0] count;
+ // Old input.
+ reg [1:0] zq;
+
+ always @(posedge clk or negedge rst) begin
+ if (!rst) begin
+ count <= 0;
+ zq <= 0;
+ end
+ else begin
+ case ({ zq, q })
+ 4'b00_01, 4'b01_11, 4'b11_10, 4'b10_00:
+ count <= count + 1;
+ 4'b01_00, 4'b11_01, 4'b10_11, 4'b00_10:
+ count <= count - 1;
+ default:
+ count <= count;
+ endcase
+ zq <= q;
+ end
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/test_counter_top.v b/digital/asserv/src/hdlcounter/test_counter_top.v
new file mode 100644
index 00000000..b0ff69a1
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_counter_top.v
@@ -0,0 +1,147 @@
+// test_counter_top.v
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module test_counter_top();
+ parameter debug = 0;
+ parameter nc = 4;
+ reg clk;
+ reg rst;
+ reg [1:0] q[0:nc-1];
+ reg oe;
+ reg [1:0] sel;
+ wire [7:0] countout;
+
+ `include "common.v"
+
+ // Clock generator.
+ always #5 clk <= !clk;
+
+ // Counter top setup.
+ wire [0:nc-1] quad_full = 4'b0011;
+ reg [31:0] filter_size[0:nc-1];
+ parameter max_filter = 5;
+ initial begin
+ filter_size[0] = 4;
+ filter_size[1] = 4;
+ filter_size[2] = 1;
+ filter_size[3] = 1;
+ end
+
+ // Instantiation.
+ counter_top uut (clk, rst, q[0], q[1], q[2], q[3], oe, sel, countout);
+
+ // The count variable is the true encoder position, multiplied by 32,
+ // which is more than one encoder minimum period with a noise filter of
+ // size 3 to satisfy decoding logic timing constraints.
+ integer i;
+ integer n[0:nc-1], diff[0:nc-1];
+ reg [31:0] count[0:nc-1];
+ reg [27:0] countdiv32[0:nc-1];
+ reg [1:0] q_nat;
+
+ wire [27:0] countdiv32_0 = countdiv32[0],
+ countdiv32_1 = countdiv32[1],
+ countdiv32_2 = countdiv32[2],
+ countdiv32_3 = countdiv32[3];
+
+ initial begin
+ $dumpfile ("test_counter_top.vcd");
+ $dumpvars (1, clk, rst, countdiv32_0, countdiv32_1, countdiv32_2,
+ countdiv32_3, uut, oe, sel, countout, countassert, a);
+ clk <= 1;
+ rst <= 0;
+ for (i = 0; i < nc; i = i + 1) begin
+ q[i] <= 0;
+ n[i] = 0;
+ count[i] = 0;
+ countdiv32[i] <= 0;
+ end
+ #2 rst <= 1;
+ #6 // 2 ns before clock edge.
+ repeat (debug ? 5000 : 100000) begin
+ #1
+ for (i = 0; i < nc; i = i + 1) begin
+ if (n[i] == 0) begin
+ n[i] = $random;
+ n[i] = n[i] > 0 ? n[i] : -n[i];
+ n[i] = n[i] % 50 + 1;
+ diff[i] = $random % 2;
+ if (debug)
+ $display ("%t.%1d: n %1d diff %1d", $time, i, n[i], diff[i]);
+ end
+ n[i] = n[i] - 1;
+ count[i] = count[i] + diff[i];
+ q_nat = (count[i] / 32) % 4;
+ countdiv32[i] <= count[i] / 32;
+ q[i] <= { q_nat[1], q_nat[0] ^ q_nat[1] };
+ end
+ end
+ $finish;
+ end
+
+ // This simulates a two dimension array.
+ reg [27:0] countdiv32_smp[0:nc*max_filter-1];
+ integer j;
+ reg [27:0] countassert;
+ reg [7:0] countassert8;
+
+ wire [27:0] a = countdiv32_smp[0*max_filter+4];
+
+ initial begin
+ forever begin
+ @(posedge clk)
+ // Sample countdiv32 at rising edge.
+ for (j = 1; j < nc*max_filter; j = j + 1)
+ countdiv32_smp[j] <= countdiv32_smp[j-1];
+ for (j = 0; j < nc; j = j + 1)
+ countdiv32_smp[j * max_filter] <= countdiv32[j];
+ @(negedge clk)
+ // Check result *after* rising edge.
+ if (oe) begin
+ countassert = countdiv32_smp[sel*max_filter + filter_size[sel]];
+ countassert8 = quad_full[sel] ? countassert[7:0] : countassert[9:2];
+ // If equiped with a noise filter, accept a difference of 1.
+ // This is more difficult to find the exact expected value (I
+ // mean, without copy-paste the unit under test verbatim).
+ if (filter_size[sel] > 1)
+ assert ((countassert8 - countout) | 1, 1);
+ else
+ assertv8 (countassert8, countout);
+ end
+ else begin
+ assertv8 (8'bz, countout);
+ end
+ // Prepare next check.
+ if (($mti_random & 6'b111111) == 0)
+ oe = 0;
+ else begin
+ oe = 1;
+ sel = $random & 2'b11;
+ end
+ end
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/test_counter_top.wave b/digital/asserv/src/hdlcounter/test_counter_top.wave
new file mode 100644
index 00000000..338660ef
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_counter_top.wave
@@ -0,0 +1,18 @@
+*-17,989300 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+@28
+test_counter_top.clk
+test_counter_top.rst
+@420
+test_counter_top.countdiv32_0[27:0]
+test_counter_top.countdiv32_1[27:0]
+test_counter_top.countdiv32_2[27:0]
+test_counter_top.countdiv32_3[27:0]
+@28
+test_counter_top.oe
+test_counter_top.sel[1:0]
+@22
+test_counter_top.countout[7:0]
+test_counter_top.uut.count0[7:0]
+test_counter_top.uut.count1[7:0]
+test_counter_top.uut.count2[7:0]
+test_counter_top.uut.count3[7:0]
diff --git a/digital/asserv/src/hdlcounter/test_input_latch.v b/digital/asserv/src/hdlcounter/test_input_latch.v
new file mode 100644
index 00000000..744ee09b
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_input_latch.v
@@ -0,0 +1,100 @@
+// test_input_latch.v
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module test_input_latch();
+ parameter debug = 0;
+ reg clk;
+ reg rst;
+ reg q;
+ wire ql;
+
+ `include "common.v"
+
+ // Clock generator.
+ always #5 clk <= !clk;
+
+ // Instantiation.
+ input_latch uut (clk, rst, q, ql);
+
+ // This demonstrates what could happen without a latch. The wire qnl1 and
+ // qnl2 represents the input signal after two different paths in the chip,
+ // where the qnl2 path is longer. On clock edge, q is sampled, but the
+ // flip flops are incoherent.
+ wire qnl1, qnl2;
+ reg qnlr1, qnlr2;
+ assign #1 qnl1 = q;
+ assign #2 qnl2 = ~q;
+ always @(posedge clk or negedge rst) begin
+ if (!rst) begin
+ qnlr1 <= 0;
+ qnlr2 <= 1;
+ end
+ else begin
+ // Actual equation is qnlr1 = q, qnl1 simulates delay inside the
+ // chip.
+ qnlr1 <= qnl1;
+ // Actual equation is qnlr2 = ~q, qnl2 simulates a longer delay
+ // inside the chip.
+ qnlr2 <= qnl2;
+ end
+ end
+
+ // Now, with latched input.
+ wire ql1, ql2;
+ reg qlr1, qlr2;
+ assign #1 ql1 = ql;
+ assign #2 ql2 = ~ql;
+ always @(posedge clk or negedge rst) begin
+ if (!rst) begin
+ qlr1 <= 0;
+ qlr2 <= 1;
+ end
+ else begin
+ qlr1 <= ql1;
+ qlr2 <= ql2;
+ end
+ end
+
+ initial begin
+ $dumpfile ("test_input_latch.vcd");
+ $dumpvars;
+ clk <= 1;
+ rst <= 0;
+ q <= 0;
+ #2 rst <= 1;
+ repeat (15) begin
+ // One more than clock period to test several switch time.
+ #11 q <= ~q;
+ end
+ $finish;
+ end
+
+ initial begin
+ #1 forever
+ #1 assert (qlr1, ~qlr2);
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/test_input_latch.wave b/digital/asserv/src/hdlcounter/test_input_latch.wave
new file mode 100644
index 00000000..50af7fd0
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_input_latch.wave
@@ -0,0 +1,14 @@
+*-15,452410 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+@28
+test_input_latch.clk
+test_input_latch.rst
+test_input_latch.q
+test_input_latch.ql
+test_input_latch.ql1
+test_input_latch.ql2
+test_input_latch.qlr1
+test_input_latch.qlr2
+test_input_latch.qnl1
+test_input_latch.qnl2
+test_input_latch.qnlr1
+test_input_latch.qnlr2
diff --git a/digital/asserv/src/hdlcounter/test_noise_filter.v b/digital/asserv/src/hdlcounter/test_noise_filter.v
new file mode 100644
index 00000000..05737845
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_noise_filter.v
@@ -0,0 +1,111 @@
+// test_noise_filter.v
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module test_noise_filter();
+ parameter debug = 0;
+ reg clk;
+ reg rst;
+ reg q;
+ wire ql2, ql3, ql5;
+ reg zql2, zql3, zql5;
+
+ `include "common.v"
+
+ // Clock generator.
+ always #5 clk <= !clk;
+
+ // Instantiation.
+ noise_filter #(2) uut_size2 (clk, rst, q, ql2);
+ noise_filter uut_size3 (clk, rst, q, ql3);
+ noise_filter #(5) uut_size5 (clk, rst, q, ql5);
+
+ integer i;
+ time r, to;
+
+ initial begin
+ $dumpfile ("test_noise_filter.vcd");
+ $dumpvars;
+ clk <= 1;
+ rst <= 0;
+ q <= 0;
+ zql2 <= 0; zql3 <= 0; zql5 <= 0;
+ #2 rst <= 1;
+ #6 // 2 ns before clock edge.
+ // Toggle q, then draw a random delay. Check that filtered output
+ // switch at the right time.
+ q <= ~q;
+ repeat (1000) begin
+ #1 r = $random;
+ r = r % 8 + 1;
+ to = (r * 10) - 1;
+ if (debug)
+ $display ("%t: rand %1d", $time, r);
+ fork
+ // This checks the filtered output is unchanged before the
+ // right clock edge, and changed after, or not changed at all
+ // when the delay is too small.
+ // Did not found an easy way to factorise this (tasks sample
+ // input at invocation):
+ begin
+ if (r > 2) begin
+ #(2 * 10) assert (ql2, zql2);
+ #2 assert (ql2, q);
+ end
+ else
+ #(to) assert (ql2, zql2);
+ if (r >= 2)
+ zql2 <= q;
+ end
+ begin
+ if (r > 3) begin
+ #(3 * 10) assert (ql3, zql3);
+ #2 assert (ql3, q);
+ end
+ else
+ #(to) assert (ql3, zql3);
+ if (r >= 3)
+ zql3 <= q;
+ end
+ begin
+ if (r > 5) begin
+ #(5 * 10) assert (ql5, zql5);
+ #2 assert (ql5, q);
+ end
+ else
+ #(to) assert (ql5, zql5);
+ if (r >= 5)
+ zql5 <= q;
+ end
+ begin
+ #(to) q <= ~q;
+ end
+ join
+ r = 0;
+ end
+ $finish;
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/test_noise_filter.wave b/digital/asserv/src/hdlcounter/test_noise_filter.wave
new file mode 100644
index 00000000..ca69157e
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_noise_filter.wave
@@ -0,0 +1,10 @@
+*-16,630791 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+@28
+test_noise_filter.clk
+test_noise_filter.rst
+test_noise_filter.q
+test_noise_filter.ql2
+test_noise_filter.ql3
+test_noise_filter.ql5
+@25
+test_noise_filter.r[63:0]
diff --git a/digital/asserv/src/hdlcounter/test_quad_decoder.v b/digital/asserv/src/hdlcounter/test_quad_decoder.v
new file mode 100644
index 00000000..8e0b0c23
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_quad_decoder.v
@@ -0,0 +1,95 @@
+// test_quad_decoder.v
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module test_quad_decoder();
+ parameter debug = 0;
+ reg clk;
+ reg rst;
+ reg [1:0] q;
+ wire [7:0] count_div4, count_full;
+
+ `include "common.v"
+
+ // Clock generator.
+ always #5 clk <= !clk;
+
+ // Instantiation.
+ quad_decoder_div4 uut_div4 (clk, rst, q, count_div4);
+ quad_decoder_full uut_full (clk, rst, q, count_full);
+
+ // The count variable is the true encoder position, multiplied by 16,
+ // which is more than one clock period to satisfy decoding logic timing
+ // constraints.
+ integer n, diff;
+ reg [31:0] count;
+ reg [27:0] countdiv16;
+ reg [1:0] q_nat;
+
+ initial begin
+ $dumpfile ("test_quad_decoder.vcd");
+ $dumpvars;
+ clk <= 1;
+ rst <= 0;
+ q <= 0;
+ n = 0;
+ count = 0;
+ countdiv16 <= 0;
+ #2 rst <= 1;
+ #6 // 2 ns before clock edge.
+ repeat (debug ? 1000 : 100000) begin
+ #1
+ if (n == 0) begin
+ n = $random;
+ n = n > 0 ? n : -n;
+ n = n % 50 + 1;
+ diff = $random % 2;
+ if (debug)
+ $display ("%t: n %1d diff %1d", $time, n, diff);
+ end
+ n = n - 1;
+ count = count + diff;
+ q_nat = (count / 16) % 4;
+ countdiv16 <= count / 16;
+ q <= { q_nat[1], q_nat[0] ^ q_nat[1] };
+ end
+ $finish;
+ end
+
+ reg [27:0] countdiv16_smp;
+
+ initial begin
+ forever begin
+ @(posedge clk)
+ // Sample countdiv16 at rising edge.
+ countdiv16_smp <= countdiv16;
+ @(negedge clk)
+ // Check result *after* rising edge.
+ assertv8 (countdiv16_smp[7:0], count_full);
+ assertv8 (countdiv16_smp[9:2], count_div4);
+ end
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/test_quad_decoder.wave b/digital/asserv/src/hdlcounter/test_quad_decoder.wave
new file mode 100644
index 00000000..01ae64b9
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_quad_decoder.wave
@@ -0,0 +1,17 @@
+*-14,155944 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
+@28
+test_quad_decoder.clk
+test_quad_decoder.rst
+@24
+test_quad_decoder.n[31:0]
+@420
+test_quad_decoder.diff[31:0]
+test_quad_decoder.count[31:0]
+test_quad_decoder.countdiv16[27:0]
+test_quad_decoder.countdiv16_smp[27:0]
+@28
+test_quad_decoder.q_nat[1:0]
+test_quad_decoder.q[1:0]
+@22
+test_quad_decoder.count_div4[7:0]
+test_quad_decoder.count_full[7:0]
diff --git a/digital/asserv/src/hdlcounter/test_updown_counter.v b/digital/asserv/src/hdlcounter/test_updown_counter.v
new file mode 100644
index 00000000..ebe5d115
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/test_updown_counter.v
@@ -0,0 +1,82 @@
+// test_updown_counter.v
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module test_updown_counter();
+ parameter debug = 0;
+ reg clk;
+ reg rst;
+ reg [1:0] updown;
+ wire [7:0] count8;
+ wire [11:0] count12;
+
+ `include "common.v"
+
+ // Clock generator.
+ always #5 clk <= !clk;
+
+ // Instantiation.
+ updown_counter uut_size8 (clk, rst, updown, count8);
+ updown_counter #(12) uut_size12 (clk, rst, updown, count12);
+
+ integer n, count, diff;
+
+ initial begin
+ $dumpfile ("test_updown_counter.vcd");
+ $dumpvars;
+ clk <= 1;
+ rst <= 0;
+ updown <= 0;
+ count = 0;
+ n = 0;
+ #2 rst <= 1;
+ repeat (1000) begin
+ @(negedge clk)
+ if (n == 0) begin
+ n = $random;
+ n = n > 0 ? n : -n;
+ n = n % 16 + 1;
+ diff = $random % 2;
+ if (debug)
+ $display ("%t: n %1d diff %1d", $time, n, diff);
+ end
+ n = n - 1;
+ count = count + diff;
+ if (diff > 0)
+ updown <= 2'b10;
+ else if (diff < 0)
+ updown <= 2'b01;
+ else
+ updown <= 2'b00;
+ @(posedge clk) #1
+ assertv8 (count[7:0], count8);
+ assertv12 (count[11:0], count12);
+ end
+ if (debug)
+ $display ("%t: count %1d", $time, count);
+ $finish;
+ end
+
+endmodule
diff --git a/digital/asserv/src/hdlcounter/updown_counter.v b/digital/asserv/src/hdlcounter/updown_counter.v
new file mode 100644
index 00000000..04f73bbd
--- /dev/null
+++ b/digital/asserv/src/hdlcounter/updown_counter.v
@@ -0,0 +1,55 @@
+// updown_counter.v - Up and down counter.
+// hdlcounter - Incremental encoder counter on programmable logic. {{{
+//
+// Copyright (C) 2007 Nicolas Schodet
+//
+// Robot APB Team 2008.
+// Web: http://apbteam.org/
+// Email: team AT apbteam DOT org
+//
+// 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 2 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.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+// }}}
+`timescale 1ns / 1ps
+
+module updown_counter(clk, rst, updown, counter);
+ parameter bits = 8;
+ input clk;
+ input rst;
+ // Bit updown[1] is for up and bit updown[0] is for down.
+ input [1:0] updown;
+ output [bits-1:0] counter;
+
+ reg [bits-1:0] counter;
+
+ always @(posedge clk or negedge rst) begin
+ if (!rst) begin
+ counter <= 0;
+ end
+ else begin
+ case (updown)
+ 2'b00:
+ counter <= counter;
+ 2'b10:
+ counter <= counter + 1;
+ 2'b01:
+ counter <= counter - 1;
+ // Do not synthesis useless logic, 2'b11 is an undefined case.
+ default:
+ counter <= { bits { 1'bX }};
+ endcase
+ end
+ end
+endmodule