-
Notifications
You must be signed in to change notification settings - Fork 154
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
VHDL: don't overflow integer range in RAM/ROM #1875
Conversation
cee00a7
to
4bb9dee
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.
@DigitalBrains1 Feel free to create a PR removing all instances of zeroAt
while replacing it with ignoreFor
. Seems a lot neater!
I don't understand this patch: wouldn't failing loudly be preferable over silently failing and introducing a Clash simulation vs RTL simulation difference? Wouldn't you only see this issue if you're surpassing your simulator's capabilities anyway?
@martijnbastiaan isn't the point of the patch clear from the testsuite addition? In Haskell, we wouldn't see the exception (X value) from indexing in the rom with a negative address, becausee we won't observe the output of the rom when the address is negative. However, when we naively translate to concurrent VHDL statements, the VHDL simulator would choke on those exceptions, in this case two even:
|
Are you sure about this? Maybe I'm misunderstanding what you are saying, but I don't see where that happens...
Restarting clashi and trying with
|
Thinking about it, it could very well be that the issue shows up if the testbench is changed a bit such that the output isn't observed when the address is negative, leading to a normally running testbench in Haskell, but failing in VHDL simulation because of the concurrent nature. So perhaps the issue is real and the fix is fine as well, but the testbench needs tweaking. |
Try running the test added in this PR on head of master, and you’ll see. |
The test fails in The test fails in both The latter is the problem here (well, alternatively, there is no problem, because the test fails in Clash, so GHDL can do that as well). |
Ah, right, the output needs to be ignored for 2 cycles, not just on the first. So it needs |
All addresses presented by the --- a/tests/shouldwork/Signal/RomNegative.hs
+++ b/tests/shouldwork/Signal/RomNegative.hs
@@ -20,14 +20,14 @@ topEntity
-> Signal System (Unsigned 8)
topEntity = exposeClockResetEnable go where
go rd = zeroAt0 (unpack <$> dout) where
- dout = romFile d256 "memory.list" rd
+ dout = mux ((< 0) <$> rd) 0 (romFile d256 "memory.list" rd)
{-# NOINLINE topEntity #-}
testBench :: Signal System Bool
testBench = done
where
testInput = Explicit.register clk rst enableGen minBound (testInput + 1)
- expectedOutput = outputVerifier' clk rst $(listToVecTH [0::Unsigned 8,0,1,2,3,4,5,6,7,8])
+ expectedOutput = outputVerifier' clk rst (replicate d10 0)
done = expectedOutput (topEntity clk rst enableGen testInput)
clk = tbSystemClockGen (not <$> done)
rst = systemResetGen gives >>> P.takeWhile not $ sample testBench
[False,False*** Exception: Ix{Int}.index: Index (-9223372036854775808) out of range ((0,255)) the following change makes it work but defeats the purpose: --- a/tests/shouldwork/Signal/RomNegative.hs
+++ b/tests/shouldwork/Signal/RomNegative.hs
@@ -20,7 +20,7 @@ topEntity
-> Signal System (Unsigned 8)
topEntity = exposeClockResetEnable go where
go rd = zeroAt0 (unpack <$> dout) where
- dout = mux ((< 0) <$> rd) 0 (romFile d256 "memory.list" rd)
+ dout = mux ((< 0) <$> rd) 0 (romFile d256 "memory.list" ((.&. 255) <$> rd))
{-# NOINLINE topEntity #-}
testBench :: Signal System Bool I have yet to discover where the strictness is coming from. |
And just to make it clear, no, using --- a/tests/shouldwork/Signal/RomNegative.hs
+++ b/tests/shouldwork/Signal/RomNegative.hs
@@ -18,9 +18,7 @@ topEntity
-> Enable System
-> Signal System (Signed 64)
-> Signal System (Unsigned 8)
-topEntity = exposeClockResetEnable go where
- go rd = zeroAt0 (unpack <$> dout) where
- dout = romFile d256 "memory.list" rd
+topEntity = exposeClockResetEnable (fmap unpack . romFile d256 "memory.list")
{-# NOINLINE topEntity #-}
testBench :: Signal System Bool
@@ -28,6 +26,7 @@ testBench = done
where
testInput = Explicit.register clk rst enableGen minBound (testInput + 1)
expectedOutput = outputVerifier' clk rst $(listToVecTH [0::Unsigned 8,0,1,2,3,4,5,6,7,8])
- done = expectedOutput (topEntity clk rst enableGen testInput)
+ done = expectedOutput . ignoreFor clk rst enableGen d2 0
+ $ topEntity clk rst enableGen testInput
clk = tbSystemClockGen (not <$> done)
rst = systemResetGen gives >>> P.takeWhile not $ sample testBench
[False,False*** Exception: Ix{Int}.index: Index (-9223372036854775808) out of range ((0,255)) and for >>> P.takeWhile not $ sample testBench
[False,False*** Exception: Ix{Int}.index: Index (-9223372036854775808) out of range ((0,255)) The |
@martijnbastiaan thinks he knows why Clash is too strict here, I'll try a fix based on his suggestion. Although really, the fix should work regardless of the cause. |
4bb9dee
to
4e139c4
Compare
4e139c4
to
9842496
Compare
There were two remaining issues which I just fixed:
[1] We use Clash to compile them, the simulation is then done in HDL, not in Clash. |
Instead of `ErrorCall` which cannot be rethrown by `seqX` or `deepSeqX`, both of which are used all through clash-prelude.
Most RAM and ROM functions take an `Int` as either a read or write index. On 64-bit systems `maxBound :: Int` and `minBound :: Int` exceed VHDL's `integer'high` and `integer'low` (on (nearly) all VHDL simulators). This poses an issue, because the blackbox implementations use `to_integer` to convert the `signed (63 downto 0)` (then translation for `Int`) to VHDL's `integer`. So any value falling outside of the range would trigger an overflow execption in the VHDL simulator. So we now masks all indexes to fit the `integer'low to integer'high` range.
9842496
to
c9da81b
Compare
Ah, and I added a missing changelog and updated copyright messages :-) |
Most RAM and ROM functions take an
Int
as either a read or write index. On 64-bit systemsmaxBound :: Int
andminBound :: Int
exceed VHDL'sinteger'high
andinteger'low
(on (nearly) all VHDL simulators). This poses an issue, because the blackboximplementations use
to_integer
to convert thesigned (63 downto 0)
(then translation forInt
) to VHDL'sinteger
. So any value falling outside of the range would trigger an overflow exception in the VHDL simulator.So we now masks all indexes to fit the
integer'low to integer'high
range.