-- EMACS settings: -*- tab-width: 2; indent-tabs-mode: t -*-
-- vim: tabstop=2:shiftwidth=2:noexpandtab
-- kate: tab-width 2; replace-tabs off; indent-width 2;
-- =============================================================================
-- Authors: Martin Zabel
-- Patrick Lehmann
--
-- Entity: True dual-port memory with write-first behavior.
--
-- Description:
--
-- Inferring / instantiating true dual-port memory, with:
--
-- * single clock, clock enable,
-- * 2 read/write ports.
--
-- Command truth table:
--
-- == === === =====================================================
-- ce we1 we2 Command
-- == === === =====================================================
-- 0 X X No operation
-- 1 0 0 Read only from memory
-- 1 0 1 Read from memory on port 1, write to memory on port 2
-- 1 1 0 Write to memory on port 1, read from memory on port 2
-- 1 1 1 Write to memory on both ports
-- == === === =====================================================
--
-- Both reads and writes are synchronous to the clock.
--
-- The generalized behavior across Altera and Xilinx FPGAs since
-- Stratix/Cyclone and Spartan-3/Virtex-5, respectively, is as follows:
--
-- Same-Port Read-During-Write
-- When writing data through port 1, the read output of the same port
-- (``q1``) will output the new data (``d1``, in the following clock cycle)
-- which is aka. "write-first behavior".
--
-- Same applies to port 2.
--
-- Mixed-Port Read-During-Write
-- When reading at the write address, the read value will be the new data,
-- aka. "write-first behavior". Of course, the read is still synchronous,
-- i.e, the latency is still one clock cyle.
--
-- If a write is issued on both ports to the same address, then the output of
-- this unit and the content of the addressed memory cell are undefined.
--
-- For simulation, always our dedicated simulation model :ref:`IP:ocram_tdp_sim`
-- is used.
--
-- License:
-- =============================================================================
-- Copyright 2008-2016 Technische Universitaet Dresden - Germany
-- Chair of VLSI-Design, Diagnostics and Architecture
--
-- 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.
-- =============================================================================
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
library PoC;
use PoC.config.all;
use PoC.utils.all;
use PoC.strings.all;
use PoC.vectors.all;
use PoC.mem.all;
entity [docs]ocram_tdp_wf is
generic (
A_BITS : positive; -- number of address bits
D_BITS : positive; -- number of data bits
FILENAME : string := "" -- file-name for RAM initialization
);
port (
clk : in std_logic; -- clock
ce : in std_logic; -- clock-enable
we1 : in std_logic; -- write-enable for 1st port
we2 : in std_logic; -- write-enable for 2nd port
a1 : in unsigned(A_BITS-1 downto 0); -- address for 1st port
a2 : in unsigned(A_BITS-1 downto 0); -- address for 2nd port
d1 : in std_logic_vector(D_BITS-1 downto 0); -- write-data for 1st port
d2 : in std_logic_vector(D_BITS-1 downto 0); -- write-data for 2nd port
q1 : out std_logic_vector(D_BITS-1 downto 0); -- read-data from 1st port
q2 : out std_logic_vector(D_BITS-1 downto 0) -- read-data from 2nd port
);
end entity;
architecture [docs]rtl of ocram_tdp_wf is
-- Two read/write ports are only supported in true-dual port block memories
-- on FPGAs. But not all synthesis tools, do infer the required bypass logic
-- as already shown for :ref:`IP:ocram_sdp_wf`.
-- Thus, bypass logic has to be explicitly described to get the intended
-- write-first behavior.
signal wd1_r : std_logic_vector(d1'range); -- write data from port 1
signal wd2_r : std_logic_vector(d2'range); -- write data from port 2
signal fwd1_r : std_logic; -- forward write data from port 1 to port 2
signal fwd2_r : std_logic; -- forward write data from port 2 to port 1
signal ram_q1 : std_logic_vector(q1'range); -- RAM output, port 1
signal ram_q2 : std_logic_vector(q2'range); -- RAM output, port 2
-- Compares two addresses, returns 'X' if either ``a1`` or ``a2`` contains
-- meta-values, otherwise returns '1' if ``a1 == a2`` is true else
-- '0'. Returns 'X' even when the addresses contain '-' values, to signal an
-- undefined outcome.
function [docs]addr_equal(a1 : unsigned; a2 : unsigned) return X01 is
begin
-- synthesis translate_off
if is_x(a1) or is_x(a2) then return 'X'; end if;
-- synthesis translate_on
if to_x01(std_logic_vector(a1)) = to_x01(std_logic_vector(a2)) then
return '1';
end if;
return '0';
end function;
begin
process(clk)
variable addr_eq : X01;
begin
if rising_edge(clk) then
case to_x01(ce) is
when '1' =>
wd1_r <= to_x01(d1);
wd2_r <= to_x01(d2);
addr_eq := addr_equal(a1, a2);
fwd1_r <= addr_eq and we1;
fwd2_r <= addr_eq and we2;
when '0' => null; -- keep previous state
when others => -- X propagation in simulation
wd1_r <= (others => 'X');
fwd1_r <= 'X';
fwd2_r <= 'X';
end case;
if SIMULATION then
if (fwd1_r and fwd2_r) = '1' then
report "ERROR: both ports write to the same address." severity error;
end if;
end if;
end if;
end process;
ram_tdp: entity work.ocram_tdp
generic map (
A_BITS => A_BITS,
D_BITS => D_BITS,
FILENAME => FILENAME)
port map (
clk1 => clk,
clk2 => clk,
ce1 => ce,
ce2 => ce,
we1 => we1,
we2 => we2,
a1 => a1,
a2 => a2,
d1 => d1,
d2 => d2,
q1 => ram_q1,
q2 => ram_q2);
with fwd1_r select q2 <=
wd1_r when '1',
ram_q2 when '0',
(others => 'X') when others; -- X propagation in simulation
with fwd2_r select q1 <=
wd2_r when '1',
ram_q1 when '0',
(others => 'X') when others; -- X propagation in simulation
end architecture;