tsfpga VHDL coverage


Directory: generated/vunit_out/preprocessed/
File: generated/vunit_out/preprocessed/common/periodic_pulser.vhd
Date: 2021-07-25 04:08:32
Exec Total Coverage
Lines: 45 45 100.0%
Branches: 61 92 66.3%

Line Branch Exec Source
1 66 -- -------------------------------------------------------------------------------------------------
2 -- Copyright (c) Lukas Vik. All rights reserved.
3 --
4 -- This file is part of the tsfpga project.
5 -- https://tsfpga.com
6 -- https://gitlab.com/tsfpga/tsfpga
7 -- -------------------------------------------------------------------------------------------------
8 -- Outputs a one cycle pulse after a generic number of assertions of count_enable.
9 --
10 -- In the worst case, this module simply creates a counter, but before that it tries to
11 -- use shift registers as far as possible. This makes the implementation resource efficient
12 -- on devices with cheap shift registers.
13 --
14 -- The period is broken down into factors that are represented using shift
15 -- registers, with the shift register length being the factor value. By rotating the shift register
16 -- on each count enable, a fixed period is created.
17 -- The remaining period is sent to a new instance of period_pulser.
18 --
19 -- Step 1:
20 -- As far as possible and-gate multiple shift registers together. The output of this stage
21 -- is then sent to the next instance of period_pulser
22 -- This method only works if the lengths are mutual primes.
23 -- One or more shift registers may be created.
24 --
25 -- Step 2:
26 -- If the factor cannot be further broken down, add a simple counter.
27 --
28 -- -------------------------------------------------------------------------------------------------
29 -- Example:
30 -- Let's say that the maximum shift register length is 16.
31 -- A period of 12*37 can then be achieved using two shift registers of length 4 and 3,
32 -- and then instantiating a new period_pulser:
33 -- [0][0][0][1]
34 -- \
35 -- [and] -> pulse -> [period_pulser of period 37]
36 -- /
37 -- [0][0][1]
38 -- The next stage will create a counter, because 37 is a prime larger than the maximum shift
39 -- register length.
40 -- -------------------------------------------------------------------------------------------------
41
42 library ieee;
43 use ieee.numeric_std.all;
44 use ieee.std_logic_1164.all;
45
46 library math;
47 use math.math_pkg.all;
48
49
50
4/6
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 6 times.
110 entity periodic_pulser is
51 generic (
52 -- The period between pulses
53 period : integer range 2 to integer'high;
54 -- The shift register length is device specific.
55 -- For Xilinx ultrascale and 7 series devices, it should be set to 32
56 shift_register_length : integer
57 );
58 port (
59 4 clk : in std_logic := '0';
60 --
61 4 count_enable : in std_logic := '1';
62 4 pulse : out std_logic := '0'
63 );
64 end entity;
65
66 22 architecture a of periodic_pulser is
67
68 -- Make a type where we can store all the factors used for Step 1.
69 -- It doesn't actually need to be this long.
70 subtype factors_vec_t is integer_vector(0 to shift_register_length - 1);
71 type stage_factors_t is record
72 this_stage : factors_vec_t;
73 next_stage : integer;
74 num_factors_this_stage : integer;
75 end record;
76 195 constant stage_factors_init : stage_factors_t := (this_stage => (others => 0), others => 0);
77
78 -- Factorize into mutual primes as far as possible
79 function get_mutual_prime_factors(value : positive) return stage_factors_t is
80 11 variable remaining_value : integer := value;
81 11 variable result : stage_factors_t := stage_factors_init;
82 11 variable idx : integer range 0 to shift_register_length := 0;
83 begin
84 -- Start with shift_register_length and work downward, as we want as large factors as
85 -- possible in each shift register
86 11 for factor_to_test in minimum(shift_register_length, value) downto 2 loop
87 170 if remaining_value rem factor_to_test = 0 then
88 28 if idx = 0 then
89 -- First factor discovered
90 10 remaining_value := remaining_value / factor_to_test;
91 10 result.this_stage(idx) := factor_to_test;
92 10 idx := idx + 1;
93 else
94 -- We have a candidate, but check that it is a mutual prime with other factors
95 18 if is_mutual_prime(factor_to_test, result.this_stage(0 to idx - 1)) then
96 6 remaining_value := remaining_value / factor_to_test;
97 6 result.this_stage(idx) := factor_to_test;
98 176 idx := idx + 1;
99 end if;
100 end if;
101 end if;
102 end loop;
103
104 11 result.next_stage := remaining_value;
105 11 result.num_factors_this_stage := idx;
106
107
3/4
✓ Branch 0 taken 205 times.
✓ Branch 1 taken 407 times.
✓ Branch 2 taken 204 times.
✗ Branch 3 not taken.
827 return result;
108 end function;
109
110 22 constant factors : stage_factors_t := get_mutual_prime_factors(period);
111
112 379 signal shift_reg_outputs : std_logic_vector(factors_vec_t'range) := (others => '1');
113 22 signal pulse_this_stage : std_logic := '0';
114
115 begin
116
117 ------------------------------------------------------------------------------
118 -- No further shift register factorization possible, use counter
119 ------------------------------------------------------------------------------
120 gen_counter : if factors.num_factors_this_stage = 0 generate
121 11 signal tick_count : integer range 0 to period - 1 := 0;
122 begin
123 1 count : process
124 begin
125
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 407 times.
✓ Branch 6 taken 204 times.
✓ Branch 7 taken 203 times.
816 wait until rising_edge(clk);
126
127
2/2
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 92 times.
204 if count_enable then
128
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 109 times.
112 if tick_count = period - 1 then
129
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
3 tick_count <= 0;
130 else
131
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 109 times.
✓ Branch 5 taken 109 times.
✗ Branch 6 not taken.
408 tick_count <= tick_count + 1;
132 end if;
133 end if;
134 end process;
135
136
11/14
✓ Branch 0 taken 155002 times.
✓ Branch 1 taken 309956 times.
✓ Branch 2 taken 154986 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 153 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 153 times.
✓ Branch 13 taken 3 times.
✓ Branch 14 taken 150 times.
620288 pulse <= count_enable when tick_count = period - 1 else '0';
137 end generate;
138
139 ------------------------------------------------------------------------------
140 -- Create one or more shift registers
141 -- This generate is "else generate" with gen_counter, but "else generate"
142 -- statements seem to fail recursive instantiation in Vivado.
143 ------------------------------------------------------------------------------
144 gen_shift_registers : if factors.num_factors_this_stage > 0 generate
145 186 gen_mutual_prime_srls : for idx in factors.this_stage'range generate
146 176 gen_only_if_not_0 : if factors.this_stage(idx) /= 0 generate
147 -- Create a shift register of the length of the current factor factor
148 562 signal shift_reg : std_logic_vector(0 to factors.this_stage(idx) - 1) := (0 => '1', others => '0');
149 begin
150 204 shift : process
151 begin
152
3/4
✗ Branch 3 not taken.
✓ Branch 4 taken 309956 times.
✓ Branch 6 taken 154986 times.
✓ Branch 7 taken 154970 times.
619944 wait until rising_edge(clk);
153
2/2
✓ Branch 1 taken 53188 times.
✓ Branch 2 taken 101798 times.
154986 if count_enable then
154
14/24
✗ Branch 0 not taken.
✓ Branch 1 taken 53188 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 53188 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 53188 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 53188 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 53188 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 53188 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 53188 times.
✓ Branch 20 taken 53188 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✓ Branch 23 taken 53188 times.
✓ Branch 26 taken 702241 times.
✓ Branch 27 taken 53188 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 702241 times.
✓ Branch 30 taken 106376 times.
✓ Branch 31 taken 595865 times.
1012213 shift_reg <= shift_reg(shift_reg'high) & shift_reg(0 to shift_reg'high - 1);
155 end if;
156 end process;
157
158
4/8
✗ Branch 0 not taken.
✓ Branch 1 taken 16306 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 16306 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 16306 times.
✓ Branch 7 taken 16306 times.
✗ Branch 8 not taken.
16492 shift_reg_outputs(idx) <= shift_reg(shift_reg'high);
159 end generate;
160 end generate;
161
162 -- Gate all shift register results.
163 -- Because they are mutual primes, the total period will be the product of their lengthes
164
3/4
✗ Branch 2 not taken.
✓ Branch 3 taken 25508 times.
✓ Branch 4 taken 1200 times.
✓ Branch 5 taken 24308 times.
25694 pulse_this_stage <= and(shift_reg_outputs) and count_enable;
165
166
167 ------------------------------------------------------------------------------
168 -- Instantiate next stage with the remaining period, or end recursion if done
169 ------------------------------------------------------------------------------
170 gen_next_stage : if factors.next_stage > 1 generate
171
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
214 periodic_pulser_next_stage : entity work.periodic_pulser
172 generic map (
173 period => factors.next_stage,
174 shift_register_length => shift_register_length)
175 port map (
176 clk => clk,
177 count_enable => pulse_this_stage,
178 pulse => pulse
179 );
180 end generate;
181 do_not_gen_next_stage : if factors.next_stage <= 1 generate
182 -- Another stage is not needed
183
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 92 times.
✓ Branch 3 taken 6 times.
239 pulse <= pulse_this_stage;
184 end generate;
185 end generate;
186
187 end architecture;
188