diff --git a/.gitattributes b/.gitattributes index 93ffa03a6..2e718e31c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,6 @@ # SPDX-License-Identifier: Apache-2.0 *.lock lingust-generated=true -doc/img/memmap.svg linguist-generated=true doc/img/mocha.svg linguist-generated=true hw/top_chip/ip/xbar_peri/*/autogen/* linguist-generated=true hw/top_chip/ip_autogen/* linguist-generated=true diff --git a/doc/img/memmap.svg b/doc/img/memmap.svg deleted file mode 100644 index 5cfbb781d..000000000 --- a/doc/img/memmap.svg +++ /dev/null @@ -1,2 +0,0 @@ - -Base addressTop addressReservedFunction0x000800000x00087fff32.0 kiBROM0x100000000x1001ffff128.0 kiBSRAM0x200000000x2000ffff64.0 kiBDEBUG_MODULE0x200100000x2001ffff64.0 kiBMAILBOX0x200200000x2002ffff64.0 kiBDV_SW_IFC0x300000000x30007fff32.0 kiBETHERNET0x400000000x4000ffff64.0 kiBGPIO0x400200000x4002ffff64.0 kiBCLKMGR0x400300000x4003ffff64.0 kiBRSTMGR0x400400000x4004ffff64.0 kiBPOWER_MANAGER0x400500000x4005ffff64.0 kiBROM_CTRL0x400600000x4006ffff64.0 kiBENTROPY_SRC0x410000000x4100ffff64.0 kiBUART0x420000000x4200ffff64.0 kiBI2C0x430000000x4300ffff64.0 kiBSPI_DEVICE0x440000000x4400ffff64.0 kiBTIMER0x450000000x4500ffff64.0 kiBSPI_HOST0x480000000x4c00400364.0 MiBPLIC0x800000000xbf7fffff1016.0 MiBDRAM \ No newline at end of file diff --git a/doc/ref/arch.md b/doc/ref/arch.md index 0dc6e8a36..2a592d852 100644 --- a/doc/ref/arch.md +++ b/doc/ref/arch.md @@ -1,76 +1,99 @@ # Architecture -The Mocha architecture contains two crossbars. -One crossbar is 64-bit width and is meant for the main memory. -The other crossbar is uncached and meant to contain the peripherals. -Because most of these peripherals are imported from OpenTitan, in the first instance this bus is implemented as a TileLink Ultra-Lightweight bus with 32-bit width. +The Mocha architecture contains two crossbars. +One crossbar is 64-bit width and is meant for the main memory. +The other crossbar is uncached and meant to contain the peripherals. +Because most of these peripherals are imported from OpenTitan, in the first instance this bus is implemented as a TileLink Ultra-Lightweight bus with 32-bit width. -![Mocha block diagram](../img/mocha.svg) +![Mocha block diagram](../img/mocha.svg) ## Clock domains -There are three clock domains in Mocha. +There are three clock domains in Mocha. -1. Main: The main clock domain is the high-speed clock domain that runs the CVA6 core as well as the AXI crossbar it connects to, the AXI tag controller, debug module and the SRAM. -2. IO: The IO clock drives most of the peripherals and runs at a lower speed than the main clock. - It drives the TileLink bus and most of the peripherals that are connected to it like the UART and the SPI device. -3. AON: The always on clock is also a low-speed clock with the difference being that it is always on. - Both the main and IO clocks can be disabled and are turned off when a system reset is requested. - The always on clock drives the clock, reset and power managers and allows the system to come out of reset. +1. Main: The main clock domain is the high-speed clock domain that runs the CVA6 core as well as the AXI crossbar it connects to, the AXI tag controller, debug module and the SRAM. +2. IO: The IO clock drives most of the peripherals and runs at a lower speed than the main clock. + It drives the TileLink bus and most of the peripherals that are connected to it like the UART and the SPI device. +3. AON: The always on clock is also a low-speed clock with the difference being that it is always on. + Both the main and IO clocks can be disabled and are turned off when a system reset is requested. + The always on clock drives the clock, reset and power managers and allows the system to come out of reset. ## Memory map -This is the current memory map for Mocha, where the base and top addresses are inclusive, and reserved is the amount of memory reserved for this function: - -![Mocha memory map](../img/memmap.svg) +This is the current memory map for Mocha, where the base and top addresses are inclusive, and reserved is the amount of memory reserved for this function: + + +| Base address | Top address | Reserved | Function | +|--------------|-------------|------------|---------------| +| 0x00080000 | 0x00087fff | 32.0 kiB | ROM | +| 0x10000000 | 0x1001ffff | 128.0 kiB | SRAM | +| 0x20000000 | 0x2000ffff | 64.0 kiB | DEBUG_MODULE | +| 0x20010000 | 0x2001ffff | 64.0 kiB | MAILBOX | +| 0x20020000 | 0x2002ffff | 64.0 kiB | DV_SW_IFC | +| 0x30000000 | 0x30007fff | 32.0 kiB | ETHERNET | +| 0x40000000 | 0x4000ffff | 64.0 kiB | GPIO | +| 0x40020000 | 0x4002ffff | 64.0 kiB | CLKMGR | +| 0x40030000 | 0x4003ffff | 64.0 kiB | RSTMGR | +| 0x40040000 | 0x4004ffff | 64.0 kiB | POWER_MANAGER | +| 0x40050000 | 0x4005ffff | 64.0 kiB | ROM_CTRL | +| 0x40060000 | 0x4006ffff | 64.0 kiB | ENTROPY_SRC | +| 0x41000000 | 0x4100ffff | 64.0 kiB | UART | +| 0x42000000 | 0x4200ffff | 64.0 kiB | I2C | +| 0x43000000 | 0x4300ffff | 64.0 kiB | SPI_DEVICE | +| 0x44000000 | 0x4400ffff | 64.0 kiB | TIMER | +| 0x45000000 | 0x4500ffff | 64.0 kiB | SPI_HOST | +| 0x48000000 | 0x4c004003 | 64.0 MiB | PLIC | +| 0x80000000 | 0xbf7fffff | 1016.0 MiB | DRAM | + ## Top-level interface -The Mocha top will need a few top-level inputs. -Some of these are listed here: -- Clock outputs from PLLs. -- Rollback counter backed by OTP. -- Debug and design for test enable pins. -- True random noise source to drive the entropy source. -- AXI subordinate port to connect to the mailbox. +The Mocha top will need a few top-level inputs. +Some of these are listed here: +- Clock outputs from PLLs. +- Rollback counter backed by OTP. +- Debug and design for test enable pins. +- True random noise source to drive the entropy source. +- AXI subordinate port to connect to the mailbox. -In terms of output, the top-level will need output signals: -- Key to provide an AES engine outside of the secure enclave with the memory encryption key. -- AXI manager port to interact with the rest of the chip. +In terms of output, the top-level will need output signals: +- Key to provide an AES engine outside of the secure enclave with the memory encryption key. +- AXI manager port to interact with the rest of the chip. ## SRAM specification -The static random-access memory (SRAM) in CHERI Mocha is mainly used as the stack and heap for the boot firmware that lives in the read-only memory (ROM). -However, it should also be possible to execute from SRAM. -Once code starts executing from dynamic random-access memory (DRAM), we don't envision using SRAM anymore. - -The SRAM block has four ports: -- Clock input -- Reset input -- AXI4 request input from the main SoC sub-system crossbar -- AXI4 response output back to the main crossbar - -Inside the block it translates the AXI4 requests into an SRAM interface that our primitive RAM wrappers use. -It needs to support AXI4 protocol including: -- Bursts, where the last signal must be indicated correctly. -- Response must have the same AXI4 ID as the request -- Atomic support is *excluded*. -- The data width is 64 bits. -- The address range and size of the SRAM are defined in the [memory map](#memory-map). Accesses outside this range must return an error, including if only part of the burst is outside the memory range. -- Responses must return within a bounded amount of time that may be proportional to the length of the burst. -- Only aligned 64-bit accesses are allowed. - -There needs to be 1 CHERI tag bit per 128-bit aligned region. -A tag should only be set to 1 by writing a full 128-bit aligned region. -This 128-bit aligned transaction must be part of a single burst. -The CHERI tag bits are communicated through a single user bit per AXI4 flit (`wuser` and `ruser` for writes and reads respectively). -There should be an assertion to notify when writes occur where `wuser` is set to 1 which is not part of a full capability write. -There should also be an assertion for `wuser` mismatches, where one part of the capability is marked as valid while another is invalid in the same transaction. -If a portion of the 128-bit aligned region is written it must clear the tag for the whole region including when a partial write strobe is used. - -Reads that only read part of a 64-bit value are allowed from valid capability regions, but these should have their tag cleared. -Burst reads from the SRAM must have the appropriate CHERI tags set for each address, so a valid capability must have the user bits set for both of the 64-bit flits it is being sent back, and a mixture of capability and non-capability data is allowed in a burst. -The SRAM is allowed to mark a capability as invalid by setting one or both of the `ruser` bits to zero, so the core must AND the two `ruser` values together to determine the validity of a capability. -Tags should be stored in a separate block of memory from the data, this is to allow future optimisations where bulk-reads of tags are desired. - -The initial value of the SRAM including the tags is undefined at start-up. +The static random-access memory (SRAM) in CHERI Mocha is mainly used as the stack and heap for the boot firmware that lives in the read-only memory (ROM). +However, it should also be possible to execute from SRAM. +Once code starts executing from dynamic random-access memory (DRAM), we don't envision using SRAM anymore. + +The SRAM block has four ports: +- Clock input +- Reset input +- AXI4 request input from the main SoC sub-system crossbar +- AXI4 response output back to the main crossbar + +Inside the block it translates the AXI4 requests into an SRAM interface that our primitive RAM wrappers use. +It needs to support AXI4 protocol including: +- Bursts, where the last signal must be indicated correctly. +- Response must have the same AXI4 ID as the request +- Atomic support is *excluded*. An atomic accesses should return an error. +- The data width is 64 bits. +- The address range and size of the SRAM are defined in the [memory map](#memory-map). For accesses outside this range, the xbar must return a decode error (DECERR), including if only part of the burst is outside the memory range. +- Responses must return within a bounded amount of time that may be proportional to the length of the burst. +- Only aligned 64-bit accesses are allowed. + +There needs to be 1 CHERI tag bit per 128-bit aligned region. +A tag should only be set to 1 by writing a full 128-bit aligned region. +This 128-bit aligned transaction must be part of a single burst. +The CHERI tag bits are communicated through a single user bit per AXI4 flit (`wuser` and `ruser` for writes and reads respectively). +There should be an assertion to notify when writes occur where `wuser` is set to 1 which is not part of a full capability write. +There should also be an assertion for `wuser` mismatches, where one part of the capability is marked as valid while another is invalid in the same transaction. +If a portion of the 128-bit aligned region is written it must clear the tag for the whole region including when a partial write strobe is used. + +Reads that only read part of a 64-bit value are allowed from valid capability regions, but these reads should have their tag cleared in the response. +These reads do not modify the state of the tag in memory. +Burst reads from the SRAM must have the appropriate CHERI tags set for each address, so a valid capability must have the user bits set for both of the 64-bit flits it is being sent back, and a mixture of capability and non-capability data is allowed in a burst. +The SRAM is allowed to mark a capability as invalid by setting one or both of the `ruser` bits to zero, so the core must AND the two `ruser` values together to determine the validity of a capability. +Tags should be stored in a separate block of memory from the data, this is to allow future optimisations where bulk-reads of tags are desired. + +The initial value of the SRAM including the tags is undefined at start-up. diff --git a/hw/top_chip/dv/axi_sram/axi_sram_vplan.hjson b/hw/top_chip/dv/axi_sram/axi_sram_vplan.hjson new file mode 100644 index 000000000..a222efde7 --- /dev/null +++ b/hw/top_chip/dv/axi_sram/axi_sram_vplan.hjson @@ -0,0 +1,449 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// TODO: Add metrics for each item in the vPlan (issue #696) +{ + axi_sram: + { + Description: Verification plan for the AXI4 SRAM. Split across both block level and integration level tests. + Node_Type: dut_name + Block_level: + { + Description: Block-level verification of axi_sram. Tests both data and tag arrays. + Node_Type: section_title + Parameterization: + { + Node_Type: section_title + interface_geometry: + { + Reference: qohtih + Description: The SRAM block has four ports: clock / reset / AXI4 request in / AXI4 response out + Node_Type: metric + Priority: 0 + } + sram_geometry: + { + Reference: + ''' + jeluga + 01skcc + u0s8nt + ''' + Description: Confirm DUT parameterisation matches spec: AxiDataWidth = 64 (8-byte word), CapSize = 128-bit (16-byte capability), 1 CHERI tag bit per 128-bit aligned region, SramMemSize = 128 KiB + Node_Type: metric + Priority: 0 + Comment: SramMemSize 128 KiB, last word 0x1FFF8 and last cap slot 0x1FFF0 are derived from SRAMLength in top_pkg (0x2_0000) and 5scstu. + } + } + Data_Path: + { + Description: Translation of AXI4 requests into the SRAM interface; data integrity + Node_Type: section_title + Clock_and_Reset: + { + Description: Clock and reset input behaviour + Node_Type: section_title + rst_sanity: + { + Reference: + ''' + 947rwh + rrni5j + ''' + Description: DUT comes out of reset cleanly and accepts the first transaction + Node_Type: metric + Priority: 0 + } + rst_mid_burst: + { + Reference: rrni5j + Description: DUT is reset during an AXI burst + Node_Type: metric + Priority: 0 + Comment: Transaction should be dropped cleanly. The next transaction after an in-flight reset should still be accepted as normal. Would be good to clarification on the expected state of SRAM after a reset (i.e if a reset comes in during a 2-beat capability write) + } + init_value_undefined: + { + Reference: hqbiau + Description: Initial SRAM and tag contents are undefined at start-up; no read should be expected to return a known value before a write + Node_Type: metric + Priority: 2 + Comment: We model this by not pre-loading the SRAM (leave a X's). Not an explicit testcase, rather an environment configuration. + } + } + Basic_access: + { + Description: Single-beat aligned 64-bit read/write + Node_Type: section_title + write_read: + { + Reference: + ''' + vknoin + pgo845 + n5txiq + ''' + Description: Single-beat 8-byte write followed by readback returns identical data + Node_Type: metric + Priority: 0 + } + address_boundaries: + { + Reference: u0s8nt + Description: Write/read first (0x0) and last valid word (0x1FFF8) and last capability slot (0x1FFF0) + Node_Type: metric + Priority: 0 + } + data_all_bits: + { + Reference: vknoin + Description: Walk a 1 and a 0 through all 64 data bits to catch stuck-at faults + Node_Type: metric + Priority: 0 + } + aligned_only: + { + Reference: lfcb7q + Description: Only aligned 64-bit accesses are exercised; misaligned data accesses are not expected/supported + Node_Type: metric + Priority: 1 + } + } + Bursts: + { + Description: AXI4 burst handling + Node_Type: section_title + burst_last: + { + Reference: + ''' + o02amt + mcykq8 + ''' + Description: Last beat of a burst asserts rlast/expects wlast correctly + Node_Type: metric + Priority: 0 + } + resp_id_match: + { + Reference: 4t4cew + Description: Response (B/R) AXI4 ID equals the originating request ID + Node_Type: metric + Priority: 0 + Comment: Checked explicitly by the SV scoreboard + } + atomic_excluded: + { + Reference: bsi4rc + Description: Atomic support is excluded, so an access carrying a non-zero awatop must be answered with a slave error rather than being serviced. Drive ATOP_ATOMICSTORE (awatop[5:4]=01), ATOP_ATOMICLOAD (10), ATOP_ATOMICSWAP (6'b110000) and ATOP_ATOMICCMP (6'b110001) at an in-range address and check that bresp is RESP_SLVERR (2'b10) with the request ID, that the atop[5] classes which also return read data report rresp RESP_SLVERR on every R beat with rlast on the final beat, that the addressed memory is left unchanged, and that the CHERI tag of the covering 128-bit slot is left unchanged because the rejected access must not write. A plain access to the same address afterwards must still succeed, so the error must not wedge the block + Node_Type: metric + Priority: 1 + } + bounded_response: + { + Reference: 34ld5i + Description: Every request receives a response within a bounded time proportional to burst length + Node_Type: metric + Priority: 1 + } + backpressure: + { + Reference: NA + Description: Master side constrained random ready-throttling under load. the VIP holds r_ready/b_ready low with a random low duty cycle and injects valid-delays on the request channels (aw/w/ar), forcing the DUT's shallow internal FIFOs to fill and deassert its own aw_ready/w_ready/ar_ready. Data and CHERI tag must be intact once the master resumes. + Node_Type: metric + Priority: 1 + } + } + Concurrency: + { + Description: Multiple AXI transactions in flight + Node_Type: section_title + concurrent_data_tag: + { + Reference: vknoin + Description: A plain-data write and a capability write issued concurrently both complete and read back correctly + Node_Type: metric + Priority: 1 + } + multiple_outstanding_transactions: + { + Reference: mcykq8 + Description: multiple reads+writes to distinct addresses in flight all complete, data+tag are correct, and ids matched + Node_Type: metric + Priority: 1 + } + waw_same_address: + { + Reference: mcykq8 + Description: back-to-back writes to the same address+tag result in the last write data+tag being read back. + Node_Type: metric + Priority: 1 + } + raw_war_same_region: + { + Reference: NA + Description: A read and a write to the same region both in flight concurrently. Exercises both RAW (the read may observe the region before or after the write lands) and WAR (the write lands while the read is still in flight). + Node_Type: metric + Priority: 2 + Comment: both old and new data are legal outcomes (axi doesn't mandate how the race is handled), so the scoreboard is relaxed on value. Still, the racing read's data must equal the pre or post-write value fully (not bit-torn or X), each beat of a multi-beat read is independently a clean old or new value, and the CHERI tag (ruser) must be consistent with whichever data snapshot that beat observed. Strict invariants are still checked: no deadlock, both transactions receive responses with correct ID / BRESP / RRESP / RLAST. a subsequent ordered read returns the new data (RAW) and the final stored value equals the write data (WAR). + } + } + } + CHERI_tags: + { + Description: CHERI tag bit storage and read-back behaviour (1 bit per 128-bit region; communicated via wuser/ruser) + Node_Type: section_title + Tag_write: + { + Description: Conditions under which a tag is set to 1 + Node_Type: section_title + tag_write: + { + Reference: 01skcc + Description: Each data field has a corresponding tag field which can be accessed in the same transaction + Node_Type: metric + Priority: 0 + } + no_tag_single_beat: + { + Reference: + ''' + 8rlwol + 35vdeg + ''' + Description: wuser=1 on a single-beat (awlen=0) write must NOT set the tag (not a full region in a single burst) + Node_Type: metric + Priority: 0 + Comment: Corner: tag only on full capability burst + } + no_tag_misaligned: + { + Reference: 8rlwol + Description: 2-beat wuser=1 burst starting at a non-16-byte-aligned address must NOT set the tag (is_w_cap_aligned requires addr[3:0]==0) + Node_Type: metric + Priority: 1 + Comment: Corner: alignment gating + } + no_tag_two_bursts: + { + Reference: + ''' + 35vdeg + 8rlwol + ''' + Description: Writing both halves of a 128-bit region as two separate single-beat bursts must NOT set the tag; the tag requires a single 2-beat capability burst + Node_Type: metric + Priority: 1 + Comment: Corner: full region covered across two separate bursts still leaves the tag 0 + } + assert_wuser_not_full_cap: + { + Reference: bj8we7 + Description: Assertion fires when wuser=1 on a write that is not part of a full capability write + Node_Type: metric + Priority: 1 + } + assert_wuser_mismatch: + { + Reference: + ''' + 9a3xf6 + 8rlwol + ''' + Description: A mismatched wuser pair is not a full capability write, so it must never leave a set tag. Drive both beat orders of an otherwise well-formed 2-beat aligned capability write explicitly - wuser = (1, 0) and wuser = (0, 1) - and for each check that the assertion fires and, separately, that a readback of the covering 128-bit slot returns a cleared tag. The readback is the part that still holds when assertions are disabled around deliberate error injection + Node_Type: metric + Priority: 1 + Comment: The two orders are not equivalent, so neither may be dropped. The tag RAM is written on every beat - u_tag_ram.write_i is sram_we_d - and both beats of a 2-beat capability write resolve to the same tag bit, because sram_tag_bit_addr is the byte address shifted right by $clog2(CapSizeBits/8) and that is identical for X and X+8. The stored value is therefore the last beat's cheri_w_tag, which is w.user & is_w_cap_sized & is_w_cap_aligned (axi_to_detailed_mem.sv:414) and reduces to the last beat's wuser for an aligned, correctly sized burst. So (1, 0) stores 0 and passes, while (0, 1) stores 1 - a forged capability tag on a write that was never a full capability write. Stated against the architectural requirement, so the testpoint stays valid whatever the RTL currently does + } + } + Tag_clear: + { + Description: Conditions under which a previously-set tag is cleared + Node_Type: section_title + tag_cleared_by_write: + { + Reference: 893tz4 + Description: A plain data write to either word of a tagged capability slot clears the tag for the whole region + Node_Type: metric + Priority: 0 + Comment: Corner: any non-cap write zeroes the tag bit + } + partial_strobe_clears_tag: + { + Reference: 893tz4 + Description: A sub-64-bit (partial write-strobe) write to a tagged slot clears the tag + Node_Type: metric + Priority: 1 + Comment: Corner: partial strobe clears tag + } + subword_read_clears_tag: + { + Reference: + ''' + raa5pw + 832lpx + ''' + Description: Reads of only part of a 64-bit value from a valid cap region return data but with the tag cleared + Node_Type: metric + Priority: 1 + Comment: Tag is returned as clear in the response, but not modified in memory. + } + } + Tag_read: + { + Description: Per-beat ruser (CHERI tag) generation on reads + Node_Type: section_title + cap_both_ruser_set: + { + Reference: + ''' + kn6exz + af8sx6 + u95b14 + ''' + Description: Both ruser flits of a valid capability read are 1 + Node_Type: metric + Priority: 0 + Comment: the SRAM invalidate a cap by clearing one or both `ruser` bits, so the two flits are not guaranteed equal by spec. Rather than treat the asymmetric (1,0) case as not-applicable, bind an SVA that on every 2-beat capability read both flits are equal and match the single stored tag bit (ruser_flit0 == ruser_flit1). This proves the one-bit-per-region design never emits a false-valid asymmetric result. + } + burst_read_mixed_tags: + { + Reference: kn6exz + Description: A read spanning a tagged and an untagged capability slot returns the correct per-beat ruser for each + Node_Type: metric + Priority: 2 + } + } + Tag_isolation: + { + Description: Tags are stored independently and in separate memory from data + Node_Type: section_title + tag_isolation: + { + Reference: 01skcc + Description: Writing/clearing the tag of one 128-bit slot must not affect an adjacent slot's tag + Node_Type: metric + Priority: 0 + } + tag_aliasing: + { + Reference: 01skcc + Description: Every 128-bit slot across the whole 128 KiB must own a distinct tag bit, so slots that are far apart must not share tag storage. Write a valid capability at slot i, then a plain-data write at slot i+1024 (16 KiB higher), for several i spread over the address space, and check each slot's tag is unaffected by the other. Repeat with the order reversed + Node_Type: metric + Priority: 0 + Comment: The i+1024 stride targets a suspected tag address truncation in axi_sram.sv. TagBitAddrWidth subtracts a byte-based shift ($clog2(CapSizeBits/8) = 4) from AddrWidth, which is a 64-bit-word width, giving 10 instead of $clog2(CapSizeBits/AxiDataWidth) = 1 subtracted for 13. u_tag_ram is then 16 x 64 = 1024 tag bits for the 8192 slots in 128 KiB, aliasing every 16 KiB. The metric is written against the architectural requirement so it stays valid once the width is corrected, at which point no stride aliases + } + tag_separate_memory: + { + Reference: lzoy40 + Description: Tags are stored in a separate memory block from data + Node_Type: metric + Priority: 2 + } + } + } + Stress_random: + { + Description: Randomised back-to-back access sweeps + Node_Type: section_title + Comment: randomization should be seeded for reproducibility + random_data: + { + Reference: NA + Description: N random (aligned addr, data) write/read pairs across the address space + Node_Type: metric + Priority: 1 + } + random_capabilities: + { + Reference: + ''' + 8rlwol + kn6exz + ''' + Description: N random 16-byte-aligned capability write/read pairs verifying data and tag + Node_Type: metric + Priority: 1 + } + } + Coverage: + { + Description: Functional coverage collection for the AXI SRAM + Node_Type: section_title + cg_tag_write: + { + Reference: + ''' + 8rlwol + 35vdeg + 893tz4 + ''' + Description: Coverage of the tag-write gating conditions (burst length/size, alignment, strobe, wuser), each crossed with the tag bit. + Node_Type: metric + Priority: 1 + } + cg_tag_read: + { + Reference: + ''' + kn6exz + raa5pw + ''' + Description: Coverage of the ruser tag returned on reads: the per-flit tag pair on capability reads, and cleared tags on sub-word reads + Node_Type: metric + Priority: 1 + } + } + } + Integration: + { + Description: Verification of axi_sram integrated behind the real Mocha interconnect. + Node_Type: section_title + Address_map: + { + Reference: lhfz6r + Description: Address decode through the real mocha AXI crossbar. Only the SRAM aperture (SRAMBase=0x10000000, 128 KiB) returns OKAY; every other address returns DECERR (the xbar has no default master port; non-SRAM device ports carry axi_err_slv). The scoreboard is decode-aware: in-aperture it checks data/tag, out-of-aperture it expects DECERR and flags any OKAY as a decode bug. + Node_Type: section_title + sram_reachable: + { + Description: Accesses inside the SRAM range reach axi_sram and round-trip correctly through the interconnect + Node_Type: metric + Priority: 0 + Comment: Implicit + } + out_of_range_error: + { + Reference: u0s8nt + Description: Accesses outside every mapped SRAM region return DECERR + Node_Type: metric + Priority: 1 + Comment: Fully-out-of-range accesses result in an error; the partial-out-of-range part is enforced by proxy of AXI4 not allowing bursts to span a 4KB boundary + } + burst_wrap: + { + Reference: u0s8nt + Description: A burst starting in SRAM whose later beats overflow from the address range. + Node_Type: metric + Priority: 2 + Comment: AXI4 forbids a burst from crossing a 4 KB boundary, so a compliant master can't issue such a burst. axi_sram itself does not range-check (mem_err_i=0; it masks the address and would silently wrap), so range enforcement rests entirely on the AXI driver being compliant. + } + sram_boundary: + { + Reference: lhfz6r + Description: Address range edges: the first and last SRAM words round-trip OK; the word just below SRAMBase and the word just past the top are unmapped and return DECERR + Node_Type: metric + Priority: 1 + } + execute_from_sram: + { + Reference: lhjkel + Description: Instruction-flavoured reads (arprot[2]=1) return stored words identically to data reads + Node_Type: metric + Priority: 2 + Comment: only the arprot[2]=instruction dimension is SRAM-specific + } + } + } + } +} diff --git a/pyproject.toml b/pyproject.toml index b65a9317d..ce7f56ad1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,6 @@ dependencies = [ "twine>=6.1.0", # Design verification runner. "dvsim>=1.33", - "svgwrite>=1.4.3", "pyelftools>=0.32", # Tool schema validation. "jsonschema>=4.26.0", diff --git a/util/artefacts.py b/util/artefacts.py index 7b6424280..19e4c2f2e 100755 --- a/util/artefacts.py +++ b/util/artefacts.py @@ -36,12 +36,12 @@ "build/rdl/rdl.json", "sw/device/lib/boot/memory.ld", ], - # rdl: generate memory map image + # rdl: embed the memory map table into the architecture document [ "util/rdlgenerator.py", - "gen-memory-map", + "embed-memory-map-md", "build/rdl/rdl.json", - "doc/img/memmap.svg", + "doc/ref/arch.md", ], # generate devicetree [ diff --git a/util/rdlgenerator.py b/util/rdlgenerator.py index 9984d138a..21bc22524 100755 --- a/util/rdlgenerator.py +++ b/util/rdlgenerator.py @@ -12,7 +12,6 @@ from typing import TextIO import jinja2 -import svgwrite # root of the project directory PROJECT_ROOT = Path.resolve(Path(__file__)).parent.parent @@ -106,8 +105,57 @@ def gen_linker_script(args) -> None: gen_from_template(input_file, out_file, LD_TEMPLATE) -def gen_memory_map(args) -> None: - """Generate a svg file with the memory map.""" +MEMORY_MAP_HEADER = ["Base address", "Top address", "Reserved", "Function"] + +"""Comments delimiting the generated memory map table inside a markdown document.""" +MEMORY_MAP_MD_BEGIN = "" +MEMORY_MAP_MD_END = "" + + +def memory_map_table(rdljson: dict[str, list[dict]]) -> list[str]: + """Build the memory map as the lines of a markdown table, one row per device.""" + rows: list[list[str]] = [] + for device in rdljson["devices"]: + base_addr = device["offset"] if "offset" in device else device["offsets"][0] + end_addr = base_addr + device["size"] - 1 + size = human_size(device["size"]) + rows.append([f"{base_addr:#010x}", f"{end_addr:#010x}", size, device["name"]]) + + widths = [max(len(row[c]) for row in [MEMORY_MAP_HEADER, *rows]) for c in range(len(rows[0]))] + + def emit_row(cells: list[str]) -> str: + padded = [cell.ljust(width) for cell, width in zip(cells, widths, strict=True)] + return "| " + " | ".join(padded) + " |" + + separator = "|" + "|".join("-" * (width + 2) for width in widths) + "|" + return [emit_row(MEMORY_MAP_HEADER), separator, *(emit_row(row) for row in rows)] + + +def row_function(row: str) -> str: + """The function column of an untagged memory map table row, naming the entry it describes.""" + return row.rsplit("|", 2)[-2].strip() + + +def read_row_tags(rows: list[str]) -> dict[str, str]: + """ + Recover the tags appended after the closing pipe of a memory map table row, keyed by the + function column of the row they are attached to. + """ + tags = {} + for row in rows: + cells, _, tag = row.rpartition("|") + tag = tag.strip() + if tag: + tags[cells.rsplit("|", 1)[-1].strip()] = tag + return tags + + +def embed_memory_map_md(args) -> None: + """ + Embed the memory map as a markdown table into a document, replacing whatever sits between the + marker comments. Tags appended to the rows of the document are written back onto the rows of + the regenerated table, so that they survive regeneration. + """ input_file = Path(args.input_file) out_file = Path(args.out_file) @@ -116,34 +164,40 @@ def gen_memory_map(args) -> None: with input_file.open("r") as f: rdljson = json.load(f) - cell_w = 160 - cell_h = 30 - rows_n = len(rdljson["devices"]) + 1 - cols = 4 + # the markers are matched on their start, as a spec tagger may have appended a tag to them + doc = out_file.read_text().splitlines() + begin = next((i for i, line in enumerate(doc) if line.startswith(MEMORY_MAP_MD_BEGIN)), None) + end = next((i for i, line in enumerate(doc) if line.startswith(MEMORY_MAP_MD_END)), None) + if begin is None or end is None: + raise ValueError( + f"'{out_file}' needs a '{MEMORY_MAP_MD_BEGIN}' and a '{MEMORY_MAP_MD_END}' line " + "delimiting where the memory map table is to be embedded" + ) - dwg = svgwrite.Drawing(out_file, size=(cell_w * cols, cell_h * rows_n)) - font = {"font_size": 14, "font_family": "monospace"} + table = memory_map_table(rdljson) + tags = read_row_tags(doc[begin + 1 : end]) + for function in tags.keys() - {row_function(row) for row in table}: + print(f"Warning: dropping tag '{tags[function]}' of removed memory map entry '{function}'") - # Header - header = ["Base address", "Top address", "Reserved", "Function"] - for c, h in enumerate(header): - x = c * cell_w - dwg.add(dwg.rect((x, 0), (cell_w, cell_h), fill="black", stroke="white")) - dwg.add(dwg.text(h, insert=(x + 6, 20), **font, fill="white")) + tagged = [row + tags.get(row_function(row), "") for row in table] + out_file.write_text("\n".join(doc[: begin + 1] + tagged + doc[end:]) + "\n") - # Rows - for r, device in enumerate(rdljson["devices"], start=1): - base_addr = device["offset"] if "offset" in device else device["offsets"][0] - end_addr = base_addr + device["size"] - 1 - size = human_size(device["size"]) - line = [f"{base_addr:#010x}", f"{end_addr:#010x}", size, device["name"]] - for c, cell in enumerate(line): - x = c * cell_w - y = r * cell_h - dwg.add(dwg.rect((x, y), (cell_w, cell_h), fill="white", stroke="black")) - dwg.add(dwg.text(cell, insert=(x + 6, y + 20), **font)) + print(f"Successfully embedded the memory map into {out_file}") + + +def gen_memory_map_md(args) -> None: + """ + Generate a markdown file with the memory map as a table. The file is created holding just the + marker comments, which the table is then embedded between. + """ + + out_file = Path(args.out_file) + + with out_file.open("w") as f: + emit_file_header(f, args, comment_open="") + f.write(f"\n{MEMORY_MAP_MD_BEGIN}\n{MEMORY_MAP_MD_END}\n") - dwg.save() + embed_memory_map_md(args) def check_register_rdl(devices: list) -> None: @@ -637,19 +691,31 @@ def main(): ) linker_parser.set_defaults(func=gen_linker_script) - # Subparser for gen_memory_map - map_parser = subparsers.add_parser( - "gen-memory-map", help="Generate a svg file with the memory map." + # Subparser for gen_memory_map_md + map_md_parser = subparsers.add_parser( + "gen-memory-map-md", help="Generate a markdown file with the memory map table." ) - map_parser.add_argument("input_file", type=Path, help="Input JSON file generated by rdl2ot") - map_parser.add_argument( + map_md_parser.add_argument("input_file", type=Path, help="Input JSON file generated by rdl2ot") + map_md_parser.add_argument( "out_file", type=Path, nargs="?", - default=root_resolve(PROJECT_ROOT / Path("build/memmap.svg")), + default=root_resolve(PROJECT_ROOT / Path("build/memmap.md")), help="Output filename. (default: %(default)s)", ) - map_parser.set_defaults(func=gen_memory_map) + map_md_parser.set_defaults(func=gen_memory_map_md) + + # Subparser for embed_memory_map_md + embed_md_parser = subparsers.add_parser( + "embed-memory-map-md", help="Embed the memory map table into a markdown document." + ) + embed_md_parser.add_argument( + "input_file", type=Path, help="Input JSON file generated by rdl2ot" + ) + embed_md_parser.add_argument( + "out_file", type=Path, help="Markdown document to embed the memory map table into" + ) + embed_md_parser.set_defaults(func=embed_memory_map_md) # Subparser for gen_register_dif register_parser = subparsers.add_parser( diff --git a/uv.lock b/uv.lock index afb8a2cec..cbeefe172 100644 --- a/uv.lock +++ b/uv.lock @@ -135,43 +135,31 @@ sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8 wheels = [ { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, - { url = "https://files.pythonhosted.org/packages/05/eb/b86f2a2645b62adcfff53b0dd97e8dfafb5c8aa864bd0d9a2c2049a0d551/cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0", size = 203217, upload-time = "2025-09-08T23:22:14.596Z" }, - { url = "https://files.pythonhosted.org/packages/9f/e0/6cbe77a53acf5acc7c08cc186c9928864bd7c005f9efd0d126884858a5fe/cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4", size = 203079, upload-time = "2025-09-08T23:22:15.769Z" }, { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, - { url = "https://files.pythonhosted.org/packages/dc/7f/55fecd70f7ece178db2f26128ec41430d8720f2d12ca97bf8f0a628207d5/cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5", size = 203374, upload-time = "2025-09-08T23:22:32.507Z" }, - { url = "https://files.pythonhosted.org/packages/84/ef/a7b77c8bdc0f77adc3b46888f1ad54be8f3b7821697a7b89126e829e676a/cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664", size = 202597, upload-time = "2025-09-08T23:22:34.132Z" }, { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, - { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" }, - { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" }, { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, @@ -316,33 +304,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, - { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, - { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, - { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, @@ -825,7 +807,6 @@ dependencies = [ { name = "ruff" }, { name = "semantic-version" }, { name = "setuptools" }, - { name = "svgwrite" }, { name = "tabulate" }, { name = "twine" }, { name = "types-jsonschema" }, @@ -855,7 +836,6 @@ requires-dist = [ { name = "ruff", specifier = ">=0.9.6" }, { name = "semantic-version", specifier = ">=2.10.0,<3" }, { name = "setuptools", specifier = ">=80.9.0" }, - { name = "svgwrite", specifier = ">=1.4.3" }, { name = "tabulate", specifier = ">=0.9.0,<0.10" }, { name = "twine", specifier = ">=6.1.0" }, { name = "types-jsonschema", specifier = ">=4.26.0" }, @@ -1647,15 +1627,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, ] -[[package]] -name = "svgwrite" -version = "1.4.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/16/c1/263d4e93b543390d86d8eb4fc23d9ce8a8d6efd146f9427364109004fa9b/svgwrite-1.4.3.zip", hash = "sha256:a8fbdfd4443302a6619a7f76bc937fc683daf2628d9b737c891ec08b8ce524c3", size = 189516, upload-time = "2022-07-14T14:05:26.107Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/15/640e399579024a6875918839454025bb1d5f850bb70d96a11eabb644d11c/svgwrite-1.4.3-py3-none-any.whl", hash = "sha256:bb6b2b5450f1edbfa597d924f9ac2dd099e625562e492021d7dd614f65f8a22d", size = 67122, upload-time = "2022-07-14T14:05:24.459Z" }, -] - [[package]] name = "systemrdl-compiler" version = "1.32.2"