Last active
March 25, 2021 13:59
-
-
Save DigitalBrains1/43ecbc72090c17604135e181966688da to your computer and use it in GitHub Desktop.
Blackbox spec for dual-port block RAM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{- | |
- 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') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ { "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