-- wg_mem -- -- Generate weights using stream of idx -- -- Inputs are spike index and location of kernel to read -- -- Current implementation assumes that all weights are cached -- and that sizes of and KI and KO are powers of 2 use work.pkg_sbs.all; entity wg_mem is generic ( LOG2_H : natural := 2; -- size of H (number of output IPs per -- output location) LOG2_KI : natural := 4; -- number IPs per input (thus spike index) LOG2_KO : natural := 3); -- number connections from IPi -- block to IPo block (thus, -- number of output IPs of full connected, -- kernel size in conv) port ( clk, rstn : in bit; -- Initial update do_init_str : in bit; -- First step in init process do_init_nxt : in bit; -- Next step in init process w_init : in real; -- Weight value to update -- Normal idx : in bit_vector(LOG2_KI-1 downto 0); -- Index of spike pos : in bit_vector(LOG2_KO-1 downto 0); -- Location of output (edge, kernel) ena_idx : in bit; busy_idx : out bit; ena_w : out bit; -- Send a weight w : out real); -- stream of states end entity wg_mem; library ieee; use ieee.numeric_bit.all; architecture rtl of wg_mem is signal busy_rg, busy_nxt, i_done, idx_done, pos_done : bit; signal idx_rg, idx_nxt : unsigned(LOG2_KI-1 downto 0); signal pos_rg, pos_nxt : unsigned(LOG2_KO-1 downto 0); signal i_rg, i_nxt : unsigned(LOG2_H-1 downto 0); -- All these params could be configurable.. constant I_LAST : unsigned(LOG2_H-1 downto 0) := (others=>'1'); constant IDX_LAST : unsigned(LOG2_KI-1 downto 0) := (others=>'1'); constant POS_LAST : unsigned(LOG2_KO-1 downto 0) := (others=>'1'); constant I_ZERO : unsigned(LOG2_H-1 downto 0) := (others=>'0'); constant IDX_ZERO : unsigned(LOG2_KI-1 downto 0) := (others=>'0'); constant POS_ZERO : unsigned(LOG2_KO-1 downto 0) := (others=>'0'); -- Memory signal mem_addr, mem_addr_nxt : unsigned(LOG2_H+LOG2_KI+LOG2_KO-1 downto 0); signal mem_wr, mem_rd : bit; begin -- architecture rtl busy_idx <= busy_rg; ena_w <= busy_rg; i_done <= '1' when i_rg = I_ZERO else '0'; idx_done <= '1' when idx_rg = IDX_ZERO else '0'; pos_done <= '1' when pos_rg = POS_ZERO else '0'; busy_nxt <= '1' when ena_idx='1' else '0' when i_done='1' else busy_rg; i_nxt <= I_LAST when (ena_idx='1') or (do_init_str='1') else i_nxt-1 when (busy_rg='1') or (do_init_nxt='1') else i_rg; idx_nxt <= unsigned(idx) when ena_idx='1' else IDX_LAST when do_init_str='1' else idx_nxt-1 when (do_init_nxt='1') and (i_done='1') else idx_rg; pos_nxt <= unsigned(pos) when ena_idx='1' else POS_LAST when do_init_str='1' else pos_nxt-1 when (do_init_nxt='1') and (idx_done='1') else pos_rg; reg: process (clk, rstn) is begin -- process reg if rstn = '0' then -- asynchronous reset (active low) idx_rg <= IDX_LAST; pos_rg <= POS_LAST; i_rg <= I_LAST; busy_rg <= '0'; elsif clk'event and clk = '1' then -- rising clock edge idx_rg <= idx_nxt; pos_rg <= pos_nxt; i_rg <= i_nxt; busy_rg <= busy_nxt; end if; end process reg; mem_addr_nxt <= pos_nxt & idx_nxt & i_nxt; mem_wr <= do_init_str or do_init_nxt; mem_rd <= '1'; -- Implementation of a synchronous single port memory mem: process (clk) is constant mem_size : natural := 2**(mem_addr'length); type mem_ty is array (0 to mem_size-1) of real; variable w_mem : mem_ty; begin -- process mem if clk'event and clk = '1' then -- rising clock edge mem_addr <= mem_addr_nxt; if mem_wr='1' then w_mem(to_integer(mem_addr)) := w_init; end if; if mem_rd='1' then w <= w_mem(to_integer(mem_addr)); end if; end if; end process mem; end architecture rtl;