Skip to content
Snippets Groups Projects
Commit f90bb671 authored by Daniel van der Schuur's avatar Daniel van der Schuur
Browse files

-Added dp_counter.

parent 544dc4fd
Branches
No related tags found
No related merge requests found
...@@ -129,6 +129,7 @@ synth_files = ...@@ -129,6 +129,7 @@ synth_files =
src/vhdl/dp_folder.vhd src/vhdl/dp_folder.vhd
src/vhdl/dp_unfolder.vhd src/vhdl/dp_unfolder.vhd
src/vhdl/dp_switch.vhd src/vhdl/dp_switch.vhd
src/vhdl/dp_counter.vhd
tb/vhdl/dp_stream_player.vhd tb/vhdl/dp_stream_player.vhd
tb/vhdl/dp_sosi_recorder.vhd tb/vhdl/dp_sosi_recorder.vhd
tb/vhdl/dp_stream_rec_play.vhd tb/vhdl/dp_stream_rec_play.vhd
...@@ -196,6 +197,7 @@ test_bench_files = ...@@ -196,6 +197,7 @@ test_bench_files =
tb/vhdl/tb_dp_sync_insert.vhd tb/vhdl/tb_dp_sync_insert.vhd
tb/vhdl/tb_dp_folder.vhd tb/vhdl/tb_dp_folder.vhd
tb/vhdl/tb_dp_switch.vhd tb/vhdl/tb_dp_switch.vhd
tb/vhdl/tb_dp_counter.vhd
tb/vhdl/tb_tb_dp_block_gen.vhd tb/vhdl/tb_tb_dp_block_gen.vhd
tb/vhdl/tb_tb_dp_block_gen_arr.vhd tb/vhdl/tb_tb_dp_block_gen_arr.vhd
......
--------------------------------------------------------------------------------
--
-- Copyright (C) 2017
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- 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 3 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, see <http://www.gnu.org/licenses/>.
--
--------------------------------------------------------------------------------
-- Author:
-- . Daniel van der Schuur
-- Purpose:
-- . Provide an array of counters with dimension carryover
-- Description:
-- . Provides 5 counters, [c4][c3][c2][c1][c0]
-- . Every counter is specified like Python's range(start,stop,step).
-- . c0 changes the fastest, c4 changes the slowest
-- . Faster changing dimensions carry over in slower changing dimensions
-- when dimension maximum is reached
-- . The outputs are in sync with / apply to src_out.
-- . range(0,2,1) = [0, 1] is the smallest count range allowed
-- . By default, c0 starts on the first snk_in.valid. Use
-- g_c0_trigger and the c0_trigger input for an alternative trigger pulse.
-- . Flow control and input data gaps are fully supported.
-- Usage:
-- . The count values themselves (c0..c4) are very useful to tag streaming
-- data with corresponding ID indices.
-- . The extra outputs (e.g. c0_min, c0_max) can be used to trigger other
-- logic when minimum/maximum values per dimension are reached.
LIBRARY IEEE,common_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE work.dp_stream_pkg.ALL;
ENTITY dp_counter IS
GENERIC (
g_c0_trigger : BOOLEAN := FALSE; --FALSE: start counters on first valid; else use c0_trigger input pulse
g_c0 : t_natural_arr(0 TO 2) := (0,2,1); -- (start,stop,step) like python range(start, stop, step)
g_c1 : t_natural_arr(0 TO 2) := (0,2,1)
);
PORT (
clk : IN STD_LOGIC;
rst : IN STD_LOGIC;
snk_in : IN t_dp_sosi; -- The first snk_in.valid will start the counters when g_c0_trigger=FALSE
snk_out : OUT t_dp_siso;
src_out : OUT t_dp_sosi;
src_in : IN t_dp_siso;
c0_trigger : IN STD_LOGIC := '0'; -- Alternative (not snk_in.valid) input pulse to c0_trigger counters (sop, eop, sync, ..)
-- All count outputs are in sync with src_out.valid
c0 : OUT STD_LOGIC_VECTOR(ceil_log2(g_c0(0)+((g_c0(1)-1-g_c0(0))/g_c0(2))*g_c0(2)+1)-1 DOWNTO 0);
c0_min : OUT STD_LOGIC; --Pulses when c0=start
c0_max : OUT STD_LOGIC; --Pulses when c0=max
c1 : OUT STD_LOGIC_VECTOR(ceil_log2(g_c1(0)+((g_c1(1)-1-g_c1(0))/g_c1(2))*g_c1(2)+1)-1 DOWNTO 0);
c1_min : OUT STD_LOGIC; --Pulses when c1=start
c1_max : OUT STD_LOGIC --Pulses when c1=max AND c0=max
);
END dp_counter;
ARCHITECTURE rtl OF dp_counter IS
-- Start, stop, step indices in g_c0 .. g_c4
CONSTANT c_start : NATURAL := 0;
CONSTANT c_stop : NATURAL := 1;
CONSTANT c_step : NATURAL := 2;
-- The user defines the counters like a Python range(start,stop,step) in which the stop value
-- is never actually reached. Calculate the actual maximum values here.
-- . Example:
-- . range(0,4,2) = [0, 2]
-- . range(0,5,2) = [0, 2, 4]
-- . range(0,6,2) = [0, 2, 4]
-- . range(0,7,2) = [0, 2, 4, 6]
-- . range(1,7,2) = [1, 3, 5]
-- . The maximum value is: start+((stop-1-start)/step)*step
CONSTANT c_c0_max : NATURAL := g_c0(c_start)+((g_c0(c_stop)-1-g_c0(c_start))/g_c0(c_step))*g_c0(c_step);
CONSTANT c_c0_w : NATURAL := ceil_log2(c_c0_max+1);
CONSTANT c_c1_max : NATURAL := g_c1(c_start)+((g_c1(c_stop)-1-g_c1(c_start))/g_c1(c_step))*g_c1(c_step);
CONSTANT c_c1_w : NATURAL := ceil_log2(c_c1_max+1);
TYPE t_reg IS RECORD
c0_trigger : STD_LOGIC;
c0 : STD_LOGIC_VECTOR(c_c0_w-1 DOWNTO 0);
c0_min : STD_LOGIC;
c0_max : STD_LOGIC;
c1 : STD_LOGIC_VECTOR(c_c1_w-1 DOWNTO 0);
c1_min : STD_LOGIC;
c1_max : STD_LOGIC;
END RECORD;
SIGNAL r, nxt_r : t_reg;
BEGIN
--------------------------------------------------------------------------------
-- Combinational logic
--------------------------------------------------------------------------------
p_comb : PROCESS(rst, r, snk_in)
VARIABLE v : t_reg;
BEGIN
v := r;
v.c0_min := '0';
v.c0_max := '0';
v.c1_min := '0';
v.c1_max := '0';
-- Start c0 on c0_trigger / assign start values of c0..c4
IF r.c0_trigger='0' THEN
IF (g_c0_trigger=TRUE AND c0_trigger='1') OR (g_c0_trigger=FALSE AND snk_in.valid='1') THEN
v.c0_trigger := '1';
v.c0 := TO_UVEC(g_c0(c_start), c_c0_w);
v.c0_min := '1';
v.c1 := TO_UVEC(g_c1(c_start), c_c1_w);
v.c1_min := '1';
END IF;
END IF;
IF r.c0_trigger='1' AND snk_in.valid='1' THEN
-- Increment c0 by default
v.c0 := INCR_UVEC(r.c0, g_c0(c_step));
--------------------------------------------------------------------------------
-- Assert c#_max pulses
--------------------------------------------------------------------------------
IF r.c0 = TO_UVEC(c_c0_max-g_c0(c_step), c_c0_w) THEN -- c0 max almost reached
v.c0_max := '1';
IF r.c1 = TO_UVEC(c_c1_max, c_c1_w) THEN -- c1 max reached
v.c1_max := '1';
END IF;
--------------------------------------------------------------------------------
-- Assert c#_min pulses and reset the counters, increment non-c0 counters
--------------------------------------------------------------------------------
ELSIF r.c0 = TO_UVEC(c_c0_max, c_c0_w) THEN -- c0 max reached
-- Reset c0 to start value
v.c0 := TO_UVEC(g_c0(c_start), c_c0_w);
v.c0_min := '1';
-- Increment c1 on maximum of c0
v.c1 := INCR_UVEC(r.c1, g_c1(c_step));
IF r.c1 = TO_UVEC(c_c1_max, c_c1_w) THEN -- c1 max reached
-- Reset c1 to start value
v.c1 := TO_UVEC(g_c1(c_start), c_c1_w);
v.c1_min := '1';
END IF;
END IF;
END IF;
IF rst = '1' THEN
v.c0_trigger := '0';
v.c0 := (OTHERS=>'0');
v.c0_min := '0';
v.c0_max := '0';
v.c1 := (OTHERS=>'0');
v.c1_min := '0';
v.c1_max := '0';
END IF;
nxt_r <= v;
END PROCESS;
--------------------------------------------------------------------------------
-- Register stage
--------------------------------------------------------------------------------
r <= nxt_r WHEN rising_edge(clk);
--------------------------------------------------------------------------------
-- Outputs
--------------------------------------------------------------------------------
c0 <= nxt_r.c0;
c0_min <= nxt_r.c0_min;
c0_max <= nxt_r.c0_max;
c1 <= nxt_r.c1;
c1_min <= nxt_r.c1_min;
c1_max <= nxt_r.c1_max;
src_out <= snk_in;
snk_out <= src_in;
END rtl;
-------------------------------------------------------------------------------
--
-- Copyright (C) 2017
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- 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 3 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, see <http://www.gnu.org/licenses/>.
--
-------------------------------------------------------------------------------
-- Author:
-- . Daniel van der Schuur
-- Purpose:
-- . Test bench for dp_counter
-- Description:
-- .
--Usage:
-- . as 10
-- . run -all
LIBRARY IEEE, common_lib;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_lfsr_sequences_pkg.ALL;
USE common_lib.tb_common_pkg.ALL;
USE work.dp_stream_pkg.ALL;
USE work.tb_dp_pkg.ALL;
ENTITY tb_dp_counter IS
GENERIC (
g_c0 : t_natural_arr(0 TO 2) := (0,2,1); -- (start,stop,step) like python range(start, stop, step)
g_c1 : t_natural_arr(0 TO 2) := (0,2,1);
g_flow_control_stimuli : t_dp_flow_control_enum := e_active; -- always active, random or pulse flow control
g_flow_control_verify : t_dp_flow_control_enum := e_active -- always active, random or pulse flow control
);
END tb_dp_counter;
ARCHITECTURE tb OF tb_dp_counter IS
------------------------------------------------------------------------------
-- Clock & reset
------------------------------------------------------------------------------
CONSTANT c_clk_period : TIME := 5 ns;
SIGNAL clk : STD_LOGIC := '1';
SIGNAL rst : STD_LOGIC := '1';
------------------------------------------------------------------------------
-- Valid gaps & flow control
------------------------------------------------------------------------------
CONSTANT c_pulse_active : NATURAL := 1;
CONSTANT c_pulse_period : NATURAL := 7;
SIGNAL random_0 : STD_LOGIC_VECTOR(14 DOWNTO 0) := (OTHERS=>'0'); -- use different lengths to have different random sequences
SIGNAL random_1 : STD_LOGIC_VECTOR(15 DOWNTO 0) := (OTHERS=>'0'); -- use different lengths to have different random sequences
SIGNAL pulse_0 : STD_LOGIC;
SIGNAL pulse_1 : STD_LOGIC;
SIGNAL pulse_en : STD_LOGIC := '1';
------------------------------------------------------------------------------
-- Stimuli: generate input data stream
------------------------------------------------------------------------------
CONSTANT c_data_w : NATURAL := 16;
CONSTANT c_pkt_len : NATURAL := 20;
CONSTANT c_pkt_gap : NATURAL := 5;
CONSTANT c_sync_period : NATURAL := 10;
CONSTANT c_sync_offset : NATURAL := 7;
CONSTANT c_data_init : INTEGER := -1;
CONSTANT c_bsn_init : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := X"0000000000000000";
CONSTANT c_err_init : NATURAL := 247;
CONSTANT c_channel_init : INTEGER := 5;
SIGNAL stimuli_en : STD_LOGIC := '1';
SIGNAL stimuli_src_out : t_dp_sosi;
SIGNAL stimuli_src_in : t_dp_siso;
------------------------------------------------------------------------------
-- dp_counter
------------------------------------------------------------------------------
SIGNAL dp_counter_snk_in : t_dp_sosi;
SIGNAL dp_counter_snk_out : t_dp_siso;
SIGNAL dp_counter_src_out : t_dp_sosi;
SIGNAL dp_counter_src_in : t_dp_siso;
SIGNAL dp_counter_c0 : STD_LOGIC_VECTOR(ceil_log2(g_c0(0)+((g_c0(1)-1-g_c0(0))/g_c0(2))*g_c0(2)+1)-1 DOWNTO 0);
SIGNAL dp_counter_c0_min : STD_LOGIC;
SIGNAL dp_counter_c0_max : STD_LOGIC;
SIGNAL dp_counter_c1 : STD_LOGIC_VECTOR(ceil_log2(g_c1(0)+((g_c1(1)-1-g_c1(0))/g_c1(2))*g_c1(2)+1)-1 DOWNTO 0);
SIGNAL dp_counter_c1_min : STD_LOGIC;
SIGNAL dp_counter_c1_max : STD_LOGIC;
------------------------------------------------------------------------------
-- Verification
------------------------------------------------------------------------------
SIGNAL tb_end : STD_LOGIC := '0';
SIGNAL verify_snk_out : t_dp_siso := c_dp_siso_rdy;
BEGIN
------------------------------------------------------------------------------
-- Clock & reset
------------------------------------------------------------------------------
clk <= (NOT clk) OR tb_end AFTER c_clk_period/2;
rst <= '1', '0' AFTER c_clk_period*7;
------------------------------------------------------------------------------
-- Valid gaps & flow control
------------------------------------------------------------------------------
random_0 <= func_common_random(random_0) WHEN rising_edge(clk);
random_1 <= func_common_random(random_1) WHEN rising_edge(clk);
proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period, '1', rst, clk, pulse_en, pulse_0);
proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period+1, '1', rst, clk, pulse_en, pulse_1);
stimuli_en <= '1' WHEN g_flow_control_stimuli=e_active ELSE
random_0(random_0'HIGH) WHEN g_flow_control_stimuli=e_random ELSE
pulse_0 WHEN g_flow_control_stimuli=e_pulse;
verify_snk_out.ready <= '1' WHEN g_flow_control_verify=e_active ELSE
random_1(random_1'HIGH) WHEN g_flow_control_verify=e_random ELSE
pulse_1 WHEN g_flow_control_verify=e_pulse;
------------------------------------------------------------------------------
-- Stimuli: generate input data stream
------------------------------------------------------------------------------
p_stimuli_st : PROCESS
VARIABLE v_sosi : t_dp_sosi := c_dp_sosi_rst;
BEGIN
-- Adjust initial sosi field values by -1 to compensate for auto increment
v_sosi.bsn := INCR_UVEC(c_bsn_init, -1);
v_sosi.channel := INCR_UVEC(TO_DP_CHANNEL(c_channel_init), -1);
v_sosi.data := INCR_UVEC(TO_DP_DATA(c_data_init), -1);
v_sosi.err := INCR_UVEC(TO_DP_ERROR(c_err_init), -1);
stimuli_src_out <= c_dp_sosi_rst;
proc_common_wait_until_low(clk, rst);
proc_common_wait_some_cycles(clk, 5);
-- Generate c_nof_repeat packets
FOR I IN 0 TO 10-1 LOOP
-- Auto increment v_sosi field values for this packet
v_sosi.bsn := INCR_UVEC(v_sosi.bsn, 1);
v_sosi.sync := sel_a_b((UNSIGNED(v_sosi.bsn) MOD c_sync_period) = c_sync_offset, '1', '0'); -- insert sync starting at BSN=c_sync_offset and with period c_sync_period
v_sosi.channel := INCR_UVEC(v_sosi.channel, 1);
v_sosi.data := INCR_UVEC(v_sosi.data, 20);
v_sosi.data := RESIZE_DP_DATA(v_sosi.data(c_data_w-1 DOWNTO 0)); -- wrap when >= 2**c_data_w
v_sosi.err := INCR_UVEC(v_sosi.err, 1);
-- Send packet
proc_dp_gen_block_data(c_data_w, TO_UINT(v_sosi.data), c_pkt_len, TO_UINT(v_sosi.channel), TO_UINT(v_sosi.err), v_sosi.sync, v_sosi.bsn, clk, stimuli_en, stimuli_src_in, stimuli_src_out);
-- Insert optional gap between the packets
proc_common_wait_some_cycles(clk, c_pkt_gap);
END LOOP;
WAIT;
END PROCESS;
------------------------------------------------------------------------------
-- dp_counter
------------------------------------------------------------------------------
stimuli_src_in <= c_dp_siso_rdy; --dp_counter_snk_out;
dp_counter_snk_in <= stimuli_src_out;
u_dp_counter : ENTITY work.dp_counter
GENERIC MAP (
g_c0 => g_c0,
g_c1 => g_c1
)
PORT MAP (
rst => rst,
clk => clk,
snk_in => dp_counter_snk_in,
snk_out => dp_counter_snk_out,
src_out => dp_counter_src_out,
src_in => dp_counter_src_in,
c0 => dp_counter_c0,
c0_min => dp_counter_c0_min,
c0_max => dp_counter_c0_max,
c1 => dp_counter_c1,
c1_min => dp_counter_c1_min,
c1_max => dp_counter_c1_max
);
------------------------------------------------------------------------------
-- Verification
------------------------------------------------------------------------------
END tb;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment