-
Notifications
You must be signed in to change notification settings - Fork 150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add true, dual-port blockram #1726
Conversation
98b78ba
to
efe1208
Compare
@martijnbastiaan @leonschoorl the distinction between slow and fast seems off, at least, veryUnsafeSynchronizer
:: Int
-- ^ Period of clock belonging to @dom1@
-> Int
-- ^ Period of clock belonging to @dom2@
-> Signal dom1 a
-> Signal dom2 a
veryUnsafeSynchronizer t1 t2
-- this case is just an optimization for when the periods are the same
| t1 == t2 = same
| otherwise = go 0
where
same :: Signal dom1 a -> Signal dom2 a
same (s :- ss) = s :- same ss
go :: Int -> Signal dom1 a -> Signal dom2 a
go relativeTime (a :- s)
| relativeTime <= 0 = a :- go (relativeTime + t2) (a :- s)
| otherwise = go (relativeTime - t1) s so when I guess what I'm trying to say is, given that the dual port ram model takes after
|
The purpose of the slow/fast clocks is simply because we know the slow clock will consume exactly one cycle from one of its streams. I.e., the next iteration of
Without switching the clocks at the very start you'd need to check explicitly whether the "next" tick is going to be spent in the "other" part of go (or precalculate how many cycles you spend, but that's ugly IMO as you need to do it again and again). |
Alright, that makes sense. |
efe1208
to
b12210e
Compare
afe8308
to
0ae350f
Compare
0ae350f
to
de8e52e
Compare
de8e52e
to
18d9e70
Compare
|
||
-> Clock domB | ||
-- ^ Clock for port B | ||
-> Signal domB (Maybe a) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather have a new type for this or an Either
. I assume if you pass in a Just
here then it means you want to write and if you pass Nothing
then a value is only read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rowanG077 What do you think of this new API?
2c0411e
It's isomorphic with Either
but a bit more descriptive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@leonschoorl Thx! This is exactly what I meant. There can be no misunderstanding now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There can be no misunderstanding now
Famous last words
18d9e70
to
2c0411e
Compare
For a next PR/Issue:
|
dae7e0c
to
38a9b76
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo some minor comments
I want an implicitly clocked version as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see an implicitly same-clocked counterpart of Clash.Explicit.BlockRam.trueDualPortBlockRam
in Clash.Prelude.BlockRam
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM modulo the minor comments.
|
||
{-# LANGUAGE Trustworthy #-} | ||
|
||
{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-} | ||
{-# OPTIONS_HADDOCK show-extensions #-} | ||
|
||
--Prevent generation of eta port names for trueDualPortBlockRam |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--Prevent generation of eta port names for trueDualPortBlockRam | |
-- Prevent generation of eta port names for trueDualPortBlockRam |
trueDualPortBlockRam :: | ||
forall nAddrs dom a . | ||
( HasCallStack | ||
, KnownNat nAddrs | ||
, HiddenClock dom | ||
, NFDataX a | ||
) | ||
=> Signal dom (E.RamOp nAddrs a) | ||
-- ^ ram operation for port A | ||
-> Signal dom (E.RamOp nAddrs a) | ||
-- ^ ram operation for port B | ||
-> (Signal dom a, Signal dom a) | ||
-- ^ Outputs data on /next/ cycle. When writing, the data written | ||
-- will be echoed. When reading, the read data is returned. | ||
trueDualPortBlockRam inA inB = E.trueDualPortBlockRam hasClock hasClock inA inB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trueDualPortBlockRam :: | |
forall nAddrs dom a . | |
( HasCallStack | |
, KnownNat nAddrs | |
, HiddenClock dom | |
, NFDataX a | |
) | |
=> Signal dom (E.RamOp nAddrs a) | |
-- ^ ram operation for port A | |
-> Signal dom (E.RamOp nAddrs a) | |
-- ^ ram operation for port B | |
-> (Signal dom a, Signal dom a) | |
-- ^ Outputs data on /next/ cycle. When writing, the data written | |
-- will be echoed. When reading, the read data is returned. | |
trueDualPortBlockRam inA inB = E.trueDualPortBlockRam hasClock hasClock inA inB | |
trueDualPortBlockRam :: | |
#ifdef CLASH_MULTIPLE_HIDDEN | |
forall nAddrs dom1 dom2 a . | |
( HasCallStack | |
, KnownNat nAddrs | |
, HiddenClock dom | |
, NFDataX a | |
) | |
=> Signal dom1 (E.RamOp nAddrs a) | |
-- ^ ram operation for port A | |
-> Signal dom2 (E.RamOp nAddrs a) | |
-- ^ ram operation for port B | |
-> (Signal dom1 a, Signal dom2 a) | |
-- ^ Outputs data on /next/ cycle. When writing, the data written | |
-- will be echoed. When reading, the read data is returned. | |
trueDualPortBlockRam inA inB = E.trueDualPortBlockRam (hasClock @dom1) (hasClock @dom2) inA inB | |
#else | |
forall nAddrs dom a . | |
( HasCallStack | |
, KnownNat nAddrs | |
, HiddenClock dom | |
, NFDataX a | |
) | |
=> Signal dom (E.RamOp nAddrs a) | |
-- ^ ram operation for port A | |
-> Signal dom (E.RamOp nAddrs a) | |
-- ^ ram operation for port B | |
-> (Signal dom a, Signal dom a) | |
-- ^ Outputs data on /next/ cycle. When writing, the data written | |
-- will be echoed. When reading, the read data is returned. | |
trueDualPortBlockRam inA inB = E.trueDualPortBlockRam hasClock hasClock inA inB | |
#endif |
@@ -0,0 +1 @@ | |||
ADDED: Added basic support for true dual port block ram in WriteFirst mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ADDED: Added basic support for true dual port block ram in WriteFirst mode. | |
ADDED: Added support for true dual ported block ram: | |
* Implicitly clocked: `Clash.Prelude.BlockRam.trueDualPortBlockRam` and | |
* Explicitly clocked: `Clash.Explicit.BlockRam.trueDualPortBlockRam` | |
Any values that's being written on a particular port is also the value that will be read on that port, i.e. the same-port read/write behavior is: WriteFirst. For mixed port read/write, when both ports have the same address, when there is a write on the port A, the output of port B is undefined, and visa versa. |
-- | Produces vendor-agnostic HDL that will be inferred as a true, dual port | ||
-- block ram. It has write-first (or "new data") behavior, no clock enables, | ||
-- no byte enables, and is symmetric in its data ports. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-- | Produces vendor-agnostic HDL that will be inferred as a true, dual port | |
-- block ram. It has write-first (or "new data") behavior, no clock enables, | |
-- no byte enables, and is symmetric in its data ports. | |
-- | Produces vendor-agnostic HDL that will be inferred as a true, dual port | |
-- block ram. Any values that's being written on a particular port is also the | |
-- value that will be read on that port, i.e. the same-port read/write behavior | |
-- is: WriteFirst. For mixed port read/write, when both ports have the same | |
-- address, when there is a write on the port A, the output of port B is | |
-- undefined, and visa versa. |
@lmbollen You've set off the end of line whitespace check in these places:
|
77748ac
to
1cb4ac6
Compare
1cb4ac6
to
e8c94df
Compare
Add SystemVerilog / Verilog / VHDL primitives dualport bram: Move fast/slow domain ordering into the the simulation model This way the domain swapping never ends up in the HDL. Because the HDL primitives don't care about which domain is faster or slower. Add NOINLINEd wrapper around trueDualPortBlockRam# Vivado doesn't like to see bitslices in the path to the data input. Allow non-power-of-two sized rams Added read / write tests, conflict tests and non multiple clock frequency tests.
e8c94df
to
3e86156
Compare
TODO:
(same as Verilog?)B
+C