feat: Add support for SKY130 OpenRAM SRAM integration

- Introduced  to handle the preparation of the SKY130 OpenRAM SRAM views.
- Updated  to download and prepare the SRAM archive during PDK setup.
- Enhanced OpenSTA and Yosys configurations to accommodate SRAM macro integration.
- Added Verilog simulation models for GF180 and SKY130 SRAM macros.
- Implemented tests for SRAM setup and validation, ensuring correct integration and functionality.
- Updated various Makefiles and scripts to support SRAM size and macro configurations.
This commit is contained in:
maksyuki committed 2026-08-25 12:55:47 +08:00
1 parent fe347ed583
commit 71d52f357e
40 files changed
+749 -61

No files matched your search

+6
View File
@@ -57,4 +57,10 @@ Reserved" notice; its inclusion does not relicense it under Mulan PSL v2.
## 6. Notes on redistribution
The optional checksum-locked SKY130 SRAM artifact is generated with OpenRAM at
the revision recorded in its manifest. OpenRAM is distributed under the BSD
3-Clause License; the SkyWater-provided SRAM build-space used by the generator
is distributed under Apache License 2.0. The artifact retains both exact
license texts and is materialized only below the ignored `.cache/` tree.
The preserved third-party notices are included to maintain attribution for code that was adapted or reused in this repository. They do not replace the repository's main license declaration. The retroSoC project itself is distributed under the Mulan PSL v2 license.
+11 -4
View File
@@ -37,9 +37,9 @@ STA ?= NONE
# HW
PDK ?= IHP130
HAVE_PLL ?= NO
HAVE_SRAM_IF ?= NO
HAVE_SRAM_MACRO ?= NO
SRAM_SIZE_KIB ?= 128
HAVE_SRAM_IF ?= $(if $(filter ICS55,$(PDK)),NO,YES)
HAVE_SRAM_MACRO ?= $(if $(filter ICS55,$(PDK)),NO,YES)
SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32)
PDK_BEHAV ?= NO
HAVE_SVA ?= NO
BUILD_RELEASE ?= NO
@@ -161,6 +161,12 @@ $(call validate_value,APP,$(VALID_APP))
$(call validate_value,LINK_TYPE,$(VALID_LINK_TYPE))
$(call validate_value,COREMARK_MODE,$(VALID_COREMARK_MODE))
ifeq ($(HAVE_SRAM_MACRO),YES)
ifneq ($(HAVE_SRAM_IF),YES)
$(error HAVE_SRAM_MACRO=YES requires HAVE_SRAM_IF=YES)
endif
endif
JTAG_IDCODE_VALID := $(shell printf '%s' '$(JTAG_IDCODE)' | grep -E '^[[:xdigit:]]{8}$$')
ifneq ($(JTAG_IDCODE_VALID),$(JTAG_IDCODE))
$(error JTAG_IDCODE='$(JTAG_IDCODE)' must be exactly eight hexadecimal digits)
@@ -364,7 +370,8 @@ config:
doctor:
@python3 $(ROOT_PATH)/scripts/doctor.py \
--root $(ROOT_PATH) --simu $(SIMU) --synth $(SYNTH) --sta $(STA) \
--pdk $(PDK) --formal $(FORMAL) --lock $(LOCK_FILE)
--pdk $(PDK) --have-sram-macro $(HAVE_SRAM_MACRO) \
--formal $(FORMAL) --lock $(LOCK_FILE)
benchmark-report: firmware
+3
View File
@@ -12,5 +12,8 @@ The following third-party projects or authors are referenced in the RTL source h
- Device simulation models: the models under rtl/model/ preserve their upstream
notices, including the Winbond Electronics Corporation notice in the
W25Q128JVxIM model.
- SKY130 OpenRAM SRAM artifact: generated using OpenRAM under BSD-3-Clause and
the SkyWater SRAM build-space under Apache-2.0. Both license texts and the
exact source revisions are retained in the downloaded artifact manifest.
These notices are included solely to preserve attribution for the retained third-party source material. They do not alter the licensing terms of the retroSoC repository itself, which is distributed under the Mulan PSL v2 license.
+8 -5
View File
@@ -70,21 +70,24 @@ PDK, application, linker layout, and optional features as one reproducible
configuration. The management core is fixed to Hazard3. The user-extension
fabric exposes C0-C3 user-core slots and software selects one active user core.
`SRAM_SIZE_KIB` selects a generated 4/16/32/64/128 KiB native-AXI4 on-chip
SRAM window; committed profiles explicitly retain the 128 KiB default.
SRAM window. The IHP130, GF180, and SKY130 CI profiles enable a 32 KiB macro-backed
window; ICS55 keeps it absent. IHP130 benchmark/CoreMark profiles retain 128 KiB
because their fixed data placement and SRAM-resident image require the larger capacity.
| Profile | ISA | Application | Coverage |
| --- | --- | --- | --- |
| [`configs/ci/ihp130.mk`](configs/ci/ihp130.mk) | RV32IM | `bringup` | Pull-request open-source regression: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
| [`configs/ci/gf180.mk`](configs/ci/gf180.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, and Icarus netlist coverage. |
| [`configs/ci/ihp130.mk`](configs/ci/ihp130.mk) | RV32IM | `bringup` | Pull-request open-source regression with a 32 KiB IHP SRAM: firmware, Verilator, Icarus, Yosys, Icarus netlist simulation, and OpenSTA. |
| [`configs/ci/gf180.mk`](configs/ci/gf180.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist, and OpenSTA coverage with a 32 KiB SRAM assembled from GF180 macros. |
| [`configs/ci/ics55.mk`](configs/ci/ics55.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist simulation, and OpenSTA core timing coverage. |
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, and Icarus netlist coverage. |
| [`configs/ci/sky130.mk`](configs/ci/sky130.mk) | RV32IM | `bringup` | Pull-request firmware, RTL simulation, Yosys, Icarus netlist, and OpenSTA coverage with a 32 KiB OpenRAM SRAM. |
| [`configs/ci/ihp130-shell.mk`](configs/ci/ihp130-shell.mk) | RV32IM | `shell` | Pull-request firmware build with CSR support enabled. |
| [`configs/ci/ihp130-debug.mk`](configs/ci/ihp130-debug.mk) | RV32IM | `debug` | Verilator remote-bitbang acceptance of the Hazard3 JTAG DTM, Debug Module, OpenOCD, and GDB. |
| [`configs/benchmark/ihp130-hazard3-coremark.mk`](configs/benchmark/ihp130-hazard3-coremark.mk) | RV32IM | `coremark` | Fixed four-iteration SRAM CoreMark quick measurement, recorded by nightly IHP130 regression. |
| [`configs/cluster/ics55.mk`](configs/cluster/ics55.mk) | RV32IM | `bringup` | Compatibility profile for site-specific ICS55 runs. |
CI Verilator firmware simulations explicitly select the `ci_smoke`
application, which checks UART, archinfo APB readback, RNG fail-closed behavior, and test-status
application, which checks UART, archinfo APB readback, macro-backed on-chip SRAM,
RNG fail-closed behavior, and test-status
completion without the verbose startup report. ARCHINFO checks include its
ABI and the build/configuration identifiers generated for that variant. The profiles retain `bringup`
as their default for manual diagnostics. To run the full report in Verilator,
+28 -1
View File
@@ -39,6 +39,33 @@ static bool rs_ci_smoke_onchip_sram(void) {
return (info.memory_bytes == 0U) && (info.bank_count == 0U);
}
static bool rs_ci_smoke_onchip_sram_access(void) {
#if RS_SOC_HAS_SRAM
volatile uint8_t *const bytes = (volatile uint8_t *)(uintptr_t)RS_SOC_SRAM_BASE;
volatile uint16_t *const halfs = (volatile uint16_t *)(uintptr_t)RS_SOC_SRAM_BASE;
volatile uint32_t *const words = (volatile uint32_t *)(uintptr_t)RS_SOC_SRAM_BASE;
volatile uint32_t *const last =
(volatile uint32_t *)(uintptr_t)(RS_SOC_SRAM_BASE + RS_SOC_SRAM_SIZE - 4U);
words[0] = UINT32_C(0x11223344);
if (words[0] != UINT32_C(0x11223344)) {
return false;
}
bytes[1] = UINT8_C(0xA5);
if (words[0] != UINT32_C(0x1122A544)) {
return false;
}
halfs[1] = UINT16_C(0xBEEF);
if (words[0] != UINT32_C(0xBEEFA544)) {
return false;
}
*last = UINT32_C(0xCAFEF00D);
return *last == UINT32_C(0xCAFEF00D);
#else
return true;
#endif
}
static bool rs_ci_smoke_user_ip(void) {
rs_status_t status;
uint32_t identifier = 0U;
@@ -306,7 +333,7 @@ int main(void) {
rs_test_finish(RS_TEST_FAILED, 1U);
}
printf("ci_smoke: archinfo passed\n");
if (!rs_ci_smoke_onchip_sram()) {
if (!rs_ci_smoke_onchip_sram() || !rs_ci_smoke_onchip_sram_access()) {
rs_test_finish(RS_TEST_FAILED, 12U);
}
printf("ci_smoke: on-chip SRAM passed\n");
+9 -3
View File
@@ -18,9 +18,15 @@ Start builds from a committed profile rather than setting an unreviewed mix of
variables on the command line.
`SRAM_SIZE_KIB` selects 4, 16, 32, 64, or 128 KiB of on-chip SRAM and is part
of the build variant key. Every committed profile sets 128 explicitly;
profiles with `HAVE_SRAM_IF=NO` keep a reproducible capacity selection while
the hardware capability reports the memory absent.
of the build variant key. IHP130, GF180, and SKY130 CI profiles select eight
4 KiB banks for 32 KiB total and enable both the interface and technology
macro. ICS55 profiles keep the memory absent. IHP130 benchmark and CoreMark
profiles explicitly retain 128 KiB for their fixed workload placement and
SRAM-resident image.
`HAVE_SRAM_MACRO=YES` requires `HAVE_SRAM_IF=YES`. The generic manual defaults
enable 32 KiB for IHP130, GF180, and SKY130 and keep ICS55 disabled; committed
profiles remain the supported reproducible entry points.
`PDK_BEHAV=YES` selects technology-wrapper functional models for behavioral
simulation. It is a simulation-only setting, participates in the build variant
+3 -3
View File
@@ -1,9 +1,9 @@
SOC := MINI
PDK := GF180
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -3,7 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
SRAM_SIZE_KIB := 32
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+3 -3
View File
@@ -1,9 +1,9 @@
SOC := MINI
PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+1 -1
View File
@@ -3,7 +3,7 @@ PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 128
SRAM_SIZE_KIB := 32
PDK_BEHAV := YES
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
+3 -3
View File
@@ -1,9 +1,9 @@
SOC := MINI
PDK := IHP130
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+3 -3
View File
@@ -1,9 +1,9 @@
SOC := MINI
PDK := SKY130
HAVE_PLL := NO
HAVE_SRAM_IF := NO
HAVE_SRAM_MACRO := NO
SRAM_SIZE_KIB := 128
HAVE_SRAM_IF := YES
HAVE_SRAM_MACRO := YES
SRAM_SIZE_KIB := 32
HAVE_SVA := NO
EXT_CLK_HZ := 72000000
AUD_CLK_HZ := 18432000
+6
View File
@@ -14,3 +14,9 @@ python3 scripts/dependency_lock.py --lock dependencies/dependencies.lock.json
Then run the affected setup, doctor, test, and regression flow described in
[Engineering Workflow](../docs/engineering.md).
The locked SKY130 OpenRAM SRAM archive is generated and published by the
`retroSoC/artifact` workflow. `physical/pdk/setup.py` verifies its SHA-256,
manifest, geometry, source revisions, generated-file hashes, and TT/SS views
before materializing it below `.cache/retrosoc/pdk/sky130/openram/`. Generated
Verilog, Liberty, LEF, GDS, and SPICE views are never committed here.
+6
View File
@@ -82,6 +82,12 @@
"destination": ".cache/retrosoc/downloads/pdk/ics55/ics55_LLSC_H7CR_liberty.tar.bz2",
"license": "Apache-2.0"
},
"sky130_openram_sram_4kbyte_1rw_32x1024_8": {
"url": "https://github.com/retroSoC/artifact/releases/download/openram-sky130_sram_4kbyte_1rw_32x1024_8-b2b069ce119d-24dfd2f0371c/sky130_sram_4kbyte_1rw_32x1024_8-openram-b2b069ce119d.tar.gz",
"sha256": "75d987556561c7f3e9bc29e59b854849c2938c509c1b2e37906eb2af6aae211e",
"destination": ".cache/retrosoc/downloads/pdk/sky130/sky130_sram_4kbyte_1rw_32x1024_8-openram-b2b069ce119d.tar.gz",
"license": "LicenseRef-OpenRAM-SKY130-Mixed"
},
"serv": {
"url": "https://github.com/olofk/serv/archive/refs/tags/1.4.0.tar.gz",
"sha256": "f81c37b9f9d548c658e23bb7eb90fbed8b54e2e9e51fa42b8cd2f34cecc472ab",
+5 -2
View File
@@ -15,7 +15,8 @@
Regression Verilator firmware simulations override the profiles' manual
`bringup` default with `APP=ci_smoke`. This application verifies UART output,
archinfo APB readback, RNG integration, SDRAM 8/16/32-bit mapped-window access,
archinfo APB readback, on-chip SRAM first/last-word and 8/16/32-bit access,
RNG integration, SDRAM 8/16/32-bit mapped-window access,
and test-status completion within the CI time budget;
`bringup` retains the full automatic application-information report for
manual runs.
@@ -41,7 +42,9 @@ completing the source-specific characterization and NIST SP 800-90B evidence.
OpenSTA runs a reproducible core-STA baseline for every CI PDK: IHP130 uses
`slow_1p08V_125C`, GF180 uses `ss_125C_4v50`, ICS55 uses the H7CR
`ss_1p08_125C` view, and SKY130 uses `ss_100C_1v40`.
`ss_1p08_125C` view, and SKY130 uses `ss_100C_1v40`. Enabled SRAM macros use
matching slow-corner Liberty views; the SKY130 SRAM view is analytically
generated by the locked OpenRAM artifact.
The SDC is generated from `rtl/mini/integration/clock_reset_domains.json`, applies
the external, audio, and DVP clocks at their technology-buffer observation pins,
and groups unrelated clocks asynchronously. It intentionally excludes package,
+20 -9
View File
@@ -37,10 +37,10 @@ cost-effective in the current one-owner-per-target fabric.
## Configuration and address contract
`SRAM_SIZE_KIB` is part of the reproducible configuration digest and accepts
only `4`, `16`, `32`, `64`, or `128`. Every committed profile sets `128`
explicitly to preserve the prior address map. The address-map generator uses
the selected value for RTL decode macros, SDK base/size/end constants, and the
linker `LENGTH(SRAM)`.
only `4`, `16`, `32`, `64`, or `128`. IHP130, GF180, and SKY130 CI profiles
select `32`; ICS55 keeps the SRAM absent. IHP130 benchmark/CoreMark profiles
retain `128`. The address-map generator uses the selected value for RTL decode
macros, SDK base/size/end constants, and the linker `LENGTH(SRAM)`.
`HAVE_SRAM_IF=YES` marks the memory present and elaborates the bank array. When
it is `NO`, the AXI target returns `DECERR`; the APB window remains readable
@@ -111,10 +111,18 @@ the register parity test prevents drift without adding a generator.
## Technology mapping
`tc_sram_1024x32` is the stable technology boundary. Behavioral simulation
uses an inferred 1024-by-32 array. IHP130 maps to
`RM_IHPSG13_1P_1024x32_c2_bm_bist`; the former 1024-by-64 mapping and discarded
upper half are removed. ICS55 continues to map to its 1024-by-32 wrapper. Bank
count, rather than technology depth or width, implements every product size.
uses the Common byte-mask RAM model. IHP130 maps to
`RM_IHPSG13_1P_1024x32_c2_bm_bist`. GF180 composes four byte lanes across two
`gf180mcu_fd_ip_sram__sram512x8m8wm1` depth halves and registers the address
high bit for synchronous read selection. SKY130 maps to the OpenRAM-generated
`sky130_sram_4kbyte_1rw_32x1024_8`. Its Liberty-compatible logical boundary
exports 32 data bits and hides OpenRAM's required physical spare column; a
future physical adapter must tie the spare enable and spare input low. The
required spare row expands the macro address port to 11 bits, so the wrapper
ties its high bit low and exposes exactly logical addresses 0 through 1023.
ICS55 retains its existing mapping but its supported profiles keep the macro
disabled. Bank count, rather than technology depth or width, implements every
product size.
The wrapper preserves byte masks and one-cycle synchronous reads. PDK timing,
LEF/GDS placement, power hookup, BIST pins, and foundry signoff remain
@@ -125,7 +133,10 @@ physical-flow responsibilities.
`tests/test_onchip_ram.py` elaborates all five capacities and covers bank
boundaries, first/last words, byte/half/word masks, `FIXED`, `INCR`, `WRAP`,
16-beat continuous traffic, backpressure, AR/AW priority, absent-memory and
malformed accesses, counters, and IHP130/ICS55 x32 wrapper polarity and masks.
malformed accesses, counters, all four PDK wrapper polarities and masks, and
the GF180 511/512 depth boundary. The setup tests independently validate the
SKY130 artifact manifest and generated-file hashes. Full Verilator regression
firmware exercises the first and last locations through the SoC interconnect.
`make CONFIG=configs/ci/ihp130.mk formal-onchip-ram` runs a 20-cycle bounded
proof and cover set for AXI stability, channel exclusion, response bounds,
+32
View File
@@ -0,0 +1,32 @@
BSD 3-Clause License
Copyright (c) 2019, Regents of the University of California and The Board of
Regents for the Oklahoma Agricultural and Mechanical College (acting for and
on behalf of Oklahoma State University)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
+5
View File
@@ -7,3 +7,8 @@ and `ATTRIBUTIONS.md`.
Do not replace, remove, or rewrite a third-party notice when updating a
dependency. Add the applicable license and attribution material in the same
review as a new bundled component, and preserve upstream license text exactly.
The SKY130 OpenRAM SRAM dependency uses
[`OpenRAM-BSD-3-Clause`](OpenRAM-BSD-3-Clause) and
[`SKY130-OpenRAM-Mixed-NOTICE`](SKY130-OpenRAM-Mixed-NOTICE). Its downloaded
artifact also contains the exact Apache-2.0 SkyWater build-space license.
+18
View File
@@ -0,0 +1,18 @@
SKY130 OpenRAM SRAM Artifact Notice
The checksum-locked SKY130 SRAM archive referenced by
dependencies/dependencies.lock.json is generated by OpenRAM using the
SkyWater-provided SKY130 SRAM build-space.
OpenRAM source:
https://github.com/VLSIDA/OpenRAM
BSD-3-Clause; the exact license text is retained in this directory and in the
artifact as LICENSE.OpenRAM-BSD-3-Clause.
SkyWater SRAM build-space source:
https://github.com/VLSIDA/sky130_fd_bd_sram
Apache-2.0; the exact upstream license text is retained in the artifact as
LICENSE.SkyWater-Apache-2.0.
The artifact manifest records the exact source revisions, logical geometry,
characterization corners, generation policy, and generated-file checksums.
+1 -1
View File
@@ -158,4 +158,4 @@ librelane-package:
+$(MAKE) LIBRELANE_TARGET=chip librelane-package-run
librelane-core-package:
+$(MAKE) LIBRELANE_TARGET=core librelane-package-run
+$(MAKE) LIBRELANE_TARGET=core librelane-package-run
+21 -3
View File
@@ -21,10 +21,13 @@ self-feedback models without modifying the vendor checkout. GF180 assembles its
locked `gf180mcu_fd_sc_mcu7t5v0` Liberty fragments and the two SoC-used IO
cells (`in_c` and `bi_t`) at both its TT and core-STA slow corners. SKY130
generates its `sky130_fd_sc_hd` Liberty model from locked upstream JSON files
at TT and slow corners. Derived Liberty files are stored below
at TT and slow corners. It also downloads the checksum-locked OpenRAM
`sky130_sram_4kbyte_1rw_32x1024_8` artifact and validates its source manifest,
logical geometry, generated-file hashes, TT 1.8 V/25 C Liberty, and SS
1.4 V/100 C Liberty. Derived and generated views are stored below
`.cache/retrosoc/pdk/` and are not committed. SKY130 setup initializes only
the locked HD library submodule; the remaining upstream libraries are outside
the SoC integration boundary.
the locked HD library submodule; OpenRAM's build-space and Ciel inputs are
recorded in the macro artifact rather than added to the SoC PDK checkout.
The IHP130 dependency is also the technology source for the independent
`physical/librelane/` core-hardening and full-chip pad-ring flows. The locked
@@ -58,3 +61,18 @@ unsupported switch primitives and drive strengths. Synthesis still preserves
the original PDK cell instances. GF180 netlist simulation also substitutes
pin-compatible sequential-cell models because the upstream functional models
propagate an unconnected synthesized notifier as an unknown value.
GF180 implements each logical `1024 x 32` SRAM bank with eight public
`512 x 8` macros: four byte lanes across two address-depth halves. SKY130 uses
one OpenRAM logical `1024 x 32` macro per bank. The Liberty-compatible wrapper
hides its required physical spare column, and its spare row remains outside the
logical address space. A future physical adapter must tie the spare enable and
spare input low. The wrapper also fixes the generated 11-bit address port's
high bit low to expose words 0 through 1023. Fast logical models are used for
RTL and functional netlist simulation, while Yosys and OpenSTA consume the
real macro Liberty views.
The SKY130 artifact uses analytical characterization and does not gate release
on OpenRAM DRC/LVS. Its LEF/GDS/SPICE files are development collateral, not
foundry-qualified or production-signoff evidence. GF180/SKY130 still lack
project-owned full-chip placement, extraction, DRC, and LVS adapters.
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import os
import shutil
import sys
import tempfile
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
from scripts.install_toolchain import safe_extract # noqa: E402
from scripts.setup_helpers import atomic_write, sha256 # noqa: E402
MACRO = "sky130_sram_4kbyte_1rw_32x1024_8"
OPENRAM_COMMIT = "b2b069ce119d1488cbe6883b2240bceb5c7ce29a"
INPUT_CONFIG_SHA256 = "24dfd2f0371c6007015ccd825d13f5227a5cc7ffb158bc0ce35d4a62aa2fc646"
SKY130_CIEL_REVISION = "e8294524e5f67c533c5d0c3afa0bcc5b2a5fa066"
SKY130_BUILDSPACE_REVISION = "dd64256961317205343a3fd446908b42bafba388"
EXPECTED_GEOMETRY = {
"words": 1024,
"data_bits": 32,
"write_granularity_bits": 8,
"read_write_ports": 1,
"spare_rows": 1,
"spare_columns": 1,
}
EXPECTED_CORNERS = ["TT_1p8V_25C", "SS_1p4V_100C"]
REQUIRED_FILES = (
f"{MACRO}.v",
f"{MACRO}.lef",
f"{MACRO}.gds",
f"{MACRO}.sp",
f"{MACRO}.lvs.sp",
f"{MACRO}.html",
f"{MACRO}.py",
f"{MACRO}_TT_1p8V_25C.lib",
f"{MACRO}_SS_1p4V_100C.lib",
"INPUT_CONFIG.py",
"LICENSE.OpenRAM-BSD-3-Clause",
"LICENSE.SkyWater-Apache-2.0",
"SHA256SUMS",
"manifest.json",
)
def load_manifest(source: Path) -> dict[str, Any]:
manifest_path = source / "manifest.json"
try:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise RuntimeError(f"cannot read SKY130 SRAM manifest: {error}") from error
if not isinstance(manifest, dict):
raise RuntimeError("SKY130 SRAM manifest must be an object")
return manifest
def validate_generated_sram(source: Path) -> None:
missing = [name for name in REQUIRED_FILES if not (source / name).is_file()]
if missing:
raise RuntimeError("SKY130 SRAM archive is missing: " + ", ".join(missing))
manifest = load_manifest(source)
expected = {
"schema_version": 1,
"macro": MACRO,
"geometry": EXPECTED_GEOMETRY,
"corners": EXPECTED_CORNERS,
"openram_commit": OPENRAM_COMMIT,
"input_config_sha256": INPUT_CONFIG_SHA256,
"sky130_ciel_revision": SKY130_CIEL_REVISION,
"sky130_buildspace_revision": SKY130_BUILDSPACE_REVISION,
"analytical_characterization": True,
"drc_lvs_checked": False,
}
for key, value in expected.items():
if manifest.get(key) != value:
raise RuntimeError(
f"SKY130 SRAM manifest {key}={manifest.get(key)!r}, expected {value!r}"
)
file_hashes = manifest.get("files")
if not isinstance(file_hashes, dict) or not file_hashes:
raise RuntimeError("SKY130 SRAM manifest has no generated-file hashes")
for name, expected_hash in file_hashes.items():
path = source / name
if not isinstance(name, str) or not isinstance(expected_hash, str) or not path.is_file():
raise RuntimeError(f"invalid SKY130 SRAM manifest file entry: {name!r}")
actual_hash = sha256(path)
if actual_hash != expected_hash:
raise RuntimeError(
f"SKY130 SRAM generated-file checksum mismatch for {name}: "
f"{actual_hash}, expected {expected_hash}"
)
def prepare(archive: Path, output_dir: Path, archive_sha256: str) -> None:
complete = output_dir / ".complete"
if complete.is_file() and complete.read_text(encoding="utf-8").strip() == archive_sha256:
validate_generated_sram(output_dir)
print(f"SKY130 OpenRAM SRAM is ready: {output_dir}")
return
output_dir.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix="sky130-openram-", dir=output_dir.parent) as temp:
extracted = Path(temp) / "extracted"
extracted.mkdir()
safe_extract(archive, extracted)
validate_generated_sram(extracted)
atomic_write(extracted / ".complete", archive_sha256 + "\n")
candidate = Path(temp) / "candidate"
os.replace(extracted, candidate)
if output_dir.exists():
shutil.rmtree(output_dir)
os.replace(candidate, output_dir)
print(f"SKY130 OpenRAM SRAM is ready: {output_dir}")
def main() -> int:
parser = argparse.ArgumentParser(description="Prepare the locked SKY130 OpenRAM SRAM views")
parser.add_argument("--archive", type=Path, required=True)
parser.add_argument("--output-dir", type=Path, required=True)
parser.add_argument("--archive-sha256", required=True)
args = parser.parse_args()
prepare(args.archive.resolve(), args.output_dir.resolve(), args.archive_sha256)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+22
View File
@@ -102,6 +102,28 @@ def main() -> int:
),
check=True,
)
sram = archive("sky130_openram_sram_4kbyte_1rw_32x1024_8")
sram_archive = ROOT / sram["destination"]
download_file(
sram["url"],
sram_archive,
sram["sha256"],
update=args.update,
timeout=120,
)
subprocess.run(
(
sys.executable,
str(PDK_DIR / "prepare_sky130_sram.py"),
"--archive",
str(sram_archive),
"--output-dir",
str(ROOT / ".cache/retrosoc/pdk/sky130/openram"),
"--archive-sha256",
sram["sha256"],
),
check=True,
)
subprocess.run(
(
sys.executable,
+8
View File
@@ -26,6 +26,14 @@ sta: $(OPENSTA_SDC) | manifest
echo "OpenSTA netlist configuration does not match PDK=$(PDK); rerun synthesis" >&2; \
exit 1; \
}
@grep -qx 'HAVE_SRAM_MACRO=$(HAVE_SRAM_MACRO)' $(OPENSTA_CONFIG) || { \
echo "OpenSTA netlist SRAM macro configuration mismatch; rerun synthesis" >&2; \
exit 1; \
}
@grep -qx 'SRAM_SIZE_KIB=$(SRAM_SIZE_KIB)' $(OPENSTA_CONFIG) || { \
echo "OpenSTA netlist SRAM capacity mismatch; rerun synthesis" >&2; \
exit 1; \
}
python3 $(ROOT_PATH)/scripts/run_flow.py --tool opensta --log $(OPENSTA_LOG) \
--result $(STA_BUILD_ROOT)/result-sta.json \
--env OPENSTA_NETLIST=$(OPENSTA_NETLIST) --env OPENSTA_LIBERTY=$(OPENSTA_LIBERTY) \
+6
View File
@@ -14,9 +14,15 @@ endif
else ifeq ($(PDK),GF180)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/gf180/gf180mcu_fd_sc_mcu7t5v0__ss_125C_4v50.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/.cache/retrosoc/pdk/gf180/gf180mcu_fd_io_retrosoc__ss_125C_4v50.lib
ifeq ($(HAVE_SRAM_MACRO),YES)
OPENSTA_SRAM_LIBS := $(ROOT_PATH)/physical/pdk/gf180mcu-pdk/macros/gf180mcu_fd_ip_sram/latest/cells/gf180mcu_fd_ip_sram__sram512x8m8wm1/gf180mcu_fd_ip_sram__sram512x8m8wm1__ss_125C_4v50.lib
endif
else ifeq ($(PDK),SKY130)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/sky130/sky130_fd_sc_hd__ss_100C_1v40.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/physical/smoke/sta/opensta/sky130_io_blackbox.liberty
ifeq ($(HAVE_SRAM_MACRO),YES)
OPENSTA_SRAM_LIBS := $(ROOT_PATH)/.cache/retrosoc/pdk/sky130/openram/sky130_sram_4kbyte_1rw_32x1024_8_SS_1p4V_100C.lib
endif
else ifeq ($(PDK),ICS55)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/ics55/ics55_h7cr_ss.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/physical/pdk/icsprout55-pdk/IP/IO/ICsprout_55LLULP1233_IO_251013/liberty/ICSIOA_N55_3P3_ss_1p08_2p97_125c.lib
+16 -1
View File
@@ -22,13 +22,20 @@ set root_dir [file normalize [file join [file dirname [info script]] ../../../..
puts "0. Executing init_tech: load technology from Github PDK"
set have_sram_macro [expr {
[info exists ::env(HAVE_SRAM_MACRO)] && $::env(HAVE_SRAM_MACRO) eq "YES"
}]
if {$pdk == "IHP130"} {
set pdk_dir "$root_dir/physical/pdk/IHP-Open-PDK"
set pdk_cells_lib ${pdk_dir}/ihp-sg13g2/libs.ref/sg13g2_stdcell/lib
set pdk_sram_lib ${pdk_dir}/ihp-sg13g2/libs.ref/sg13g2_sram/lib
set pdk_io_lib ${pdk_dir}/ihp-sg13g2/libs.ref/sg13g2_io/lib
set tech_cells [list "$pdk_cells_lib/sg13g2_stdcell_typ_1p20V_25C.lib"]
set tech_macros [glob -nocomplain -directory $pdk_sram_lib *_typ_1p20V_25C.lib]
set tech_macros [list]
if {$have_sram_macro} {
set tech_macros [glob -nocomplain -directory $pdk_sram_lib *_typ_1p20V_25C.lib]
}
lappend tech_macros "$pdk_io_lib/sg13g2_io_typ_1p2V_3p3V_25C.lib"
# for hilomap
set tech_cell_tiehi {sg13g2_tiehi L_HI}
@@ -47,9 +54,13 @@ if {$pdk == "IHP130"} {
set abc_driver BUFX4H7R
set abc_load 6.0
} elseif {$pdk == "GF180"} {
set pdk_sram_lib "$root_dir/physical/pdk/gf180mcu-pdk/macros/gf180mcu_fd_ip_sram/latest/cells/gf180mcu_fd_ip_sram__sram512x8m8wm1"
set pdk_cells_lib "$root_dir/.cache/retrosoc/pdk/gf180"
set tech_cells [list "$pdk_cells_lib/gf180mcu_fd_sc_mcu7t5v0__tt_025C_5v00.lib"]
set tech_macros [list]
if {$have_sram_macro} {
lappend tech_macros "$pdk_sram_lib/gf180mcu_fd_ip_sram__sram512x8m8wm1__tt_025C_5v00.lib"
}
# The aggregate GF180 Liberty does not characterize the available tie cells.
# Keep constants as logic instead of mapping them to an uncharacterized cell.
set tech_cell_tiehi {}
@@ -58,8 +69,12 @@ if {$pdk == "IHP130"} {
set abc_load 13.43
} elseif {$pdk == "SKY130"} {
set pdk_cells_lib "$root_dir/.cache/retrosoc/pdk/sky130"
set pdk_sram_lib "$pdk_cells_lib/openram"
set tech_cells [list "$pdk_cells_lib/sky130_fd_sc_hd__tt_025C_1v80.lib"]
set tech_macros [list]
if {$have_sram_macro} {
lappend tech_macros "$pdk_sram_lib/sky130_sram_4kbyte_1rw_32x1024_8_TT_1p8V_25C.lib"
}
set tech_cell_tiehi {sky130_fd_sc_hd__conb_1 HI}
set tech_cell_tielo {sky130_fd_sc_hd__conb_1 LO}
set abc_driver sky130_fd_sc_hd__buf_1
@@ -170,5 +170,7 @@ puts $config_file "SOC=$soc"
puts $config_file "TOP_DESIGN=$top_design"
puts $config_file "SYNTH_RECIPE=$synth_recipe"
puts $config_file "PERIOD_PS=$period_ps"
puts $config_file "HAVE_SRAM_MACRO=$::env(HAVE_SRAM_MACRO)"
puts $config_file "SRAM_SIZE_KIB=$::env(SRAM_SIZE_KIB)"
close $config_file
file rename -force $config_tmp $config
+1
View File
@@ -64,6 +64,7 @@ $(NETLIST): $(SV_FLIST) $(YOSYS_SCRIPTS)
python3 $(ROOT_PATH)/scripts/run_flow.py --tool yosys \
--log $(YOSYS_BUILD)/$(RTL_NAME).log --result $(YOSYS_BUILD)/result-synth.json \
--env PDK=$(PDK) --env SOC=$(SOC) --env SYNTH_RECIPE=$(SYNTH_RECIPE) \
--env HAVE_SRAM_MACRO=$(HAVE_SRAM_MACRO) --env SRAM_SIZE_KIB=$(SRAM_SIZE_KIB) \
--env YOSYS_TARGET_PERIOD_PS=$(YOSYS_TARGET_PERIOD_PS) \
--env SV_FLIST=$(SV_FLIST) --env TOP_DESIGN=$(TOP_DESIGN) --env CONFIG=$(NETLIST_CONFIG) \
--env PROJ_NAME=$(RTL_NAME) --env WORK=$(YOSYS_TMP) --env BUILD=$(YOSYS_OUT) \
+1
View File
@@ -2,6 +2,7 @@
# Technology wrappers instantiate these models directly in RTL simulation.
/tech/gf180_io_sim_cells.v
/tech/gf180_seq_sim_cells.v
/tech/gf180_sram_sim_cells.sv
/physical/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/clkbuf/gf180mcu_fd_sc_mcu7t5v0__clkbuf_1.functional.v
/physical/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/clkinv/gf180mcu_fd_sc_mcu7t5v0__clkinv_1.functional.v
/physical/pdk/gf180mcu-pdk/libraries/gf180mcu_fd_sc_mcu7t5v0/latest/cells/mux2/gf180mcu_fd_sc_mcu7t5v0__mux2_1.functional.v
+2
View File
@@ -6,6 +6,8 @@
+incdir+/physical/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/mux2
+incdir+/physical/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/xor2
/tech/sky130_io_sim_cells.v
/clusterip/common/rtl/tech/ram.sv
/tech/sky130_sram_sim_cells.sv
/physical/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/clkbuf/sky130_fd_sc_hd__clkbuf_1.v
/physical/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/clkinv/sky130_fd_sc_hd__clkinv_1.v
/physical/pdk/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/mux2/sky130_fd_sc_hd__mux2_1.v
+2 -1
View File
@@ -111,7 +111,8 @@ sim: comp
debug-sim: comp firmware
@test "$(SIMU)" = VERILATOR || { echo "debug-sim requires SIMU=VERILATOR" >&2; exit 2; }
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/doctor.py --root $(ROOT_PATH) --simu $(SIMU) \
--synth $(SYNTH) --sta $(STA) --pdk $(PDK) --formal $(FORMAL) \
--synth $(SYNTH) --sta $(STA) --pdk $(PDK) --have-sram-macro $(HAVE_SRAM_MACRO) \
--formal $(FORMAL) \
--require-debug-tools --lock $(LOCK_FILE)
python3 $(DEBUG_SIM_SESSION) \
--emulator $(VERILATOR_EMU) \
+42
View File
@@ -0,0 +1,42 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
// Pin-compatible functional model for the GF180 512x8 bit-write SRAM macro.
/* verilator lint_off DECLFILENAME */
module gf180mcu_fd_ip_sram__sram512x8m8wm1 (
// verilog_format: off -- preserve the PDK memory interface columns
input logic CLK,
input logic CEN,
input logic GWEN,
input logic [7:0] WEN,
input logic [8:0] A,
input logic [7:0] D,
output logic [7:0] Q,
inout wire VDD,
inout wire VSS
// verilog_format: on
);
logic [7:0] s_storage_q[0:511];
always_ff @(posedge CLK) begin
if (!CEN && (VDD !== 1'b0) && (VSS !== 1'b1)) begin
if (GWEN) begin
Q <= s_storage_q[A];
end else begin
for (int unsigned bit_index = 0; bit_index < 8; bit_index++) begin
if (!WEN[bit_index]) begin
s_storage_q[A][bit_index] <= D[bit_index];
end
end
end
end
end
endmodule
/* verilator lint_on DECLFILENAME */
+49
View File
@@ -0,0 +1,49 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
// Liberty-compatible fast model for the OpenRAM-generated SKY130 1024x32 macro.
/* verilator lint_off DECLFILENAME */
module sky130_sram_4kbyte_1rw_32x1024_8 (
// verilog_format: off -- preserve the OpenRAM memory interface columns
`ifdef USE_POWER_PINS
inout wire vccd1,
inout wire vssd1,
`endif
input logic clk0,
input logic csb0,
input logic web0,
input logic [ 3:0] wmask0,
input logic [10:0] addr0,
input logic [31:0] din0,
output logic [31:0] dout0
// verilog_format: on
);
logic s_disable;
`ifdef USE_POWER_PINS
assign s_disable = csb0 || !vccd1 || vssd1;
`else
assign s_disable = csb0;
`endif
tech_ram_bm #(
.BIT_WIDTH (32),
.WORD_DEPTH(1024)
) u_tech_ram_bm (
.clk_i (clk0),
.en_i (s_disable),
.wen_i (web0),
.bm_i (~wmask0),
.addr_i(addr0[9:0]),
.dat_i (din0),
.dat_o (dout0)
);
endmodule
/* verilator lint_on DECLFILENAME */
+86 -17
View File
@@ -9,6 +9,7 @@
// See the Mulan PSL v2 for more details.
module tc_sram_1024x32 (
// verilog_format: off -- preserve the technology memory interface columns
input logic clk_i,
input logic cs_i,
input logic [ 9:0] addr_i,
@@ -16,23 +17,24 @@ module tc_sram_1024x32 (
input logic [ 3:0] mask_i,
input logic wren_i,
output logic [31:0] data_o
// verilog_format: on
);
`ifdef PDK_BEHAV
logic [31:0] r_data;
logic [31:0] mem [0:1023];
logic [31:0] s_data_q;
logic [31:0] s_storage_q[0:1023];
assign data_o = r_data;
assign data_o = s_data_q;
always_ff @(posedge clk_i) begin
if (cs_i) begin
if (!wren_i) begin
r_data <= mem[addr_i];
s_data_q <= s_storage_q[addr_i];
end else begin
if (mask_i[0]) mem[addr_i][7:0] <= data_i[7:0];
if (mask_i[1]) mem[addr_i][15:8] <= data_i[15:8];
if (mask_i[2]) mem[addr_i][23:16] <= data_i[23:16];
if (mask_i[3]) mem[addr_i][31:24] <= data_i[31:24];
r_data <= 32'bx;
if (mask_i[0]) s_storage_q[addr_i][7:0] <= data_i[7:0];
if (mask_i[1]) s_storage_q[addr_i][15:8] <= data_i[15:8];
if (mask_i[2]) s_storage_q[addr_i][23:16] <= data_i[23:16];
if (mask_i[3]) s_storage_q[addr_i][31:24] <= data_i[31:24];
s_data_q <= 32'bx;
end
end
end
@@ -48,14 +50,81 @@ module tc_sram_1024x32 (
.A_DIN (data_i),
.A_DOUT (data_o),
.A_DLY (1'b1),
.A_BIST_CLK ('0),
.A_BIST_EN ('0),
.A_BIST_MEN ('0),
.A_BIST_WEN ('0),
.A_BIST_REN ('0),
.A_BIST_ADDR('0),
.A_BIST_DIN ('0),
.A_BIST_BM ('0)
.A_BIST_CLK (1'b0),
.A_BIST_EN (1'b0),
.A_BIST_MEN (1'b0),
.A_BIST_WEN (1'b0),
.A_BIST_REN (1'b0),
.A_BIST_ADDR(10'b0),
.A_BIST_DIN (32'b0),
.A_BIST_BM (32'b0)
);
`endif
`elsif PDK_GF180
`ifdef HAVE_SRAM_MACRO
logic s_read_depth_q;
logic [1:0][3:0][7:0] s_macro_data_o;
`ifndef SYNTHESIS
wire s_vdd = 1'b1;
wire s_vss = 1'b0;
`endif
dffl #(
.DATA_WIDTH(1)
) u_read_depth_dffl (
.clk_i(clk_i),
.en_i (cs_i && !wren_i),
.dat_i(addr_i[9]),
.dat_o(s_read_depth_q)
);
for (genvar depth = 0; depth < 2; depth++) begin : gen_depth
for (genvar byte_index = 0; byte_index < 4; byte_index++) begin : gen_byte
// verilog_format: off -- preserve conditional PDK power-pin connections
gf180mcu_fd_ip_sram__sram512x8m8wm1 u_sram (
.CLK (clk_i),
.CEN (~(cs_i && (addr_i[9] == 1'(depth)))),
.GWEN(~wren_i),
.WEN (~{8{mask_i[byte_index]}}),
.A (addr_i[8:0]),
.D (data_i[byte_index*8+:8]),
`ifndef SYNTHESIS
.Q (s_macro_data_o[depth][byte_index]),
.VDD (s_vdd),
.VSS (s_vss)
`else
.Q (s_macro_data_o[depth][byte_index])
`endif
);
// verilog_format: on
end
end
assign data_o = {
s_macro_data_o[s_read_depth_q][3],
s_macro_data_o[s_read_depth_q][2],
s_macro_data_o[s_read_depth_q][1],
s_macro_data_o[s_read_depth_q][0]
};
`endif
`elsif PDK_SKY130
`ifdef HAVE_SRAM_MACRO
`ifdef USE_POWER_PINS
wire s_vccd1 = 1'b1;
wire s_vssd1 = 1'b0;
`endif
sky130_sram_4kbyte_1rw_32x1024_8 u_sram (
`ifdef USE_POWER_PINS
.vccd1 (s_vccd1),
.vssd1 (s_vssd1),
`endif
.clk0 (clk_i),
.csb0 (~cs_i),
.web0 (~wren_i),
.wmask0(mask_i),
.addr0 ({1'b0, addr_i}),
.din0 (data_i),
.dout0 (data_o)
);
`endif
`elsif PDK_S110
+3
View File
@@ -23,6 +23,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--synth", required=True)
parser.add_argument("--sta", required=True)
parser.add_argument("--pdk", required=True)
parser.add_argument("--have-sram-macro", choices=("YES", "NO"), default="NO")
parser.add_argument("--formal", choices=("YES", "NO"), default="NO")
parser.add_argument("--require-debug-tools", action="store_true")
parser.add_argument("--lock", type=Path, default=DEFAULT_LOCK)
@@ -81,6 +82,8 @@ def main() -> int:
if args.pdk in pdk_names:
source_names.append(pdk_names[args.pdk])
paths = {name: root / lock["sources"][name]["destination"] for name in source_names}
if args.pdk == "SKY130" and args.have_sram_macro == "YES":
paths["sky130_openram_sram"] = root / ".cache/retrosoc/pdk/sky130/openram"
tool_results = {
tool: {"path": shutil.which(tool), "version": version(tool)}
for tool in dict.fromkeys(tools)
+22
View File
@@ -53,10 +53,32 @@ module tc_sram_1024x32_tb;
end
endtask
task automatic expect_read_hold(input logic [9:0] idle_address, input logic [31:0] expected);
begin
@(negedge clk_i);
cs_i = 1'b0;
addr_i = idle_address;
@(posedge clk_i);
@(negedge clk_i);
if (data_o !== expected) begin
$fatal(1, "1024x32 SRAM held data=%08x expected=%08x", data_o, expected);
end
end
endtask
initial begin
write_word(10'h000, 32'hCAFE_BABE, 4'hF);
write_word(10'h155, 32'h1122_3344, 4'hF);
write_word(10'h155, 32'hAABB_CCDD, 4'b0101);
write_word(10'h1FF, 32'h0102_0304, 4'hF);
write_word(10'h200, 32'h1122_3344, 4'hF);
write_word(10'h3FF, 32'hA5A5_5A5A, 4'hF);
read_word(10'h000, 32'hCAFE_BABE);
read_word(10'h155, 32'h11BB_33DD);
read_word(10'h1FF, 32'h0102_0304);
expect_read_hold(10'h200, 32'h0102_0304);
read_word(10'h200, 32'h1122_3344);
read_word(10'h3FF, 32'hA5A5_5A5A);
$display("1024x32 SRAM wrapper test passed");
$finish;
end
+55
View File
@@ -61,6 +61,7 @@ def test_onchip_ram_axi4_capacity_protocol_and_performance(
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
str(ROOT / "rtl/mini/top/onchip_ram.sv"),
@@ -122,6 +123,7 @@ def test_absent_onchip_ram_reports_capability_and_decode_errors(tmp_path: Path)
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/apb4_if.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/interface/axi4_addr_gen.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
str(ROOT / "rtl/mini/top/onchip_ram.sv"),
@@ -235,6 +237,58 @@ def test_ics55_wrapper_preserves_active_low_controls_and_byte_masks(tmp_path: Pa
assert "1024x32 SRAM wrapper test passed" in result.stdout
@pytest.mark.parametrize(
("pdk", "model"),
(
("GF180", "gf180_sram_sim_cells.sv"),
("SKY130", "sky130_sram_sim_cells.sv"),
),
)
def test_open_pdk_sram_wrappers_preserve_depth_and_byte_masks(
tmp_path: Path, pdk: str, model: str
) -> None:
verilator = shutil.which("verilator")
if verilator is None:
return
output = tmp_path / f"tc_sram_{pdk.lower()}"
ccache_tmp = tmp_path / f"ccache-{pdk.lower()}"
ccache_tmp.mkdir()
subprocess.run(
[
verilator,
"--binary",
"--timing",
"-Wno-fatal",
"--top-module",
"tc_sram_1024x32_tb",
f"+define+PDK_{pdk}",
"+define+HAVE_SRAM_MACRO",
"+define+SV_ASSRT_DISABLE",
"-I" + str(ROOT / "rtl/managed/clusterip/common/rtl"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"),
str(ROOT / "rtl/tech" / model),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "tests/rtl/tc_sram_1024x32_tb.sv"),
"-Mdir",
str(tmp_path / f"obj-{pdk.lower()}"),
"-o",
str(output),
],
check=True,
text=True,
capture_output=True,
env={
**os.environ,
"CCACHE_DIR": str(ccache_tmp),
"CCACHE_TEMPDIR": str(ccache_tmp),
},
)
result = subprocess.run([output], check=True, text=True, capture_output=True)
assert "1024x32 SRAM wrapper test passed" in result.stdout
def test_native_axi4_burst_reaches_peak_and_removes_bridge_overhead(tmp_path: Path) -> None:
verilator = shutil.which("verilator")
if verilator is None:
@@ -280,6 +334,7 @@ def test_native_axi4_burst_reaches_peak_and_removes_bridge_overhead(tmp_path: Pa
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/xchecker.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/utils/spill_register.sv"),
str(ROOT / "rtl/managed/clusterip/common/rtl/tech/ram.sv"),
str(ROOT / "rtl/tech/tc_sram.sv"),
str(ROOT / "rtl/mini/top/axi42ram.sv"),
str(ROOT / "rtl/mini/top/onchip_ram_reg.sv"),
+17
View File
@@ -1025,10 +1025,27 @@ def test_benchmark_profile_uses_functional_sram_and_reserved_data() -> None:
assert "PDK_BEHAV HAVE_SVA" in makefile
assert "PDK_BEHAV=YES is for functional simulation" in makefile
assert re.search(r"^HAVE_SRAM_MACRO\s*:= YES$", profile, re.MULTILINE)
assert re.search(r"^SRAM_SIZE_KIB\s*:= 128$", profile, re.MULTILINE)
assert re.search(r"^PDK_BEHAV\s*:= YES$", profile, re.MULTILINE)
assert "RS_BENCHMARK_SRAM_OFFSET UINT32_C(0x10000)" in benchmark
def test_open_pdk_profiles_enable_32kib_macro_sram_and_ics55_stays_absent() -> None:
for pdk in ("ihp130", "gf180", "sky130"):
profile = (ROOT / f"configs/ci/{pdk}.mk").read_text(encoding="utf-8")
assert re.search(r"^HAVE_SRAM_IF\s*:= YES$", profile, re.MULTILINE)
assert re.search(r"^HAVE_SRAM_MACRO\s*:= YES$", profile, re.MULTILINE)
assert re.search(r"^SRAM_SIZE_KIB\s*:= 32$", profile, re.MULTILINE)
ics55 = (ROOT / "configs/ci/ics55.mk").read_text(encoding="utf-8")
assert re.search(r"^HAVE_SRAM_IF\s*:= NO$", ics55, re.MULTILINE)
assert re.search(r"^HAVE_SRAM_MACRO\s*:= NO$", ics55, re.MULTILINE)
for name in ("ihp130-hazard3", "ihp130-hazard3-coremark"):
benchmark = (ROOT / f"configs/benchmark/{name}.mk").read_text(encoding="utf-8")
assert re.search(r"^SRAM_SIZE_KIB\s*:= 128$", benchmark, re.MULTILINE)
def test_smoke_regression_uses_ihp130_behavioral_coverage_only() -> None:
commands, profiles = select_regression("smoke", "IHP130")
+76
View File
@@ -0,0 +1,76 @@
"""Tests for the locked SKY130 OpenRAM SRAM setup path."""
from __future__ import annotations
import hashlib
import io
import json
import tarfile
from pathlib import Path
import pytest
from physical.pdk.prepare_sky130_sram import (
EXPECTED_CORNERS,
EXPECTED_GEOMETRY,
INPUT_CONFIG_SHA256,
MACRO,
OPENRAM_COMMIT,
REQUIRED_FILES,
SKY130_BUILDSPACE_REVISION,
SKY130_CIEL_REVISION,
prepare,
)
def make_archive(path: Path, *, corners: list[str] | None = None) -> str:
payloads = {
name: f"generated {name}\n".encode()
for name in REQUIRED_FILES
if name not in {"manifest.json", "SHA256SUMS"}
}
manifest = {
"schema_version": 1,
"macro": MACRO,
"geometry": EXPECTED_GEOMETRY,
"corners": EXPECTED_CORNERS if corners is None else corners,
"openram_commit": OPENRAM_COMMIT,
"input_config_sha256": INPUT_CONFIG_SHA256,
"sky130_ciel_revision": SKY130_CIEL_REVISION,
"sky130_buildspace_revision": SKY130_BUILDSPACE_REVISION,
"analytical_characterization": True,
"drc_lvs_checked": False,
"files": {
name: hashlib.sha256(content).hexdigest() for name, content in payloads.items()
},
}
payloads["manifest.json"] = (json.dumps(manifest) + "\n").encode()
payloads["SHA256SUMS"] = b"fixture checksums\n"
with tarfile.open(path, "w:gz") as archive:
for name, content in payloads.items():
member = tarfile.TarInfo(name)
member.size = len(content)
archive.addfile(member, io.BytesIO(content))
return hashlib.sha256(path.read_bytes()).hexdigest()
def test_prepare_sky130_sram_is_verified_and_idempotent(tmp_path: Path) -> None:
archive = tmp_path / "sram.tar.gz"
digest = make_archive(archive)
output = tmp_path / "openram"
prepare(archive, output, digest)
first_mtime = (output / ".complete").stat().st_mtime_ns
prepare(archive, output, digest)
assert (output / f"{MACRO}.v").is_file()
assert (output / ".complete").read_text(encoding="utf-8").strip() == digest
assert (output / ".complete").stat().st_mtime_ns == first_mtime
def test_prepare_sky130_sram_rejects_wrong_corners(tmp_path: Path) -> None:
archive = tmp_path / "sram.tar.gz"
digest = make_archive(archive, corners=["TT_1p8V_25C"])
with pytest.raises(RuntimeError, match="corners"):
prepare(archive, tmp_path / "openram", digest)
+10
View File
@@ -93,6 +93,16 @@ def test_ihp130_core_sta_loads_usb2_packet_ram_liberties() -> None:
assert "OPENSTA_SRAM_LIBS := $(IHP130_USB2_SRAM_LIBS)" in makefile
def test_open_pdk_core_sta_uses_matching_sram_corners() -> None:
makefile = PDK_TIMING_MAKEFILE.read_text(encoding="utf-8")
opensta = OPENSTA_MAKEFILE.read_text(encoding="utf-8")
assert "gf180mcu_fd_ip_sram__sram512x8m8wm1__ss_125C_4v50.lib" in makefile
assert "sky130_sram_4kbyte_1rw_32x1024_8_SS_1p4V_100C.lib" in makefile
assert "HAVE_SRAM_MACRO=$(HAVE_SRAM_MACRO)" in opensta
assert "SRAM_SIZE_KIB=$(SRAM_SIZE_KIB)" in opensta
def test_core_sdc_rejects_pads_missing_from_the_pin_map(tmp_path: Path) -> None:
document = json.loads(DOMAINS.read_text(encoding="utf-8"))
document["domains"][3]["sta"]["source_port"] = "missing_pad"