-------------------------------------------------------------------------------
--
-- Complex Multiplier OPB Interface
--
-- Uses an OPB interface, e.g., for use with the Microblaze soft core
--
-- Marcio Buss, Stephen Edwards
-- {marcio, sedwards}@cs.columbia.edu
--
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;

entity opb_complex_multiplier is
  generic (
    C_OPB_AWIDTH : integer                    := 32;
    C_OPB_DWIDTH : integer                    := 32;
    C_BASEADDR   : std_logic_vector(31 downto 0) := X"01800000"; 
    C_HIGHADDR   : std_logic_vector(31 downto 0) := X"01803FFF");

  port (
    -- Global signals
    OPB_Clk : in std_logic;
    OPB_Rst : in std_logic;

    -- OPB signals
    OPB_ABus    : in std_logic_vector (31 downto 0);
    OPB_BE      : in std_logic_vector (3 downto 0);
    OPB_DBus    : in std_logic_vector (31 downto 0);
    OPB_RNW     : in std_logic;
    OPB_select  : in std_logic;
    OPB_seqAddr : in std_logic;

     -- Slave signals
    MULT_DBus       : out std_logic_vector (31 downto 0);
    MULT_errAck     : out std_logic;
    MULT_retry      : out std_logic;
    MULT_toutSup    : out std_logic;
    MULT_xferAck    : out std_logic
    );
end opb_complex_multiplier;

architecture Behavioral of opb_complex_multiplier is

  -- Multiplier built by using coregen 
  component mult_s8_s8
        port (
        a: IN std_logic_VECTOR(7 downto 0);
        b: IN std_logic_VECTOR(7 downto 0);
        o: OUT std_logic_VECTOR(15 downto 0));
  end component;


-- Signals used for  reading/writing  registers
-- and implementing the OPB bus "xfer" protocol 
signal cs   : std_logic;   -- chip select
signal re   : std_logic;   -- read enable
signal we   : std_logic;   -- write enable
signal rnw  : std_logic;   -- read-not-write ('1' when using XIo_In, '0' XIo_Out)
signal xfer : std_logic;   -- transfer acknoledge signal microblaze waits for

-- Address used to read/write from/to this entity
signal addr : std_logic_vector (31 downto 0);

-- Registers we are writing when using XIo_Out
signal reg1, reg2, reg3, reg4 : std_logic_vector(7 downto 0);

-- Register used when reading the result from the multiplier
signal regout : std_logic_vector(15 downto 0);
  
-- Signals  for  the "one-hot-like"  state  machine
-- implementing the OPB bus "transfer ack" protocol
signal q2, q1, q0 : std_logic;

-- Data FROM multiplier (output regs) TO microblaze (XIo_In)
signal rdata : std_logic_vector (31 downto 0);

-- Data FROM microblaze TO multipler (input regs) (XIo_Out)
signal wdata : std_logic_vector (31 downto 0);

-- Signals used to connect the multiplier
signal input1, input2 : std_logic_vector(7 downto 0);
signal output : std_logic_vector(15 downto 0);

begin

-- Instantiate multiplier. Operands and
-- result are in  Two's  complement
M1 : mult_s8_s8
  port map (
    a => input1,
    b => input2,
    o => output);
  
-- Chip select for this entity - '1' when address is in the range 0x0180000 - 0x0180FFFF
cs <= OPB_select when OPB_ABus(31 downto 14) = "000000011000000000" else '0';
          
-- Latching read/write address
process (OPB_Clk)
begin
  if OPB_Clk'event and OPB_Clk = '1' then
    if OPB_RST = '1' then
      addr <= X"00000000";
    else
      addr <= OPB_ABus;
    end if;
  end if;
end process;


-- Latching RNW signal
process (OPB_Clk)
begin
  if OPB_Clk'event and OPB_Clk = '1' then
    if OPB_Rst = '1' then
      rnw <= '0';
    else
      rnw <= OPB_RNW;
    end if;
  end if;
end process;


-- Latching the data microblaze writes
process (OPB_Clk)
begin
  if OPB_Clk'event and OPB_Clk = '1' then
    if OPB_Rst = '1' then
      wdata <= X"0000_0000";
    else
      wdata <= OPB_DBus; 
    end if;
  end if;   
end process;


-- One-hot-like state machine implementing OPB bus xfer protocol
process (OPB_Clk)
begin
  if OPB_Clk'event and OPB_Clk='1' then
    q2 <= (not q2 and q1) or (q2 and not q1);
    q1 <= (cs and not q2 and not q1) or (q2 and not q1);
    q0 <= q2 and not q1;
  end if;
end process;

-- RE is data  read  enable
-- When '1', microblaze has
-- requested a read (XIo_In)
re <= q2 and not q1 and rnw;

-- WE is data write  enable
-- When '1', microblaze has
-- send some data (XIo_Out)
we <= q2 and not q1 and not rnw;


-- Latch the data coming from the multiplier. That's
-- the data read by microblaze when executing XIo_In
process (OPB_Clk, OPB_Rst)
begin
  if OPB_Rst='1' then
    rdata <= X"00000000";
  elsif OPB_Clk'event and OPB_Clk='1' then
    if re='1' then
      if addr(13)='1' and addr(12)='1' then     -- address 0x01803FFC
        rdata <= reg1 & reg2 & reg3 & reg4;     -- registers written
      elsif addr(13)='1' and addr(12)='0' then  -- address 0x01802FFC
        rdata <= X"0000" & regout;              -- result of reg1*reg2 
      else 
        rdata <= X"00000000";
      end if;
    else      
      rdata <= X"00000000"; -- this is needed because OPB_DBus is ORed
    end if;
  end if;
end process;

-- Connecting the multiplier to some of the registers
input1 <= reg1;
input2 <= reg2;
regout <= output;

-- Data coming from microblaze  when  writing
-- to the multiplier (thru XIo_Out). OPB_DBus
-- is constantly latched in "wdata".
process (OPB_Clk, OPB_Rst)
begin
  if OPB_Rst='1' then
    reg1 <= X"00";
    reg2 <= X"00";
    reg3 <= X"00";
    reg4 <= X"00";
  elsif OPB_Clk'event and OPB_Clk='1' then
    if we='1' then
      if    addr(13)='1' and addr(12)='1' then                   -- address 0x01803FFC
        reg1 <= wdata(7 downto 0);      
      elsif addr(13)='1' and addr(12)='0' then                   -- address 0x01802FFC
        reg2 <= wdata(7 downto 0);
      elsif addr(13)='0' and addr(12)='1' then                   -- address 0x01801FFC
        reg3 <= wdata(7 downto 0);
      elsif addr(13)='0' and addr(12)='0' and addr(11)='1' then  -- address 0x01800FFC
        reg4 <= wdata(7 downto 0);
      end if;
    end if;
  end if;
end process;

-- XFER is transfer acknowledge
-- of the OPB bus protocol
xfer <= q0;

-- Slave data bus
MULT_DBus(31 downto 0) <= rdata;

-- Tie unused signals to zero
MULT_errAck <= '0';
MULT_retry <= '0';
MULT_toutSup <= '0';

MULT_xferAck <= xfer;

end Behavioral;
