-
Notifications
You must be signed in to change notification settings - Fork 1
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
Solve the constant crisis by adding memory map description to circuits #581
Draft
hydrolarus
wants to merge
5
commits into
staging
Choose a base branch
from
memory-map
base: staging
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
17abeb4
add clash-protocols-memmap
hydrolarus eae6799
add memory mapped version of first components
hydrolarus fb3077c
Clean up
hydrolarus 065862f
dynamically create memory.x based on memory map
hydrolarus c3bc778
add circuit versions of scatter- and gatherUnit
hydrolarus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[ | ||
{ | ||
"top": "vexRiscvTest", | ||
"stage": "test", | ||
"targets": "Specific [-1]" | ||
} | ||
] |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
-- | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE NumericUnderscores #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# OPTIONS_GHC -fplugin=Protocols.Plugin #-} | ||
|
@@ -28,19 +29,156 @@ import Bittide.ProcessingElement | |
import Bittide.SharedTypes | ||
import Bittide.Wishbone | ||
|
||
import qualified Data.Map.Strict as Map | ||
|
||
import BitPackC (BitPackC) | ||
import GHC.Stack (HasCallStack, callStack, getCallStack) | ||
import Protocols.MemoryMap ( | ||
Access (..), | ||
BackwardAnnotated, | ||
DeviceDefinition (..), | ||
MemoryMap (..), | ||
MemoryMapTree (DeviceInstance), | ||
MemoryMapped, | ||
Name (..), | ||
Register (..), | ||
withPrefix, | ||
) | ||
import Protocols.MemoryMap.FieldType (ToFieldType (toFieldType)) | ||
|
||
-- import Protocols.MemoryMap (BackwardAnnotated) | ||
|
||
data TestStatus = Running | Success | Fail | ||
deriving (Enum, Eq, Generic, NFDataX, BitPack) | ||
deriving (Enum, Eq, Generic, NFDataX, BitPack, ToFieldType, BitPackC) | ||
|
||
type TestDone = Bool | ||
type TestSuccess = Bool | ||
type UartRx = Bit | ||
type UartTx = Bit | ||
|
||
-- ╭────────┬───────┬───────┬────────────────────────────────────╮ | ||
-- │ bin │ hex │ bus │ description │ | ||
-- ├────────┼───────┼───────┼────────────────────────────────────┤ | ||
-- │ 0b000. │ 0x0 │ │ │ | ||
-- │ 0b001. │ 0x2 │ │ │ | ||
-- │ 0b010. │ 0x4 │ 1 │ Data memory │ | ||
-- │ 0b011. │ 0x6 │ │ │ | ||
-- │ 0b100. │ 0x8 │ 0 │ Instruction memory │ | ||
-- │ 0b101. │ 0xA │ 2 │ Time │ | ||
-- │ 0b110. │ 0xC │ 3 │ UART │ | ||
-- │ 0b111. │ 0xE │ 4 │ Test status register │ | ||
-- ╰────────┴───────┴───────┴────────────────────────────────────╯ | ||
|
||
-- | The internal CPU circuit used for this test. | ||
cpuCircuit :: | ||
forall dom. | ||
( HiddenClockResetEnable dom | ||
, 1 <= DomainPeriod dom | ||
, ValidBaud dom 921600 | ||
, HasCallStack | ||
) => | ||
Circuit | ||
(CSignal dom Bit, MemoryMapped (Jtag dom)) | ||
(CSignal dom TestStatus, CSignal dom Bit) | ||
cpuCircuit = circuit $ \(uartRx, jtag) -> do | ||
[timeBus, uartBus, statusRegisterBus] <- | ||
processingElementM | ||
0b100 | ||
(Undefined @(Div (64 * 1024) 4)) | ||
0b010 | ||
(Undefined @(Div (64 * 1024) 4)) | ||
-< jtag | ||
withPrefix 0b101 (timeM "Timer") -< timeBus | ||
(uartTx, _uartStatus) <- | ||
withPrefixFst | ||
0b110 | ||
(uartM @dom "UART" d16 d16 (SNat @921600)) | ||
-< (uartBus, uartRx) | ||
testResult <- withPrefix 0b111 statusRegM -< statusRegisterBus | ||
idC -< (testResult, uartTx) | ||
where | ||
withPrefixFst :: | ||
BitVector n -> | ||
Circuit (BackwardAnnotated ann a, b) c -> | ||
Circuit (BackwardAnnotated (BitVector n, ann) a, b) c | ||
withPrefixFst prefix (Circuit f) = Circuit $ \(fwd, bwd) -> | ||
let (((ann, bwdA), bwdB), fwd') = f (fwd, bwd) | ||
in ((((prefix, ann), bwdA), bwdB), fwd') | ||
|
||
-- | Memory-mapped wrapper for 'statusRegister' | ||
statusRegM :: | ||
forall dom addr. | ||
(HiddenClockResetEnable dom, HasCallStack) => | ||
Circuit | ||
(MemoryMapped (Wishbone dom 'Standard addr (Bytes 4))) | ||
(CSignal dom TestStatus) | ||
statusRegM = Circuit go | ||
where | ||
go (fwd, _) = | ||
((SimOnly memMap, bwd), status') | ||
where | ||
Circuit f = statusRegister | ||
(bwd, status') = f (fwd, pure ()) | ||
|
||
((_, defLoc) : (_, instanceLoc) : _) = getCallStack callStack | ||
deviceDef = | ||
DeviceDefinition | ||
{ deviceName = Name{name = "StatusReg", description = ""} | ||
, registers = [(statusRegName, defLoc, statusRegDesc)] | ||
, defLocation = defLoc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to hide this away in a smart constructor |
||
} | ||
statusRegName = Name{name = "status", description = ""} | ||
statusRegDesc = | ||
Register | ||
{ access = WriteOnly | ||
, address = 0 | ||
, reset = Nothing | ||
, fieldType = toFieldType @TestStatus | ||
, fieldSize = 1 | ||
} | ||
deviceInstance = DeviceInstance instanceLoc Nothing "StatusReg" "StatusReg" | ||
memMap = MemoryMap{deviceDefs = Map.singleton "StatusReg" deviceDef, tree = deviceInstance} | ||
|
||
-- | A memory mapped register to communicate the test result to the system. | ||
statusRegister :: | ||
forall dom addr. | ||
(HiddenClockResetEnable dom) => | ||
Circuit | ||
(Wishbone dom 'Standard addr (Bytes 4)) | ||
(CSignal dom TestStatus) | ||
statusRegister = Circuit $ \(fwd, _) -> | ||
let (unbundle -> (m2s, st)) = mealy go Running fwd | ||
in (m2s, st) | ||
where | ||
go st WishboneM2S{..} | ||
-- out of cycle, no response, same state | ||
| not (busCycle && strobe) = (st, (emptyWishboneS2M, st)) | ||
-- already done, ACK and same state | ||
| st /= Running = (st, (emptyWishboneS2M{acknowledge = True}, st)) | ||
-- read, this is write-only, so error, same state | ||
| not writeEnable = | ||
( st | ||
, | ||
( (emptyWishboneS2M @(Bytes 4)) | ||
{ err = True | ||
, readData = errorX "status register is write-only" | ||
} | ||
, st | ||
) | ||
) | ||
-- write! change state, ACK | ||
| otherwise = | ||
let state = case writeData of | ||
1 -> Success | ||
_ -> Fail | ||
in (state, (emptyWishboneS2M{acknowledge = True}, state)) | ||
|
||
vexRiscvInner :: | ||
forall dom. | ||
( HiddenClockResetEnable dom | ||
, 1 <= DomainPeriod dom | ||
, ValidBaud dom 921600 | ||
, HasCallStack | ||
) => | ||
Signal dom JtagIn -> | ||
Signal dom UartRx -> | ||
|
@@ -61,60 +199,22 @@ vexRiscvInner jtagIn0 uartRx = | |
((_, jtagOut), (status, uartTx)) = | ||
circuitFn ((uartRx, jtagIn0), (pure (), pure ())) | ||
|
||
Circuit circuitFn = circuit $ \(uartRx, jtag) -> do | ||
[timeBus, uartBus, statusRegisterBus] <- processingElement peConfig -< jtag | ||
(uartTx, _uartStatus) <- uartWb @dom d16 d16 (SNat @921600) -< (uartBus, uartRx) | ||
timeWb -< timeBus | ||
testResult <- statusRegister -< statusRegisterBus | ||
idC -< (testResult, uartTx) | ||
|
||
statusRegister :: Circuit (Wishbone dom 'Standard 29 (Bytes 4)) (CSignal dom TestStatus) | ||
statusRegister = Circuit $ \(fwd, _) -> | ||
let (unbundle -> (m2s, st)) = mealy go Running fwd | ||
in (m2s, st) | ||
where | ||
go st WishboneM2S{..} | ||
-- out of cycle, no response, same state | ||
| not (busCycle && strobe) = (st, (emptyWishboneS2M, st)) | ||
-- already done, ACK and same state | ||
| st /= Running = (st, (emptyWishboneS2M{acknowledge = True}, st)) | ||
-- read, this is write-only, so error, same state | ||
| not writeEnable = | ||
( st | ||
, | ||
( (emptyWishboneS2M @(Bytes 4)) | ||
{ err = True | ||
, readData = errorX "status register is write-only" | ||
} | ||
, st | ||
) | ||
) | ||
-- write! change state, ACK | ||
| otherwise = | ||
let state = case writeData of | ||
1 -> Success | ||
_ -> Fail | ||
in (state, (emptyWishboneS2M{acknowledge = True}, state)) | ||
|
||
-- ╭────────┬───────┬───────┬────────────────────────────────────╮ | ||
-- │ bin │ hex │ bus │ description │ | ||
-- ├────────┼───────┼───────┼────────────────────────────────────┤ | ||
-- │ 0b000. │ 0x0 │ │ │ | ||
-- │ 0b001. │ 0x2 │ │ │ | ||
-- │ 0b010. │ 0x4 │ 1 │ Data memory │ | ||
-- │ 0b011. │ 0x6 │ │ │ | ||
-- │ 0b100. │ 0x8 │ 0 │ Instruction memory │ | ||
-- │ 0b101. │ 0xA │ 2 │ Time │ | ||
-- │ 0b110. │ 0xC │ 3 │ UART │ | ||
-- │ 0b111. │ 0xE │ 4 │ Test status register │ | ||
-- ╰────────┴───────┴───────┴────────────────────────────────────╯ | ||
-- | ||
-- peConfig :: PeConfig 5 | ||
peConfig = | ||
PeConfig | ||
(0b100 :> 0b010 :> 0b101 :> 0b110 :> 0b111 :> Nil) | ||
(Undefined @(Div (64 * 1024) 4)) -- 64 KiB | ||
(Undefined @(Div (64 * 1024) 4)) -- 64 KiB | ||
circuitFn :: | ||
( Fwd (CSignal dom Bit, Jtag dom) | ||
, Bwd (CSignal dom TestStatus, CSignal dom Bit) | ||
) -> | ||
( Bwd (CSignal dom Bit, Jtag dom) | ||
, Fwd (CSignal dom TestStatus, CSignal dom Bit) | ||
) | ||
circuitFn = toSignals circ' | ||
|
||
circ' :: Circuit (CSignal dom Bit, Jtag dom) (CSignal dom TestStatus, CSignal dom Bit) | ||
circ' = removeAnnSnd cpuCircuit | ||
|
||
removeAnnSnd :: Circuit (a, BackwardAnnotated ann b) c -> Circuit (a, b) c | ||
removeAnnSnd (Circuit f) = Circuit $ \(fwd, bwd) -> | ||
let ((bwdA, (_, bwdB)), fwd') = f (fwd, bwd) | ||
in ((bwdA, bwdB), fwd') | ||
|
||
vexRiscvTest :: | ||
"CLK_125MHZ" ::: DiffClock Ext125 -> | ||
|
115 changes: 115 additions & 0 deletions
115
bittide-instances/src/Bittide/Instances/MemoryMapLogic.hs
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
-- SPDX-FileCopyrightText: 2024 Google LLC | ||
-- | ||
-- SPDX-License-Identifier: Apache-2.0 | ||
{-# LANGUAGE NumericUnderscores #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
|
||
{- | Logic for extracting memory maps from circuits in this project. | ||
This is a separate module from MemoryMaps because of GHC's stage restrictions | ||
around TemplateHaskell. | ||
Comment on lines
+7
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be generic code hosted in |
||
-} | ||
module Bittide.Instances.MemoryMapLogic where | ||
|
||
import Clash.Prelude | ||
|
||
import Control.Monad (forM_) | ||
|
||
import Bittide.Instances.Hitl.VexRiscv (cpuCircuit) | ||
import Language.Haskell.TH (Q, reportError, runIO) | ||
import Project.FilePath (buildDir, findParentContaining) | ||
import Protocols.MemoryMap (MemoryMap, annotationSnd) | ||
import Protocols.MemoryMap.Check ( | ||
CheckConfiguration (..), | ||
MemoryMapValidationErrors (..), | ||
check, | ||
prettyPrintPath, | ||
shortLocation, | ||
) | ||
import Protocols.MemoryMap.Check.AbsAddress (AbsAddressValidateError (..)) | ||
import Protocols.MemoryMap.Check.Overlap (OverlapError (..)) | ||
import System.Directory (createDirectoryIfMissing) | ||
import System.FilePath | ||
|
||
import Data.Aeson (encode) | ||
import qualified Data.ByteString.Lazy as BS | ||
import qualified Data.List as L | ||
import Protocols.MemoryMap.Json (memoryMapJson) | ||
import Text.Printf (printf) | ||
|
||
data MemMapProcessing = MemMapProcessing | ||
{ name :: String | ||
, memMap :: MemoryMap | ||
, checkConfig :: CheckConfiguration | ||
, jsonOutput :: Maybe FilePath | ||
} | ||
|
||
defaultCheckConfig :: CheckConfiguration | ||
defaultCheckConfig = | ||
CheckConfiguration | ||
{ startAddr = 0x0000_0000 | ||
, endAddr = 0xFFFF_FFFF | ||
} | ||
|
||
unSimOnly :: SimOnly a -> a | ||
unSimOnly (SimOnly x) = x | ||
|
||
memMaps :: [MemMapProcessing] | ||
memMaps = | ||
[ MemMapProcessing | ||
{ name = "VexRiscv" | ||
, memMap = unSimOnly $ annotationSnd @System cpuCircuit | ||
, checkConfig = defaultCheckConfig | ||
, jsonOutput = Just "vexriscv.json" | ||
} | ||
] | ||
|
||
processMemoryMaps :: Q () | ||
processMemoryMaps = do | ||
memMapDir <- runIO $ do | ||
root <- findParentContaining "cabal.project" | ||
let dir = root </> buildDir </> "memory_maps" | ||
createDirectoryIfMissing True dir | ||
pure dir | ||
|
||
forM_ memMaps $ \(MemMapProcessing{..}) -> do | ||
case check checkConfig memMap of | ||
Left MemoryMapValidationErrors{..} -> do | ||
let absErrorMsgs = flip L.map absAddrErrors $ \AbsAddressValidateError{..} -> | ||
let path' = prettyPrintPath path | ||
component = case componentName of | ||
Just name' -> name' | ||
Nothing -> "interconnect " <> path' | ||
in printf | ||
"Expected component %s at %08X but found %08X (%s)" | ||
component | ||
expected | ||
got | ||
(shortLocation location) | ||
|
||
let overlapErrorMsgs = flip L.map overlapErrors $ \case | ||
OverlapError{..} -> | ||
printf | ||
"Component %s (%08X + %08X) overlaps with %s at address (%08X) (%s)" | ||
(prettyPrintPath path) | ||
startAddr | ||
componentSize | ||
(prettyPrintPath overlapsWith) | ||
overlapsAt | ||
(shortLocation location) | ||
SizeExceedsError{..} -> | ||
printf | ||
"Component %s (%08X + %08X) exceeds available size %08X (%s)" | ||
(prettyPrintPath path) | ||
startAddr | ||
requestedSize | ||
availableSize | ||
(shortLocation location) | ||
reportError (unlines $ absErrorMsgs <> overlapErrorMsgs) | ||
Right checkedMemMap -> do | ||
case jsonOutput of | ||
Nothing -> pure () | ||
Just path -> do | ||
let json = memoryMapJson checkedMemMap | ||
runIO $ BS.writeFile (memMapDir </> path) (encode json) | ||
pure () | ||
pure () |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 think we can delete this now :)