-
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
base: staging
Are you sure you want to change the base?
Conversation
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.
Thanks for all the hard work :) Here's a first short round of review. Could you first add some documentation to the functions you export and to help me better interpret the intentions of the code?
clash-protocols-memmap/.gitignore
Outdated
@@ -0,0 +1,6 @@ | |||
# SPDX-FileCopyrightText: 2022 Google LLC |
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.
We switched to year of creation copyrights :)
@@ -0,0 +1,228 @@ | |||
-- SPDX-FileCopyrightText: 2022-2024 Google LLC |
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.
We switched to year of creation copyrights :)
cabal.project
Outdated
-- flags: -rusty-callisto | ||
|
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.
Was this added by accident?
For development purposes you can create a cabal.project.local
-- be possible to generate an equivalent data structure in Rust or C. | ||
-- | ||
|
||
data BackwardAnnotated (annotation :: Type) (a :: Type) |
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.
BackAnnotated
?
BwdAnnotated
?
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.
It's a bit of a mouthful, yeah. We could also go with BwdAnn
as the shortest option 😅 Probably somewhere in between is good?
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.
"Back annotation" has a specific meaning in the hardware world: https://www.quora.com/What-is-meant-by-back-annotated-netlist-in-asic-design-flow. Just a heads up. I think BwdAnn
is best (though I'm generally a bit meh on abbreviations like this).
, fieldSize :: Size | ||
-- ^ Size of the register in bytes |
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.
Could we not derive this from the FieldType
?
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.
In theory, yes, but it would involve a bunch of duplicated work. This field is meant to be filled by the BitPackC
, I initially added a sizeOf :: FieldType -> Size
function but it was pretty complex as it has to encode the same rules as BitPackC
does. If we're fine with this duplication then I could add a function like that.
@@ -1,4 +1,4 @@ | |||
# SPDX-FileCopyrightText: 2022 Google LLC | |||
# SPDX-FileCopyrightText: 2024 Google LLC |
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.
no need for this anymore :)
counter: addr.add(2).cast::<u64>(), | ||
frequency: addr.add(4).cast::<u64>(), |
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.
Is this related to alignment requirements?
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.
Yes exactly, probably should have gone in a separate commit, it's been a bit messy 🥴
"end_line": 435, | ||
"file": "src/Protocols/MemoryMap.hs", | ||
"module": "Protocols.MemoryMap", | ||
"package": "clash-protocols-memmap-0.1-inplace", | ||
"start_col": 15, | ||
"start_line": 435 | ||
} | ||
} | ||
} | ||
}, | ||
{ | ||
"relative_address": 2684354560, | ||
"size": 536870911, | ||
"tree": { | ||
"device_instance": { | ||
"absolute_address": 2684354560, | ||
"device_name": "Timer", | ||
"instance_name": "Timer", | ||
"src_location": { | ||
"end_col": 24, | ||
"end_line": 435, | ||
"file": "src/Protocols/MemoryMap.hs", | ||
"module": "Protocols.MemoryMap", | ||
"package": "clash-protocols-memmap-0.1-inplace", | ||
"start_col": 15, | ||
"start_line": 435 | ||
} | ||
} | ||
} | ||
}, | ||
{ | ||
"relative_address": 3221225472, | ||
"size": 536870911, | ||
"tree": { | ||
"device_instance": { | ||
"absolute_address": 3221225472, | ||
"device_name": "UART", | ||
"instance_name": "UART", | ||
"src_location": { | ||
"end_col": 60, | ||
"end_line": 45, | ||
"file": "src/Bittide/Instances/MemoryMapLogic.hs", | ||
"module": "Bittide.Instances.MemoryMapLogic", | ||
"package": "bittide-instances-0.1-inplace", | ||
"start_col": 50, | ||
"start_line": 45 | ||
} | ||
} | ||
} | ||
}, | ||
{ | ||
"relative_address": 3758096384, | ||
"size": 536870911, | ||
"tree": { | ||
"device_instance": { | ||
"absolute_address": 3758096384, | ||
"device_name": "StatusReg", | ||
"instance_name": "StatusReg", | ||
"src_location": { | ||
"end_col": 24, | ||
"end_line": 435, | ||
"file": "src/Protocols/MemoryMap.hs", | ||
"module": "Protocols.MemoryMap", | ||
"package": "clash-protocols-memmap-0.1-inplace", | ||
"start_col": 15, | ||
"start_line": 435 | ||
} | ||
} | ||
} | ||
} | ||
], | ||
"src_location": { | ||
"end_col": 48, | ||
"end_line": 233, | ||
"file": "src/Bittide/ProcessingElement.hs", | ||
"module": "Bittide.ProcessingElement", | ||
"package": "bittide-0.1-inplace", | ||
"start_col": 23, | ||
"start_line": 233 | ||
} | ||
} | ||
} | ||
} | ||
|
||
"#; | ||
|
||
#[test] | ||
fn register_access() { | ||
let src = "\"read_only\""; | ||
let x: RegisterAccess = serde_json::from_str(src).unwrap(); | ||
assert_eq!(x, RegisterAccess::ReadOnly); | ||
} | ||
|
||
#[test] | ||
fn test_deserialise_memmap() { | ||
let memmap: MemoryMapDesc = serde_json::from_str(TEST_SRC).unwrap(); | ||
|
||
dbg!(&memmap); | ||
} | ||
} |
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.
Could we use an actual generated description from a clash design instead of hardcoding this?
UART: mut uart, | ||
StatusReg: status, |
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.
Am I correct in understanding that DeviceInstances
is used to auto generated components at compile time (e.g. UART
based on the json description, and we automatically get generate the API (e.g. data
based on the field and set_data
to write?)
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.
Yes exactly, DeviceInstances
contains all the different accessible components specified in the memory map! Every device type gets a Rust wrapper and every register/field of a device gets the appropriate getter and setter.
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 this looks good in general! Let's replace all error "shouldn't happen"
with breadcrumbs. The more we introduce the more likely the unexpected is expected to happen.
-- ╭────────┬───────┬───────┬────────────────────────────────────╮ | ||
-- │ 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 │ | ||
-- ╰────────┴───────┴───────┴────────────────────────────────────╯ |
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 :)
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to hide this away in a smart constructor deviceDefinition
?
{- | Typeclass that can be implemented (or derived) to enable C-FFI safe | ||
representation of Haskell types. | ||
|
||
This can be derived automatically as long as 'Generic' is implemented for the | ||
type. | ||
Currently there is a high likelihood that more complex types need an unusual | ||
amount of constraints on the instance. Ideally this won't be necessary when | ||
@ghc-typelits-extra@ gets extended to automatically discharge those | ||
constraints. |
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.
Could you add a few words on why this is more difficult than just BitPack
? I.e., you need alignment --- what does that mean.
( BitPackC a | ||
, BitPackC b | ||
, BitPackC c | ||
, 1 <= Max (AlignmentC a) (AlignmentC b) | ||
, 1 <= Max (AlignmentC b) (AlignmentC c) | ||
, 1 <= Max (AlignmentC a) (Max (AlignmentC b) (AlignmentC c)) | ||
, Mod (ByteSizeC a) (AlignmentC b) <= AlignmentC b | ||
, Mod (ByteSizeC a) (AlignmentC b) <= ByteSizeC a | ||
, Mod (ByteSizeC b) (AlignmentC c) <= AlignmentC c | ||
, Mod (ByteSizeC b) (AlignmentC c) <= ByteSizeC b | ||
, Mod (ByteSizeC a) (Max (AlignmentC b) (AlignmentC c)) | ||
<= Max (AlignmentC b) (AlignmentC c) | ||
, Mod (ByteSizeC a) (Max (AlignmentC b) (AlignmentC c)) | ||
<= ByteSizeC 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.
L U V I T
type Size = Integer | ||
|
||
{- | A protocol agnostic wrapper for memory mapped protocols. It provides a way | ||
for memory mapped subordinates to propagate their memory map to the top level, |
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.
for memory mapped subordinates to propagate their memory map to the top level, | |
for memory mapped devices to propagate their memory map to the top level, |
You call them devices elsewhere right?
validateAbsAddresses :: | ||
ComponentPath -> |
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.
validateAbsAddresses :: | |
ComponentPath -> | |
validateAbsAddresses :: | |
HasCallStack => | |
ComponentPath -> |
Given that you're using error
here.
} | ||
| interconnectSize > availableSize | ||
] | ||
componentErrors = flip map comps2 $ \(path', (addr', size', comp)) -> walk path' (start + addr', start + addr' + size') comp |
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.
componentErrors = flip map comps2 $ \(path', (addr', size', comp)) -> walk path' (start + addr', start + addr' + size') comp | |
componentErrors = flip map comps2 $ \(path', (addr', size', comp)) -> | |
walk path' (start + addr', start + addr' + size') comp |
toFieldType :: FieldType | ||
|
||
args :: Vec (Generics a) FieldType | ||
args = repeat undefined |
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.
args = repeat undefined | |
args = repeat (error "something something") |
(Generic (WithVars a), GToFieldType (Rep (WithVars a))) => FieldType | ||
toFieldType = case (gToFieldType @(Rep (WithVars a)), toList (args @a)) of | ||
(ft, []) -> TypeReference ft [] | ||
(TypeReference _ft _args', _args'') -> error "shouldn't happen!??" |
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.
(TypeReference _ft _args', _args'') -> error "shouldn't happen!??" | |
(TypeReference _ft _args', _args'') -> error "toFieldType: TypeReference: unexpected constructor" |
// TODO handle things like CamelCase/snake_case conversion? | ||
let s = match n.as_ref() { | ||
"(,)" => "Pair", | ||
"(,,)" => "Thruple", |
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.
"(,,)" => "Thruple", | |
"(,,)" => "Thruple", |
Haha, haven't seen that one before! Triple
?
This implements #481
There's a few noteworthy parts to this PR:
Protocols.MemoryMap
/.Check
)BitPackC
type classToFieldType
type classfirmware-support/memmap-generate