PCS-2/filter.vhd
2023-12-12 16:08:43 +01:00

204 lines
7.4 KiB
VHDL

-- filter.vhd: Exact match filter for IP addresses using multiple parallel hash tables (architecture)
-- Copyright (C) 2019 FIT BUT
-- Author(s): Lukas Kekely <ikekely@fit.vutbr.cz>
--
-- SPDX-License-Identifier: BSD-3-Clause
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use IEEE.std_logic_arith.all;
use work.functions_package.all;
architecture structural of filter is
constant RULE_WIDTH : natural := KEY_WIDTH + DATA_WIDTH + 1; -- each rule record consists of key, data, and empty flag
subtype RULE_KEY is natural range KEY_WIDTH+DATA_WIDTH downto DATA_WIDTH+1;
subtype RULE_DATA is natural range DATA_WIDTH downto 1;
constant RULE_EMPTY : natural := 0;
constant KEY_WORDS : natural := cdiv(KEY_WIDTH, 32); -- hash component operates with 32-bit words
type uint32_array is array (natural range<>) of std_logic_vector(32-1 downto 0);
type rule_array is array (natural range <>) of std_logic_vector(RULE_WIDTH-1 downto 0);
signal in_key : std_logic_vector(KEY_WORDS*32-1 downto 0) := (others => '0');
signal in_valid : std_logic;
signal out_key : std_logic_vector(KEY_WIDTH-1 downto 0);
signal out_key_found : std_logic;
signal out_data : std_logic_vector(DATA_WIDTH-1 downto 0);
signal out_valid : std_logic;
signal cfg_record : std_logic_vector(RULE_WIDTH-1 downto 0);
signal cfg_table : std_logic_vector(clog2(TABLES)-1 downto 0);
signal cfg_item : std_logic_vector(clog2(TABLE_SIZE)-1 downto 0);
signal cfg_write : std_logic;
signal hash_values : uint32_array(0 to TABLES-1);
signal hash_key : std_logic_vector(KEY_WIDTH-1 downto 0);
signal hash_valid : std_logic_vector(TABLES-1 downto 0);
signal memory_rule : rule_array(0 to TABLES-1);
signal memory_key, memory_key_reg : std_logic_vector(KEY_WIDTH-1 downto 0);
signal memory_valid : std_logic_vector(TABLES-1 downto 0);
signal match_flags : std_logic_vector(TABLES-1 downto 0);
begin
-- Unsupported configurations checks -----------------------------------------
assert KEY_WIDTH > 0 report "FAILURE: Filter key must be at least one bit wide!" severity failure;
assert DATA_WIDTH > 0 report "FAILURE: Filter data must be at least one bit wide!" severity failure;
assert TABLES >= 2 report "FAILURE: Filter must have at least two tables to support effective hashing scheme!" severity failure;
assert TABLE_SIZE > 1 report "FAILURE: Filter table must have multiple items!" severity failure;
assert 2**clog2(TABLE_SIZE) = TABLE_SIZE report "FAILURE: Filter table must have size that is a power of 2!" severity failure;
-- Registering of entity interfaces ------------------------------------------
-- Input interface
registered_input: if INPUT_REGISTER generate
input_register: process(CLK)
begin
if rising_edge(CLK) then
in_key(KEY_WIDTH-1 downto 0) <= INPUT_KEY;
if RESET = '1' then
in_valid <= '0';
else
in_valid <= INPUT_VALID;
end if;
end if;
end process;
end generate;
dirrect_input: if not INPUT_REGISTER generate
in_key(KEY_WIDTH-1 downto 0) <= INPUT_KEY;
in_valid <= INPUT_VALID;
end generate;
-- Output interface
registered_output: if OUTPUT_REGISTER generate
output_register: process(CLK)
begin
if rising_edge(CLK) then
OUTPUT_KEY <= out_key;
OUTPUT_KEY_FOUND <= out_key_found;
OUTPUT_DATA <= out_data;
if RESET = '1' then
OUTPUT_VALID <= '0';
else
OUTPUT_VALID <= out_valid;
end if;
end if;
end process;
end generate;
dirrect_output: if not OUTPUT_REGISTER generate
OUTPUT_KEY_FOUND <= out_key_found;
OUTPUT_DATA <= out_data;
OUTPUT_VALID <= out_valid;
end generate;
-- Configuration interface
registered_config: if CONFIG_REGISTER generate
config_register: process(CLK)
begin
if rising_edge(CLK) then
cfg_record(RULE_KEY) <= CONFIG_KEY;
cfg_record(RULE_DATA) <= CONFIG_DATA;
cfg_record(RULE_EMPTY) <= CONFIG_EMPTY;
cfg_table <= CONFIG_ADDRESS_TABLE;
cfg_item <= CONFIG_ADDRESS_ITEM;
if RESET = '1' then
cfg_write <= '0';
else
cfg_write <= CONFIG_WRITE;
end if;
end if;
end process;
end generate;
dirrect_config: if not CONFIG_REGISTER generate
cfg_record(RULE_KEY) <= CONFIG_KEY;
cfg_record(RULE_DATA) <= CONFIG_DATA;
cfg_record(RULE_EMPTY) <= CONFIG_EMPTY;
cfg_table <= CONFIG_ADDRESS_TABLE;
cfg_item <= CONFIG_ADDRESS_ITEM;
cfg_write <= CONFIG_WRITE;
end generate;
-- Hashing stage -------------------------------------------------------------
hash_generate: for t in 0 to TABLES-1 generate
signal local_key : std_logic_vector(KEY_WORDS*32-1 downto 0);
signal local_valid : std_logic;
begin
hash: entity work.jenkins_hash
generic map (
LENGTH => KEY_WORDS,
INITVAL => conv_std_logic_vector(t+1,32)
) port map (
CLK => CLK,
RESET => RESET,
INPUT_KEY => in_key,
INPUT_VALID => in_valid,
OUTPUT_HASH => hash_values(t),
OUTPUT_KEY => local_key,
OUTPUT_VALID => hash_valid(t)
);
key_valid_connection: if t = 0 generate -- key is pipelined inside the hash for table #0
hash_key <= local_key(KEY_WIDTH-1 downto 0);
end generate;
end generate;
-- Storage stage -------------------------------------------------------------
storage_generate: for t in 0 to TABLES-1 generate
signal table_select : std_logic;
signal write_here : std_logic;
begin
storage: entity work.block_memory
generic map (
ITEM_WIDTH => RULE_WIDTH,
ITEMS => TABLE_SIZE,
OUTPUT_REGISTER => true
) port map (
CLK => CLK,
RESET => RESET,
READ_ADDRESS => hash_values(t)(clog2(TABLE_SIZE)-1 downto 0), -- lowest hash bits are used as address
READ_VALID => hash_valid(t),
READ_DATA => memory_rule(t),
READ_DATA_VALID => memory_valid(t),
WRITE_DATA => cfg_record,
WRITE_ADDRESS => cfg_item,
WRITE_VALID => write_here
);
table_select <= '1' when cfg_table = conv_std_logic_vector(t,clog2(TABLES)) else '0'; -- table write enable selection
write_here <= cfg_write and table_select;
end generate;
key_memory_register: process(CLK) -- key pipeline to match memory latency
begin
if rising_edge(CLK) then
--memory_key <= hash_key; -- memory is configured to have one cycle read latecy
memory_key_reg <= hash_key;
memory_key <= memory_key_reg;
end if;
end process;
-- Match decoding stage ------------------------------------------------------
compare_and_select: process(memory_key, memory_rule)
begin
out_key_found <= '0';
out_data <= (others => 'X');
for t in 0 to TABLES-1 loop
if memory_key = memory_rule(t)(RULE_KEY) then -- main key compares
if memory_rule(t)(RULE_EMPTY) = '0' then -- do not forget to ignore empty rules
out_key_found <= '1';
out_data <= memory_rule(t)(RULE_DATA);
end if;
end if;
end loop;
end process;
out_valid <= memory_valid(0); -- select pipelined valid flag from table #0
out_key <= memory_key;
end architecture;