Select Git revision
CodeLoggingProcessor.log_prop.in
-
Ruud Overeem authored
Removed link to PVSS in KetValueLoggerDaemon.
Ruud Overeem authoredRemoved link to PVSS in KetValueLoggerDaemon.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
st_histogram.vhd 19.91 KiB
-------------------------------------------------------------------------------
--
-- Copyright 2020
-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
--
-- Author: J.W.E. Oudman
-- Purpose: Create a histogram from the input data and present it to the MM bus
-- Description:
-- The histogram component separates it's input samples in counter bins based
-- on the value of the MSbits of the input. These bins are adresses on a RAM
-- block that is swapped with another RAM block at every sync pulse plus 2
-- cycles. While one RAM block is used to count the input samples, the other
-- is read by the MM bus through a fifo.
--
--
-- ram_pointer ram_pointer
-- | |
-- | /o--- RAM_0 ---o |
-- |/ |
-- / |
-- snk_in ----o/ | /o----- FIFO to MM ---- MM
-- |/
-- /
-- o--- RAM_1 ---o/
--
--
-- The input data is a dp stream which obviously uses a dp_clk. Because the
-- RAM is swapped after every sync both RAM blocks need to use the dp_clk.
-- Because the MM bus needs to access one RAM block but can't use it's mm_clk
-- on it a FIFO is used.
--
-- Remarks:
-- . Because the values of the generics g_nof_bins depends on g_in_data_w
-- (you should not have more bins than data values) an assert is made to
-- warn in the simulation when the maximum value of g_nof_bins is reached.
-- If exceeded the simulator will throw fatal error ("...Port length (#) does
-- not match actual length (#)...")
--
-- . when an adress is determined it takes 1 cycle to receive it's value and
-- another cycle before the calculated value can be written into that RAM
-- adress. There is also the limitation of not being able to read and write
-- on the same adress at the same time. These limitations cause the following
-- complications in the implementation:
-- . repeating samples of the same adress have to be counted first till
-- another adress appears before written (as you would miss the second and
-- further consecutive samples and have the read/write limitation)
-- . If adresses are toggling at every cycle (e.g. adress 0; 1; 0; 1) you
-- have to remember the data to be written and increment it as you have the
-- read/write limitation and writing takes priority in this case
-- . When a sync signal appears the RAM has to be swapped 2 cycles later so
-- the first 2 cycles may not be read from the old RAM block
--
-------------------------------------------------------------------------------
LIBRARY IEEE, common_lib, mm_lib, technology_lib, dp_lib;
USE IEEE.std_logic_1164.ALL;
USE common_lib.common_pkg.ALL;
USE common_lib.common_mem_pkg.ALL;
USE dp_lib.dp_stream_pkg.ALL;
USE technology_lib.technology_select_pkg.ALL;
ENTITY st_histogram IS
GENERIC (
g_in_data_w : NATURAL := 14; -- >= 9 when g_nof_bins is 512; (max. c_dp_stream_data_w =768)
g_nof_bins : NATURAL := 512; -- is a power of 2 and g_nof_bins <= c_data_span; max. 512
g_nof_data : NATURAL; -- any use?
g_str : STRING := "freq.density" -- to select output to MM bus ("frequency" or "freq.density")
);
PORT (
mm_rst : IN STD_LOGIC;
mm_clk : IN STD_LOGIC;
dp_rst : IN STD_LOGIC;
dp_clk : IN STD_LOGIC;
-- Streaming
snk_in : IN t_dp_sosi;
-- Memory Mapped
ram_mosi : IN t_mem_mosi;
ram_miso : OUT t_mem_miso
);
END st_histogram;
ARCHITECTURE str OF st_histogram IS
CONSTANT c_data_span : NATURAL := pow2(g_in_data_w); -- any use?
CONSTANT c_bin_w : NATURAL := ceil_log2(g_nof_data); -- any use?
CONSTANT c_adr_w : NATURAL := ceil_log2(g_nof_bins);
CONSTANT c_adr_low_calc : INTEGER := g_in_data_w-c_adr_w; -- Calculation might yield a negative number
CONSTANT c_adr_low : NATURAL := largest(0, c_adr_low_calc); -- Override any negative value of c_adr_low_calc
CONSTANT c_ram : t_c_mem := (latency => 1,
adr_w => c_adr_w, -- 9 bits needed to adress/select 512 adresses
dat_w => c_word_w, -- 32bit, def. in common_pkg; >= c_bin_w
nof_dat => g_nof_bins, -- 512 adresses with 32 bit words, so 512
init_sl => '0'); -- MM side : sla_in, sla_out
SIGNAL dp_pipeline_src_out_p : t_dp_sosi;
SIGNAL dp_pipeline_src_out_pp : t_dp_sosi;
SIGNAL adr_b_cnt : NATURAL := 1;
SIGNAL nxt_adr_b_cnt : NATURAL;
SIGNAL prev_adr_b : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low);
-- Toggle implementation signals
SIGNAL prev_same_r_w_adr : STD_LOGIC := '0';
SIGNAL same_r_w_adr : STD_LOGIC := '0';
SIGNAL nxt_same_r_w_adr : STD_LOGIC := '0';
SIGNAL ram_pointer : STD_LOGIC := '0';
SIGNAL wr_en_a : STD_LOGIC := '0';
SIGNAL nxt_wr_en_a : STD_LOGIC;
SIGNAL wr_dat_a : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0);
SIGNAL nxt_wr_dat_a : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0);
SIGNAL adr_a : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low);
SIGNAL adr_b : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low);
SIGNAL rd_en_b : STD_LOGIC := '0';
SIGNAL rd_dat_b : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0);
SIGNAL rd_val_b : STD_LOGIC;
SIGNAL ram_0_rst_a : STD_LOGIC;
SIGNAL ram_0_rst_b : STD_LOGIC;
SIGNAL ram_0_clk_a : STD_LOGIC;
SIGNAL ram_0_clk_b : STD_LOGIC;
SIGNAL ram_0_wr_en_a : STD_LOGIC;
SIGNAL ram_0_wr_en_b : STD_LOGIC := '0'; -- pointer=1, temp'0'
SIGNAL ram_0_wr_dat_a : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0);
SIGNAL ram_0_wr_dat_b : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0) := (OTHERS =>'0'); -- pointer=1, temp'0'
SIGNAL ram_0_adr_a : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low);
SIGNAL ram_0_adr_b : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low);
SIGNAL ram_0_rd_en_b : STD_LOGIC;
SIGNAL ram_0_rd_dat_b : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0);
SIGNAL ram_0_rd_val_b : STD_LOGIC;
BEGIN
-----------------------------------------------------------------------------
-- Check Generics
-----------------------------------------------------------------------------
ASSERT c_adr_low_calc>0 REPORT "ceil_log2(g_nof_bins) is as large as g_in_data_w, don't increase g_nof_bins" SEVERITY WARNING;
-----------------------------------------------------------------------------
-- Assign inputs of RAM:
-- . Determine address based on input data
-- . Compare adress with the two previous adresses and if:
-- . it is the same as the last adress increase a counter
-- . it is the same as 2 cycles back but not the last copy the data to be
-- written directly into the counter instead of trying to read (ask) it
-- back from RAM at the same clock cycle (which is impossible)
-- . it is not the same enable the nxt_wr_dat_a data to be written
-- at the next cycle by making nxt_wr_en_a high
-- . Write the wr_dat_a data to the RAM
-- . At the snk_in.sync pulse:
-- . let first 2 cycles start counting from 0 again
-- . (plus 2 cycles) let counting depend on values in RAM (which should
-- be 0)
-- . Restart or pause counting when a snk_in.valid = '0' appears:
-- . pause when adress is the same as the previous adress
-- . restart from 0 when adress is not the same as previous adress
-- . restart from 0 when also a sync appears
--
----------------------------------------------------------------------------
p_bin_cnt_switch : PROCESS(snk_in) IS
BEGIN
adr_b <= (OTHERS =>'0');
IF g_nof_bins>1 THEN -- IF snk_in.valid='1' AND
adr_b <= snk_in.data(g_in_data_w-1 DOWNTO c_adr_low);-- WHEN snk_in.valid='1' ELSE (OTHERS =>'0'); -- AND dp_rst='0';
END IF;
END PROCESS;
p_rd_en_b : PROCESS(dp_pipeline_src_out_p.sync, snk_in.valid, wr_en_a, adr_a, adr_b, prev_adr_b) IS
BEGIN
rd_en_b <= '1';
IF dp_pipeline_src_out_p.sync = '1' AND wr_en_a = '1' THEN --
rd_en_b <= '0';
ELSIF adr_a = adr_b AND adr_a /= prev_adr_b THEN -- toggle implementation
rd_en_b <= '0'; -- toggle implementation
ELSIF snk_in.valid = '0' AND wr_en_a = '1' THEN
rd_en_b <= '1';
END IF;
END PROCESS;
p_nxt_wr_dat : PROCESS(rd_dat_b, adr_b_cnt, rd_val_b, dp_pipeline_src_out_p.sync, dp_pipeline_src_out_pp.sync, wr_en_a) IS
BEGIN
nxt_wr_dat_a <= (OTHERS => '0');
IF dp_pipeline_src_out_p.sync = '1' THEN
nxt_wr_dat_a <= TO_UVEC(adr_b_cnt, c_word_w);
ELSIF dp_pipeline_src_out_pp.sync = '1' THEN
nxt_wr_dat_a <= TO_UVEC(adr_b_cnt, c_word_w);
ELSIF same_r_w_adr = '1' AND rd_val_b = '0' THEN -- toggle implementation: same adress forced rd_val to 0, counter instead of ram knows what to write
nxt_wr_dat_a <= TO_UVEC(adr_b_cnt, c_word_w); -- toggle implementation
ELSIF dp_pipeline_src_out_pp.valid = '0' AND prev_same_r_w_adr = '1' THEN -- toggle implementation: prevent 2* rd_dat_b
nxt_wr_dat_a <= TO_UVEC(adr_b_cnt, c_word_w); -- toggle implementation
ELSIF rd_val_b = '1' THEN
nxt_wr_dat_a <= INCR_UVEC(rd_dat_b, adr_b_cnt);
END IF;
END PROCESS;
p_nxt_wr_en : PROCESS(prev_adr_b, adr_b, snk_in.sync) IS
BEGIN
nxt_wr_en_a <= '0';
IF adr_b /= prev_adr_b THEN
nxt_wr_en_a <= '1';
ELSIF snk_in.sync = '1' AND g_nof_bins = 1 THEN
nxt_wr_en_a <= '1';
ELSIF snk_in.sync = '1' THEN
nxt_wr_en_a <= '1';
END IF;
END PROCESS;
p_prev_adr_cnt : PROCESS(adr_a, adr_b, prev_adr_b, adr_b_cnt, snk_in.sync, snk_in.valid, dp_pipeline_src_out_p.valid, dp_pipeline_src_out_p.sync) IS
BEGIN
nxt_adr_b_cnt <= 1;
IF adr_b = prev_adr_b AND snk_in.valid = '1' AND snk_in.sync = '0' THEN
nxt_adr_b_cnt <= adr_b_cnt + 1 ;
ELSIF snk_in.valid = '0' AND snk_in.sync = '1' THEN --address doesn't matter at unvalid and sync, removed: adr_b = prev_adr_b AND
nxt_adr_b_cnt <= 0;
ELSIF adr_b = prev_adr_b AND snk_in.valid = '0' THEN
nxt_adr_b_cnt <= adr_b_cnt;
ELSIF adr_b = prev_adr_b AND snk_in.valid = '1' AND dp_pipeline_src_out_p.valid = '0' AND snk_in.sync = '1' THEN -- toggle implementation; do the adresses even matter?
nxt_adr_b_cnt <= 1; -- toggle implementation
ELSIF adr_b = prev_adr_b AND snk_in.valid = '1' AND dp_pipeline_src_out_p.valid = '0' THEN -- toggle implementation
nxt_adr_b_cnt <= adr_b_cnt + 1; -- toggle implementation
ELSIF adr_a = adr_b AND snk_in.valid = '1' AND snk_in.sync = '1' THEN -- toggle implementation; do the adresses even matter?
nxt_adr_b_cnt <= 1; -- toggle implementation
ELSIF adr_a = adr_b AND adr_b /= prev_adr_b AND snk_in.valid = '0' THEN -- toggle implementation: disable count; -2 cycles count + 0
nxt_adr_b_cnt <= TO_UINT(wr_dat_a); -- toggle implementation
ELSIF adr_a = adr_b AND snk_in.valid = '1' AND dp_pipeline_src_out_p.sync = '0' THEN -- toggle implentation
nxt_adr_b_cnt <= TO_UINT(wr_dat_a) + 1; -- toggle implentation
ELSIF adr_a = adr_b AND snk_in.valid = '0' THEN -- toggle implentation
nxt_adr_b_cnt <= adr_b_cnt; -- toggle implentation
ELSIF snk_in.valid = '0' AND adr_b /= prev_adr_b AND adr_a /= adr_b THEN
nxt_adr_b_cnt <= 0;
END IF;
END PROCESS;
p_nxt_same_r_w_adr : PROCESS(adr_a, adr_b) IS -- toggle implentation
BEGIN
nxt_same_r_w_adr <= '0';
IF adr_a = adr_b AND g_nof_bins > 1 THEN
nxt_same_r_w_adr <= '1';
END IF;
END PROCESS;
p_clk : PROCESS(dp_clk, dp_rst)
BEGIN
IF dp_rst='1' THEN
adr_b_cnt <= 0;
wr_en_a <= '0';
ELSIF rising_edge(dp_clk) THEN
adr_b_cnt <= nxt_adr_b_cnt;
wr_dat_a <= nxt_wr_dat_a;
wr_en_a <= nxt_wr_en_a;
same_r_w_adr <= nxt_same_r_w_adr;
END IF;
END PROCESS;
-----------------------------------------------------------------------------
-- RAM selector
-----------------------------------------------------------------------------
p_ram_pointer : PROCESS(ram_pointer, dp_rst, dp_clk, wr_en_a, wr_dat_a, adr_a, adr_b, rd_en_b, ram_0_rd_dat_b, ram_0_rd_val_b) IS
BEGIN
-- Default / init values
-- ram_miso <= c_mem_miso_rst;
-- ram_0_mosi <= c_common_pipelinemem_mosi_rst;
IF ram_pointer='0' THEN
-- ST side (RAM 0)
-- ram_0_rst_a <= dp_rst;
-- ram_0_rst_b <= dp_rst;
-- ram_0_clk_a <= dp_clk;
-- ram_0_clk_b <= dp_clk;
ram_0_wr_en_a <= wr_en_a;
ram_0_wr_dat_a <= wr_dat_a;
ram_0_adr_a <= adr_a;
ram_0_adr_b <= adr_b;
ram_0_rd_en_b <= rd_en_b;
rd_dat_b <= ram_0_rd_dat_b;
rd_val_b <= ram_0_rd_val_b;
-- -- MM side (RAM 1)
-- ram_1_rst_a <= dp_rst;
-- ram_1_clk_a <=common_pipeline dp_clk;
-- ram_1_wr_en_a <= clear_en_a;
-- ram_1_wr_dat_a <= clear_dat_a;
-- ram_1_adr_a <= clear_adr_a;
--
--2
-- ram_1_rst_b <= mm_rst;
-- ram_1_clk_b <= mm_clk;
-- ram_1_wr_en_b <= ram_mosi.wr; -- only for diagnostic purposes, typically statistics are read only
-- ram_1_wr_dat_b <= ram_mosi.wrdata(c_ram.dat_w-1 DOWNTO 0);
-- ram_1_adr_b <= ram_mosi.address(c_ram.adr_w-1 DOWNTO 0);
-- ram_1_rd_en_b <= ram_mosi.rd;
-- ram_miso.rddata <= ram_1_rd_dat_b(c_ram.dat_w-1 DOWNTO 0); -- needs 'histogram/frequency' switch
-- ram_miso.rdval <= ram_1_rd_val_b;
ELSE -- ram_pointer='1'
-- ram_1_wr_en_a <= wr_en_a;
-- ram_1_wr_dat_a <= wr_dat_a;
-- ram_1_adr_a <= adr_a;
-- ram_1_adr_b <= adr_b;
-- ram_1_rd_en_b <= rd_en_b;
-- rd_dat_b <= ram_1_rd_dat_b;
-- rd_val_b <= ram_1_rd_val_b;
--
--
-- ram_miso <= ram_0_miso;
-- ram_0_mosi <= ram_mosi;2
END IF;
END PROCESS;
-----------------------------------------------------------------------------
-- Pipeline for adress
-----------------------------------------------------------------------------
u_common_pipeline_adr : ENTITY common_lib.common_pipeline
GENERIC MAP (
g_representation => "UNSIGNED", --orig. signed
g_pipeline => 2,
g_in_dat_w => c_adr_w,
g_out_dat_w => c_adr_w
)
PORT MAP (
clk => dp_clk,
clken => '1',
in_dat => STD_LOGIC_VECTOR(adr_b),
out_dat => adr_a
);
-----------------------------------------------------------------------------
-- Pipeline for toggle issue
-----------------------------------------------------------------------------
u_common_pipeline_sl_same_r_w_adr : ENTITY common_lib.common_pipeline_sl
GENERIC MAP(
g_pipeline => 1 -- 0 for wires, > 0 for registers,
)
PORT MAP (
clk => dp_clk,
in_dat => same_r_w_adr,
out_dat => prev_same_r_w_adr
);
-----------------------------------------------------------------------------
-- Pipeline for adresscounter
-----------------------------------------------------------------------------
u_common_pipeline_adr_cnt : ENTITY common_lib.common_pipeline
GENERIC MAP (
g_representation => "UNSIGNED", --orig. signed
g_pipeline => 1,
g_in_dat_w => c_adr_w,
g_out_dat_w => c_adr_w
)
PORT MAP (
clk => dp_clk,
clken => '1',
in_dat => STD_LOGIC_VECTOR(adr_b),
out_dat => prev_adr_b
);
-----------------------------------------------------------------------------
-- Pipeline for cycles after sync
-----------------------------------------------------------------------------
u_dp_pipeline_snk_in_1_cycle : ENTITY dp_lib.dp_pipeline
GENERIC MAP (
g_pipeline => 1 -- 0 for wires, > 0 for registers,
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
snk_in => snk_in,
src_out => dp_pipeline_src_out_p
);
u_dp_pipeline_snk_in_2_cycle : ENTITY dp_lib.dp_pipeline
GENERIC MAP (
g_pipeline => 2 -- 0 for wires, > 0 for registers,
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
snk_in => snk_in,
src_out => dp_pipeline_src_out_pp
);
-----------------------------------------------------------------------------
-- Dual swapped RAM instance
-----------------------------------------------------------------------------
ram_0: ENTITY common_lib.common_ram_r_w
GENERIC MAP (
g_technology => c_tech_select_default,
g_ram => c_ram,
g_init_file => "UNUSED"
)
PORT MAP (
rst => dp_rst,
clk => dp_clk,
clken => '1', -- only necessary for Stratix iv
wr_en => ram_0_wr_en_a,
wr_adr => ram_0_adr_a,
wr_dat => ram_0_wr_dat_a,
rd_en => ram_0_rd_en_b,
rd_adr => ram_0_adr_b,
rd_dat => ram_0_rd_dat_b,
rd_val => ram_0_rd_val_b
);
-- ram_0: ENTITY common_lib.common_ram_crw_crw
-- GENERIC MAP (
-- g_technology => c_tech_select_default,
-- g_ram => c_ram,
-- g_init_file => "UNUSED",
-- g_true_dual_port => FALSE
-- )
-- PORT MAP (
-- rst_a => ram_0_rst_a,
-- rst_b => ram_0_rst_b,
-- clk_a => ram_0_clk_a,
-- clk_b => ram_0_clk_b,
-- clken_a => '1', -- only necessary for Stratix iv
-- clken_b => '1',
-- wr_en_a => ram_0_wr_en_a,
-- wr_en_b => ram_0_wr_en_b, -- pointer=1, temp'0'
-- wr_dat_a => ram_0_wr_dat_a,
-- wr_dat_b => ram_0_wr_dat_b, -- pointer=1, temp'0'
-- adr_a => ram_0_adr_a,
-- adr_b => ram_0_adr_b,
-- rd_en_a => '0',
-- rd_en_b => ram_0_rd_en_b,
-- rd_dat_a => OPEN,
-- rd_dat_b => ram_0_rd_dat_b,
-- rd_val_a => OPEN,
-- rd_val_b => ram_0_rd_val_b
-- );
--
-- ram_1: ENTITY common_lib.common_ram_crw_crw
-- GENERIC MAP (
-- g_technology => c_tech_select_default,
-- g_ram => c_ram,
-- g_init_file => "UNUSED",
-- g_true_dual_port => TRUE
-- )
-- PORT MAP (
-- rst_a => ram_1_rst_a,
-- rst_b => ram_1_rst_b,
-- clk_a => ram_1_clk_a,
-- clk_b => ram_1_clk_b,
-- clken_a => '1',
-- clken_b => '1',
-- wr_en_a => ram_1_wr_en_a,
-- wr_en_b => ram_1_wr_en_b,
-- wr_dat_a => ram_1_wr_dat_a,
-- wr_dat_b => ram_1_wr_dat_b,
-- adr_a => ram_1_adr_a,
-- adr_b => ram_1_adr_b,
-- rd_en_a => '0',
-- rd_en_b => ram_1_rd_en_b,
-- rd_dat_a => OPEN,
-- rd_dat_b => ram_1_rd_dat_b,
-- rd_val_a => OPEN,
-- rd_val_b => ram_1_rd_val_b
-- );
-----------------------------------------------------------------------------
-- Connect MM interface to DUAL swapped RAM read out histogram statistics
-----------------------------------------------------------------------------
END str;