GCC Code Coverage Report
Directory: generated/vunit_out/preprocessed/ Exec Total Coverage
File: generated/vunit_out/preprocessed/common/periodic_pulser.vhd Lines: 45 45 100.0 %
Date: 2021-06-12 04:12:08 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

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

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

816
      wait until rising_edge(clk);
126
127
204
      if count_enable then
128
112
        if tick_count = period - 1 then
129

3
          tick_count <= 0;
130
        else
131

408
          tick_count <= tick_count + 1;
132
        end if;
133
      end if;
134
    end process;
135
136



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

619944
          wait until rising_edge(clk);
153
154986
          if count_enable then
154






1012213
            shift_reg <= shift_reg(shift_reg'high) & shift_reg(0 to shift_reg'high - 1);
155
          end if;
156
        end process;
157
158


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

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
14
      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

141
      pulse <= pulse_this_stage;
184
    end generate;
185
  end generate;
186
187
end architecture;