33
44SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
55
6- Sphinx extension for partition layout bar charts.
7-
6+ Sphinx extension for Matter reference partition layout bar charts.
87"""
98
109from __future__ import annotations
1716from docutils import nodes
1817from docutils .parsers .rst import directives
1918from docutils .statemachine import StringList
20- from memory_data import load_memory_yaml , plain_tab_label , sort_boards_by_internal_memory
19+ from memory_data import load_board_data
2120from sphinx .application import Sphinx
2221from sphinx .util .docutils import SphinxDirective
2322
3837 "#65a30d" ,
3938]
4039
41-
4240_LAYOUT_FIXED_COLORS = {
4341 "padding" : "#e2e8f0" ,
4442 "boot_partition" : "#2563eb" ,
4543 "slot0_partition" : "#16a34a" ,
4644 "slot1_partition" : "#0369a1" ,
4745 "factory_data_partition" : "#ea580c" ,
4846 "storage_partition" : "#ca8a04" ,
47+ "tfm_storage_partition" : "#0d9488" ,
4948}
5049
5150TOOLTIP_ALIGN_START_THRESHOLD_PCT = 20.0
@@ -70,7 +69,6 @@ def _hex_label(size_bytes: int) -> str:
7069
7170
7271def _display_widths (sizes_bytes : list [int ]) -> list [float ]:
73- """Map partition sizes to bar widths proportional to byte size."""
7472 if not sizes_bytes :
7573 return []
7674 total = sum (max (size , 0 ) for size in sizes_bytes ) or 1
@@ -201,63 +199,155 @@ def _render_region_html(region: dict[str, Any]) -> str:
201199 )
202200
203201
204- def _render_board_layout_html (board : dict [str , Any ]) -> str :
205- regions = "" .join (_render_region_html (region ) for region in board .get ("regions" , []))
202+ def _format_offset (offset_bytes : int ) -> str :
203+ return f"{ offset_bytes } (0x{ offset_bytes :x} )"
204+
205+
206+ def _format_size (size_bytes : int ) -> str :
207+ return f"{ _bytes_label (size_bytes )} ({ _hex_label (size_bytes )} )"
208+
209+
210+ def _iter_partition_rows (
211+ partitions : list [dict [str , Any ]],
212+ * ,
213+ depth : int = 0 ,
214+ ) -> list [tuple [str , int , int ]]:
215+ rows : list [tuple [str , int , int ]] = []
216+ indent = " " * depth
217+ for part in partitions :
218+ rows .append ((f"{ indent } { part ['label' ]} " , int (part ["offset_bytes" ]), int (part ["size_bytes" ])))
219+ children = part .get ("children" ) or []
220+ if children :
221+ rows .extend (_iter_partition_rows (children , depth = depth + 1 ))
222+ return rows
223+
224+
225+ def _append_rst_paragraph (state , parent : nodes .Element , rst_text : str ) -> None :
226+ paragraph = nodes .paragraph ()
227+ content = StringList ([rst_text ], "<memory_layout_table>" )
228+ state .nested_parse (content , 0 , paragraph )
229+ if len (paragraph ) == 1 and isinstance (paragraph [0 ], nodes .paragraph ):
230+ inner = paragraph [0 ]
231+ paragraph .remove (inner )
232+ paragraph .extend (inner .children )
233+ parent += paragraph
234+
235+
236+ def _build_region_table (region : dict [str , Any ]) -> nodes .table :
237+ table = nodes .table ()
238+ table ["classes" ] = ["memory-layout-table" ]
239+
240+ tgroup = nodes .tgroup (cols = 3 )
241+ table += tgroup
242+ tgroup .extend ([nodes .colspec (colwidth = 24 ), nodes .colspec (colwidth = 12 ), nodes .colspec (colwidth = 12 )])
243+
244+ thead = nodes .thead ()
245+ tgroup += thead
246+ header_row = nodes .row ()
247+ thead += header_row
248+ for title in ("Partition" , "Offset" , "Size" ):
249+ entry = nodes .entry ()
250+ entry += nodes .Text (title )
251+ header_row += entry
252+
253+ tbody = nodes .tbody ()
254+ tgroup += tbody
255+ for label , offset_bytes , size_bytes in _iter_partition_rows (region .get ("partitions" ) or []):
256+ row = nodes .row ()
257+ tbody += row
258+
259+ label_entry = nodes .entry ()
260+ label_entry += nodes .Text (label )
261+ row += label_entry
262+
263+ offset_entry = nodes .entry ()
264+ offset_entry += nodes .Text (_format_offset (offset_bytes ))
265+ row += offset_entry
266+
267+ size_entry = nodes .entry ()
268+ size_entry += nodes .Text (_format_size (size_bytes ))
269+ row += size_entry
270+
271+ return table
272+
273+
274+ def build_layout_table_nodes (directive : SphinxDirective , data : dict [str , Any ]) -> nodes .Element :
275+ wrapper = nodes .container ()
276+ wrapper ["classes" ] = ["memory-layout-table-board" ]
277+
278+ regions = data .get ("reference_regions" ) or []
279+ for index , region in enumerate (regions ):
280+ if index :
281+ wrapper += nodes .raw ("" , '<div class="memory-layout-table-region-spacer"></div>' , format = "html" )
282+
283+ total_bytes = int (region ["total_bytes" ])
284+ title = nodes .paragraph ()
285+ title ["classes" ] = ["memory-layout-table-region-title" ]
286+ emphasis = nodes .strong ()
287+ emphasis += nodes .Text (
288+ f'{ region ["title" ]} (size: { _hex_label (total_bytes )} = { _bytes_label (total_bytes )} )'
289+ )
290+ title += emphasis
291+ wrapper += title
292+
293+ if region .get ("address_note_rst" ):
294+ _append_rst_paragraph (directive .state , wrapper , str (region ["address_note_rst" ]))
295+
296+ wrapper += _build_region_table (region )
297+
298+ return wrapper
299+
300+
301+ def _render_board_layout_html (data : dict [str , Any ]) -> str :
302+ regions = "" .join (_render_region_html (region ) for region in data .get ("reference_regions" , []))
303+ board_name = data .get ("board" , {}).get ("name" , "" )
206304 return (
207305 f'<div class="memory-layout-board" '
208- f'data-board="{ html .escape (board [ "board_id" ] , quote = True )} ">'
306+ f'data-board="{ html .escape (board_name , quote = True )} ">'
209307 f"{ regions } </div>"
210308 )
211309
212310
213- def _build_layout_tabs_rst (
214- directive : SphinxDirective ,
215- boards : list [dict [str , Any ]],
216- render_fn ,
217- empty_message : str ,
218- ) -> list [nodes .Node ]:
219- if not boards :
220- note = nodes .paragraph ()
221- note += nodes .emphasis (text = empty_message )
222- return [note ]
223-
224- lines = [".. tabs::" , "" ]
225- for board in boards :
226- lines .append (f" .. group-tab:: { plain_tab_label (board ['tab_title' ])} " )
227- lines .append ("" )
228- for intro_line in board .get ("tab_intro_rst" , "" ).splitlines ():
229- lines .append (f" { intro_line } " )
230- lines .append ("" )
231- lines .append (" .. raw:: html" )
232- lines .append ("" )
233- for html_line in render_fn (board ).splitlines ():
234- lines .append (f" { html_line } " )
235- lines .append ("" )
236-
237- container = nodes .container ()
238- directive .state .nested_parse (StringList (lines , "<memory_layout>" ), 0 , container )
239- return container .children
240-
241-
242- class MemoryLayouts (SphinxDirective ):
243- """Render reference memory layout charts from a YAML data file."""
311+ class MemoryLayoutBoard (SphinxDirective ):
312+ """Render reference memory layout charts from docs/data/memory/<board>.yaml."""
244313
245314 required_arguments = 0
246315 optional_arguments = 0
247316 final_argument_whitespace = True
248317 has_content = False
249318 option_spec = {
250- "file " : directives .unchanged_required ,
319+ "board " : directives .unchanged_required ,
251320 }
252321
253322 def run (self ) -> list [nodes .Node ]:
254- data = load_memory_yaml (self , self .options ["file" ])
255- return _build_layout_tabs_rst (
256- self ,
257- sort_boards_by_internal_memory (data .get ("boards" , [])),
258- _render_board_layout_html ,
259- "No memory layout data found." ,
260- )
323+ data = load_board_data (self .options ["board" ])
324+ if not data .get ("reference_regions" ):
325+ note = nodes .paragraph ()
326+ note += nodes .emphasis (text = "No memory layout data found." )
327+ return [note ]
328+ container = nodes .container ()
329+ container += nodes .raw ("" , _render_board_layout_html (data ), format = "html" )
330+ return [container ]
331+
332+
333+ class MemoryLayoutTable (SphinxDirective ):
334+ """Render reference memory layout tables from docs/data/memory/<board>.yaml."""
335+
336+ required_arguments = 0
337+ optional_arguments = 0
338+ final_argument_whitespace = True
339+ has_content = False
340+ option_spec = {
341+ "board" : directives .unchanged_required ,
342+ }
343+
344+ def run (self ) -> list [nodes .Node ]:
345+ data = load_board_data (self .options ["board" ])
346+ if not data .get ("reference_regions" ):
347+ note = nodes .paragraph ()
348+ note += nodes .emphasis (text = "No memory layout data found." )
349+ return [note ]
350+ return [build_layout_table_nodes (self , data )]
261351
262352
263353def add_memory_layout_viz_resources (app : Sphinx ) -> None :
@@ -268,7 +358,8 @@ def add_memory_layout_viz_resources(app: Sphinx) -> None:
268358
269359
270360def setup (app : Sphinx ) -> dict [str , Any ]:
271- app .add_directive ("memory-layouts" , MemoryLayouts )
361+ app .add_directive ("memory-layout-board" , MemoryLayoutBoard )
362+ app .add_directive ("memory-layout-table" , MemoryLayoutTable )
272363 app .connect ("builder-inited" , add_memory_layout_viz_resources )
273364 return {
274365 "version" : __version__ ,
0 commit comments