Select Git revision
-
Alissa Cheng authoredAlissa Cheng authored
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;