Skip to content

Instantly share code, notes, and snippets.

@DigitalBrains1
Last active March 25, 2021 13:59
Show Gist options
  • Save DigitalBrains1/43ecbc72090c17604135e181966688da to your computer and use it in GitHub Desktop.
Save DigitalBrains1/43ecbc72090c17604135e181966688da to your computer and use it in GitHub Desktop.
Blackbox spec for dual-port block RAM
{-
- Copyright (c) 2015, 2017 Peter Lebbing <[email protected]>
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- 1. Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
-
-
- The implementation for blockram2p'' was written after looking at the source
- for ClaSH.Prelude.BitIndex in clash-prelude 0.11.
- That file is Copyright 2013-2016 University of Twente (and is under
- 2-clause BSD as well).
-}
module Toolbox.Blockram2p where
import CLaSH.Prelude
import qualified Prelude as P
import Data.Maybe
import Toolbox.Misc (showCodeLoc)
{-
- Instantiate a black box dual port blockram with the following parameters:
-
- `aaw` - Address bus width of port A
- `baw` - Address bus width of port B
- `aw` - Data width of port A
- `bw` - Data width of port B
-
- Both ports need to address the same amount of memory. So if you would, for
- instance, have 4096 words of 4 bits on port A, and port B uses 16-bit data,
- port B would need to address 1024 words. The type checker will error if this
- constraint is not satisfied.
-
- The inputs are:
- `aAddr` - Address bus port A
- `aDIn` - Data in port A
- `aWrEn` - Write enable for port A (read is always enabled)
- ... and then similar for port B
-
- The outputs are:
- `qA` - Data out port A
- `qB` - Data out port B
-
- The blockram has registers preceding and registers following the actual
- blockram. During a write, the old data from that address is on the output
- port; this goes for both same-port and mixed-port access.
-
- Because of the registers, if you offer an address in clock cycle 0, you see
- the data in clock cycle 2. Both reads and writes can be fully pipelined.
- Write conflicts are unhandled and have undefined results.
-
- The current implementation instantiates an Altera M9K block (such as is
- present in the Altera DE0-Nano board). For more details, look at the VHDL
- that the blackbox instantiates and consult the Altera documentation. If you
- swap out the blackbox for a different implementation, watch out for the
- precise settings with regard to read-during-write and such details.
-}
blockram2p :: ( KnownNat aaw, KnownNat baw, KnownNat aw, KnownNat bw
, KnownNat memw, KnownNat logaw, KnownNat logbw
, ((2 ^ logaw) ~ aw)
, ((2 ^ logbw) ~ bw)
, (memw ~ ((2 ^ aaw) * aw))
, (memw ~ ((2 ^ baw) * bw)))
=> SNat aaw
-> SNat baw
-> SNat aw
-> SNat bw
-> ( Signal (Unsigned aaw), Signal (Unsigned aw), Signal Bool
, Signal (Unsigned baw) , Signal (Unsigned bw), Signal Bool)
-> (Signal (Unsigned aw), Signal (Unsigned bw))
blockram2p aaw baw aw bw (aAddr, aDIn, aWrEn, bAddr, bDIn, bWrEn)
= blockram2p' aAddr aDIn aWrEn bAddr bDIn bWrEn
{-# NOINLINE blockram2p' #-}
blockram2p' :: ( BitPack a
, BitPack b
, KnownNat (BitSize a)
, KnownNat (BitSize b)
, KnownNat logaw
, KnownNat logbw
, KnownNat aaw
, KnownNat baw
, KnownNat memw
, ((2 ^ logaw) ~ BitSize a)
, ((2 ^ logbw) ~ BitSize b)
, KnownNat (2 ^ aaw)
, KnownNat (2 ^ baw)
, (memw ~ ((2 ^ aaw) * BitSize a))
, (memw ~ ((2 ^ baw) * BitSize b)))
=> Signal (Unsigned aaw)
-> Signal a
-> Signal Bool
-> Signal (Unsigned baw)
-> Signal b
-> Signal Bool
-> (Signal a, Signal b)
blockram2p' aAddr aDIn aWrEn bAddr bDIn bWrEn = (qA, qB)
where
(qA, qB) = unbundle $ register un o
o = mealy (withSNat $ withSNat blockram2p'') 0
$ register (0,un,False,0,un,False) i
i = bundle (aAddr, aDIn, aWrEn, bAddr, bDIn, bWrEn)
un = errorX "blockram2p': intial value undefined"
blockram2p'' :: ( BitPack a
, BitPack b
, KnownNat (BitSize a)
, KnownNat (BitSize b)
, KnownNat aw
, KnownNat bw
, KnownNat aaw
, KnownNat baw
, KnownNat memw
, aw ~ BitSize a
, bw ~ BitSize b
, KnownNat (2 ^ aaw)
, KnownNat (2 ^ baw)
, (memw ~ ((2 ^ aaw) * BitSize a))
, (memw ~ ((2 ^ baw) * BitSize b)))
=> SNat aw
-> SNat bw
-> BitVector memw
-> ( Unsigned aaw, a, Bool, Unsigned baw, b
, Bool)
-> (BitVector memw, (a, b))
blockram2p'' aw bw ram (aAddr, aDIn, aWrEn, bAddr, bDIn, bWrEn)
= (ram', (qA, qB))
where
qA = unpack $ readRam ram (fromIntegral aAddr)
qB = unpack $ readRam ram (fromIntegral bAddr)
aWritten | aWrEn = writeRam ram (fromIntegral aAddr) (pack aDIn)
| otherwise = ram
ram' | not bWrEn = aWritten
| aWrEn && overlap = error ($(showCodeLoc) P.++
" blockram2p'': Write conflict")
| otherwise = writeRam aWritten (fromIntegral bAddr)
(pack bDIn)
astart = (fromIntegral aAddr) * aw'
aend = astart + aw'
bstart = (fromIntegral bAddr) * bw'
bend = bstart + bw'
overlap = (bstart >= astart && bstart < aend)
|| (astart >= bstart && astart < bend)
aw' = snatToNum aw :: Int
bw' = snatToNum bw
readRam = withSNat readRam'
readRam' :: (KnownNat m, KnownNat n)
=> SNat n -> BitVector m -> Int -> BitVector n
readRam' n bv i = resize $ shiftR bv (i * n')
where
n' = snatToNum n
writeRam = withSNat writeRam'
writeRam' :: (KnownNat m, KnownNat n)
=> SNat n -> BitVector m -> Int -> BitVector n
-> BitVector m
writeRam' n bv i x = (bv .&. mask) .|. x'
where
mask = complement (shiftL (2 ^ n' - 1) (i * n'))
n' = snatToNum n
x' = shiftL (resize x) (i * n')
LIBRARY ieee;
USE ieee.std_logic_1164.all;
LIBRARY altera_mf;
USE altera_mf.all;
ENTITY blockramwrapper IS
GENERIC (
address_reg_b : STRING;
clock_enable_input_a : STRING;
clock_enable_input_b : STRING;
clock_enable_output_a : STRING;
clock_enable_output_b : STRING;
indata_reg_b : STRING;
intended_device_family : STRING;
lpm_type : STRING;
numwords_a : NATURAL;
numwords_b : NATURAL;
operation_mode : STRING;
outdata_aclr_a : STRING;
outdata_aclr_b : STRING;
outdata_reg_a : STRING;
outdata_reg_b : STRING;
power_up_uninitialized : STRING;
ram_block_type : STRING;
read_during_write_mode_mixed_ports : STRING;
read_during_write_mode_port_a : STRING;
read_during_write_mode_port_b : STRING;
widthad_a : NATURAL;
widthad_b : NATURAL;
width_a : NATURAL;
width_b : NATURAL;
width_byteena_a : NATURAL;
width_byteena_b : NATURAL;
wrcontrol_wraddress_reg_b : STRING
);
PORT (
clock0 : IN STD_LOGIC ;
wren_a : IN STD_LOGIC ;
address_b : IN STD_LOGIC_VECTOR;
data_b : IN STD_LOGIC_VECTOR;
q_a : OUT STD_LOGIC_VECTOR;
wren_b : IN STD_LOGIC;
address_a : IN STD_LOGIC_VECTOR;
data_a : IN STD_LOGIC_VECTOR;
q_b : OUT STD_LOGIC_VECTOR
);
END blockramwrapper;
ARCHITECTURE SYN OF blockramwrapper IS
COMPONENT altsyncram
GENERIC (
address_reg_b : STRING;
clock_enable_input_a : STRING;
clock_enable_input_b : STRING;
clock_enable_output_a : STRING;
clock_enable_output_b : STRING;
indata_reg_b : STRING;
intended_device_family : STRING;
lpm_type : STRING;
numwords_a : NATURAL;
numwords_b : NATURAL;
operation_mode : STRING;
outdata_aclr_a : STRING;
outdata_aclr_b : STRING;
outdata_reg_a : STRING;
outdata_reg_b : STRING;
power_up_uninitialized : STRING;
ram_block_type : STRING;
read_during_write_mode_mixed_ports : STRING;
read_during_write_mode_port_a : STRING;
read_during_write_mode_port_b : STRING;
widthad_a : NATURAL;
widthad_b : NATURAL;
width_a : NATURAL;
width_b : NATURAL;
width_byteena_a : NATURAL;
width_byteena_b : NATURAL;
wrcontrol_wraddress_reg_b : STRING
);
PORT (
clock0 : IN STD_LOGIC ;
wren_a : IN STD_LOGIC ;
address_b : IN STD_LOGIC_VECTOR;
data_b : IN STD_LOGIC_VECTOR;
q_a : OUT STD_LOGIC_VECTOR;
wren_b : IN STD_LOGIC;
address_a : IN STD_LOGIC_VECTOR;
data_a : IN STD_LOGIC_VECTOR;
q_b : OUT STD_LOGIC_VECTOR
);
END COMPONENT;
BEGIN
altsyncram_component : altsyncram
GENERIC MAP (
address_reg_b => address_reg_b,
clock_enable_input_a => clock_enable_input_a,
clock_enable_input_b => clock_enable_input_b,
clock_enable_output_a => clock_enable_output_a,
clock_enable_output_b => clock_enable_output_b,
indata_reg_b => indata_reg_b,
intended_device_family => intended_device_family,
lpm_type => lpm_type,
numwords_a => numwords_a,
numwords_b => numwords_b,
operation_mode => operation_mode,
outdata_aclr_a => outdata_aclr_a,
outdata_aclr_b => outdata_aclr_b,
outdata_reg_a => outdata_reg_a,
outdata_reg_b => outdata_reg_b,
power_up_uninitialized => power_up_uninitialized,
ram_block_type => ram_block_type,
read_during_write_mode_mixed_ports => read_during_write_mode_mixed_ports,
read_during_write_mode_port_a => read_during_write_mode_port_a,
read_during_write_mode_port_b => read_during_write_mode_port_b,
widthad_a => widthad_a,
widthad_b => widthad_b,
width_a => width_a,
width_b => width_b,
width_byteena_a => width_byteena_a,
width_byteena_b => width_byteena_b,
wrcontrol_wraddress_reg_b => wrcontrol_wraddress_reg_b
)
PORT MAP (
clock0 => clock0,
wren_a => wren_a,
address_b => address_b,
data_b => data_b,
wren_b => wren_b,
address_a => address_a,
data_a => data_a,
q_a => q_a,
q_b => q_b
);
END SYN;
[ { "BlackBox" :
{ "name" : "Toolbox.Blockram2p.blockram2p'"
, "type" :
"blockram2p' :: ( BitPack a -- LIT[0]
, BitPack b -- LIT[1]
, KnownNat (BitSize a) -- LIT[2]
, KnownNat (BitSize b) -- LIT[3]
, KnownNat logaw -- LIT[4]
, KnownNat logbw -- LIT[5]
, KnownNat aaw -- LIT[6]
, KnownNat baw -- LIT[7]
, KnownNat memw -- LIT[8]
, ((2 ^ logaw) ~ BitSize a) -- LIT[9]
, ((2 ^ logbw) ~ BitSize b) -- LIT[10]
, KnownNat (2 ^ aaw) -- LIT[11]
, KnownNat (2 ^ baw) -- LIT[12]
, (memw ~ ((2 ^ aaw) * BitSize a)) -- LIT[13]
, (memw ~ ((2 ^ baw) * BitSize b))) -- LIT[14]
=> Signal (Unsigned aaw) -- aAddr, ARG[15]
-> Signal a -- aDIn, ARG[16]
-> Signal Bool -- aWrEn, ARG[17]
-> Signal (Unsigned baw) -- bAddr, ARG[18]
-> Signal b -- bDIn, ARG[19]
-> Signal Bool -- bWrEn, ARG[20]
-> Signal (a, b)"
, "templateD" :
"~GENSYM[~COMPNAME_blockram2p][0]: block
signal ~GENSYM[qA][1] : std_logic_vector(~SIZE[~TYP[16]]-1 downto 0);
signal ~GENSYM[qB][2] : std_logic_vector(~SIZE[~TYP[19]]-1 downto 0);
signal ~GENSYM[wren_a][3] : std_logic_vector(0 downto 0);
signal ~GENSYM[wren_b][4] : std_logic_vector(0 downto 0);
begin
~GENSYM[blockram2p_inst][5] : entity blockramwrapper
generic map (
address_reg_b => \"CLOCK0\",
clock_enable_input_a => \"BYPASS\",
clock_enable_input_b => \"BYPASS\",
clock_enable_output_a => \"BYPASS\",
clock_enable_output_b => \"BYPASS\",
indata_reg_b => \"CLOCK0\",
intended_device_family => \"Cyclone IV E\",
lpm_type => \"altsyncram\",
numwords_a => ~LIT[11],
numwords_b => ~LIT[12],
operation_mode => \"BIDIR_DUAL_PORT\",
outdata_aclr_a => \"NONE\",
outdata_aclr_b => \"NONE\",
outdata_reg_a => \"CLOCK0\",
outdata_reg_b => \"CLOCK0\",
power_up_uninitialized => \"FALSE\",
ram_block_type => \"M9K\",
read_during_write_mode_mixed_ports => \"OLD_DATA\",
read_during_write_mode_port_a => \"OLD_DATA\",
read_during_write_mode_port_b => \"OLD_DATA\",
widthad_a => ~LIT[6],
widthad_b => ~LIT[7],
width_a => ~LIT[2],
width_b => ~LIT[3],
width_byteena_a => 1,
width_byteena_b => 1,
wrcontrol_wraddress_reg_b => \"CLOCK0\"
)
port map (
clock0 => ~CLKO,
wren_a => ~SYM[3](0),
address_b => ~TOBV[~ARG[18]][~TYP[18]],
data_b => ~TOBV[~ARG[19]][~TYP[19]],
wren_b => ~SYM[4](0),
address_a => ~TOBV[~ARG[15]][~TYP[15]],
data_a => ~TOBV[~ARG[16]][~TYP[16]],
q_a => ~SYM[1],
q_b => ~SYM[2]
);
~SYM[3] <= ~TOBV[~ARG[17]][~TYP[17]];
~SYM[4] <= ~TOBV[~ARG[20]][~TYP[20]];
~RESULT <= (~FROMBV[~SYM[1]][~TYP[16]]
,~FROMBV[~SYM[2]][~TYP[19]]);
end block;"
}
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment