diff --git a/libraries/dsp/st/hdllib.cfg b/libraries/dsp/st/hdllib.cfg index 660c6f533ce2a2b0b6ce93d12acab8a7683d0441..21443d39f3629e98383ef0678cc9191f97d8bb4b 100644 --- a/libraries/dsp/st/hdllib.cfg +++ b/libraries/dsp/st/hdllib.cfg @@ -10,11 +10,17 @@ synth_files = src/vhdl/st_calc.vhd src/vhdl/st_sst.vhd # src/vhdl/st_top.vhd + src/vhdl/st_histogram.vhd + src/vhdl/st_histogram_reg.vhd + src/vhdl/mms_st_histogram.vhd + src/vhdl/st_histogram_8_april.vhd test_bench_files = tb/vhdl/tb_st_acc.vhd tb/vhdl/tb_st_calc.vhd - tb/vhdl/tb_mmf_st_sst.vhd + tb/vhdl/tb_mmf_st_sst.vhd + tb/vhdl/tb_st_histogram.vhd + tb/vhdl/tb_mms_st_histogram.vhd regression_test_vhdl = tb/vhdl/tb_st_acc.vhd diff --git a/libraries/dsp/st/src/vhdl/mms_st_histogram.vhd b/libraries/dsp/st/src/vhdl/mms_st_histogram.vhd new file mode 100644 index 0000000000000000000000000000000000000000..372f5187091d077d31a483556dbfb94ac2b4360d --- /dev/null +++ b/libraries/dsp/st/src/vhdl/mms_st_histogram.vhd @@ -0,0 +1,125 @@ +------------------------------------------------------------------------------- +-- +-- 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: +-- mms_st_histogram couples the st_histogram component which works entirely +-- in the dp clock domain through st_histogram_reg that handles the cross +-- domain conversion to the MM bus. +-- +-- +-- -------------------------------------- +-- | mms_st_histogram | +-- | | +-- | ---------------- | ------- +-- snk_in -->|-->| st_histogram | | ^ +-- | ---------------- | | +-- | | ^ | +-- | | | | dp clock domain +-- | ram_st_histogram_miso | +-- | | | | +-- | | ram_st_histogram_mosi | | +-- | v | | v +-- | -------------------- | ------- +-- | | st_histogram_reg |-- ram_miso -->|--> mm clock domain +-- | | |<-- ram_mosi --|<-- +-- | -------------------- | ------- +-- | | +-- -------------------------------------- +-- +-- +------------------------------------------------------------------------------- + +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 mms_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; -- + g_str : STRING := "freq.density" -- to select output to MM bus ("frequency" or "freq.density") + ); + PORT ( + dp_rst : IN STD_LOGIC; + dp_clk : IN STD_LOGIC; + mm_rst : IN STD_LOGIC; + mm_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 mms_st_histogram; + +ARCHITECTURE str OF mms_st_histogram IS + + SIGNAL ram_st_histogram_mosi : t_mem_mosi; + SIGNAL ram_st_histogram_miso : t_mem_miso; + +BEGIN + + u_st_histogram : ENTITY work.st_histogram + GENERIC MAP( + g_in_data_w => g_in_data_w, + g_nof_bins => g_nof_bins, + g_nof_data => g_nof_data, + g_str => g_str + ) + PORT MAP ( + dp_rst => dp_rst, + dp_clk => dp_clk, + + snk_in => snk_in, + sla_in_ram_mosi => ram_st_histogram_mosi, + sla_out_ram_miso => ram_st_histogram_miso + ); + + u_st_histogram_reg : ENTITY work.st_histogram_reg +-- GENERIC MAP( +-- g_in_data_w => +-- g_nof_bins => +-- g_nof_data => +-- g_str => +-- ) + PORT MAP ( + dp_rst => dp_rst, + dp_clk => dp_clk, + mm_rst => mm_rst, + mm_clk => mm_clk, + + mas_out_ram_mosi => ram_st_histogram_mosi, + mas_in_ram_miso => ram_st_histogram_miso, + + ram_mosi => ram_mosi, + ram_miso => ram_miso + ); + +END str; diff --git a/libraries/dsp/st/src/vhdl/st_histogram.vhd b/libraries/dsp/st/src/vhdl/st_histogram.vhd new file mode 100644 index 0000000000000000000000000000000000000000..4177fdd6c43189ed20f8075d5abe46372fae8057 --- /dev/null +++ b/libraries/dsp/st/src/vhdl/st_histogram.vhd @@ -0,0 +1,575 @@ +------------------------------------------------------------------------------- +-- +-- 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 +-- st_histogram_reg +-- 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 st_histogram_reg. +-- +-- +-- ram_pointer ram_pointer +-- | | +-- | /o--- RAM_0 ---o | +-- |/ | +-- / | +-- snk_in ----o/ | /o----- ram_miso (st_histogram_reg) +-- |/ _mosi +-- / +-- 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. +-- If the MM bus needs to acces the data in a RAM block it has to acces it +-- through st_histogram_reg as the mm_clk can't be 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) <-- maybe just g_data_w ?? + g_nof_bins : NATURAL := 512; -- is a power of 2 and g_nof_bins <= c_data_span; max. 512 + g_nof_data : NATURAL; -- + g_str : STRING := "freq.density" -- to select output to MM bus ("frequency" or "freq.density") + ); + PORT ( + dp_rst : IN STD_LOGIC; + dp_clk : IN STD_LOGIC; + + -- Streaming + snk_in : IN t_dp_sosi; + + -- DP clocked memory bus + sla_in_ram_mosi : IN t_mem_mosi; -- Beware, works in dp clock domain ! + sla_out_ram_miso : OUT t_mem_miso -- '' ! +-- ram_mosi : IN t_mem_mosi; -- Beware, works in dp clock domain ! +-- ram_miso : OUT t_mem_miso -- '' ! + ); +END st_histogram; + + +ARCHITECTURE rtl 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_clear : NATURAL := g_nof_data - g_nof_bins; + 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 rd_adr_cnt : NATURAL := 1; + SIGNAL nxt_rd_adr_cnt : NATURAL; + SIGNAL prev_rd_adr : 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 cycle_cnt : NATURAL := 0 ; + SIGNAL nxt_cycle_cnt : NATURAL := 0 ; + SIGNAL wr_en : STD_LOGIC := '0'; + SIGNAL nxt_wr_en : STD_LOGIC; + SIGNAL wr_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL nxt_wr_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL wr_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL rd_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL rd_en : STD_LOGIC := '0'; + SIGNAL rd_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL rd_val : STD_LOGIC; + + SIGNAL mm_adr_cnt : NATURAL := 0 ; + SIGNAL mm_adr_illegal : STD_LOGIC := '0'; + SIGNAL mm_adr_illegal_pp : STD_LOGIC := '0'; + + + SIGNAL ram_0_wr_en : STD_LOGIC; +-- SIGNAL ram_0_wr_en_b : STD_LOGIC := '0'; -- pointer=1, temp'0' + SIGNAL ram_0_wr_dat : 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_wr_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_0_rd_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_0_rd_en : STD_LOGIC; + SIGNAL ram_0_rd_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL ram_0_rd_val : STD_LOGIC; + + SIGNAL ram_1_wr_en : STD_LOGIC; + SIGNAL ram_1_wr_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL ram_1_wr_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_1_rd_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_1_rd_en : STD_LOGIC; + SIGNAL ram_1_rd_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL ram_1_rd_val : STD_LOGIC; + + SIGNAL ram_out_wr_en : STD_LOGIC; + SIGNAL ram_out_wr_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL ram_out_wr_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_out_rd_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_out_rd_en : STD_LOGIC; + SIGNAL ram_out_rd_dat : STD_LOGIC_VECTOR(c_word_w -1 DOWNTO 0); + SIGNAL ram_out_rd_val : STD_LOGIC; + + SIGNAL prev_ram_out_wr_adr : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL ram_out_same_w_r_adr : 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 data to be written + -- at the next cycle by making nxt_wr_en high + -- . Write the wr_dat 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 + -- + -- input: snk_in; rd_dat; rd_val + -- output: wr_adr; rd_adr; wr_en; rd_en; wr_dat; + ---------------------------------------------------------------------------- + + -- 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 + ); + + p_bin_cnt_switch : PROCESS(snk_in) IS -- misses g_nof_bins ?? + BEGIN + rd_adr <= (OTHERS =>'0'); + IF g_nof_bins>1 THEN + rd_adr <= 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; + + -- Pipelined to compare previous rd_adr against current + u_common_pipeline_adr_cnt : ENTITY common_lib.common_pipeline --rename to u_common_pipeline_rd_adr + 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(rd_adr), + out_dat => prev_rd_adr + ); + + p_nxt_wr_en : PROCESS(prev_rd_adr, rd_adr, snk_in.sync) IS -- misses g_nof_bins ?? + BEGIN + nxt_wr_en <= '0'; + IF rd_adr /= prev_rd_adr THEN + nxt_wr_en <= '1'; + ELSIF snk_in.sync = '1' AND g_nof_bins = 1 THEN + nxt_wr_en <= '1'; + ELSIF snk_in.sync = '1' THEN + nxt_wr_en <= '1'; + END IF; + END PROCESS; + + -- requested data on adress can be written back 2 cycles later + 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(rd_adr), + out_dat => wr_adr + ); + + p_rd_en : PROCESS(dp_pipeline_src_out_p.sync, snk_in.valid, wr_en, wr_adr, rd_adr, prev_rd_adr) IS + BEGIN + rd_en <= '1'; + IF dp_pipeline_src_out_p.sync = '1' AND wr_en = '1' THEN -- + rd_en <= '0'; + ELSIF wr_adr = rd_adr AND wr_adr /= prev_rd_adr THEN -- toggle implementation + rd_en <= '0'; -- toggle implementation + ELSIF snk_in.valid = '0' AND wr_en = '1' THEN + rd_en <= '1'; + END IF; + END PROCESS; + + -- cycles after sync + 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 + ); + + p_prev_adr_cnt : PROCESS(wr_adr, rd_adr, prev_rd_adr, rd_adr_cnt, snk_in.sync, snk_in.valid, dp_pipeline_src_out_p.valid, dp_pipeline_src_out_p.sync) IS --change to p_nxt_rd_adr_cnt ;; misses wr_dat; + BEGIN + nxt_rd_adr_cnt <= 1; + IF rd_adr = prev_rd_adr AND snk_in.valid = '1' AND snk_in.sync = '0' THEN + nxt_rd_adr_cnt <= rd_adr_cnt + 1 ; + ELSIF snk_in.valid = '0' AND snk_in.sync = '1' THEN --address doesn't matter at unvalid and sync, removed: rd_adr = prev_rd_adr AND + nxt_rd_adr_cnt <= 0; + ELSIF rd_adr = prev_rd_adr AND snk_in.valid = '0' THEN + nxt_rd_adr_cnt <= rd_adr_cnt; + ELSIF rd_adr = prev_rd_adr 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_rd_adr_cnt <= 1; -- toggle implementation + ELSIF rd_adr = prev_rd_adr AND snk_in.valid = '1' AND dp_pipeline_src_out_p.valid = '0' THEN -- toggle implementation + nxt_rd_adr_cnt <= rd_adr_cnt + 1; -- toggle implementation + ELSIF wr_adr = rd_adr AND snk_in.valid = '1' AND snk_in.sync = '1' THEN -- toggle implementation; do the adresses even matter? + nxt_rd_adr_cnt <= 1; -- toggle implementation + ELSIF wr_adr = rd_adr AND rd_adr /= prev_rd_adr AND snk_in.valid = '0' THEN -- toggle implementation: disable count; -2 cycles count + 0 + nxt_rd_adr_cnt <= TO_UINT(wr_dat); -- toggle implementation + ELSIF wr_adr = rd_adr AND snk_in.valid = '1' AND dp_pipeline_src_out_p.sync = '0' THEN -- toggle implentation + nxt_rd_adr_cnt <= TO_UINT(wr_dat) + 1; -- toggle implentation + ELSIF wr_adr = rd_adr AND snk_in.valid = '0' THEN -- toggle implentation + nxt_rd_adr_cnt <= rd_adr_cnt; -- toggle implentation + ELSIF snk_in.valid = '0' AND rd_adr /= prev_rd_adr AND wr_adr /= rd_adr THEN + nxt_rd_adr_cnt <= 0; + END IF; + END PROCESS; + + p_nxt_same_r_w_adr : PROCESS(wr_adr, rd_adr) IS -- toggle implentation ;; misses g_nof_bins ?? + BEGIN + nxt_same_r_w_adr <= '0'; + IF wr_adr = rd_adr AND g_nof_bins > 1 THEN + nxt_same_r_w_adr <= '1'; + END IF; + END PROCESS; + + -- 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 + ); + + p_nxt_wr_dat : PROCESS(rd_dat, rd_adr_cnt, rd_val, dp_pipeline_src_out_p.sync, dp_pipeline_src_out_pp.sync, wr_en) IS --misses: same_r_w_adr; c_word_w?; prev_same_r_w_adr; + BEGIN + nxt_wr_dat <= (OTHERS => '0'); + IF dp_pipeline_src_out_p.sync = '1' THEN + nxt_wr_dat <= TO_UVEC(rd_adr_cnt, c_word_w); + ELSIF dp_pipeline_src_out_pp.sync = '1' THEN + nxt_wr_dat <= TO_UVEC(rd_adr_cnt, c_word_w); + ELSIF same_r_w_adr = '1' AND rd_val = '0' THEN -- toggle implementation: same adress forced rd_val to 0, counter instead of ram knows what to write + nxt_wr_dat <= TO_UVEC(rd_adr_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 + nxt_wr_dat <= TO_UVEC(rd_adr_cnt, c_word_w); -- toggle implementation + ELSIF rd_val = '1' THEN + nxt_wr_dat <= INCR_UVEC(rd_dat, rd_adr_cnt); + END IF; + END PROCESS; + + p_clk : PROCESS(dp_clk, dp_rst) + BEGIN + IF dp_rst='1' THEN + rd_adr_cnt <= 0; + wr_en <= '0'; + ELSIF rising_edge(dp_clk) THEN + rd_adr_cnt <= nxt_rd_adr_cnt; + wr_dat <= nxt_wr_dat; + wr_en <= nxt_wr_en; + same_r_w_adr <= nxt_same_r_w_adr; + cycle_cnt <= nxt_cycle_cnt; -- ( ander functieblok ) + prev_ram_out_wr_adr <= ram_out_wr_adr; -- '' + END IF; + END PROCESS; + + + ----------------------------------------------------------------------------- + -- RAM selector & Dual swapped RAM instances: + -- 2 cycles after a sync the RAM block is swapped for an empty one to allow + -- the block to be read out till the next sync+2 cycles + -- + -- Depending on ram_pointer: + -- ram_pointer = '0': input RAM_0, output RAM_1 + -- ram_pointer = '1': input RAM_1, output RAM_0 + -- + -- input in: dp_pipeline_src_out_pp.sync; wr_en; wr_dat; wr_adr; + -- rd_adr; rd_en; + -- out: rd_dat, rd_val + -- + -- output in: ram_out_wr_en; ram_out_wr_dat; ram_out_wr_adr; ram_out_rd_adr; + -- ram_out_rd_en + -- out: ram_out_rd_dat; ram_out_rd_val + ----------------------------------------------------------------------------- + p_ram_pointer_at_sync : PROCESS(dp_pipeline_src_out_pp) IS -- needs nxt_ram_pointer ?? + BEGIN + IF dp_pipeline_src_out_pp.sync = '1' THEN + ram_pointer <= NOT(ram_pointer); + END IF; + END PROCESS; + + p_ram_pointer : PROCESS(ram_pointer, wr_en, wr_dat, wr_adr, rd_adr, rd_en, ram_0_rd_dat, ram_0_rd_val, + ram_out_wr_en, ram_out_wr_dat, ram_out_wr_adr, ram_out_rd_adr, ram_out_rd_en, ram_1_rd_dat, ram_1_rd_val) IS + BEGIN + IF ram_pointer='0' THEN + + -- ST side (RAM 0) + ram_0_wr_en <= wr_en; + ram_0_wr_dat <= wr_dat; + ram_0_wr_adr <= wr_adr; + ram_0_rd_adr <= rd_adr; + ram_0_rd_en <= rd_en; + rd_dat <= ram_0_rd_dat; + rd_val <= ram_0_rd_val; + + + -- dp_clk'd MM side (RAM 1) + ram_1_wr_en <= ram_out_wr_en; + ram_1_wr_dat <= ram_out_wr_dat; + ram_1_wr_adr <= ram_out_wr_adr; + ram_1_rd_adr <= ram_out_rd_adr; + ram_1_rd_en <= ram_out_rd_en; + ram_out_rd_dat <= ram_1_rd_dat; + ram_out_rd_val <= ram_1_rd_val; + + + ELSE -- ram_pointer='1' + + -- ST side (RAM 1) + ram_1_wr_en <= wr_en; + ram_1_wr_dat <= wr_dat; + ram_1_wr_adr <= wr_adr; + ram_1_rd_adr <= rd_adr; + ram_1_rd_en <= rd_en; + rd_dat <= ram_1_rd_dat; + rd_val <= ram_1_rd_val; + + --dp_clk'd MM side (RAM 0) + ram_0_wr_en <= ram_out_wr_en; + ram_0_wr_dat <= ram_out_wr_dat; + ram_0_wr_adr <= ram_out_wr_adr; + ram_0_rd_adr <= ram_out_rd_adr; + ram_0_rd_en <= ram_out_rd_en; + ram_out_rd_dat <= ram_0_rd_dat; + ram_out_rd_val <= ram_0_rd_val; + + END IF; + END PROCESS; + + + -- Dual swapped RAM instances + 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, + wr_adr => ram_0_wr_adr, + wr_dat => ram_0_wr_dat, + rd_en => ram_0_rd_en, + rd_adr => ram_0_rd_adr, + rd_dat => ram_0_rd_dat, + rd_val => ram_0_rd_val + ); + + ram_1: 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_1_wr_en, + wr_adr => ram_1_wr_adr, + wr_dat => ram_1_wr_dat, + rd_en => ram_1_rd_en, + rd_adr => ram_1_rd_adr, + rd_dat => ram_1_rd_dat, + rd_val => ram_1_rd_val + ); + + + + ----------------------------------------------------------------------------- + -- Connect interface to DUAL swapped RAM, read out histogram statistics: + -- . Limit the data read by the MM master to the RAM block where it started + -- to read (the values read after a new sync will be OTHERS => '0') + -- . In the last g_nof_bins cycles all addresses will sequentially be cleared + -- + -- RAM selector: + -- input: ram_out_rd_dat; ram_out_rd_val + -- output: ram_out_wr_en; ram_out_wr_dat; ram_out_wr_adr; ram_out_rd_adr; + -- ram_out_wr_en + -- (PORT): + -- input: snk_in; sla_in_ram_mosi + -- output: sla_out_ram_miso + ----------------------------------------------------------------------------- + + -- Pipeline for identified illegal read requests after new sync + u_common_pipeline_sl_mm_adr_illegal : ENTITY common_lib.common_pipeline_sl + GENERIC MAP( + g_pipeline => 2 -- 0 for wires, > 0 for registers, + ) + PORT MAP ( + clk => dp_clk, + in_dat => mm_adr_illegal, + out_dat => mm_adr_illegal_pp + ); + + p_mm_adr_illegal : PROCESS(snk_in.sync, mm_adr_cnt) IS + BEGIN + IF snk_in.sync = '1' AND mm_adr_cnt /= 0 THEN + mm_adr_illegal <= '1'; + ELSIF mm_adr_cnt = g_nof_bins-1 THEN + mm_adr_illegal <= '0'; + ELSE + END IF; + END PROCESS; + + mm_adr_cnt <= TO_UINT(sla_in_ram_mosi.address(c_ram.adr_w-1 DOWNTO 0)) WHEN sla_in_ram_mosi.rd = '1'; + ram_out_same_w_r_adr <= '1' WHEN ram_out_wr_adr = sla_in_ram_mosi.address(c_ram.adr_w-1 DOWNTO 0) ELSE '0'; + + p_ram_to_fifo : PROCESS(dp_pipeline_src_out_pp.sync, cycle_cnt, sla_in_ram_mosi.address, sla_in_ram_mosi.rd, ram_out_rd_dat, ram_out_rd_val, prev_ram_out_wr_adr, mm_adr_illegal, ram_out_same_w_r_adr) IS + BEGIN + IF dp_pipeline_src_out_pp.sync = '1' THEN + ram_out_wr_en <= '0'; + nxt_cycle_cnt <= 0; + ELSIF cycle_cnt = c_clear THEN + ram_out_wr_adr <= (OTHERS => '0'); + ram_out_wr_dat <= (OTHERS => '0'); + ram_out_wr_en <= '1'; + IF ram_out_same_w_r_adr = '1' THEN + ram_out_rd_en <= '0'; + sla_out_ram_miso.rddata(c_ram.dat_w-1 DOWNTO 0) <= (OTHERS => '0'); + sla_out_ram_miso.rdval <= ram_out_rd_val; + ELSE + ram_out_rd_adr <= sla_in_ram_mosi.address(c_ram.adr_w-1 DOWNTO 0); + ram_out_rd_en <= sla_in_ram_mosi.rd; + sla_out_ram_miso.rddata(c_ram.dat_w-1 DOWNTO 0) <= ram_out_rd_dat; + sla_out_ram_miso.rdval <= ram_out_rd_val; + END IF; + nxt_cycle_cnt <= cycle_cnt +1; + ELSIF cycle_cnt > c_clear THEN + ram_out_wr_adr <= INCR_UVEC(prev_ram_out_wr_adr, 1); + nxt_cycle_cnt <= cycle_cnt +1; + IF ram_out_same_w_r_adr = '1' OR snk_in.sync = '1' THEN + sla_out_ram_miso.rddata(c_ram.dat_w-1 DOWNTO 0) <= (OTHERS => '0'); + sla_out_ram_miso.rdval <= ram_out_rd_val; + ELSE + ram_out_rd_adr <= sla_in_ram_mosi.address(c_ram.adr_w-1 DOWNTO 0); + ram_out_rd_en <= sla_in_ram_mosi.rd; + sla_out_ram_miso.rddata(c_ram.dat_w-1 DOWNTO 0) <= ram_out_rd_dat; + sla_out_ram_miso.rdval <= ram_out_rd_val; + END IF; + ELSIF mm_adr_illegal_pp = '1' THEN + ram_out_rd_adr <= sla_in_ram_mosi.address(c_ram.adr_w-1 DOWNTO 0); + ram_out_rd_en <= sla_in_ram_mosi.rd; + sla_out_ram_miso.rddata(c_ram.dat_w-1 DOWNTO 0) <= (OTHERS => '0'); + sla_out_ram_miso.rdval <= ram_out_rd_val; + nxt_cycle_cnt <= cycle_cnt +1; + ELSE + ram_out_rd_adr <= sla_in_ram_mosi.address(c_ram.adr_w-1 DOWNTO 0); + ram_out_rd_en <= sla_in_ram_mosi.rd; + sla_out_ram_miso.rddata(c_ram.dat_w-1 DOWNTO 0) <= ram_out_rd_dat; + sla_out_ram_miso.rdval <= ram_out_rd_val; + nxt_cycle_cnt <= cycle_cnt +1; + END IF; + END PROCESS; + + + + +END rtl; diff --git a/libraries/dsp/st/src/vhdl/st_histogram_8_april.vhd b/libraries/dsp/st/src/vhdl/st_histogram_8_april.vhd new file mode 100644 index 0000000000000000000000000000000000000000..965564ea25c13c9cf8c3ca7feaf62bd5c7b1593b --- /dev/null +++ b/libraries/dsp/st/src/vhdl/st_histogram_8_april.vhd @@ -0,0 +1,399 @@ + +-- Daniel's suggested restructured st_hitogram.vhd. + +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_8_april IS + GENERIC ( + g_in_data_w : NATURAL := 14; -- >= 9 when g_nof_bins is 512; (max. c_dp_stream_data_w =768) <-- maybe just g_data_w ?? + g_nof_bins : NATURAL := 512; -- is a power of 2 and g_nof_bins <= c_data_span; max. 512 + g_nof_data : NATURAL + ); + PORT ( + dp_rst : IN STD_LOGIC; + dp_clk : IN STD_LOGIC; + + -- Streaming + snk_in : IN t_dp_sosi; + + -- DP clocked memory bus + ram_mosi : IN t_mem_mosi; + ram_miso : OUT t_mem_miso + ); +END st_histogram_8_april; + + +ARCHITECTURE rtl OF st_histogram_8_april IS + + CONSTANT c_adr_w : NATURAL := ceil_log2(g_nof_bins); + 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 + +-- CONSTANT c_mem_miso_setting : t_mem_miso := (rddata => mem_miso_init, -- c_mem_miso_rst; -- limit to 32 bit +-- rdval => '0', +-- waitrequest => '0' ); + + 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 + +-- SIGNAL mem_miso_init : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := (OTHERS => '0'); + + SIGNAL bin_reader_mosi : t_mem_mosi := c_mem_mosi_rst; + + SIGNAL nxt_bin_writer_mosi : t_mem_mosi; + SIGNAL bin_writer_mosi : t_mem_mosi; + + SIGNAL nxt_bin_arbiter_wr_mosi : t_mem_mosi; + SIGNAL bin_arbiter_wr_mosi : t_mem_mosi; + + SIGNAL nxt_bin_arbiter_rd_mosi : t_mem_mosi; + SIGNAL bin_arbiter_rd_mosi : t_mem_mosi; + + SIGNAL common_ram_r_w_0_miso : t_mem_miso := c_mem_miso_rst; + + SIGNAL init_phase : STD_LOGIC := '1'; + SIGNAL rd_cnt_allowed : STD_LOGIC := '0'; + SIGNAL rd_cnt_allowed_pp : STD_LOGIC := '0'; + SIGNAL nxt_rd_adr_cnt : NATURAL := 0; + SIGNAL rd_adr_cnt : NATURAL;-- := 0; + SIGNAL toggle_detect : STD_LOGIC := '0'; + SIGNAL toggle_detect_pp : STD_LOGIC; + SIGNAL toggle_detect_false : STD_LOGIC := '1'; +-- SIGNAL nxt_toggle_adr_cnt : NATURAL := 0; +-- SIGNAL toggle_adr_cnt : NATURAL;-- := 0; + SIGNAL nxt_prev_wrdata : NATURAL; + SIGNAL prev_wrdata : NATURAL; + SIGNAL prev_prev_wrdata : NATURAL; + SIGNAL prev_prev_prev_wrdata: NATURAL; + SIGNAL sync_detect : STD_LOGIC := '0'; + SIGNAL sync_detect_pp : STD_LOGIC; +-- SIGNAL adr_w : STD_LOGIC_VECTOR(g_in_data_w -1 DOWNTO c_adr_low); + SIGNAL same_r_w_address : STD_LOGIC; + SIGNAL same_r_w_address_pp : STD_LOGIC; + + --pipelined signals + SIGNAL dp_pipeline_src_out_p : t_dp_sosi; + SIGNAL dp_pipeline_src_out_pp : t_dp_sosi; + SIGNAL prev_bin_reader_mosi : t_mem_mosi := c_mem_mosi_rst ; + SIGNAL bin_reader_mosi_pp : t_mem_mosi := c_mem_mosi_rst; + SIGNAL bin_reader_mosi_ppp : t_mem_mosi := c_mem_mosi_rst; + + --debug signals +-- SIGNAL nxt_dbg_sync_detect : STD_LOGIC; +-- SIGNAL dbg_sync_detect : STD_LOGIC; + SIGNAL dbg_state_string : STRING(1 TO 3) := " "; + SIGNAL dbg_snk_data : STD_LOGIC_VECTOR(g_in_data_w-1 DOWNTO 0); + + +BEGIN + + ----------------------------------------------------------------------------- + -- Bin reader: Convert snk_in data to bin_reader_mosi with read request + -- . in : snk_in (latency: 0) + -- . out : bin_reader_mosi (latency: 0) + -- . out : bin_reader_mosi_pp (latency: 2) + -- - out : rd_cnt_allowed_pp (latency: 2) + ----------------------------------------------------------------------------- + bin_reader_mosi.rd <= snk_in.valid; -- when 1, count allowed + bin_reader_mosi.address(c_adr_w-1 DOWNTO 0) <= snk_in.data(g_in_data_w-1 DOWNTO c_adr_low); + + --snk_in pipeline + 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 + ); + + init_phase <= '0' WHEN dp_pipeline_src_out_p.sync = '1'; + + 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 + ); + + dbg_snk_data <= dp_pipeline_src_out_pp.data(g_in_data_w-1 DOWNTO 0); + + toggle_detect_false <= '0' WHEN dp_pipeline_src_out_pp.sync = '1'; + sync_detect <= snk_in.valid WHEN (snk_in.sync='1' OR dp_pipeline_src_out_p.sync='1' OR dp_pipeline_src_out_pp.sync='1') ELSE '0'; + +-- u_dp_sync_detect_3_cycle : ENTITY dp_lib.dp_pipeline +-- GENERIC MAP ( +-- g_pipeline => 3 -- 0 for wires, > 0 for registers, +-- ) +-- PORT MAP ( +-- rst => dp_rst, +-- clk => dp_clk, +-- snk_in => sync_detect, +-- src_out => sync_detect_ppp +-- ); + + u_common_pipeline_sl_sync_detect_2_cycle : ENTITY common_lib.common_pipeline_sl + GENERIC MAP( + g_pipeline => 2 -- 0 for wires, > 0 for registers, + ) + PORT MAP ( + clk => dp_clk, + in_dat => sync_detect, + out_dat => sync_detect_pp + ); + + --prev_bin_reader_mosi pipeline +-- u_dp_pipeline_bin_reader_mosi_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 => bin_reader_mosi, +-- src_out => prev_bin_reader_mosi +-- ); + + + u_common_pipeline_bin_reader_mosi_1_cycle : ENTITY common_lib.common_pipeline + GENERIC MAP ( + g_representation => "UNSIGNED", --orig. signed + g_pipeline => 1, + g_in_dat_w => c_adr_w, -- c_mem_address_w + g_out_dat_w => c_adr_w + ) + PORT MAP ( + clk => dp_clk, + clken => bin_reader_mosi.rd, -- '1', + in_dat => STD_LOGIC_VECTOR(bin_reader_mosi.address(c_adr_w-1 DOWNTO 0)), + out_dat => prev_bin_reader_mosi.address(c_adr_w-1 DOWNTO 0) + ); + + u_common_pipeline_bin_reader_mosi_2_cycle : ENTITY common_lib.common_pipeline -- better to pipeline prev_bin_reader_mosi?? + 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, + in_dat => STD_LOGIC_VECTOR(prev_bin_reader_mosi.address(c_adr_w-1 DOWNTO 0)), + out_dat => bin_reader_mosi_pp.address(c_adr_w-1 DOWNTO 0) + ); + + u_common_pipeline_bin_reader_mosi_3_cycle : ENTITY common_lib.common_pipeline -- better to pipeline prev_bin_reader_mosi?? + 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, + in_dat => STD_LOGIC_VECTOR(prev_bin_reader_mosi.address(c_adr_w-1 DOWNTO 0)), + out_dat => bin_reader_mosi_ppp.address(c_adr_w-1 DOWNTO 0) + ); + + + --bin_reader_mosi_pp pipeline +-- u_dp_pipeline_bin_reader_mosi_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 => bin_reader_mosi, +-- src_out => bin_reader_mosi_pp +-- ); + +-- rd_cnt_allowed <= snk_in.valid WHEN (bin_reader_mosi.address = prev_bin_reader_mosi.address AND init_phase = '0') ELSE '0'; -- AND snk_in.sync='0' + rd_cnt_allowed <= snk_in.valid WHEN ( bin_reader_mosi.address = prev_bin_reader_mosi.address AND ( (dp_pipeline_src_out_p.sync='1' AND dp_pipeline_src_out_p.valid='1') OR (dp_pipeline_src_out_pp.sync='1' AND dp_pipeline_src_out_p.valid='1') ) ) + ELSE snk_in.valid WHEN (bin_reader_mosi.address = prev_bin_reader_mosi.address AND init_phase='0' AND snk_in.sync='0') + ELSE '0'; + + --rd_cnt_allowed_pp pipeline + u_common_pipeline_sl_rd_cnt_allowed : ENTITY common_lib.common_pipeline_sl + GENERIC MAP( + g_pipeline => 2 -- 0 for wires, > 0 for registers, + ) + PORT MAP ( + clk => dp_clk, + in_dat => rd_cnt_allowed, + out_dat => rd_cnt_allowed_pp + ); + + toggle_detect <= snk_in.valid WHEN (bin_reader_mosi_pp.address = bin_reader_mosi.address AND bin_reader_mosi_pp.address /= prev_bin_reader_mosi.address AND toggle_detect_false = '0') ELSE '0'; --AND (snk_in.sync='0' OR dp_pipeline_src_out_p.sync='0') + + u_common_pipeline_sl_toggle_detect : ENTITY common_lib.common_pipeline_sl + GENERIC MAP( + g_pipeline => 2 -- 0 for wires, > 0 for registers, + ) + PORT MAP ( + clk => dp_clk, + in_dat => toggle_detect, + out_dat => toggle_detect_pp + ); + + same_r_w_address <= snk_in.valid WHEN (bin_reader_mosi.address = bin_reader_mosi_ppp.address AND init_phase = '0' AND sync_detect = '0') ELSE '0'; + + u_common_pipeline_sl_same_r_w_address : ENTITY common_lib.common_pipeline_sl + GENERIC MAP( + g_pipeline => 2 -- 0 for wires, > 0 for registers, + ) + PORT MAP ( + clk => dp_clk, + in_dat => same_r_w_address, + out_dat => same_r_w_address_pp + ); + + + ----------------------------------------------------------------------------- + -- Bin writer : increments current bin value and sets up write request + -- . in : dp_pipeline_src_out_pp (latency: 2) + -- . in : toggle_detect_pp (latency: 2) + -- . in : same_r_w_address_pp (latency: 2) + -- . in : bin_reader_mosi_pp (latency: 2) + -- . in : common_ram_r_w_0_miso (latency: 2) + -- . in : rd_cnt_allowed_pp (latency: 2) + -- . out : bin_writer_mosi (latency: 3) + ----------------------------------------------------------------------------- + p_nxt_bin_writer_mosi : PROCESS(common_ram_r_w_0_miso, common_ram_r_w_0_miso.rdval, common_ram_r_w_0_miso.rddata, + bin_reader_mosi_pp.address, toggle_detect, rd_cnt_allowed_pp, rd_adr_cnt, init_phase, prev_wrdata, prev_prev_wrdata, sync_detect_pp, same_r_w_address_pp, dp_pipeline_src_out_pp.valid) IS + BEGIN + nxt_bin_writer_mosi <= c_mem_mosi_rst; + dbg_state_string <= "unv"; + IF common_ram_r_w_0_miso.rdval='1' THEN -- OR rd_cnt_allowed_pp = '1' -- when not same as last 2 adresses + nxt_bin_writer_mosi.wr <= '1'; + nxt_bin_writer_mosi.wrdata <= INCR_UVEC(common_ram_r_w_0_miso.rddata, 1); -- c_word_w); -- depends on count case -- rd_adr_cnt + nxt_bin_writer_mosi.address <= bin_reader_mosi_pp.address; --TODO: what other input do we need for this? -- becomes bin_reader_mosi.address +-- reset count? if toggle detected copy count to toggle counter + nxt_prev_wrdata <= TO_UINT(common_ram_r_w_0_miso.rddata) + 1; +-- nxt_rd_adr_cnt <= 0; -- really necessary ?? + dbg_state_string <= "val"; +-- IF bin_reader_mosi_pp.address = bin_reader_mosi.address THEN -- Double implemented ?? toggle? +-- nxt_toggle_adr_cnt <= INCR_UVEC(common_ram_r_w_0_miso.rddata, 1); -- Double implemented ?? + ELSIF toggle_detect_pp = '1' THEN -- dp_pipeline_src_out_pp: 2 + nxt_bin_writer_mosi.wr <= '1'; + nxt_bin_writer_mosi.wrdata <= TO_UVEC( (prev_prev_wrdata+1), c_mem_data_w); -- prev_wrdata + rd_adr_cnt + toggle_adr_cnt??? + 1 òf prev_prev_wrdata + 1 ?? + nxt_bin_writer_mosi.address <= bin_reader_mosi_pp.address; +-- nxt_toggle_adr_cnt <= 0; + nxt_prev_wrdata <= prev_prev_wrdata+1; + dbg_state_string <= "td "; + + ELSIF rd_cnt_allowed_pp = '1' THEN +-- nxt_rd_adr_cnt <= rd_adr_cnt + 1; -- << !! is rd_adr_cnt really necessary? prev_wrdata might fulfill the need !! + nxt_bin_writer_mosi.wr <= '1'; +-- IF sync_detect_ppp = '1' THEN +-- nxt_bin_writer_mosi.wrdata <= TO_UVEC( (rd_adr_cnt + 1), c_mem_data_w); -- snk_in.sync (impossible); dp_pipeline_src_out_p (thus 1st cnt): 2 (cnt+1?); dp_pipeline_src_out_pp (1st or maybe 2nd cnt): cnt+1 +-- dbg_state_string <= "rs "; +-- ELSE + nxt_bin_writer_mosi.wrdata <= TO_UVEC( (prev_wrdata + rd_adr_cnt + 1), c_mem_data_w); -- c_word_w); -- maybe RAM + cnt + 1 ?? -- only prev_wrdata + 1 necessary + nxt_prev_wrdata <= prev_wrdata + 1; + dbg_state_string <= "r# "; +-- END IF; + nxt_bin_writer_mosi.address <= bin_reader_mosi_pp.address; + + ELSIF sync_detect_pp = '1' THEN -- snk_in.sync at least -- good as it is! + nxt_bin_writer_mosi.wr <= '1'; + nxt_bin_writer_mosi.wrdata <= TO_UVEC(1, c_mem_data_w); -- snk_in.sync: 1; dp_pipeline_src_out_p.sync (thus new adress): 1; dp_pipeline_src_out_pp.sync (thus new adress): 1 + nxt_bin_writer_mosi.address <= bin_reader_mosi_pp.address; +-- nxt_rd_adr_cnt <= 0; -- really necessary ?? + nxt_prev_wrdata <= 1; + dbg_state_string <= "sd "; + + ELSIF same_r_w_address_pp = '1' THEN + nxt_bin_writer_mosi.wr <= '1'; + nxt_bin_writer_mosi.wrdata <= TO_UVEC( (prev_prev_prev_wrdata+1), c_mem_data_w); + nxt_bin_writer_mosi.address <= bin_reader_mosi_pp.address; + nxt_prev_wrdata <= prev_prev_prev_wrdata + 1; + dbg_state_string <= "srw"; + END IF; + END PROCESS; + + p_bin_writer_mosi : PROCESS(dp_clk, dp_rst, nxt_bin_writer_mosi, nxt_rd_adr_cnt, nxt_prev_wrdata, prev_wrdata, prev_prev_wrdata) IS + BEGIN + IF dp_rst = '1' THEN + bin_writer_mosi <= c_mem_mosi_rst; + ELSIF RISING_EDGE(dp_clk) THEN + bin_writer_mosi <= nxt_bin_writer_mosi; +-- rd_adr_cnt <= nxt_rd_adr_cnt; +-- toggle_adr_cnt <= nxt_toggle_adr_cnt; + prev_wrdata <= nxt_prev_wrdata; + prev_prev_wrdata<= prev_wrdata; + prev_prev_prev_wrdata <= prev_prev_wrdata; + END IF; + END PROCESS; + + + ----------------------------------------------------------------------------- + -- Bin Arbiter: Determine next RAM access + -- . in : bin_reader_mosi (latency: 0) + -- : init_phase (latency: 0) + -- : prev_bin_reader_mosi (latency: 1) + -- : bin_writer_mosi (latency: 3) + -- . out : bin_arbiter_rd_mosi (latency: 1) + -- . : bin_arbiter_wr_mosi (latency: 4) + ----------------------------------------------------------------------------- + nxt_bin_arbiter_wr_mosi <= bin_writer_mosi; --TODO - The rd and wr mosi should not have the same address. v met 2 cycles rd mag, met 3 cycles niet, dus klopt dit wel?, moet hier niet bin_reader_mosi_pp staan? --AND !(A=B) + nxt_bin_arbiter_rd_mosi.rd <= bin_reader_mosi.rd WHEN (bin_reader_mosi.address /= prev_bin_reader_mosi.address AND bin_reader_mosi.address /= bin_reader_mosi_pp.address AND NOT(bin_reader_mosi.address = bin_reader_mosi_ppp.address) ) + -- AND sync_detect='0') + OR (init_phase = '1') ELSE '0'; -- bin_writer_mosi(adress 3cycles ago?) .address when .rd='1' ???? + nxt_bin_arbiter_rd_mosi.address <= bin_reader_mosi.address; + + p_bin_arbiter_mosi : PROCESS(dp_clk, dp_rst, nxt_bin_arbiter_wr_mosi, nxt_bin_arbiter_rd_mosi) IS + BEGIN + IF dp_rst = '1' THEN + bin_arbiter_wr_mosi <= c_mem_mosi_rst; + bin_arbiter_rd_mosi <= c_mem_mosi_rst; + ELSIF RISING_EDGE(dp_clk) THEN + bin_arbiter_wr_mosi <= nxt_bin_arbiter_wr_mosi; + bin_arbiter_rd_mosi <= nxt_bin_arbiter_rd_mosi; + END IF; + END PROCESS; + + + ----------------------------------------------------------------------------- + -- RAM that contains the bins + -- . in : bin_arbiter_wr_mosi (latency: 4) + -- . in : bin_arbiter_rd_mosi (latency: 1) + -- . out : common_ram_r_w_0_miso (latency: 2) + ----------------------------------------------------------------------------- + common_ram_r_w_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', + wr_en => bin_arbiter_wr_mosi.wr, + wr_adr => bin_arbiter_wr_mosi.address(c_adr_w-1 DOWNTO 0), + wr_dat => bin_arbiter_wr_mosi.wrdata(c_word_w-1 DOWNTO 0), + rd_en => bin_arbiter_rd_mosi.rd, + rd_adr => bin_arbiter_rd_mosi.address(c_adr_w-1 DOWNTO 0), + rd_dat => common_ram_r_w_0_miso.rddata(c_word_w-1 DOWNTO 0), + rd_val => common_ram_r_w_0_miso.rdval + ); + + + +END rtl; + diff --git a/libraries/dsp/st/src/vhdl/st_histogram_reg.vhd b/libraries/dsp/st/src/vhdl/st_histogram_reg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..98424485a4e1ca3439959fe4098c2b610cf9aa4e --- /dev/null +++ b/libraries/dsp/st/src/vhdl/st_histogram_reg.vhd @@ -0,0 +1,115 @@ +------------------------------------------------------------------------------- +-- +-- 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: Provide MM slave register for st_histogram +-- Description: +-- Because the st_histogram component uses 2 RAM blocks that are swapped +-- after every sync pulse, both blocks have to work in the dp clock domain +-- and the Memory Mapped bus coming out of the component consequently also +-- works in the dp clock domain. +-- +-- To convert the signals to the mm clock domain the common_reg_cross_domain +-- component is used. Because the inner workings of that component is +-- dependent on some components that take time to reliably stabialize the +-- conversion takes 12 mm clock cycles before the next address may be +-- requested. +-- +-- +-- [Alternative: shared dual clocked RAM block] +-- +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib;-- mm_lib, technology_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_reg IS +-- GENERIC ( +-- g_nof_bins : NATURAL := 512; -- is a power of 2 and g_nof_bins <= c_data_span; max. 512 +-- g_str : STRING := "freq.density" -- to select output to MM bus ("frequency" or "freq.density") +-- ); + PORT ( + dp_rst : IN STD_LOGIC; + dp_clk : IN STD_LOGIC; + mm_rst : IN STD_LOGIC; + mm_clk : IN STD_LOGIC; + + -- DP clocked memory bus + mas_out_ram_mosi : OUT t_mem_mosi ;--:= c_mem_mosi_rst; -- Beware, works in dp clock domain ! + mas_in_ram_miso : IN t_mem_miso ;--:= c_mem_miso_rst; -- '' ! +-- ram_st_histogram_mosi : OUT t_mem_mosi; -- Beware, works in dp clock domain ! +-- ram_st_histogram_miso : IN t_mem_miso; -- '' ! + + -- Memory Mapped + ram_mosi : IN t_mem_mosi; + ram_miso : OUT t_mem_miso + ); +END st_histogram_reg; + +ARCHITECTURE str OF st_histogram_reg IS + +-- CONSTANT c_mm_reg : t_c_mem := (latency => 1, +-- adr_w => 1, +-- dat_w => c_word_w, +-- nof_dat => 1, +-- init_sl => g_default_value); + + +BEGIN + + + u_common_reg_cross_domain_mosi_address : ENTITY common_lib.common_reg_cross_domain + PORT MAP ( + in_rst => mm_rst, + in_clk => mm_clk, + + in_new => ram_mosi.rd, + in_dat => ram_mosi.address, + + out_rst => dp_rst, + out_clk => dp_clk, + + out_dat => mas_out_ram_mosi.address, + out_new => mas_out_ram_mosi.rd + ); + + u_reg_cross_domain_miso_rddata : ENTITY common_lib.common_reg_cross_domain + PORT MAP ( + in_rst => dp_rst, + in_clk => dp_clk, + + in_new => mas_in_ram_miso.rdval, + in_dat => mas_in_ram_miso.rddata, + + out_rst => mm_rst, + out_clk => mm_clk, + + out_dat => ram_miso.rddata, + out_new => ram_miso.rdval + ); + +END str; diff --git a/libraries/dsp/st/tb/vhdl/tb_mms_st_histogram.vhd b/libraries/dsp/st/tb/vhdl/tb_mms_st_histogram.vhd new file mode 100644 index 0000000000000000000000000000000000000000..8c74592e65fa4a7776fe01c12e73c17808437444 --- /dev/null +++ b/libraries/dsp/st/tb/vhdl/tb_mms_st_histogram.vhd @@ -0,0 +1,302 @@ +------------------------------------------------------------------------------- +-- +-- 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: +-- +-- +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, mm_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; + +ENTITY tb_mms_st_histogram IS + GENERIC( + g_sync_length : NATURAL := 338; + g_nof_sync : NATURAL := 3; + g_data_w : NATURAL := 4; + g_nof_bins : NATURAL := 8; + g_nof_data : NATURAL := 338; + g_str : STRING := "freq.density"; + g_valid_gap : BOOLEAN := FALSE; + g_snk_in_data_sim_type : STRING := "counter" -- "counter" or "toggle" + ); +END tb_mms_st_histogram; + + +ARCHITECTURE tb OF tb_mms_st_histogram IS + + CONSTANT c_adr_w : NATURAL := ceil_log2(g_nof_bins); + + CONSTANT c_mm_init_time : NATURAL := 5; + CONSTANT c_dp_inti_time : NATURAL := 5; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL first_sync : STD_LOGIC := '0'; + + ---------------------------------------------------------------------------- + -- Clocks and resets + ---------------------------------------------------------------------------- + CONSTANT c_mm_clk_period : TIME := 20 ns; + CONSTANT c_dp_clk_period : TIME := 5 ns; + + + SIGNAL mm_rst : STD_LOGIC := '1'; + SIGNAL mm_clk : STD_LOGIC := '1'; + + SIGNAL dp_rst : STD_LOGIC; + SIGNAL dp_clk : STD_LOGIC := '1'; + + + + + ---------------------------------------------------------------------------- + -- Streaming Input + ---------------------------------------------------------------------------- + + SIGNAL st_histogram_snk_in : t_dp_sosi; + + ---------------------------------------------------------------------------- + -- Memory Mapped Input + ---------------------------------------------------------------------------- + + SIGNAL st_histogram_ram_mosi : t_mem_mosi; + SIGNAL st_histogram_ram_miso : t_mem_miso; + + +BEGIN + + ---------------------------------------------------------------------------- + -- Clock and reset generation + ---------------------------------------------------------------------------- + mm_clk <= NOT mm_clk OR tb_end AFTER c_mm_clk_period/2; + mm_rst <= '1', '0' AFTER c_mm_clk_period*c_mm_init_time; + + dp_clk <= NOT dp_clk OR tb_end AFTER c_dp_clk_period/2; + dp_rst <= '1', '0' AFTER c_dp_clk_period*c_dp_inti_time; + + + + + ---------------------------------------------------------------------------- + -- Source: counter stimuli + ---------------------------------------------------------------------------- + + p_data : PROCESS(dp_rst, dp_clk, st_histogram_snk_in) + BEGIN + IF g_snk_in_data_sim_type = "counter" THEN + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND st_histogram_snk_in.valid='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), 1); + END IF; + ELSIF g_snk_in_data_sim_type = "toggle" THEN + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND st_histogram_snk_in.valid='1' THEN + IF st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) = TO_UVEC(0, g_data_w) THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= TO_UVEC(1, g_data_w); + ELSE + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= TO_UVEC(0, g_data_w); + END IF; + END IF; + END IF; + END PROCESS; + + p_stimuli : PROCESS + BEGIN + IF g_valid_gap = FALSE THEN +-- dp_rst <= '1'; + st_histogram_snk_in.sync <= '0'; + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); +-- FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; +-- dp_rst <= '0'; + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + st_histogram_snk_in.valid <= '1'; + + + FOR I IN 0 TO g_nof_sync-1 LOOP + st_histogram_snk_in.sync <= '1'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.sync <= '0'; + FOR I IN 0 TO g_sync_length-1 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + + END LOOP; + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + tb_end <= '1'; + WAIT; + + ELSIF g_valid_gap = TRUE THEN +-- dp_rst <= '1'; + st_histogram_snk_in.sync <= '0'; + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); +-- FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; +-- dp_rst <= '0'; + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + st_histogram_snk_in.valid <= '1'; + + + FOR I IN 0 TO g_nof_sync-2 LOOP + st_histogram_snk_in.sync <= '1'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.sync <= '0'; + FOR I IN 0 TO (g_sync_length/2)-1 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + --WAIT UNTIL rising_edge(dp_clk); + --WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.valid <= '1'; + FOR I IN 0 TO (g_sync_length/4)-1 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + --st_histogram_snk_in.valid <= '0'; + st_histogram_snk_in.sync <= '1'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.valid <= '1'; + st_histogram_snk_in.sync <= '0'; + FOR I IN 0 TO (g_sync_length/4)-1 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + + END LOOP; + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + tb_end <= '1'; + WAIT; + END IF; + END PROCESS; + + ---------------------------------------------------------------------------- + -- Source: read MM bus stimuli + ---------------------------------------------------------------------------- + +-- p_mm_stimuli : PROCESS --(st_histogram_snk_in.sync) +-- BEGIN +-- IF mm_rst='1' THEN +-- st_histogram_ram_mosi <= c_mem_mosi_rst; --.address(c_adr_w-1 DOWNTO 0) <= (OTHERS=>'0'); +---- ELSIF rising_edge(mm_clk) THEN --AND st_histogram_snk_in.valid='1' +-- ELSE +-- IF first_sync = '0' THEN +-- WAIT UNTIL st_histogram_snk_in.sync = '1'; +-- first_sync <= '1'; +-- -- wait till one RAM block is written +-- FOR I IN 0 TO (g_sync_length/4) LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP; +-- -- wait for some more cycles +-- FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP; +---- ELSIF rising_edge(mm_clk) THEN +-- ELSE +-- FOR I IN 0 TO g_nof_bins-1 +-- -- +-- st_histogram_ram_mosi.rd <= '1'; +-- st_histogram_ram_mosi.address(c_adr_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_ram_mosi.address(c_adr_w-1 DOWNTO 0), 1); +-- END IF; +-- END IF; +-- END PROCESS; + + p_mm_stimuli : PROCESS --(st_histogram_snk_in.sync) + BEGIN + --IF mm_rst='1' THEN + st_histogram_ram_mosi <= c_mem_mosi_rst; --.address(c_adr_w-1 DOWNTO 0) <= (OTHERS=>'0'); +-- ELSIF rising_edge(mm_clk) THEN --AND st_histogram_snk_in.valid='1' + --ELSE + --IF first_sync = '0' THEN + WAIT UNTIL st_histogram_snk_in.sync = '1'; + --first_sync <= '1'; + -- wait till one RAM block is written + FOR I IN 0 TO (g_sync_length/4) LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP; + -- wait for some more cycles + FOR I IN 0 TO 2 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP; +-- ELSIF rising_edge(mm_clk) THEN + --ELSE + FOR I IN 0 TO g_nof_bins-1 LOOP + proc_mem_mm_bus_rd(I, mm_clk, st_histogram_ram_mosi); + proc_common_wait_some_cycles(mm_clk, 11); + -- miso.rddata arrives + END LOOP; + -- + --st_histogram_ram_mosi.rd <= '1'; + --st_histogram_ram_mosi.address(c_adr_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_ram_mosi.address(c_adr_w-1 DOWNTO 0), 1); + --END IF; + --END IF; + END PROCESS; + +-- -- Read data request to the MM bus +-- -- Use proc_mem_mm_bus_rd_latency() to wait for the MM MISO rd_data signal +-- -- to show the data after some read latency +-- PROCEDURE proc_mem_mm_bus_rd(CONSTANT rd_addr : IN NATURAL; +-- SIGNAL mm_clk : IN STD_LOGIC; +-- SIGNAL mm_miso : IN t_mem_miso; +-- SIGNAL mm_mosi : OUT t_mem_mosi) IS +-- BEGIN +-- mm_mosi.address <= TO_MEM_ADDRESS(rd_addr); +-- proc_mm_access(mm_clk, mm_miso.waitrequest, mm_mosi.rd); +-- END proc_mem_mm_bus_rd; + +---- Issues a rd or a wr MM access and wait for it to have finished +-- PROCEDURE proc_mm_access(SIGNAL mm_clk : IN STD_LOGIC; +-- SIGNAL mm_waitreq : IN STD_LOGIC; +-- SIGNAL mm_access : OUT STD_LOGIC) IS +-- BEGIN +-- mm_access <= '1'; +-- WAIT UNTIL rising_edge(mm_clk); +-- WHILE mm_waitreq='1' LOOP +-- WAIT UNTIL rising_edge(mm_clk); +-- END LOOP; +-- mm_access <= '0'; +-- END proc_mm_access; + +-- proc_mem_mm_bus_rd(0, mm_clk, mm_mosi); -- Read nof_early_syncs +-- proc_common_wait_some_cycles(mm_clk, 1); +-- mm_nof_early_syncs <= mm_miso.rddata(c_word_w-1 DOWNTO 0); + + ---------------------------------------------------------------------------- + -- DUT: Device Under Test + ---------------------------------------------------------------------------- + + u_mms_st_histogram : ENTITY work.mms_st_histogram + GENERIC MAP( + g_in_data_w => g_data_w, + g_nof_bins => g_nof_bins, + g_nof_data => g_nof_data, + g_str => g_str + ) + PORT MAP ( + dp_rst => dp_rst, + dp_clk => dp_clk, + mm_rst => mm_rst, + mm_clk => mm_clk, + + -- Streaming + snk_in => st_histogram_snk_in, + + -- Memory Mapped + ram_mosi => st_histogram_ram_mosi, + ram_miso => st_histogram_ram_miso --OPEN + ); + +END tb; diff --git a/libraries/dsp/st/tb/vhdl/tb_st_histogram.vhd b/libraries/dsp/st/tb/vhdl/tb_st_histogram.vhd new file mode 100644 index 0000000000000000000000000000000000000000..e997850df3698990fdbd06a4a0badc7598ac386b --- /dev/null +++ b/libraries/dsp/st/tb/vhdl/tb_st_histogram.vhd @@ -0,0 +1,307 @@ +------------------------------------------------------------------------------- +-- +-- 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: Testing the st_histogram component on it's pecularities +-- Description: +-- The st_histogram component is mainly about saving counter data and +-- making the saved data available for the MM master. The working of the +-- RAM blocks has a big influence on this. That is why the testbench is made +-- to generate data that can make related problems with that vissible. +-- +-- To know if there can constantly new data be witten to the RAM blocks +-- a simple counter is sufficient. +-- +-- Because there is a delay between requesting and writing back of data of +-- 2 cycles and it is illegal to read and write on the same adres at the +-- same time, a special situation can happen where the addresses can toggle +-- (e.g. 0; 1; 0; 1) which causes incorrect counting. To simulate this the +-- g_snk_in_data_sim_type can be set to 'toggle' +-- +-- Only incoming data while snk_in.valid = '1' may be counted. To keep the +-- simulation simple there is the option to let there be some gap's in the +-- valid data (or not) where snk_in.valid = '0' by setting the g_valid_gap +-- to TRUE or FALSE. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, mm_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; + +ENTITY tb_st_histogram IS + GENERIC( + g_sync_length : NATURAL := 200; + g_nof_sync : NATURAL := 3; + g_data_w : NATURAL := 4; --4 ; 1 + g_nof_bins : NATURAL := 8; --8 ; 2 + g_nof_data : NATURAL := 200; + --g_str : STRING := "freq.density"; + g_valid_gap : BOOLEAN := TRUE; + g_snk_in_data_sim_type : STRING := "counter" -- "counter" or "toggle" or "same rw" or "mix" + ); +END tb_st_histogram; + + +ARCHITECTURE tb OF tb_st_histogram IS + + CONSTANT c_adr_w : NATURAL := ceil_log2(g_nof_bins); + CONSTANT c_adr_low_calc : INTEGER := g_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 + --SIGNAL position : INTEGER range g_data_w'RANGE; + + CONSTANT c_dp_inti_time : NATURAL := 5; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL pre_valid : STD_LOGIC := '0'; + SIGNAL prev_unvalid : STD_LOGIC := '0'; + SIGNAL init_phase : STD_LOGIC := '1'; + SIGNAL toggle_start : STD_LOGIC := '0'; + + + ---------------------------------------------------------------------------- + -- Same read write test stimuli + ---------------------------------------------------------------------------- + TYPE t_srw_arr IS ARRAY (NATURAL RANGE <>) OF INTEGER; + CONSTANT c_srw_arr : t_srw_arr := (0,0,1,1,0,0,1,2,3, 1, 2, 3, 0, 3, 3, 0, 3); + -- 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17 + + SIGNAL srw_index_cnt : NATURAL := 0; + + + ---------------------------------------------------------------------------- + -- Clocks and resets + ---------------------------------------------------------------------------- + CONSTANT c_dp_clk_period : TIME := 5 ns; + + SIGNAL dp_rst : STD_LOGIC; + SIGNAL dp_clk : STD_LOGIC := '1'; + + + + + ---------------------------------------------------------------------------- + -- Streaming Input + ---------------------------------------------------------------------------- + + SIGNAL st_histogram_snk_in : t_dp_sosi; + + +BEGIN + + ---------------------------------------------------------------------------- + -- Clock and reset generation + ---------------------------------------------------------------------------- + dp_clk <= NOT dp_clk OR tb_end AFTER c_dp_clk_period/2; + dp_rst <= '1', '0' AFTER c_dp_clk_period*c_dp_inti_time; + + + + + ---------------------------------------------------------------------------- + -- Source: stimuli + -- st_histogram_snk_in.data counter or toggle stimuli + -- .valid with or without gap's in valid stimuli + -- .sync sync stimuli + ---------------------------------------------------------------------------- + + init_phase <= '0' WHEN st_histogram_snk_in.sync = '1'; + + p_data : PROCESS(dp_rst, dp_clk, st_histogram_snk_in) + BEGIN + IF g_snk_in_data_sim_type = "counter" THEN + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND pre_valid='1' THEN -- st_histogram_snk_in.valid='1' THEN -- maybe needs init_cnt_start = '1' instead? + IF prev_unvalid = '0' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), 1); + ELSIF prev_unvalid = '1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), -1); + prev_unvalid <= '0'; + END IF; + ELSIF rising_edge(dp_clk) AND pre_valid='0' AND init_phase='0' THEN -- st_histogram_snk_in.valid='0' AND init_phase = '0' THEN + IF prev_unvalid = '0' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), 2); + prev_unvalid <= '1'; + END IF; + END IF; + + ELSIF g_snk_in_data_sim_type = "toggle" THEN + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND st_histogram_snk_in.valid='1' THEN -- maybe needs init_cnt_start = '1' instead? + IF st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) = TO_UVEC(0, g_data_w) THEN -- c_adr_low + st_histogram_snk_in.data(c_adr_low) <= '1'; -- TO_UVEC(1, g_data_w); --g_data_w-1 DOWNTO 0 + ELSE + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= TO_UVEC(0, g_data_w); + END IF; + END IF; + + ELSIF g_snk_in_data_sim_type = "same rw" THEN + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND pre_valid='1' THEN -- AND init_phase='0' didn't work + st_histogram_snk_in.data(g_data_w-1 DOWNTO c_adr_low) <= TO_UVEC(c_srw_arr(srw_index_cnt), c_adr_w); --placeholder ! + IF srw_index_cnt = c_srw_arr'LENGTH -1 THEN + srw_index_cnt <= 0; + ELSE + srw_index_cnt <= srw_index_cnt+1; + END IF; + END IF; + + ELSIF g_snk_in_data_sim_type = "mix" THEN + IF toggle_start = '1' THEN + -- toggle part + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND st_histogram_snk_in.valid='1' THEN -- maybe needs init_cnt_start = '1' instead? + IF st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) = TO_UVEC(0, g_data_w) THEN -- c_adr_low + st_histogram_snk_in.data(c_adr_low) <= '1'; -- TO_UVEC(1, g_data_w); --g_data_w-1 DOWNTO 0 + ELSE + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= TO_UVEC(0, g_data_w); + END IF; + END IF; + -- end toggle part + ELSE + -- counter part + IF dp_rst='1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= (OTHERS=>'0'); + ELSIF rising_edge(dp_clk) AND pre_valid='1' THEN -- st_histogram_snk_in.valid='1' THEN -- maybe needs init_cnt_start = '1' instead? + IF prev_unvalid = '0' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), 1); + ELSIF prev_unvalid = '1' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), -1); + prev_unvalid <= '0'; + END IF; + ELSIF rising_edge(dp_clk) AND pre_valid='0' AND init_phase='0' THEN -- st_histogram_snk_in.valid='0' AND init_phase = '0' THEN + IF prev_unvalid = '0' THEN + st_histogram_snk_in.data(g_data_w-1 DOWNTO 0) <= INCR_UVEC(st_histogram_snk_in.data(g_data_w-1 DOWNTO 0), 2); + prev_unvalid <= '1'; + END IF; + END IF; + -- end counter part + END IF; + END IF; + END PROCESS; + + + p_stimuli : PROCESS + BEGIN + IF g_valid_gap = FALSE THEN + + -- initializing + st_histogram_snk_in.sync <= '0'; + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + pre_valid <= '1'; + st_histogram_snk_in.valid <= '1'; + -- generating g_nof_sync sync pulses with g_sync_length cycles between + FOR I IN 0 TO g_nof_sync-1 LOOP + toggle_start <= '1'; + st_histogram_snk_in.sync <= '1'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.sync <= '0'; + proc_common_wait_some_cycles(dp_clk, 2); + toggle_start <= '0'; + FOR I IN 0 TO g_sync_length-1 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; -- -4 ipv -1 ? + END LOOP; + -- ending + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + tb_end <= '1'; + WAIT; + + ELSIF g_valid_gap = TRUE THEN + + -- initializing + st_histogram_snk_in.sync <= '0'; + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + pre_valid <= '1'; + st_histogram_snk_in.valid <= '1'; + -- generating g_nof_sync-1 sync pulses with gaps in 'valid' + FOR I IN 0 TO g_nof_sync-2 LOOP + toggle_start <= '1'; + st_histogram_snk_in.sync <= '1'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.sync <= '0'; + proc_common_wait_some_cycles(dp_clk, 2); + toggle_start <= '0'; + FOR I IN 0 TO (g_sync_length/2)-5 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; -- -5 ipv -2 ? + pre_valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.valid <= '0'; + pre_valid <= '1'; -- gap 1 clock cycles + WAIT UNTIL rising_edge(dp_clk); + --WAIT UNTIL rising_edge(dp_clk); -- gap 2 clock cycles + --WAIT UNTIL rising_edge(dp_clk); -- gap 3 clock cycles + st_histogram_snk_in.valid <= '1'; + FOR I IN 0 TO (g_sync_length/4)-2 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + pre_valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.valid <= '0'; + WAIT UNTIL rising_edge(dp_clk); + --st_histogram_snk_in.valid <= '0'; -- gap while sync + st_histogram_snk_in.sync <= '1'; + pre_valid <= '1'; + WAIT UNTIL rising_edge(dp_clk); + st_histogram_snk_in.valid <= '1'; + st_histogram_snk_in.sync <= '0'; + FOR I IN 0 TO (g_sync_length/4)-1 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + END LOOP; + -- ending + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(dp_clk); END LOOP; + tb_end <= '1'; + WAIT; + END IF; + END PROCESS; + + + + ---------------------------------------------------------------------------- + -- DUT: Device Under Test + ---------------------------------------------------------------------------- + + u_st_histogram : ENTITY work.st_histogram_8_april + GENERIC MAP( + g_in_data_w => g_data_w, + g_nof_bins => g_nof_bins, + g_nof_data => g_nof_data + --g_str => g_str + ) + PORT MAP ( + dp_rst => dp_rst, + dp_clk => dp_clk, + + -- Streaming + snk_in => st_histogram_snk_in, + + -- Memory Mapped + ram_mosi => c_mem_mosi_rst,-- sla_in_ + ram_miso => OPEN -- sla_out_ + ); + +END tb;