Essentially, my question is: "can this not be done any easier?"; and what 'this' is, follows below (code too):
I wanted to have a sort of a 'complement' counter function, implemented in VHDL, which would basically invert/complement/not the counter value in each step, giving slightly richer bit patterns for testing. Of course, I wanted this as synthesizable (so the counter value could be possibly assigned to pins) and portable code (i.e. implemented only IEEE libraries, no STD_LOGIC_ARITH
). I also don't want to by default treat all as unsigned (so I'd like to avoid STD_LOGIC_UNSIGNED
).
In brief, this counter could be described as: given initial value C[0], then values at each clock tick would be:
C[i+1] = not(C[i]) + ( ( C[i]<(Cmax/2) ) ? 0 : 1 )
... or given C is 16-bit wide (which will result with unsigned Cmax = 65535 and Cmax/2 = 32768), it could also be written as:
C[i+1] = 65535 - C[i] + ( ( C[i]<32768 ) ? 0 : 1 )
The trick here is that the counter should only increment once - if it increases for both complementary and 'normal' ranges, then no changes will happen (equation will 'oscillate' between two values).
So, given that the check C[i]<(Cmax/2) is basically the same as checking the most significant (15th) bit of C, I thought that I could easily implement something like this in VHDL using something like:
Y <= not(Y) + Y(15);
Boy, was I wrong about "easily" :)
First problem is that it is possible the above equation could end up at 65535+1, in which case the result will need 17 bits (i.e. overflow); in my case, I'd simply want to truncate/ignore any 'carry bits'.
This leads into the problem of what to use:
std_logic_vector
has complementnot()
defined; but it doesn't have+
(addition) definednatural
/integer
may internally take up 32 bits, and as such bit width for them is not necesarilly specified; they support arithmetic+
, but no complementnot()
- I tried
unsigned
too, had some problems also (cannot remember which)
The 15th (MSB) bit can only be extracted when Y is std_logic_vector
, in which case, Y(15) is a single std_logic
- but then, it needs to be converted to integer
type, because otherwise addition +
is not defined :|
So, my current solution (below) first has two copies of the counter register; one is SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0)
; the other is SIGNAL tmp_na : natural
. Then:
- There are two clocks: one 'master' @ 50 MHz, the other is the 'counter' clock: the master 16-times frequency divided (3.125 MHz).
- The 'counter' clock should activate calculation of counter values on falling edge
- The calculation is performed via
natural
variable (which copies from theSTD_LOGIC_VECTOR
one) - Apparently,
std_logic
can only be converted tointeger
if its converted tostd_logic_vector
first (I was lucky to find thevectorize
function on the net).
The nastiest part here was how to feed back the natural
variable value, back to the STD_LOGIC_VECTOR
one; the only working command I could construct is:
wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
...; however, note that this command basically 'sets' the value, which will be 'effectuated' next time this same command runs. Thus, it cannot run in the 'counter' clock process - in the code below, I have it in the faster 'master' clock process.
Finally, the code below does work (goes through behavioral simulation in ISE WebPack) - but, I'd still like to know if there is a more straightforward way to solve this.
Thanks in advance for any answers, Cheers!
The code:
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- use IEEE.STD_LOGIC_ARITH.ALL;
-- use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
ENTITY complement_count_test_tbw IS
END complement_count_test_tbw;
ARCHITECTURE testbench_arch OF complement_count_test_tbw IS
-- http://www.ingenieurbuero-eschemann.de/downloads/ipicregs/example/vhdl/test/timer_regs_tb.vhd
-- convert std_logic to std_logic_vector(0 downto 0)
function vectorize(s: std_logic) return std_logic_vector is
variable v: std_logic_vector(0 downto 0);
begin
v(0) := s;
return v;
end;
-- DECLARE REGISTERS ==========================
-- 'wires'
SIGNAL wtCLK : std_logic := '0';
-- counter register: 16 bit
SIGNAL wCntReg : STD_LOGIC_VECTOR(15 DOWNTO 0) := (others => 'Z');
-- temporary 'natural' copy of counter register
-- http://www.velocityreviews.com/forums/t21700-std_logic_vector-to-unsigned-type-casting.html
SIGNAL tmp_na : natural;
-- clock parameters
constant PERIODN : natural := 20; -- can be real := 20.0;
constant PERIOD : time := PERIODN * 1 ns;
constant DUTY_CYCLE : real := 0.5;
constant OFFSET : time := 100 ns;
-- freq divisor; with initial values
constant fdiv : natural := 16;
SIGNAL fdiv_cnt : natural := 1;
SIGNAL wfdiv_CLK : std_logic := '0';
BEGIN
-- initializations of connections:
-- instances of components, and their wiring (port maps)...
-- END instances of components, and their wiring (port maps)...
-- PROCESSES (STATE MACHINES) CODE =========
-- clock process for generating CLK
PROCESS
BEGIN
WAIT for OFFSET;
CLOCK_LOOP : LOOP
wtCLK <= '0';
-- MUST refresh counter reg here with value of tmp_na
wCntReg <= std_logic_vector(to_unsigned(natural'pos(tmp_na), wCntReg'length));
WAIT FOR (PERIOD - (PERIOD * DUTY_CYCLE));
wtCLK <= '1';
WAIT FOR (PERIOD * DUTY_CYCLE);
END LOOP CLOCK_LOOP;
END PROCESS;
-- freq divided clock
freq_divisor: PROCESS(wtCLK)
BEGIN
IF rising_edge(wtCLK) THEN -- posedge
IF fdiv_cnt = fdiv THEN
-- reset
fdiv_cnt <= 1 ;
wfdiv_CLK <= not(wfdiv_CLK);
ELSE
fdiv_cnt <= fdiv_cnt + 1;
END IF;
END IF;
END PROCESS freq_divisor;
-- sim: count
PROCESS
BEGIN
WAIT for 10 ns;
tmp_na <= 125;
WAIT for 10 ns;
TESTCOUNT_LOOP: LOOP
-- change counter on negedge of freq. divided clock
WAIT until falling_edge(wfdiv_CLK);
tmp_na <= to_integer(unsigned(not(wCntReg))) + to_integer(unsigned(vectorize(wCntReg(15))));
WAIT for 10 ns;
END LOOP TESTCOUNT_LOOP;
END PROCESS;
-- END PROCESSES (STATE MACHINES) CODE =====
-- END IMPLEMENT ENGINE of 'CORE' ===============
END testbench_arch;
-- END ARCHITECTURE -----------------------------