wk_sbs_hdl/hw/beh/wg_mem.vhd

126 lines
4.1 KiB
VHDL

-- 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;