mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
Merge branch 'feat/tiny' into dev
# Conflicts: # Makefile # README.md # publications/README.md # rtl/mk/software.mk # rtl/rtl_style_audit.json
This commit is contained in:
253 files changed
+15235
-501
No files matched your search
@@ -22,7 +22,7 @@ truth wins.
|
|||||||
records approved Required-rule deviations.
|
records approved Required-rule deviations.
|
||||||
- [crt/README.md](crt/README.md) defines the freestanding RISC-V runtime and
|
- [crt/README.md](crt/README.md) defines the freestanding RISC-V runtime and
|
||||||
SDK layout; [app/README.md](app/README.md) defines application composition.
|
SDK layout; [app/README.md](app/README.md) defines application composition.
|
||||||
- `Makefile`, `rtl/mini/mk/software.mk`, committed `configs/*.mk`, and the
|
- `Makefile`, `rtl/mk/software.mk`, committed `configs/*.mk`, and the
|
||||||
GitHub workflows define the active build and regression behavior.
|
GitHub workflows define the active build and regression behavior.
|
||||||
- `quality/embedded_c_policy.json`, `quality/warnings/`, and
|
- `quality/embedded_c_policy.json`, `quality/warnings/`, and
|
||||||
`quality/metrics/policy.json` define the active quality policy.
|
`quality/metrics/policy.json` define the active quality policy.
|
||||||
@@ -77,7 +77,9 @@ README that states its ownership, source of truth, and validation expectations.
|
|||||||
`bringup`, `debug`, and `shell`; each profile is declared in
|
`bringup`, `debug`, and `shell`; each profile is declared in
|
||||||
`app/apps/<name>/app.mk`. The `debug` application is an RTL debug-transport
|
`app/apps/<name>/app.mk`. The `debug` application is an RTL debug-transport
|
||||||
acceptance image, not a user-facing firmware profile.
|
acceptance image, not a user-facing firmware profile.
|
||||||
- Do not add new dependencies on `crt/inc`, retired `tiny` names, or legacy
|
- Tiny is a distinct wired MCU product under `rtl/tiny`; `SOC=TINY` selects its
|
||||||
|
IHP130 profile and AXI4/APB4 integration.
|
||||||
|
- Do not add new dependencies on `crt/inc`, retired SDK `tiny` names, or legacy
|
||||||
`rs_*.h`/`tiny*.h` include paths.
|
`rs_*.h`/`tiny*.h` include paths.
|
||||||
- Treat `app/coremark/coremark-main`, `app/fatfs/ff16`, `app/lvgl/lvgl-main`,
|
- Treat `app/coremark/coremark-main`, `app/fatfs/ff16`, `app/lvgl/lvgl-main`,
|
||||||
and managed ports as third-party or managed code. Update them through their
|
and managed ports as third-party or managed code. Update them through their
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ $(error HAVE_DEBUG has been removed; the Hazard3 Debug Module is always enabled)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
SOC ?= MINI
|
SOC ?= MINI
|
||||||
MINI_MODE ?= PRODUCT
|
MINI_MODE ?= $(if $(filter MINI,$(SOC)),PRODUCT,NONE)
|
||||||
SIMU ?= VCS
|
SIMU ?= VCS
|
||||||
SYNTH ?= NONE
|
SYNTH ?= NONE
|
||||||
SYNTH_RECIPE ?= balanced
|
SYNTH_RECIPE ?= balanced
|
||||||
@@ -44,7 +44,7 @@ SRAM_SIZE_KIB ?= $(if $(filter ICS55,$(PDK)),128,32)
|
|||||||
PDK_BEHAV ?= NO
|
PDK_BEHAV ?= NO
|
||||||
HAVE_SVA ?= NO
|
HAVE_SVA ?= NO
|
||||||
APU_ENABLE_P7 ?= NO
|
APU_ENABLE_P7 ?= NO
|
||||||
HAVE_HP ?= YES
|
HAVE_HP ?= $(if $(filter TINY,$(SOC)),NO,YES)
|
||||||
HP_CONFIG ?= rv64imafdc_zicbom_max
|
HP_CONFIG ?= rv64imafdc_zicbom_max
|
||||||
BUILD_RELEASE ?= NO
|
BUILD_RELEASE ?= NO
|
||||||
JTAG_IDCODE ?= DEADBEEF
|
JTAG_IDCODE ?= DEADBEEF
|
||||||
@@ -65,7 +65,7 @@ RTL_SIM_TIMEOUT ?= -1
|
|||||||
SIM_FIRMWARE_NAME ?= $(FIRMWARE_NAME)
|
SIM_FIRMWARE_NAME ?= $(FIRMWARE_NAME)
|
||||||
SIM_SUCCESS_MARKER ?= SIM_TEST_PASS
|
SIM_SUCCESS_MARKER ?= SIM_TEST_PASS
|
||||||
|
|
||||||
RTL_PATH := $(ROOT_PATH)/rtl/mini
|
RTL_PATH := $(ROOT_PATH)/rtl/$(shell printf '%s' '$(SOC)' | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
RTL_TOP ?= retrosoc_tb
|
RTL_TOP ?= retrosoc_tb
|
||||||
|
|
||||||
@@ -218,8 +218,8 @@ else
|
|||||||
FLOW_FILELIST_DIR := $(SIM_BUILD_ROOT)/filelists
|
FLOW_FILELIST_DIR := $(SIM_BUILD_ROOT)/filelists
|
||||||
endif
|
endif
|
||||||
|
|
||||||
VALID_SOC := MINI
|
VALID_SOC := MINI TINY
|
||||||
VALID_MINI_MODE := PRODUCT MPW
|
VALID_MINI_MODE := PRODUCT MPW NONE
|
||||||
VALID_SIMU := VCS VERILATOR IVERILOG
|
VALID_SIMU := VCS VERILATOR IVERILOG
|
||||||
VALID_SYNTH := NONE YOSYS
|
VALID_SYNTH := NONE YOSYS
|
||||||
VALID_SYNTH_RECIPE := balanced area speed
|
VALID_SYNTH_RECIPE := balanced area speed
|
||||||
@@ -282,11 +282,18 @@ $(error NPU_P5_ACCEPTANCE and NPU_P6_ACCEPTANCE are mutually exclusive)
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(SOC),MINI)
|
||||||
|
ifeq ($(MINI_MODE),NONE)
|
||||||
|
$(error SOC=MINI requires MINI_MODE=PRODUCT or MPW)
|
||||||
|
endif
|
||||||
ifeq ($(MINI_MODE),PRODUCT)
|
ifeq ($(MINI_MODE),PRODUCT)
|
||||||
ifneq ($(HAVE_HP),YES)
|
ifneq ($(HAVE_HP),YES)
|
||||||
$(error MINI_MODE=PRODUCT requires HAVE_HP=YES)
|
$(error MINI_MODE=PRODUCT requires HAVE_HP=YES)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
else
|
||||||
|
include $(ROOT_PATH)/rtl/tiny/mk/config.mk
|
||||||
|
endif
|
||||||
|
|
||||||
MISSING_LOCAL_RTL_FILES := $(filter-out $(wildcard $(LOCAL_RTL_FILES)),$(LOCAL_RTL_FILES))
|
MISSING_LOCAL_RTL_FILES := $(filter-out $(wildcard $(LOCAL_RTL_FILES)),$(LOCAL_RTL_FILES))
|
||||||
ifneq ($(strip $(MISSING_LOCAL_RTL_FILES)),)
|
ifneq ($(strip $(MISSING_LOCAL_RTL_FILES)),)
|
||||||
@@ -395,7 +402,9 @@ ifeq ($(APU_ENABLE_P7), YES)
|
|||||||
DEF_LIST += +define+APU_ENABLE_P7
|
DEF_LIST += +define+APU_ENABLE_P7
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(MINI_MODE), PRODUCT)
|
ifeq ($(SOC), TINY)
|
||||||
|
DEF_LIST += +define+RETROSOC_SOC__TINY
|
||||||
|
else ifeq ($(MINI_MODE), PRODUCT)
|
||||||
DEF_LIST += +define+MINI_PRODUCT
|
DEF_LIST += +define+MINI_PRODUCT
|
||||||
else
|
else
|
||||||
DEF_LIST += +define+MINI_MPW
|
DEF_LIST += +define+MINI_MPW
|
||||||
@@ -405,8 +414,10 @@ ifeq ($(SYNTH), YOSYS)
|
|||||||
DEF_LIST += +define+SYNTHESIS
|
DEF_LIST += +define+SYNTHESIS
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include rtl/mini/Makefile
|
include $(RTL_PATH)/Makefile
|
||||||
|
ifeq ($(SOC),MINI)
|
||||||
include rtl/mini/mk/formal.mk
|
include rtl/mini/mk/formal.mk
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(SIMU),IVERILOG)
|
ifeq ($(SIMU),IVERILOG)
|
||||||
PERF_LOG ?= $(IVERILOG_BEHV_DIR)/sim.log
|
PERF_LOG ?= $(IVERILOG_BEHV_DIR)/sim.log
|
||||||
@@ -416,15 +427,21 @@ endif
|
|||||||
|
|
||||||
ifeq ($(SYNTH), YOSYS)
|
ifeq ($(SYNTH), YOSYS)
|
||||||
include physical/smoke/syn/yosys/yosys.mk
|
include physical/smoke/syn/yosys/yosys.mk
|
||||||
|
ifeq ($(SOC),MINI)
|
||||||
include physical/smoke/syn/yosys/ga2d_block.mk
|
include physical/smoke/syn/yosys/ga2d_block.mk
|
||||||
include physical/smoke/syn/yosys/apu_block.mk
|
include physical/smoke/syn/yosys/apu_block.mk
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(STA), OPENSTA)
|
ifeq ($(STA), OPENSTA)
|
||||||
include physical/smoke/sta/opensta/opensta.mk
|
include physical/smoke/sta/opensta/opensta.mk
|
||||||
endif
|
endif
|
||||||
|
|
||||||
include physical/librelane/Makefile
|
ifeq ($(SOC),TINY)
|
||||||
|
include physical/librelane/tiny/Makefile
|
||||||
|
else
|
||||||
|
include physical/librelane/mini/Makefile
|
||||||
|
endif
|
||||||
include physical/ecc/Makefile
|
include physical/ecc/Makefile
|
||||||
|
|
||||||
.PHONY: crypto-p0-constants crypto-p0-rtl crypto-p0-baseline crypto-p0-report
|
.PHONY: crypto-p0-constants crypto-p0-rtl crypto-p0-baseline crypto-p0-report
|
||||||
@@ -486,7 +503,7 @@ crypto-p2-report: manifest
|
|||||||
apu-block-filelist apu-block-synth apu-block-sta apu-block-report apu-block-evidence apu-block-clean \
|
apu-block-filelist apu-block-synth apu-block-sta apu-block-report apu-block-evidence apu-block-clean \
|
||||||
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
|
pin-map check-pin-map soc-topology check-soc-topology user-extensions check-user-extensions \
|
||||||
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
|
check-clock-reset-domains tech-cell-test rtl-lint check-rtl-lint \
|
||||||
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-kws formal-apu-p9 formal-gateway-a formal-sdio formal-clean formal-doctor \
|
formal formal-bus formal-rib-adapter formal-rib2apb formal-gpio formal-ws2812 formal-uart formal-i2c formal-timer formal-dvp formal-i2s formal-onchip-ram formal-opipsram formal-dma formal-apu formal-apu-kws formal-apu-p9 formal-gateway-a formal-sdio formal-clean formal-doctor \
|
||||||
rtl-style-check-all rtl-readiness-check rtl-readiness-check-all vexii-generate
|
rtl-style-check-all rtl-readiness-check rtl-readiness-check-all vexii-generate
|
||||||
.NOTPARALLEL: setup
|
.NOTPARALLEL: setup
|
||||||
|
|
||||||
@@ -619,7 +636,7 @@ config:
|
|||||||
doctor:
|
doctor:
|
||||||
@python3 $(ROOT_PATH)/scripts/doctor.py \
|
@python3 $(ROOT_PATH)/scripts/doctor.py \
|
||||||
--root $(ROOT_PATH) --simu $(SIMU) --synth $(SYNTH) --sta $(STA) \
|
--root $(ROOT_PATH) --simu $(SIMU) --synth $(SYNTH) --sta $(STA) \
|
||||||
--pdk $(PDK) --have-sram-macro $(HAVE_SRAM_MACRO) \
|
--pdk $(PDK) --soc $(SOC) --have-sram-macro $(HAVE_SRAM_MACRO) \
|
||||||
--formal $(FORMAL) --lock $(LOCK_FILE)
|
--formal $(FORMAL) --lock $(LOCK_FILE)
|
||||||
|
|
||||||
benchmark-report: firmware
|
benchmark-report: firmware
|
||||||
@@ -641,7 +658,13 @@ hp-performance-check:
|
|||||||
--lp $(LP_COREMARK_REPORT) --hp $(HP_COREMARK_REPORT) \
|
--lp $(LP_COREMARK_REPORT) --hp $(HP_COREMARK_REPORT) \
|
||||||
--minimum-ratio $(HP_PERF_MIN_RATIO) --output $(META_DIR)/lp-hp-performance.json
|
--minimum-ratio $(HP_PERF_MIN_RATIO) --output $(META_DIR)/lp-hp-performance.json
|
||||||
|
|
||||||
|
ifeq ($(SOC),TINY)
|
||||||
|
setup: setup-tiny
|
||||||
|
setup-tiny:
|
||||||
|
python3 $(ROOT_PATH)/scripts/setup_tiny.py
|
||||||
|
else
|
||||||
setup: setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app
|
setup: setup-mpw setup-vexiiriscv setup-clusterip setup-ip setup-pdk setup-app
|
||||||
|
endif
|
||||||
|
|
||||||
setup-regression:
|
setup-regression:
|
||||||
$(MAKE) CONFIG=configs/ci/ihp130.mk setup
|
$(MAKE) CONFIG=configs/ci/ihp130.mk setup
|
||||||
@@ -1158,19 +1181,22 @@ commercial-package: $(MPW_VARIANT_DEP) $(FILELIST_STAMP) manifest
|
|||||||
--variant-root $(VARIANT_ROOT) --output-dir $(VARIANT_ROOT)/commercial/input
|
--variant-root $(VARIANT_ROOT) --output-dir $(VARIANT_ROOT)/commercial/input
|
||||||
|
|
||||||
regress-smoke:
|
regress-smoke:
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk IHP130
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite smoke --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY)
|
||||||
|
|
||||||
regress-rtl:
|
regress-rtl:
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk IHP130
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite rtl --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY)
|
||||||
|
|
||||||
regress-pr:
|
regress-pr:
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk IHP130 $(if $(filter TINY,$(SOC)),--soc TINY) $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
||||||
|
ifneq ($(SOC),TINY)
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk GF180 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk GF180 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk SKY130 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk SKY130 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk ICS55 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite pr --pdk ICS55 $(if $(filter YES,$(REGRESS_NETSIM_BOOT_ONLY)),--netsim-boot-only)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
regress-nightly:
|
regress-nightly:
|
||||||
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly
|
python3 $(ROOT_PATH)/scripts/regress.py --root $(ROOT_PATH) --suite nightly $(if $(filter TINY,$(SOC)),--soc TINY)
|
||||||
|
|
||||||
sim-asm: asm
|
sim-asm: asm
|
||||||
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
|
$(MAKE) SIM_FIRMWARE_NAME=$(ASM_FIRMWARE_NAME) sim
|
||||||
@@ -66,6 +66,19 @@ under the [Mulan Permissive Software License, Version 2](LICENSE).
|
|||||||
|
|
||||||
## Repository Layout
|
## Repository Layout
|
||||||
|
|
||||||
|
**Mini and Tiny have independent integration flows.** Mini combines a
|
||||||
|
low-power Hazard3 management hart and a VexiiRiscv application hart; its separate
|
||||||
|
MPW profile retains selectable-core compatibility. [Tiny](docs/ip/tiny-soc.md)
|
||||||
|
is a wired MCU with one Hazard3 RV32IMC hart, 128 KiB SRAM, AXI4/APB4 and no
|
||||||
|
wireless IP. Tiny first targets IHP130; [Std and Pro](docs/soc-family-positioning.md)
|
||||||
|
remain roadmap products. Qualification is determined by each profile's retained
|
||||||
|
verification and physical reports.
|
||||||
|
|
||||||
|
The [Tiny Gen1 datasheet](publications/datasheets/tiny/README.md) provides the
|
||||||
|
independent product, register, software and evidence reference using the
|
||||||
|
established publication layout. Its build does not require Mini's HP or
|
||||||
|
multimedia inputs.
|
||||||
|
|
||||||
| Path | Contents |
|
| Path | Contents |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [`rtl/`](rtl) | SoC RTL, CPU integration, peripherals, interfaces, testbenches, and technology wrappers. |
|
| [`rtl/`](rtl) | SoC RTL, CPU integration, peripherals, interfaces, testbenches, and technology wrappers. |
|
||||||
@@ -209,6 +222,40 @@ Select the interactive shell application without editing source files:
|
|||||||
make CONFIG=configs/ci/ihp130-shell.mk firmware
|
make CONFIG=configs/ci/ihp130-shell.mk firmware
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Build and simulate Tiny
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk setup
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk doctor
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk firmware sim
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG firmware sim
|
||||||
|
```
|
||||||
|
|
||||||
|
Tiny uses its own source lists and product maps under `rtl/tiny`. Its firmware
|
||||||
|
loads into on-chip SRAM without PSRAM/SDRAM initialization. The first release
|
||||||
|
uses a fixed 24 MHz clock and disables atomic instructions. Wireless, external
|
||||||
|
RAM, HP cores, accelerators and advanced low-power modes remain deferred.
|
||||||
|
|
||||||
|
## Configurations and common flows
|
||||||
|
|
||||||
|
Start from a [committed profile](configs/README.md). These are selected entry
|
||||||
|
points, not the complete profile or application inventory.
|
||||||
|
|
||||||
|
| Profile | Purpose |
|
||||||
|
| --- | --- |
|
||||||
|
| [IHP130 Tiny](configs/ci/ihp130-tiny.mk) | Single-hart wired MCU, 128 KiB SRAM, pin-level Flash boot and automated acceptance. |
|
||||||
|
| [IHP130 Mini](configs/ci/ihp130.mk) | Manual bring-up with 32 KiB macro-backed SRAM. |
|
||||||
|
| [GF180](configs/ci/gf180.mk) / [SKY130](configs/ci/sky130.mk) | Alternative PDK profiles with 32 KiB macro-backed SRAM. |
|
||||||
|
| [ICS55](configs/ci/ics55.mk) | Regression-compatible profile with SRAM and PLL disabled. |
|
||||||
|
| [Interactive shell](configs/ci/ihp130-shell.mk) | Shell firmware with CSR support enabled. |
|
||||||
|
| [Hazard3 debug](configs/ci/ihp130-debug.mk) | JTAG acceptance using Verilator, OpenOCD, and GDB. |
|
||||||
|
| [HP Linux](configs/ci/ihp130-hp.mk) | HP image/bundle flow; outside the supported PR matrix. |
|
||||||
|
| [APU LP/HP](configs/ci/ihp130-apu.mk) | Audio and KWS evidence flow with ownership handoff to HP. |
|
||||||
|
| [CoreMark](configs/benchmark/ihp130-hazard3-coremark.mk) | Fixed LP SRAM benchmark with the product HP core present. |
|
||||||
|
| [Mini MPW](configs/cluster/mini-mpw.mk) | Legacy C0-C3/user-IP compatibility, separate from the product ABI. |
|
||||||
|
|
||||||
|
After the corresponding setup, use these common commands:
|
||||||
|
|
||||||
## Common Flows
|
## Common Flows
|
||||||
|
|
||||||
All commands use a committed profile. `make config` prints the effective configuration and
|
All commands use a committed profile. `make config` prints the effective configuration and
|
||||||
|
|||||||
@@ -146,3 +146,10 @@ make regress-nightly
|
|||||||
|
|
||||||
See the [repository README](../README.md) for prerequisites, supported
|
See the [repository README](../README.md) for prerequisites, supported
|
||||||
configurations, and build artifact locations.
|
configurations, and build artifact locations.
|
||||||
|
|
||||||
|
For `SOC=TINY`, `bringup` and `ci_smoke` use the MCU acceptance composition. It
|
||||||
|
boots through XPI into SRAM, checks CPU C/A behavior and bus exceptions, DMA
|
||||||
|
(including UART requests), interrupts, RTC, GPIO, UART1 and I2C error completion,
|
||||||
|
then verifies watchdog reset by completing after the next NOR boot. It uses
|
||||||
|
SYSCTRL terminal status. The small `asm/tiny_boot.S` image separately qualifies
|
||||||
|
the synthesized SRAM/pad path; it is not a replacement for the full RTL test.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
|
||||||
|
APP_CFLAGS += -Os
|
||||||
|
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/* Copyright (c) 2026 Yuchi Miao; SPDX-License-Identifier: MulanPSL-2.0 */
|
||||||
|
.section .text
|
||||||
|
.balign 4
|
||||||
|
.global rs_mcu_compressed_probe
|
||||||
|
.type rs_mcu_compressed_probe,@function
|
||||||
|
rs_mcu_compressed_probe:
|
||||||
|
.option push
|
||||||
|
.option rvc
|
||||||
|
c.li a0, 21
|
||||||
|
c.slli a0, 1
|
||||||
|
c.jr ra
|
||||||
|
.option pop
|
||||||
|
.size rs_mcu_compressed_probe, .-rs_mcu_compressed_probe
|
||||||
|
|
||||||
|
.balign 4
|
||||||
|
.global rs_mcu_atomic_probe
|
||||||
|
.type rs_mcu_atomic_probe,@function
|
||||||
|
rs_mcu_atomic_probe:
|
||||||
|
/* lr.w a0,(a0): must trap because the Tiny A extension is disabled. */
|
||||||
|
.word 0x1005252f
|
||||||
|
ret
|
||||||
|
.size rs_mcu_atomic_probe, .-rs_mcu_atomic_probe
|
||||||
|
|
||||||
|
.balign 4
|
||||||
|
.global rs_mcu_unmapped_probe
|
||||||
|
.type rs_mcu_unmapped_probe,@function
|
||||||
|
rs_mcu_unmapped_probe:
|
||||||
|
li a0, 0x38000000
|
||||||
|
lw a0, 0(a0)
|
||||||
|
ret
|
||||||
|
.size rs_mcu_unmapped_probe, .-rs_mcu_unmapped_probe
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef RETROSOC_MCU_ACCEPTANCE_H
|
||||||
|
#define RETROSOC_MCU_ACCEPTANCE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint32_t rs_mcu_compressed_probe(void);
|
||||||
|
void rs_mcu_atomic_probe(uint32_t *address);
|
||||||
|
void rs_mcu_unmapped_probe(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <retrosoc/arch/riscv/system_base.h>
|
||||||
|
#include <retrosoc/core/archinfo.h>
|
||||||
|
#include <retrosoc/core/irq.h>
|
||||||
|
#include <retrosoc/core/soc.h>
|
||||||
|
#include <retrosoc/generated/irq_metadata.h>
|
||||||
|
#include <retrosoc/hal/clint.h>
|
||||||
|
#include <retrosoc/hal/dma.h>
|
||||||
|
#include <retrosoc/hal/gpio.h>
|
||||||
|
#include <retrosoc/hal/i2c.h>
|
||||||
|
#include <retrosoc/hal/onchip_sram.h>
|
||||||
|
#include <retrosoc/hal/pwm.h>
|
||||||
|
#include <retrosoc/hal/rtc.h>
|
||||||
|
#include <retrosoc/hal/sysctrl.h>
|
||||||
|
#include <retrosoc/hal/timer.h>
|
||||||
|
#include <retrosoc/hal/uart.h>
|
||||||
|
#include <retrosoc/hal/watchdog.h>
|
||||||
|
#include <retrosoc/lib/printf.h>
|
||||||
|
|
||||||
|
#include "mcu_acceptance.h"
|
||||||
|
|
||||||
|
static _Alignas(64) uint32_t rs_mcu_source[80];
|
||||||
|
static _Alignas(64) uint32_t rs_mcu_destination[80];
|
||||||
|
static volatile uint32_t rs_mcu_irq_count;
|
||||||
|
static volatile uint32_t rs_mcu_exception_count;
|
||||||
|
static volatile bool rs_mcu_handler_failed;
|
||||||
|
|
||||||
|
static void rs_mcu_finish(bool pass, uint8_t code) {
|
||||||
|
printf("Tiny acceptance %s code=%u\n", pass ? "passed" : "failed", code);
|
||||||
|
rs_sysctrl_write_test_status(pass, code);
|
||||||
|
/* The terminal status must retain the first accepted full-word write. */
|
||||||
|
rs_sysctrl_write_test_status(!pass, UINT8_C(255));
|
||||||
|
for (;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rs_mcu_exception(uintptr_t mcause, uintptr_t stack_pointer) {
|
||||||
|
volatile uint32_t *const frame = (volatile uint32_t *)stack_pointer;
|
||||||
|
|
||||||
|
if ((mcause != UINT32_C(2)) && (mcause != UINT32_C(5))) {
|
||||||
|
rs_mcu_handler_failed = true;
|
||||||
|
}
|
||||||
|
++rs_mcu_exception_count;
|
||||||
|
/* system_irq.S saves MEPC in slot 11 of the RV32 trap frame. Both test
|
||||||
|
* instructions are explicitly 32 bits; normal exceptions do not use this handler. */
|
||||||
|
frame[11] += UINT32_C(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rs_mcu_timer_irq(uintptr_t mcause, uintptr_t stack_pointer) {
|
||||||
|
(void)mcause;
|
||||||
|
(void)stack_pointer;
|
||||||
|
++rs_mcu_irq_count;
|
||||||
|
if (rs_timer_interrupt_clear(RS_TIMER_0, RS_TIMER_INTERRUPT_ALL) != RS_OK) {
|
||||||
|
rs_mcu_handler_failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rs_mcu_dma(void) {
|
||||||
|
rs_dma_config_t config = {
|
||||||
|
.kind = RS_DMA_KIND_MM_TO_MM,
|
||||||
|
.request = RS_DMA_REQUEST_SOFTWARE,
|
||||||
|
.source = (uintptr_t)rs_mcu_source,
|
||||||
|
.destination = (uintptr_t)rs_mcu_destination,
|
||||||
|
.byte_count = UINT32_C(259),
|
||||||
|
.width = RS_DMA_WIDTH_32,
|
||||||
|
.source_increment = true,
|
||||||
|
.destination_increment = true,
|
||||||
|
.priority = 0U,
|
||||||
|
.burst_beats = 16U,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (uint32_t index = 0U; index < 80U; ++index) {
|
||||||
|
rs_mcu_source[index] = UINT32_C(0xa519c300) ^ index;
|
||||||
|
rs_mcu_destination[index] = UINT32_C(0xcccccccc);
|
||||||
|
}
|
||||||
|
if ((rs_dma_configure(4U, &config) != RS_EINVAL) ||
|
||||||
|
(rs_dma_configure(RS_DMA_CHANNEL_BULK, &config) != RS_OK) ||
|
||||||
|
(rs_dma_start(RS_DMA_CHANNEL_BULK) != RS_OK) ||
|
||||||
|
(rs_dma_wait(RS_DMA_CHANNEL_BULK, RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (uint32_t index = 0U; index < 64U; ++index) {
|
||||||
|
if (rs_mcu_source[index] != rs_mcu_destination[index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rs_mcu_destination[64] !=
|
||||||
|
((rs_mcu_source[64] & UINT32_C(0x00ffffff)) | UINT32_C(0xcc000000))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
config.kind = RS_DMA_KIND_MM_TO_STREAM;
|
||||||
|
config.request = RS_DMA_REQUEST_I2S_TX;
|
||||||
|
return rs_dma_config_validate(0U, &config) != RS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rs_mcu_interrupts(void) {
|
||||||
|
uint32_t timeout = UINT32_C(20000);
|
||||||
|
|
||||||
|
if ((rs_irq_enable_external(RS_SOC_EXT_IRQ_TIMER0, rs_mcu_timer_irq) != RS_OK) ||
|
||||||
|
(rs_timer_interrupt_enable(RS_TIMER_0, RS_TIMER_INTERRUPT_TIMEOUT) != RS_OK) ||
|
||||||
|
(rs_timer_interrupt_test(RS_TIMER_0, RS_TIMER_INTERRUPT_TIMEOUT) != RS_OK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
__RV_CSR_SET(CSR_MSTATUS, MSTATUS_MIE);
|
||||||
|
while ((rs_mcu_irq_count == 0U) && (timeout != 0U)) {
|
||||||
|
--timeout;
|
||||||
|
}
|
||||||
|
__RV_CSR_CLEAR(CSR_MSTATUS, MSTATUS_MIE);
|
||||||
|
return (rs_mcu_irq_count == 1U) && !rs_mcu_handler_failed &&
|
||||||
|
(rs_irq_disable_external(RS_SOC_EXT_IRQ_TIMER0) == RS_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rs_mcu_io(void) {
|
||||||
|
const rs_gpio_config_t output = {
|
||||||
|
.mode = RS_GPIO_MODE_OUTPUT,
|
||||||
|
.pull = RS_GPIO_PULL_NONE,
|
||||||
|
.trigger = RS_GPIO_TRIGGER_NONE,
|
||||||
|
.output_high = false,
|
||||||
|
};
|
||||||
|
const rs_uart_config_t loopback = {
|
||||||
|
.source_clock_hz = RS_CPU_CLOCK_HZ,
|
||||||
|
.baud_rate = UART_BPS,
|
||||||
|
.data_bits = 8U,
|
||||||
|
.stop_bits = 1U,
|
||||||
|
.parity = RS_UART_PARITY_NONE,
|
||||||
|
.rx_watermark = 1U,
|
||||||
|
.rx_timeout_bits = 16U,
|
||||||
|
.tx_enable = true,
|
||||||
|
.rx_enable = true,
|
||||||
|
.loopback_enable = true,
|
||||||
|
};
|
||||||
|
const uint32_t transmit[4] = {UINT32_C(0x54), UINT32_C(0x49), UINT32_C(0x4e), UINT32_C(0x59)};
|
||||||
|
uint32_t receive[4] = {0U};
|
||||||
|
rs_uart_timing_t timing;
|
||||||
|
bool high;
|
||||||
|
uint32_t timeout = UINT32_C(10000);
|
||||||
|
uint8_t payload = UINT8_C(0x55);
|
||||||
|
const rs_i2c_transfer_t transfer = {
|
||||||
|
.address = UINT16_C(0x50),
|
||||||
|
.tx_data = &payload,
|
||||||
|
.tx_length = 1U,
|
||||||
|
};
|
||||||
|
rs_i2c_status_t status;
|
||||||
|
|
||||||
|
if ((rs_gpio_configure(15U, &output) != RS_OK) || (rs_gpio_write(15U, true) != RS_OK) ||
|
||||||
|
(rs_gpio_read(15U, &high) != RS_OK) || !high || (rs_gpio_write(15U, false) != RS_OK) ||
|
||||||
|
(rs_gpio_read(15U, &high) != RS_OK) || high) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((rs_uart_configure(&loopback, RS_TIMEOUT_DEFAULT) != RS_OK) ||
|
||||||
|
(rs_uart_write_dma(transmit, 4U, RS_TIMEOUT_DEFAULT) != RS_OK) ||
|
||||||
|
(rs_uart_read_dma(receive, 4U, RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (uint32_t index = 0U; index < 4U; ++index) {
|
||||||
|
if (transmit[index] != receive[index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) ||
|
||||||
|
(rs_uart_timing_calculate(RS_CPU_CLOCK_HZ, UART_BPS, &timing) != RS_OK)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* UART1 uses the same register ABI; the current shared high-level UART
|
||||||
|
* API selects UART0. Exercise UART1 PIO and its internal loopback here. */
|
||||||
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x00)) = timing.baud_integer;
|
||||||
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x04)) = timing.baud_fraction;
|
||||||
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x08)) = UINT32_C(3);
|
||||||
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x0c)) = UINT32_C(7);
|
||||||
|
RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x10)) = UINT32_C(0x5a);
|
||||||
|
while (((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x18)) & UINT32_C(0x40)) != 0U) &&
|
||||||
|
(timeout != 0U)) {
|
||||||
|
--timeout;
|
||||||
|
}
|
||||||
|
if ((timeout == 0U) || ((RS_SOC_REG32(RS_SOC_APB4_UART1_BASE, UINT32_C(0x14)) &
|
||||||
|
UINT32_C(0xff)) != UINT32_C(0x5a))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* There is no slave on the pulled-up acceptance board: a real address
|
||||||
|
* transaction must terminate as NACK rather than stick waiting on the bus. */
|
||||||
|
for (uint32_t bus = 0U; bus < 2U; ++bus) {
|
||||||
|
const rs_i2c_bus_t instance = (bus == 0U) ? RS_I2C_BUS_0 : RS_I2C_BUS_1;
|
||||||
|
if ((rs_i2c_init(instance, RS_CPU_CLOCK_HZ, UINT32_C(100000)) != RS_OK) ||
|
||||||
|
(rs_i2c_transfer(instance, &transfer, RS_TIMEOUT_DEFAULT) != RS_EIO) ||
|
||||||
|
(rs_i2c_get_status(instance, &status) != RS_OK) ||
|
||||||
|
((status.errors & RS_I2C_ERROR_ADDR_NACK) == 0U)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool rs_mcu_rtc(void) {
|
||||||
|
const rs_rtc_config_t config = {
|
||||||
|
.second_cycles = UINT32_C(4096),
|
||||||
|
.calibration_ppm = 0,
|
||||||
|
.interrupt_enable = 0U,
|
||||||
|
.wake_enable = 0U,
|
||||||
|
.enable = true,
|
||||||
|
};
|
||||||
|
const rs_rtc_time_t value = {.seconds = UINT64_C(1234), .subsecond = 0U};
|
||||||
|
rs_rtc_time_t observed;
|
||||||
|
|
||||||
|
return (rs_rtc_probe() == RS_OK) && (rs_rtc_configure(&config, RS_TIMEOUT_DEFAULT) == RS_OK) &&
|
||||||
|
(rs_rtc_set_time(&value, RS_TIMEOUT_DEFAULT) == RS_OK) &&
|
||||||
|
(rs_rtc_get_time(&observed, RS_TIMEOUT_DEFAULT) == RS_OK) &&
|
||||||
|
(observed.seconds >= value.seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
rs_archinfo_t info;
|
||||||
|
rs_onchip_sram_info_t memory;
|
||||||
|
rs_sysctrl_fault_status_t fault;
|
||||||
|
rs_watchdog_status_t watchdog;
|
||||||
|
uint64_t before;
|
||||||
|
uint64_t after;
|
||||||
|
const rs_watchdog_config_t watchdog_config = {
|
||||||
|
.prescale_divider = 1U,
|
||||||
|
.timeout_ticks = UINT32_C(20000),
|
||||||
|
.window_min_ticks = 0U,
|
||||||
|
.early_warning_ticks = 0U,
|
||||||
|
.window_enable = false,
|
||||||
|
.early_warning_enable = false,
|
||||||
|
.debug_freeze_enable = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rs_uart_init(RS_CPU_CLOCK_HZ, UART_BPS) != RS_OK) {
|
||||||
|
rs_mcu_finish(false, 1U);
|
||||||
|
}
|
||||||
|
printf("Hello retroSoC! Tiny MCU\n");
|
||||||
|
if (rs_watchdog_get_status(&watchdog, RS_TIMEOUT_DEFAULT) != RS_OK) {
|
||||||
|
rs_mcu_finish(false, 2U);
|
||||||
|
}
|
||||||
|
if (watchdog.reset_count != 0U) {
|
||||||
|
rs_mcu_finish(true, 0U);
|
||||||
|
}
|
||||||
|
if ((rs_archinfo_read(&info) != RS_OK) || (rs_archinfo_validate_build(&info) != RS_OK) ||
|
||||||
|
(info.soc_id != RS_SOC_ID) || (info.topology != UINT32_C(0x20200001)) ||
|
||||||
|
(rs_onchip_sram_probe(&memory) != RS_OK) || (memory.memory_bytes != UINT32_C(131072)) ||
|
||||||
|
(memory.data_bytes != 4U) || (memory.bank_count != 32U)) {
|
||||||
|
rs_mcu_finish(false, 3U);
|
||||||
|
}
|
||||||
|
if ((rs_mcu_compressed_probe() != 42U) || ((__RV_CSR_READ(CSR_MISA) & UINT32_C(1)) != 0U) ||
|
||||||
|
(rs_irq_register_exception(2U, rs_mcu_exception) != RS_OK) ||
|
||||||
|
(rs_irq_register_exception(5U, rs_mcu_exception) != RS_OK)) {
|
||||||
|
rs_mcu_finish(false, 4U);
|
||||||
|
}
|
||||||
|
rs_mcu_atomic_probe(rs_mcu_source);
|
||||||
|
rs_mcu_unmapped_probe();
|
||||||
|
if ((rs_mcu_exception_count != 2U) || rs_mcu_handler_failed ||
|
||||||
|
(rs_sysctrl_get_fault_status(&fault) != RS_OK) || !fault.pending ||
|
||||||
|
(fault.address != UINT32_C(0x38000000)) || (rs_sysctrl_clear_fault() != RS_OK)) {
|
||||||
|
rs_mcu_finish(false, 5U);
|
||||||
|
}
|
||||||
|
if (!rs_mcu_dma()) {
|
||||||
|
rs_mcu_finish(false, 6U);
|
||||||
|
}
|
||||||
|
if ((rs_clint_get_time(&before) != RS_OK) || (rs_clint_get_time(&after) != RS_OK) ||
|
||||||
|
(after <= before) || !rs_mcu_interrupts()) {
|
||||||
|
rs_mcu_finish(false, 7U);
|
||||||
|
}
|
||||||
|
if (!rs_mcu_rtc() || (rs_pwm_probe() != RS_OK) ||
|
||||||
|
(rs_sysctrl_set_hp_release(true) != RS_ENOTSUP)) {
|
||||||
|
rs_mcu_finish(false, 8U);
|
||||||
|
}
|
||||||
|
if (!rs_mcu_io()) {
|
||||||
|
rs_mcu_finish(false, 10U);
|
||||||
|
}
|
||||||
|
printf("Tiny CPU/DMA/IRQ/RTC/I/O passed; exercising watchdog reset\n");
|
||||||
|
if ((rs_watchdog_configure(&watchdog_config) != RS_OK) ||
|
||||||
|
(rs_watchdog_start(RS_TIMEOUT_DEFAULT) != RS_OK)) {
|
||||||
|
rs_mcu_finish(false, 9U);
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/tiny.c
|
||||||
|
APP_CFLAGS += -Os
|
||||||
|
APP_SRCS += $(ROOT_PATH)/app/apps/ci_smoke/mcu_acceptance.S
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/* Copyright (c) 2026 Yuchi Miao; SPDX-License-Identifier: MulanPSL-2.0 */
|
||||||
|
#include "retrosoc/generated/memory_map.h"
|
||||||
|
|
||||||
|
/* Small gate-level acceptance image. Executes through the real XPI pins,
|
||||||
|
* checks both ends of SRAM and byte strobes, then checks APB/UART/SYSCTRL.
|
||||||
|
* The full SDK/application acceptance runs separately in both RTL simulators. */
|
||||||
|
.section .text
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
li t0, RS_SOC_SRAM_BASE
|
||||||
|
li t1, 0x12345678
|
||||||
|
sw t1, 0(t0)
|
||||||
|
lw t2, 0(t0)
|
||||||
|
bne t1, t2, failed
|
||||||
|
li t0, RS_SOC_SRAM_END - 3
|
||||||
|
sw t1, 0(t0)
|
||||||
|
li t2, 0xab
|
||||||
|
sb t2, 1(t0)
|
||||||
|
lw t2, 0(t0)
|
||||||
|
li t1, 0x1234ab78
|
||||||
|
bne t1, t2, failed
|
||||||
|
|
||||||
|
li s0, RS_SOC_APB4_UART0_BASE
|
||||||
|
li t0, 26
|
||||||
|
sw t0, 0(s0)
|
||||||
|
li t0, 11
|
||||||
|
sw t0, 4(s0)
|
||||||
|
li t0, 3
|
||||||
|
sw t0, 8(s0)
|
||||||
|
sw t0, 12(s0)
|
||||||
|
la s1, message
|
||||||
|
print_loop:
|
||||||
|
lbu s2, 0(s1)
|
||||||
|
beqz s2, passed
|
||||||
|
wait_uart:
|
||||||
|
lw t0, 24(s0)
|
||||||
|
andi t0, t0, 32
|
||||||
|
bnez t0, wait_uart
|
||||||
|
sw s2, 16(s0)
|
||||||
|
addi s1, s1, 1
|
||||||
|
j print_loop
|
||||||
|
passed:
|
||||||
|
/* Let the serial output drain before setting the terminal verdict. */
|
||||||
|
lw t0, 24(s0)
|
||||||
|
andi t0, t0, 4
|
||||||
|
bnez t0, passed
|
||||||
|
li t0, RS_SOC_APB4_SYSCTRL_BASE
|
||||||
|
li t1, 0x80000001
|
||||||
|
sw t1, RS_SOC_SYSCTRL_TEST_STATUS_OFFSET(t0)
|
||||||
|
halt:
|
||||||
|
j halt
|
||||||
|
failed:
|
||||||
|
li t0, RS_SOC_APB4_SYSCTRL_BASE
|
||||||
|
li t1, 0x80000100
|
||||||
|
sw t1, RS_SOC_SYSCTRL_TEST_STATUS_OFFSET(t0)
|
||||||
|
j halt
|
||||||
|
message:
|
||||||
|
.asciz "Hello retroSoC! Tiny netlist boot\n"
|
||||||
@@ -51,3 +51,12 @@ macro must select either this mode or a qualified macro timing model.
|
|||||||
|
|
||||||
For supported profiles and commands, see the root [README](../README.md) and
|
For supported profiles and commands, see the root [README](../README.md) and
|
||||||
[agent contract](../AGENTS.md).
|
[agent contract](../AGENTS.md).
|
||||||
|
|
||||||
|
## Tiny MCU
|
||||||
|
|
||||||
|
`ci/ihp130-tiny.mk` selects `SOC=TINY`, one RV32IMC Hazard3 (A disabled),
|
||||||
|
128 KiB SRAM, AXI32/APB4, four DMA channels and the wired peripheral subset.
|
||||||
|
The profile uses 24 MHz, `HAVE_HP=NO`, `MINI_MODE=NONE`, CSR-enabled RV32IM
|
||||||
|
firmware and `ld2_all_sram`. Both `bringup` and `ci_smoke` select Tiny acceptance
|
||||||
|
firmware. Icarus and Verilator use the same pin-level Flash model. Other PDKs,
|
||||||
|
wireless, external RAM and HP applications are rejected by the Tiny config gate.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
SOC := TINY
|
||||||
|
MINI_MODE := NONE
|
||||||
|
PDK := IHP130
|
||||||
|
SIMU := VERILATOR
|
||||||
|
HAVE_PLL := NO
|
||||||
|
HAVE_SRAM_IF := YES
|
||||||
|
HAVE_SRAM_MACRO := YES
|
||||||
|
SRAM_SIZE_KIB := 128
|
||||||
|
HAVE_HP := NO
|
||||||
|
HAVE_SVA := NO
|
||||||
|
EXT_CLK_HZ := 24000000
|
||||||
|
AUD_CLK_HZ := 24000000
|
||||||
|
CLINT_TIMEBASE_HZ := 1000000
|
||||||
|
ISA := RV32IM
|
||||||
|
HAVE_CSR := YES
|
||||||
|
APP := bringup
|
||||||
|
LINK_TYPE := ld2_all_sram
|
||||||
|
RTL_TOP := retrosoc_tiny_tb
|
||||||
+15
-1
@@ -167,7 +167,7 @@ on it.
|
|||||||
## Build Integration
|
## Build Integration
|
||||||
|
|
||||||
The central software build assembles the runtime sources in
|
The central software build assembles the runtime sources in
|
||||||
[`../rtl/mini/mk/software.mk`](../rtl/mini/mk/software.mk). It selects the
|
[`../rtl/mk/software.mk`](../rtl/mk/software.mk). It selects the
|
||||||
RISC-V ISA, optional CSR interrupt support, linker layout, and the application
|
RISC-V ISA, optional CSR interrupt support, linker layout, and the application
|
||||||
profile. Applications add their own sources after the common runtime is
|
profile. Applications add their own sources after the common runtime is
|
||||||
selected.
|
selected.
|
||||||
@@ -226,3 +226,17 @@ hardware validation.
|
|||||||
|
|
||||||
For the complete firmware, simulation, synthesis, and timing flow, see the
|
For the complete firmware, simulation, synthesis, and timing flow, see the
|
||||||
[repository README](../README.md).
|
[repository README](../README.md).
|
||||||
|
|
||||||
|
## Product composition
|
||||||
|
|
||||||
|
Tiny uses the same `rs_` SDK and selects its generated memory, IRQ and capability
|
||||||
|
headers through the committed profile. The generated memory-map header includes
|
||||||
|
Tiny capability metadata, so public DMA headers report four channels without
|
||||||
|
requiring an application to reproduce private compiler flags. Unavailable IP
|
||||||
|
drivers are omitted; Tiny SYSCTRL rejects unsupported lifecycle/PLL operations
|
||||||
|
before MMIO. Startup skips external-RAM initialization and loads into SRAM.
|
||||||
|
|
||||||
|
`<retrosoc/hal/watchdog.h>` provides `rs_watchdog_configure`, `rs_watchdog_start`,
|
||||||
|
`rs_watchdog_service`, `rs_watchdog_get_status` and `rs_watchdog_clear_reset_cause`,
|
||||||
|
with SDK status values and bounded command waits. These wrappers preserve the
|
||||||
|
locked watchdog register ABI.
|
||||||
@@ -17,6 +17,7 @@ _start:
|
|||||||
/* Direct Mode: All exceptions set pc to BASE */
|
/* Direct Mode: All exceptions set pc to BASE */
|
||||||
csrc CSR_MTVEC, 0x3
|
csrc CSR_MTVEC, 0x3
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef RS_SOC_TINY
|
||||||
# wait psram reset done
|
# wait psram reset done
|
||||||
la a0, _flash_wait_start
|
la a0, _flash_wait_start
|
||||||
la a1, 2000
|
la a1, 2000
|
||||||
@@ -25,6 +26,8 @@ wait_psram_reset:
|
|||||||
addi a0, a0, 4
|
addi a0, a0, 4
|
||||||
blt a0, a1, wait_psram_reset
|
blt a0, a1, wait_psram_reset
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
# ===== Startup Stage 1 =====
|
# ===== Startup Stage 1 =====
|
||||||
# init the regfile
|
# init the regfile
|
||||||
# zero-initialize register file
|
# zero-initialize register file
|
||||||
@@ -68,6 +71,7 @@ wait_psram_reset:
|
|||||||
# when code LMA is different with VMA
|
# when code LMA is different with VMA
|
||||||
# test: using psram before loading
|
# test: using psram before loading
|
||||||
|
|
||||||
|
#ifndef RS_SOC_TINY
|
||||||
# Define the addresses
|
# Define the addresses
|
||||||
.equ GPIO_ALT_ENABLE, RS_SOC_APB4_GPIO_ADMIN_BASE + 0x34
|
.equ GPIO_ALT_ENABLE, RS_SOC_APB4_GPIO_ADMIN_BASE + 0x34
|
||||||
.equ GPIO_ALT_SELECT, RS_SOC_APB4_GPIO_ADMIN_BASE + 0x38
|
.equ GPIO_ALT_SELECT, RS_SOC_APB4_GPIO_ADMIN_BASE + 0x38
|
||||||
@@ -98,6 +102,8 @@ PSRAM_READY_WAIT:
|
|||||||
andi t1, t1, PSRAM_READY
|
andi t1, t1, PSRAM_READY
|
||||||
beqz t1, PSRAM_READY_WAIT
|
beqz t1, PSRAM_READY_WAIT
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
app_loader_start:
|
app_loader_start:
|
||||||
la a0, _ram_lma
|
la a0, _ram_lma
|
||||||
la a1, _ram_vma
|
la a1, _ram_vma
|
||||||
|
|||||||
@@ -77,11 +77,15 @@
|
|||||||
#define RS_DMA_ERROR_READ UINT32_C(0x00000040)
|
#define RS_DMA_ERROR_READ UINT32_C(0x00000040)
|
||||||
#define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100)
|
#define RS_DMA_ERROR_STREAM_LAST UINT32_C(0x00000100)
|
||||||
|
|
||||||
#define RS_DMA_CHANNEL_COUNT 8U
|
#ifdef RS_SOC_TINY
|
||||||
#define RS_DMA_CHANNEL_HP 6U
|
#define RS_DMA_CHANNEL_COUNT 4U
|
||||||
#define RS_DMA_CHANNEL_RESERVED 7U
|
#else
|
||||||
#define RS_DMA_MAX_BURST_BEATS 16U
|
#define RS_DMA_CHANNEL_COUNT 8U
|
||||||
#define RS_DMA_DATA_WIDTH_BITS 32U
|
#endif
|
||||||
|
#define RS_DMA_CHANNEL_HP 6U
|
||||||
|
#define RS_DMA_CHANNEL_RESERVED 7U
|
||||||
|
#define RS_DMA_MAX_BURST_BEATS 16U
|
||||||
|
#define RS_DMA_DATA_WIDTH_BITS 32U
|
||||||
|
|
||||||
#define RS_DMA_CHANNEL_OFFSET(channel, offset) \
|
#define RS_DMA_CHANNEL_OFFSET(channel, offset) \
|
||||||
(RS_DMA_CH_BASE + ((uint32_t)(channel)*RS_DMA_CH_STRIDE) + (offset))
|
(RS_DMA_CH_BASE + ((uint32_t)(channel)*RS_DMA_CH_STRIDE) + (offset))
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
#ifndef RETROSOC_WDG_H
|
#ifndef RETROSOC_WDG_H
|
||||||
#define RETROSOC_WDG_H
|
#define RETROSOC_WDG_H
|
||||||
|
|
||||||
|
#include <wdg.h>
|
||||||
|
|
||||||
|
#include <retrosoc/core/status.h>
|
||||||
|
|
||||||
|
typedef wdg_config_t rs_watchdog_config_t;
|
||||||
|
typedef wdg_snapshot_t rs_watchdog_status_t;
|
||||||
|
|
||||||
|
rs_status_t rs_watchdog_configure(const rs_watchdog_config_t *config);
|
||||||
|
rs_status_t rs_watchdog_start(rs_timeout_t timeout);
|
||||||
|
rs_status_t rs_watchdog_service(rs_timeout_t timeout);
|
||||||
|
rs_status_t rs_watchdog_get_status(rs_watchdog_status_t *status, rs_timeout_t timeout);
|
||||||
|
rs_status_t rs_watchdog_clear_reset_cause(rs_timeout_t timeout);
|
||||||
|
|
||||||
void ip_wdg_test(int argc, char **argv);
|
void ip_wdg_test(int argc, char **argv);
|
||||||
#endif
|
#endif
|
||||||
@@ -39,7 +39,20 @@ rs_status_t rs_archinfo_validate_build(const rs_archinfo_t *info) {
|
|||||||
.config_id = ARCHINFO_INTEGRATION_CONFIG_ID,
|
.config_id = ARCHINFO_INTEGRATION_CONFIG_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
if ((info == NULL) || (info->soc_id != RS_SOC_ID) ||
|
||||||
|
(info->component_id != ARCHINFO_COMPONENT_ID_VALUE) ||
|
||||||
|
(info->ip_version != ARCHINFO_IP_VERSION_VALUE) ||
|
||||||
|
(((info->capability & ARCHINFO_CAPABILITY_ABI_MASK) >> ARCHINFO_CAPABILITY_ABI_SHIFT) !=
|
||||||
|
ARCHINFO_ABI_VERSION) ||
|
||||||
|
(expectation.check_build_id && (info->build_id != expectation.build_id)) ||
|
||||||
|
(info->config_id != expectation.config_id)) {
|
||||||
|
return RS_EFORMAT;
|
||||||
|
}
|
||||||
|
return RS_OK;
|
||||||
|
#else
|
||||||
return rs_archinfo_status(archinfo_validate(info, &expectation));
|
return rs_archinfo_status(archinfo_validate(info, &expectation));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_archinfo_read_device_id(uint32_t device_id[4]) {
|
rs_status_t rs_archinfo_read_device_id(uint32_t device_id[4]) {
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ static bool rs_dma_address_valid(uintptr_t address, uint32_t byte_count, bool in
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool rs_dma_request_valid(const rs_dma_config_t *config) {
|
static bool rs_dma_request_valid(const rs_dma_config_t *config) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
if (config->kind != RS_DMA_KIND_MM_TO_MM) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
switch (config->kind) {
|
switch (config->kind) {
|
||||||
case RS_DMA_KIND_MM_TO_MM:
|
case RS_DMA_KIND_MM_TO_MM:
|
||||||
return (config->request == RS_DMA_REQUEST_SOFTWARE) ||
|
return (config->request == RS_DMA_REQUEST_SOFTWARE) ||
|
||||||
@@ -91,6 +96,11 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) {
|
|||||||
kind = (tcd->control >> RS_DMA_TCD_KIND_SHIFT) & UINT32_C(0x7);
|
kind = (tcd->control >> RS_DMA_TCD_KIND_SHIFT) & UINT32_C(0x7);
|
||||||
request = (tcd->control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF);
|
request = (tcd->control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF);
|
||||||
burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F);
|
burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F);
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
if (kind != (uint32_t)RS_DMA_KIND_MM_TO_MM) {
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ((kind > (uint32_t)RS_DMA_KIND_STREAM_TO_MM) ||
|
if ((kind > (uint32_t)RS_DMA_KIND_STREAM_TO_MM) ||
|
||||||
(request > (uint32_t)RS_DMA_REQUEST_CRYPTO_OUT) || (burst > RS_DMA_MAX_BURST_BEATS) ||
|
(request > (uint32_t)RS_DMA_REQUEST_CRYPTO_OUT) || (burst > RS_DMA_MAX_BURST_BEATS) ||
|
||||||
((tcd->source & UINT32_C(0x3)) != UINT32_C(0)) ||
|
((tcd->source & UINT32_C(0x3)) != UINT32_C(0)) ||
|
||||||
|
|||||||
+2
-2
@@ -454,7 +454,7 @@ rs_status_t rs_i2c_write_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_ad
|
|||||||
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
|
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
|
||||||
const rs_dma_request_t request =
|
const rs_dma_request_t request =
|
||||||
(bus == RS_I2C_BUS_0) ? RS_DMA_REQUEST_I2C0_TX : RS_DMA_REQUEST_I2C1_TX;
|
(bus == RS_I2C_BUS_0) ? RS_DMA_REQUEST_I2C0_TX : RS_DMA_REQUEST_I2C1_TX;
|
||||||
rs_dma_config_t dma_config;
|
rs_dma_config_t dma_config = {0};
|
||||||
|
|
||||||
if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) ||
|
if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) ||
|
||||||
(length > RS_I2C_DMA_MAX_BYTES)) {
|
(length > RS_I2C_DMA_MAX_BYTES)) {
|
||||||
@@ -499,7 +499,7 @@ rs_status_t rs_i2c_read_dma(rs_i2c_bus_t bus, uint16_t address, bool ten_bit_add
|
|||||||
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
|
const uint32_t channel = (bus == RS_I2C_BUS_0) ? RS_DMA_CHANNEL_I2C0 : RS_DMA_CHANNEL_I2C1;
|
||||||
const rs_dma_request_t request =
|
const rs_dma_request_t request =
|
||||||
(bus == RS_I2C_BUS_0) ? RS_DMA_REQUEST_I2C0_RX : RS_DMA_REQUEST_I2C1_RX;
|
(bus == RS_I2C_BUS_0) ? RS_DMA_REQUEST_I2C0_RX : RS_DMA_REQUEST_I2C1_RX;
|
||||||
rs_dma_config_t dma_config;
|
rs_dma_config_t dma_config = {0};
|
||||||
|
|
||||||
if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) ||
|
if (!rs_i2c_bus_valid(bus) || (data == NULL) || (workspace == NULL) || (length == 0U) ||
|
||||||
(length > RS_I2C_DMA_MAX_BYTES)) {
|
(length > RS_I2C_DMA_MAX_BYTES)) {
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ static rs_status_t rs_sysctrl_perf_offsets(rs_sysctrl_perf_counter_t counter, ui
|
|||||||
return RS_EINVAL;
|
return RS_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
if ((counter != RS_SYSCTRL_PERF_MGMT_WAIT) && (counter != RS_SYSCTRL_PERF_DMA_WAIT)) {
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
switch (counter) {
|
switch (counter) {
|
||||||
case RS_SYSCTRL_PERF_MGMT_WAIT:
|
case RS_SYSCTRL_PERF_MGMT_WAIT:
|
||||||
*low_offset = RS_SYSCTRL_PERF_MGMT_WAIT_LO_OFFSET;
|
*low_offset = RS_SYSCTRL_PERF_MGMT_WAIT_LO_OFFSET;
|
||||||
@@ -215,6 +221,9 @@ rs_status_t rs_sysctrl_clear_user_core_config_error(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_get_pll_config(uint8_t *selection) {
|
rs_status_t rs_sysctrl_get_pll_config(uint8_t *selection) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
if (selection == NULL) {
|
if (selection == NULL) {
|
||||||
return RS_EINVAL;
|
return RS_EINVAL;
|
||||||
}
|
}
|
||||||
@@ -224,6 +233,9 @@ rs_status_t rs_sysctrl_get_pll_config(uint8_t *selection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_set_pll_config(uint8_t selection) {
|
rs_status_t rs_sysctrl_set_pll_config(uint8_t selection) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
if (((uint32_t)selection & ~RS_SYSCTRL_PLL_SELECTION_MASK) != 0U) {
|
if (((uint32_t)selection & ~RS_SYSCTRL_PLL_SELECTION_MASK) != 0U) {
|
||||||
return RS_EINVAL;
|
return RS_EINVAL;
|
||||||
}
|
}
|
||||||
@@ -232,16 +244,25 @@ rs_status_t rs_sysctrl_set_pll_config(uint8_t selection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_apply_pll_config(void) {
|
rs_status_t rs_sysctrl_apply_pll_config(void) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
*rs_sysctrl_register(RS_SYSCTRL_PLL_CMD_OFFSET) = RS_SYSCTRL_PLL_CMD_APPLY;
|
*rs_sysctrl_register(RS_SYSCTRL_PLL_CMD_OFFSET) = RS_SYSCTRL_PLL_CMD_APPLY;
|
||||||
return RS_OK;
|
return RS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_clear_pll_error(void) {
|
rs_status_t rs_sysctrl_clear_pll_error(void) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
*rs_sysctrl_register(RS_SYSCTRL_PLL_CMD_OFFSET) = RS_SYSCTRL_PLL_CMD_CLEAR_ERROR;
|
*rs_sysctrl_register(RS_SYSCTRL_PLL_CMD_OFFSET) = RS_SYSCTRL_PLL_CMD_CLEAR_ERROR;
|
||||||
return RS_OK;
|
return RS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_get_pll_status(rs_sysctrl_pll_status_t *status) {
|
rs_status_t rs_sysctrl_get_pll_status(rs_sysctrl_pll_status_t *status) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
|
|
||||||
if (status == NULL) {
|
if (status == NULL) {
|
||||||
@@ -282,6 +303,9 @@ rs_status_t rs_sysctrl_clear_fault(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_set_hp_release(bool release) {
|
rs_status_t rs_sysctrl_set_hp_release(bool release) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
rs_sysctrl_hp_status_t status;
|
rs_sysctrl_hp_status_t status;
|
||||||
|
|
||||||
if ((rs_sysctrl_get_hp_status(&status) != RS_OK) || !status.present) {
|
if ((rs_sysctrl_get_hp_status(&status) != RS_OK) || !status.present) {
|
||||||
@@ -295,6 +319,9 @@ rs_status_t rs_sysctrl_set_hp_release(bool release) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) {
|
rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
|
|
||||||
if (status == NULL) {
|
if (status == NULL) {
|
||||||
@@ -311,6 +338,9 @@ rs_status_t rs_sysctrl_get_hp_status(rs_sysctrl_hp_status_t *status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_sysctrl_select_hp_debug(bool select_hp) {
|
rs_status_t rs_sysctrl_select_hp_debug(bool select_hp) {
|
||||||
|
#ifdef RS_SOC_TINY
|
||||||
|
return RS_ENOTSUP;
|
||||||
|
#endif
|
||||||
rs_sysctrl_hp_status_t status;
|
rs_sysctrl_hp_status_t status;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -279,7 +279,7 @@ rs_status_t rs_uart_irq_test(uint32_t mask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_uart_write_dma(const uint32_t *words, size_t count, rs_timeout_t timeout) {
|
rs_status_t rs_uart_write_dma(const uint32_t *words, size_t count, rs_timeout_t timeout) {
|
||||||
rs_dma_config_t config;
|
rs_dma_config_t config = {0};
|
||||||
size_t index;
|
size_t index;
|
||||||
rs_status_t status;
|
rs_status_t status;
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ rs_status_t rs_uart_write_dma(const uint32_t *words, size_t count, rs_timeout_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
rs_status_t rs_uart_read_dma(uint32_t *words, size_t count, rs_timeout_t timeout) {
|
rs_status_t rs_uart_read_dma(uint32_t *words, size_t count, rs_timeout_t timeout) {
|
||||||
rs_dma_config_t config;
|
rs_dma_config_t config = {0};
|
||||||
rs_status_t status;
|
rs_status_t status;
|
||||||
|
|
||||||
if ((words == NULL) || (count == 0U) || (count > (UINT32_MAX / sizeof(*words)))) {
|
if ((words == NULL) || (count == 0U) || (count > (UINT32_MAX / sizeof(*words)))) {
|
||||||
|
|||||||
@@ -7,6 +7,41 @@
|
|||||||
|
|
||||||
#define RS_WDG_COMMAND_TIMEOUT UINT32_C(1024)
|
#define RS_WDG_COMMAND_TIMEOUT UINT32_C(1024)
|
||||||
|
|
||||||
|
static rs_status_t rs_watchdog_status(wdg_status_t status) {
|
||||||
|
switch (status) {
|
||||||
|
case WDG_STATUS_OK:
|
||||||
|
return RS_OK;
|
||||||
|
case WDG_STATUS_INVALID_ARGUMENT:
|
||||||
|
return RS_EINVAL;
|
||||||
|
case WDG_STATUS_TIMEOUT:
|
||||||
|
return RS_ETIMEOUT;
|
||||||
|
case WDG_STATUS_INCOMPATIBLE:
|
||||||
|
return RS_EFORMAT;
|
||||||
|
default:
|
||||||
|
return RS_EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rs_status_t rs_watchdog_configure(const rs_watchdog_config_t *config) {
|
||||||
|
return rs_watchdog_status(wdg_configure((uintptr_t)RS_SOC_APB4_WDG_BASE, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
rs_status_t rs_watchdog_start(rs_timeout_t timeout) {
|
||||||
|
return rs_watchdog_status(wdg_start((uintptr_t)RS_SOC_APB4_WDG_BASE, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
rs_status_t rs_watchdog_service(rs_timeout_t timeout) {
|
||||||
|
return rs_watchdog_status(wdg_service((uintptr_t)RS_SOC_APB4_WDG_BASE, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
rs_status_t rs_watchdog_get_status(rs_watchdog_status_t *status, rs_timeout_t timeout) {
|
||||||
|
return rs_watchdog_status(wdg_snapshot((uintptr_t)RS_SOC_APB4_WDG_BASE, status, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
rs_status_t rs_watchdog_clear_reset_cause(rs_timeout_t timeout) {
|
||||||
|
return rs_watchdog_status(wdg_clear_reset_cause((uintptr_t)RS_SOC_APB4_WDG_BASE, timeout));
|
||||||
|
}
|
||||||
|
|
||||||
void ip_wdg_test(int argc, char **argv) {
|
void ip_wdg_test(int argc, char **argv) {
|
||||||
static const wdg_config_t config = {
|
static const wdg_config_t config = {
|
||||||
.prescale_divider = 1U,
|
.prescale_divider = 1U,
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
This directory contains repository-level engineering policy that supplements
|
This directory contains repository-level engineering policy that supplements
|
||||||
the root README and subsystem guides.
|
the root README and subsystem guides.
|
||||||
|
|
||||||
|
- [Tiny MCU](ip/tiny-soc.md) defines the independent wired Tiny product, AXI4/APB4, SRAM-only startup and IHP130 acceptance.
|
||||||
|
- [Tiny Gen1 datasheet](../publications/datasheets/tiny/README.md) describes the independently built product, register, software and qualification-boundary reference.
|
||||||
|
|
||||||
- [Contributing](../CONTRIBUTING.md) describes community discussion, development,
|
- [Contributing](../CONTRIBUTING.md) describes community discussion, development,
|
||||||
review, validation evidence, and maintainer responsibilities.
|
review, validation evidence, and maintainer responsibilities.
|
||||||
- [git-workflow.md](git-workflow.md) defines branch and commit conventions,
|
- [git-workflow.md](git-workflow.md) defines branch and commit conventions,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
| Tier | Profile | Automated coverage |
|
| Tier | Profile | Automated coverage |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| Pull request | `configs/ci/ihp130-tiny.mk` | Tiny firmware, Verilator/Icarus, lint; local Yosys, netlist and OpenSTA |
|
||||||
| Smoke | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator SVA compilation, Icarus assembly self-test |
|
| Smoke | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator SVA compilation, Icarus assembly self-test |
|
||||||
| Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus |
|
| Pull request | `configs/ci/ihp130.mk` | strict Verilator RTL lint, firmware, Verilator, Icarus |
|
||||||
| Pull request | `configs/ci/ihp130-debug.mk` | Verilator remote-bitbang JTAG DTM, Debug Module, OpenOCD, and GDB acceptance |
|
| Pull request | `configs/ci/ihp130-debug.mk` | Verilator remote-bitbang JTAG DTM, Debug Module, OpenOCD, and GDB acceptance |
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ This directory owns architecture, register ABI, software contract, and
|
|||||||
verification documentation for self-owned retroSoC peripheral IP and platform
|
verification documentation for self-owned retroSoC peripheral IP and platform
|
||||||
extension interfaces.
|
extension interfaces.
|
||||||
|
|
||||||
|
- [Tiny MCU](tiny-soc.md) defines the independent wired Tiny product, AXI4/APB4, SRAM-only startup and IHP130 acceptance.
|
||||||
|
|
||||||
- [timer.md](timer.md) defines the dual APB4 general timer.
|
- [timer.md](timer.md) defines the dual APB4 general timer.
|
||||||
- [sysctrl.md](sysctrl.md) defines the APB4 SystemCtrl register ABI, control-plane contracts, HAL, and verification.
|
- [sysctrl.md](sysctrl.md) defines the APB4 SystemCtrl register ABI, control-plane contracts, HAL, and verification.
|
||||||
- [resource-controller.md](resource-controller.md) defines root-managed resource ownership, IRQ routing, and cache-maintenance handoff.
|
- [resource-controller.md](resource-controller.md) defines root-managed resource ownership, IRQ routing, and cache-maintenance handoff.
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
# Tiny MCU Verification Record
|
||||||
|
|
||||||
|
This record accompanies the [frozen Tiny contract](tiny-soc.md). It describes
|
||||||
|
local implementation evidence from 2026-09-25, not a release or silicon signoff.
|
||||||
|
The working tree is uncommitted and the readiness status remains `prototype`.
|
||||||
|
|
||||||
|
## Configuration and artifact roots
|
||||||
|
|
||||||
|
The selected profile is `configs/ci/ihp130-tiny.mk`: IHP130, 128 KiB SRAM,
|
||||||
|
24 MHz, RV32IM firmware on an RV32IMC hart with A disabled. Runs below used
|
||||||
|
`BUILD_TIMESTAMP=2026-09-25-12-00`:
|
||||||
|
|
||||||
|
- Default `bringup`: `build/ihp130-tiny-2026-09-25-12-00-d6009644a134/`.
|
||||||
|
- `APP=ci_smoke`: `build/ihp130-tiny-2026-09-25-12-00-044d45791ef7/`.
|
||||||
|
- `APP=ci_smoke HAVE_SVA=YES`:
|
||||||
|
`build/ihp130-tiny-2026-09-25-12-00-6874f2ffdf11/`.
|
||||||
|
|
||||||
|
Configuration digests identify settings and the dependency lock, not a clean
|
||||||
|
source revision. A release requires rerunning from its reviewed commit.
|
||||||
|
|
||||||
|
## Functional and quality checks
|
||||||
|
|
||||||
|
| Check | Evidence/boundary |
|
||||||
|
| --- | --- |
|
||||||
|
| Tiny maps and source isolation | Generated address, IRQ, pad and clock/reset inputs validate; source closure excludes Mini product RTL, RIB/RIBP and HP generation. |
|
||||||
|
| AXI fabric, both simulators | Directed tests cover competing CPU/DMA requests, W-before-AW, delayed W, response backpressure, ID/RLAST, 16-beat bursts, 4 KiB crossing, alignment, reserved regions and continued operation after errors. |
|
||||||
|
| Tiny SYSCTRL, both simulators | APB tests cover unsupported controls, partial terminal writes, sticky first status, counter snapshots, first-fault retention, W1C, RTC wake and reset. |
|
||||||
|
| Verilator full firmware | `SIM_TEST_PASS`; the CI composition also passes with SVA enabled. Pin-level NOR, compressed instructions, disabled atomics, load faults, SRAM discovery, memory/UART DMA, external timer IRQ, CLINT time, RTC, GPIO, UART1 loopback, I2C NACK and watchdog reboot are exercised. |
|
||||||
|
| Icarus full firmware | Final expanded CI firmware passes at 3,705,751 cycles, matching the SVA-enabled Verilator run. |
|
||||||
|
| Icarus synthesized boot | `netsim-boot` passes at 29,362 cycles using the compact assembly image. Covers pin-level NOR, SRAM first/last words, byte writes, UART and terminal status; does not establish full C-firmware gate-level coverage. |
|
||||||
|
| SDK gates | C formatting, embedded-C policy and host tests pass, including Tiny channel/endpoint rejection compiled from the real DMA validator. No new Required-rule MISRA deviation is recorded; these partial checks do not certify MISRA conformance. |
|
||||||
|
| Focused Python tests | 112 tests passed across Tiny, address/pad generation and build/regression tooling. Shared debug-reset and SRAM/DMA register-parity checks also pass. After fixing parameterization/publication compatibility and preparing the existing MPW/device-model inputs, all 54 non-reference failures selected for rerun pass. |
|
||||||
|
| RTL policy | Changed RTL formatting, full owned RTL style audit and readiness metadata pass. Global formatting limitations are listed below. |
|
||||||
|
| Reproducibility | Dependency lock validation, Ruff, PR/nightly dry-runs and `git diff --check` pass. Tiny source packaging exports the Tiny top, actual filelist and its core timing SDC. |
|
||||||
|
|
||||||
|
The full-firmware verdict is authoritative only when both the tool result and
|
||||||
|
`result-sim-check.json` pass. UART text alone is not sufficient. Assembly netlist
|
||||||
|
acceptance uses the same strict terminal marker policy.
|
||||||
|
|
||||||
|
## Synthesis and timing
|
||||||
|
|
||||||
|
For the CI composition, Yosys completed with the IHP130 technology libraries
|
||||||
|
and 32 `tc_sram_1024x32` banks. The `balanced` report records 207,739 cells and
|
||||||
|
9,798,326.725198 square micrometers of library area. The area includes the
|
||||||
|
instantiated memory/pad hierarchy and is not a placed die-area estimate.
|
||||||
|
|
||||||
|
OpenSTA completed with the selected slow library corner and the 41.666666667 ns
|
||||||
|
system clock. Reported setup WNS is **-455.18 ns**, setup TNS is
|
||||||
|
**-13,049,600 ns**, and hold WNS/TNS are zero. The worst path starts at the
|
||||||
|
synchronized system-reset register and reaches peripheral FIFO storage through
|
||||||
|
the high-fanout reset distribution. This is an open physical/timing issue;
|
||||||
|
24 MHz is a functional target, not a timing-qualified operating point.
|
||||||
|
|
||||||
|
Reports reside under the CI variant's `syn/yosys/rpt/` and `sta/opensta/`.
|
||||||
|
The metric collector understands the Tiny top and timestamp-prefixed Yosys JSON
|
||||||
|
reports. Metrics remain in `observe` mode; no warning or metric baseline has
|
||||||
|
been edited to accept this feature.
|
||||||
|
|
||||||
|
## Commands and remaining coverage
|
||||||
|
|
||||||
|
The build/verification commands were run with the profile and timestamp above:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk doctor firmware sim
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke HAVE_SVA=YES firmware sim
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke SIMU=IVERILOG firmware sim
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke SYNTH=YOSYS synth
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke SIMU=IVERILOG netsim-boot
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk APP=ci_smoke STA=OPENSTA sta
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk metrics package
|
||||||
|
make sw-format-check sw-policy-check sw-host-test rtl-style-check-all rtl-readiness-check
|
||||||
|
python3 -m pytest -q tests/test_tiny.py tests/test_memory_map.py tests/test_pin_map.py tests/test_script_tools.py
|
||||||
|
python3 scripts/regress.py --root . --suite pr --pdk IHP130 --netsim-boot-only
|
||||||
|
```
|
||||||
|
|
||||||
|
The combined IHP130 regression stops in its Mini phase because the locked
|
||||||
|
VexiiRiscv checkout is absent. Tiny can be selected independently with
|
||||||
|
`--soc TINY`, or `make CONFIG=configs/ci/ihp130-tiny.mk regress-pr`.
|
||||||
|
The initial full Pytest run completed with 1,234 passed, 26 failed, 19 skipped
|
||||||
|
and 71 errors. Parameterization/format-sensitive expectations and the Mini
|
||||||
|
publication's constant/cast parsing were corrected; the affected cases were
|
||||||
|
rerun successfully. MPW/device-model inputs and a writable test ccache resolved
|
||||||
|
additional environment failures. The remaining 16 cases require the absent
|
||||||
|
locked FLAC corpus, MLPerf Tiny models or TensorFlow oracle sources. These are
|
||||||
|
not Tiny setup dependencies. The full suite was not rerun after the focused
|
||||||
|
corrections; it is not reported as a clean full-suite pass.
|
||||||
|
|
||||||
|
The initial run, focused results, combined-regression failure and remaining
|
||||||
|
reference-input case list are retained under the CI variant's
|
||||||
|
`meta/validation/`.
|
||||||
|
Global RTL/Make formatting checks encounter existing APU/accelerator files
|
||||||
|
outside this change. Changed-file formatting is checked independently.
|
||||||
|
|
||||||
|
Remaining qualification includes reset-tree/timing closure, placement/routing,
|
||||||
|
PVT/MMMC, CDC/RDC signoff, silicon and power measurements, full C netlist
|
||||||
|
acceptance, a Tiny-specific external OpenOCD session, positive external I2C
|
||||||
|
slave transactions and comprehensive pad-level PWM/RTC coverage. Wireless,
|
||||||
|
retention, secure boot and other PDKs remain explicit deferred features.
|
||||||
|
|
||||||
|
## Shared-path migration follow-up
|
||||||
|
|
||||||
|
Mini now references the canonical shared RTL, `scripts/rtl` helpers and
|
||||||
|
`rtl/mk/software.mk` directly. The 21 Mini compatibility links and seven Tiny
|
||||||
|
helper links have been removed. Filelists, simulator/formal/synthesis rules,
|
||||||
|
Python imports, tests, publication tooling and ownership documents use the new
|
||||||
|
paths. No Git file-type changes remain. Historical warning baselines are
|
||||||
|
unchanged.
|
||||||
|
|
||||||
|
The resolved Mini `commonip.fl`, `ip.fl` and `top.fl` retain the same 299
|
||||||
|
unique sources in the same order. `netlist_support.fl` retains its two sources;
|
||||||
|
`inc.fl` adds `rtl/ip/core`. All relocated shared RTL content hashes match the
|
||||||
|
pre-migration snapshot. This follow-up changes paths, not RTL behavior or the
|
||||||
|
public hardware/software interface.
|
||||||
|
|
||||||
|
Validation after removing the links:
|
||||||
|
|
||||||
|
- 245 focused tests passed: 144 build/generator/topology/style/Tiny tests,
|
||||||
|
96 publication/LibreLane tests and five debug-reset/SRAM simulations.
|
||||||
|
Full-suite collection succeeds with 1,356 tests; the full suite was not
|
||||||
|
rerun because of the reference-input gaps documented above.
|
||||||
|
- Mini `configs/ci/ihp130.mk` address/pad/topology/clock-reset checks and firmware
|
||||||
|
build pass. Its Verilator compile reaches HP generation and stops because
|
||||||
|
the locked VexiiRiscv checkout is missing; full Mini simulation and the
|
||||||
|
combined regression remain unverified.
|
||||||
|
- Tiny `configs/ci/ihp130-tiny.mk`, `APP=ci_smoke HAVE_SVA=YES`, firmware and
|
||||||
|
Verilator simulation pass again at 3,705,751 cycles. Full Icarus, synthesis
|
||||||
|
and STA were not repeated for this path-only follow-up.
|
||||||
|
- Eleven Python CLI entrypoints work outside the repository directory.
|
||||||
|
Mini SRAM formal filelist generation, `sv2v` conversion, filelist combination
|
||||||
|
and dependency generation pass using the canonical shared paths, including
|
||||||
|
the relocated SRAM header. Formal proofs were not rerun.
|
||||||
|
- Ruff, owned RTL style, readiness metadata, 251 local Markdown links and
|
||||||
|
`git diff --check` pass. The Make formatter still reports existing EOF and
|
||||||
|
formal-command indentation differences in APU/GA2D/formal fragments; those
|
||||||
|
unrelated differences were preserved.
|
||||||
|
|
||||||
|
Run logs, exact commands and path snapshots are retained under
|
||||||
|
`build/ihp130-2026-09-25-12-00-9903b3d112b7/meta/validation/shared-path-migration/`.
|
||||||
|
No C register/API change or additional MISRA deviation belongs to this
|
||||||
|
follow-up; the SDK gates from the implementation record above were not rerun.
|
||||||
|
|
||||||
|
## Next review
|
||||||
|
|
||||||
|
```text
|
||||||
|
Use $retrosoc-mini-feature-review in review mode for feature tiny-soc. Review the current worktree diff against docs/ip/tiny-soc.md and docs/ip/tiny-soc-verification.md. Verify Tiny isolation, default Mini compatibility, AXI/APB/error/reset behavior and the SDK ABI. Treat the observed reset-fanout timing deficit and missing APU/NPU/VexiiRiscv inputs as explicit qualification gaps; do not claim timing closure or alter warning/metric baselines.
|
||||||
|
```
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# retroSoC Tiny MCU Contract
|
||||||
|
|
||||||
|
## Purpose and research boundary
|
||||||
|
|
||||||
|
This contract freezes the approved Tiny wired-MCU first release. Tiny owns its
|
||||||
|
integration in `rtl/tiny`; Mini remains a separate product. The committed
|
||||||
|
`configs/ci/ihp130-tiny.mk` profile is the reproducible implementation boundary.
|
||||||
|
The design was approved on 2026-09-25. Source presence does not establish
|
||||||
|
verification, timing closure, silicon qualification, or low-power performance.
|
||||||
|
|
||||||
|
The [RP2350](https://www.raspberrypi.com/products/rp2350/) is a Hazard3-based MCU
|
||||||
|
reference for software, SRAM, and deterministic I/O. Its dual-core, security,
|
||||||
|
USB, and PIO features are not Tiny requirements. The
|
||||||
|
[CAST SRAM controller](https://www.cast-inc.com/peripherals/memory-controllers/sram-ctrl)
|
||||||
|
illustrates native AXI synchronous SRAM and documented verification delivery;
|
||||||
|
no commercial implementation or qualification is reused.
|
||||||
|
|
||||||
|
## Requirements and non-goals
|
||||||
|
|
||||||
|
- TINY-001: one Hazard3 hart, RV32IMC with A disabled, mandatory debug support;
|
||||||
|
existing RV32IM firmware remains the default compiler target.
|
||||||
|
- TINY-002: native 32-bit AXI4 data and APB4 control. The CPU's native AHB-Lite
|
||||||
|
terminates at a direct AXI adapter. No RIB/RIBP source or interface is part of
|
||||||
|
Tiny's compilation or elaboration closure.
|
||||||
|
- TINY-003: 128 KiB technology-backed SRAM, XPI NOR boot, no external RAM
|
||||||
|
dependency. Reset vector is `0x00000000`; SRAM begins at `0x30000000`.
|
||||||
|
- TINY-004: 32 GPIO, two UARTs, two I2C controllers, two general timers,
|
||||||
|
CLINT, four DMA channels, PWM, RTC, watchdog, SYSCTRL and architecture info.
|
||||||
|
- TINY-005: IHP130, 24 MHz system clock, no PLL, 1 MHz CLINT timebase. CPU,
|
||||||
|
AXI, APB, RTC and watchdog share the system clock.
|
||||||
|
- TINY-006: Tiny must build independently of MPW, HP CPU generation and
|
||||||
|
multimedia inputs. Shared SDK APIs retain the `rs_` namespace.
|
||||||
|
- TINY-007: no wireless IP, radio-specific host integration, or wireless stack.
|
||||||
|
|
||||||
|
Deferred: RV32 A atomics, RTOS ports, authenticated boot, retention/power gating,
|
||||||
|
independent sleep clock, 256–512 KiB SRAM, USB, SDIO, standalone general SPI,
|
||||||
|
I2S, CAN, ADC, multimedia accelerators and other PDK qualification. XPI retains
|
||||||
|
the existing four-chip-select interface; initial pin-level acceptance uses NOR
|
||||||
|
on NSS0. The unpopulated chip selects are not qualified device configurations.
|
||||||
|
|
||||||
|
## Selected architecture and interfaces
|
||||||
|
|
||||||
|
`retrosoc_tiny` integrates the CPU, two-master fabric, SRAM, XPI and one APB
|
||||||
|
island. `retrosoc_tiny_asic` owns technology pads; `retrosoc_tiny_tb` owns device
|
||||||
|
models and terminal verdicts. Product maps, filelists and bindings belong to
|
||||||
|
Tiny; reusable core/debug, memory and bus utilities belong to shared RTL IP.
|
||||||
|
|
||||||
|
AXI has 32-bit addresses/data and one-bit ID/USER. CPU and DMA share one
|
||||||
|
globally active read or write transaction. Round-robin ownership changes only
|
||||||
|
after B or RLAST handshake; accepted W traffic cannot change owners. Backpressure
|
||||||
|
must preserve VALID payloads. Memory accepts naturally aligned 1/2/4-byte INCR
|
||||||
|
transactions of 1–16 beats within a target and 4 KiB page. MMIO accepts one-beat
|
||||||
|
FIXED/INCR accesses. Unsupported size/burst/lock/alignment or crossing returns
|
||||||
|
SLVERR; absent regions return DECERR. Rejected reads return the advertised
|
||||||
|
number of response beats; rejected writes drain their accepted data before B.
|
||||||
|
|
||||||
|
APB uses full setup/access phases, PSTRB and PSLVERR. Registers retain their
|
||||||
|
existing IP offset, access and reset contracts. A missing peripheral has no
|
||||||
|
successful decode. GPIO alternate functions retain the selected Mini mappings;
|
||||||
|
unsupported alternate functions drive neither pads nor peripheral requests.
|
||||||
|
UART TX/RX and XPI have dedicated pins.
|
||||||
|
|
||||||
|
## DMA and interrupt contract
|
||||||
|
|
||||||
|
DMA retains the V2 manual register ABI, direct and TCD modes, 32-bit transfers,
|
||||||
|
up-to-16-beat memory bursts and existing completion/error/W1C/abort semantics.
|
||||||
|
Four channels are available: 0 UART0, 1 I2C0, 2 I2C1, 3 bulk/XPI. UART1 uses
|
||||||
|
PIO/IRQ. Stream endpoints and requests for omitted devices are unsupported and
|
||||||
|
must fail validation rather than wait indefinitely. Capability discovery and
|
||||||
|
SDK range checks must agree with hardware.
|
||||||
|
|
||||||
|
CLINT software/timer occupy core bits 0/1. Reused external sources keep Mini
|
||||||
|
core-bit numbering: UART0 2, timer0 3, timer1 4, I2C0 7, XPI 9, PWM 11,
|
||||||
|
RTC 13, watchdog 14, GPIO 18, I2C1 19, DMA 20, UART1 26. The vector has 32
|
||||||
|
bits (30 Hazard3 external inputs); all omitted sources are zero.
|
||||||
|
|
||||||
|
## Register and software ABI
|
||||||
|
|
||||||
|
Tiny maps reuse the addresses of implemented Mini peripherals, Flash alias and
|
||||||
|
XPI aperture. Generated outputs contain address/IRQ/capability metadata and
|
||||||
|
linker regions, not new generated IP register definitions. SVH/C register pairs
|
||||||
|
remain handwritten and checked for parity. Hardware identity must describe Tiny,
|
||||||
|
one MCU hart, 128 KiB SRAM and actual capabilities.
|
||||||
|
|
||||||
|
Tiny SYSCTRL preserves fault capture, performance enable/clear and TEST_STATUS
|
||||||
|
at the existing offsets. The first full-word TEST_STATUS write with bit 31 set
|
||||||
|
is sticky until system reset; bit 0 is pass and bits 15:8 are the result code.
|
||||||
|
Unsupported Mini lifecycle/clock controls fail safely; they do not enable HP or
|
||||||
|
user-core behavior. Unavailable IP drivers are omitted from the Tiny firmware composition.
|
||||||
|
Unsupported SYSCTRL lifecycle/PLL operations and DMA channel/endpoint requests
|
||||||
|
return an error before MMIO. GPIO alternate modes follow the Tiny pin map.
|
||||||
|
|
||||||
|
The `bringup` and `ci_smoke` applications select product-specific entrypoints.
|
||||||
|
Startup skips PSRAM setup on Tiny and uses `ld2_all_sram`: code/data copy from
|
||||||
|
Flash into SRAM, with SRAM BSS and stack. IRQ/CSR support is enabled. Public
|
||||||
|
headers remain under `<retrosoc/...>`; retired `tiny*.h` paths are not restored.
|
||||||
|
|
||||||
|
## Clock, reset and lifecycle
|
||||||
|
|
||||||
|
External reset is asynchronously asserted and synchronously released. Watchdog
|
||||||
|
reset restarts the system. Debug hart reset waits for the CPU AXI adapter to
|
||||||
|
be idle; it must not abandon an accepted transaction. JTAG and asynchronous
|
||||||
|
external inputs retain the approved Common synchronizer/handshake boundaries.
|
||||||
|
There is no cross-domain system bus or dynamic clock switching in this release.
|
||||||
|
RTC/WDG run at the configured system frequency and provide no independent
|
||||||
|
deep-sleep guarantee. SRAM contents are not initialized by reset.
|
||||||
|
|
||||||
|
## Errors, observability and security
|
||||||
|
|
||||||
|
Decode/protocol faults complete with AXI errors and feed sticky SYSCTRL fault
|
||||||
|
information. DMA records errors and supports bounded software waits and abort.
|
||||||
|
Counters and terminal-test status permit firmware and simulation diagnosis.
|
||||||
|
Watchdog provides whole-system recovery from a nonresponsive target; this
|
||||||
|
release makes no unrestricted AXI liveness, secure-boot, production-entropy,
|
||||||
|
isolation, or safety-certification claim.
|
||||||
|
|
||||||
|
## Development order and acceptance
|
||||||
|
|
||||||
|
### TINY-P0 — Freeze Tiny MCU Contract
|
||||||
|
|
||||||
|
Record the approved requirements here and update document indexes. Acceptance:
|
||||||
|
reviewable specification, valid links and `git diff --check`.
|
||||||
|
|
||||||
|
### TINY-P1 — Product Selection and Shared Infrastructure
|
||||||
|
|
||||||
|
Add SOC=TINY, the committed profile, dependency selection and shared build/IP
|
||||||
|
boundaries. Preserve Mini defaults. Acceptance: configuration/generator tests,
|
||||||
|
dependency lock validation, no implicit Tiny dependency on Mini product RTL.
|
||||||
|
|
||||||
|
### TINY-P2 — AXI4/APB4 Tiny RTL
|
||||||
|
|
||||||
|
Implement the hierarchy and contracts above. Acceptance: lint, standalone AXI,
|
||||||
|
APB, memory, reset and IRQ checks; no RIB/RIBP in the resolved source closure.
|
||||||
|
|
||||||
|
### TINY-P3 — SDK Boot and System Verification
|
||||||
|
|
||||||
|
Implement generated capabilities, SRAM-only startup, HAL selection and firmware.
|
||||||
|
Acceptance: both simulators boot through pin-level NOR and emit SIM_TEST_PASS;
|
||||||
|
test SRAM boundaries, DMA, peripherals, IRQ, disabled A, C instructions and debug.
|
||||||
|
|
||||||
|
### TINY-P4 — IHP130 Qualification and Documentation
|
||||||
|
|
||||||
|
Add Tiny to IHP130 regression without replacing Mini coverage. Run format/style,
|
||||||
|
lint, software policy/host tests, Ruff, Pytest, firmware and supported regressions.
|
||||||
|
Run Yosys, Icarus netlist and OpenSTA; archive macro mapping, area, cell count,
|
||||||
|
24 MHz timing and warnings under the configuration's build variant. Check docs
|
||||||
|
and example commands. Record unrun/failed gates explicitly; do not promote
|
||||||
|
warning baselines or metrics policy as part of this feature.
|
||||||
|
|
||||||
|
## Reproducible commands and current evidence
|
||||||
|
|
||||||
|
Use the committed profile, and preserve one `BUILD_TIMESTAMP` when commands
|
||||||
|
must share artifacts:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk setup doctor
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk firmware sim
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG firmware sim
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk SYNTH=YOSYS synth
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG netsim-boot
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk STA=OPENSTA sta
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk metrics package
|
||||||
|
python3 scripts/regress.py --root . --suite pr --pdk IHP130 --soc TINY --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
`netsim-boot` runs the dedicated `app/asm/tiny_boot.S` gate-level image. It checks
|
||||||
|
pin-level NOR boot, both ends of physical SRAM, a byte-lane write, UART and the
|
||||||
|
SYSCTRL terminal result. It requires `SIM_TEST_PASS`; this is separate from the
|
||||||
|
complete SDK/IRQ/DMA/watchdog acceptance in both RTL simulators. `netsim` retains
|
||||||
|
the full firmware option. The shared SRAM IP's capability register describes
|
||||||
|
the IP's native superset; end-to-end requests must obey Tiny's narrower fabric
|
||||||
|
contract above.
|
||||||
|
|
||||||
|
Tiny uses two-cycle AHB ERROR completion so Hazard3 can trap AXI failures. The
|
||||||
|
shared core/bridge parameters retain Mini's existing default behavior. Tiny
|
||||||
|
ArchInfo reports SOC_ID `0x54494e59`, topology `0x20200001`, and FEATURES0
|
||||||
|
`0x00007ffe`: bits 1/2 SRAM interface/macro, 3 AXI, 4 APB, 5 DMA, 6 UART,
|
||||||
|
7 I2C, 8 PWM, 9 RTC, 10 watchdog, 11 GPIO, 12 CLINT, 13 timers and 14 XPI.
|
||||||
|
Bit 0 (PLL) is zero. Tiny FAULT_DETAIL contains the raw AXI response (2/3),
|
||||||
|
FAULT_STATUS reason is 1 for decode and 2 for slave/protocol errors,
|
||||||
|
and FAULT_MASTER uses 0 for CPU and 1 for DMA; no RIB encoding is exported.
|
||||||
|
The public watchdog API is `rs_watchdog_*` with `rs_status_t` and bounded waits.
|
||||||
|
|
||||||
|
The [verification record](tiny-soc-verification.md) distinguishes successful
|
||||||
|
functional checks from missing reference inputs and physical closure. The
|
||||||
|
product remains `prototype`: negative pre-layout reset-fanout timing must not
|
||||||
|
be presented as a qualified 24 MHz implementation.
|
||||||
|
|
||||||
|
## Commercial delivery gaps
|
||||||
|
|
||||||
|
Current-revision verification and physical reports govern readiness. Reusable
|
||||||
|
VIP, coverage closure, full CDC/RDC, DFT/MBIST, PVT/MMMC, post-layout timing,
|
||||||
|
power characterization, regulatory and silicon qualification remain separate.
|
||||||
@@ -32,8 +32,10 @@ and reviewed evidence for the applicable rules.
|
|||||||
- Use `rs_status_t` for fallible operations. Prefer bounded string/format APIs
|
- Use `rs_status_t` for fallible operations. Prefer bounded string/format APIs
|
||||||
and timeout-based register waits. Dynamic allocation is not permitted in
|
and timeout-based register waits. Dynamic allocation is not permitted in
|
||||||
self-owned embedded C.
|
self-owned embedded C.
|
||||||
- New `tiny` identifiers, legacy include paths, and prohibited library calls
|
- Retired `tiny`-prefixed SDK identifiers, legacy include paths, and prohibited library calls
|
||||||
are not allowed outside the compatibility files explicitly listed by policy.
|
are not allowed outside the compatibility files explicitly listed by policy.
|
||||||
|
The Tiny product name in prose/string literals and `RS_SOC_TINY` product
|
||||||
|
selection do not restore those retired SDK interfaces.
|
||||||
|
|
||||||
The standard is referenced by name and category only. Consult the licensed
|
The standard is referenced by name and category only. Consult the licensed
|
||||||
MISRA publication for the complete rule text and applicability guidance; do
|
MISRA publication for the complete rule text and applicability guidance; do
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ validated without changing functional behavior.
|
|||||||
The owned profile in
|
The owned profile in
|
||||||
[`rtl/rtl_style_manifest.json`](../rtl/rtl_style_manifest.json) is the source
|
[`rtl/rtl_style_manifest.json`](../rtl/rtl_style_manifest.json) is the source
|
||||||
of truth for the reviewed source set. It includes `rtl/ip/**` and
|
of truth for the reviewed source set. It includes `rtl/ip/**` and
|
||||||
`rtl/mini/top/**`, but excludes managed, generated, PDK, formal, device-model,
|
`rtl/mini/top/**` and `rtl/tiny/top/**`, but excludes managed, generated, PDK, formal, device-model,
|
||||||
and third-party sources.
|
and third-party sources.
|
||||||
|
|
||||||
[`rtl/rtl_style_audit.json`](../rtl/rtl_style_audit.json) records the exact
|
[`rtl/rtl_style_audit.json`](../rtl/rtl_style_audit.json) records the exact
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ differ: retroSoC uses `clk_i` and `rst_n_i`, not `rst_ni`.
|
|||||||
|
|
||||||
## 1. Scope and rule levels
|
## 1. Scope and rule levels
|
||||||
|
|
||||||
The owned profile applies to `rtl/ip/**` and `rtl/mini/top/**`. Formal,
|
The owned profile applies to `rtl/ip/**`, `rtl/mini/top/**`, and `rtl/tiny/top/**`. Formal,
|
||||||
device-model, PDK, generated, managed, and third-party sources use the profiles
|
device-model, PDK, generated, managed, and third-party sources use the profiles
|
||||||
declared in [`rtl/rtl_style_manifest.json`](../rtl/rtl_style_manifest.json).
|
declared in [`rtl/rtl_style_manifest.json`](../rtl/rtl_style_manifest.json).
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
## Status and Scope
|
## Status and Scope
|
||||||
|
|
||||||
This document defines the intended Tiny, Mini, Std, and Pro product ladder.
|
This document defines the intended Tiny, Mini, Std, and Pro product ladder.
|
||||||
It is a product roadmap, not a statement of implemented repository support.
|
Tiny and Mini have executable build profiles; Std and Pro remain roadmap
|
||||||
The current build system accepts only `SOC=MINI`, and the committed Mini
|
targets. Committed product profiles and retained validation evidence define
|
||||||
profiles remain the executable source of truth.
|
implemented support. Tiny first targets IHP130 as a wired MCU.
|
||||||
|
|
||||||
Mini is the family anchor. It establishes the common product model: an open
|
Mini is the family anchor. It establishes the common product model: an open
|
||||||
RISC-V SoC in which a small, always-available Hazard3 management core owns
|
RISC-V SoC in which a small, always-available Hazard3 management core owns
|
||||||
@@ -14,8 +14,8 @@ add application processors and accelerators without transferring final
|
|||||||
lifecycle control to Linux. Tiny reduces this model to an MCU-class device and
|
lifecycle control to Linux. Tiny reduces this model to an MCU-class device and
|
||||||
does not require a separate application processor.
|
does not require a separate application processor.
|
||||||
|
|
||||||
All frequencies, memory sizes, bus widths, accelerator rates, and software
|
Except for the explicitly identified executable baselines, frequencies, memory
|
||||||
features below are product targets. They require separate RTL integration,
|
sizes, bus widths, accelerator rates, and software features below are product targets. They require separate RTL integration,
|
||||||
driver enablement, verification, and physical qualification before they can be
|
driver enablement, verification, and physical qualification before they can be
|
||||||
advertised for a device or PDK.
|
advertised for a device or PDK.
|
||||||
|
|
||||||
@@ -47,45 +47,36 @@ nodes, protocol bridges, and smart-home edge endpoints. It is MCU-first: boot
|
|||||||
latency, sleep behavior, deterministic I/O, security, package cost, and usable
|
latency, sleep behavior, deterministic I/O, security, package cost, and usable
|
||||||
energy per event take precedence over Linux compatibility.
|
energy per event take precedence over Linux compatibility.
|
||||||
|
|
||||||
Tiny has two productization profiles:
|
The first executable product is **Tiny-MCU**, an independent wired MCU
|
||||||
|
integration in `rtl/tiny`, selected with `configs/ci/ihp130-tiny.mk`. It has no
|
||||||
|
wireless IP, wireless protocol stack, or radio-specific companion integration.
|
||||||
|
Tiny-Connect, BLE, IEEE 802.15.4 and Wi-Fi are deferred product research.
|
||||||
|
|
||||||
- **Tiny-MCU** contains no radio and targets wired control or products that do
|
### First-release Architecture
|
||||||
not need connectivity on every unit.
|
|
||||||
- **Tiny-Connect** provides Bluetooth Low Energy and IEEE 802.15.4 capability.
|
|
||||||
The open digital-RTL baseline should expose a qualified SPI or SDIO host,
|
|
||||||
interrupt, reset, wake, and power-sequencing interface to a certified radio.
|
|
||||||
On-die or co-packaged RF becomes a product option only after suitable RF IP,
|
|
||||||
analog integration, PDK support, and regulatory qualification exist.
|
|
||||||
|
|
||||||
Wi-Fi 6 is an optional upper Tiny-Connect SKU rather than a baseline
|
| Area | Tiny MCU baseline |
|
||||||
requirement. This avoids imposing its RF, memory, and active-power costs on
|
|
||||||
802.15.4 and Bluetooth endpoint products.
|
|
||||||
|
|
||||||
### Recommended Architecture
|
|
||||||
|
|
||||||
| Area | Product target |
|
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| CPU | One Hazard3 RV32IMAC core at 64-160 MHz, subject to PDK qualification |
|
| CPU | One Hazard3 RV32IMC hart; A extension disabled until atomic bus semantics are qualified |
|
||||||
| On-chip memory | 256-512 KiB banked SRAM, boot ROM, and 8-32 KiB retention SRAM |
|
| On-chip memory | 128 KiB macro-backed SRAM; 256–512 KiB and retention SRAM deferred |
|
||||||
| Code storage | QSPI flash with execute-in-place and authenticated recovery boot |
|
| Code storage | XPI NOR boot, loaded into SRAM; authenticated recovery boot deferred |
|
||||||
| Memory model | No MMU and no external DRAM dependency |
|
| Memory model | No MMU, HP hart, or external DRAM/PSRAM dependency |
|
||||||
| Interconnect | 32-bit RIB; bounded `INCR4` support for DMA and XIP, without a full AXI4 fabric |
|
| Interconnect | 32-bit AXI4 data plane and APB4 control; no RIB/RIBP |
|
||||||
| Software | Freestanding SDK, bare metal, and Zephyr- or FreeRTOS-class RTOS ports |
|
| Software | Existing freestanding SDK, RV32IM compiler target, CSR/IRQ-enabled acceptance firmware |
|
||||||
| Low power | Clock gating, switchable SRAM banks, RTC/event wake, retention, and a separately measured deep-sleep state |
|
| Clock/reset | 24 MHz system clock, no PLL, 1 MHz CLINT timebase, watchdog and JTAG reset |
|
||||||
| Security | Immutable boot root, signed boot, OTP key material, PMP, TRNG, and symmetric/hash acceleration |
|
| Edge I/O | 32 GPIO, two UARTs, two I2C controllers, two timers, four DMA channels, PWM, RTC, watchdog and XPI |
|
||||||
| Edge I/O | UART, SPI, I2C, PWM, ADC, I2S/PDM, USB full speed, and CAN FD selected by package profile |
|
|
||||||
|
|
||||||
No power-current number should be published until it is measured on a
|
The normative [Tiny MCU contract](ip/tiny-soc.md) defines the AXI subset,
|
||||||
qualified physical implementation with the wake sources and retention state
|
address/interrupt ABI, startup, errors and phased acceptance. Independent
|
||||||
specified. Tiny's acceptance criteria should include sleep-to-active latency,
|
low-power clocks, clock/power gating, retention, secure boot, RTOS ports, USB,
|
||||||
energy per sensing/reporting cycle, and certified-radio interoperability, not
|
SDIO, standalone general SPI, CAN and ADC remain future work. No measured
|
||||||
only CPU benchmarks.
|
frequency ceiling, power-current, wake-latency or security claim follows from
|
||||||
|
this initial functional profile.
|
||||||
|
|
||||||
### Commercial Reference Points
|
### Commercial Reference Points
|
||||||
|
|
||||||
| Commercial SoC | Relevant axis | Position relative to Tiny |
|
| Commercial SoC | Relevant axis | Position relative to Tiny |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| [Espressif ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | Low-power RV32 MCU with Bluetooth LE and IEEE 802.15.4 | Primary Tiny-Connect endpoint and Thread/Zigbee market reference |
|
| [Espressif ESP32-H2](https://www.espressif.com/en/products/socs/esp32-h2) | Low-power RV32 MCU with Bluetooth LE and IEEE 802.15.4 | Deferred Tiny-Connect research reference |
|
||||||
| [Espressif ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | RV32 high- and low-power cores with Wi-Fi 6, Bluetooth LE, and IEEE 802.15.4 | Upper connectivity reference; RF and protocol integration are well beyond a radio-companion Tiny baseline |
|
| [Espressif ESP32-C6](https://www.espressif.com/en/products/socs/esp32-c6) | RV32 high- and low-power cores with Wi-Fi 6, Bluetooth LE, and IEEE 802.15.4 | Upper connectivity reference; RF and protocol integration are well beyond a radio-companion Tiny baseline |
|
||||||
| [Raspberry Pi RP2350](https://www.raspberrypi.com/products/rp2350/) | Dual Hazard3 option, 520 KiB SRAM, security, USB, and programmable I/O | Closest open-core MCU and deterministic-I/O reference, without integrated radio |
|
| [Raspberry Pi RP2350](https://www.raspberrypi.com/products/rp2350/) | Dual Hazard3 option, 520 KiB SRAM, security, USB, and programmable I/O | Closest open-core MCU and deterministic-I/O reference, without integrated radio |
|
||||||
| [ST STM32U5](https://www.st.com/en/microcontrollers-microprocessors/stm32u5-series.html) | Ultra-low-power secure MCU family with large embedded memory and graphics options | Energy-efficiency, security, and industrial MCU ecosystem reference rather than an ISA-equivalent peer |
|
| [ST STM32U5](https://www.st.com/en/microcontrollers-microprocessors/stm32u5-series.html) | Ultra-low-power secure MCU family with large embedded memory and graphics options | Energy-efficiency, security, and industrial MCU ecosystem reference rather than an ISA-equivalent peer |
|
||||||
@@ -416,7 +407,7 @@ software quality.
|
|||||||
|
|
||||||
| Tier | Claim gate |
|
| Tier | Claim gate |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| Tiny | Measured active and sleep power, bounded wake latency, secure-boot validation, and qualified edge-connectivity operation |
|
| Tiny | First release: wired-MCU boot, AXI/APB/DMA/IRQ and IHP130 evidence; future low-power/security claims require separate measurements and qualification |
|
||||||
| Mini | Repeatable Linux boot, at least 64 MiB usable main memory, native memory bursts, and management-controlled start/stop recovery |
|
| Mini | Repeatable Linux boot, at least 64 MiB usable main memory, native memory bursts, and management-controlled start/stop recovery |
|
||||||
| Std | Full AXI4 ordering tests, coherent accelerator traffic, 1080p60 graphical desktop, audio playback, and NPU inference under concurrent DMA load |
|
| Std | Full AXI4 ordering tests, coherent accelerator traffic, 1080p60 graphical desktop, audio playback, and NPU inference under concurrent DMA load |
|
||||||
| Pro | Four-hart coherent SMP stress, RV64 distribution boot, more-than-4-GiB memory validation, and concurrent GPU/NPU/video operation |
|
| Pro | Four-hart coherent SMP stress, RV64 distribution boot, more-than-4-GiB memory validation, and concurrent GPU/NPU/video operation |
|
||||||
|
|||||||
+3
-1
@@ -7,7 +7,9 @@ drivers.
|
|||||||
- `pdk/` contains the locked PDK setup helpers and managed PDK checkouts.
|
- `pdk/` contains the locked PDK setup helpers and managed PDK checkouts.
|
||||||
- `smoke/syn/` contains Yosys synthesis and source-export integration.
|
- `smoke/syn/` contains Yosys synthesis and source-export integration.
|
||||||
- `smoke/sta/` contains OpenSTA constraints and timing integration.
|
- `smoke/sta/` contains OpenSTA constraints and timing integration.
|
||||||
- `librelane/` contains the open-source IHP130 core-hardening and full-chip pad-ring flows.
|
- `librelane/` contains the open-source IHP130 implementation flows, organized
|
||||||
|
per product line: `librelane/mini/` (core hardening and full-chip pad ring)
|
||||||
|
and `librelane/tiny/` (Tiny full-chip pad ring).
|
||||||
- `ecc/` contains the open-source ICS55 padless-core hardening flow backed by
|
- `ecc/` contains the open-source ICS55 padless-core hardening flow backed by
|
||||||
the locked ECOS Chip Compiler release.
|
the locked ECOS Chip Compiler release.
|
||||||
- `commercial/` contains licensed-tool orchestration and flow logic. PDKs,
|
- `commercial/` contains licensed-tool orchestration and flow logic. PDKs,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from typing import Any
|
|||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[3]
|
ROOT = Path(__file__).resolve().parents[3]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
from rtl.mini.pin_map.generate_pin_map import read_map # noqa: E402
|
from scripts.rtl.generate_pin_map import read_map # noqa: E402
|
||||||
from scripts.check_clock_reset_domains import validate # noqa: E402
|
from scripts.check_clock_reset_domains import validate # noqa: E402
|
||||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,27 @@
|
|||||||
# LibreLane IHP130 Flows
|
# LibreLane IHP130 Flows
|
||||||
|
|
||||||
This directory owns the open-source IHP130 implementation entry points for two
|
This directory groups the open-source LibreLane/IHP130 implementation flows by
|
||||||
independent `retrosoc` deliverables:
|
product line:
|
||||||
|
|
||||||
- `librelane-core` hardens `retrosoc_core`, a padless logical macro with the
|
- [`mini/`](mini/README.md) hardens the Mini SoC deliverables (`retrosoc_core`
|
||||||
canonical external pin contract and the three USB packet SRAM macros.
|
padless macro and the bare-die `retrosoc_asic` pad-ring chip) for
|
||||||
- `librelane-chip` implements the bare-die `retrosoc_asic` package-facing SoC,
|
`SOC=MINI` profiles such as `configs/ci/ihp130.mk`.
|
||||||
including its pad ring, bondpads, fillers, corners, seal ring, and the same
|
- [`tiny/`](tiny/README.md) implements the bare-die Tiny MCU
|
||||||
SRAM macros.
|
(`retrosoc_tiny_asic`, pad-ring chip only) for `SOC=TINY` with
|
||||||
|
`configs/ci/ihp130-tiny.mk`.
|
||||||
|
- `bondpad/` holds the shared 70 um bondpad LEF collateral used by both chip
|
||||||
|
flows.
|
||||||
|
|
||||||
These are separate single-level runs. The chip flow does not consume the core
|
The top-level `Makefile` includes `mini/Makefile` or `tiny/Makefile` according
|
||||||
run as a hierarchical macro, so either artifact can be developed and reviewed
|
to `SOC`, so the target names (`librelane-doctor`, `librelane-chip`,
|
||||||
without a two-level integration dependency.
|
`librelane-package`, and the Mini-only `librelane-core`) are uniform across
|
||||||
|
product lines.
|
||||||
The flow consumes the committed `configs/ci/ihp130.mk` profile, canonical SoC
|
|
||||||
pin map, and IHP-Open-PDK revision in `dependencies/dependencies.lock.json`.
|
|
||||||
LibreLane reads the upstream `ihp-sg13g2/libs.tech/librelane` configuration
|
|
||||||
directly through manual-PDK mode. Generated RTL, SDC, configuration, run
|
|
||||||
databases, reports, and final views are written below the selected
|
|
||||||
`build/<variant>/physical/librelane/{core,chip}/` directory.
|
|
||||||
|
|
||||||
Use the LibreLane 3.0.5 environment supplied by the development shell:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-doctor
|
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-core
|
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-chip
|
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-core-package
|
|
||||||
make CONFIG=configs/ci/ihp130.mk librelane-package
|
|
||||||
```
|
|
||||||
|
|
||||||
The chip target places all 111 signal pads, 80 core/IO power and ground pads,
|
|
||||||
four corners, I/O fillers, and 70 um bondpads. It is a bare-die plan, not a
|
|
||||||
QFN/BGA package definition. The SDC constrains the external clock at 72 MHz;
|
|
||||||
mid-PnR timing report passes are skipped because LibreLane 3.0.5 emits 1000
|
|
||||||
expanded paths per group, while final post-RCX multi-corner STA remains enabled.
|
|
||||||
|
|
||||||
|
Both lines require the LibreLane 3.0.5 development environment, the locked
|
||||||
|
IHP-Open-PDK checkout under `physical/pdk/`, and the locked OpenSTA 3.0.0.
|
||||||
`PDK=GF180`, `PDK=SKY130`, and `PDK=ICS55` are intentionally rejected until
|
`PDK=GF180`, `PDK=SKY130`, and `PDK=ICS55` are intentionally rejected until
|
||||||
their technology-specific LibreLane adapters, pad cells, macro collateral, and
|
their technology-specific LibreLane adapters, pad cells, macro collateral, and
|
||||||
signoff decks are qualified. The target split is technology-neutral so those
|
signoff decks are qualified.
|
||||||
adapters can reuse the core/chip interface when they are ready.
|
|
||||||
|
|
||||||
IHP SG13G2 does not currently provide pad cells implementing retroSoC's
|
|
||||||
programmable pull-up, pull-down, or Schmitt controls. The IHP implementation
|
|
||||||
uses input-only, 4 mA output, and 4 mA bidirectional cells; unsupported GPIO
|
|
||||||
pad-control features must not be represented as production-qualified behavior.
|
|
||||||
|
|
||||||
A successful open-source run is evidence for implementation development, not a
|
A successful open-source run is evidence for implementation development, not a
|
||||||
foundry production-signoff claim. Release review still requires qualified
|
foundry production-signoff claim. Release review still requires qualified
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
LIBRELANE_FLOW_ROOT := $(ROOT_PATH)/physical/librelane
|
LIBRELANE_FLOW_ROOT := $(ROOT_PATH)/physical/librelane/mini
|
||||||
LIBRELANE_TARGET ?= chip
|
LIBRELANE_TARGET ?= chip
|
||||||
LIBRELANE_RUN_ROOT := $(VARIANT_ROOT)/physical/librelane/$(LIBRELANE_TARGET)
|
LIBRELANE_RUN_ROOT := $(VARIANT_ROOT)/physical/librelane/mini/$(LIBRELANE_TARGET)
|
||||||
LIBRELANE_INPUT_DIR := $(LIBRELANE_RUN_ROOT)/input
|
LIBRELANE_INPUT_DIR := $(LIBRELANE_RUN_ROOT)/input
|
||||||
LIBRELANE_RTL := $(LIBRELANE_INPUT_DIR)/retrosoc_asic_sources.sv
|
LIBRELANE_RTL := $(LIBRELANE_INPUT_DIR)/retrosoc_asic_sources.sv
|
||||||
LIBRELANE_SDC := $(LIBRELANE_INPUT_DIR)/retrosoc_$(LIBRELANE_TARGET).sdc
|
LIBRELANE_SDC := $(LIBRELANE_INPUT_DIR)/retrosoc_$(LIBRELANE_TARGET).sdc
|
||||||
LIBRELANE_BONDPAD_GDS := $(LIBRELANE_INPUT_DIR)/bondpad_70x70.gds
|
LIBRELANE_BONDPAD_GDS := $(LIBRELANE_INPUT_DIR)/bondpad_70x70.gds
|
||||||
LIBRELANE_BONDPAD_LEF := $(LIBRELANE_FLOW_ROOT)/bondpad/bondpad_70x70.lef
|
LIBRELANE_BONDPAD_LEF := $(ROOT_PATH)/physical/librelane/bondpad/bondpad_70x70.lef
|
||||||
LIBRELANE_CONFIG := $(LIBRELANE_RUN_ROOT)/config.json
|
LIBRELANE_CONFIG := $(LIBRELANE_RUN_ROOT)/config.json
|
||||||
LIBRELANE_LOG := $(LIBRELANE_RUN_ROOT)/librelane.log
|
LIBRELANE_LOG := $(LIBRELANE_RUN_ROOT)/librelane.log
|
||||||
LIBRELANE_RESULT := $(LIBRELANE_RUN_ROOT)/result.json
|
LIBRELANE_RESULT := $(LIBRELANE_RUN_ROOT)/result.json
|
||||||
@@ -65,7 +65,7 @@ endif
|
|||||||
|
|
||||||
librelane-check-pdk:
|
librelane-check-pdk:
|
||||||
@test '$(PDK)' = IHP130 || { \
|
@test '$(PDK)' = IHP130 || { \
|
||||||
echo 'physical/librelane currently implements only PDK=IHP130' >&2; exit 2; }
|
echo 'physical/librelane/mini currently implements only PDK=IHP130' >&2; exit 2; }
|
||||||
@test '$(HAVE_PLL)' = NO || { \
|
@test '$(HAVE_PLL)' = NO || { \
|
||||||
echo 'the IHP130 LibreLane flow has no qualified crystal PAD' >&2; exit 2; }
|
echo 'the IHP130 LibreLane flow has no qualified crystal PAD' >&2; exit 2; }
|
||||||
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Mini LibreLane IHP130 Flows
|
||||||
|
|
||||||
|
This directory owns the Mini product line's open-source IHP130 implementation
|
||||||
|
entry points for two independent `retrosoc` deliverables:
|
||||||
|
|
||||||
|
- `librelane-core` hardens `retrosoc_core`, a padless logical macro with the
|
||||||
|
canonical external pin contract and the three USB packet SRAM macros.
|
||||||
|
- `librelane-chip` implements the bare-die `retrosoc_asic` package-facing SoC,
|
||||||
|
including its pad ring, bondpads, fillers, corners, seal ring, and the same
|
||||||
|
SRAM macros.
|
||||||
|
|
||||||
|
These are separate single-level runs. The chip flow does not consume the core
|
||||||
|
run as a hierarchical macro, so either artifact can be developed and reviewed
|
||||||
|
without a two-level integration dependency.
|
||||||
|
|
||||||
|
The flow consumes the committed `configs/ci/ihp130.mk` profile, canonical SoC
|
||||||
|
pin map, and IHP-Open-PDK revision in `dependencies/dependencies.lock.json`.
|
||||||
|
LibreLane reads the upstream `ihp-sg13g2/libs.tech/librelane` configuration
|
||||||
|
directly through manual-PDK mode. Generated RTL, SDC, configuration, run
|
||||||
|
databases, reports, and final views are written below the selected
|
||||||
|
`build/<variant>/physical/librelane/mini/{core,chip}/` directory.
|
||||||
|
|
||||||
|
Use the LibreLane 3.0.5 environment supplied by the development shell:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-doctor
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-core
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-chip
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-core-package
|
||||||
|
make CONFIG=configs/ci/ihp130.mk librelane-package
|
||||||
|
```
|
||||||
|
|
||||||
|
The chip target places all 111 signal pads, 80 core/IO power and ground pads,
|
||||||
|
four corners, I/O fillers, and 70 um bondpads. It is a bare-die plan, not a
|
||||||
|
QFN/BGA package definition. The SDC constrains the external clock at 72 MHz;
|
||||||
|
mid-PnR timing report passes are skipped because LibreLane 3.0.5 emits 1000
|
||||||
|
expanded paths per group, while final post-RCX multi-corner STA remains enabled.
|
||||||
|
|
||||||
|
`PDK=GF180`, `PDK=SKY130`, and `PDK=ICS55` are intentionally rejected until
|
||||||
|
their technology-specific LibreLane adapters, pad cells, macro collateral, and
|
||||||
|
signoff decks are qualified. The target split is technology-neutral so those
|
||||||
|
adapters can reuse the core/chip interface when they are ready.
|
||||||
|
|
||||||
|
IHP SG13G2 does not currently provide pad cells implementing retroSoC's
|
||||||
|
programmable pull-up, pull-down, or Schmitt controls. The IHP implementation
|
||||||
|
uses input-only, 4 mA output, and 4 mA bidirectional cells; unsupported GPIO
|
||||||
|
pad-control features must not be represented as production-qualified behavior.
|
||||||
|
|
||||||
|
A successful open-source run is evidence for implementation development, not a
|
||||||
|
foundry production-signoff claim. Release review still requires qualified
|
||||||
|
foundry decks, package and bond planning, electrical/ESD review, and approved
|
||||||
|
waivers.
|
||||||
File renamed without changes.
File renamed without changes.
@@ -11,10 +11,10 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[3]
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
from librelane.flows import Flow # noqa: E402
|
from librelane.flows import Flow # noqa: E402
|
||||||
from rtl.mini.pin_map.generate_pin_map import IHP130_POWER_PAD_COUNTS # noqa: E402
|
from scripts.rtl.generate_pin_map import IHP130_POWER_PAD_COUNTS # noqa: E402
|
||||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
+4
-4
@@ -11,9 +11,9 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[3]
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
from rtl.mini.pin_map.generate_pin_map import ( # noqa: E402
|
from scripts.rtl.generate_pin_map import ( # noqa: E402
|
||||||
IHP130_POWER_PAD_COUNTS,
|
IHP130_POWER_PAD_COUNTS,
|
||||||
Pad,
|
Pad,
|
||||||
ihp130_pad_instance,
|
ihp130_pad_instance,
|
||||||
@@ -206,7 +206,7 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
|
|||||||
"SIGNOFF_SDC_FILE": config_path(args.sdc, config_dir),
|
"SIGNOFF_SDC_FILE": config_path(args.sdc, config_dir),
|
||||||
"FALLBACK_SDC": config_path(args.sdc, config_dir),
|
"FALLBACK_SDC": config_path(args.sdc, config_dir),
|
||||||
"STA_EXTRA_CORNER_TCL_FILE": config_path(
|
"STA_EXTRA_CORNER_TCL_FILE": config_path(
|
||||||
ROOT / "physical/librelane/sta_report_limit.tcl", config_dir
|
ROOT / "physical/librelane/mini/sta_report_limit.tcl", config_dir
|
||||||
),
|
),
|
||||||
"CLOCK_PORT": list(CLOCK_PORTS),
|
"CLOCK_PORT": list(CLOCK_PORTS),
|
||||||
"CLOCK_PERIOD": 1_000_000_000 / args.ext_clk_hz,
|
"CLOCK_PERIOD": 1_000_000_000 / args.ext_clk_hz,
|
||||||
@@ -259,7 +259,7 @@ def build_config(args: argparse.Namespace) -> dict[str, object]:
|
|||||||
"CORE_AREA": [365, 365, 7635, 7635],
|
"CORE_AREA": [365, 365, 7635, 7635],
|
||||||
"PL_TARGET_DENSITY_PCT": 45,
|
"PL_TARGET_DENSITY_PCT": 45,
|
||||||
"PDN_CORE_RING_CONNECT_TO_PADS": True,
|
"PDN_CORE_RING_CONNECT_TO_PADS": True,
|
||||||
"PAD_CFG": config_path(ROOT / "physical/librelane/pad_cfg.tcl", config_dir),
|
"PAD_CFG": config_path(ROOT / "physical/librelane/mini/pad_cfg.tcl", config_dir),
|
||||||
"PAD_BONDPAD_NAME": "bondpad_70x70",
|
"PAD_BONDPAD_NAME": "bondpad_70x70",
|
||||||
"EXTRA_GDS": [config_path(args.bondpad_gds, config_dir)],
|
"EXTRA_GDS": [config_path(args.bondpad_gds, config_dir)],
|
||||||
"EXTRA_LEFS": [config_path(args.bondpad_lef, config_dir)],
|
"EXTRA_LEFS": [config_path(args.bondpad_lef, config_dir)],
|
||||||
+3
-3
@@ -9,9 +9,9 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[3]
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
from rtl.mini.pin_map.generate_pin_map import Pad, ihp130_pad_instance, read_map # noqa: E402
|
from scripts.rtl.generate_pin_map import Pad, ihp130_pad_instance, read_map # noqa: E402
|
||||||
from scripts.check_clock_reset_domains import validate # noqa: E402
|
from scripts.check_clock_reset_domains import validate # noqa: E402
|
||||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ def render(args: argparse.Namespace) -> str:
|
|||||||
groups[clock]["outputs"].append(pad.name)
|
groups[clock]["outputs"].append(pad.name)
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
"# Generated by physical/librelane/scripts/generate_sdc.py; do not edit.",
|
"# Generated by physical/librelane/mini/scripts/generate_sdc.py; do not edit.",
|
||||||
"current_design $::env(DESIGN_NAME)",
|
"current_design $::env(DESIGN_NAME)",
|
||||||
"set_units -time ns",
|
"set_units -time ns",
|
||||||
"",
|
"",
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,133 @@
|
|||||||
|
LIBRELANE_FLOW_ROOT := $(ROOT_PATH)/physical/librelane/tiny
|
||||||
|
LIBRELANE_RUN_ROOT := $(VARIANT_ROOT)/physical/librelane/tiny/chip
|
||||||
|
LIBRELANE_INPUT_DIR := $(LIBRELANE_RUN_ROOT)/input
|
||||||
|
LIBRELANE_RTL := $(LIBRELANE_INPUT_DIR)/retrosoc_tiny_asic_sources.sv
|
||||||
|
LIBRELANE_SDC := $(LIBRELANE_INPUT_DIR)/retrosoc_tiny_chip.sdc
|
||||||
|
LIBRELANE_BONDPAD_GDS := $(LIBRELANE_INPUT_DIR)/bondpad_70x70.gds
|
||||||
|
LIBRELANE_BONDPAD_LEF := $(ROOT_PATH)/physical/librelane/bondpad/bondpad_70x70.lef
|
||||||
|
LIBRELANE_CONFIG := $(LIBRELANE_RUN_ROOT)/config.json
|
||||||
|
LIBRELANE_LOG := $(LIBRELANE_RUN_ROOT)/librelane.log
|
||||||
|
LIBRELANE_RESULT := $(LIBRELANE_RUN_ROOT)/result.json
|
||||||
|
LIBRELANE_META_RESULT := $(META_DIR)/librelane-chip.json
|
||||||
|
LIBRELANE_DOCTOR_RESULT := $(META_DIR)/librelane-chip-doctor.json
|
||||||
|
LIBRELANE_FINAL_DIR := $(LIBRELANE_RUN_ROOT)/final
|
||||||
|
LIBRELANE_ARCHIVE := $(LIBRELANE_RUN_ROOT)/retrosoc-tiny-ihp130-chip.tar.gz
|
||||||
|
LIBRELANE_RUN_TAG ?= current
|
||||||
|
LIBRELANE_PDK_ROOT := $(ROOT_PATH)/physical/pdk/IHP-Open-PDK
|
||||||
|
LIBRELANE_PDK := ihp-sg13g2
|
||||||
|
LIBRELANE_SCL := sg13g2_stdcell
|
||||||
|
LIBRELANE_PAD := sg13g2_io
|
||||||
|
LIBRELANE_KLAYOUT_PATH := $(LIBRELANE_PDK_ROOT)/$(LIBRELANE_PDK)/libs.tech/klayout
|
||||||
|
LIBRELANE_BONDPAD_SCRIPT := $(LIBRELANE_PDK_ROOT)/$(LIBRELANE_PDK)/libs.tech/klayout/tech/scripts/bondpad.py
|
||||||
|
LIBRELANE_PDN := $(LIBRELANE_FLOW_ROOT)/pdn_cfg.tcl
|
||||||
|
LIBRELANE_SRAM_VH := $(LIBRELANE_FLOW_ROOT)/sram_blackboxes.vh
|
||||||
|
LIBRELANE_STA_REPORT_LIMIT := $(LIBRELANE_FLOW_ROOT)/sta_report_limit.tcl
|
||||||
|
LIBRELANE_CLOCK_DOMAINS := $(RTL_PATH)/integration/clock_reset_domains.json
|
||||||
|
LIBRELANE_PIN_MAP := $(RTL_PATH)/pin_map/pin_map.json
|
||||||
|
LIBRELANE_TRACKED_RTL := $(shell git -C $(ROOT_PATH) ls-files \
|
||||||
|
'rtl/**/*.sv' 'rtl/**/*.svh' 'rtl/**/*.v' 'rtl/**/*.vh')
|
||||||
|
|
||||||
|
LIBRELANE_SOURCE_PYTHON ?= /usr/bin/python3
|
||||||
|
LIBRELANE_PYTHON ?= python3
|
||||||
|
LIBRELANE_COMMAND := $(LIBRELANE_PYTHON) -m librelane
|
||||||
|
# LibreLane 3.0.5 writes 1000 expanded paths per group at every mid-PnR STA.
|
||||||
|
# Final multi-corner RCX STA remains mandatory; skip only the redundant reports.
|
||||||
|
LIBRELANE_MID_PNR_SKIP ?= --skip OpenROAD.STAMidPNR
|
||||||
|
|
||||||
|
ifneq ($(filter librelane-input librelane-doctor librelane-chip \
|
||||||
|
librelane-openroad librelane-klayout librelane-package,$(MAKECMDGOALS)),)
|
||||||
|
FLOW_PYTHON := $(LIBRELANE_SOURCE_PYTHON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LIBRELANE_FEATURE_ARGS := \
|
||||||
|
$(if $(filter YES,$(HAVE_PLL)),--have-pll) \
|
||||||
|
$(if $(filter YES,$(HAVE_SRAM_IF)),--have-sram-if) \
|
||||||
|
$(if $(filter YES,$(HAVE_SRAM_MACRO)),--have-sram-macro) \
|
||||||
|
$(if $(filter YES,$(HAVE_SVA)),--have-sva)
|
||||||
|
|
||||||
|
LIBRELANE_CONFIG_DEPS := $(LIBRELANE_RTL) $(LIBRELANE_SDC) \
|
||||||
|
$(LIBRELANE_PDN) $(LIBRELANE_SRAM_VH) \
|
||||||
|
$(LIBRELANE_STA_REPORT_LIMIT) $(LIBRELANE_BONDPAD_GDS) $(LIBRELANE_BONDPAD_LEF) \
|
||||||
|
$(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py
|
||||||
|
|
||||||
|
.PHONY: librelane-check-pdk librelane-input librelane-doctor librelane-chip \
|
||||||
|
librelane-openroad librelane-klayout librelane-package
|
||||||
|
|
||||||
|
librelane-check-pdk:
|
||||||
|
@test '$(PDK)' = IHP130 || { \
|
||||||
|
echo 'physical/librelane/tiny currently implements only PDK=IHP130' >&2; exit 2; }
|
||||||
|
@test '$(HAVE_PLL)' = NO || { \
|
||||||
|
echo 'the IHP130 LibreLane flow has no qualified crystal PAD' >&2; exit 2; }
|
||||||
|
|
||||||
|
$(LIBRELANE_RTL): $(FILELIST_STAMP) $(LIBRELANE_TRACKED_RTL) \
|
||||||
|
$(ROOT_PATH)/physical/smoke/syn/tools/export_soc_sources.py | librelane-check-pdk
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(FLOW_PYTHON) $(ROOT_PATH)/physical/smoke/syn/tools/export_soc_sources.py sv \
|
||||||
|
--output-dir $(@D) --soc TINY --pdk IHP130 --simu $(SIMU) \
|
||||||
|
--jtag-idcode $(JTAG_IDCODE) \
|
||||||
|
--ext-clk-hz $(EXT_CLK_HZ) --aud-clk-hz $(AUD_CLK_HZ) \
|
||||||
|
--clint-timebase-hz $(CLINT_TIMEBASE_HZ) \
|
||||||
|
--source-filelist-dir $(GENERATED_FL_DIR) \
|
||||||
|
--librelane-safe \
|
||||||
|
$(LIBRELANE_FEATURE_ARGS)
|
||||||
|
|
||||||
|
$(LIBRELANE_SDC): $(LIBRELANE_FLOW_ROOT)/scripts/generate_sdc.py \
|
||||||
|
$(LIBRELANE_CLOCK_DOMAINS) $(LIBRELANE_PIN_MAP) | librelane-check-pdk
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/generate_sdc.py \
|
||||||
|
--domains $(LIBRELANE_CLOCK_DOMAINS) --pin-map $(LIBRELANE_PIN_MAP) \
|
||||||
|
--ext-clk-hz $(EXT_CLK_HZ) --output $@
|
||||||
|
|
||||||
|
$(LIBRELANE_BONDPAD_GDS): $(LIBRELANE_BONDPAD_SCRIPT) | librelane-check-pdk
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
KLAYOUT_PATH=$(LIBRELANE_KLAYOUT_PATH) klayout -n sg13g2 -zz \
|
||||||
|
-r $(LIBRELANE_BONDPAD_SCRIPT) -rd diameter=70.0 -rd shape=square \
|
||||||
|
-rd output=$@
|
||||||
|
@test -s $@
|
||||||
|
|
||||||
|
$(LIBRELANE_CONFIG): $(LIBRELANE_CONFIG_DEPS)
|
||||||
|
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/generate_chip_config.py \
|
||||||
|
--pin-map $(LIBRELANE_PIN_MAP) --rtl $(LIBRELANE_RTL) --sdc $(LIBRELANE_SDC) \
|
||||||
|
--pdn $(LIBRELANE_PDN) --bondpad-gds $(LIBRELANE_BONDPAD_GDS) \
|
||||||
|
--bondpad-lef $(LIBRELANE_BONDPAD_LEF) --sram-vh $(LIBRELANE_SRAM_VH) \
|
||||||
|
--ext-clk-hz $(EXT_CLK_HZ) --sram-size-kib $(SRAM_SIZE_KIB) \
|
||||||
|
$(if $(filter YES,$(HAVE_SRAM_MACRO)),--have-sram-macro) \
|
||||||
|
--output $@
|
||||||
|
|
||||||
|
librelane-input: $(LIBRELANE_CONFIG)
|
||||||
|
|
||||||
|
$(LIBRELANE_DOCTOR_RESULT): $(LIBRELANE_CONFIG) \
|
||||||
|
$(LIBRELANE_FLOW_ROOT)/scripts/doctor.py
|
||||||
|
@mkdir -p $(@D)
|
||||||
|
$(LIBRELANE_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/doctor.py \
|
||||||
|
--root $(ROOT_PATH) --config $(LIBRELANE_CONFIG) --lock $(LOCK_FILE) \
|
||||||
|
--pin-map $(LIBRELANE_PIN_MAP) --pdk-root $(LIBRELANE_PDK_ROOT) --output $@
|
||||||
|
|
||||||
|
librelane-doctor: $(LIBRELANE_DOCTOR_RESULT)
|
||||||
|
|
||||||
|
librelane-chip: librelane-doctor manifest
|
||||||
|
$(FLOW_PYTHON) $(ROOT_PATH)/scripts/run_flow.py --tool librelane \
|
||||||
|
--log $(LIBRELANE_LOG) --result $(LIBRELANE_RESULT) --cwd $(LIBRELANE_RUN_ROOT) -- \
|
||||||
|
$(LIBRELANE_COMMAND) $(LIBRELANE_CONFIG) --manual-pdk --pdk $(LIBRELANE_PDK) \
|
||||||
|
--pdk-root $(LIBRELANE_PDK_ROOT) --scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
||||||
|
--run-tag $(LIBRELANE_RUN_TAG) --overwrite --jobs $(JOBS) $(LIBRELANE_MID_PNR_SKIP) \
|
||||||
|
--condensed --hide-progress-bar --save-views-to $(LIBRELANE_FINAL_DIR)
|
||||||
|
@mkdir -p $(META_DIR)
|
||||||
|
@cp $(LIBRELANE_RESULT) $(LIBRELANE_META_RESULT)
|
||||||
|
|
||||||
|
librelane-openroad: librelane-doctor
|
||||||
|
cd $(LIBRELANE_RUN_ROOT) && $(LIBRELANE_COMMAND) $(LIBRELANE_CONFIG) --manual-pdk \
|
||||||
|
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
|
||||||
|
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
||||||
|
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInOpenROAD
|
||||||
|
|
||||||
|
librelane-klayout: librelane-doctor
|
||||||
|
cd $(LIBRELANE_RUN_ROOT) && $(LIBRELANE_COMMAND) $(LIBRELANE_CONFIG) --manual-pdk \
|
||||||
|
--pdk $(LIBRELANE_PDK) --pdk-root $(LIBRELANE_PDK_ROOT) \
|
||||||
|
--scl $(LIBRELANE_SCL) --pad $(LIBRELANE_PAD) \
|
||||||
|
--run-tag $(LIBRELANE_RUN_TAG) --last-run --flow OpenInKLayout
|
||||||
|
|
||||||
|
librelane-package: librelane-chip
|
||||||
|
$(FLOW_PYTHON) $(LIBRELANE_FLOW_ROOT)/scripts/package.py \
|
||||||
|
--root $(ROOT_PATH) --variant-root $(VARIANT_ROOT) \
|
||||||
|
--run-root $(LIBRELANE_RUN_ROOT) --output $(LIBRELANE_ARCHIVE)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Tiny LibreLane IHP130 Chip Flow
|
||||||
|
|
||||||
|
This directory owns the Tiny product line's open-source IHP130 implementation
|
||||||
|
entry point: `librelane-chip` implements the bare-die `retrosoc_tiny_asic`
|
||||||
|
MCU, including its pad ring, bondpads, fillers, corners, seal ring, and the
|
||||||
|
32 on-chip SRAM bank macros.
|
||||||
|
|
||||||
|
The flow consumes the committed `configs/ci/ihp130-tiny.mk` profile, the
|
||||||
|
canonical Tiny pin map (`rtl/tiny/pin_map/pin_map.json`), the Tiny clock/reset
|
||||||
|
inventory (`rtl/tiny/integration/clock_reset_domains.json`), and the
|
||||||
|
IHP-Open-PDK revision in `dependencies/dependencies.lock.json`. LibreLane reads
|
||||||
|
the upstream `ihp-sg13g2/libs.tech/librelane` configuration directly through
|
||||||
|
manual-PDK mode. Generated RTL, SDC, configuration, run databases, reports, and
|
||||||
|
final views are written below `build/<variant>/physical/librelane/tiny/chip/`.
|
||||||
|
|
||||||
|
Use the LibreLane 3.0.5 development shell (`~/squashfs-root/AppRun`) and put
|
||||||
|
the locked OpenSTA 3.0.0 first on `PATH`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
~/squashfs-root/AppRun bash -c 'export PATH=/nfs/home/miaoyuchi/artifact-opensta/bin:$PATH; cd <repo>'
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk librelane-doctor
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk librelane-chip
|
||||||
|
make CONFIG=configs/ci/ihp130-tiny.mk librelane-package
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pad Ring
|
||||||
|
|
||||||
|
The Tiny pin map declares 52 signal PADs (external clock/reset, JTAG, two
|
||||||
|
UARTs, 32 GPIOs, and the quad-XPI NOR flash interface). Tiny ships a reduced
|
||||||
|
supply set of 8 VDD + 8 VSS + 8 IOVDD + 8 IOVSS (two pairs of each per side),
|
||||||
|
84 placed PADs in total. The reduction from the Mini line's 24/24/16/16 follows
|
||||||
|
the taped-out Basilisk IHP SG13G2 SoC pad frame (68 signals with 24/24/16/16
|
||||||
|
supplies feeding a Linux-capable CVA6 at a much higher clock and IO rate):
|
||||||
|
Tiny's 24 MHz Hazard3 core draws a few mA, and even with all 52 4 mA outputs
|
||||||
|
switching simultaneously each IO supply pair carries only ~26 mA.
|
||||||
|
|
||||||
|
The side assignment is south = clock/reset/JTAG/UART, north = XPI,
|
||||||
|
east = GPIO[15:0], west = GPIO[31:16]; the `power_pads` counts come from
|
||||||
|
`rtl/tiny/pin_map/pin_map.json` and must match `pad_cfg.tcl`.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
The SDC constrains the external clock at 24 MHz and JTAG at 10 MHz, defines a
|
||||||
|
generated system clock at the top-level clock buffer, and keeps the two groups
|
||||||
|
asynchronous. Mid-PnR timing report passes are skipped because LibreLane 3.0.5
|
||||||
|
emits 1000 expanded paths per group; final post-RCX multi-corner STA remains
|
||||||
|
enabled.
|
||||||
|
|
||||||
|
A successful open-source run is evidence for implementation development, not a
|
||||||
|
foundry production-signoff claim. Release review still requires qualified
|
||||||
|
foundry decks, package and bond planning, electrical/ESD review, and approved
|
||||||
|
waivers.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# IHP SG13G2 supply pads are physical-only cells and are not retained by Yosys.
|
||||||
|
# Tiny ships two core and two IO supply pairs per pad-ring side (8/8/8/8).
|
||||||
|
foreach {kind master count} {
|
||||||
|
vdd sg13g2_IOPadVdd 8
|
||||||
|
vss sg13g2_IOPadVss 8
|
||||||
|
iovdd sg13g2_IOPadIOVdd 8
|
||||||
|
iovss sg13g2_IOPadIOVss 8
|
||||||
|
} {
|
||||||
|
for {set index 0} {$index < $count} {incr index} {
|
||||||
|
set instance_name [format {%s_pads[%d].%s_pad} $kind $index $kind]
|
||||||
|
set db_block [ord::get_db_block]
|
||||||
|
if {[$db_block findInst $instance_name] == "NULL"} {
|
||||||
|
make_instance $instance_name $master
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source $::env(SCRIPTS_DIR)/openroad/common/pad_cfg.tcl
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
# Copyright 2025-2026 LibreLane Contributors and retroSoC Authors
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
# Tiny IHP130 chip PDN: LibreLane default sg13g2 topology (M1 rails connect to
|
||||||
|
# the TopMetal1 stripe network directly) plus a macro grid that ties the SRAM
|
||||||
|
# banks' Metal4 power columns into the stripe network with Metal5 cross straps.
|
||||||
|
|
||||||
|
source $::env(SCRIPTS_DIR)/openroad/common/io.tcl
|
||||||
|
source $::env(SCRIPTS_DIR)/openroad/common/set_global_connections.tcl
|
||||||
|
set_global_connections
|
||||||
|
|
||||||
|
# Use the PDK-native alternating routing directions (H/V/H/V/H).
|
||||||
|
set secondary_supplies []
|
||||||
|
foreach vdd $::env(VDD_NETS) gnd $::env(GND_NETS) {
|
||||||
|
if {$vdd != $::env(VDD_NET)} {
|
||||||
|
lappend secondary_supplies $vdd
|
||||||
|
set db_net [[ord::get_db_block] findNet $vdd]
|
||||||
|
if {$db_net == "NULL"} {
|
||||||
|
set net [odb::dbNet_create [ord::get_db_block] $vdd]
|
||||||
|
$net setSpecial
|
||||||
|
$net setSigType "POWER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if {$gnd != $::env(GND_NET)} {
|
||||||
|
lappend secondary_supplies $gnd
|
||||||
|
set db_net [[ord::get_db_block] findNet $gnd]
|
||||||
|
if {$db_net == "NULL"} {
|
||||||
|
set net [odb::dbNet_create [ord::get_db_block] $gnd]
|
||||||
|
$net setSpecial
|
||||||
|
$net setSigType "GROUND"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# I/O rails are pad-ring internal and are absent from the synthesized top-level.
|
||||||
|
foreach {net signal_type} {IOVDD POWER IOVSS GROUND} {
|
||||||
|
set db_net [[ord::get_db_block] findNet $net]
|
||||||
|
if {$db_net == "NULL"} {
|
||||||
|
set db_net [odb::dbNet_create [ord::get_db_block] $net]
|
||||||
|
$db_net setSpecial
|
||||||
|
$db_net setSigType $signal_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# IHP IO cells carry both core and external-IO power rails on lower-case pins.
|
||||||
|
add_global_connection -net $::env(VDD_NET) -inst_pattern .* -pin_pattern vdd -power
|
||||||
|
add_global_connection -net $::env(GND_NET) -inst_pattern .* -pin_pattern vss -ground
|
||||||
|
add_global_connection -net IOVDD -inst_pattern .* -pin_pattern iovdd -power
|
||||||
|
add_global_connection -net IOVSS -inst_pattern .* -pin_pattern iovss -ground
|
||||||
|
global_connect -verbose
|
||||||
|
|
||||||
|
# connect_by_abutment assigns IHP pad rails to corner-local *_RING nets. Rebind
|
||||||
|
# those physical pad and filler pins so the PDN knows they are VDD/VSS terminals.
|
||||||
|
proc reconnect_ihp_padring_rails {block} {
|
||||||
|
foreach {net_name pin_name} {
|
||||||
|
VDD vdd
|
||||||
|
VSS vss
|
||||||
|
IOVDD iovdd
|
||||||
|
IOVSS iovss
|
||||||
|
} {
|
||||||
|
set net [$block findNet $net_name]
|
||||||
|
foreach inst [$block getInsts] {
|
||||||
|
set master_name [[$inst getMaster] getName]
|
||||||
|
if {![string match "sg13g2_IOPad*" $master_name] &&
|
||||||
|
![string match "sg13g2_Filler*" $master_name] &&
|
||||||
|
![string match "sg13g2_Corner*" $master_name]} {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
foreach iterm [$inst getITerms] {
|
||||||
|
if {[[$iterm getMTerm] getName] != $pin_name} {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
odb::dbITerm_disconnect $iterm
|
||||||
|
odb::dbITerm_connect $iterm $net
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reconnect_ihp_padring_rails [ord::get_db_block]
|
||||||
|
|
||||||
|
set_voltage_domain -name CORE -power $::env(VDD_NET) -ground $::env(GND_NET) \
|
||||||
|
-secondary_power $secondary_supplies
|
||||||
|
|
||||||
|
set stdcell_grid_args [list]
|
||||||
|
if {$::env(PDN_ENABLE_PINS) == 1} {
|
||||||
|
lappend stdcell_grid_args -pins "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)"
|
||||||
|
}
|
||||||
|
define_pdn_grid \
|
||||||
|
-name stdcell_grid \
|
||||||
|
-starts_with POWER \
|
||||||
|
-voltage_domain CORE \
|
||||||
|
{*}$stdcell_grid_args
|
||||||
|
|
||||||
|
add_pdn_stripe \
|
||||||
|
-grid stdcell_grid \
|
||||||
|
-layer $::env(PDN_VERTICAL_LAYER) \
|
||||||
|
-width $::env(PDN_VWIDTH) \
|
||||||
|
-pitch $::env(PDN_VPITCH) \
|
||||||
|
-offset $::env(PDN_VOFFSET) \
|
||||||
|
-spacing $::env(PDN_VSPACING) \
|
||||||
|
-starts_with POWER \
|
||||||
|
-extend_to_core_ring
|
||||||
|
|
||||||
|
add_pdn_stripe \
|
||||||
|
-grid stdcell_grid \
|
||||||
|
-layer $::env(PDN_HORIZONTAL_LAYER) \
|
||||||
|
-width $::env(PDN_HWIDTH) \
|
||||||
|
-pitch $::env(PDN_HPITCH) \
|
||||||
|
-offset $::env(PDN_HOFFSET) \
|
||||||
|
-spacing $::env(PDN_HSPACING) \
|
||||||
|
-starts_with POWER \
|
||||||
|
-extend_to_core_ring
|
||||||
|
|
||||||
|
# A low-metal ring just inside the core edge collects the Metal1 rails; the
|
||||||
|
# top-metal stripe mesh bridges to it where the stripes cross the ring legs.
|
||||||
|
add_pdn_ring \
|
||||||
|
-grid stdcell_grid \
|
||||||
|
-layers "Metal2 Metal3" \
|
||||||
|
-widths "0.52 0.52" \
|
||||||
|
-spacings "0.8 0.8" \
|
||||||
|
-core_offsets "1.6 1.6" \
|
||||||
|
-starts_with POWER \
|
||||||
|
-add_connect
|
||||||
|
|
||||||
|
if {$::env(PDN_ENABLE_RAILS) == 1} {
|
||||||
|
add_pdn_stripe \
|
||||||
|
-grid stdcell_grid \
|
||||||
|
-layer $::env(PDN_RAIL_LAYER) \
|
||||||
|
-width $::env(PDN_RAIL_WIDTH) \
|
||||||
|
-followpins \
|
||||||
|
-extend_to_core_ring
|
||||||
|
add_pdn_connect -grid stdcell_grid -layers "Metal1 Metal2"
|
||||||
|
add_pdn_connect -grid stdcell_grid -layers "Metal1 Metal3"
|
||||||
|
# The rail network meets the top-metal mesh at the low ring legs and at
|
||||||
|
# every Metal1 rail / TopMetal1 stripe crossing (covers the standard-cell
|
||||||
|
# channels between SRAM banks as well).
|
||||||
|
add_pdn_connect -grid stdcell_grid -layers "Metal3 TopMetal1"
|
||||||
|
add_pdn_connect -grid stdcell_grid -layers "Metal1 TopMetal1"
|
||||||
|
add_pdn_connect -grid stdcell_grid -layers "Metal2 TopMetal2"
|
||||||
|
}
|
||||||
|
|
||||||
|
if {$::env(PDN_CORE_RING) == 1} {
|
||||||
|
add_pdn_ring \
|
||||||
|
-grid stdcell_grid \
|
||||||
|
-layers "$::env(PDN_VERTICAL_LAYER) $::env(PDN_HORIZONTAL_LAYER)" \
|
||||||
|
-widths "$::env(PDN_CORE_RING_VWIDTH) $::env(PDN_CORE_RING_HWIDTH)" \
|
||||||
|
-spacings "$::env(PDN_CORE_RING_VSPACING) $::env(PDN_CORE_RING_HSPACING)" \
|
||||||
|
-core_offsets "$::env(PDN_CORE_RING_VOFFSET) $::env(PDN_CORE_RING_HOFFSET)" \
|
||||||
|
-connect_to_pads \
|
||||||
|
-connect_to_pad_layers TopMetal2 \
|
||||||
|
-add_connect
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tiny hardens a single SRAM master: the 128 KiB on-chip RAM banks. Their power
|
||||||
|
# pins are vertical Metal4 columns spanning the bank height; horizontal Metal5
|
||||||
|
# straps cross every column and hand off to the stdcell TopMetal1 stripes
|
||||||
|
# outside the bank boundary.
|
||||||
|
set sram_cells [list \
|
||||||
|
RM_IHPSG13_1P_1024x32_c2_bm_bist]
|
||||||
|
|
||||||
|
define_pdn_grid \
|
||||||
|
-macro \
|
||||||
|
-cells $sram_cells \
|
||||||
|
-name sram_bank_grid \
|
||||||
|
-grid_over_boundary \
|
||||||
|
-voltage_domains CORE \
|
||||||
|
-starts_with POWER
|
||||||
|
|
||||||
|
add_pdn_ring \
|
||||||
|
-grid sram_bank_grid \
|
||||||
|
-layers "Metal2 Metal3" \
|
||||||
|
-widths "0.52 0.52" \
|
||||||
|
-spacings "0.8 0.8" \
|
||||||
|
-core_offsets "1.6 1.6" \
|
||||||
|
-starts_with POWER \
|
||||||
|
-add_connect
|
||||||
|
|
||||||
|
add_pdn_stripe \
|
||||||
|
-grid sram_bank_grid \
|
||||||
|
-layer Metal5 \
|
||||||
|
-width 2.5 \
|
||||||
|
-pitch 67.24 \
|
||||||
|
-offset 10.0 \
|
||||||
|
-spacing 2.5 \
|
||||||
|
-starts_with POWER
|
||||||
|
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "Metal4 Metal5"
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "Metal5 TopMetal1"
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "Metal2 TopMetal1"
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "Metal3 TopMetal1"
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "TopMetal1 TopMetal2"
|
||||||
|
# Rails trapped in the channels between banks tie into the bank rings.
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "Metal1 Metal2"
|
||||||
|
add_pdn_connect -grid sram_bank_grid -layers "Metal1 Metal3"
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Validate the local LibreLane/IHP130 environment and Tiny chip configuration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from librelane.flows import Flow # noqa: E402
|
||||||
|
from scripts.rtl.generate_pin_map import read_power_pad_counts # noqa: E402
|
||||||
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
EXPECTED_LIBRELANE_VERSION = "LibreLane v3.0.5"
|
||||||
|
EXPECTED_OPENSTA_VERSION = "3.0.0"
|
||||||
|
EXPECTED_SIGNAL_PADS = 52
|
||||||
|
|
||||||
|
|
||||||
|
def command_output(command: list[str]) -> str:
|
||||||
|
return subprocess.check_output(command, text=True, stderr=subprocess.STDOUT).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--root", type=Path, required=True)
|
||||||
|
parser.add_argument("--config", type=Path, required=True)
|
||||||
|
parser.add_argument("--lock", type=Path, required=True)
|
||||||
|
parser.add_argument("--pin-map", type=Path, required=True)
|
||||||
|
parser.add_argument("--pdk-root", type=Path, required=True)
|
||||||
|
parser.add_argument("--output", type=Path, required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
errors: list[str] = []
|
||||||
|
details: dict[str, object] = {}
|
||||||
|
|
||||||
|
for tool in ("librelane", "klayout", "sta"):
|
||||||
|
path = shutil.which(tool)
|
||||||
|
details[f"{tool}_path"] = path
|
||||||
|
if path is None:
|
||||||
|
errors.append(f"required tool is missing: {tool}")
|
||||||
|
|
||||||
|
if shutil.which("librelane") is not None:
|
||||||
|
version = command_output(
|
||||||
|
[sys.executable, "-m", "librelane", "--version"]
|
||||||
|
).splitlines()[0]
|
||||||
|
details["librelane_version"] = version
|
||||||
|
if version != EXPECTED_LIBRELANE_VERSION:
|
||||||
|
errors.append(f"expected {EXPECTED_LIBRELANE_VERSION}, found {version or '<empty>'}")
|
||||||
|
|
||||||
|
if shutil.which("sta") is not None:
|
||||||
|
version = command_output(["sta", "-version"])
|
||||||
|
details["opensta_version"] = version
|
||||||
|
if version != EXPECTED_OPENSTA_VERSION:
|
||||||
|
errors.append(
|
||||||
|
f"expected OpenSTA {EXPECTED_OPENSTA_VERSION}, "
|
||||||
|
f"found {version or '<empty>'}"
|
||||||
|
)
|
||||||
|
|
||||||
|
lock = json.loads(args.lock.read_text(encoding="utf-8"))
|
||||||
|
expected_pdk_revision = lock["sources"]["pdk_ihp130"]["revision"]
|
||||||
|
details["locked_pdk_revision"] = expected_pdk_revision
|
||||||
|
|
||||||
|
pdk_checkout = args.pdk_root.resolve()
|
||||||
|
if not (pdk_checkout / ".git").exists():
|
||||||
|
errors.append(f"IHP PDK checkout is missing: {pdk_checkout}")
|
||||||
|
else:
|
||||||
|
revision = command_output(["git", "-C", str(pdk_checkout), "rev-parse", "HEAD"])
|
||||||
|
details["pdk_revision"] = revision
|
||||||
|
if revision != expected_pdk_revision:
|
||||||
|
errors.append(f"expected IHP PDK {expected_pdk_revision}, found {revision}")
|
||||||
|
|
||||||
|
required = (
|
||||||
|
args.config,
|
||||||
|
pdk_checkout / "ihp-sg13g2/libs.tech/librelane/config.tcl",
|
||||||
|
pdk_checkout / "ihp-sg13g2/libs.tech/klayout/tech/drc/ihp-sg13g2.drc",
|
||||||
|
pdk_checkout / "ihp-sg13g2/libs.tech/klayout/tech/scripts/sealring.py",
|
||||||
|
pdk_checkout / "ihp-sg13g2/libs.ref/sg13g2_io/spice/sg13g2_io.spice",
|
||||||
|
)
|
||||||
|
for path in required:
|
||||||
|
if not path.is_file():
|
||||||
|
errors.append(f"required LibreLane/IHP file is missing: {path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
power_counts = read_power_pad_counts(args.pin_map)
|
||||||
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
|
power_counts = None
|
||||||
|
errors.append(f"failed to read power PAD counts: {error}")
|
||||||
|
|
||||||
|
config: dict[str, object] = {}
|
||||||
|
flow_name = ""
|
||||||
|
if args.config.is_file():
|
||||||
|
try:
|
||||||
|
config = json.loads(args.config.read_text(encoding="utf-8"))
|
||||||
|
meta = config.get("meta", {})
|
||||||
|
if not isinstance(meta, dict) or not isinstance(meta.get("flow"), str):
|
||||||
|
raise ValueError("config meta.flow must name a LibreLane flow")
|
||||||
|
flow_name = meta["flow"]
|
||||||
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
|
errors.append(f"failed to read LibreLane configuration: {error}")
|
||||||
|
|
||||||
|
if args.config.is_file() and not errors:
|
||||||
|
try:
|
||||||
|
flow_class = Flow.factory.get(flow_name)
|
||||||
|
if flow_class is None:
|
||||||
|
raise ValueError(f"unknown LibreLane flow: {flow_name}")
|
||||||
|
flow = flow_class(
|
||||||
|
str(args.config.resolve()),
|
||||||
|
pdk="ihp-sg13g2",
|
||||||
|
pdk_root=str(pdk_checkout),
|
||||||
|
scl="sg13g2_stdcell",
|
||||||
|
pad="sg13g2_io",
|
||||||
|
design_dir=str(args.config.resolve().parent),
|
||||||
|
)
|
||||||
|
details["flow"] = flow_name
|
||||||
|
details["config_design"] = flow.config["DESIGN_NAME"]
|
||||||
|
details["drc_runset"] = str(flow.config["KLAYOUT_DRC_RUNSET"])
|
||||||
|
details["lvs_setup"] = str(flow.config["NETGEN_SETUP"])
|
||||||
|
details["pad_spice"] = [str(path) for path in flow.config["PAD_SPICE_MODELS"]]
|
||||||
|
except Exception as error: # LibreLane reports structured configuration exceptions.
|
||||||
|
errors.append(f"LibreLane {flow_name or 'unknown'} configuration failed to load: {error}")
|
||||||
|
|
||||||
|
if config:
|
||||||
|
if details.get("config_design") not in (None, "retrosoc_tiny_asic"):
|
||||||
|
errors.append(
|
||||||
|
f"Tiny chip flow expects DESIGN_NAME retrosoc_tiny_asic, "
|
||||||
|
f"found {details['config_design']}"
|
||||||
|
)
|
||||||
|
placed = [
|
||||||
|
item
|
||||||
|
for side in ("PAD_SOUTH", "PAD_EAST", "PAD_NORTH", "PAD_WEST")
|
||||||
|
for item in config.get(side, [])
|
||||||
|
]
|
||||||
|
details["placed_pad_count"] = len(placed)
|
||||||
|
if flow_name == "Chip" and power_counts is not None:
|
||||||
|
expected = EXPECTED_SIGNAL_PADS + sum(power_counts.values())
|
||||||
|
if len(placed) != expected or len(placed) != len(set(placed)):
|
||||||
|
errors.append(
|
||||||
|
f"expected {expected} unique placed signal/power PADs, found {len(placed)}"
|
||||||
|
)
|
||||||
|
elif flow_name != "Chip":
|
||||||
|
errors.append(f"Tiny supports only the Chip flow, found {flow_name}")
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"schema_version": 1,
|
||||||
|
"status": "passed" if not errors else "failed",
|
||||||
|
"errors": errors,
|
||||||
|
"details": details,
|
||||||
|
}
|
||||||
|
atomic_write(args.output, json.dumps(result, indent=2, sort_keys=True) + "\n")
|
||||||
|
if errors:
|
||||||
|
for error in errors:
|
||||||
|
print(f"ERROR: {error}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
print(f"LibreLane doctor passed: {args.output}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,295 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate the IHP130 LibreLane Chip configuration for the Tiny SoC."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from scripts.rtl.generate_pin_map import ( # noqa: E402
|
||||||
|
POWER_PAD_KINDS,
|
||||||
|
Pad,
|
||||||
|
ihp130_pad_instance,
|
||||||
|
read_map,
|
||||||
|
read_power_pad_counts,
|
||||||
|
)
|
||||||
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
SIDE_ORDER = ("south", "east", "north", "west")
|
||||||
|
EXPECTED_SIGNAL_PADS = 52
|
||||||
|
CLOCK_PORTS = ("extclk_i_pad", "jtag_tck_i_pad")
|
||||||
|
SRAM_MASTER = "RM_IHPSG13_1P_1024x32_c2_bm_bist"
|
||||||
|
SRAM_INSTANCE = "u_soc.u_sram.gen_memory.gen_bank[{index}].u_ram.u_mem"
|
||||||
|
SRAM_COLUMNS = 8
|
||||||
|
SRAM_ORIGIN = (400, 600)
|
||||||
|
# Detailed routing requires an M2 track inside each bank's 0.26 um bottom-edge
|
||||||
|
# pin stubs: the bank y origin must keep residue {0, 0.24} mod 0.42 um (the M2
|
||||||
|
# vertical track pitch); a y pitch of 504 um (1200 x 0.42) preserves it per row.
|
||||||
|
SRAM_PITCH = (480, 504)
|
||||||
|
DIE_AREA = [0, 0, 4700, 4700]
|
||||||
|
CORE_AREA = [365, 365, 4335, 4335]
|
||||||
|
|
||||||
|
|
||||||
|
def config_path(path: Path, base: Path) -> str:
|
||||||
|
return f"dir::{os.path.relpath(path.resolve(), base.resolve())}"
|
||||||
|
|
||||||
|
|
||||||
|
def signal_side(pad: Pad) -> str:
|
||||||
|
name = pad.name
|
||||||
|
if name.startswith("gpio_"):
|
||||||
|
return "east" if pad.index is not None and pad.index < 16 else "west"
|
||||||
|
if name.startswith("xpi_"):
|
||||||
|
return "north"
|
||||||
|
return "south"
|
||||||
|
|
||||||
|
|
||||||
|
def power_instances(counts: dict[str, int], side_index: int) -> list[str]:
|
||||||
|
per_side: dict[str, int] = {}
|
||||||
|
for kind in POWER_PAD_KINDS:
|
||||||
|
count = counts[kind]
|
||||||
|
if count % len(SIDE_ORDER) != 0:
|
||||||
|
raise ValueError(
|
||||||
|
f"power_pads.{kind}={count} cannot be distributed across "
|
||||||
|
f"{len(SIDE_ORDER)} pad-ring sides"
|
||||||
|
)
|
||||||
|
per_side[kind] = count // len(SIDE_ORDER)
|
||||||
|
result: list[str] = []
|
||||||
|
for local_index in range(per_side["vdd"]):
|
||||||
|
for kind in ("vdd", "vss"):
|
||||||
|
index = (side_index * per_side[kind]) + local_index
|
||||||
|
result.append(f"{kind}_pads[{index}].{kind}_pad")
|
||||||
|
for local_index in range(per_side["iovdd"]):
|
||||||
|
for kind in ("iovdd", "iovss"):
|
||||||
|
index = (side_index * per_side[kind]) + local_index
|
||||||
|
result.append(f"{kind}_pads[{index}].{kind}_pad")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def interleave(signals: list[str], supplies: list[str]) -> list[str]:
|
||||||
|
result: list[str] = []
|
||||||
|
signal_index = 0
|
||||||
|
for supply_index, supply in enumerate(supplies):
|
||||||
|
target = round(((supply_index + 1) * len(signals)) / (len(supplies) + 1))
|
||||||
|
result.extend(signals[signal_index:target])
|
||||||
|
signal_index = target
|
||||||
|
result.append(supply)
|
||||||
|
result.extend(signals[signal_index:])
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def sram_instances(capacity_kib: int) -> dict[str, tuple[list[int], str]]:
|
||||||
|
if capacity_kib <= 0 or capacity_kib % 4 != 0:
|
||||||
|
raise ValueError("SRAM_SIZE_KIB must be a positive multiple of 4")
|
||||||
|
instances: dict[str, tuple[list[int], str]] = {}
|
||||||
|
for index in range(capacity_kib // 4):
|
||||||
|
column, row = index % SRAM_COLUMNS, index // SRAM_COLUMNS
|
||||||
|
location = [
|
||||||
|
SRAM_ORIGIN[0] + column * SRAM_PITCH[0],
|
||||||
|
SRAM_ORIGIN[1] + row * SRAM_PITCH[1],
|
||||||
|
]
|
||||||
|
instances[SRAM_INSTANCE.format(index=index)] = (location, "N")
|
||||||
|
return instances
|
||||||
|
|
||||||
|
|
||||||
|
def macro_config(instances: dict[str, tuple[list[int], str]], header: str) -> dict[str, object]:
|
||||||
|
base = "pdk_dir::libs.ref/sg13g2_sram"
|
||||||
|
master = SRAM_MASTER
|
||||||
|
return {
|
||||||
|
"gds": [f"{base}/gds/{master}.gds"],
|
||||||
|
"lef": [f"{base}/lef/{master}.lef"],
|
||||||
|
"vh": [header],
|
||||||
|
"spice": [f"{base}/cdl/{master}.cdl"],
|
||||||
|
"lib": {
|
||||||
|
"*_typ_1p20V_25C": [f"{base}/lib/{master}_typ_1p20V_25C.lib"],
|
||||||
|
"*_fast_1p32V_m40C": [f"{base}/lib/{master}_fast_1p32V_m55C.lib"],
|
||||||
|
"*_slow_1p08V_125C": [f"{base}/lib/{master}_slow_1p08V_125C.lib"],
|
||||||
|
},
|
||||||
|
"instances": {
|
||||||
|
name: {"location": location, "orientation": orientation}
|
||||||
|
for name, (location, orientation) in instances.items()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def macro_hooks(instances: dict[str, tuple[list[int], str]]) -> list[str]:
|
||||||
|
hooks: list[str] = []
|
||||||
|
for instance in instances:
|
||||||
|
bracket_start = instance.find("[")
|
||||||
|
if bracket_start >= 0:
|
||||||
|
bracket_end = instance.index("]", bracket_start)
|
||||||
|
instance_pattern = (
|
||||||
|
re.escape(instance[:bracket_start])
|
||||||
|
+ ".*"
|
||||||
|
+ instance[bracket_start + 1 : bracket_end]
|
||||||
|
+ ".*"
|
||||||
|
+ re.escape(instance[bracket_end + 1 :])
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
instance_pattern = re.escape(instance)
|
||||||
|
hooks.extend(
|
||||||
|
[
|
||||||
|
f"{instance_pattern} VDD VSS VDDARRAY! VSS!",
|
||||||
|
f"{instance_pattern} VDD VSS VDD! VSS!",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return hooks
|
||||||
|
|
||||||
|
|
||||||
|
def build_config(args: argparse.Namespace) -> dict[str, object]:
|
||||||
|
pads, _ = read_map(args.pin_map)
|
||||||
|
power_counts = read_power_pad_counts(args.pin_map)
|
||||||
|
signal_instances: dict[str, list[str]] = {side: [] for side in SIDE_ORDER}
|
||||||
|
for pad in pads:
|
||||||
|
instance = ihp130_pad_instance(pad)
|
||||||
|
if instance is None:
|
||||||
|
raise ValueError(f"IHP130 physical PAD mapping is missing for {pad.name}")
|
||||||
|
signal_instances[signal_side(pad)].append(instance)
|
||||||
|
|
||||||
|
flattened = [item for side in SIDE_ORDER for item in signal_instances[side]]
|
||||||
|
if len(flattened) != EXPECTED_SIGNAL_PADS:
|
||||||
|
raise ValueError(
|
||||||
|
f"expected {EXPECTED_SIGNAL_PADS} signal PADs, got {len(flattened)}"
|
||||||
|
)
|
||||||
|
if len(flattened) != len(set(flattened)):
|
||||||
|
raise ValueError("physical signal PAD instances are not unique")
|
||||||
|
|
||||||
|
pad_sides = {
|
||||||
|
side.upper(): interleave(signal_instances[side], power_instances(power_counts, index))
|
||||||
|
for index, side in enumerate(SIDE_ORDER)
|
||||||
|
}
|
||||||
|
all_placed = [item for side in SIDE_ORDER for item in pad_sides[side.upper()]]
|
||||||
|
expected_total = len(flattened) + sum(power_counts.values())
|
||||||
|
if len(all_placed) != expected_total or len(all_placed) != len(set(all_placed)):
|
||||||
|
raise ValueError(
|
||||||
|
"PAD ring placement does not cover every signal and power PAD exactly once"
|
||||||
|
)
|
||||||
|
|
||||||
|
config_dir = args.output.resolve().parent
|
||||||
|
sram_header = config_path(args.sram_vh, config_dir)
|
||||||
|
active_sram_instances = sram_instances(args.sram_size_kib) if args.have_sram_macro else {}
|
||||||
|
macros = (
|
||||||
|
{SRAM_MASTER: macro_config(active_sram_instances, sram_header)}
|
||||||
|
if active_sram_instances
|
||||||
|
else {}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"meta": {"version": 3, "flow": "Chip"},
|
||||||
|
"DESIGN_NAME": "retrosoc_tiny_asic",
|
||||||
|
"VERILOG_FILES": [config_path(args.rtl, config_dir)],
|
||||||
|
"USE_SLANG": True,
|
||||||
|
"SLANG_ARGUMENTS": ["--keep-hierarchy"],
|
||||||
|
"VERILOG_POWER_DEFINE": None,
|
||||||
|
"SYNTH_SHARE_RESOURCES": False,
|
||||||
|
"SYNTH_HIERARCHY_MODE": "deferred_flatten",
|
||||||
|
"SYNTH_KEEP_HIERARCHY_MODULES": [
|
||||||
|
"sg13g2_IOPadVdd",
|
||||||
|
"sg13g2_IOPadVss",
|
||||||
|
"sg13g2_IOPadIOVdd",
|
||||||
|
"sg13g2_IOPadIOVss",
|
||||||
|
],
|
||||||
|
"YOSYS_LOG_LEVEL": "WARNING",
|
||||||
|
"SYNTH_STRATEGY": "AREA 3",
|
||||||
|
# Hotspots sit at the SRAM bank pin edges; detailed routing resolves the
|
||||||
|
# moderate estimated overflow. The signoff bar remains detailed-routing
|
||||||
|
# DRC, post-RCX multi-corner STA, KLayout DRC, and netgen LVS.
|
||||||
|
"GRT_ALLOW_CONGESTION": True,
|
||||||
|
# This OpenROAD build's antenna analysis leaks/stalls on the Tiny netlist
|
||||||
|
# (check_antennas -verbose ballooned to 248 GB RSS; repair_antennas did
|
||||||
|
# not converge in 100 minutes). Skip the report/repair steps; antenna
|
||||||
|
# coverage is a documented gap for this flow revision.
|
||||||
|
"RUN_ANTENNA_REPAIR": False,
|
||||||
|
# Hazard3 with EXTENSION_A=0 (RV32IM) leaves x_amo_phase undriven by
|
||||||
|
# design; every use is masked by a constant-false guard. Waive the
|
||||||
|
# three benign "used but has no driver" check errors.
|
||||||
|
"ERROR_ON_SYNTH_CHECKS": False,
|
||||||
|
"RUN_POST_GPL_DESIGN_REPAIR": False,
|
||||||
|
"RUN_CTS": False,
|
||||||
|
"RUN_POST_CTS_RESIZER_TIMING": False,
|
||||||
|
"EXTRA_EXCLUDED_CELLS": ["sg13g2_IOPad*"],
|
||||||
|
"PRIMARY_GDSII_STREAMOUT_TOOL": "klayout",
|
||||||
|
"PNR_SDC_FILE": config_path(args.sdc, config_dir),
|
||||||
|
"SIGNOFF_SDC_FILE": config_path(args.sdc, config_dir),
|
||||||
|
"FALLBACK_SDC": config_path(args.sdc, config_dir),
|
||||||
|
"STA_EXTRA_CORNER_TCL_FILE": config_path(
|
||||||
|
ROOT / "physical/librelane/tiny/sta_report_limit.tcl", config_dir
|
||||||
|
),
|
||||||
|
"CLOCK_PORT": list(CLOCK_PORTS),
|
||||||
|
"CLOCK_PERIOD": 1_000_000_000 / args.ext_clk_hz,
|
||||||
|
"VDD_NETS": ["VDD"],
|
||||||
|
"GND_NETS": ["VSS"],
|
||||||
|
"FP_SIZING": "absolute",
|
||||||
|
"PDN_CORE_RING": True,
|
||||||
|
"PDN_CORE_RING_VWIDTH": 15,
|
||||||
|
"PDN_CORE_RING_HWIDTH": 15,
|
||||||
|
"PDN_CORE_RING_VSPACING": 5,
|
||||||
|
"PDN_CORE_RING_HSPACING": 5,
|
||||||
|
"PDN_ENABLE_PINS": True,
|
||||||
|
"ERROR_ON_PDN_VIOLATIONS": True,
|
||||||
|
"PDN_CFG": config_path(args.pdn, config_dir),
|
||||||
|
"MACROS": macros,
|
||||||
|
"PDN_MACRO_CONNECTIONS": macro_hooks(active_sram_instances),
|
||||||
|
"MAGIC_GDS_FLATGLOB": [
|
||||||
|
"lvsres_*",
|
||||||
|
"VIA_M1_*",
|
||||||
|
"VIA_M2_*",
|
||||||
|
"*_CELL_CORNER",
|
||||||
|
"RSC_*",
|
||||||
|
"*_CELL_SUB",
|
||||||
|
],
|
||||||
|
"PAD_SOUTH": pad_sides["SOUTH"],
|
||||||
|
"PAD_EAST": pad_sides["EAST"],
|
||||||
|
"PAD_NORTH": pad_sides["NORTH"],
|
||||||
|
"PAD_WEST": pad_sides["WEST"],
|
||||||
|
"DIE_AREA": DIE_AREA,
|
||||||
|
"CORE_AREA": CORE_AREA,
|
||||||
|
"PL_TARGET_DENSITY_PCT": 45,
|
||||||
|
"PDN_CORE_RING_CONNECT_TO_PADS": True,
|
||||||
|
"PAD_CFG": config_path(ROOT / "physical/librelane/tiny/pad_cfg.tcl", config_dir),
|
||||||
|
"PAD_BONDPAD_NAME": "bondpad_70x70",
|
||||||
|
"EXTRA_GDS": [config_path(args.bondpad_gds, config_dir)],
|
||||||
|
"EXTRA_LEFS": [config_path(args.bondpad_lef, config_dir)],
|
||||||
|
"IGNORE_DISCONNECTED_MODULES": ["bondpad_70x70"],
|
||||||
|
"MAGIC_EXT_UNIQUE": "notopports",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def positive_integer(value: str) -> int:
|
||||||
|
parsed = int(value)
|
||||||
|
if parsed <= 0:
|
||||||
|
raise argparse.ArgumentTypeError("value must be positive")
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--pin-map", type=Path, required=True)
|
||||||
|
parser.add_argument("--rtl", type=Path, required=True)
|
||||||
|
parser.add_argument("--sdc", type=Path, required=True)
|
||||||
|
parser.add_argument("--pdn", type=Path, required=True)
|
||||||
|
parser.add_argument("--bondpad-gds", type=Path, required=True)
|
||||||
|
parser.add_argument("--bondpad-lef", type=Path, required=True)
|
||||||
|
parser.add_argument("--sram-vh", type=Path, required=True)
|
||||||
|
parser.add_argument("--ext-clk-hz", type=positive_integer, required=True)
|
||||||
|
parser.add_argument("--have-sram-macro", action="store_true")
|
||||||
|
parser.add_argument("--sram-size-kib", type=positive_integer, default=128)
|
||||||
|
parser.add_argument("--output", type=Path, required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
config = build_config(args)
|
||||||
|
atomic_write(args.output, json.dumps(config, indent=2, sort_keys=True) + "\n")
|
||||||
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
|
parser.error(str(error))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate pad-aware timing constraints for the Tiny IHP130 Chip flow."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from scripts.rtl.generate_pin_map import Pad, ihp130_pad_instance, read_map # noqa: E402
|
||||||
|
from scripts.check_clock_reset_domains import validate # noqa: E402
|
||||||
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
CLOCK_PADS = {
|
||||||
|
"external": "extclk_i_pad",
|
||||||
|
"jtag": "jtag_tck_i_pad",
|
||||||
|
}
|
||||||
|
RESET_PADS = {"ext_rst_n_i_pad", "jtag_trst_n_i_pad"}
|
||||||
|
IHP130_SYSTEM_CLOCK_PIN = "u_clock_buffer.u_sg13g2_buf_1/X"
|
||||||
|
JTAG_PERIOD_NS = 100.0
|
||||||
|
|
||||||
|
|
||||||
|
def positive_integer(value: str) -> int:
|
||||||
|
parsed = int(value)
|
||||||
|
if parsed <= 0:
|
||||||
|
raise argparse.ArgumentTypeError("value must be positive")
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def timing_clock(pad: Pad) -> str:
|
||||||
|
if pad.name.startswith("jtag_"):
|
||||||
|
return "jtag"
|
||||||
|
return "external"
|
||||||
|
|
||||||
|
|
||||||
|
def logical_directions(pad: Pad) -> tuple[bool, bool]:
|
||||||
|
input_kinds = {"tri_input", "schmitt_input"}
|
||||||
|
output_kinds = {"tri_output", "peripheral_output"}
|
||||||
|
bidir_kinds = {"full", "peripheral_bidir", "xpi_data"}
|
||||||
|
if pad.kind in input_kinds:
|
||||||
|
return True, False
|
||||||
|
if pad.kind in output_kinds:
|
||||||
|
return False, True
|
||||||
|
if pad.kind in bidir_kinds:
|
||||||
|
return True, True
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
|
||||||
|
def render(args: argparse.Namespace) -> str:
|
||||||
|
validate(args.domains, ROOT)
|
||||||
|
pads, _ = read_map(args.pin_map)
|
||||||
|
by_name = {pad.name: pad for pad in pads}
|
||||||
|
for name in CLOCK_PADS.values():
|
||||||
|
if name not in by_name:
|
||||||
|
raise ValueError(f"clock PAD is missing from the pin map: {name}")
|
||||||
|
|
||||||
|
periods = {
|
||||||
|
"external": 1_000_000_000 / args.ext_clk_hz,
|
||||||
|
"jtag": JTAG_PERIOD_NS,
|
||||||
|
}
|
||||||
|
groups: dict[str, dict[str, list[str]]] = {
|
||||||
|
name: {"inputs": [], "outputs": []} for name in periods
|
||||||
|
}
|
||||||
|
excluded = set(CLOCK_PADS.values()) | RESET_PADS
|
||||||
|
for pad in pads:
|
||||||
|
if pad.name in excluded:
|
||||||
|
continue
|
||||||
|
is_input, is_output = logical_directions(pad)
|
||||||
|
clock = timing_clock(pad)
|
||||||
|
if is_input:
|
||||||
|
groups[clock]["inputs"].append(pad.name)
|
||||||
|
if is_output:
|
||||||
|
groups[clock]["outputs"].append(pad.name)
|
||||||
|
|
||||||
|
lines = [
|
||||||
|
"# Generated by physical/librelane/tiny/scripts/generate_sdc.py; do not edit.",
|
||||||
|
"current_design $::env(DESIGN_NAME)",
|
||||||
|
"set_units -time ns",
|
||||||
|
"",
|
||||||
|
"proc require_pins {label name} {",
|
||||||
|
" set objects [get_pins -quiet $name]",
|
||||||
|
' if {$objects eq ""} { error "required SDC pin is missing: $label ($name)" }',
|
||||||
|
" return $objects",
|
||||||
|
"}",
|
||||||
|
"proc require_ports {label name} {",
|
||||||
|
" set objects [get_ports -quiet $name]",
|
||||||
|
' if {$objects eq ""} { error "required SDC port is missing: $label ($name)" }',
|
||||||
|
" return $objects",
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
for name, pad_name in CLOCK_PADS.items():
|
||||||
|
instance = ihp130_pad_instance(by_name[pad_name])
|
||||||
|
if instance is None:
|
||||||
|
raise ValueError(f"clock PAD has no IHP130 physical instance: {pad_name}")
|
||||||
|
lines.append(
|
||||||
|
f'set clk_{name}_pin [require_pins "clock {name}" {{{instance}/p2c}}]'
|
||||||
|
)
|
||||||
|
lines.append(
|
||||||
|
f"create_clock -name clk_{name} -period {periods[name]:.12g} $clk_{name}_pin"
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
f'set clk_system_pin [require_pins "clock system" {{{IHP130_SYSTEM_CLOCK_PIN}}}]',
|
||||||
|
"create_generated_clock -name clk_system -source $clk_external_pin "
|
||||||
|
"-divide_by 1 $clk_system_pin",
|
||||||
|
"",
|
||||||
|
"set_clock_groups -name retrosoc_async -asynchronous \\",
|
||||||
|
" -group [get_clocks {clk_external clk_system}] \\",
|
||||||
|
" -group [get_clocks {clk_jtag}]",
|
||||||
|
"",
|
||||||
|
"set all_retrosoc_clocks [get_clocks {clk_external clk_system clk_jtag}]",
|
||||||
|
"set_clock_uncertainty -setup 0.2 $all_retrosoc_clocks",
|
||||||
|
"set_clock_uncertainty -hold 0.1 $all_retrosoc_clocks",
|
||||||
|
"set_clock_transition 0.1 $all_retrosoc_clocks",
|
||||||
|
"set_timing_derate -early 0.95",
|
||||||
|
"set_timing_derate -late 1.05",
|
||||||
|
"set_max_fanout 10 [current_design]",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
for reset in sorted(RESET_PADS):
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
f'set reset_{reset} [require_ports "reset {reset}" {{{reset}}}]',
|
||||||
|
f"set_false_path -from $reset_{reset}",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
for clock, port_groups in groups.items():
|
||||||
|
delay = periods[clock] * 0.2
|
||||||
|
for direction, ports in port_groups.items():
|
||||||
|
if not ports:
|
||||||
|
continue
|
||||||
|
variable = f"{clock}_{direction}"
|
||||||
|
lines.append(f"set {variable} [get_ports {{{' '.join(ports)}}}]")
|
||||||
|
if direction == "inputs":
|
||||||
|
lines.append(f"set_input_delay -min 0 -clock clk_{clock} ${variable}")
|
||||||
|
lines.append(f"set_input_delay -max {delay:.12g} -clock clk_{clock} ${variable}")
|
||||||
|
else:
|
||||||
|
lines.append(f"set_output_delay -min 0 -clock clk_{clock} ${variable}")
|
||||||
|
lines.append(f"set_output_delay -max {delay:.12g} -clock clk_{clock} ${variable}")
|
||||||
|
lines.append(f"set_load 0.006 ${variable}")
|
||||||
|
lines.append("")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--domains", type=Path, required=True)
|
||||||
|
parser.add_argument("--pin-map", type=Path, required=True)
|
||||||
|
parser.add_argument("--ext-clk-hz", type=positive_integer, required=True)
|
||||||
|
parser.add_argument("--output", type=Path, required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
atomic_write(args.output, render(args))
|
||||||
|
except (OSError, ValueError, json.JSONDecodeError) as error:
|
||||||
|
parser.error(str(error))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Create a checksummed Tiny LibreLane implementation delivery archive."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import shutil
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def hash_file(path: Path) -> str:
|
||||||
|
digest = hashlib.sha256()
|
||||||
|
with path.open("rb") as source:
|
||||||
|
while block := source.read(1024 * 1024):
|
||||||
|
digest.update(block)
|
||||||
|
return digest.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--root", type=Path, required=True)
|
||||||
|
parser.add_argument("--variant-root", type=Path, required=True)
|
||||||
|
parser.add_argument("--run-root", type=Path, required=True)
|
||||||
|
parser.add_argument("--output", type=Path, required=True)
|
||||||
|
args = parser.parse_args()
|
||||||
|
root = args.root.resolve()
|
||||||
|
variant_root = args.variant_root.resolve()
|
||||||
|
run_root = args.run_root.resolve()
|
||||||
|
required = (
|
||||||
|
run_root / "config.json",
|
||||||
|
run_root / "result.json",
|
||||||
|
run_root / "final",
|
||||||
|
run_root / "runs/current",
|
||||||
|
variant_root / "meta/manifest.json",
|
||||||
|
variant_root / "meta/librelane-chip-doctor.json",
|
||||||
|
variant_root / "meta/librelane-chip.json",
|
||||||
|
root / "dependencies/dependencies.lock.json",
|
||||||
|
)
|
||||||
|
missing = [str(path) for path in required if not path.exists()]
|
||||||
|
if missing:
|
||||||
|
parser.error("missing LibreLane delivery input(s): " + ", ".join(missing))
|
||||||
|
|
||||||
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.TemporaryDirectory(prefix="retrosoc-librelane-package-") as temporary:
|
||||||
|
staging = Path(temporary) / "retrosoc-tiny-ihp130-chip"
|
||||||
|
shutil.copytree(run_root / "final", staging / "final")
|
||||||
|
shutil.copytree(run_root / "runs/current", staging / "evidence/run")
|
||||||
|
shutil.copy2(run_root / "config.json", staging / "evidence/config.json")
|
||||||
|
shutil.copy2(run_root / "result.json", staging / "evidence/result.json")
|
||||||
|
shutil.copy2(variant_root / "meta/manifest.json", staging / "evidence/manifest.json")
|
||||||
|
shutil.copy2(
|
||||||
|
variant_root / "meta/librelane-chip-doctor.json",
|
||||||
|
staging / "evidence/librelane-doctor.json",
|
||||||
|
)
|
||||||
|
shutil.copy2(
|
||||||
|
variant_root / "meta/librelane-chip.json",
|
||||||
|
staging / "evidence/librelane-chip.json",
|
||||||
|
)
|
||||||
|
shutil.copy2(
|
||||||
|
root / "dependencies/dependencies.lock.json",
|
||||||
|
staging / "evidence/dependencies.lock.json",
|
||||||
|
)
|
||||||
|
files = sorted(path for path in staging.rglob("*") if path.is_file())
|
||||||
|
sums = "\n".join(f"{hash_file(path)} {path.relative_to(staging)}" for path in files)
|
||||||
|
(staging / "SHA256SUMS").write_text(sums + "\n", encoding="utf-8")
|
||||||
|
with tarfile.open(args.output, "w:gz") as archive:
|
||||||
|
archive.add(staging, arcname=staging.name)
|
||||||
|
print(f"created LibreLane delivery: {args.output}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
|
||||||
|
// SPDX-License-Identifier: MulanPSL-2.0
|
||||||
|
// Synthesis-only header for the locked IHP SRAM macro physical views.
|
||||||
|
|
||||||
|
(* blackbox *)
|
||||||
|
module RM_IHPSG13_1P_1024x32_c2_bm_bist (
|
||||||
|
input wire A_CLK,
|
||||||
|
input wire A_MEN,
|
||||||
|
input wire A_WEN,
|
||||||
|
input wire A_REN,
|
||||||
|
input wire [ 9:0] A_ADDR,
|
||||||
|
input wire [31:0] A_DIN,
|
||||||
|
input wire A_DLY,
|
||||||
|
output wire [31:0] A_DOUT,
|
||||||
|
input wire [31:0] A_BM,
|
||||||
|
input wire A_BIST_CLK,
|
||||||
|
input wire A_BIST_EN,
|
||||||
|
input wire A_BIST_MEN,
|
||||||
|
input wire A_BIST_WEN,
|
||||||
|
input wire A_BIST_REN,
|
||||||
|
input wire [ 9:0] A_BIST_ADDR,
|
||||||
|
input wire [31:0] A_BIST_DIN,
|
||||||
|
input wire [31:0] A_BIST_BM
|
||||||
|
);
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# LibreLane 3.0.5 hard-codes 1000 expanded paths per clock group. Cap only
|
||||||
|
# report enumeration; worst-slack/TNS analysis still covers the full graph.
|
||||||
|
set ::retrosoc_sta_group_path_count 10
|
||||||
|
puts "\[INFO\] OpenSTA executable: [info nameofexecutable]"
|
||||||
|
rename report_checks ::retrosoc_report_checks
|
||||||
|
proc report_checks {args} {
|
||||||
|
set count_index [lsearch -exact $args -group_path_count]
|
||||||
|
if {$count_index >= 0} {
|
||||||
|
set value_index [expr {$count_index + 1}]
|
||||||
|
if {$value_index < [llength $args]} {
|
||||||
|
lset args $value_index $::retrosoc_sta_group_path_count
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lappend args -group_path_count $::retrosoc_sta_group_path_count
|
||||||
|
}
|
||||||
|
uplevel 1 [list ::retrosoc_report_checks {*}$args]
|
||||||
|
}
|
||||||
|
|
||||||
|
rename report_check_types ::retrosoc_report_check_types
|
||||||
|
proc report_check_types {args} {
|
||||||
|
set violator_index [lsearch -exact $args -violators]
|
||||||
|
if {$violator_index >= 0} {
|
||||||
|
set args [lreplace $args $violator_index $violator_index]
|
||||||
|
}
|
||||||
|
uplevel 1 [list ::retrosoc_report_check_types {*}$args]
|
||||||
|
}
|
||||||
|
|
||||||
|
rename report_parasitic_annotation ::retrosoc_report_parasitic_annotation
|
||||||
|
proc report_parasitic_annotation {args} {
|
||||||
|
set detail_index [lsearch -exact $args -report_unannotated]
|
||||||
|
if {$detail_index >= 0} {
|
||||||
|
set args [lreplace $args $detail_index $detail_index]
|
||||||
|
}
|
||||||
|
uplevel 1 [list ::retrosoc_report_parasitic_annotation {*}$args]
|
||||||
|
}
|
||||||
@@ -30,7 +30,8 @@ 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.
|
recorded in the macro artifact rather than added to the SoC PDK checkout.
|
||||||
|
|
||||||
The IHP130 dependency is also the technology source for the independent
|
The IHP130 dependency is also the technology source for the independent
|
||||||
`physical/librelane/` core-hardening and full-chip pad-ring flows. The locked
|
`physical/librelane/{mini,tiny}/` core-hardening and full-chip pad-ring flows.
|
||||||
|
The locked
|
||||||
checkout includes the upstream `libs.tech/librelane` standard-cell, pad,
|
checkout includes the upstream `libs.tech/librelane` standard-cell, pad,
|
||||||
extraction, DRC, LVS, filler, bondpad, and seal-ring configuration; LibreLane
|
extraction, DRC, LVS, filler, bondpad, and seal-ring configuration; LibreLane
|
||||||
consumes it directly in manual-PDK mode. The other three open PDKs do not yet
|
consumes it directly in manual-PDK mode. The other three open PDKs do not yet
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from typing import Any
|
|||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[4]
|
ROOT = Path(__file__).resolve().parents[4]
|
||||||
sys.path.insert(0, str(ROOT))
|
sys.path.insert(0, str(ROOT))
|
||||||
from rtl.mini.pin_map.generate_pin_map import read_map # noqa: E402
|
from scripts.rtl.generate_pin_map import read_map # noqa: E402
|
||||||
from scripts.check_clock_reset_domains import validate # noqa: E402
|
from scripts.check_clock_reset_domains import validate # noqa: E402
|
||||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
OPENSTA ?= sta
|
OPENSTA ?= sta
|
||||||
|
OPENSTA_TOP ?= $(if $(filter TINY,$(SOC)),retrosoc_tiny_asic,retrosoc_asic)
|
||||||
OPENSTA_THREADS ?= $(JOBS)
|
OPENSTA_THREADS ?= $(JOBS)
|
||||||
OPENSTA_NETLIST ?= $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.v
|
OPENSTA_NETLIST ?= $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.v
|
||||||
OPENSTA_REPORT ?= $(STA_BUILD_ROOT)/retrosoc_sta.log
|
OPENSTA_REPORT ?= $(STA_BUILD_ROOT)/retrosoc_sta.log
|
||||||
@@ -8,8 +9,8 @@ OPENSTA_METRICS ?= $(STA_BUILD_ROOT)/timing_metrics.rpt
|
|||||||
OPENSTA_CONFIG ?= $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.config
|
OPENSTA_CONFIG ?= $(SYN_BUILD_ROOT)/out/retrosoc_asic_yosys.config
|
||||||
OPENSTA_SDC ?= $(STA_BUILD_ROOT)/retrosoc_core.sdc
|
OPENSTA_SDC ?= $(STA_BUILD_ROOT)/retrosoc_core.sdc
|
||||||
OPENSTA_SDC_GEN := $(ROOT_PATH)/physical/smoke/sta/opensta/generate_sdc.py
|
OPENSTA_SDC_GEN := $(ROOT_PATH)/physical/smoke/sta/opensta/generate_sdc.py
|
||||||
OPENSTA_DOMAIN_MAP := $(ROOT_PATH)/rtl/mini/integration/clock_reset_domains.json
|
OPENSTA_DOMAIN_MAP := $(RTL_PATH)/integration/clock_reset_domains.json
|
||||||
OPENSTA_PIN_MAP := $(ROOT_PATH)/rtl/mini/pin_map/pin_map.json
|
OPENSTA_PIN_MAP := $(RTL_PATH)/pin_map/pin_map.json
|
||||||
|
|
||||||
include $(ROOT_PATH)/physical/smoke/sta/opensta/pdk_timing.mk
|
include $(ROOT_PATH)/physical/smoke/sta/opensta/pdk_timing.mk
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ sta: $(OPENSTA_SDC) | manifest
|
|||||||
}
|
}
|
||||||
python3 $(ROOT_PATH)/scripts/run_flow.py --tool opensta --log $(OPENSTA_LOG) \
|
python3 $(ROOT_PATH)/scripts/run_flow.py --tool opensta --log $(OPENSTA_LOG) \
|
||||||
--result $(STA_BUILD_ROOT)/result-sta.json \
|
--result $(STA_BUILD_ROOT)/result-sta.json \
|
||||||
--env OPENSTA_NETLIST=$(OPENSTA_NETLIST) --env OPENSTA_LIBERTY=$(OPENSTA_LIBERTY) \
|
--env OPENSTA_TOP=$(OPENSTA_TOP) --env OPENSTA_NETLIST=$(OPENSTA_NETLIST) --env OPENSTA_LIBERTY=$(OPENSTA_LIBERTY) \
|
||||||
--env 'OPENSTA_LINK_LIBS=$(OPENSTA_LINK_LIBS)' --env 'OPENSTA_SRAM_LIBS=$(OPENSTA_SRAM_LIBS)' \
|
--env 'OPENSTA_LINK_LIBS=$(OPENSTA_LINK_LIBS)' --env 'OPENSTA_SRAM_LIBS=$(OPENSTA_SRAM_LIBS)' \
|
||||||
--env OPENSTA_SDC=$(OPENSTA_SDC) --env OPENSTA_REPORT=$(OPENSTA_REPORT) \
|
--env OPENSTA_SDC=$(OPENSTA_SDC) --env OPENSTA_REPORT=$(OPENSTA_REPORT) \
|
||||||
--env OPENSTA_METRICS=$(OPENSTA_METRICS) -- \
|
--env OPENSTA_METRICS=$(OPENSTA_METRICS) -- \
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ if {[info exists ::env(OPENSTA_SRAM_LIBS)] && $::env(OPENSTA_SRAM_LIBS) ne ""} {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_verilog $::env(OPENSTA_NETLIST)
|
read_verilog $::env(OPENSTA_NETLIST)
|
||||||
link_design retrosoc_asic
|
set top_design retrosoc_asic
|
||||||
|
if {[info exists ::env(OPENSTA_TOP)] && $::env(OPENSTA_TOP) ne ""} {
|
||||||
|
set top_design $::env(OPENSTA_TOP)
|
||||||
|
}
|
||||||
|
link_design $top_design
|
||||||
read_sdc $::env(OPENSTA_SDC)
|
read_sdc $::env(OPENSTA_SDC)
|
||||||
|
|
||||||
report_checks -path_delay min_max -sort_by_slack -slack_max 0.0 -group_path_count 1000 -endpoint_path_count 1000 > $::env(OPENSTA_REPORT)
|
report_checks -path_delay min_max -sort_by_slack -slack_max 0.0 -group_path_count 1000 -endpoint_path_count 1000 > $::env(OPENSTA_REPORT)
|
||||||
|
|||||||
@@ -18,10 +18,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[4]
|
REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||||
SCRIPT_DIR = REPO_ROOT / "rtl/mini/script"
|
sys.path.insert(0, str(REPO_ROOT))
|
||||||
sys.path.insert(0, str(SCRIPT_DIR))
|
from scripts.rtl.filelist import FileList, parse_filelists, write_filelist # noqa: E402
|
||||||
from filelist import FileList, parse_filelists, write_filelist # noqa: E402
|
from rtl.mini.script.generate_filelist import generate_all # noqa: E402
|
||||||
from generate_filelist import generate_all # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
APU_SOURCE_MARKERS = ("/ip/multimedia/apu_", "/ip/multimedia/apb4_apu.sv")
|
APU_SOURCE_MARKERS = ("/ip/multimedia/apu_", "/ip/multimedia/apb4_apu.sv")
|
||||||
|
|||||||
@@ -13,13 +13,10 @@ from pathlib import Path
|
|||||||
|
|
||||||
|
|
||||||
REPO_ROOT = Path(__file__).resolve().parents[4]
|
REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||||
SCRIPT_DIR = REPO_ROOT / "rtl/mini/script"
|
sys.path.insert(0, str(REPO_ROOT))
|
||||||
INTEGRATION_DIR = REPO_ROOT / "rtl/mini/integration"
|
from scripts.rtl.filelist import FileList, atomic_write, parse_filelists # noqa: E402
|
||||||
sys.path.insert(0, str(SCRIPT_DIR))
|
from rtl.mini.script.generate_filelist import generate_all # noqa: E402
|
||||||
sys.path.insert(0, str(INTEGRATION_DIR))
|
from rtl.mini.integration.generate_user_extensions import generate as generate_user_extensions # noqa: E402
|
||||||
from filelist import FileList, atomic_write, parse_filelists # noqa: E402
|
|
||||||
from generate_filelist import generate_all # noqa: E402
|
|
||||||
from generate_user_extensions import generate as generate_user_extensions # noqa: E402
|
|
||||||
|
|
||||||
|
|
||||||
INCLUDE_RE = re.compile(r'^\s*`include\s+"([^"]+)"')
|
INCLUDE_RE = re.compile(r'^\s*`include\s+"([^"]+)"')
|
||||||
@@ -69,6 +66,20 @@ def configured_filelist(
|
|||||||
*,
|
*,
|
||||||
require_files: bool = True,
|
require_files: bool = True,
|
||||||
) -> FileList:
|
) -> FileList:
|
||||||
|
if getattr(args, "soc", "MINI") == "TINY":
|
||||||
|
directory = getattr(args, "source_filelist_dir", None)
|
||||||
|
if directory is None:
|
||||||
|
raise ValueError("Tiny export requires --source-filelist-dir")
|
||||||
|
names = ("def.fl", "inc.fl", "commonip.fl", "clusterip.fl", "ip.fl",
|
||||||
|
"tech.fl", "core_hazard3.fl", "top.fl")
|
||||||
|
filelist = parse_filelists((directory / name for name in names), require_files=require_files)
|
||||||
|
if "+define+RETROSOC_SOC__TINY" not in filelist.defines:
|
||||||
|
raise ValueError("Tiny export requires Tiny source lists")
|
||||||
|
if "+define+SYNTHESIS" not in filelist.defines:
|
||||||
|
filelist.defines.append("+define+SYNTHESIS")
|
||||||
|
return filelist
|
||||||
|
if args.dynamic_core_filelist is None or args.dynamic_ip_filelist is None:
|
||||||
|
raise ValueError("Mini export requires dynamic core and IP filelists")
|
||||||
names: list[str | Path] = []
|
names: list[str | Path] = []
|
||||||
for name in (
|
for name in (
|
||||||
"memory_map_filelist",
|
"memory_map_filelist",
|
||||||
@@ -271,7 +282,7 @@ def metadata_file(value: str) -> tuple[Path, Path]:
|
|||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
parser = argparse.ArgumentParser(description="Export configured retroSoC RTL sources")
|
parser = argparse.ArgumentParser(description="Export configured retroSoC RTL sources")
|
||||||
parser.add_argument("mode", choices=("tar", "sv"))
|
parser.add_argument("mode", choices=("tar", "sv"))
|
||||||
parser.add_argument("--soc", default="MINI", choices=("MINI",))
|
parser.add_argument("--soc", default="MINI", choices=("MINI", "TINY"))
|
||||||
parser.add_argument("--pdk", default="IHP130", choices=("IHP130", "ICS55", "SKY130", "GF180"))
|
parser.add_argument("--pdk", default="IHP130", choices=("IHP130", "ICS55", "SKY130", "GF180"))
|
||||||
parser.add_argument("--simu", default="VCS", choices=("VCS", "VERILATOR", "IVERILOG"))
|
parser.add_argument("--simu", default="VCS", choices=("VCS", "VERILATOR", "IVERILOG"))
|
||||||
parser.add_argument("--have-pll", action="store_true")
|
parser.add_argument("--have-pll", action="store_true")
|
||||||
@@ -285,18 +296,19 @@ def parse_args() -> argparse.Namespace:
|
|||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--dynamic-core-filelist",
|
"--dynamic-core-filelist",
|
||||||
type=Path,
|
type=Path,
|
||||||
required=True,
|
required=False,
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--dynamic-ip-filelist",
|
"--dynamic-ip-filelist",
|
||||||
type=Path,
|
type=Path,
|
||||||
required=True,
|
required=False,
|
||||||
)
|
)
|
||||||
parser.add_argument("--memory-map-filelist", type=Path)
|
parser.add_argument("--memory-map-filelist", type=Path)
|
||||||
parser.add_argument("--soc-topology-filelist", type=Path)
|
parser.add_argument("--soc-topology-filelist", type=Path)
|
||||||
parser.add_argument("--user-extensions-filelist", type=Path)
|
parser.add_argument("--user-extensions-filelist", type=Path)
|
||||||
parser.add_argument("--pin-map-filelist", type=Path)
|
parser.add_argument("--pin-map-filelist", type=Path)
|
||||||
parser.add_argument("--archinfo-incdir", type=Path)
|
parser.add_argument("--archinfo-incdir", type=Path)
|
||||||
|
parser.add_argument("--source-filelist-dir", type=Path)
|
||||||
parser.add_argument("--librelane-safe", action="store_true")
|
parser.add_argument("--librelane-safe", action="store_true")
|
||||||
parser.add_argument("--metadata-file", type=metadata_file, action="append", default=[])
|
parser.add_argument("--metadata-file", type=metadata_file, action="append", default=[])
|
||||||
parser.add_argument("--output-dir", type=Path, default=REPO_ROOT / "export")
|
parser.add_argument("--output-dir", type=Path, default=REPO_ROOT / "export")
|
||||||
@@ -312,15 +324,16 @@ def main() -> int:
|
|||||||
with tempfile.TemporaryDirectory(prefix="retrosoc-filelists-") as temporary:
|
with tempfile.TemporaryDirectory(prefix="retrosoc-filelists-") as temporary:
|
||||||
generated_dir = Path(temporary)
|
generated_dir = Path(temporary)
|
||||||
user_extensions_dir = generated_dir / "user_extensions"
|
user_extensions_dir = generated_dir / "user_extensions"
|
||||||
generate_all(generated_dir, build_defines(args))
|
if args.soc == "MINI":
|
||||||
if args.user_extensions_filelist is None:
|
generate_all(generated_dir, build_defines(args))
|
||||||
|
if args.soc == "MINI" and args.user_extensions_filelist is None:
|
||||||
generate_user_extensions(
|
generate_user_extensions(
|
||||||
REPO_ROOT / "rtl/mini/integration/user_extensions.json",
|
REPO_ROOT / "rtl/mini/integration/user_extensions.json",
|
||||||
user_extensions_dir,
|
user_extensions_dir,
|
||||||
)
|
)
|
||||||
filelist = configured_filelist(args, generated_dir, user_extensions_dir)
|
filelist = configured_filelist(args, generated_dir, user_extensions_dir)
|
||||||
if args.mode == "sv":
|
if args.mode == "sv":
|
||||||
output = export_dir / "retrosoc_asic_sources.sv"
|
output = export_dir / ("retrosoc_tiny_asic_sources.sv" if args.soc == "TINY" else "retrosoc_asic_sources.sv")
|
||||||
write_single_sv(filelist, output, librelane_safe=args.librelane_safe)
|
write_single_sv(filelist, output, librelane_safe=args.librelane_safe)
|
||||||
else:
|
else:
|
||||||
output = write_tar(filelist, export_dir, args.soc, dict(args.metadata_file))
|
output = write_tar(filelist, export_dir, args.soc, dict(args.metadata_file))
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ APU_BLOCK_STA_RESULT := $(APU_BLOCK_STA)/result-sta.json
|
|||||||
APU_BLOCK_SYN_RESULT := $(APU_BLOCK_SYN)/result-synth.json
|
APU_BLOCK_SYN_RESULT := $(APU_BLOCK_SYN)/result-synth.json
|
||||||
|
|
||||||
$(APU_BLOCK_FL): $(APU_BLOCK_EXPORTER) $(FILELIST_TEMPLATES) \
|
$(APU_BLOCK_FL): $(APU_BLOCK_EXPORTER) $(FILELIST_TEMPLATES) \
|
||||||
$(RTL_PATH)/script/generate_filelist.py $(RTL_PATH)/script/filelist.py
|
$(RTL_PATH)/script/generate_filelist.py $(ROOT_PATH)/scripts/rtl/filelist.py
|
||||||
@mkdir -p $(APU_BLOCK_FL_DIR)
|
@mkdir -p $(APU_BLOCK_FL_DIR)
|
||||||
python3 $(APU_BLOCK_EXPORTER) --output-dir $(APU_BLOCK_FL_DIR) \
|
python3 $(APU_BLOCK_EXPORTER) --output-dir $(APU_BLOCK_FL_DIR) \
|
||||||
$(foreach define,$(DEF_LIST),--define $(define))
|
$(foreach define,$(DEF_LIST),--define $(define))
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ endif
|
|||||||
# active DEF_LIST but without the SoC generated-include chain, so the block
|
# active DEF_LIST but without the SoC generated-include chain, so the block
|
||||||
# flow does not depend on the HP/pin-map generation stamps.
|
# flow does not depend on the HP/pin-map generation stamps.
|
||||||
$(GA2D_BLOCK_FL_DIR)/.stamp: $(RTL_PATH)/script/generate_filelist.py \
|
$(GA2D_BLOCK_FL_DIR)/.stamp: $(RTL_PATH)/script/generate_filelist.py \
|
||||||
$(RTL_PATH)/script/filelist.py \
|
$(ROOT_PATH)/scripts/rtl/filelist.py \
|
||||||
$(sort $(wildcard $(ROOT_PATH)/rtl/filelist/pdk_*.fl) \
|
$(sort $(wildcard $(ROOT_PATH)/rtl/filelist/pdk_*.fl) \
|
||||||
$(wildcard $(RTL_PATH)/filelist/*.fl) $(RTL_PATH)/lint.msg)
|
$(wildcard $(RTL_PATH)/filelist/*.fl) $(RTL_PATH)/lint.msg)
|
||||||
$(FLOW_PYTHON) $(RTL_PATH)/script/generate_filelist.py \
|
$(FLOW_PYTHON) $(RTL_PATH)/script/generate_filelist.py \
|
||||||
@@ -57,11 +57,11 @@ $(GA2D_BLOCK_FL_DIR)/.stamp: $(RTL_PATH)/script/generate_filelist.py \
|
|||||||
@touch $@
|
@touch $@
|
||||||
|
|
||||||
$(GA2D_BLOCK_FLIST): $(GA2D_BLOCK_FL_DIR)/.stamp $(MEMORY_MAP_STAMP)
|
$(GA2D_BLOCK_FLIST): $(GA2D_BLOCK_FL_DIR)/.stamp $(MEMORY_MAP_STAMP)
|
||||||
@python3 $(RTL_PATH)/script/comb.py \
|
@python3 $(ROOT_PATH)/scripts/rtl/comb.py \
|
||||||
-f $(GA2D_BLOCK_FL_DIR)/def.fl \
|
-f $(GA2D_BLOCK_FL_DIR)/def.fl \
|
||||||
-f $(MEMORY_MAP_FILELIST) \
|
-f $(MEMORY_MAP_FILELIST) \
|
||||||
-f $(GA2D_BLOCK_FL_DIR)/ga2d_block.fl --output $@
|
-f $(GA2D_BLOCK_FL_DIR)/ga2d_block.fl --output $@
|
||||||
@python3 $(RTL_PATH)/script/filelist_deps.py -f $@ \
|
@python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py -f $@ \
|
||||||
--extra $(MEMORY_MAP_FILELIST) \
|
--extra $(MEMORY_MAP_FILELIST) \
|
||||||
--target $(GA2D_BLOCK_NETLIST) --output $(GA2D_BLOCK_DEPFILE)
|
--target $(GA2D_BLOCK_NETLIST) --output $(GA2D_BLOCK_DEPFILE)
|
||||||
|
|
||||||
|
|||||||
@@ -19,18 +19,19 @@
|
|||||||
|
|
||||||
# Directories
|
# Directories
|
||||||
# directory of the path to the last called Makefile (this one)
|
# directory of the path to the last called Makefile (this one)
|
||||||
YOSYS_DIR := $(realpath $(dir $(realpath $(lastword $(MAKEFILE_LIST)))))
|
YOSYS_DIR := $(realpath $(dir $(realpath $(lastword $(MAKEFILE_LIST)))))
|
||||||
YOSYS_BUILD := $(SYN_BUILD_ROOT)
|
YOSYS_BUILD := $(SYN_BUILD_ROOT)
|
||||||
YOSYS_OUT := $(YOSYS_BUILD)/out
|
YOSYS_OUT := $(YOSYS_BUILD)/out
|
||||||
YOSYS_TMP := $(YOSYS_BUILD)/tmp
|
YOSYS_TMP := $(YOSYS_BUILD)/tmp
|
||||||
YOSYS_RPT := $(YOSYS_BUILD)/rpt
|
YOSYS_RPT := $(YOSYS_BUILD)/rpt
|
||||||
YOSYS_TIMEOUT ?= 10800
|
YOSYS_TIMEOUT ?= 10800
|
||||||
|
|
||||||
include $(YOSYS_DIR)/synth_config.mk
|
include $(YOSYS_DIR)/synth_config.mk
|
||||||
|
|
||||||
ifeq ($(origin YOSYS_TARGET_PERIOD_PS),undefined)
|
ifeq ($(origin YOSYS_TARGET_PERIOD_PS),undefined)
|
||||||
YOSYS_TARGET_PERIOD_PS := $(shell python3 $(ROOT_PATH)/scripts/yosys_period.py \
|
YOSYS_TARGET_PERIOD_PS := $(shell python3 $(ROOT_PATH)/scripts/yosys_period.py \
|
||||||
--domains $(ROOT_PATH)/rtl/mini/integration/clock_reset_domains.json)
|
--domains $(RTL_PATH)/integration/clock_reset_domains.json \
|
||||||
|
--domain $(if $(filter TINY,$(SOC)),system,hp))
|
||||||
ifeq ($(strip $(YOSYS_TARGET_PERIOD_PS)),)
|
ifeq ($(strip $(YOSYS_TARGET_PERIOD_PS)),)
|
||||||
$(error Failed to derive YOSYS_TARGET_PERIOD_PS from the clock/reset inventory)
|
$(error Failed to derive YOSYS_TARGET_PERIOD_PS from the clock/reset inventory)
|
||||||
endif
|
endif
|
||||||
@@ -49,8 +50,8 @@ YOSYS_SCRIPTS := $(wildcard $(YOSYS_DIR)/script/*) $(YOSYS_DIR)/synth_config.mk
|
|||||||
-include $(YOSYS_DEPFILE)
|
-include $(YOSYS_DEPFILE)
|
||||||
|
|
||||||
$(SV_FLIST): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP)
|
$(SV_FLIST): $(MPW_VARIANT_STAMP) $(FILELIST_STAMP)
|
||||||
@python3 $(RTL_PATH)/script/comb.py $(RTL_FLIST) --output $(SV_FLIST)
|
@python3 $(ROOT_PATH)/scripts/rtl/comb.py $(RTL_FLIST) --output $(SV_FLIST)
|
||||||
@python3 $(RTL_PATH)/script/filelist_deps.py $(RTL_FLIST) --target $(NETLIST) \
|
@python3 $(ROOT_PATH)/scripts/rtl/filelist_deps.py $(RTL_FLIST) --target $(NETLIST) \
|
||||||
--output $(YOSYS_DEPFILE)
|
--output $(YOSYS_DEPFILE)
|
||||||
|
|
||||||
gen_synth_filelist: $(SV_FLIST)
|
gen_synth_filelist: $(SV_FLIST)
|
||||||
@@ -79,4 +80,4 @@ $(NETLIST_DEBUG): $(NETLIST)
|
|||||||
synth_clean:
|
synth_clean:
|
||||||
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(YOSYS_BUILD)
|
python3 $(ROOT_PATH)/scripts/clean.py --root $(ROOT_PATH) --path $(YOSYS_BUILD)
|
||||||
|
|
||||||
.PHONY: gen_synth_filelist synth_clean synth
|
.PHONY: gen_synth_filelist synth_clean synth
|
||||||
@@ -12,6 +12,15 @@ These edits do not advance `mini.json.source_revision` or relabel an existing
|
|||||||
PDF. Review and commit the implementation snapshot before producing a matching
|
PDF. Review and commit the implementation snapshot before producing a matching
|
||||||
new PDF; the source-drift check remains mandatory.
|
new PDF; the source-drift check remains mandatory.
|
||||||
|
|
||||||
|
## Tiny Gen1 datasheet
|
||||||
|
|
||||||
|
The independent [Tiny Gen1 publication](datasheets/tiny/README.md) uses the same
|
||||||
|
visual language with Tiny-only configuration, register/IRQ/pad extraction,
|
||||||
|
fourteen IP chapters and its own source/structure/evidence checks. Run
|
||||||
|
`python3 publications/build_tiny_datasheet.py setup`, then `build` or `check`.
|
||||||
|
Its output and `latest-tiny` record are separate; the existing Mini entrypoint,
|
||||||
|
templates, configuration, review and delivered PDF are unchanged.
|
||||||
|
|
||||||
## Mini datasheet
|
## Mini datasheet
|
||||||
|
|
||||||
The prototype chip brand is **Rill(清溪)**. Its bilingual label replaces the
|
The prototype chip brand is **Rill(清溪)**. Its bilingual label replaces the
|
||||||
|
|||||||
@@ -146,9 +146,9 @@ def collect_data(config: dict[str, Any], *, check_snapshot: bool = True) -> dict
|
|||||||
product = profiles["profile"]
|
product = profiles["profile"]
|
||||||
if product.get("MINI_MODE") != "PRODUCT" or product.get("HAVE_HP") != "YES":
|
if product.get("MINI_MODE") != "PRODUCT" or product.get("HAVE_HP") != "YES":
|
||||||
raise ValueError("datasheet requires the reviewed dual-hart PRODUCT profile")
|
raise ValueError("datasheet requires the reviewed dual-hart PRODUCT profile")
|
||||||
address = load_generator("datasheet_memory", "rtl/mini/address_map/generate_memory_map.py")
|
address = load_generator("datasheet_memory", "scripts/rtl/generate_memory_map.py")
|
||||||
topology = load_generator("datasheet_topology", "rtl/mini/integration/generate_soc_topology.py")
|
topology = load_generator("datasheet_topology", "rtl/mini/integration/generate_soc_topology.py")
|
||||||
pinmap = load_generator("datasheet_pins", "rtl/mini/pin_map/generate_pin_map.py")
|
pinmap = load_generator("datasheet_pins", "scripts/rtl/generate_pin_map.py")
|
||||||
reset, regions, _ = address.read_map(ROOT / MAP, int(product["SRAM_SIZE_KIB"]))
|
reset, regions, _ = address.read_map(ROOT / MAP, int(product["SRAM_SIZE_KIB"]))
|
||||||
_, _, _, gpio, _, _, irqs, policies = topology.read_topology(ROOT / TOPOLOGY, ROOT / MAP)
|
_, _, _, gpio, _, _, irqs, policies = topology.read_topology(ROOT / TOPOLOGY, ROOT / MAP)
|
||||||
pads, _ = pinmap.read_map(ROOT / PINS)
|
pads, _ = pinmap.read_map(ROOT / PINS)
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Prepare, build and verify the independent Tiny Gen1 publication."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from publications.tiny_reference import BOOK, MANAGED, collect, read, validate_configuration # noqa: E402
|
||||||
|
from publications.package_reference import directory_hashes, package_records, validate_package_closure # noqa: E402
|
||||||
|
from publications.structure_reference import validate_structure # noqa: E402
|
||||||
|
from publications.report_changes import repository_footer_pages # noqa: E402
|
||||||
|
from scripts.dependency_lock import load_lock # noqa: E402
|
||||||
|
from scripts.install_toolchain import safe_extract # noqa: E402
|
||||||
|
from scripts.setup_helpers import atomic_write, download_file, ensure_git_repo, sha256 # noqa: E402
|
||||||
|
|
||||||
|
CACHE = ROOT / ".cache/retrosoc/publications"
|
||||||
|
LATEST = CACHE / "latest-tiny"
|
||||||
|
|
||||||
|
|
||||||
|
def write(path: Path, value) -> None:
|
||||||
|
atomic_write(path, json.dumps(value, indent=2, ensure_ascii=False) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def assets(lock: dict) -> dict:
|
||||||
|
for name in MANAGED:
|
||||||
|
spec = lock["sources"][name]
|
||||||
|
location = ROOT / spec["destination"]
|
||||||
|
if not (location / ".git").exists():
|
||||||
|
raise ValueError(f"Tiny managed input absent: {name}; run setup")
|
||||||
|
revision = subprocess.check_output(["git", "-C", str(location), "rev-parse", "HEAD"], text=True).strip()
|
||||||
|
dirty = subprocess.check_output(["git", "-C", str(location), "status", "--porcelain"], text=True).strip()
|
||||||
|
if revision != spec["revision"] or dirty:
|
||||||
|
raise ValueError(f"Tiny managed input differs from its lock: {name}")
|
||||||
|
media = ROOT / lock["sources"]["publication_media"]["destination"]
|
||||||
|
manifest = read(media / "assets.json")
|
||||||
|
for path, expected in manifest["files"].items():
|
||||||
|
if sha256(media / path) != expected:
|
||||||
|
raise ValueError(f"Tiny media digest mismatch: {path}")
|
||||||
|
validate_package_closure(ROOT, package_records(lock))
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
|
||||||
|
def prepare(lock: dict) -> None:
|
||||||
|
for name in sorted(MANAGED):
|
||||||
|
spec = lock["sources"][name]
|
||||||
|
ensure_git_repo(spec["url"], ROOT / spec["destination"], spec["revision"], update=False)
|
||||||
|
for package in package_records(lock):
|
||||||
|
archive = CACHE / "downloads" / f"{package['name']}-{package['version']}.tar.gz"
|
||||||
|
download_file(package["url"], archive, package["sha256"], update=False)
|
||||||
|
destination = ROOT / package["destination"]
|
||||||
|
manifest = destination / ".manifest.json"
|
||||||
|
if manifest.is_file() and read(manifest) == {"archive": package["sha256"], "files": directory_hashes(destination)}:
|
||||||
|
continue
|
||||||
|
if destination.exists():
|
||||||
|
raise ValueError("Existing package cache differs from its lock; review the cache before replacing it")
|
||||||
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.TemporaryDirectory(dir=destination.parent) as temp:
|
||||||
|
unpacked = Path(temp) / "content"
|
||||||
|
unpacked.mkdir()
|
||||||
|
safe_extract(archive, unpacked)
|
||||||
|
write(unpacked / ".manifest.json", {"archive": package["sha256"], "files": directory_hashes(unpacked)})
|
||||||
|
unpacked.rename(destination)
|
||||||
|
assets(lock)
|
||||||
|
|
||||||
|
|
||||||
|
def inputs(data: dict) -> dict[str, str]:
|
||||||
|
paths = set(data["source_paths"])
|
||||||
|
paths.update(p.relative_to(ROOT).as_posix() for p in (ROOT / BOOK).rglob("*") if p.suffix in {".typ", ".json"})
|
||||||
|
paths.update(p.relative_to(ROOT).as_posix() for p in (ROOT / "publications/examples/tiny").glob("*.c"))
|
||||||
|
paths.update({"publications/build_tiny_datasheet.py", "publications/check_tiny_examples.py", "tests/test_publication_tiny.py", "publications/tiny_reference.py", "publications/register_reference.py",
|
||||||
|
"publications/waveform_reference.py", "publications/structure_reference.py", "publications/package_reference.py",
|
||||||
|
"publications/report_changes.py", "publications/page_reference.py", "scripts/rtl/generate_memory_map.py",
|
||||||
|
"scripts/rtl/generate_pin_map.py", "scripts/check_clock_reset_domains.py"})
|
||||||
|
return {path: sha256(ROOT / path) for path in sorted(paths)}
|
||||||
|
|
||||||
|
|
||||||
|
def typst_binary(explicit: str | None, lock: dict) -> str:
|
||||||
|
executable = explicit or os.environ.get("TYPST") or shutil.which("typst")
|
||||||
|
if not executable:
|
||||||
|
raise ValueError("Typst not found; use --typst")
|
||||||
|
version = subprocess.check_output([executable, "--version"], text=True).strip()
|
||||||
|
if not version.startswith("typst " + lock["publication_tools"]["typst"]["version"] + " "):
|
||||||
|
raise ValueError("Tiny requires the locked Typst version")
|
||||||
|
return executable
|
||||||
|
|
||||||
|
|
||||||
|
def validate_output(out: Path, config: dict) -> Path:
|
||||||
|
out = out.resolve()
|
||||||
|
if not out.is_relative_to((ROOT / "build").resolve()):
|
||||||
|
raise ValueError("Tiny outputs must be inside build/")
|
||||||
|
previous = out / "manifest.json"
|
||||||
|
if previous.is_file() and read(previous).get("document", {}).get("document_id") != config["document_id"]:
|
||||||
|
raise ValueError("Tiny output cannot overwrite another product's build record")
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def validate_rendered_registers(data: dict, items: list) -> int:
|
||||||
|
expected = {(family, r["key"]) for family, value in data["registers"].items() for r in value["registers"]}
|
||||||
|
actual = [(r["family"], r["key"]) for r in items if isinstance(r, dict) and r.get("kind") == "tiny-register"]
|
||||||
|
if len(actual) != len(set(actual)) or set(actual) != expected:
|
||||||
|
raise ValueError("Tiny PDF omitted or duplicated a register definition")
|
||||||
|
return len(actual)
|
||||||
|
|
||||||
|
|
||||||
|
def build(config: dict, executable: str, out: Path | None = None) -> Path:
|
||||||
|
lock = load_lock()
|
||||||
|
media = assets(lock)
|
||||||
|
data = collect(config)
|
||||||
|
hashes = inputs(data)
|
||||||
|
signature = hashlib.sha256(json.dumps(hashes, sort_keys=True).encode()).hexdigest()[:12]
|
||||||
|
out = out or ROOT / "build" / f"datasheet-tiny-{datetime.now(timezone.utc):%Y-%m-%d-%H-%M}-{signature}"
|
||||||
|
out = validate_output(out, config)
|
||||||
|
out.mkdir(parents=True, exist_ok=True)
|
||||||
|
write(out / "data.json", data)
|
||||||
|
pdf = out / config["filename"]
|
||||||
|
epoch = int(datetime.fromisoformat(config["date"]).replace(tzinfo=timezone.utc).timestamp())
|
||||||
|
options = ["--root", str(ROOT), "--font-path", "publications/media/fonts", "--ignore-system-fonts",
|
||||||
|
"--package-path", str(CACHE / "packages"), "--package-cache-path", str(CACHE / "packages"),
|
||||||
|
"--input", "data=/" + (out / "data.json").relative_to(ROOT).as_posix(), "--creation-timestamp", str(epoch)]
|
||||||
|
result = subprocess.run([executable, "compile", config["entrypoint"], str(pdf), *options], cwd=ROOT, text=True, capture_output=True, encoding="utf-8")
|
||||||
|
atomic_write(out / "typst.log", result.stdout + result.stderr)
|
||||||
|
if result.returncode or "warning:" in result.stderr:
|
||||||
|
raise ValueError("Tiny Typst build failed or warned:\n" + result.stderr)
|
||||||
|
query = '(headings:query(heading).map(h=>(level:h.level,outlined:h.outlined,title:h.body,label:repr(h.at("label",default:none)),page:h.location().page())),items:query(metadata).map(it=>it.value))'
|
||||||
|
result = subprocess.run([executable, "eval", query, "--in", config["entrypoint"], *options], cwd=ROOT, text=True, capture_output=True, encoding="utf-8")
|
||||||
|
if result.returncode or "warning:" in result.stderr:
|
||||||
|
raise ValueError("Tiny layout query failed:\n" + result.stderr)
|
||||||
|
layout = json.loads(result.stdout)
|
||||||
|
validate_structure(read(ROOT / BOOK / "structure-contract.json"), layout["headings"], data["chapter_index"])
|
||||||
|
validate_rendered_registers(data, layout["items"])
|
||||||
|
write(out / "document-structure.json", layout["headings"])
|
||||||
|
write(out / "layout.json", layout["items"])
|
||||||
|
write(out / "ip-pages.json", [r for r in layout["items"] if isinstance(r, dict) and r.get("kind") in {"ip-start", "ip-end"}])
|
||||||
|
write(out / "waveform-audit.json", data["waveform_audit"])
|
||||||
|
diagrams = [r for r in layout["items"] if isinstance(r, dict) and r.get("kind") == "tiny-diagram"]
|
||||||
|
write(out / "diagram-inventory.json", diagrams)
|
||||||
|
manifest = dict(document=config, inputs=hashes, assets=media, pdf_sha256=sha256(pdf),
|
||||||
|
publication_checkout=dict(head=subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip(), path=str(ROOT)),
|
||||||
|
typst=lock["publication_tools"]["typst"]["version"], media_revision=lock["sources"]["publication_media"]["revision"],
|
||||||
|
packages={p["identity"]: p["sha256"] for p in package_records(lock)},
|
||||||
|
document_structure_sha256=sha256(out / "document-structure.json"), diagram_inventory_sha256=sha256(out / "diagram-inventory.json"))
|
||||||
|
write(out / "manifest.json", manifest)
|
||||||
|
atomic_write(LATEST, str(out) + "\n")
|
||||||
|
print(f"Built {pdf}")
|
||||||
|
return pdf
|
||||||
|
|
||||||
|
|
||||||
|
def check_pdf(pdf: Path, data: dict) -> dict:
|
||||||
|
from pypdf import PdfReader
|
||||||
|
import pdfplumber
|
||||||
|
manifest = read(pdf.parent / "manifest.json")
|
||||||
|
if sha256(pdf) != manifest["pdf_sha256"]:
|
||||||
|
raise ValueError("Tiny PDF digest does not match manifest")
|
||||||
|
for path, digest in manifest["inputs"].items():
|
||||||
|
if sha256(ROOT / path) != digest:
|
||||||
|
raise ValueError(f"Tiny input changed after build: {path}")
|
||||||
|
reader = PdfReader(pdf)
|
||||||
|
if reader.metadata.title != data["document"]["title"] or data["document"]["document_id"] not in reader.metadata.keywords:
|
||||||
|
raise ValueError("Tiny PDF identity mismatch")
|
||||||
|
if repository_footer_pages(reader) != list(range(1, len(reader.pages) + 1)):
|
||||||
|
raise ValueError("Tiny repository footer missing")
|
||||||
|
layout = read(pdf.parent / "ip-pages.json")
|
||||||
|
register_count = validate_rendered_registers(data, read(pdf.parent / "layout.json"))
|
||||||
|
starts = [r["id"] for r in layout if r["kind"] == "ip-start"]
|
||||||
|
ends = [r["id"] for r in layout if r["kind"] == "ip-end"]
|
||||||
|
if starts != ends or starts != [r["id"] for r in data["chapter_index"]]:
|
||||||
|
raise ValueError("Tiny IP chapter coverage missing")
|
||||||
|
if not reader.outline:
|
||||||
|
raise ValueError("Tiny bookmarks missing")
|
||||||
|
internal = 0
|
||||||
|
for page in reader.pages:
|
||||||
|
if not (page.extract_text() or "").strip():
|
||||||
|
raise ValueError("Tiny PDF contains a blank/raster-only page")
|
||||||
|
for annotation in page.get("/Annots", []):
|
||||||
|
item = annotation.get_object()
|
||||||
|
destination = item.get("/Dest", item.get("/A", {}).get("/D"))
|
||||||
|
if destination is not None:
|
||||||
|
if isinstance(destination, list) and (not destination or destination[0].get_object().get("/Type") != "/Page"):
|
||||||
|
raise ValueError("Tiny link has no page destination")
|
||||||
|
internal += 1
|
||||||
|
for ref in page["/Resources"].get("/Font", {}).get_object().values():
|
||||||
|
font = ref.get_object()
|
||||||
|
child = font.get("/DescendantFonts", [font])[0].get_object()
|
||||||
|
descriptor = child.get("/FontDescriptor", {}).get_object()
|
||||||
|
if not any(key in descriptor for key in ("/FontFile", "/FontFile2", "/FontFile3")):
|
||||||
|
raise ValueError("Tiny font is not embedded")
|
||||||
|
with pdfplumber.open(pdf) as rendered:
|
||||||
|
for page in rendered.pages:
|
||||||
|
for char in page.chars:
|
||||||
|
if char["text"].strip() and char["size"] < 8.45:
|
||||||
|
raise ValueError(f"Tiny undersized text on page {page.page_number}: {char['text']}")
|
||||||
|
footer = page.crop((0, page.height - 45, page.width, page.height)).extract_text() or ""
|
||||||
|
if f"{page.page_number} / {len(rendered.pages)}" not in footer:
|
||||||
|
raise ValueError("Tiny printed footer numbering mismatch")
|
||||||
|
result = dict(pages=len(reader.pages), internal_links=internal, ip_chapters=len(starts),
|
||||||
|
pdf_sha256=manifest["pdf_sha256"], fonts_embedded=True, register_definitions=register_count, source_inputs=len(manifest["inputs"]))
|
||||||
|
write(pdf.parent / "check-report.json", result)
|
||||||
|
print("Tiny PDF checks passed:", result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("command", choices=["setup", "build", "check"])
|
||||||
|
parser.add_argument("--config", type=Path, default=ROOT / BOOK / "tiny.json")
|
||||||
|
parser.add_argument("--typst")
|
||||||
|
parser.add_argument("--output-dir", type=Path)
|
||||||
|
parser.add_argument("--pdf", type=Path)
|
||||||
|
parser.add_argument("--source-only", action="store_true")
|
||||||
|
args = parser.parse_args()
|
||||||
|
try:
|
||||||
|
config, lock = read(args.config), load_lock()
|
||||||
|
validate_configuration(config)
|
||||||
|
if args.command == "setup":
|
||||||
|
prepare(lock)
|
||||||
|
elif args.command == "build":
|
||||||
|
build(config, typst_binary(args.typst, lock), args.output_dir)
|
||||||
|
else:
|
||||||
|
data = collect(config)
|
||||||
|
if not args.source_only:
|
||||||
|
assets(lock)
|
||||||
|
check_pdf(args.pdf or Path(LATEST.read_text().strip()) / config["filename"], data)
|
||||||
|
print("Tiny source checks passed: configuration, 14 IPs, registers, maps, interrupts, pads and waveforms")
|
||||||
|
except (OSError, ValueError, KeyError, subprocess.CalledProcessError) as error:
|
||||||
|
parser.exit(1, f"tiny datasheet: {error}\n")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -20,7 +20,7 @@ def check_examples(compiler: str) -> int:
|
|||||||
subprocess.run(
|
subprocess.run(
|
||||||
[
|
[
|
||||||
sys.executable,
|
sys.executable,
|
||||||
str(ROOT / "rtl/mini/address_map/generate_memory_map.py"),
|
str(ROOT / "scripts/rtl/generate_memory_map.py"),
|
||||||
"--map",
|
"--map",
|
||||||
str(ROOT / "rtl/mini/address_map/memory_map.json"),
|
str(ROOT / "rtl/mini/address_map/memory_map.json"),
|
||||||
"--output-dir",
|
"--output-dir",
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
"""Compile Tiny publication examples with Tiny-generated SDK metadata only."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
from scripts.generate_tiny import generate_bindings, read_topology # noqa: E402
|
||||||
|
from scripts.rtl.generate_memory_map import generate # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def check_examples(compiler: str) -> int:
|
||||||
|
with tempfile.TemporaryDirectory(prefix="retrosoc-tiny-examples-") as location:
|
||||||
|
generated = Path(location)
|
||||||
|
generate_bindings(read_topology(ROOT / "rtl/tiny/integration/soc_topology.json", ROOT / "rtl/tiny/address_map/memory_map.json"), generated)
|
||||||
|
generate(ROOT / "rtl/tiny/address_map/memory_map.json", generated / "memory_map", "YES", 128)
|
||||||
|
includes = [ROOT / "crt/include", generated / "include", generated / "memory_map/include"]
|
||||||
|
includes += [ROOT / f"rtl/managed/clusterip/{name}/sw/include" for name in ("archinfo", "pwm", "rtc", "wdg")]
|
||||||
|
command = [compiler, "-std=c11", "-ffreestanding", "-Wall", "-Wextra", "-Werror", "-fsyntax-only", "-DRS_SOC_TINY=1"]
|
||||||
|
for path in includes:
|
||||||
|
command += ["-I", str(path)]
|
||||||
|
examples = sorted((ROOT / "publications/examples/tiny").glob("*.c"))
|
||||||
|
if len(examples) != 13:
|
||||||
|
raise ValueError("Tiny example inventory changed; review the complete set")
|
||||||
|
for path in examples:
|
||||||
|
subprocess.run([*command, str(path)], check=True, cwd=ROOT)
|
||||||
|
print(f"Tiny SDK examples: {len(examples)} syntax checks passed")
|
||||||
|
return len(examples)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--cc", default="gcc")
|
||||||
|
check_examples(parser.parse_args().cc)
|
||||||
@@ -8451,13 +8451,13 @@
|
|||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
"rtl/mini/top/retrosoc.sv",
|
"rtl/mini/top/retrosoc.sv",
|
||||||
"rtl/mini/top/onchip_ram.sv"
|
"rtl/ip/memory/onchip_ram.sv"
|
||||||
],
|
],
|
||||||
"instance_source": {
|
"instance_source": {
|
||||||
"file": "rtl/mini/top/retrosoc.sv",
|
"file": "rtl/mini/top/retrosoc.sv",
|
||||||
"name": "u_onchip_ram",
|
"name": "u_onchip_ram",
|
||||||
"module": "onchip_ram",
|
"module": "onchip_ram",
|
||||||
"declaration_file": "rtl/mini/top/onchip_ram.sv"
|
"declaration_file": "rtl/ip/memory/onchip_ram.sv"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -8495,7 +8495,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
"rtl/mini/top/onchip_ram.sv"
|
"rtl/ip/memory/onchip_ram.sv"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -8562,11 +8562,11 @@
|
|||||||
"style": "direct",
|
"style": "direct",
|
||||||
"bindings": [
|
"bindings": [
|
||||||
{
|
{
|
||||||
"file": "rtl/mini/top/onchip_ram.sv",
|
"file": "rtl/ip/memory/onchip_ram.sv",
|
||||||
"text": ".data_i(s_mem_wdata[31:0])"
|
"text": ".data_i(s_mem_wdata[31:0])"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "rtl/mini/top/onchip_ram.sv",
|
"file": "rtl/ip/memory/onchip_ram.sv",
|
||||||
"text": ".data_i(s_mem_wdata[63:32])"
|
"text": ".data_i(s_mem_wdata[63:32])"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -10929,13 +10929,13 @@
|
|||||||
],
|
],
|
||||||
"sources": [
|
"sources": [
|
||||||
"rtl/mini/top/retrosoc.sv",
|
"rtl/mini/top/retrosoc.sv",
|
||||||
"rtl/mini/top/onchip_ram.sv"
|
"rtl/ip/memory/onchip_ram.sv"
|
||||||
],
|
],
|
||||||
"instance_source": {
|
"instance_source": {
|
||||||
"file": "rtl/mini/top/retrosoc.sv",
|
"file": "rtl/mini/top/retrosoc.sv",
|
||||||
"name": "u_onchip_ram",
|
"name": "u_onchip_ram",
|
||||||
"module": "onchip_ram",
|
"module": "onchip_ram",
|
||||||
"declaration_file": "rtl/mini/top/onchip_ram.sv"
|
"declaration_file": "rtl/ip/memory/onchip_ram.sv"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1430,10 +1430,10 @@
|
|||||||
"sram": {
|
"sram": {
|
||||||
"document": "docs/ip/onchip-sram.md",
|
"document": "docs/ip/onchip-sram.md",
|
||||||
"defines": [
|
"defines": [
|
||||||
"rtl/mini/top/onchip_ram_define.svh"
|
"rtl/ip/memory/onchip_ram_define.svh"
|
||||||
],
|
],
|
||||||
"rtl": [
|
"rtl": [
|
||||||
"rtl/mini/top/onchip_ram_reg.sv"
|
"rtl/ip/memory/onchip_ram_reg.sv"
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Present": 1,
|
"Present": 1,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ the existing non-coherent buffer protocol. LP and HP configuration differences d
|
|||||||
two harts into a symmetric cache-coherent SMP platform.
|
two harts into a symmetric cache-coherent SMP platform.
|
||||||
#block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
#block(above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
||||||
#set text(size:9pt)
|
#set text(size:9pt)
|
||||||
#source("rtl/mini/top/mgmt_core_wrapper.sv",title:"LP instance") ·
|
#source("rtl/ip/core/mgmt_core_wrapper.sv",title:"LP instance") ·
|
||||||
#source("scripts/vexiiriscv/GenerateRetroSocHp.scala",title:"HP configuration/PMA") ·
|
#source("scripts/vexiiriscv/GenerateRetroSocHp.scala",title:"HP configuration/PMA") ·
|
||||||
#source("scripts/generate_vexiiriscv.py",title:"Artifact manifest") ·
|
#source("scripts/generate_vexiiriscv.py",title:"Artifact manifest") ·
|
||||||
#source("app/ports/linux/linux/retrosoc_hp.dts",title:"HP platform properties")
|
#source("app/ports/linux/linux/retrosoc_hp.dts",title:"HP platform properties")
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ The debug replacement sets the stack and calls main, without the generic PSRAM i
|
|||||||
relocation or BSS loop. The XPI flash-loader replacement sets the stack, clears BSS and calls
|
relocation or BSS loop. The XPI flash-loader replacement sets the stack, clears BSS and calls
|
||||||
main; if main returns it executes EBREAK and loops. Those compact entries are tied to their
|
main; if main returns it executes EBREAK and loops. Those compact entries are tied to their
|
||||||
application/linker assumptions and do not establish a universal alternative startup sequence.
|
application/linker assumptions and do not establish a universal alternative startup sequence.
|
||||||
#source-note("rtl/mini/mk/software.mk",title:"Generic CRT, conditional IRQ objects and application CRT replacement")
|
#source-note("rtl/mk/software.mk",title:"Generic CRT, conditional IRQ objects and application CRT replacement")
|
||||||
#source-note("crt/arch/riscv/startup.S",title:"Actual generic LP entry and PSRAM-ready loop")
|
#source-note("crt/arch/riscv/startup.S",title:"Actual generic LP entry and PSRAM-ready loop")
|
||||||
#source-note("app/apps/debug/startup.S",title:"Dedicated debug entry")
|
#source-note("app/apps/debug/startup.S",title:"Dedicated debug entry")
|
||||||
#source-note("app/apps/xpi_flash_loader/startup.S",title:"Dedicated SRAM flash-loader entry")
|
#source-note("app/apps/xpi_flash_loader/startup.S",title:"Dedicated SRAM flash-loader entry")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
"rtl/mini/top/apb4_periph.sv",
|
"rtl/mini/top/apb4_periph.sv",
|
||||||
"rtl/mini/top/apb4_system.sv",
|
"rtl/mini/top/apb4_system.sv",
|
||||||
"rtl/mini/top/memory_pad_mux.sv",
|
"rtl/mini/top/memory_pad_mux.sv",
|
||||||
"rtl/mini/top/mgmt_debug_wrapper.sv",
|
"rtl/ip/core/mgmt_debug_wrapper.sv",
|
||||||
"rtl/mini/top/retrosoc.sv",
|
"rtl/mini/top/retrosoc.sv",
|
||||||
"rtl/mini/top/soc_clock_reset_subsystem.sv",
|
"rtl/mini/top/soc_clock_reset_subsystem.sv",
|
||||||
"rtl/mini/top/soc_data_plane.sv"
|
"rtl/mini/top/soc_data_plane.sv"
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
"ips": [],
|
"ips": [],
|
||||||
"rotate": false,
|
"rotate": false,
|
||||||
"clock": {
|
"clock": {
|
||||||
"file": "rtl/mini/top/mgmt_debug_wrapper.sv",
|
"file": "rtl/ip/core/mgmt_debug_wrapper.sv",
|
||||||
"name": "u_jtag_rst_sync",
|
"name": "u_jtag_rst_sync",
|
||||||
"module": "rst_sync",
|
"module": "rst_sync",
|
||||||
"port": "clk_i",
|
"port": "clk_i",
|
||||||
|
|||||||
@@ -132,7 +132,7 @@
|
|||||||
"crt/include/retrosoc/hal/onchip_sram.h",
|
"crt/include/retrosoc/hal/onchip_sram.h",
|
||||||
"docs/ip/onchip-sram.md",
|
"docs/ip/onchip-sram.md",
|
||||||
"publications/examples/sram.c",
|
"publications/examples/sram.c",
|
||||||
"rtl/mini/top/onchip_ram_reg.sv"
|
"rtl/ip/memory/onchip_ram_reg.sv"
|
||||||
],
|
],
|
||||||
"tests": [
|
"tests": [
|
||||||
"tests/test_onchip_sram_register_parity.py"
|
"tests/test_onchip_sram_register_parity.py"
|
||||||
@@ -1065,7 +1065,7 @@
|
|||||||
"applies_to": "Reviewed generic LP CRT; dedicated debug and XPI-loader startups have separate initialization contracts.",
|
"applies_to": "Reviewed generic LP CRT; dedicated debug and XPI-loader startups have separate initialization contracts.",
|
||||||
"sources": [
|
"sources": [
|
||||||
"crt/arch/riscv/startup.S",
|
"crt/arch/riscv/startup.S",
|
||||||
"rtl/mini/mk/software.mk"
|
"rtl/mk/software.mk"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -3253,7 +3253,7 @@
|
|||||||
"rtl/managed/clusterip/common/rtl/utils/register.sv",
|
"rtl/managed/clusterip/common/rtl/utils/register.sv",
|
||||||
"rtl/mini/top/clock_config_controller.sv",
|
"rtl/mini/top/clock_config_controller.sv",
|
||||||
"rtl/mini/top/hp_lifecycle_controller.sv",
|
"rtl/mini/top/hp_lifecycle_controller.sv",
|
||||||
"rtl/mini/top/mgmt_core_wrapper.sv",
|
"rtl/ip/core/mgmt_core_wrapper.sv",
|
||||||
"rtl/mini/top/resource_controller.sv",
|
"rtl/mini/top/resource_controller.sv",
|
||||||
"rtl/mini/top/soc_clock_reset_subsystem.sv",
|
"rtl/mini/top/soc_clock_reset_subsystem.sv",
|
||||||
"scripts/generate_vexiiriscv.py",
|
"scripts/generate_vexiiriscv.py",
|
||||||
@@ -5065,7 +5065,7 @@
|
|||||||
"crt/src/service/test.c",
|
"crt/src/service/test.c",
|
||||||
"publications/datasheets/mini.json",
|
"publications/datasheets/mini.json",
|
||||||
"rtl/mini/integration/soc_topology.json",
|
"rtl/mini/integration/soc_topology.json",
|
||||||
"rtl/mini/mk/software.mk",
|
"rtl/mk/software.mk",
|
||||||
"scripts/build_hp_linux.py",
|
"scripts/build_hp_linux.py",
|
||||||
"scripts/regress.py",
|
"scripts/regress.py",
|
||||||
"app/ports/linux/init"
|
"app/ports/linux/init"
|
||||||
@@ -5073,17 +5073,17 @@
|
|||||||
"bindings": [
|
"bindings": [
|
||||||
{
|
{
|
||||||
"id": "csr-selection",
|
"id": "csr-selection",
|
||||||
"file": "rtl/mini/mk/software.mk",
|
"file": "rtl/mk/software.mk",
|
||||||
"text": "ifeq ($(HAVE_CSR),YES)\nDEF_VAL += -DCSR_ENABLE\nendif"
|
"text": "ifeq ($(HAVE_CSR),YES)\nDEF_VAL += -DCSR_ENABLE\nendif"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "irq-objects",
|
"id": "irq-objects",
|
||||||
"file": "rtl/mini/mk/software.mk",
|
"file": "rtl/mk/software.mk",
|
||||||
"text": "ifeq ($(HAVE_CSR),YES)\nCRT_SRCS += $(ROOT_PATH)/crt/arch/riscv/system_irq.S\nCRT_SRCS += $(ROOT_PATH)/crt/src/core/system_irq_handler.c\nCRT_SRCS += $(ROOT_PATH)/crt/src/core/irq.c"
|
"text": "ifeq ($(HAVE_CSR),YES)\nCRT_SRCS += $(ROOT_PATH)/crt/arch/riscv/system_irq.S\nCRT_SRCS += $(ROOT_PATH)/crt/src/core/system_irq_handler.c\nCRT_SRCS += $(ROOT_PATH)/crt/src/core/irq.c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "app-override",
|
"id": "app-override",
|
||||||
"file": "rtl/mini/mk/software.mk",
|
"file": "rtl/mk/software.mk",
|
||||||
"text": "ifneq ($(strip $(APP_CRT_SRCS)),)\nCRT_SRCS := $(APP_CRT_SRCS)\nendif"
|
"text": "ifneq ($(strip $(APP_CRT_SRCS)),)\nCRT_SRCS := $(APP_CRT_SRCS)\nendif"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -5953,8 +5953,8 @@
|
|||||||
"rtl/mini/top/fabric_monitor.sv",
|
"rtl/mini/top/fabric_monitor.sv",
|
||||||
"rtl/mini/top/hp_subsystem.sv",
|
"rtl/mini/top/hp_subsystem.sv",
|
||||||
"rtl/mini/top/memory_pad_mux.sv",
|
"rtl/mini/top/memory_pad_mux.sv",
|
||||||
"rtl/mini/top/mgmt_debug_wrapper.sv",
|
"rtl/ip/core/mgmt_debug_wrapper.sv",
|
||||||
"rtl/mini/top/onchip_ram.sv",
|
"rtl/ip/memory/onchip_ram.sv",
|
||||||
"rtl/mini/top/rcu.sv",
|
"rtl/mini/top/rcu.sv",
|
||||||
"rtl/mini/top/resource_controller.sv",
|
"rtl/mini/top/resource_controller.sv",
|
||||||
"rtl/mini/top/retrosoc.sv",
|
"rtl/mini/top/retrosoc.sv",
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Tiny Gen1 publication
|
||||||
|
|
||||||
|
This directory owns the English **retroSoC Tiny Gen1 / Rill (清溪)** datasheet,
|
||||||
|
its independent v0.1 DRAFT identity, 14 IP chapters and 39 frozen structural
|
||||||
|
entries. It is a publication adapter, not an RTL or SDK register generator.
|
||||||
|
|
||||||
|
The source of truth is `rtl/tiny`, `configs/ci/ihp130-tiny.mk`, the active shared
|
||||||
|
RTL/SDK and the locked Common/Hazard3/peripheral repositories. The reviewed
|
||||||
|
SHA belongs in [tiny.json](tiny.json). Address, IRQ and pad mappings are read
|
||||||
|
from Tiny's canonical inputs. The register profiles select only applicable
|
||||||
|
families; the owned SYSCTRL and ARCHINFO overrides are checked separately.
|
||||||
|
|
||||||
|
## Build and check
|
||||||
|
|
||||||
|
From the repository root, with the pinned publication Python dependencies and
|
||||||
|
Typst version available:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
python3 publications/build_tiny_datasheet.py setup
|
||||||
|
python3 publications/build_tiny_datasheet.py check --source-only
|
||||||
|
python3 publications/build_tiny_datasheet.py build --typst /path/to/locked/typst
|
||||||
|
python3 publications/build_tiny_datasheet.py check --pdf build/<tiny-variant>/retrosoc-tiny-gen1-datasheet.pdf
|
||||||
|
python3 publications/check_tiny_examples.py --cc gcc
|
||||||
|
python3 -m pytest -q tests/test_publication_tiny.py tests/test_publication_registers.py tests/test_publication_waveforms.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional `--config` selects a Tiny configuration file. Outputs belong to
|
||||||
|
`build/datasheet-tiny-<UTC-date-time>-<input-hash>/`. The only latest-build marker
|
||||||
|
written is `.cache/retrosoc/publications/latest-tiny`; Mini markers and products
|
||||||
|
are not replaced. Font/package caches are shared and hash-checked. Tiny setup
|
||||||
|
requires only the managed inputs it uses, not MPW, VexiiRiscv or accelerator data.
|
||||||
|
|
||||||
|
## Content and validation boundary
|
||||||
|
|
||||||
|
- [Layout supplement](style.md) defines the Tiny use of the approved Mini visual language.
|
||||||
|
- [Content review](content-review.md) records retained coverage and known gaps.
|
||||||
|
- [Chapter review](chapter-review.md) binds chapter/IP coverage to the delivered PDF.
|
||||||
|
- [Structure contract](structure-contract.json) freezes entry order and labels independently of page count.
|
||||||
|
- [Source contract](source-contract.json) binds numerical maps and critical behavior.
|
||||||
|
- [Evidence](evidence.json) distinguishes dated repository narratives from matching executed reports.
|
||||||
|
|
||||||
|
The broad validation report and PDF hash accompany each build. Verify links,
|
||||||
|
source/parameter consistency, character sizes, outlines, complete register/IP
|
||||||
|
coverage and representative color/grayscale rendering. Repeat the build offline
|
||||||
|
and compare PDF bytes. New test counts must come from the current run.
|
||||||
|
|
||||||
|
The 24 MHz configuration is not timing-qualified. Publication checks do not
|
||||||
|
establish simulation, physical, board or silicon qualification. Do not repair
|
||||||
|
RTL, relax checks, or change warning/metric baselines as a document workaround.
|
||||||
@@ -0,0 +1,591 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "xpi",
|
||||||
|
"family": "xpi",
|
||||||
|
"title": "XPI Universal Controller",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"FLASH",
|
||||||
|
"APB4_XPI",
|
||||||
|
"XPI"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"xpi"
|
||||||
|
],
|
||||||
|
"summary": "Four-chip-select SDR serial-memory controller; pin-level boot uses NOR on NSS0.",
|
||||||
|
"features": [
|
||||||
|
"Four chip selects; 16 programmable LUT sequences.",
|
||||||
|
"Single, dual and quad SDR phases; mapped reads plus indirect PIO/DMA and status polling.",
|
||||||
|
"64-word TX and RX staging FIFOs; device-specific commands and timing remain software responsibilities."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"The Flash alias at address zero and XPI aperture route to the same command engine. They are address windows, not two independent memories. The boot setup is the implemented NOR profile on NSS0; other chip selects are not qualified board configurations.",
|
||||||
|
"Memory reads and indirect operations share the controlled transaction engine. A write to a mapped Flash address is not a Flash program command. Execute erase/program service from SRAM with all users of the affected memory quiesced.",
|
||||||
|
"LUT phase widths, dummy cycles, clock divider and device/page bounds must match the selected NOR. Unsupported modes, FIFO starvation and device timeout require status inspection and bounded recovery. The generic controller does not imply DDR, HyperBus or arbitrary-device compatibility."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Identify the actual NOR and configure the supported slot, timing and LUT while idle.",
|
||||||
|
"Keep the executing service and its working state in SRAM before erase/program.",
|
||||||
|
"Configure source/destination ranges and the supported DMA request, then start and use a bounded wait.",
|
||||||
|
"Inspect engine/FIFO/device status, verify completion and contents as required before making an updated image executable."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB registers / LUT",
|
||||||
|
"Mapped read frontend",
|
||||||
|
"Transaction / SDR PHY",
|
||||||
|
"TX / RX FIFOs",
|
||||||
|
"NSS[3:0] / SCK / DQ[3:0]"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/xpi.md",
|
||||||
|
"rtl/ip/storage/xpi_reg.sv",
|
||||||
|
"rtl/ip/storage/xpi_pkg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sram",
|
||||||
|
"family": "sram",
|
||||||
|
"title": "On-Chip SRAM",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"SRAM",
|
||||||
|
"APB4_SRAM"
|
||||||
|
],
|
||||||
|
"irqs": [],
|
||||||
|
"summary": "128 KiB on-chip SRAM; AXI32 data and one APB discovery/counter window.",
|
||||||
|
"features": [
|
||||||
|
"32 banks of 4 KiB in the selected profile.",
|
||||||
|
"32-bit AXI data, one-bit ID; byte writes and supported memory bursts.",
|
||||||
|
"SRAM code, data, BSS and stack; reset does not erase memory."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"The Tiny top overrides the reusable SRAM to 128 KiB and 32-bit data. The native memory aperture is 0x30000000 through 0x3001FFFF. APB registers describe the integrated configuration and observations; they are not another storage aperture.",
|
||||||
|
"Technology macros are selected by the IHP130 profile. Logical bank capacity is not a claim of timing closure, yield or physical area. Preserve byte-lane and natural alignment rules, and keep transfers within the decoded aperture.",
|
||||||
|
"Reset clears control state, not SRAM contents. Startup copies initialized sections from Flash and clears BSS explicitly. Stack placement at the SRAM end does not establish a measured maximum stack demand."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Probe memory size, bank count and data width before using a linked image.",
|
||||||
|
"Compare ELF/MAP section sizes, buffers and stack margin against the actual 128 KiB region.",
|
||||||
|
"Use naturally aligned memory transfers and preserve ownership while DMA is active.",
|
||||||
|
"Read/reset counters according to their register semantics; reset is not data sanitization."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"AXI32 adapter",
|
||||||
|
"Bank / byte selection",
|
||||||
|
"32 x 4 KiB SRAM",
|
||||||
|
"APB discovery / counters"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/onchip-sram.md",
|
||||||
|
"rtl/ip/memory/onchip_ram_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gpio",
|
||||||
|
"family": "gpio",
|
||||||
|
"title": "General-Purpose Input/Output (GPIO)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_GPIO",
|
||||||
|
"APB4_GPIO_ADMIN"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"gpio"
|
||||||
|
],
|
||||||
|
"summary": "32 physical GPIOs with management and restricted-user register windows.",
|
||||||
|
"features": [
|
||||||
|
"Atomic set, clear and toggle outputs; mode, output-enable and open-drain control.",
|
||||||
|
"Per-pin interrupts and filtering; alternate functions from the Tiny pin map.",
|
||||||
|
"Both windows address the same pads, not two GPIO banks."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"GPIO administration controls pad mode, filtering, masks and locks. The restricted window exposes only permitted data/event operations. The user-access mask starts clear; grant only the pins required by the application.",
|
||||||
|
"Tiny retains only its implemented alternate functions. Unavailable alternatives drive neither peripheral requests nor pads. UART TX/RX and XPI use dedicated pads and are not inferred from a Mini pin assignment.",
|
||||||
|
"Before changing a driven alternate function, disable its producer, release output enable and use the controller guard/ownership sequence. Electrical drive settings require a reviewed pad/board specification; a register field is not a voltage rating."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Confirm the Tiny alternate-function map and external circuit.",
|
||||||
|
"Prepare output data before enabling the driver; set open-drain/pull behavior only as supported by the pad.",
|
||||||
|
"Clear observed event causes before enabling the intended interrupt mask.",
|
||||||
|
"Use atomic aliases for concurrent updates and apply irreversible locks only after validation."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB user / admin",
|
||||||
|
"Mode / mask / locks",
|
||||||
|
"Data / event / filter",
|
||||||
|
"Alternate-function mux",
|
||||||
|
"32 shared GPIO pads"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/gpio.md",
|
||||||
|
"rtl/ip/peripheral/gpio_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dma",
|
||||||
|
"family": "dma",
|
||||||
|
"title": "Direct Memory Access (DMA)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_DMA"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"dma"
|
||||||
|
],
|
||||||
|
"summary": "Four channel contexts share one AXI32 master; memory-mapped endpoints only.",
|
||||||
|
"features": [
|
||||||
|
"Four channels; direct transfers and 64-byte linked TCDs.",
|
||||||
|
"32-bit transfers, up to 16 beats, 32-word FIFO per channel.",
|
||||||
|
"Request mask 0x07F9; stream modes are disabled in Tiny."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"Channel conventions are 0 UART0, 1 I2C0, 2 I2C1 and 3 bulk/XPI. They are software allocation conventions for one shared engine. Reserve a channel and all buffers through completion or confirmed abort.",
|
||||||
|
"Only request 0 and requests 3 through 10 are enabled. UART1 has no DMA request binding. Request enum names for omitted peripherals in the shared SDK do not establish Tiny hardware support; invalid kinds/requests must be rejected.",
|
||||||
|
"The fabric admits only one read or write transaction globally. CPU and DMA contend for that admission; burst length times bus width is not sustained throughput. A software wait timeout does not prove the engine is idle.",
|
||||||
|
"TCDs remain 64 bytes and require 64-byte alignment. Stride fields and y_count do not advertise 2D transfers: current validation requires zero strides and at most one row. Do not modify a descriptor or data buffer while hardware may still consume it.",
|
||||||
|
"The CAPABILITY maximum-burst field currently casts the configured 16 beats to four bits and therefore reads zero. The implemented parameter and SDK limit remain 16. Treat this as a discovery-encoding limitation; do not infer a zero-length engine or a qualified performance result from that field."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Check channel < 4, MM-to-MM kind, 32-bit width, nonzero bounded length and an enabled request.",
|
||||||
|
"Prepare aligned descriptors/buffers and publish them with the required memory ordering.",
|
||||||
|
"Configure, start, then wait with a bounded software budget; distinguish submission from completion.",
|
||||||
|
"On failure capture error address/response and progress, request abort, and confirm terminal/drained state before reuse."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB / 4 channel state",
|
||||||
|
"TCD fetch / validation",
|
||||||
|
"4 x 32-word FIFOs",
|
||||||
|
"Shared AXI32 master",
|
||||||
|
"Memory / paced I/O windows"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/dma.md",
|
||||||
|
"rtl/ip/peripheral/dma_reg.sv",
|
||||||
|
"rtl/ip/peripheral/dma_pkg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timer",
|
||||||
|
"family": "timer",
|
||||||
|
"title": "General-Purpose Timers (TIM0, TIM1)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_TIM0",
|
||||||
|
"APB4_TIM1"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"timer0",
|
||||||
|
"timer1"
|
||||||
|
],
|
||||||
|
"summary": "Two 32-bit timer instances with prescalers and interrupt support.",
|
||||||
|
"features": [
|
||||||
|
"Free-running, periodic and one-shot modes.",
|
||||||
|
"16-bit prescalers and two compare channels per instance.",
|
||||||
|
"Debug-freeze integration; no additional advanced timer."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"TIM0 and TIM1 share one register definition but have separate base addresses and IRQ bits. Program the selected timer against the actual 24 MHz system clock, not the CLINT microsecond timebase.",
|
||||||
|
"A delay API has both a requested time interval and a separate software polling budget. A zero polling budget or timeout is not proof that a previously running timer stopped.",
|
||||||
|
"Use documented status/W1C handling and capture event causes before rearming. Debug halt may stop counting when configured; it does not prevent all register access or provide a system-wide freeze."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Stop the selected timer before changing prescale/reload/compare values.",
|
||||||
|
"Compute timing from its functional clock and check representable range.",
|
||||||
|
"Clear stale events, configure interrupt selection and start.",
|
||||||
|
"Acknowledge observed events, recheck pending state and verify stop on recovery."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB register bank",
|
||||||
|
"Prescale / counter",
|
||||||
|
"Compare / reload",
|
||||||
|
"Event / debug-freeze"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/timer.md",
|
||||||
|
"rtl/ip/peripheral/timer_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pwm",
|
||||||
|
"family": "pwm",
|
||||||
|
"title": "Pulse Width Modulation (PWM)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_PWM"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"pwm"
|
||||||
|
],
|
||||||
|
"summary": "Two internal timers and four waveform output channels with capture/fault control.",
|
||||||
|
"features": [
|
||||||
|
"Four PWM outputs, two capture inputs and fault/synchronization routes.",
|
||||||
|
"Shadow configuration and documented update boundaries.",
|
||||||
|
"Functional clock parameter is 24 MHz in Tiny."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"PWM outputs and capture/fault inputs are selected through Tiny GPIO alternate functions. The reusable block has timer/operator state, not an unrestricted programmable I/O processor.",
|
||||||
|
"Shadow values become active at the configured update event. Writing a duty field alone does not guarantee an immediate glitch-free transition. Establish safe output levels before enabling external power circuitry.",
|
||||||
|
"Capture entries and overflow status require coherent reads and explicit acknowledgement. Fault handling and debug-halt behavior must be checked with the actual pin route; no electrical frequency limit is qualified here."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Probe ABI and use the actual system clock in timing calculations.",
|
||||||
|
"Keep outputs inactive while programming timer/operator/channel state.",
|
||||||
|
"Latch shadow values at the selected boundary, then enable output routes.",
|
||||||
|
"On fault, preserve status and disable/recover outputs before clearing the cause."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB / shadow registers",
|
||||||
|
"Timer / operator update",
|
||||||
|
"4 waveform channels",
|
||||||
|
"Capture / fault / IRQ"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/doc/datasheet.md",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_reg.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_pkg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rtc",
|
||||||
|
"family": "rtc",
|
||||||
|
"title": "Real-Time Clock (RTC)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_RTC"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"rtc"
|
||||||
|
],
|
||||||
|
"summary": "64-bit seconds, 1/256-second subsecond time, alarms and wake status.",
|
||||||
|
"features": [
|
||||||
|
"Two alarms, periodic wake and calibration controls.",
|
||||||
|
"Atomic time snapshot and command/response interface.",
|
||||||
|
"Tiny clocks the RTC from the system clock, not a separate sleep crystal."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"The managed RTC engine is configured for 24 MHz and is wired to the same system clock as the MCU. Its separate logical clock/reset ports do not establish an independent low-power timing source.",
|
||||||
|
"Time reads use the documented snapshot/command sequence. Poll command completion before assuming a write has taken effect. Alarm interrupt and wake signals are separate; SYSCTRL exposes both live and sticky wake observations.",
|
||||||
|
"Battery-backed retention, oscillator accuracy, calendar drift and deep-sleep operation remain unspecified. A digital counter resolution is not a measured time accuracy."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Probe the engine and configure it against the actual clock.",
|
||||||
|
"Set time through the bounded command API and verify completion.",
|
||||||
|
"Configure alarm/periodic selections and clear prior causes before enabling events.",
|
||||||
|
"Read snapshots consistently and capture wake/IRQ causes before acknowledgement."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB command bridge",
|
||||||
|
"Time / calibration",
|
||||||
|
"Alarm / periodic logic",
|
||||||
|
"Snapshot / IRQ / wake"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"rtl/managed/clusterip/rtc/doc/datasheet.md",
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_reg.sv",
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_pkg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "wdg",
|
||||||
|
"family": "wdg",
|
||||||
|
"title": "Watchdog (WDG)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_WDG"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"watchdog_early_warning"
|
||||||
|
],
|
||||||
|
"summary": "Window watchdog with early warning and whole-system reset request.",
|
||||||
|
"features": [
|
||||||
|
"24 MHz system-clock reference in Tiny.",
|
||||||
|
"Two-key service, window checking and retained reset-cause observation.",
|
||||||
|
"Eight watchdog-clock reset-request cycles in this integration."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"Configure timing and window policy while inactive, then start and service using the prescribed key sequence. An early or malformed service can request reset just as an expired timeout can.",
|
||||||
|
"Tiny routes the watchdog request into system reset generation. Its own reset/retention boundary is distinct from the reset it requests; the acceptance application checks retained reset_count on reboot.",
|
||||||
|
"Because the watchdog uses the same system clock, it does not guarantee recovery from a failed system clock or provide independent deep-sleep supervision. The current integration is not a safety certificate."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Probe status and inspect retained reset cause before starting a new diagnostic campaign.",
|
||||||
|
"Program prescaler, timeout, window and warning policy while inactive.",
|
||||||
|
"Start once and service inside the allowed window with bounded checks.",
|
||||||
|
"After reset distinguish watchdog reboot from fresh power-on and retain diagnostic evidence."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB command bridge",
|
||||||
|
"Prescale / window counter",
|
||||||
|
"Service-key checker",
|
||||||
|
"Warning / retained cause / reset"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"rtl/managed/clusterip/wdg/doc/datasheet.md",
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_reg.sv",
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_pkg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "uart0",
|
||||||
|
"family": "uart",
|
||||||
|
"title": "Management UART (UART0)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_UART0"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"uart0"
|
||||||
|
],
|
||||||
|
"summary": "Primary console UART with 64-byte transmit and receive FIFOs.",
|
||||||
|
"features": [
|
||||||
|
"Fractional baud generation, 5-8 data bits, parity, break and diagnostics.",
|
||||||
|
"Dedicated TX/RX pads; UART0 DMA requests available.",
|
||||||
|
"RTS/CTS require the selected GPIO alternate routes."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"The shared rs_uart_* convenience HAL targets UART0. It is the normal Tiny diagnostic console. Configure baud against 24 MHz and the permitted fractional divider range; sampling margin remains a board/peer requirement.",
|
||||||
|
"FIFO acceptance is not completed wire transmission. A UART write timeout can leave a transmitted/queued prefix. Preserve protocol framing and inspect RX error flags instead of blindly retrying an entire message.",
|
||||||
|
"The polling budget is consumed while the FIFO blocks progress and is shared across the call. It is not a duration in milliseconds and cannot bound a stalled bus access."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Configure baud, framing and optional flow-control routing before enabling the peer.",
|
||||||
|
"Clear stale error/event state and use bounded FIFO operations.",
|
||||||
|
"Reserve channel 0 when using UART0 DMA, and follow buffer lifetime rules.",
|
||||||
|
"Wait for the required queue/serializer completion boundary before changing framing or disabling clocks."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB / baud registers",
|
||||||
|
"64-byte TX FIFO / serializer",
|
||||||
|
"RX sampler / 64-byte FIFO",
|
||||||
|
"IRQ / error / DMA requests"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/uart.md",
|
||||||
|
"rtl/ip/serial/uart_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "uart1",
|
||||||
|
"family": "uart",
|
||||||
|
"title": "Second UART (UART1)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_UART1"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"uart1"
|
||||||
|
],
|
||||||
|
"summary": "Second UART instance with dedicated TX/RX and its own interrupt.",
|
||||||
|
"features": [
|
||||||
|
"Same UART v3 register ABI and 64-byte FIFOs.",
|
||||||
|
"PIO/IRQ integration; no Tiny UART1 DMA request route.",
|
||||||
|
"CTS input is tied permissive; no dedicated RTS/CTS pad pair."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"UART1 has its own base and core IRQ bit 26. The generic UART0 helper must not be mistaken for an instance-selecting UART1 API. Use the correct base/register contract or the documented Tiny loopback acceptance source.",
|
||||||
|
"The instance reuses the UART FIFO, divider, framing and error semantics. Tiny ties its CTS input low and leaves the DMA stall outputs unconnected to requests; shared register fields do not create the omitted routing.",
|
||||||
|
"Loopback acceptance is a source-level test facility. It does not qualify an external baud rate, cable, voltage domain or Linux driver."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Select the UART1 base and confirm framing/divider settings before use.",
|
||||||
|
"Use PIO or core IRQ bit 26; do not allocate a UART0 DMA selector for UART1.",
|
||||||
|
"Handle RX error entries and use bounded TX/RX waits.",
|
||||||
|
"Confirm serializer idle before changing framing, then clear only captured interrupt causes."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"UART1 APB bank",
|
||||||
|
"64-byte TX / RX FIFOs",
|
||||||
|
"Framing / fractional divider",
|
||||||
|
"Dedicated TX/RX / IRQ26"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/uart.md",
|
||||||
|
"rtl/ip/serial/uart_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "i2c",
|
||||||
|
"family": "i2c",
|
||||||
|
"title": "Inter-Integrated Circuit (I2C0, I2C1)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_I2C0",
|
||||||
|
"APB4_I2C1"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"i2c0",
|
||||||
|
"i2c1"
|
||||||
|
],
|
||||||
|
"summary": "Two controller-mode I2C instances with command/receive queues.",
|
||||||
|
"features": [
|
||||||
|
"7-bit/10-bit addressing, repeated START and clock stretching.",
|
||||||
|
"Sixteen-entry command and RX queues; arbitration and recovery reporting.",
|
||||||
|
"DMA pacing uses channels 1 and 2 by software convention."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"I2C0 and I2C1 share the register layout but have separate base addresses, GPIO alternate routes and IRQ bits. They are controllers, not target/slave peripherals. Board pull-ups and compatible voltage levels are required.",
|
||||||
|
"The queued command grammar and FIFO limits define how START, data, repeated START and STOP are submitted. A NACK is an observed protocol outcome; do not treat command FIFO acceptance as a successful bus transaction.",
|
||||||
|
"Use bounded transaction and recovery waits. Arbitration loss, stuck-low lines or clock stretching require inspection and a bus-safe recovery sequence rather than concurrent driving or automatic infinite retry."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Select the bus, verify its GPIO route and calculate timing from 24 MHz.",
|
||||||
|
"Ensure the bus is released, clear stale events and configure supported address mode.",
|
||||||
|
"Submit the documented command sequence and inspect terminal/NACK/arbitration status.",
|
||||||
|
"Recover and release lines before retrying; preserve timeout/error context."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB / timing / status",
|
||||||
|
"16-entry command queue",
|
||||||
|
"START / shift / STOP engine",
|
||||||
|
"RX queue / IRQ / DMA pacing"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/i2c.md",
|
||||||
|
"rtl/ip/serial/i2c_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sysctrl",
|
||||||
|
"family": "sysctrl",
|
||||||
|
"title": "System Controller (SYSCTRL)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_SYSCTRL"
|
||||||
|
],
|
||||||
|
"irqs": [],
|
||||||
|
"summary": "Tiny-owned control and first-fault/acceptance register bank.",
|
||||||
|
"features": [
|
||||||
|
"Sticky first-fault address, master, response and saturating event count.",
|
||||||
|
"CPU/DMA wait-counter snapshot; live/sticky RTC wake status.",
|
||||||
|
"First full-word valid TEST_STATUS result remains sticky until reset."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"Only the decoded Tiny offsets below are implemented. Compatibility identity reads do not create selectable cores, a PLL or an application processor. Other offsets fail; writes to read-only locations, unaligned accesses and partial writes return APB error.",
|
||||||
|
"FAULT_STATUS captures the first pending event. Clearing pending permits a subsequent fault record; FAULT_COUNT counts events independently and saturates. Capture address/master/response before clearing.",
|
||||||
|
"PERF_CTRL controls wait counters and snapshots. Read low/high words from the captured snapshot, not from independently changing live values. A wait count is not end-to-end throughput.",
|
||||||
|
"TEST_STATUS accepts a full-word write with VALID set only while the prior result is invalid. Reserved input bits are masked; the terminal result is not UART text. A later result cannot overwrite the first valid one before reset."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Use the Tiny identity before choosing control operations.",
|
||||||
|
"Read and preserve pending fault status/address/master/detail before W1C acknowledgement.",
|
||||||
|
"Enable/capture CPU and DMA wait counters through PERF_CTRL and then read both snapshot words.",
|
||||||
|
"For automated acceptance write VALID, PASS and result code once; an earlier failure cannot be replaced by a later pass."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB alignment / strobe gate",
|
||||||
|
"Tiny offset decoder",
|
||||||
|
"First-fault / snapshots",
|
||||||
|
"Sticky TEST_STATUS / RTC wake"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"rtl/tiny/top/tiny_sysctrl.sv",
|
||||||
|
"docs/ip/tiny-soc.md"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "clint",
|
||||||
|
"family": "clint",
|
||||||
|
"title": "Local Interrupt Controller (CLINT)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_CLINT"
|
||||||
|
],
|
||||||
|
"irqs": [
|
||||||
|
"clint_software",
|
||||||
|
"clint_timer"
|
||||||
|
],
|
||||||
|
"summary": "Machine software interrupt and 64-bit timer for hart slot zero.",
|
||||||
|
"features": [
|
||||||
|
"Core interrupt bits 0 and 1 are local software/timer causes.",
|
||||||
|
"1 MHz timebase derived from the 24 MHz system clock.",
|
||||||
|
"64-bit time and compare are accessed as RV32 words."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"Tiny has one management hart and uses hart slot zero. CLINT is distinct from the external interrupt vector and does not provide a PLIC.",
|
||||||
|
"Read time using a coherent high/low/high retry sequence. Update compare with the documented safe word order so an intermediate value does not trigger an unintended interrupt.",
|
||||||
|
"The 1 MHz timebase supports timing measurements/deadlines, while peripheral dividers still use their own 24 MHz engine clock. A software busy-loop count is not this timebase."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Use slot zero and the CLINT base from the generated Tiny map.",
|
||||||
|
"Read coherent time through the SDK helper.",
|
||||||
|
"Mask/prepare the timer cause and update compare in safe word order.",
|
||||||
|
"Handle and rearm the appropriate local cause; do not subtract two to form an external IRQ ordinal."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB word accesses",
|
||||||
|
"64-bit time counter",
|
||||||
|
"Hart0 compare / MSIP",
|
||||||
|
"Local software / timer IRQ"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"docs/ip/clint.md",
|
||||||
|
"rtl/ip/peripheral/clint_reg.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "archinfo",
|
||||||
|
"family": "archinfo",
|
||||||
|
"title": "Architecture Information (ARCHINFO)",
|
||||||
|
"depth": 3,
|
||||||
|
"regions": [
|
||||||
|
"APB4_ARCHINFO"
|
||||||
|
],
|
||||||
|
"irqs": [],
|
||||||
|
"summary": "Read-only Tiny identity and build/configuration discovery.",
|
||||||
|
"features": [
|
||||||
|
"Tiny SOC_ID 0x54494E59; 128 KiB SRAM and 24 MHz reference clock.",
|
||||||
|
"Topology 0x20200001, features 0x00007FFE and technology 0x02010082.",
|
||||||
|
"No provisioned unique device ID is advertised."
|
||||||
|
],
|
||||||
|
"paragraphs": [
|
||||||
|
"The owned Tiny adapter replaces the managed block's fixed SoC identity while retaining its register ABI. BUILD_ID, CONFIG_ID and BUILD_STATUS are supplied by generated build metadata; they must not be published as default zero values for a built image.",
|
||||||
|
"Read and validate Tiny identity before interpreting product-specific capabilities or selecting a firmware layout. The build/profile identity is distinct from a silicon lot/serial number.",
|
||||||
|
"Device-ID inputs are tied invalid/read-disabled. Their register offsets do not establish a provisioned unique identity, secure key storage or production traceability."
|
||||||
|
],
|
||||||
|
"steps": [
|
||||||
|
"Read discovery through the SDK and validate the build identity.",
|
||||||
|
"Confirm Tiny SOC_ID, one-hart topology, 128 KiB memory and active capabilities.",
|
||||||
|
"Reject mismatched images/configurations before hardware initialization.",
|
||||||
|
"Read device ID only when qualified and enabled; this integration does not supply it."
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"APB read-only ABI",
|
||||||
|
"Build / config metadata",
|
||||||
|
"Tiny identity override",
|
||||||
|
"Capability / device-ID gate"
|
||||||
|
],
|
||||||
|
"sources": [
|
||||||
|
"rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"rtl/managed/clusterip/archinfo/doc/datasheet.md",
|
||||||
|
"rtl/managed/clusterip/archinfo/rtl/archinfo_reg.sv",
|
||||||
|
"rtl/tiny/top/tiny_archinfo.sv"
|
||||||
|
],
|
||||||
|
"evidence": "Source reviewed; current-commit matching raw qualification report not supplied."
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "xpi",
|
||||||
|
"family": "xpi",
|
||||||
|
"title": "XPI Universal Controller",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sram",
|
||||||
|
"family": "sram",
|
||||||
|
"title": "On-Chip SRAM",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gpio",
|
||||||
|
"family": "gpio",
|
||||||
|
"title": "General-Purpose Input/Output (GPIO)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dma",
|
||||||
|
"family": "dma",
|
||||||
|
"title": "Direct Memory Access (DMA)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timer",
|
||||||
|
"family": "timer",
|
||||||
|
"title": "General-Purpose Timers (TIM0, TIM1)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pwm",
|
||||||
|
"family": "pwm",
|
||||||
|
"title": "Pulse Width Modulation (PWM)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rtc",
|
||||||
|
"family": "rtc",
|
||||||
|
"title": "Real-Time Clock (RTC)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "wdg",
|
||||||
|
"family": "wdg",
|
||||||
|
"title": "Watchdog (WDG)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "uart0",
|
||||||
|
"family": "uart",
|
||||||
|
"title": "Management UART (UART0)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "uart1",
|
||||||
|
"family": "uart",
|
||||||
|
"title": "Second UART (UART1)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "i2c",
|
||||||
|
"family": "i2c",
|
||||||
|
"title": "Inter-Integrated Circuit (I2C0, I2C1)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sysctrl",
|
||||||
|
"family": "sysctrl",
|
||||||
|
"title": "System Controller (SYSCTRL)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "clint",
|
||||||
|
"family": "clint",
|
||||||
|
"title": "Local Interrupt Controller (CLINT)",
|
||||||
|
"depth": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "archinfo",
|
||||||
|
"family": "archinfo",
|
||||||
|
"title": "Architecture Information (ARCHINFO)",
|
||||||
|
"depth": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Tiny Gen1 v0.1 DRAFT chapter review
|
||||||
|
|
||||||
|
Source: `9c706ba0ff1da1028c299cfff573010a86dfd7ce`. PDF: **204 pages**, SHA-256 `44d2b6140395ab3adea6e1ca64ce72ba65fa4727213bfca136a6e47010958d4a`.
|
||||||
|
|
||||||
|
All **39 frozen entries / 14 IP chapters** are reviewed below. Register, address, IRQ and pad values follow the Tiny implementation. The dated evidence narrative is not promoted to a current-commit report. See [content review](content-review.md), [layout rules](style.md) and the delivered validation record.
|
||||||
|
|
||||||
|
| # | Chapter / page | Coverage and implementation | Basis | Recommendation |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 1 | Product Brief / 1 | Covered. Rill Tiny Gen1 identity and source-backed overview; prototype badge and timing qualification kept separate. | [tiny.json](../../../publications/datasheets/tiny/tiny.json), [ihp130-tiny.mk](../../../configs/ci/ihp130-tiny.mk) | Keep the two-column cover, logo reservation and partial Functional mark; do not promote it from host test counts. |
|
||||||
|
| 2 | Features / 1 | Covered. Rill Tiny Gen1 identity and source-backed overview; prototype badge and timing qualification kept separate. | [tiny.json](../../../publications/datasheets/tiny/tiny.json), [ihp130-tiny.mk](../../../configs/ci/ihp130-tiny.mk) | Keep the two-column cover, logo reservation and partial Functional mark; do not promote it from host test counts. |
|
||||||
|
| 3 | Configuration and Scope / 2 | Covered. IHP130, 128 KiB, 24 MHz target, four DMA channels; hardware RV32IMC versus default RV32IM firmware. | [ihp130-tiny.mk](../../../configs/ci/ihp130-tiny.mk), [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv) | Use the committed profile and identity; keep deferred interfaces and physical claims excluded. |
|
||||||
|
| 4 | Reading and Evidence Conventions / 2 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
|
||||||
|
| 5 | System Architecture / 17 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
|
||||||
|
| 6 | Processor and Integration / 17 | Covered. One RV32IMC MCU hart, A disabled, direct AHB-Lite/AXI32 adapter and debug reset boundary. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [mgmt_core_wrapper.sv](../../../rtl/ip/core/mgmt_core_wrapper.sv) | Retain actual wrapper parameters; do not infer firmware ISA flags from the hardware C extension. |
|
||||||
|
| 7 | Clock, Reset and Operating State / 18 | Covered. System and asynchronous JTAG domains; RTC/WDG share the system clock. | [clock_reset_domains.json](../../../rtl/tiny/integration/clock_reset_domains.json), [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv) | Obtain CDC/RDC and clock/power evidence separately; do not claim independent sleep timing. |
|
||||||
|
| 8 | Bus Transactions, Arbitration and Errors / 19 | Covered. One globally active transaction, round-robin acceptance, saved ownership, bounded legal bursts and error/drain behavior. | [tiny_axi4_fabric.sv](../../../rtl/tiny/top/tiny_axi4_fabric.sv), [axi4_error_slave.sv](../../../rtl/ip/interconnect/axi4_error_slave.sv) | Keep local IP capability separate from end-to-end fabric admission; no unlimited liveness guarantee. |
|
||||||
|
| 9 | Memory Map and Access Conventions / 21 | Covered. Twenty canonical address windows, including alias versus physical-capacity distinctions. | [memory_map.json](../../../rtl/tiny/address_map/memory_map.json) | Reconcile every source-map change; do not use unmapped holes as scratch memory. |
|
||||||
|
| 10 | Interrupt and DMA Request Routing / 22 | Covered. Fourteen allocated core causes, thirty external positions, enabled DMA request map and channel conventions. | [soc_topology.json](../../../rtl/tiny/integration/soc_topology.json), [dma_math.c](../../../crt/src/hal/dma_math.c) | Keep core input bits, external ordinals, CSR positions and DMA selectors distinct. |
|
||||||
|
| 11 | Peripherals / 23 | Covered. Fourteen IP chapters and 337 source-extracted register definitions, with instance-specific routing. | [catalog.json](../../../publications/datasheets/tiny/catalog.json), [tiny_reference.py](../../../publications/tiny_reference.py) | Update source inventory, frozen register keys and rendered-coverage checks together after reviewed ABI changes. |
|
||||||
|
| 12 | XPI Universal Controller / 24-40 | Covered. Four-chip-select SDR serial-memory controller; pin-level boot uses NOR on NSS0. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [xpi.md](../../../docs/ip/xpi.md), [xpi_reg.sv](../../../rtl/ip/storage/xpi_reg.sv) | Inspect engine/FIFO/device status, verify completion and contents as required before making an updated image executable. |
|
||||||
|
| 13 | On-Chip SRAM / 41-46 | Covered. 128 KiB on-chip SRAM; AXI32 data and one APB discovery/counter window. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [onchip-sram.md](../../../docs/ip/onchip-sram.md), [onchip_ram_reg.sv](../../../rtl/ip/memory/onchip_ram_reg.sv) | Keep Tiny fabric restrictions despite local WRAP/FIXED flags; reset does not initialize SRAM. |
|
||||||
|
| 14 | General-Purpose Input/Output (GPIO) / 47-62 | Covered. 32 physical GPIOs with management and restricted-user register windows. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [gpio.md](../../../docs/ip/gpio.md), [gpio_reg.sv](../../../rtl/ip/peripheral/gpio_reg.sv) | Use atomic aliases for concurrent updates and apply irreversible locks only after validation. |
|
||||||
|
| 15 | Direct Memory Access (DMA) / 63-76 | Covered. Four channel contexts share one AXI32 master; memory-mapped endpoints only. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [dma.md](../../../docs/ip/dma.md), [dma_reg.sv](../../../rtl/ip/peripheral/dma_reg.sv) | Retain the explicit zero-readback burst-field limitation and 16-beat implementation limit; a future ABI/RTL fix is separate work. Confirm drain before reusing buffers. |
|
||||||
|
| 16 | General-Purpose Timers (TIM0, TIM1) / 77-83 | Covered. Two 32-bit timer instances with prescalers and interrupt support. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [timer.md](../../../docs/ip/timer.md), [timer_reg.sv](../../../rtl/ip/peripheral/timer_reg.sv) | Acknowledge observed events, recheck pending state and verify stop on recovery. |
|
||||||
|
| 17 | Pulse Width Modulation (PWM) / 84-107 | Covered. Two internal timers and four waveform output channels with capture/fault control. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [datasheet.md](../../../rtl/managed/clusterip/pwm/doc/datasheet.md), [pwm_reg.sv](../../../rtl/managed/clusterip/pwm/rtl/pwm_reg.sv) | On fault, preserve status and disable/recover outputs before clearing the cause. |
|
||||||
|
| 18 | Real-Time Clock (RTC) / 108-120 | Covered. 64-bit seconds, 1/256-second subsecond time, alarms and wake status. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [datasheet.md](../../../rtl/managed/clusterip/rtc/doc/datasheet.md), [rtc_reg.sv](../../../rtl/managed/clusterip/rtc/rtl/rtc_reg.sv) | Read snapshots consistently and capture wake/IRQ causes before acknowledgement. |
|
||||||
|
| 19 | Watchdog (WDG) / 121-129 | Covered. Window watchdog with early warning and whole-system reset request. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [datasheet.md](../../../rtl/managed/clusterip/wdg/doc/datasheet.md), [wdg_reg.sv](../../../rtl/managed/clusterip/wdg/rtl/wdg_reg.sv) | After reset distinguish watchdog reboot from fresh power-on and retain diagnostic evidence. |
|
||||||
|
| 20 | Management UART (UART0) / 130-140 | Covered. Primary console UART with 64-byte transmit and receive FIFOs. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [uart.md](../../../docs/ip/uart.md), [uart_reg.sv](../../../rtl/ip/serial/uart_reg.sv) | Wait for the required queue/serializer completion boundary before changing framing or disabling clocks. |
|
||||||
|
| 21 | Second UART (UART1) / 141-142 | Covered. Second UART instance with dedicated TX/RX and its own interrupt. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [uart.md](../../../docs/ip/uart.md), [uart_reg.sv](../../../rtl/ip/serial/uart_reg.sv) | Confirm serializer idle before changing framing, then clear only captured interrupt causes. |
|
||||||
|
| 22 | Inter-Integrated Circuit (I2C0, I2C1) / 143-154 | Covered. Two controller-mode I2C instances with command/receive queues. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [i2c.md](../../../docs/ip/i2c.md), [i2c_reg.sv](../../../rtl/ip/serial/i2c_reg.sv) | Recover and release lines before retrying; preserve timeout/error context. |
|
||||||
|
| 23 | System Controller (SYSCTRL) / 155-162 | Covered. Tiny-owned control and first-fault/acceptance register bank. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [tiny_sysctrl.sv](../../../rtl/tiny/top/tiny_sysctrl.sv), [tiny-soc.md](../../../docs/ip/tiny-soc.md) | For automated acceptance write VALID, PASS and result code once; an earlier failure cannot be replaced by a later pass. |
|
||||||
|
| 24 | Local Interrupt Controller (CLINT) / 163-166 | Covered. Machine software interrupt and 64-bit timer for hart slot zero. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [clint.md](../../../docs/ip/clint.md), [clint_reg.sv](../../../rtl/ip/peripheral/clint_reg.sv) | Handle and rearm the appropriate local cause; do not subtract two to form an external IRQ ordinal. |
|
||||||
|
| 25 | Architecture Information (ARCHINFO) / 167-175 | Covered. Read-only Tiny identity and build/configuration discovery. | [retrosoc_tiny.sv](../../../rtl/tiny/top/retrosoc_tiny.sv), [datasheet.md](../../../rtl/managed/clusterip/archinfo/doc/datasheet.md), [archinfo_reg.sv](../../../rtl/managed/clusterip/archinfo/rtl/archinfo_reg.sv) | Read device ID only when qualified and enabled; this integration does not supply it. |
|
||||||
|
| 26 | Pins and Board Connections / 176 | Covered. Fifty-two logical signal pads and all thirty-two GPIO alternate-function rows. | [pin_map.json](../../../rtl/tiny/pin_map/pin_map.json), [soc_topology.json](../../../rtl/tiny/integration/soc_topology.json) | Supply package/board mapping and electrical constraints before treating these as physical pins. |
|
||||||
|
| 27 | Electrical, Package and Reliability Boundaries / 179 | Covered; physical evidence needed. Explicit missing-evidence table; no fabricated limits or package identifiers. | [tiny-soc-verification.md](../../../docs/ip/tiny-soc-verification.md), [evidence.json](../../../publications/datasheets/tiny/evidence.json) | Attach conditions, units, Min/Typ/Max meaning and matching characterization before publishing numerical ratings. |
|
||||||
|
| 28 | Software / 180 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
|
||||||
|
| 29 | Reset, Boot and Linker Layout / 180 | Covered. Tiny startup excludes PSRAM setup and relocates initialized code/data to SRAM with BSS/stack initialization. | [startup.S](../../../crt/arch/riscv/startup.S), [ld2_all_sram.lds](../../../crt/linker/ld2_all_sram.lds) | Use actual ELF/MAP and measured/worst-case stack demand; placement is not a memory-use measurement. |
|
||||||
|
| 30 | SDK, API Completion and Polling / 181 | Covered. Public SDK namespace and Tiny composition, operation-specific completion and bounded polling semantics. | [dma.c](../../../crt/src/hal/dma.c), [sysctrl.c](../../../crt/src/hal/sysctrl.c), [check_tiny_examples.py](../../../publications/check_tiny_examples.py) | Confirm each API completion state and distinguish timeout from idle or rollback. |
|
||||||
|
| 31 | DMA Descriptors and Buffer Lifetime / 182 | Covered. Seventeen C descriptor members, 64-byte layout/alignment, control positions, bounded HAL chain walking and buffer lifetime. | [dma.h](../../../crt/include/retrosoc/hal/dma.h), [dma_math.c](../../../crt/src/hal/dma_math.c), [dma.c](../../../crt/src/hal/dma.c) | Keep one-descriptor HAL submission/result writeback distinct from an autonomous queue; retain zero-burst default and unsupported-field qualifications. |
|
||||||
|
| 32 | Acceptance and Diagnostics / 184 | Covered. Source-bound acceptance stages, sticky terminal result and retained watchdog-reset early-pass branch. | [tiny.c](../../../app/apps/ci_smoke/tiny.c), [mcu_acceptance.S](../../../app/apps/ci_smoke/mcu_acceptance.S) | Control initial reset_count and preserve full simulator verdict; a warm reboot alone does not replay every test. |
|
||||||
|
| 33 | Implementation and Evidence / 185 | Covered. Independent Tiny setup/build/check flow; no Mini/HP/accelerator prerequisite. | [build_tiny_datasheet.py](../../../publications/build_tiny_datasheet.py), [generate_tiny.py](../../../scripts/generate_tiny.py), [tiny-soc-verification.md](../../../docs/ip/tiny-soc-verification.md) | Record actual tool/profile/stage outcomes and preserve missing-input failures. |
|
||||||
|
| 34 | Reproducible Development Flow / 185 | Covered. Independent Tiny setup/build/check flow; no Mini/HP/accelerator prerequisite. | [build_tiny_datasheet.py](../../../publications/build_tiny_datasheet.py), [generate_tiny.py](../../../scripts/generate_tiny.py), [tiny-soc-verification.md](../../../docs/ip/tiny-soc-verification.md) | Record actual tool/profile/stage outcomes and preserve missing-input failures. |
|
||||||
|
| 35 | Verification Status and Qualification Gaps / 186 | Covered. Dated repository narratives retained; slow-corner setup WNS -455.18 ns is not 24 MHz closure. | [tiny-soc-verification.md](../../../docs/ip/tiny-soc-verification.md) | Obtain exact-commit raw reports; keep failed, skipped and partial campaigns distinct. |
|
||||||
|
| 36 | Appendix: Global Register Index / 187 | Covered. Instance-qualified global lookup for every extracted register group and repeated index. | [register-profiles.json](../../../publications/datasheets/tiny/register-profiles.json), [register-contract.json](../../../publications/datasheets/tiny/register-contract.json) | Check unique expanded addresses and canonical register links after any map/group change. |
|
||||||
|
| 37 | Document Control / 203 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
|
||||||
|
| 38 | Revision History / 203 | Covered. Product-specific organization, navigation or revision control; no additional hardware capability. | [datasheet.typ](../../../publications/datasheets/tiny/datasheet.typ), [structure-contract.json](../../../publications/datasheets/tiny/structure-contract.json) | Retain the declared scope, complete source links and independent Tiny identity when updating this section. |
|
||||||
|
| 39 | Sources, License and Contact / 204 | Covered. Complete source identity, dependency/font/package hashes, document ID and author/contact retained. | [tiny.json](../../../publications/datasheets/tiny/tiny.json), [dependencies.lock.json](../../../dependencies/dependencies.lock.json) | Ship the matching manifest and raw validation report; document dates are not measurement dates. |
|
||||||
|
|
||||||
|
Every row is source/document review. Matching current physical/board/silicon qualification remains unavailable; host publication tests do not change that state.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Tiny Gen1 v0.1 DRAFT content review
|
||||||
|
|
||||||
|
The publication describes the independent IHP130 Tiny profile: one RV32IMC
|
||||||
|
Hazard3 with A disabled, RV32IM default firmware, 128 KiB SRAM, 24 MHz target,
|
||||||
|
four DMA channels and AXI32/APB4. Rill (清溪) is the selected prototype brand.
|
||||||
|
The font/palette/grid are inherited as design rules; product facts are not
|
||||||
|
inherited from Mini's descriptions or qualification records.
|
||||||
|
|
||||||
|
| Area | Review and publication decision | Follow-up boundary |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| CPU and startup | Publish the actual C/M extensions, disabled A, CSR-enabled firmware and Tiny guard around PSRAM setup. | Keep compiler target separate from implemented ISA; a reset vector is not a completed boot. |
|
||||||
|
| Fabric | Document one globally active CPU/DMA read or write, saved ownership, naturally aligned bounded bursts and terminal response/drain. | Do not claim arbitrary AXI4 features, independent read/write concurrency or unrestricted liveness. |
|
||||||
|
| Address/IRQ/pads | Derive all 20 regions, 14 allocated core causes and 52 logical signal pads; retain 32 GPIO alternate entries. | Logical pads are not package pin numbers or a board schematic. |
|
||||||
|
| IP/register scope | Fourteen chapters, thirteen unique register families; Tiny SYSCTRL is decoded independently and ARCHINFO overrides the SoC identity. | Reject missing/extra decode and defaults belonging to other products. |
|
||||||
|
| DMA | Four channels, request mask 0x07F9, no stream modes, 64-byte descriptors and source-derived field layout. | **Discovery discrepancy:** the four-bit maximum-burst field reads zero after truncating 16. Publish that value and the actual 16-beat limit separately; propose a future ABI/RTL review, do not change RTL here. |
|
||||||
|
| SRAM | 32 x 4 KiB banks, four data bytes, no reset erase. | Local FIXED/WRAP capability bits do not override the narrower Tiny fabric subset. |
|
||||||
|
| UART/I2C/timers | Reused family ABI with actual instances, routing and 24 MHz engine clock. UART1 is PIO/IRQ without a Tiny DMA route. | Generic UART HAL example is UART0; peripheral command acceptance is not necessarily completed I/O. |
|
||||||
|
| Supervision | RTC and watchdog share the system clock; WDG requests system reset and retains its own reset observation. | No independent sleep clock, battery-backed retention or clock-failure watchdog guarantee. |
|
||||||
|
| Acceptance | The application source performs CPU, fault, DMA, timer, RTC and I/O checks, then watchdog reboot. | A nonzero pre-existing watchdog reset count takes an early pass branch; controlled initial state is required. |
|
||||||
|
| Verification | Retain the dated repository narrative, including slow-corner setup WNS -455.18 ns at a 41.666666667 ns constraint. | Raw matching artifacts are not present in this publication workspace; no current-commit qualification is promoted. |
|
||||||
|
| Electrical/package/power | Preserve explicit missing-evidence tables and required measurement context. | No numerical production limits, die area, thermal rating or silicon claim is invented. |
|
||||||
|
|
||||||
|
The existing Tiny verification document reports an originally uncommitted run
|
||||||
|
and a later path-migration follow-up. Neither automatically qualifies the source
|
||||||
|
SHA selected for this document. The final publication validation report records
|
||||||
|
only checks executed for this build; hardware campaign repair is separate work.
|
||||||
|
|
||||||
|
The [chapter review](chapter-review.md) covers all frozen entries and gives
|
||||||
|
source/evidence/maintenance advice. Full registers and software guidance are
|
||||||
|
retained even when a board or physical result is unavailable. No Mini document,
|
||||||
|
version, review or PDF is updated by this addition.
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
#import "style.typ": *
|
||||||
|
#import "diagrams.typ": architecture, flow, tcd-diagram
|
||||||
|
#import "waveforms.typ": timing
|
||||||
|
#import "ip-reference.typ": reference
|
||||||
|
#show: template
|
||||||
|
|
||||||
|
#cover(evaluation:((name:"Available",fill:"full"),(name:"Functional",fill:"left-half"),(name:"Reproduced",fill:"empty"),(name:"Tapedout",fill:"empty")))[
|
||||||
|
#text(9pt,weight:"semibold",fill:gold)[PRODUCT DATASHEET / #doc.status]
|
||||||
|
#v(rhythm.cover-gap)
|
||||||
|
#text(30pt,weight:"semibold")[retroSoC Tiny]
|
||||||
|
#linebreak()
|
||||||
|
#text(23pt,weight:"semibold",fill:gold)[Gen1]
|
||||||
|
#v(rhythm.cover-gap)
|
||||||
|
#text(13pt)[An Open-Source, Single-Core RISC-V MCU]
|
||||||
|
#v(rhythm.cover-meta-gap)
|
||||||
|
#grid(columns:(1fr,1fr),
|
||||||
|
[#text(font:("Inter","Noto Sans CJK SC"),weight:"bold",top-edge:0.7275390625em)[#doc.brand_name(#doc.brand_name_zh)] · v#doc.version \ #doc.date],
|
||||||
|
align(right)[#doc.author#link("mailto:"+doc.author_email)[(#doc.author_email)] \ #doc.maintainer])
|
||||||
|
#v(rhythm.cover-rule-gap)
|
||||||
|
#line(length:100%,stroke:1.1pt+gold)
|
||||||
|
= Product Brief
|
||||||
|
== Features
|
||||||
|
#columns(2,gutter:rhythm.cover-gutter)[
|
||||||
|
#set par(leading:rhythm.cover-leading,spacing:rhythm.cover-spacing)
|
||||||
|
#set list(spacing:rhythm.cover-list-spacing)
|
||||||
|
*Compute and debug*
|
||||||
|
- One Hazard3 RISC-V hart: *RV32IMC*, A disabled.
|
||||||
|
- Default RV32IM firmware; IRQ/CSR support enabled.
|
||||||
|
- JTAG debug with coordinated hart reset.
|
||||||
|
|
||||||
|
*Memory and interconnect*
|
||||||
|
- *128 KiB SRAM*, 32 banks of 4 KiB.
|
||||||
|
- Native AXI32 memory paths and one APB4 island.
|
||||||
|
- CPU and DMA share one active transaction globally.
|
||||||
|
- XPI NOR boot and SRAM-resident application layout.
|
||||||
|
|
||||||
|
*DMA and control*
|
||||||
|
- *Four DMA channels*, 32-bit transfers, up to 16 beats.
|
||||||
|
- Direct and 64-byte linked-descriptor operation.
|
||||||
|
- Sticky first-fault records and CPU/DMA wait snapshots.
|
||||||
|
- Sticky automated firmware acceptance result.
|
||||||
|
#colbreak()
|
||||||
|
*Wired connectivity*
|
||||||
|
- 32 GPIOs with atomic output aliases and interrupts.
|
||||||
|
- Two FIFO UARTs; dedicated TX/RX pads.
|
||||||
|
- Two controller-mode I2C instances with 7/10-bit addressing.
|
||||||
|
- Four-chip-select XPI with single/dual/quad SDR phases.
|
||||||
|
|
||||||
|
*Timing and supervision*
|
||||||
|
- Configured *24 MHz* system clock; no PLL.
|
||||||
|
- Two 32-bit general timers; four PWM outputs.
|
||||||
|
- 1 MHz CLINT timebase; local software/timer interrupts.
|
||||||
|
- RTC alarms/wake and window watchdog on the system clock.
|
||||||
|
|
||||||
|
*Development boundary*
|
||||||
|
- Freestanding SDK and Tiny acceptance application.
|
||||||
|
- IHP130 macro/behavioral integration and simulation flows.
|
||||||
|
- No external RAM, wireless, HP core or multimedia accelerator.
|
||||||
|
]
|
||||||
|
#v(1fr)
|
||||||
|
#note(below:0pt)[This DRAFT describes an implemented *prototype*. The 24 MHz configuration is a functional target,
|
||||||
|
*not a timing-qualified operating point*. Electrical, package, thermal, power and silicon qualification remain unprovided.
|
||||||
|
The partial Functional mark does not establish complete protocol or physical qualification.]
|
||||||
|
]
|
||||||
|
#pagebreak()
|
||||||
|
== Configuration and Scope <tiny-configuration>
|
||||||
|
The main reference is #code(doc.profile). *Tiny is an independent wired MCU integration*; it is not a selectable Mini mode.
|
||||||
|
The reviewed implementation is identified by #code(doc.source_revision). An interface name, shared SDK declaration or mapped
|
||||||
|
offset does not establish an optional feature. Identify the product before using an inherited register or API name.
|
||||||
|
#ds-table("configuration",[Tiny reference configuration],([Item],[Selected value],[Boundary]),(
|
||||||
|
([Product],[Tiny Gen1 / IHP130],[Prototype; one management MCU hart.]),
|
||||||
|
([Hardware / firmware],[RV32IMC, A disabled / RV32IM],[Compressed execution is implemented; the default compiler target omits C.]),
|
||||||
|
([Clock],[24 MHz; CLINT 1 MHz],[Functional targets; no PLL or qualified maximum-frequency rating.]),
|
||||||
|
([Memory],[128 KiB SRAM],[External NOR for boot; no external RAM initialization dependency.]),
|
||||||
|
([Bus / DMA],[AXI32/APB4; four channels],[One active read or write globally; memory-mapped endpoints only.]),
|
||||||
|
([Firmware],code("APP=bringup; HAVE_CSR=YES; LINK_TYPE=ld2_all_sram"),[Tiny-specific acceptance entry point.])),widths:(0.8fr,1.55fr,1.75fr))
|
||||||
|
|
||||||
|
=== Implemented scope and deferred capabilities
|
||||||
|
The 14 IP chapters cover every decoded region and all allocated interrupt causes. UART and I2C instances retain distinct
|
||||||
|
addresses and wiring. The GPIO user/administration windows are views of one pad bank. A restricted register window is not
|
||||||
|
a claim of a separate security domain.
|
||||||
|
|
||||||
|
Atomics, wireless, external RAM, USB, SDIO, standalone general SPI, I2S, CAN, ADC, accelerators, authenticated boot,
|
||||||
|
RTOS ports and independent deep-sleep timing are outside this release. Additional XPI chip selects remain available at
|
||||||
|
the interface, but the initial pin-level NOR acceptance uses NSS0; other populated-device configurations require evidence.
|
||||||
|
#source-note("docs/ip/tiny-soc.md",title:"Frozen Tiny requirements and non-goals")
|
||||||
|
|
||||||
|
== Reading and Evidence Conventions
|
||||||
|
Addresses are byte addresses; register offsets are relative to their named instance/group. *Source reviewed*, *test available*,
|
||||||
|
*executed result* and *physical qualification* are separate states. Source links target the reviewed commit or locked managed
|
||||||
|
input. Diagrams describe digital structure and ordering; they do not specify measured latency or board timing.
|
||||||
|
|
||||||
|
Use @tiny-addresses for memory windows, @tiny-irq for interrupt numbering, @tiny-register-index for address-first register
|
||||||
|
lookup, @tiny-boot for startup, @tiny-diagnostics for terminal results and @tiny-evidence for verification boundaries.
|
||||||
|
The document uses the approved Mini visual language; all product facts and source inventories here belong to Tiny.
|
||||||
|
#pagebreak()
|
||||||
|
#contents(depth:3)
|
||||||
|
#pagebreak()
|
||||||
|
#figure-directory(tables:true)
|
||||||
|
#pagebreak()
|
||||||
|
#figure-directory()
|
||||||
|
#pagebreak()
|
||||||
|
|
||||||
|
= System Architecture
|
||||||
|
== Processor and Integration <tiny-core>
|
||||||
|
Hazard3 is hart zero. The shared wrapper selects the C and M extensions and disables A for Tiny. Its AHB-Lite bus terminates
|
||||||
|
at a direct AXI32 adapter. JTAG supports the implemented halt/resume, register and system-bus debug path; debug hart reset
|
||||||
|
waits for the adapter to become idle instead of abandoning an accepted transaction.
|
||||||
|
|
||||||
|
The system has no application hart, MMU/Linux platform, hardware cache-coherent multicore organization or product extension
|
||||||
|
slots. CPU-visible SRAM, XPI and APB routes are the implemented interconnect. The single-hart architecture does not remove
|
||||||
|
the need for software/DMA buffer ownership and fences.
|
||||||
|
#architecture()
|
||||||
|
#source-note("rtl/tiny/top/retrosoc_tiny.sv",title:"Tiny product integration and parameters")
|
||||||
|
#source-note("rtl/ip/core/mgmt_core_wrapper.sv",title:"CPU ISA, AHB adapter and debug wrapper")
|
||||||
|
|
||||||
|
== Clock, Reset and Operating State <tiny-clock>
|
||||||
|
CPU, AXI, APB, RTC and watchdog share the configured 24 MHz system clock. CLINT derives a 1 MHz tick. JTAG has its own
|
||||||
|
external clock and managed asynchronous debug crossing. Other asynchronous external inputs use their specified synchronizers;
|
||||||
|
the absence of a system-bus clock crossing is not complete CDC/RDC signoff.
|
||||||
|
#ds-table("clocks",[Declared Tiny clock/reset domains],([Domain],[Clock],[Reset],[Reset primitive]),
|
||||||
|
data.clocks.map(c=>(c.name,code(c.clock),code(c.reset),code(c.reset_primitive))),widths:(0.65fr,1.1fr,1.5fr,0.9fr))
|
||||||
|
External reset is asynchronously asserted and synchronously released. The watchdog requests a whole-system restart while
|
||||||
|
retaining its own reset-cause information. Debug hart reset has a narrower scope. SRAM contents are *not initialized by reset*;
|
||||||
|
the runtime explicitly establishes initialized data and BSS.
|
||||||
|
|
||||||
|
There is no dynamic frequency switching, independent sleep clock or guaranteed retention mode in this release. A watchdog
|
||||||
|
sharing the failed system clock cannot promise recovery from that clock failure. The existing RTL timing target must not be
|
||||||
|
advertised as a qualified voltage/frequency operating point.
|
||||||
|
#source-note("rtl/tiny/integration/clock_reset_domains.json",title:"Clock/reset inventory and debug crossings")
|
||||||
|
|
||||||
|
== Bus Transactions, Arbitration and Errors <tiny-bus>
|
||||||
|
The two initiators are CPU and DMA. They share *one active read or write transaction globally*. The fabric saves source,
|
||||||
|
direction, address and attributes, forwards the request, and retains ownership until the terminal B or RLAST handshake.
|
||||||
|
Read/write request arbitration is round-robin; it is not a throughput guarantee or an unrestricted liveness guarantee.
|
||||||
|
|
||||||
|
Memory transfers support naturally aligned 1/2/4-byte beats and up to 16 INCR beats. Single-beat FIXED is also admitted.
|
||||||
|
The complete address span must remain within one decoded target and one 4 KiB page without overflow. MMIO accepts only
|
||||||
|
one beat. The addressed register can impose stricter width/strobe rules; Tiny SYSCTRL requires aligned full-word writes.
|
||||||
|
#ds-table("bus-rules",[Transfer outcome and software consequence],([Condition],[Result],[Required handling]),(
|
||||||
|
([Legal SRAM access],[Target response with saved ID/owner],[Do not reuse DMA buffers before completion.]),
|
||||||
|
([XPI memory aperture],[Mapped reads; target controls memory-write rejection],[Programming uses the indirect command interface.]),
|
||||||
|
([Illegal size/burst/lock/alignment/span],[SLVERR responder],[Correct the request; preserve diagnostic context.]),
|
||||||
|
([Unmapped target],[DECERR responder],[Treat the address as unavailable, not scratch memory.]),
|
||||||
|
([Unsupported APB offset/write],[PSLVERR, propagated as a bus error],[Do not retry an unsupported control indefinitely.])),widths:(1.1fr,1.4fr,1.65fr))
|
||||||
|
|
||||||
|
AW and W are independent. W is backpressured until the saved write address reaches its target; another requester cannot
|
||||||
|
take over accepted write data. Rejected reads return the advertised response beat count, and rejected writes drain their
|
||||||
|
accepted data before B. Backpressure requires valid payloads to remain stable.
|
||||||
|
#timing("axi4",[Representative AXI read response and backpressure; Tiny uses 32-bit data and one-bit ID.])
|
||||||
|
#timing("apb4",[APB4 setup/access sequence with a wait state; address and write data remain stable.])
|
||||||
|
#source-note("rtl/tiny/top/tiny_axi4_fabric.sv",title:"Saved ownership, admission and terminal handshake")
|
||||||
|
#source-note("rtl/ip/interconnect/axi4_error_slave.sv",title:"Error response and write-drain implementation")
|
||||||
|
|
||||||
|
== Memory Map and Access Conventions <tiny-addresses>
|
||||||
|
#ds-table("memory-map",[Canonical Tiny address windows],([Region],[Base],[Last address],[Size]),
|
||||||
|
data.regions.map(r=>(code(r.symbol),code(r.base_hex),code(r.end_hex),r.size_label)),widths:(1.2fr,1fr,1fr,0.65fr))
|
||||||
|
The Flash alias and XPI aperture identify memory routing, not fitted NOR capacity. The selected acceptance device and slot
|
||||||
|
configuration remain separate. Only SRAM is the application RAM in this profile. Unsupported holes and omitted peripheral
|
||||||
|
addresses must not be probed speculatively or reused as memory.
|
||||||
|
|
||||||
|
Register tables specify local offsets, access, reset and side effects. *W1C acknowledgement uses an explicit mask*;
|
||||||
|
read-modify-write can clear unrelated pending events. A live value or producer-dependent field need not equal its reset
|
||||||
|
assignment when software later reads it. Use coherent snapshots for multiword counters.
|
||||||
|
|
||||||
|
== Interrupt and DMA Request Routing <tiny-irq>
|
||||||
|
#ds-table("irq-map",[Tiny interrupt wiring],([Source],[Core bit],[External ordinal],[Producer]),
|
||||||
|
data.interrupts.map(r=>(r.name,str(r.core_bit),if r.core_bit < 2 {[Local]} else {str(r.core_bit - 2)},code(r.signal))),widths:(1.05fr,0.5fr,0.7fr,1.95fr))
|
||||||
|
Core bits 0/1 are CLINT software/timer causes. The other 30 positions feed Hazard3 external interrupt inputs;
|
||||||
|
*external ordinal equals core bit minus two*. All omitted producers are tied zero. A register index, DMA selector and
|
||||||
|
interrupt number are different namespaces. Core bits here number the wrapper input vector, not architectural CSR bit positions.
|
||||||
|
#ds-table("dma-routes",[Enabled DMA requests and software channel conventions],([Selector],[Endpoint],[Channel convention]),(
|
||||||
|
([0],[Software-paced memory transfer],[3 bulk, or explicitly reserved free channel]),
|
||||||
|
([3 / 4],[XPI transmit / receive],[3]),([5 / 6],[UART0 transmit / receive],[0]),
|
||||||
|
([7 / 8],[I2C0 transmit / receive],[1]),([9 / 10],[I2C1 transmit / receive],[2])),widths:(0.7fr,2fr,1.4fr))
|
||||||
|
UART1 uses PIO/IRQ. The shared SDK names other requests and channels, but Tiny must reject unavailable selections.
|
||||||
|
The current DMA transfer validator accepts 32-bit MM-to-MM work; stream modes are disabled. Serialize shared channel
|
||||||
|
conventions rather than assuming that each API owns a permanently independent engine.
|
||||||
|
#source-note("rtl/tiny/integration/soc_topology.json",title:"Interrupt and GPIO bindings")
|
||||||
|
#source-note("crt/src/hal/dma_math.c",title:"Tiny DMA kind/request validation")
|
||||||
|
|
||||||
|
== Peripherals
|
||||||
|
Each IP starts on a new page. Repeated instances retain separate address/IRQ entries; their common register layout is
|
||||||
|
documented once. The fields below are extracted from the active common implementation with Tiny parameter overrides;
|
||||||
|
SYSCTRL and the ARCHINFO identity override are bound to Tiny-owned RTL.
|
||||||
|
#for entry in data.catalog {reference(entry)}
|
||||||
|
|
||||||
|
== Pins and Board Connections <tiny-pins>
|
||||||
|
The following is a *logical signal-pad inventory*, not a package pin numbering or an approved PCB schematic. Pad cells,
|
||||||
|
power/ground pins, voltage domains and package bonding require the selected physical implementation.
|
||||||
|
#ds-table("pads",[Tiny logical signal pads],([Signal],[RTL direction],[Pad kind]),
|
||||||
|
data.pads.map(p=>(code(p.name),p.direction,p.kind.replace("_"," "))),widths:(2fr,0.8fr,1.2fr))
|
||||||
|
#let gpio-mode(mode) = {
|
||||||
|
let terms=mode.inputs + if mode.do.contains("'") {()} else {(mode.do,)}
|
||||||
|
if terms.len()==0 {[Not routed]} else {code(terms.join(" / "))}
|
||||||
|
}
|
||||||
|
#ds-table("gpio-alternates",[Tiny GPIO alternate-function routes],([GPIO],[ALT0],[ALT1]),
|
||||||
|
data.gpio.map(g=>(str(g.pin),gpio-mode(g.alt0),gpio-mode(g.alt1))),widths:(0.4fr,1.85fr,1.85fr))
|
||||||
|
Disable producers and release output enables before changing a live pad route. I2C and open-drain signals require external
|
||||||
|
pull-ups and compatible levels. Dedicated UART and XPI pins are not reassigned through the GPIO mux. Verify complete
|
||||||
|
pin groups, reset drive state and external clock availability before enabling a device.
|
||||||
|
|
||||||
|
== Electrical, Package and Reliability Boundaries
|
||||||
|
This source-level publication supplies *no numerical production electrical ratings*. TBD means unavailable evidence,
|
||||||
|
not zero, unlimited range or permission to select a convenient value. Functional clock names are not independent power rails.
|
||||||
|
#ds-table("physical-gaps",[Evidence required before physical product claims],([Area],[Required evidence],[Current state]),(
|
||||||
|
([Supply / absolute maximum],[Pad/core rails, sequencing, injection/ESD limits, approved conditions],[TBD]),
|
||||||
|
([AC/DC timing],[PVT, loading, clock reference edge, setup/hold and input/output limits],[TBD]),
|
||||||
|
([Package / pinout],[Die pad and package pin mapping, dimensions and bonding],[TBD]),
|
||||||
|
([Thermal / power],[Package/board/test conditions, workloads, voltage and measured results],[TBD]),
|
||||||
|
([Reliability / ordering],[Qualification records, traceable device revisions and order codes],[TBD])),widths:(1fr,2.5fr,0.5fr))
|
||||||
|
No package photograph, fabricated part number, regulatory mark or silicon speed grade is inferred from the digital profile.
|
||||||
|
The documented STA deficit remains visible in @tiny-evidence; it is not an operating specification.
|
||||||
|
|
||||||
|
= Software
|
||||||
|
== Reset, Boot and Linker Layout <tiny-boot>
|
||||||
|
Reset enters the Flash alias at #code(data.reset_address). The Tiny runtime excludes the generic PSRAM delay/setup branches.
|
||||||
|
With CSR support selected it masks interrupts and installs the direct trap entry, initializes registers/stack, copies code
|
||||||
|
and data when load/run addresses differ, clears BSS, performs the configured premain work, and enters main.
|
||||||
|
#flow("boot",(
|
||||||
|
[Reset / JTAG state established · Fetch from NOR alias],
|
||||||
|
[Mask interrupts; establish trap entry and register/stack state],
|
||||||
|
[Tiny skips PSRAM setup · Copy initialized code/data into SRAM],
|
||||||
|
[Clear SRAM BSS · Perform configured premain initialization],
|
||||||
|
[Enter Tiny application · Handle terminal result or watchdog restart]
|
||||||
|
),[Tiny ordinary startup: source order, not a guaranteed boot-time measurement.])
|
||||||
|
The `ld2_all_sram` layout places runtime code, data, BSS and stack in SRAM with initialized material loaded from Flash.
|
||||||
|
Memory-map capacity, load-image bytes, runtime static occupation and stack high-water demand are different quantities.
|
||||||
|
Inspect the actual ELF/MAP and reserve buffers plus stack/scratch margin before deployment. No matching size/high-water
|
||||||
|
measurement is supplied by this datasheet build.
|
||||||
|
#source-note("crt/arch/riscv/startup.S",title:"Tiny conditional startup and relocation")
|
||||||
|
#source-note("crt/linker/ld2_all_sram.lds",title:"Flash load / SRAM runtime linker layout")
|
||||||
|
|
||||||
|
== SDK, API Completion and Polling
|
||||||
|
Consume public headers under #code("<retrosoc/...>") and the #code("rs_") namespace. The Tiny build selects product-specific
|
||||||
|
application composition and generated address/IRQ/capability metadata. This does not generate replacement IP register ABIs.
|
||||||
|
Unsupported PLL, application-hart or selectable-core APIs return an error before MMIO.
|
||||||
|
|
||||||
|
*A successful API return describes that API's completion boundary*: a start command can be accepted before its operation
|
||||||
|
finishes. FIFO acceptance is not completed serial transmission. A timeout can leave work active and buffers owned by a
|
||||||
|
device. Capture state and complete abort/drain before reuse.
|
||||||
|
|
||||||
|
Software timeout arguments are polling budgets, not milliseconds. CPU/compiler work and bus stalls affect elapsed time;
|
||||||
|
a budget cannot bound a register read that never returns. Hardware counters and device-specific timeouts use their own
|
||||||
|
documented clock units. Use CLINT or another appropriate timebase when an elapsed-time deadline is required.
|
||||||
|
|
||||||
|
== DMA Descriptors and Buffer Lifetime <tiny-dma-format>
|
||||||
|
#tcd-diagram()
|
||||||
|
#ds-table("tcd-fields",[Source-derived DMA C descriptor layout],([Byte offset],[Field],[Bytes],[Role]),
|
||||||
|
data.tcd.map(f=>(code("0x"+upper(str(f.offset,base:16))),code(f.name),str(f.bytes),f.role)),widths:(0.75fr,1.7fr,0.45fr,1.1fr))
|
||||||
|
The descriptor is 64 bytes and each descriptor address is 64-byte aligned. Tiny permits only the implemented transfer kind,
|
||||||
|
request, increment and width combinations. Stride fields remain zero and row count is at most one. A field in the ABI is
|
||||||
|
not evidence of cyclic, 2D or stream support. HAL result fields do not imply that hardware directly writes every result word.
|
||||||
|
|
||||||
|
#ds-table("tcd-control",[Descriptor control positions and portable Tiny use],([Bits],[Field],[Meaning / restriction]),(
|
||||||
|
([0],[VALID],[Must be set for a submitted descriptor.]),
|
||||||
|
([1 / 2],[SRC_INC / DST_INC],[Increment the corresponding memory address; use the endpoint's required mode.]),
|
||||||
|
([3 / 4],[CRC_ENABLE / CRC_FINAL],[Select the implemented CRC processing/final comparison.]),
|
||||||
|
([5 / 6],[INT_DONE / INT_ERROR],[Declared SDK flags; use the channel event-enable/IRQ registers rather than assuming per-descriptor IRQ gating.]),
|
||||||
|
([10:8],[KIND],[Tiny admits MM-to-MM value zero only.]),
|
||||||
|
([15:12],[REQUEST],[Zero or 3 through 10 in the current Tiny request map.]),
|
||||||
|
([17:16],[PRIORITY],[Two-bit software priority value.]),
|
||||||
|
([24:20],[BURST],[0 selects the 16-beat default in descriptor submission; 1 through 16 select a bound. Direct configuration requires 1 through 16.]),
|
||||||
|
([Other bits],[Reserved / unused],[Use zero for compatibility; a field declaration does not establish an extra operation.])),widths:(0.6fr,1.05fr,2.5fr))
|
||||||
|
The current HAL chain helper submits one descriptor at a time, waits, copies result registers into the descriptor and then
|
||||||
|
follows #code("next_ptr"), bounded by #code("max_tcds"). It is not an autonomous circular-queue API. On a timeout or error,
|
||||||
|
the helper records observed progress but the caller must still confirm abort/drain before releasing memory.
|
||||||
|
|
||||||
|
Reserve channel, descriptor and payload storage through completion or confirmed recovery. Publish CPU-produced input with
|
||||||
|
the required fence before starting the non-CPU master. Tiny has no application-hart cache handoff protocol; importing the
|
||||||
|
Mini HP cache-clean service would describe nonexistent hardware.
|
||||||
|
#flow("dma-recovery",([Validate channel / request / bounds and reserve storage],
|
||||||
|
[Publish descriptor/input and configure while idle], [Start and wait for the required completion boundary],
|
||||||
|
[On failure: capture fault/progress, abort, confirm drain], [Release or reuse storage only after ownership is safe]),
|
||||||
|
[DMA lifetime and recovery ordering. A polling timeout is not an idle acknowledgement.])
|
||||||
|
#source-note("crt/include/retrosoc/hal/dma.h",title:"Handwritten descriptor and public HAL ABI")
|
||||||
|
#source-note("crt/src/hal/dma_math.c",title:"Descriptor validation and unsupported combinations")
|
||||||
|
|
||||||
|
== Acceptance and Diagnostics <tiny-diagnostics>
|
||||||
|
Both #code("bringup") and #code("ci_smoke") select the Tiny acceptance entry point. A controlled first boot with a clear
|
||||||
|
watchdog reset count performs the tests below, then deliberately requests watchdog reboot. A subsequent boot with a nonzero
|
||||||
|
retained reset count takes the early successful terminal path. *A pre-existing watchdog reset count is not proof that this
|
||||||
|
boot executed every test.* Record initial state, image, profile and the final simulator result.
|
||||||
|
#ds-table("acceptance-codes",[Tiny application failure codes from the reviewed main sequence],([Code],[Stage]),(
|
||||||
|
([1],[Primary UART initialization]),([2],[Watchdog status read]),([3],[Tiny build/topology and SRAM discovery]),
|
||||||
|
([4],[Compressed execution, A-disabled ISA and exception handler setup]),([5],[Atomic/unmapped probes and first fault]),
|
||||||
|
([6],[Memory/UART DMA]),([7],[CLINT progression and timer interrupt]),([8],[RTC, PWM discovery and unsupported-control rejection]),
|
||||||
|
([9],[Watchdog configuration/start]),([10],[GPIO, UART1 loopback and I2C NACK checks])),widths:(0.4fr,3.8fr))
|
||||||
|
The first valid full-word SYSCTRL TEST_STATUS write is sticky. Bit 31 is valid, bit 0 pass and bits 15:8 the result code.
|
||||||
|
The testbench must emit #code("SIM_TEST_PASS") and the command must succeed without failure/timeout markers. UART startup
|
||||||
|
text is diagnostic only. The compact assembly netlist boot test has a narrower scope than the full SDK image.
|
||||||
|
#source-note("app/apps/ci_smoke/tiny.c",title:"Acceptance order, reset-count branch and terminal codes")
|
||||||
|
#source-note("app/asm/tiny_boot.S",title:"Separate compact netlist boot test")
|
||||||
|
|
||||||
|
= Implementation and Evidence
|
||||||
|
== Reproducible Development Flow
|
||||||
|
Start from the committed IHP130 Tiny profile. Use the existing locked dependency setup and doctor flow, then build the
|
||||||
|
Tiny firmware and selected simulator. The publication pipeline itself does not perform an EDA campaign.
|
||||||
|
#code-block(raw("make CONFIG=configs/ci/ihp130-tiny.mk setup\nmake CONFIG=configs/ci/ihp130-tiny.mk doctor\nmake CONFIG=configs/ci/ihp130-tiny.mk firmware sim\nmake CONFIG=configs/ci/ihp130-tiny.mk SIMU=IVERILOG firmware sim\nmake CONFIG=configs/ci/ihp130-tiny.mk regress-pr",block:true))
|
||||||
|
Tiny source lists exclude Mini product RTL, RIB/RIBP, application-CPU generation and accelerator inputs. Common RTL and
|
||||||
|
Hazard3 remain revision-locked. Keep generated files in the selected build variant; missing unrelated accelerator corpora
|
||||||
|
must not become prerequisites for the Tiny publication.
|
||||||
|
|
||||||
|
== Verification Status and Qualification Gaps <tiny-evidence>
|
||||||
|
Readiness is *prototype*. #data.evidence.boundary
|
||||||
|
#ds-table("evidence",[Dated repository verification record and publication boundary],([Area],[What the record reports],[Interpretation]),
|
||||||
|
data.evidence.records.map(r=>(r.at(0),r.at(1),r.at(2))),widths:(0.8fr,1.7fr,1.65fr))
|
||||||
|
No matching current-commit raw qualification report is attached. The documented setup WNS of *−455.18 ns* and TNS of
|
||||||
|
*−13,049,600 ns* describe the dated IHP130 slow-library analysis at a 41.666666667 ns clock constraint, with a reset-distribution deficit. They must not become a positive
|
||||||
|
24 MHz timing claim. Subsequent source-path migration has its own bounded follow-up record; it does not rerun all earlier stages.
|
||||||
|
|
||||||
|
Publication tests establish document/source consistency and layout, not electrical compliance, full protocol coverage or
|
||||||
|
silicon operation. Future pass records must identify exact source/profile, tool/model versions, executed stage, raw report
|
||||||
|
and result. Keep skipped, failed and unfinished work visible.
|
||||||
|
#source-note("docs/ip/tiny-soc-verification.md",title:"Historical evidence, timing deficit and remaining work")
|
||||||
|
|
||||||
|
= Appendix: Global Register Index <tiny-register-index>
|
||||||
|
Use the instance-qualified address below, then follow the canonical register reference. GPIO common discovery offsets
|
||||||
|
exist in both views. Repeated groups retain explicit indices; gaps are not additional registers. Addressability does not
|
||||||
|
override the register's access, alignment, strobe or side-effect rules.
|
||||||
|
#for entry in data.catalog {
|
||||||
|
if entry.id=="uart1" {minor-title("UART1 - shared UART register ABI")} else {minor-title(entry.title)}
|
||||||
|
let ref=data.registers.at(entry.family)
|
||||||
|
for symbol in entry.regions.filter(s=>s.starts-with("APB4_")) {
|
||||||
|
let region=data.regions.find(r=>r.symbol==symbol)
|
||||||
|
let records=()
|
||||||
|
for group in ref.groups {
|
||||||
|
let applies=entry.family!="gpio" or group.id=="common" or (group.id=="user" and symbol=="APB4_GPIO") or (group.id=="admin" and symbol=="APB4_GPIO_ADMIN")
|
||||||
|
if applies {
|
||||||
|
for instance in range(group.count) {
|
||||||
|
for reg in ref.registers.filter(r=>r.group==group.id) {
|
||||||
|
let address=region.base+group.base+instance*group.stride+reg.offset
|
||||||
|
records.push((code("0x"+upper(str(address,base:16))),link(label("reg-"+entry.family+"-"+reg.key),code(reg.name)),
|
||||||
|
if group.count>1 {str(instance)} else {[—]},reg.access))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ds-table("index-"+symbol,[#symbol instance addresses],([Absolute address],[Register],[Index],[Access]),records,widths:(1fr,1.9fr,0.4fr,0.85fr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
= Document Control
|
||||||
|
== Revision History
|
||||||
|
#ds-table("revision",[Document revision history],([Date],[Version],[Change]),
|
||||||
|
((doc.date,[v#doc.version #doc.status],[Initial Tiny Gen1 complete reference; independent source extraction and Tiny-qualified integration.]),),widths:(0.8fr,0.85fr,2.5fr))
|
||||||
|
|
||||||
|
== Sources, License and Contact <publication-provenance>
|
||||||
|
Hardware/software snapshot: #code(doc.source_revision). This document's source manifest records the exact technical files,
|
||||||
|
publication files, managed revisions and fonts/packages used for the build. The PDF digest, chapter review and actual
|
||||||
|
validation record accompany the output; the document date is not a hardware measurement date.
|
||||||
|
|
||||||
|
The publication uses locked Typst 0.15.1, existing Inter/Fira Code/Noto fonts and the project's managed vector/waveform packages.
|
||||||
|
The prototype brand is Rill; this identity does not establish an orderable or qualified silicon product.
|
||||||
|
|
||||||
|
The project retains its Mulan PSL v2 license and third-party component/font licenses. Obtain the current source and license
|
||||||
|
inventory from #link("https://github.com/retroSoC/retroSoC")[the project repository].
|
||||||
|
Contact: #link("mailto:"+doc.author_email)[#doc.author (#doc.author_email)]. Maintainer: #doc.maintainer.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#import "@preview/cetz:0.5.2" as cetz
|
||||||
|
#import "@preview/bytefield:0.0.8" as bf
|
||||||
|
#import "style.typ": *
|
||||||
|
|
||||||
|
#let diagram(id, body, caption) = block(width:100%,breakable:false)[
|
||||||
|
#context metadata((kind:"tiny-diagram",id:id,page:here().page()))
|
||||||
|
#figure(body,caption:caption)
|
||||||
|
]
|
||||||
|
#let schematic(nodes, routes, height:90) = {
|
||||||
|
set text(font:"Inter",size:9pt,weight:"regular",fill:ink)
|
||||||
|
cetz.canvas(length:1mm,{
|
||||||
|
import cetz.draw: rect, line, content
|
||||||
|
rect((0,0),(172,-height),stroke:none)
|
||||||
|
for path in routes {
|
||||||
|
line(..path.map(p=>(p.at(0),-p.at(1))),stroke:0.65pt+ink,mark:(end:"stealth"))
|
||||||
|
}
|
||||||
|
for node in nodes {
|
||||||
|
let (x,y,w,h,label)=node
|
||||||
|
rect((x,-y),(x+w,-y - h),fill:gray,stroke:0.6pt+ink)
|
||||||
|
content((x+w/2,-y - h/2),block(width:(w - 4)*1mm)[
|
||||||
|
#set text(font:"Inter",size:9pt,weight:"regular")
|
||||||
|
#set par(leading:2pt,spacing:0pt)
|
||||||
|
#align(center,label)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#let architecture() = diagram("tiny-architecture",schematic((
|
||||||
|
(5,4,65,19,[Hazard3 · hart 0 \ RV32IMC · A disabled]),
|
||||||
|
(104,4,63,19,[JTAG / debug transport \ External TCK domain]),
|
||||||
|
(5,37,65,15,[AHB-Lite → AXI32 adapter]),
|
||||||
|
(104,37,63,15,[Four-channel DMA \ AXI32 master]),
|
||||||
|
(30,69,112,19,[Tiny AXI32 fabric \ One active transaction globally]),
|
||||||
|
(0,110,40,22,[128 KiB SRAM \ 32 × 4 KiB]),
|
||||||
|
(44,110,40,22,[XPI read path \ Flash alias / NOR]),
|
||||||
|
(88,110,40,22,[APB4 bridge \ 16 targets]),
|
||||||
|
(132,110,40,22,[Error responders \ DECERR / SLVERR]),
|
||||||
|
),(
|
||||||
|
((104,13),(70,13)),((37,23),(37,37)),
|
||||||
|
((37,52),(37,61),(60,61),(60,69)),((135,52),(135,61),(112,61),(112,69)),
|
||||||
|
((45,88),(45,99),(20,99),(20,110)),((70,88),(70,103),(64,103),(64,110)),
|
||||||
|
((103,88),(103,103),(108,103),(108,110)),((127,88),(127,99),(152,99),(152,110)),
|
||||||
|
),height:134),[Tiny Gen1 integration. System logic runs from the configured 24 MHz clock; JTAG is asynchronous.])
|
||||||
|
|
||||||
|
#let ip-diagram(entry) = {
|
||||||
|
let children=entry.nodes.slice(1)
|
||||||
|
let positions=if children.len()<=3 {((1,40,54,20),(59,40,54,20),(117,40,54,20))}
|
||||||
|
else {((5,39,68,20),(99,39,68,20),(5,76,68,20),(99,76,68,20))}
|
||||||
|
let nodes=((50,0,72,20,entry.nodes.first()),)
|
||||||
|
let routes=()
|
||||||
|
for (i,name) in children.enumerate() {
|
||||||
|
let (x,y,w,h)=positions.at(i)
|
||||||
|
nodes.push((x,y,w,h,name))
|
||||||
|
// Control reaches each functional group independently; no invented FIFO-to-FIFO chain.
|
||||||
|
let branch=if children.len()<=3 or i < 2 {29} else {68}
|
||||||
|
routes.push(((86,20),(86,branch),(x+w/2,branch),(x+w/2,y)))
|
||||||
|
}
|
||||||
|
diagram("ip-"+entry.id,schematic(nodes,routes,height:if children.len()>3 {98} else {62}),
|
||||||
|
[#entry.title functional groups. Arrows show control reach, not every internal data wire or a cycle schedule.])
|
||||||
|
}
|
||||||
|
|
||||||
|
#let flow(id, items, caption) = {
|
||||||
|
let nodes=items.enumerate().map(((i,item))=>(16,i*23,140,16,item))
|
||||||
|
let paths=range(items.len()-1).map(i=>((86,i*23+16),(86,(i+1)*23)))
|
||||||
|
diagram(id,schematic(nodes,paths,height:items.len()*23-5),caption)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let tcd-diagram() = {
|
||||||
|
let names=("next_ptr":"Next","source":"Source","destination":"Destination","byte_count":"Byte count",
|
||||||
|
"source_stride":"Src stride","destination_stride":"Dst stride","y_count":"Rows","reserved":"Rsvd",
|
||||||
|
"control":"Control","crc_expected":"CRC expect","crc_seed":"CRC seed","crc_result":"CRC result",
|
||||||
|
"status":"Status","bytes_done":"Bytes done","error_status":"Error","reserved_tail":"Reserved","reserved_tail2":"Reserved")
|
||||||
|
let fields=()
|
||||||
|
for field in data.tcd {
|
||||||
|
if calc.rem(field.offset,16)==0 {fields.push(bf.note(left)[#("0x"+upper(str(field.offset,base:16)))])}
|
||||||
|
fields.push(bf.bytes(field.bytes,fill:if field.role=="reserved" {gray} else if field.role=="HAL result" {rgb("#E7EFF6")} else {pale-gold})[#{names.at(field.name)}])
|
||||||
|
}
|
||||||
|
diagram("dma-tcd",{
|
||||||
|
set text(font:"Inter",size:9pt)
|
||||||
|
show: bf.bf-config.with(row-height:27pt,field-font-size:9pt,note-font-size:9pt,header-font-size:9pt,stroke:0.6pt+ink)
|
||||||
|
bf.bytefield(bpr:128,msb:right,pre:(36pt,),post:(),
|
||||||
|
bf.bitheader(0,[0],32,[4],64,[8],96,[12],numbers:none,angle:0deg,text-size:9pt),..fields)
|
||||||
|
},[DMA TCD: four 16-byte rows. Gold: configuration; blue: HAL result fields; gray: reserved.])
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"status": "prototype",
|
||||||
|
"current_commit_reports": [],
|
||||||
|
"historical_source": "docs/ip/tiny-soc-verification.md",
|
||||||
|
"historical_date": "2026-09-25",
|
||||||
|
"boundary": "Repository-reported local results from an originally uncommitted implementation; raw matching artifacts are not supplied in this publication workspace. Not rerun or promoted to current-commit qualification.",
|
||||||
|
"records": [
|
||||||
|
[
|
||||||
|
"RTL and software",
|
||||||
|
"Repository records directed fabric/SYSCTRL tests and pin-level NOR firmware acceptance in Verilator/Icarus.",
|
||||||
|
"Reported historical functional evidence, not a new publication-run result."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Netlist",
|
||||||
|
"Compact assembly SRAM/pad boot test is recorded; the full C acceptance netlist campaign remains incomplete.",
|
||||||
|
"Do not substitute the compact netlist test for complete SDK acceptance."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"IHP130 STA",
|
||||||
|
"Recorded setup WNS -455.18 ns and TNS -13,049,600 ns; dominant reset-distribution path.",
|
||||||
|
"24 MHz is a functional target, not timing-qualified operation."
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Physical and silicon",
|
||||||
|
"No matching place/route, PVT/MMMC, CDC/RDC signoff, board characterization or silicon/power report.",
|
||||||
|
"No numerical electrical, package, thermal or safety qualification claimed."
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
#import "style.typ": *
|
||||||
|
#import "diagrams.typ": ip-diagram
|
||||||
|
#import "waveforms.typ": timing
|
||||||
|
#let inline(value) = {
|
||||||
|
// A deliberately limited inline grammar: explicit strong spans and raw code.
|
||||||
|
// Code is opaque, including any literal asterisks it contains.
|
||||||
|
let bold = false
|
||||||
|
for (i,part) in value.split("`").enumerate() {
|
||||||
|
if calc.rem(i,2)==1 { code(part) } else {
|
||||||
|
for (j,piece) in part.split("**").enumerate() {
|
||||||
|
if j>0 { bold = not bold }
|
||||||
|
if bold { strong(piece) } else { piece }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let subhead(ip, name, depth) = [
|
||||||
|
#{
|
||||||
|
show heading: it => heading-layout(it,before:rhythm.minor-before,after:rhythm.minor-after)
|
||||||
|
heading(level:depth+1,numbering:none,outlined:false,bookmarked:true,name)
|
||||||
|
}#label("detail-"+ip+"-"+lower(name).replace(" ","-"))
|
||||||
|
]
|
||||||
|
|
||||||
|
#let prose-blocks(ip, section-title, blocks) = {
|
||||||
|
for (n,item) in blocks.enumerate() {
|
||||||
|
if item.kind=="heading" {
|
||||||
|
minor-title(inline(item.text))
|
||||||
|
} else if item.kind=="code" {
|
||||||
|
if item.language not in ("mermaid","dot") {
|
||||||
|
code-block(raw(item.text,block:true),breakable:true)
|
||||||
|
}
|
||||||
|
} else if item.kind=="table" {
|
||||||
|
ds-table(ip+"-"+str(n),section-title,
|
||||||
|
item.headers.map(inline),item.rows.map(r=>r.map(inline)))
|
||||||
|
} else if item.kind=="list" {
|
||||||
|
let bodies=item.items.enumerate().map(((index,entry))=>{
|
||||||
|
let content=prose-blocks(ip+"-"+str(n)+"-"+str(index),section-title,entry.blocks)
|
||||||
|
if item.ordered {enum.item(entry.number,content)} else {list.item(content)}
|
||||||
|
})
|
||||||
|
if item.ordered {enum(..bodies)} else {list(..bodies)}
|
||||||
|
} else { par(inline(item.text)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let prose-sections(ip, sections, depth) = {
|
||||||
|
for (index,section) in sections.enumerate() {
|
||||||
|
if sections.len()>1 { minor-title(section.title) }
|
||||||
|
prose-blocks(ip+"-"+str(index),section.title,section.blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#let bit-layout(register) = {
|
||||||
|
let fields=register.fields.sorted(key:f=>-f.msb)
|
||||||
|
set text(size:9pt)
|
||||||
|
set par(leading:rhythm.small-leading,spacing:0pt)
|
||||||
|
let cells = ()
|
||||||
|
for high in (31,15) {
|
||||||
|
cells += range(high,high - 16,step:-1).map(b=>align(center,str(b)))
|
||||||
|
cells += fields.enumerate().filter(((n,f))=>f.lsb<=high and f.msb>=high - 15).map(((n,f))=>{
|
||||||
|
let span=calc.min(f.msb,high)-calc.max(f.lsb,high - 15)+1
|
||||||
|
let text=if f.name.len()<=span*4 {f.name} else {"F"+str(n+1)}
|
||||||
|
table.cell(colspan:span,fill:if f.name=="Reserved" {gray} else {pale-gold},align(center,text))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
table(columns:range(16).map(n=>1fr),
|
||||||
|
stroke:table-stroke,inset:rhythm.bits-inset,..cells)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let register-section(family, depth) = {
|
||||||
|
let reference=data.registers.at(family)
|
||||||
|
subhead(family,"Register Summary",depth)
|
||||||
|
[Offsets are byte offsets within the named register group. Repeated groups use the base,
|
||||||
|
stride and instance range below. Access descriptions include register-specific restrictions.
|
||||||
|
A numeric reset identifies stored or fixed state. Dynamic marks live producer/integration
|
||||||
|
state; apply the domain, validity and snapshot rules before consuming it. Not retained marks
|
||||||
|
a command or data port, rather than a persistent configuration word.]
|
||||||
|
for group in reference.groups {
|
||||||
|
let registers=reference.registers.filter(r=>r.group==group.id)
|
||||||
|
if registers.len()==0 { continue }
|
||||||
|
minor-title(group.title)
|
||||||
|
if group.count>1 {
|
||||||
|
par([Instance offset: #code("0x"+str(group.base,base:16)) + n × #code("0x"+str(group.stride,base:16)),
|
||||||
|
n = 0…#(group.count - 1). Local offsets below are added to this instance offset.])
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Register-name links keep their blue color and destination, without decoration.
|
||||||
|
show underline: omit-link-underline
|
||||||
|
ds-table(family+"-summary-"+group.id,group.title,
|
||||||
|
([Offset],[Register],[Access],[Reset],[Description]),
|
||||||
|
registers.map(r=>(code("0x"+str(r.offset,base:16)),link(label("reg-"+family+"-"+r.key),code(r.name)),r.access,inline(r.reset),inline(r.description))),
|
||||||
|
widths:(0.55fr,1.6fr,0.6fr,1.1fr,2.6fr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subhead(family,"Register Description",depth)
|
||||||
|
[Bit-layout cells refer to the numbered field rows below each diagram. Reserved bits must
|
||||||
|
be handled as specified by the register; register access checks still apply to the full word.]
|
||||||
|
for register in reference.registers {
|
||||||
|
context metadata((kind:"tiny-register",family:family,key:register.key,page:here().page()))
|
||||||
|
block(above:rhythm.register-before,below:rhythm.metadata-after,breakable:false,sticky:true)[
|
||||||
|
#show heading: it => heading-layout(it,before:0pt,after:rhythm.register-after)
|
||||||
|
#set text(size:9pt)
|
||||||
|
#set par(leading:rhythm.small-leading,spacing:rhythm.small-spacing)
|
||||||
|
#heading(level:depth+2,numbering:none,outlined:false,bookmarked:true,register.name)
|
||||||
|
#label("reg-"+family+"-"+register.key)
|
||||||
|
#text(9pt,fill:muted)[#register.group · offset #code("0x"+str(register.offset,base:16)) · #register.access · reset #inline(register.reset)]
|
||||||
|
#linebreak()
|
||||||
|
#source(register.source,title:"RTL definition",line:register.line)
|
||||||
|
#if register.c_source!=none { [ · ]; source(register.c_source,title:"C ABI / HAL") }
|
||||||
|
]
|
||||||
|
par(inline(register.description))
|
||||||
|
block(breakable:false,above:rhythm.bits-space,below:rhythm.bits-space,bit-layout(register))
|
||||||
|
let fields=register.fields.sorted(key:f=>-f.msb)
|
||||||
|
ds-table("fields-"+family+"-"+register.key,[#register.name field descriptions],
|
||||||
|
([\#],[Bits],[Field],[Reset],[Description]),
|
||||||
|
fields.enumerate().map(((n,f))=>("F"+str(n+1),str(f.msb)+if f.msb!=f.lsb {":"+str(f.lsb)} else {""},code(f.name),inline(f.reset),inline(f.description))),
|
||||||
|
widths:(0.25fr,0.5fr,1.1fr,0.75fr,2.3fr))
|
||||||
|
if register.at("notes",default:"")!="" { par(inline(register.notes)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#let reference(entry) = {
|
||||||
|
pagebreak(weak:true)
|
||||||
|
context metadata((kind:"ip-start",id:entry.id,page:here().page()))
|
||||||
|
[#heading(level:3,entry.title)#label(entry.id)]
|
||||||
|
ip(entry.id)
|
||||||
|
minor-title("Features and functional organization")
|
||||||
|
list(..entry.features.map(inline))
|
||||||
|
ip-diagram(entry)
|
||||||
|
minor-title("Functional description and integration boundary")
|
||||||
|
for paragraph in entry.paragraphs {par(inline(paragraph))}
|
||||||
|
minor-title("Protocol and representative timing")
|
||||||
|
[The common AXI/APB rules in @tiny-bus and this instance's access rules both apply.
|
||||||
|
*Example waveforms show ordering and stable handshakes, not guaranteed cycle latency or electrical timing.*]
|
||||||
|
timing(if entry.family in data.waveforms {entry.family} else {"apb4"},[#entry.title representative transaction.],instance:entry.id)
|
||||||
|
if entry.id=="uart1" {
|
||||||
|
minor-title("Shared register reference")
|
||||||
|
[Use the @uart0 register definition with the UART1 base and IRQ from this chapter.
|
||||||
|
*Do not use the UART0 convenience HAL or DMA route as a UART1 binding.*]
|
||||||
|
} else {register-section(entry.family,3)}
|
||||||
|
minor-title("Software sequence and recovery")
|
||||||
|
enum(..entry.steps.map(inline))
|
||||||
|
if entry.id!="uart1" {
|
||||||
|
let api=data.apis.at(entry.family)
|
||||||
|
if api.len()>0 {ds-table("api-"+entry.id,[#entry.title selected SDK interfaces],([Function],[Declaration]),
|
||||||
|
api.map(a=>(code(a.name),code(a.signature))),widths:(1fr,3fr))}
|
||||||
|
if entry.family in data.examples {
|
||||||
|
code-block(raw(data.examples.at(entry.family),block:true,lang:"c"),breakable:true)
|
||||||
|
text(9pt,fill:muted)[Syntax-checked API fragment; clocks, pins, memory and arguments must already be valid.
|
||||||
|
Submission is not necessarily transfer completion.]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
source-note(data.registers.at(entry.family).document,title:"Register and detailed interface contract")
|
||||||
|
context metadata((kind:"ip-end",id:entry.id,page:here().page()))
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,365 @@
|
|||||||
|
{
|
||||||
|
"xpi": {
|
||||||
|
"main.ID": 0,
|
||||||
|
"main.VERSION": 4,
|
||||||
|
"main.CAPABILITY": 8,
|
||||||
|
"main.CTRL": 12,
|
||||||
|
"main.STATUS": 16,
|
||||||
|
"main.COMMAND": 20,
|
||||||
|
"main.ERROR_STATE": 24,
|
||||||
|
"main.ERROR_ADDR": 28,
|
||||||
|
"main.ERROR_INFO": 32,
|
||||||
|
"main.INTR_STATE": 36,
|
||||||
|
"main.INTR_ENABLE": 40,
|
||||||
|
"main.INTR_STATUS": 44,
|
||||||
|
"main.INTR_TEST": 48,
|
||||||
|
"main.DMA_CTRL": 52,
|
||||||
|
"main.FIFO_CTRL": 56,
|
||||||
|
"main.FIFO_STATUS": 60,
|
||||||
|
"main.TXDATA": 64,
|
||||||
|
"main.RXDATA": 68,
|
||||||
|
"main.INDIRECT_ADDR": 72,
|
||||||
|
"main.INDIRECT_COUNT": 76,
|
||||||
|
"main.INDIRECT_CFG": 80,
|
||||||
|
"main.POLL_CFG": 84,
|
||||||
|
"main.POLL_MASK": 88,
|
||||||
|
"main.POLL_MATCH": 92,
|
||||||
|
"main.POLL_INTERVAL": 96,
|
||||||
|
"main.POLL_TIMEOUT": 100,
|
||||||
|
"main.PERF_CTRL": 104,
|
||||||
|
"main.PERF_AXI_READ_BYTES": 108,
|
||||||
|
"main.PERF_AXI_WRITE_BYTES": 112,
|
||||||
|
"main.PERF_PHY_BYTES": 116,
|
||||||
|
"main.PERF_COMMANDS": 120,
|
||||||
|
"main.PERF_STALL_CYCLES": 124,
|
||||||
|
"main.CONFIG_LOCK": 128,
|
||||||
|
"slot.CTRL": 0,
|
||||||
|
"slot.DEVICE_SIZE": 4,
|
||||||
|
"slot.SEQ_CFG": 8,
|
||||||
|
"slot.TIMING": 12,
|
||||||
|
"slot.TIMEOUT": 16,
|
||||||
|
"slot.BOUNDARY": 20,
|
||||||
|
"lut.LUT_WORD": 0
|
||||||
|
},
|
||||||
|
"sram": {
|
||||||
|
"main.IP_ID": 0,
|
||||||
|
"main.IP_VERSION": 4,
|
||||||
|
"main.CAPABILITY": 8,
|
||||||
|
"main.MEMORY_BYTES": 12,
|
||||||
|
"main.BANK_COUNT": 16,
|
||||||
|
"main.BANK_BYTES": 20,
|
||||||
|
"main.PERF_READ_REQUESTS": 32,
|
||||||
|
"main.PERF_WRITE_REQUESTS": 36,
|
||||||
|
"main.PERF_READ_BEATS": 40,
|
||||||
|
"main.PERF_WRITE_BEATS": 44,
|
||||||
|
"main.PERF_STALL_CYCLES": 48,
|
||||||
|
"main.PERF_ERROR_RESPONSES": 52
|
||||||
|
},
|
||||||
|
"gpio": {
|
||||||
|
"user.DATA_IN": 0,
|
||||||
|
"user.DATA_OUT": 4,
|
||||||
|
"user.OUT_SET": 8,
|
||||||
|
"user.OUT_CLEAR": 12,
|
||||||
|
"user.OUT_TOGGLE": 16,
|
||||||
|
"user.INTR_STATE": 20,
|
||||||
|
"user.INTR_STATUS": 24,
|
||||||
|
"user.INTR_ENABLE": 28,
|
||||||
|
"user.INTR_ENABLE_SET": 32,
|
||||||
|
"user.INTR_ENABLE_CLEAR": 36,
|
||||||
|
"admin.DATA_IN": 0,
|
||||||
|
"admin.DATA_OUT": 4,
|
||||||
|
"admin.OUT_SET": 8,
|
||||||
|
"admin.OUT_CLEAR": 12,
|
||||||
|
"admin.OUT_TOGGLE": 16,
|
||||||
|
"admin.OUTPUT_ENABLE": 20,
|
||||||
|
"admin.OE_SET": 24,
|
||||||
|
"admin.OE_CLEAR": 28,
|
||||||
|
"admin.OE_TOGGLE": 32,
|
||||||
|
"admin.OPEN_DRAIN": 36,
|
||||||
|
"admin.INPUT_CMOS": 40,
|
||||||
|
"admin.PULL_UP": 44,
|
||||||
|
"admin.PULL_DOWN": 48,
|
||||||
|
"admin.ALT_ENABLE": 52,
|
||||||
|
"admin.ALT_SELECT": 56,
|
||||||
|
"admin.USER_SELECT": 60,
|
||||||
|
"admin.USER_LOCK": 64,
|
||||||
|
"admin.USER_STATUS": 68,
|
||||||
|
"admin.USER_ACCESS_MASK": 72,
|
||||||
|
"admin.INTR_RISE_ENABLE": 76,
|
||||||
|
"admin.INTR_FALL_ENABLE": 80,
|
||||||
|
"admin.INTR_HIGH_ENABLE": 84,
|
||||||
|
"admin.INTR_LOW_ENABLE": 88,
|
||||||
|
"admin.INTR_ENABLE": 92,
|
||||||
|
"admin.INTR_STATE": 96,
|
||||||
|
"admin.INTR_STATUS": 100,
|
||||||
|
"admin.INTR_TEST": 104,
|
||||||
|
"admin.FILTER_ENABLE": 108,
|
||||||
|
"admin.FILTER_DIV": 112,
|
||||||
|
"admin.FILTER_COUNT": 116,
|
||||||
|
"admin.CONFIG_LOCK": 120,
|
||||||
|
"common.PAD_CAPABILITY": 244,
|
||||||
|
"common.IP_VERSION": 248,
|
||||||
|
"common.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"dma": {
|
||||||
|
"main.IP_ID": 0,
|
||||||
|
"main.IP_VERSION": 4,
|
||||||
|
"main.CAPABILITY": 8,
|
||||||
|
"main.GLOBAL_CTRL": 12,
|
||||||
|
"main.GLOBAL_STATUS": 16,
|
||||||
|
"main.IRQ_STATE": 20,
|
||||||
|
"main.IRQ_ENABLE": 24,
|
||||||
|
"main.IRQ_TEST": 28,
|
||||||
|
"main.ERROR_SUMMARY": 32,
|
||||||
|
"main.REQUEST_STATUS": 36,
|
||||||
|
"ch.CTRL": 0,
|
||||||
|
"ch.CFG": 4,
|
||||||
|
"ch.SRC_ADDR": 8,
|
||||||
|
"ch.DST_ADDR": 12,
|
||||||
|
"ch.BYTE_COUNT": 16,
|
||||||
|
"ch.REQUEST_SEL": 20,
|
||||||
|
"ch.BURST_CFG": 24,
|
||||||
|
"ch.EVENT_ENABLE": 28,
|
||||||
|
"ch.STATUS": 32,
|
||||||
|
"ch.EVENT_STATUS": 36,
|
||||||
|
"ch.ERROR_STATUS": 40,
|
||||||
|
"ch.ERROR_ADDR": 44,
|
||||||
|
"ch.CURRENT_SRC": 48,
|
||||||
|
"ch.CURRENT_DST": 52,
|
||||||
|
"ch.REMAINING": 56,
|
||||||
|
"ch.BYTES_DONE": 60,
|
||||||
|
"ch.STALL_CYCLES_LO": 64,
|
||||||
|
"ch.STALL_CYCLES_HI": 68,
|
||||||
|
"ch.TCD_HEAD": 72,
|
||||||
|
"ch.TCD_COUNT": 76,
|
||||||
|
"ch.CRC_EXPECTED": 80,
|
||||||
|
"ch.CRC_RESULT": 84
|
||||||
|
},
|
||||||
|
"timer": {
|
||||||
|
"main.CTRL": 0,
|
||||||
|
"main.LOAD": 4,
|
||||||
|
"main.VALUE": 8,
|
||||||
|
"main.BGLOAD": 12,
|
||||||
|
"main.PRESCALE": 16,
|
||||||
|
"main.COMPARE0": 20,
|
||||||
|
"main.COMPARE1": 24,
|
||||||
|
"main.STATUS": 28,
|
||||||
|
"main.INTR_STATE": 32,
|
||||||
|
"main.INTR_ENABLE": 36,
|
||||||
|
"main.INTR_STATUS": 40,
|
||||||
|
"main.INTR_TEST": 44,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"pwm": {
|
||||||
|
"main.CTRL": 0,
|
||||||
|
"main.STATUS": 4,
|
||||||
|
"main.COMMAND": 8,
|
||||||
|
"main.SAFETY_LOCK": 12,
|
||||||
|
"main.FAULT_CTRL": 16,
|
||||||
|
"main.FAULT_SAFE": 20,
|
||||||
|
"main.FAULT_STATUS": 24,
|
||||||
|
"main.FAULT_CLEAR": 28,
|
||||||
|
"main.INTR_STATE": 32,
|
||||||
|
"main.INTR_ENABLE": 36,
|
||||||
|
"main.INTR_TEST": 40,
|
||||||
|
"main.OUTPUT_STATUS": 44,
|
||||||
|
"main.CLOCK_HZ": 48,
|
||||||
|
"main.IP_ID": 244,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252,
|
||||||
|
"main.CAPTURE_CTRL": 1024,
|
||||||
|
"main.CAPTURE_DIVIDER": 1028,
|
||||||
|
"main.CAPTURE_COUNTER": 1032,
|
||||||
|
"main.CAPTURE_STATUS": 1036,
|
||||||
|
"main.CAPTURE0_CTRL": 1040,
|
||||||
|
"main.CAPTURE0_STATUS": 1044,
|
||||||
|
"main.CAPTURE0_DATA": 1048,
|
||||||
|
"main.CAPTURE1_CTRL": 1056,
|
||||||
|
"main.CAPTURE1_STATUS": 1060,
|
||||||
|
"main.CAPTURE1_DATA": 1064,
|
||||||
|
"timer.CTRL": 0,
|
||||||
|
"timer.DIVIDER": 4,
|
||||||
|
"timer.PERIOD": 8,
|
||||||
|
"timer.PHASE": 12,
|
||||||
|
"timer.COUNTER": 16,
|
||||||
|
"timer.ACTIVE_DIVIDER": 20,
|
||||||
|
"timer.ACTIVE_PERIOD": 24,
|
||||||
|
"timer.STATUS": 28,
|
||||||
|
"channel.CTRL": 0,
|
||||||
|
"channel.PHASE": 4,
|
||||||
|
"channel.DUTY": 8,
|
||||||
|
"channel.ACTION": 12,
|
||||||
|
"channel.FORCE": 16,
|
||||||
|
"channel.FADE_CTRL": 20,
|
||||||
|
"channel.FADE_TARGET": 24,
|
||||||
|
"channel.FADE_STEP": 28,
|
||||||
|
"channel.FADE_INTERVAL": 32,
|
||||||
|
"channel.FADE_STATUS": 36,
|
||||||
|
"channel.ACTIVE_PHASE": 40,
|
||||||
|
"channel.ACTIVE_DUTY": 44,
|
||||||
|
"channel.GAMMA_INDEX": 48,
|
||||||
|
"channel.GAMMA_TARGET": 52,
|
||||||
|
"channel.GAMMA_STEP": 56,
|
||||||
|
"channel.GAMMA_INTERVAL": 60,
|
||||||
|
"operator.CTRL": 0,
|
||||||
|
"operator.DEADTIME": 4,
|
||||||
|
"operator.CARRIER": 8,
|
||||||
|
"operator.STATUS": 12
|
||||||
|
},
|
||||||
|
"rtc": {
|
||||||
|
"main.CTRL": 0,
|
||||||
|
"main.STATUS": 4,
|
||||||
|
"main.COMMAND": 8,
|
||||||
|
"main.COMMAND_STATUS": 12,
|
||||||
|
"main.TIME_LOAD_LO": 16,
|
||||||
|
"main.TIME_LOAD_HI": 20,
|
||||||
|
"main.TIME_LOAD_SUBSEC": 24,
|
||||||
|
"main.SNAPSHOT_TIME_LO": 28,
|
||||||
|
"main.SNAPSHOT_TIME_HI": 32,
|
||||||
|
"main.SNAPSHOT_SUBSEC": 36,
|
||||||
|
"main.SECOND_CYCLES": 40,
|
||||||
|
"main.CALIB_PPM": 44,
|
||||||
|
"main.ALARM0_TIME_LO": 48,
|
||||||
|
"main.ALARM0_TIME_HI": 52,
|
||||||
|
"main.ALARM0_SUBSEC": 56,
|
||||||
|
"main.ALARM1_TIME_LO": 60,
|
||||||
|
"main.ALARM1_TIME_HI": 64,
|
||||||
|
"main.ALARM1_SUBSEC": 68,
|
||||||
|
"main.ALARM_ENABLE": 72,
|
||||||
|
"main.PERIOD_RELOAD": 76,
|
||||||
|
"main.PERIOD_CTRL": 80,
|
||||||
|
"main.EVENT_RAW": 84,
|
||||||
|
"main.EVENT_CLEAR": 88,
|
||||||
|
"main.EVENT_TEST": 92,
|
||||||
|
"main.INTR_ENABLE": 96,
|
||||||
|
"main.INTR_STATE": 100,
|
||||||
|
"main.WAKE_ENABLE": 104,
|
||||||
|
"main.WAKE_STATE": 108,
|
||||||
|
"main.CLOCK_HZ": 240,
|
||||||
|
"main.IP_ID": 244,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"wdg": {
|
||||||
|
"main.CTRL": 0,
|
||||||
|
"main.STATUS": 4,
|
||||||
|
"main.PRESCALE": 8,
|
||||||
|
"main.TIMEOUT": 12,
|
||||||
|
"main.WINDOW_MIN": 16,
|
||||||
|
"main.EARLY_WARNING": 20,
|
||||||
|
"main.CONFIG": 24,
|
||||||
|
"main.CONFIG_LOCK": 28,
|
||||||
|
"main.SERVICE": 32,
|
||||||
|
"main.COMMAND_STATUS": 36,
|
||||||
|
"main.COUNT_SNAPSHOT": 40,
|
||||||
|
"main.EVENT_RAW": 44,
|
||||||
|
"main.INTR_STATE": 48,
|
||||||
|
"main.INTR_ENABLE": 52,
|
||||||
|
"main.INTR_TEST": 56,
|
||||||
|
"main.RESET_CAUSE": 60,
|
||||||
|
"main.RESET_COUNT_SNAPSHOT": 64,
|
||||||
|
"main.CLOCK_HZ": 68,
|
||||||
|
"main.IP_ID": 244,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"uart": {
|
||||||
|
"main.BAUD_INT": 0,
|
||||||
|
"main.BAUD_FRAC": 4,
|
||||||
|
"main.LINE_CTRL": 8,
|
||||||
|
"main.CTRL": 12,
|
||||||
|
"main.TXDATA": 16,
|
||||||
|
"main.RXDATA": 20,
|
||||||
|
"main.STATUS": 24,
|
||||||
|
"main.FIFO_LEVEL": 28,
|
||||||
|
"main.FIFO_CTRL": 32,
|
||||||
|
"main.TX_WATERMARK": 36,
|
||||||
|
"main.RX_WATERMARK": 40,
|
||||||
|
"main.RX_TIMEOUT_BITS": 44,
|
||||||
|
"main.ERROR_STATUS": 48,
|
||||||
|
"main.INTR_STATE": 52,
|
||||||
|
"main.INTR_ENABLE": 56,
|
||||||
|
"main.INTR_STATUS": 60,
|
||||||
|
"main.INTR_TEST": 64,
|
||||||
|
"main.DMA_CTRL": 68,
|
||||||
|
"main.FLOW_CTRL": 72,
|
||||||
|
"main.RTS_WATERMARK": 76,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"i2c": {
|
||||||
|
"main.CTRL": 0,
|
||||||
|
"main.SCL_TIMING": 4,
|
||||||
|
"main.START_TIMING": 8,
|
||||||
|
"main.DATA_TIMING": 12,
|
||||||
|
"main.STOP_TIMING": 16,
|
||||||
|
"main.FILTER": 20,
|
||||||
|
"main.STRETCH_TIMEOUT": 24,
|
||||||
|
"main.BUS_IDLE_TIMEOUT": 28,
|
||||||
|
"main.COMMAND_TIMEOUT": 32,
|
||||||
|
"main.TARGET_ADDR": 36,
|
||||||
|
"main.DATA_CMD": 40,
|
||||||
|
"main.RXDATA": 44,
|
||||||
|
"main.STATUS": 48,
|
||||||
|
"main.FIFO_LEVEL": 52,
|
||||||
|
"main.COMMAND": 56,
|
||||||
|
"main.CMD_WATERMARK": 60,
|
||||||
|
"main.RX_WATERMARK": 64,
|
||||||
|
"main.ERROR_STATUS": 68,
|
||||||
|
"main.INTR_STATE": 72,
|
||||||
|
"main.INTR_ENABLE": 76,
|
||||||
|
"main.INTR_STATUS": 80,
|
||||||
|
"main.INTR_TEST": 84,
|
||||||
|
"main.LINE_STATE": 88,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"clint": {
|
||||||
|
"msip.MSIP": 0,
|
||||||
|
"compare.MTIMECMP": 16384,
|
||||||
|
"compare.MTIMECMP_HI": 16388,
|
||||||
|
"main.MTIME": 49144,
|
||||||
|
"main.MTIMEH": 49148
|
||||||
|
},
|
||||||
|
"archinfo": {
|
||||||
|
"main.COMPONENT_ID": 0,
|
||||||
|
"main.VENDOR_ID": 4,
|
||||||
|
"main.SOC_ID": 8,
|
||||||
|
"main.SOC_REVISION": 12,
|
||||||
|
"main.BUILD_ID_LO": 16,
|
||||||
|
"main.BUILD_ID_HI": 20,
|
||||||
|
"main.CONFIG_ID": 24,
|
||||||
|
"main.BUILD_STATUS": 28,
|
||||||
|
"main.REFERENCE_CLOCK": 32,
|
||||||
|
"main.SRAM_BYTES": 36,
|
||||||
|
"main.TOPOLOGY": 40,
|
||||||
|
"main.FEATURES0": 44,
|
||||||
|
"main.TECHNOLOGY": 48,
|
||||||
|
"main.DEVICE_ID_STATUS": 52,
|
||||||
|
"main.DEVICE_ID0": 56,
|
||||||
|
"main.DEVICE_ID1": 60,
|
||||||
|
"main.DEVICE_ID2": 64,
|
||||||
|
"main.DEVICE_ID3": 68,
|
||||||
|
"main.IP_VERSION": 248,
|
||||||
|
"main.CAPABILITY": 252
|
||||||
|
},
|
||||||
|
"sysctrl": {
|
||||||
|
"main.CORESEL": 0,
|
||||||
|
"main.IPSEL": 4,
|
||||||
|
"main.FAULT_STATUS": 16,
|
||||||
|
"main.FAULT_ADDR": 20,
|
||||||
|
"main.FAULT_COUNT": 24,
|
||||||
|
"main.USER_CORE_RESET": 32,
|
||||||
|
"main.USER_CORE_STATUS": 36,
|
||||||
|
"main.FAULT_MASTER": 40,
|
||||||
|
"main.FAULT_DETAIL": 44,
|
||||||
|
"main.PERF_CTRL": 64,
|
||||||
|
"main.PERF_MGMT_WAIT_LO": 68,
|
||||||
|
"main.PERF_MGMT_WAIT_HI": 72,
|
||||||
|
"main.PERF_DMA_WAIT_LO": 84,
|
||||||
|
"main.PERF_DMA_WAIT_HI": 88,
|
||||||
|
"main.TEST_STATUS": 132,
|
||||||
|
"main.RTC_WAKE_STATUS": 136
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,746 @@
|
|||||||
|
{
|
||||||
|
"xpi": {
|
||||||
|
"document": "docs/ip/xpi.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/storage/xpi_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/storage/xpi_reg.sv",
|
||||||
|
"rtl/ip/storage/xpi_pkg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Global XPI registers",
|
||||||
|
"pattern": "APB4_XPI__(?!SLOT_|LUT_).+",
|
||||||
|
"strip": "^APB4_XPI__",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "slot",
|
||||||
|
"title": "Chip-select slots",
|
||||||
|
"pattern": "APB4_XPI__SLOT_.+",
|
||||||
|
"strip": "^APB4_XPI__SLOT_",
|
||||||
|
"base": 256,
|
||||||
|
"stride": 32,
|
||||||
|
"count": 4,
|
||||||
|
"exclude": [
|
||||||
|
"APB4_XPI__SLOT_BASE",
|
||||||
|
"APB4_XPI__SLOT_STRIDE"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "lut",
|
||||||
|
"title": "LUT words",
|
||||||
|
"pattern": "(?!)",
|
||||||
|
"strip": "^",
|
||||||
|
"base": 512,
|
||||||
|
"stride": 4,
|
||||||
|
"count": 64
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"IRQ_STATE": [
|
||||||
|
"APB4_XPI__IRQ_"
|
||||||
|
],
|
||||||
|
"IRQ_ENABLE": [
|
||||||
|
"APB4_XPI__IRQ_"
|
||||||
|
],
|
||||||
|
"IRQ_TEST": [
|
||||||
|
"APB4_XPI__IRQ_"
|
||||||
|
],
|
||||||
|
"INTR_STATE": [
|
||||||
|
"XPI_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"XPI_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_STATUS": [
|
||||||
|
"XPI_INTR_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extra": [
|
||||||
|
{
|
||||||
|
"group": "lut",
|
||||||
|
"name": "LUT_WORD",
|
||||||
|
"offset": 0,
|
||||||
|
"access": "RW / locked",
|
||||||
|
"reset": "Boot LUT; other sequences STOP",
|
||||||
|
"description": "Two 16-bit instructions. Sequence n begins at word 4*n; PC7 must terminate or jump on CS. Bits 9:8 of each halfword are reserved.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "OPERAND0",
|
||||||
|
"lsb": 0,
|
||||||
|
"msb": 7,
|
||||||
|
"description": "Instruction operand.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed semantic field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reserved",
|
||||||
|
"lsb": 8,
|
||||||
|
"msb": 9,
|
||||||
|
"description": "Reserved; preserve zero when writing.",
|
||||||
|
"reset": "0",
|
||||||
|
"expression": "reserved"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PADS0",
|
||||||
|
"lsb": 10,
|
||||||
|
"msb": 11,
|
||||||
|
"description": "0 = one pad, 1 = two pads, 2 = four pads; 3 is invalid.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed semantic field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OPCODE0",
|
||||||
|
"lsb": 12,
|
||||||
|
"msb": 15,
|
||||||
|
"description": "0 STOP, 1 CMD, 2 ADDR, 3 MODE, 4 DUMMY, 5 TX, 6 RX, 7 JUMP_ON_CS.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed semantic field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OPERAND1",
|
||||||
|
"lsb": 16,
|
||||||
|
"msb": 23,
|
||||||
|
"description": "Instruction operand.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed semantic field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reserved",
|
||||||
|
"lsb": 24,
|
||||||
|
"msb": 25,
|
||||||
|
"description": "Reserved; preserve zero when writing.",
|
||||||
|
"reset": "0",
|
||||||
|
"expression": "reserved"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PADS1",
|
||||||
|
"lsb": 26,
|
||||||
|
"msb": 27,
|
||||||
|
"description": "0 = one pad, 1 = two pads, 2 = four pads; 3 is invalid.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed semantic field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "OPCODE1",
|
||||||
|
"lsb": 28,
|
||||||
|
"msb": 31,
|
||||||
|
"description": "0 STOP, 1 CMD, 2 ADDR, 3 MODE, 4 DUMMY, 5 TX, 6 RX, 7 JUMP_ON_CS.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed semantic field"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/storage/apb4_xpi.sv",
|
||||||
|
"rtl/ip/storage/xpi_clkgen.sv",
|
||||||
|
"rtl/ip/storage/xpi_core.sv",
|
||||||
|
"rtl/ip/storage/xpi_if.sv",
|
||||||
|
"rtl/ip/storage/xpi_indirect.sv",
|
||||||
|
"rtl/ip/storage/xpi_mm.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"sram": {
|
||||||
|
"document": "docs/ip/onchip-sram.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/memory/onchip_ram_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/memory/onchip_ram_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Present": 1,
|
||||||
|
"CapacityKiB": 128,
|
||||||
|
"BankCount": 32,
|
||||||
|
"BankBytes": 4096,
|
||||||
|
"DataBytes": 4
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "SRAM management",
|
||||||
|
"pattern": "APB4_ONCHIP_RAM__.+",
|
||||||
|
"strip": "^APB4_ONCHIP_RAM__",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_access": "RO",
|
||||||
|
"support": [],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"gpio": {
|
||||||
|
"document": "docs/ip/gpio.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/peripheral/gpio_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/peripheral/gpio_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"PinNum": 32,
|
||||||
|
"NumPins": 32,
|
||||||
|
"GPIO_NUM": 32
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "user",
|
||||||
|
"title": "User window",
|
||||||
|
"pattern": "APB4_GPIO_USER_.+",
|
||||||
|
"strip": "^APB4_GPIO_USER_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1,
|
||||||
|
"section": "User window"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "admin",
|
||||||
|
"title": "Management window",
|
||||||
|
"pattern": "APB4_GPIO_ADMIN_.+",
|
||||||
|
"strip": "^APB4_GPIO_ADMIN_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1,
|
||||||
|
"section": "Management window"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "common",
|
||||||
|
"title": "Common discovery registers",
|
||||||
|
"pattern": "APB4_GPIO_(PAD_CAPABILITY|IP_VERSION|CAPABILITY)",
|
||||||
|
"strip": "^APB4_GPIO_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/peripheral/apb4_gpio.sv",
|
||||||
|
"rtl/ip/peripheral/gpio_core.sv",
|
||||||
|
"rtl/ip/peripheral/gpio_if.sv",
|
||||||
|
"rtl/ip/peripheral/user_gpio_if.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"dma": {
|
||||||
|
"document": "docs/ip/dma.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/peripheral/dma_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/peripheral/dma_reg.sv",
|
||||||
|
"rtl/ip/peripheral/dma_pkg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"NumChannels": 4,
|
||||||
|
"ChannelIndexWidth": 2,
|
||||||
|
"EnableStreams": 0,
|
||||||
|
"DataWidth": 32,
|
||||||
|
"MaxBurstBeats": 16
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "APB4_DMA__(?!CH_).+",
|
||||||
|
"strip": "^APB4_DMA__",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ch",
|
||||||
|
"title": "DMA channel registers",
|
||||||
|
"pattern": "APB4_DMA__CH_.+",
|
||||||
|
"strip": "^APB4_DMA__CH_",
|
||||||
|
"base": 256,
|
||||||
|
"stride": 128,
|
||||||
|
"count": 4,
|
||||||
|
"exclude": [
|
||||||
|
"APB4_DMA__CH_BASE",
|
||||||
|
"APB4_DMA__CH_STRIDE",
|
||||||
|
"APB4_DMA__CH_BASE_OFFSET"
|
||||||
|
],
|
||||||
|
"doc_name_prefix": "CH_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"EVENT_ENABLE": [
|
||||||
|
"APB4_DMA__EVENT_"
|
||||||
|
],
|
||||||
|
"EVENT_STATUS": [
|
||||||
|
"APB4_DMA__EVENT_"
|
||||||
|
],
|
||||||
|
"STATUS": [
|
||||||
|
"APB4_DMA__STATUS_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/peripheral/apb4_dma.sv",
|
||||||
|
"rtl/ip/peripheral/dma_axi4_master.sv",
|
||||||
|
"rtl/ip/peripheral/dma_core.sv",
|
||||||
|
"rtl/ip/peripheral/dma_req_if.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"timer": {
|
||||||
|
"document": "docs/ip/timer.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/peripheral/timer_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/peripheral/timer_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "APB4_TIMER_.+",
|
||||||
|
"strip": "^APB4_TIMER_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"INTR_STATE": [
|
||||||
|
"TIMER_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"TIMER_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_STATUS": [
|
||||||
|
"TIMER_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_TEST": [
|
||||||
|
"TIMER_INTR_"
|
||||||
|
],
|
||||||
|
"ERROR_STATUS": [
|
||||||
|
"TIMER_ERROR_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/peripheral/apb4_timer.sv",
|
||||||
|
"rtl/ip/peripheral/timer_core.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"pwm": {
|
||||||
|
"document": "rtl/managed/clusterip/pwm/doc/datasheet.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_reg.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_pkg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"PCLK_HZ": 24000000,
|
||||||
|
"RTC_CLOCK_HZ": 24000000,
|
||||||
|
"WDG_CLOCK_HZ": 24000000,
|
||||||
|
"FIFO_DEPTH": 8
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "PWM_(?!TIMER_)(?!CHANNEL_)(?!OPERATOR_).+_OFFSET",
|
||||||
|
"strip": "^PWM_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "timer",
|
||||||
|
"title": "TIMER registers",
|
||||||
|
"pattern": "PWM_TIMER_.+",
|
||||||
|
"strip": "^PWM_TIMER_",
|
||||||
|
"base": 256,
|
||||||
|
"stride": 64,
|
||||||
|
"count": 2,
|
||||||
|
"exclude": [
|
||||||
|
"PWM_TIMER_BASE",
|
||||||
|
"PWM_TIMER_STRIDE",
|
||||||
|
"PWM_TIMER_BASE_OFFSET"
|
||||||
|
],
|
||||||
|
"section": "Timer Blocks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "channel",
|
||||||
|
"title": "CHANNEL registers",
|
||||||
|
"pattern": "PWM_CHANNEL_.+",
|
||||||
|
"strip": "^PWM_CHANNEL_",
|
||||||
|
"base": 512,
|
||||||
|
"stride": 64,
|
||||||
|
"count": 4,
|
||||||
|
"exclude": [
|
||||||
|
"PWM_CHANNEL_BASE",
|
||||||
|
"PWM_CHANNEL_STRIDE",
|
||||||
|
"PWM_CHANNEL_BASE_OFFSET"
|
||||||
|
],
|
||||||
|
"section": "Channel Blocks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "operator",
|
||||||
|
"title": "OPERATOR registers",
|
||||||
|
"pattern": "PWM_OPERATOR_.+",
|
||||||
|
"strip": "^PWM_OPERATOR_",
|
||||||
|
"base": 768,
|
||||||
|
"stride": 64,
|
||||||
|
"count": 2,
|
||||||
|
"exclude": [
|
||||||
|
"PWM_OPERATOR_BASE",
|
||||||
|
"PWM_OPERATOR_STRIDE",
|
||||||
|
"PWM_OPERATOR_BASE_OFFSET"
|
||||||
|
],
|
||||||
|
"section": "Operator Blocks"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"INTR_STATE": [
|
||||||
|
"PWM_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"PWM_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_TEST": [
|
||||||
|
"PWM_INTR_"
|
||||||
|
],
|
||||||
|
"CAPTURE0_CTRL": [
|
||||||
|
"PWM_CAPTURE_CH_"
|
||||||
|
],
|
||||||
|
"CAPTURE1_CTRL": [
|
||||||
|
"PWM_CAPTURE_CH_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/apb4_pwm.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_capture.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_carrier.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_channel.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_core.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_deadtime.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_if.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_tickgen.sv",
|
||||||
|
"rtl/managed/clusterip/pwm/rtl/pwm_timer.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"rtc": {
|
||||||
|
"document": "rtl/managed/clusterip/rtc/doc/datasheet.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_reg.sv",
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_pkg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"PCLK_HZ": 24000000,
|
||||||
|
"RTC_CLOCK_HZ": 24000000,
|
||||||
|
"WDG_CLOCK_HZ": 24000000,
|
||||||
|
"FIFO_DEPTH": 8
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "RTC_.+_OFFSET",
|
||||||
|
"strip": "^RTC_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"INTR_STATE": [
|
||||||
|
"RTC_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"RTC_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_TEST": [
|
||||||
|
"RTC_INTR_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/apb4_rtc.sv",
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_core.sv",
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_if.sv",
|
||||||
|
"rtl/managed/clusterip/rtc/rtl/rtc_tickgen.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"wdg": {
|
||||||
|
"document": "rtl/managed/clusterip/wdg/doc/datasheet.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_reg.sv",
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_pkg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"PCLK_HZ": 24000000,
|
||||||
|
"RTC_CLOCK_HZ": 24000000,
|
||||||
|
"WDG_CLOCK_HZ": 24000000,
|
||||||
|
"FIFO_DEPTH": 8
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "WDG_.+_OFFSET",
|
||||||
|
"strip": "^WDG_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"INTR_STATE": [
|
||||||
|
"WDG_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"WDG_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_TEST": [
|
||||||
|
"WDG_INTR_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/apb4_wdg.sv",
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_core.sv",
|
||||||
|
"rtl/managed/clusterip/wdg/rtl/wdg_if.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"uart": {
|
||||||
|
"document": "docs/ip/uart.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/serial/apb4_uart_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/serial/uart_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"TxFifoDepth": 64,
|
||||||
|
"RxFifoDepth": 64,
|
||||||
|
"TxFifoLogDepth": 6,
|
||||||
|
"RxFifoLogDepth": 6
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "APB4_UART_.+",
|
||||||
|
"strip": "^APB4_UART_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"LINE_CTRL": [
|
||||||
|
"UART_LINE_"
|
||||||
|
],
|
||||||
|
"CTRL": [
|
||||||
|
"UART_CTRL_"
|
||||||
|
],
|
||||||
|
"STATUS": [
|
||||||
|
"UART_STATUS_"
|
||||||
|
],
|
||||||
|
"FIFO_CTRL": [
|
||||||
|
"UART_FIFO_CTRL_"
|
||||||
|
],
|
||||||
|
"RXDATA": [
|
||||||
|
"UART_RXDATA_"
|
||||||
|
],
|
||||||
|
"ERROR_STATUS": [
|
||||||
|
"UART_ERROR_"
|
||||||
|
],
|
||||||
|
"INTR_STATE": [
|
||||||
|
"UART_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"UART_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_STATUS": [
|
||||||
|
"UART_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_TEST": [
|
||||||
|
"UART_INTR_"
|
||||||
|
],
|
||||||
|
"DMA_CTRL": [
|
||||||
|
"UART_DMA_"
|
||||||
|
],
|
||||||
|
"FLOW_CTRL": [
|
||||||
|
"UART_FLOW_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/serial/apb4_uart.sv",
|
||||||
|
"rtl/ip/serial/apb4_uart_rx.sv",
|
||||||
|
"rtl/ip/serial/apb4_uart_tx.sv",
|
||||||
|
"rtl/ip/serial/uart_baudgen.sv",
|
||||||
|
"rtl/ip/serial/uart_core.sv",
|
||||||
|
"rtl/ip/serial/uart_flow_ctrl.sv",
|
||||||
|
"rtl/ip/serial/uart_if.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"i2c": {
|
||||||
|
"document": "docs/ip/i2c.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/serial/apb4_i2c_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/serial/i2c_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "APB4_I2C_.+",
|
||||||
|
"strip": "^APB4_I2C_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"field_aliases": {
|
||||||
|
"INTR_STATE": [
|
||||||
|
"I2C_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_ENABLE": [
|
||||||
|
"I2C_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_STATUS": [
|
||||||
|
"I2C_INTR_"
|
||||||
|
],
|
||||||
|
"INTR_TEST": [
|
||||||
|
"I2C_INTR_"
|
||||||
|
],
|
||||||
|
"ERROR_STATUS": [
|
||||||
|
"I2C_ERROR_"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/serial/apb4_i2c.sv",
|
||||||
|
"rtl/ip/serial/i2c_core.sv",
|
||||||
|
"rtl/ip/serial/i2c_filter.sv",
|
||||||
|
"rtl/ip/serial/i2c_if.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"clint": {
|
||||||
|
"document": "docs/ip/clint.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/ip/peripheral/clint_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/ip/peripheral/clint_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"HartNum": 1
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "msip",
|
||||||
|
"title": "Per-hart software interrupt",
|
||||||
|
"pattern": "APB4_CLINT_MSIP",
|
||||||
|
"strip": "^APB4_CLINT_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 4,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "compare",
|
||||||
|
"title": "Per-hart timer comparator",
|
||||||
|
"pattern": "APB4_CLINT_MTIMECMP",
|
||||||
|
"strip": "^APB4_CLINT_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 8,
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Shared 64-bit time",
|
||||||
|
"pattern": "APB4_CLINT_MTIMEH?",
|
||||||
|
"strip": "^APB4_CLINT_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extra": [
|
||||||
|
{
|
||||||
|
"group": "compare",
|
||||||
|
"name": "MTIMECMP_HI",
|
||||||
|
"offset": 16388,
|
||||||
|
"access": "RW",
|
||||||
|
"reset": "0xFFFFFFFF",
|
||||||
|
"description": "High word of the per-hart 64-bit machine timer comparator.",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "VALUE",
|
||||||
|
"lsb": 0,
|
||||||
|
"msb": 31,
|
||||||
|
"description": "Comparator bits 63:32.",
|
||||||
|
"reset": "Register reset applies",
|
||||||
|
"expression": "reviewed field"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"support": [
|
||||||
|
"rtl/ip/peripheral/apb4_clint.sv",
|
||||||
|
"rtl/ip/peripheral/clint_core.sv",
|
||||||
|
"rtl/ip/peripheral/clint_if.sv",
|
||||||
|
"rtl/ip/peripheral/clint_timebase.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
},
|
||||||
|
"archinfo": {
|
||||||
|
"document": "rtl/managed/clusterip/archinfo/doc/datasheet.md",
|
||||||
|
"defines": [
|
||||||
|
"rtl/managed/clusterip/archinfo/rtl/archinfo_define.svh"
|
||||||
|
],
|
||||||
|
"rtl": [
|
||||||
|
"rtl/managed/clusterip/archinfo/rtl/archinfo_reg.sv"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"REFERENCE_CLOCK_HZ": 24000000,
|
||||||
|
"SRAM_BYTES": 131072,
|
||||||
|
"TOPOLOGY": 538968065,
|
||||||
|
"FEATURES0": 32766,
|
||||||
|
"TECHNOLOGY": 33620098
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "main",
|
||||||
|
"title": "Register bank",
|
||||||
|
"pattern": "ARCHINFO_.+_OFFSET",
|
||||||
|
"strip": "^ARCHINFO_",
|
||||||
|
"base": 0,
|
||||||
|
"stride": 0,
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_access": "RO",
|
||||||
|
"support": [
|
||||||
|
"rtl/managed/clusterip/archinfo/rtl/apb4_archinfo.sv"
|
||||||
|
],
|
||||||
|
"memory_map": "rtl/tiny/address_map/memory_map.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
{
|
||||||
|
"regions": {
|
||||||
|
"FLASH": [
|
||||||
|
0,
|
||||||
|
16777216
|
||||||
|
],
|
||||||
|
"APB4_GPIO": [
|
||||||
|
268435456,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_UART0": [
|
||||||
|
268439552,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_TIM0": [
|
||||||
|
268443648,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_TIM1": [
|
||||||
|
268447744,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_I2C0": [
|
||||||
|
268460032,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_XPI": [
|
||||||
|
268472320,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_DMA": [
|
||||||
|
268476416,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_SYSCTRL": [
|
||||||
|
268480512,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_CLINT": [
|
||||||
|
268566528,
|
||||||
|
65536
|
||||||
|
],
|
||||||
|
"APB4_I2C1": [
|
||||||
|
268505088,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_GPIO_ADMIN": [
|
||||||
|
268517376,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_SRAM": [
|
||||||
|
268529664,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_UART1": [
|
||||||
|
268533760,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_ARCHINFO": [
|
||||||
|
536870912,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_PWM": [
|
||||||
|
536879104,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_RTC": [
|
||||||
|
536887296,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"APB4_WDG": [
|
||||||
|
536891392,
|
||||||
|
4096
|
||||||
|
],
|
||||||
|
"SRAM": [
|
||||||
|
805306368,
|
||||||
|
131072
|
||||||
|
],
|
||||||
|
"XPI": [
|
||||||
|
1342177280,
|
||||||
|
268435456
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"interrupts": {
|
||||||
|
"clint_software": 0,
|
||||||
|
"clint_timer": 1,
|
||||||
|
"uart0": 2,
|
||||||
|
"timer0": 3,
|
||||||
|
"timer1": 4,
|
||||||
|
"i2c0": 7,
|
||||||
|
"xpi": 9,
|
||||||
|
"pwm": 11,
|
||||||
|
"rtc": 13,
|
||||||
|
"watchdog_early_warning": 14,
|
||||||
|
"gpio": 18,
|
||||||
|
"i2c1": 19,
|
||||||
|
"dma": 20,
|
||||||
|
"uart1": 26
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
"rtl/ip/core/mgmt_core_wrapper.sv",
|
||||||
|
"rtl/ip/core/mgmt_debug_wrapper.sv",
|
||||||
|
"rtl/ip/core/mgmt_debug_reset.sv",
|
||||||
|
"rtl/ip/interconnect/ahbl2axi4.sv",
|
||||||
|
"crt/arch/riscv/startup.S",
|
||||||
|
"crt/linker/ld2_all_sram.lds",
|
||||||
|
"crt/src/hal/dma_math.c",
|
||||||
|
"crt/src/hal/sysctrl.c",
|
||||||
|
"crt/src/core/archinfo.c",
|
||||||
|
"app/apps/ci_smoke/tiny.c",
|
||||||
|
"app/apps/ci_smoke/mcu_acceptance.S",
|
||||||
|
"app/apps/bringup/tiny.mk",
|
||||||
|
"app/apps/ci_smoke/tiny.mk",
|
||||||
|
"scripts/generate_tiny.py",
|
||||||
|
"tests/test_tiny.py"
|
||||||
|
],
|
||||||
|
"bindings": [
|
||||||
|
{
|
||||||
|
"file": "rtl/tiny/top/tiny_axi4_fabric.sv",
|
||||||
|
"text": [
|
||||||
|
"(s_selected_addr.len <= 8'd15)",
|
||||||
|
"(s_last_addr[31:12] == s_selected_addr.addr[31:12])",
|
||||||
|
"s_accept = (s_state_q == Idle) && s_selected_valid;",
|
||||||
|
"Response: if (s_terminal) s_state_d = Idle;",
|
||||||
|
"s_tgt_d = s_protocol_legal ? s_decoded_target : 3'd4;",
|
||||||
|
"assign s_protocol_legal = !s_selected_addr.lock && (s_selected_addr.size <= 3'd2) &&\n ((s_selected_addr.addr & ((32'd1 << s_selected_addr.size) - 32'd1)) == 32'd0) &&\n ((s_selected_addr.burst == 2'b01) ||\n ((s_selected_addr.burst == 2'b00) && (s_selected_addr.len == 8'd0))) &&\n (s_selected_addr.len <= 8'd15) && !s_last_addr[32] &&\n (s_last_addr[31:12] == s_selected_addr.addr[31:12]) &&\n (decode_target(\n s_last_addr[31:0]\n ) == s_decoded_target) && ((s_decoded_target != 3'd2) || (s_selected_addr.len == 8'd0));",
|
||||||
|
"assign s_terminal = (s_state_q == Response) &&\n (s_write_q ? (s_target_bvalid[s_tgt_q] && s_bready[s_owner_q]) :\n (s_target_rvalid[s_tgt_q] && s_rready[s_owner_q] && s_target_rlast[s_tgt_q]));"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "crt/src/hal/dma_math.c",
|
||||||
|
"text": [
|
||||||
|
"#ifdef RS_SOC_TINY\n if (config->kind != RS_DMA_KIND_MM_TO_MM) {\n return false;\n }\n#endif"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "crt/arch/riscv/startup.S",
|
||||||
|
"text": [
|
||||||
|
"#ifndef RS_SOC_TINY"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "rtl/tiny/top/retrosoc_tiny.sv",
|
||||||
|
"text": [
|
||||||
|
".RTC_CLOCK_HZ(24_000_000)",
|
||||||
|
".WDG_CLOCK_HZ(24_000_000)",
|
||||||
|
".RESET_PULSE_CYCLES(8)",
|
||||||
|
"assign u_uart1_if.cts_n_i = 1'b0;"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "rtl/ip/peripheral/dma_reg.sv",
|
||||||
|
"text": [
|
||||||
|
"`APB4_DMA__CAPABILITY:\n s_read_data = {\n 1'b0,\n (EnableStreams ? 3'd3 : 3'd0),\n 4'(MaxBurstBeats),\n 8'(DataWidth),\n 8'(NumChannels),\n 7'd0,\n 1'b1\n };"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "crt/include/retrosoc/hal/dma.h",
|
||||||
|
"text": [
|
||||||
|
"#define RS_DMA_TCD_VALID UINT32_C(0x00000001)",
|
||||||
|
"#define RS_DMA_TCD_SRC_INC UINT32_C(0x00000002)",
|
||||||
|
"#define RS_DMA_TCD_DST_INC UINT32_C(0x00000004)",
|
||||||
|
"#define RS_DMA_TCD_CRC_ENABLE UINT32_C(0x00000008)",
|
||||||
|
"#define RS_DMA_TCD_CRC_FINAL UINT32_C(0x00000010)",
|
||||||
|
"#define RS_DMA_TCD_INT_DONE UINT32_C(0x00000020)",
|
||||||
|
"#define RS_DMA_TCD_INT_ERROR UINT32_C(0x00000040)",
|
||||||
|
"#define RS_DMA_TCD_KIND_SHIFT 8U",
|
||||||
|
"#define RS_DMA_TCD_REQUEST_SHIFT 12U",
|
||||||
|
"#define RS_DMA_TCD_PRIORITY_SHIFT 16U",
|
||||||
|
"#define RS_DMA_TCD_BURST_SHIFT 20U"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "crt/src/hal/dma.c",
|
||||||
|
"text": [
|
||||||
|
"if (burst == UINT32_C(0)) {\n burst = RS_DMA_MAX_BURST_BEATS;\n }",
|
||||||
|
"RS_DMA_CH_REG(channel, RS_DMA_CH_REG_TCD_COUNT) = UINT32_C(1);",
|
||||||
|
"current = (rs_dma_tcd_t *)(uintptr_t)current->next_ptr;"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{
|
||||||
|
"schema_version": 1,
|
||||||
|
"state": "frozen",
|
||||||
|
"ip_ids": [
|
||||||
|
"xpi",
|
||||||
|
"sram",
|
||||||
|
"gpio",
|
||||||
|
"dma",
|
||||||
|
"timer",
|
||||||
|
"pwm",
|
||||||
|
"rtc",
|
||||||
|
"wdg",
|
||||||
|
"uart0",
|
||||||
|
"uart1",
|
||||||
|
"i2c",
|
||||||
|
"sysctrl",
|
||||||
|
"clint",
|
||||||
|
"archinfo"
|
||||||
|
],
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"level": 1,
|
||||||
|
"title": "Product Brief",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Features",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Configuration and Scope",
|
||||||
|
"anchor": "tiny-configuration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Reading and Evidence Conventions",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 1,
|
||||||
|
"title": "System Architecture",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Processor and Integration",
|
||||||
|
"anchor": "tiny-core"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Clock, Reset and Operating State",
|
||||||
|
"anchor": "tiny-clock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Bus Transactions, Arbitration and Errors",
|
||||||
|
"anchor": "tiny-bus"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Memory Map and Access Conventions",
|
||||||
|
"anchor": "tiny-addresses"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Interrupt and DMA Request Routing",
|
||||||
|
"anchor": "tiny-irq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Peripherals",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "XPI Universal Controller",
|
||||||
|
"anchor": "xpi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "On-Chip SRAM",
|
||||||
|
"anchor": "sram"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "General-Purpose Input/Output (GPIO)",
|
||||||
|
"anchor": "gpio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Direct Memory Access (DMA)",
|
||||||
|
"anchor": "dma"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "General-Purpose Timers (TIM0, TIM1)",
|
||||||
|
"anchor": "timer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Pulse Width Modulation (PWM)",
|
||||||
|
"anchor": "pwm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Real-Time Clock (RTC)",
|
||||||
|
"anchor": "rtc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Watchdog (WDG)",
|
||||||
|
"anchor": "wdg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Management UART (UART0)",
|
||||||
|
"anchor": "uart0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Second UART (UART1)",
|
||||||
|
"anchor": "uart1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Inter-Integrated Circuit (I2C0, I2C1)",
|
||||||
|
"anchor": "i2c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "System Controller (SYSCTRL)",
|
||||||
|
"anchor": "sysctrl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Local Interrupt Controller (CLINT)",
|
||||||
|
"anchor": "clint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 3,
|
||||||
|
"title": "Architecture Information (ARCHINFO)",
|
||||||
|
"anchor": "archinfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Pins and Board Connections",
|
||||||
|
"anchor": "tiny-pins"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Electrical, Package and Reliability Boundaries",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 1,
|
||||||
|
"title": "Software",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Reset, Boot and Linker Layout",
|
||||||
|
"anchor": "tiny-boot"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "SDK, API Completion and Polling",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "DMA Descriptors and Buffer Lifetime",
|
||||||
|
"anchor": "tiny-dma-format"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Acceptance and Diagnostics",
|
||||||
|
"anchor": "tiny-diagnostics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 1,
|
||||||
|
"title": "Implementation and Evidence",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Reproducible Development Flow",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Verification Status and Qualification Gaps",
|
||||||
|
"anchor": "tiny-evidence"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 1,
|
||||||
|
"title": "Appendix: Global Register Index",
|
||||||
|
"anchor": "tiny-register-index"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 1,
|
||||||
|
"title": "Document Control",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Revision History",
|
||||||
|
"anchor": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"level": 2,
|
||||||
|
"title": "Sources, License and Contact",
|
||||||
|
"anchor": "publication-provenance"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Tiny datasheet layout supplement
|
||||||
|
|
||||||
|
Tiny follows the reviewed [Mini visual language](../style.md), but owns an
|
||||||
|
independent [style implementation](style.typ). This addition does not edit,
|
||||||
|
import or reflow Mini's template. Changes to Tiny must preserve the publication
|
||||||
|
isolation boundary rather than inserting fake Mini/HP data into its model.
|
||||||
|
|
||||||
|
Use A4 portrait, 19 mm horizontal margins, a 172 mm body, 10.5 pt Inter body,
|
||||||
|
9 pt tables/source links and Fira Code identifiers, and the existing gold/gray
|
||||||
|
palette. Headings, table continuation notices, Contents hierarchy, figure/table
|
||||||
|
directories and underlined links follow the approved layout. Short critical
|
||||||
|
phrases use true Inter Bold, not an extra font-weight increment. Code remains
|
||||||
|
regular. Continuation notices retain the existing 8.5 pt exception.
|
||||||
|
|
||||||
|
The cover title is `retroSoC Tiny` (30 pt) / `Gen1` (23 pt). The 13 pt positioning
|
||||||
|
line is `An Open-Source, Single-Core RISC-V MCU`. The Rill bilingual metadata,
|
||||||
|
version/date, author email and maintainer retain the established two-line grid.
|
||||||
|
Keep the right-hand title area empty for a logo. The Chinese label uses the
|
||||||
|
locked Noto CJK Bold and the same cap-edge compensation as the approved cover.
|
||||||
|
|
||||||
|
Available is full pale gold; Functional uses a vertical left-half fill;
|
||||||
|
Reproduced/Tapedout are empty. Marks remain manually controlled prototype status,
|
||||||
|
not a score derived from a document build or historical test count.
|
||||||
|
|
||||||
|
Every IP starts on a new page. Shared UART layouts have one canonical reference
|
||||||
|
with instance-specific address, IRQ and software qualifications. GPIO views and
|
||||||
|
DMA/PWM repeated groups preserve exact base/stride/index formulas. A constant
|
||||||
|
zero capability field must not be mislabeled reserved when it has defined
|
||||||
|
semantics, such as Tiny's truncated DMA maximum-burst discovery field.
|
||||||
|
|
||||||
|
Use native Typst/CeTZ vector structure diagrams and the existing locked waveform
|
||||||
|
renderer. Keep labels at least 9 pt; Tiny has no need to invent compact bus CDC
|
||||||
|
boxes. Explain diagram abstraction: functional control reach is not a complete
|
||||||
|
netlist, and representative waveforms are not measured cycle timings. Register
|
||||||
|
bit layouts and the source-derived 64-byte DMA diagram must match their tables.
|
||||||
|
|
||||||
|
Keep implementation, configuration, available tests, executed results and
|
||||||
|
physical qualification separate. In particular, preserve the negative timing
|
||||||
|
record and external-device qualification gaps. Record full source/asset hashes,
|
||||||
|
font/package revisions, PDF digest, page map and actual verification results.
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
// Tiny-owned layout snapshot of the approved Mini style; Mini is not imported or modified.
|
||||||
|
#let ink = rgb("#292C31")
|
||||||
|
#let gold = rgb("#A38442")
|
||||||
|
#let pale-gold = rgb("#F4EFE4")
|
||||||
|
#let gray = rgb("#F1F2F3")
|
||||||
|
#let rule = rgb("#D5D6D8")
|
||||||
|
#let muted = rgb("#62666C")
|
||||||
|
#let link-color = rgb("#245A81")
|
||||||
|
#let inline-code-color = rgb("#B12146")
|
||||||
|
#let table-stroke = 0.6pt + black
|
||||||
|
#let mono = "FiraCode Nerd Font"
|
||||||
|
#let data = json(sys.inputs.at("data"))
|
||||||
|
#let doc = data.document
|
||||||
|
#let ip-chapters = json("chapter-index.json")
|
||||||
|
// RM0486-inspired spacing, adapted to the retained Inter sizes. Inter's
|
||||||
|
// cap-height-to-baseline box is 7.639 pt at 10.5 pt and 6.548 pt at 9 pt.
|
||||||
|
// Thus these leading values yield 12.6 / 10.8 pt baseline pitches, rather
|
||||||
|
// than treating Typst's `leading` as the whole distance between baselines.
|
||||||
|
#let rhythm = (
|
||||||
|
body-leading: 4.96pt, body-spacing: 11.26pt,
|
||||||
|
small-leading: 4.25pt, small-spacing: 8.25pt,
|
||||||
|
heading-before: (28pt,24pt,18pt,12pt,10pt),
|
||||||
|
heading-after: (18pt,10pt,8pt,6pt,5pt),
|
||||||
|
heading-gap: 8pt, minor-before: 12pt, minor-after: 6pt,
|
||||||
|
register-before: 12pt, register-after: 4pt,
|
||||||
|
list-spacing: 7.96pt, list-indent: 0pt, list-body-indent: 8pt,
|
||||||
|
figure-space: 12pt, figure-caption: 6pt, table-caption: 4pt,
|
||||||
|
table-inset: (x:6pt,y:5pt), table-notes: 6pt,
|
||||||
|
continuation-size: 8.5pt, continuation-gap: 2pt,
|
||||||
|
code-space: 8pt, code-inset: 8pt,
|
||||||
|
note-space: 12pt, note-inset: 10pt,
|
||||||
|
metadata-before: 4pt, metadata-after: 6pt,
|
||||||
|
bits-space: 6pt, bits-inset: (x:3pt,y:5pt),
|
||||||
|
toc-leading: 9.09pt, toc-indent: 14pt, toc-group-before: 12pt, toc-group-after: 4pt,
|
||||||
|
header-gap: 4pt,
|
||||||
|
cover-leading: 4.96pt, cover-spacing: 3.675pt, cover-list-spacing: 7.96pt,
|
||||||
|
cover-heading-before: (12pt,12pt), cover-heading-after: 6pt,
|
||||||
|
cover-gap: 3pt, cover-meta-gap: 4pt, cover-rule-gap: 2pt,
|
||||||
|
cover-gutter: 7mm, cover-list-indent: 0.5em,
|
||||||
|
diagram-leading: 0.4em, inventory-leading: 0.35em,
|
||||||
|
)
|
||||||
|
|
||||||
|
#let heading-layout(it, before: none, after: none) = {
|
||||||
|
if it.outlined and it.numbering != none and it.level <= 2 {
|
||||||
|
context {
|
||||||
|
let preceding = query(selector(heading).before(it.location(),inclusive:false)).filter(h=>h.outlined and h.numbering!=none)
|
||||||
|
let chapters = preceding.filter(h=>h.level==1)
|
||||||
|
let product-brief = if it.level==1 {it.body==[Product Brief]}
|
||||||
|
else {chapters.len()>0 and chapters.last().body==[Product Brief]}
|
||||||
|
// Chapter openers and their first section form one heading group.
|
||||||
|
// Subsequent sections break independently, even when their text is short.
|
||||||
|
let chapter-opener = it.level==2 and preceding.len()>0 and preceding.last().level==1
|
||||||
|
if not product-brief and not chapter-opener {pagebreak(weak:true)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let index = calc.min(it.level - 1,4)
|
||||||
|
let sizes = (21pt,16pt,13pt,11pt,10.5pt)
|
||||||
|
block(above: if before == none {rhythm.heading-before.at(index)} else {before},
|
||||||
|
below: if after == none {rhythm.heading-after.at(index)} else {after}, sticky:true)[
|
||||||
|
#set text(size:sizes.at(index),weight:"semibold")
|
||||||
|
#set par(leading:rhythm.body-leading,spacing:0pt)
|
||||||
|
#if it.numbering == none { it.body } else {
|
||||||
|
grid(columns:(auto,1fr),column-gutter:rhythm.heading-gap,
|
||||||
|
text(fill:gold,counter(heading).display(it.numbering)),it.body)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let minor-title(body) = block(above:rhythm.minor-before,below:rhythm.minor-after,sticky:true,strong(body))
|
||||||
|
// Only this editing round emits active ranges; earlier markers remain historical.
|
||||||
|
#let current-change(id) = id.starts-with(doc.change_prefix) or id in (
|
||||||
|
"contents","table-directory","figure-directory")
|
||||||
|
#let change-start(id, title, category:"modified") = context {
|
||||||
|
if current-change(id) {metadata((
|
||||||
|
kind:"publication-change-start",id:id,title:title,category:category,page:here().page()))}
|
||||||
|
}
|
||||||
|
#let change-end(id) = context {
|
||||||
|
if current-change(id) {metadata((kind:"publication-change-end",id:id,page:here().page()))}
|
||||||
|
}
|
||||||
|
#let code-block(body, breakable:false) = block(above:rhythm.code-space,below:rhythm.code-space,
|
||||||
|
width:100%,inset:rhythm.code-inset,fill:gray,breakable:breakable)[
|
||||||
|
#set par(leading:rhythm.small-leading,spacing:rhythm.small-spacing)
|
||||||
|
#body
|
||||||
|
]
|
||||||
|
#let artifact-mark(state) = {
|
||||||
|
assert(state in ("empty", "left-half", "full"), message:"Unknown artifact mark")
|
||||||
|
box(width:3mm,height:3mm)[
|
||||||
|
#place(top + left,circle(radius:1.5mm,fill:white,stroke:none))
|
||||||
|
#if state=="left-half" {
|
||||||
|
place(top + left,box(width:1.5mm,height:3mm,clip:true)[
|
||||||
|
#circle(radius:1.5mm,fill:pale-gold,stroke:none)
|
||||||
|
])
|
||||||
|
} else if state=="full" {
|
||||||
|
place(top + left,circle(radius:1.5mm,fill:pale-gold,stroke:none))
|
||||||
|
}
|
||||||
|
#place(top + left,circle(radius:1.5mm,fill:none,
|
||||||
|
stroke:0.6pt + if state=="empty" {ink} else {gold}))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
#let artifact-evaluation(states) = rect(width:92mm,height:10mm,radius:1.5mm,
|
||||||
|
fill:white,stroke:0.6pt + ink,inset:(x:2mm,y:1mm))[
|
||||||
|
#set text(size:9pt,weight:"regular",fill:ink)
|
||||||
|
#set par(leading:rhythm.small-leading,spacing:0pt)
|
||||||
|
#align(center + horizon)[
|
||||||
|
#grid(columns:(1fr,1fr,1fr,1fr),row-gutter:2pt,align:center,
|
||||||
|
..states.map(state=>state.name),
|
||||||
|
..states.map(state=>artifact-mark(state.fill)),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
#let cover(body, evaluation:none) = {
|
||||||
|
show heading: it => {
|
||||||
|
if it.level==1 and it.body==[Product Brief] and evaluation!=none {
|
||||||
|
block(width:100%,above:rhythm.cover-heading-before.first(),
|
||||||
|
below:rhythm.cover-heading-after,sticky:true)[
|
||||||
|
#grid(columns:(1fr,92mm),column-gutter:4mm,row-gutter:2pt,
|
||||||
|
align(horizon,heading-layout(it,before:0pt,after:0pt)),
|
||||||
|
artifact-evaluation(evaluation),
|
||||||
|
[], align(center,text(9pt,weight:"regular",fill:black)[Artifact Evaluation]),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
heading-layout(it,
|
||||||
|
before:rhythm.cover-heading-before.at(calc.min(it.level - 1,1)),
|
||||||
|
after:rhythm.cover-heading-after)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body
|
||||||
|
}
|
||||||
|
|
||||||
|
#let omit-link-underline(it) = it.body
|
||||||
|
#let repository-footer-link() = context {
|
||||||
|
show underline: omit-link-underline
|
||||||
|
show link: set text(fill:link-color,weight:"regular")
|
||||||
|
let body=text(font:"Inter",size:9pt,weight:"regular",fill:link-color)[https://github.com/retroSoC/retroSoC]
|
||||||
|
link("https://github.com/retroSoC/retroSoC",body)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let contents(depth:5) = {
|
||||||
|
set text(size:9.5pt,fill:ink)
|
||||||
|
show link: set text(fill:ink)
|
||||||
|
show underline: omit-link-underline
|
||||||
|
set par(leading:rhythm.toc-leading,spacing:rhythm.toc-leading)
|
||||||
|
set outline(indent:rhythm.toc-indent)
|
||||||
|
show outline.entry: it => context {
|
||||||
|
let peers = query(heading).filter(h=>h.outlined and h.level==it.level and h.numbering!=none)
|
||||||
|
let widths = peers.map(h=>measure(text(weight:if it.level==1 {"bold"} else {"regular"},
|
||||||
|
numbering(h.numbering,..counter(heading).at(h.location())))).width)
|
||||||
|
let prefix-width = calc.max(0pt,..widths)
|
||||||
|
let prefix = if it.prefix()==none {none} else if it.level==1 {strong(it.prefix())} else {it.prefix()}
|
||||||
|
// Scope emphasis by the section ancestor, not by a hard-coded chapter number.
|
||||||
|
let ancestors = query(selector(heading).before(it.element.location(),inclusive:true)).filter(h=>h.level<=2)
|
||||||
|
let peripheral = ancestors.len()>0 and ancestors.last().body==[Peripherals]
|
||||||
|
let inner = if it.level==1 {
|
||||||
|
[#strong(it.body())#h(0.5em)#box(width:1fr,it.fill)#h(0.5em)#it.page()]
|
||||||
|
} else if peripheral {
|
||||||
|
// Keep parentheses, separators and qualifiers such as "partial" regular.
|
||||||
|
show regex("\\([^()]*\\)"): group => {
|
||||||
|
show regex("\\b[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*\\b"): strong
|
||||||
|
group
|
||||||
|
}
|
||||||
|
it.inner()
|
||||||
|
} else {it.inner()}
|
||||||
|
block(above:rhythm.toc-leading + if it.level==1 {rhythm.toc-group-before} else {0pt},
|
||||||
|
below:rhythm.toc-leading + if it.level==1 {rhythm.toc-group-after} else {0pt},breakable:false)[
|
||||||
|
#link(it.element.location(),it.indented(
|
||||||
|
if prefix==none {none} else {box(width:prefix-width,prefix)},inner,gap:rhythm.heading-gap))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
outline(title:[Contents],depth:depth)
|
||||||
|
}
|
||||||
|
#let figure-directory(tables:false) = context {
|
||||||
|
set text(font:"Inter",size:9.5pt,fill:ink,weight:"regular")
|
||||||
|
show link: set text(fill:ink)
|
||||||
|
show underline: omit-link-underline
|
||||||
|
set par(leading:rhythm.toc-leading,spacing:rhythm.toc-leading)
|
||||||
|
set outline(indent:0pt)
|
||||||
|
show outline: set heading(bookmarked:true)
|
||||||
|
// Include every numbered non-table figure, including native vector diagrams.
|
||||||
|
// Continuation notices and unnumbered register layouts are not figure entries.
|
||||||
|
let included(f) = (f.kind==table)==tables and f.numbering!=none and f.caption!=none
|
||||||
|
let prefix(entry) = [#(entry.prefix()).]
|
||||||
|
let peers = query(figure).filter(included)
|
||||||
|
let widths = peers.map(f=>measure(prefix(outline.entry(1,f))).width)
|
||||||
|
let prefix-width = calc.max(0pt,..widths)
|
||||||
|
show outline.entry: it => {
|
||||||
|
if included(it.element) {
|
||||||
|
block(above:rhythm.toc-leading,below:rhythm.toc-leading,breakable:false)[
|
||||||
|
#link(it.element.location(),it.indented(
|
||||||
|
box(width:prefix-width,prefix(it)),it.inner(),gap:rhythm.heading-gap))
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outline(title:if tables {[List of tables]} else {[List of figures]},target:figure)
|
||||||
|
}
|
||||||
|
#let inline-code(body) = context {
|
||||||
|
// Link scopes already select link-color; keep that semantic cue and underline.
|
||||||
|
set text(fill:if text.fill==link-color {link-color} else {inline-code-color})
|
||||||
|
body
|
||||||
|
}
|
||||||
|
#let code(value) = inline-code(text(font: mono, size: 9pt, weight:"regular",
|
||||||
|
value.replace("_", "_\u{200b}").replace(".", ".\u{200b}")))
|
||||||
|
#let source(path, title: "Interface and implementation reference", line:none) = {
|
||||||
|
// Publication metadata can postdate the frozen hardware commit. Route readers
|
||||||
|
// to the delivered manifest instead of constructing a nonexistent old blob.
|
||||||
|
if path.starts-with("publications/") {
|
||||||
|
return link(<publication-provenance>,text(size:9pt,title))
|
||||||
|
}
|
||||||
|
let managed = data.managed_sources.find(s=>path.starts-with(s.destination + "/"))
|
||||||
|
let url = if managed == none {
|
||||||
|
"https://github.com/retroSoC/retroSoC/blob/" + doc.source_revision + "/" + path
|
||||||
|
} else {
|
||||||
|
managed.url.trim(".git", at:end) + "/blob/" + managed.revision + "/" + path.slice(managed.destination.len()+1)
|
||||||
|
}
|
||||||
|
if line!=none { url += "#L"+str(line) }
|
||||||
|
link(url, text(size: 9pt, title))
|
||||||
|
}
|
||||||
|
#let source-note(path, title:"Interface and implementation reference", line:none) = block(
|
||||||
|
above:rhythm.metadata-before,below:rhythm.metadata-after,breakable:false)[
|
||||||
|
#set par(leading:rhythm.small-leading,spacing:rhythm.small-spacing)
|
||||||
|
#source(path,title:title,line:line)
|
||||||
|
]
|
||||||
|
#let note(body, title: "Integration note", below: rhythm.note-space) = block(
|
||||||
|
width: 100%, inset: rhythm.note-inset, fill: pale-gold, stroke: (left: 2pt + gold),
|
||||||
|
above: rhythm.note-space, below: below, breakable: false,
|
||||||
|
)[#text(weight: "semibold", title) #linebreak() #body]
|
||||||
|
#let tbd(body) = note(body, title: "TBD - specification not available")
|
||||||
|
#let placeholder(title, height: 35mm) = block(
|
||||||
|
width: 100%, height: height, stroke: 0.7pt + rule, fill: gray,
|
||||||
|
inset: rhythm.note-inset, above: rhythm.note-space, below: rhythm.note-space, breakable: false,
|
||||||
|
)[#align(center + horizon)[#text(weight: "semibold", title) \
|
||||||
|
#text(9pt, fill: muted)[TBD - awaiting reviewed engineering data]]]
|
||||||
|
|
||||||
|
// Frame each row at its outer edges without adding internal vertical rules.
|
||||||
|
// Repeated header rows supply the top edge on every continued table segment.
|
||||||
|
#let table-rules(columns, header-row:0) = (x,y) => (
|
||||||
|
top: if y==header-row {table-stroke} else {none},
|
||||||
|
bottom: table-stroke,
|
||||||
|
left: if x==0 {table-stroke} else {none},
|
||||||
|
right: if x==columns - 1 {table-stroke} else {none},
|
||||||
|
)
|
||||||
|
|
||||||
|
#let continuation-notice() = layout(size => {
|
||||||
|
let body = block(width:100%,above:0pt,below:0pt)[
|
||||||
|
#set text(size:rhythm.continuation-size,weight:"regular",fill:black,
|
||||||
|
top-edge:"ascender",bottom-edge:"descender")
|
||||||
|
#set par(leading:4pt,spacing:0pt)
|
||||||
|
(continued)
|
||||||
|
]
|
||||||
|
let extent = measure(body,width:size.width)
|
||||||
|
block(width:100%,above:rhythm.table-caption,below:0pt,
|
||||||
|
inset:(bottom:rhythm.continuation-gap))[
|
||||||
|
#block(width:extent.width,height:extent.height,above:0pt,below:0pt,body)<table-continuation-region>
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
#let ds-table(key, caption, headers, rows, widths: auto, notes: none) = {
|
||||||
|
let anchor = label("start-" + key)
|
||||||
|
figure(
|
||||||
|
kind: table, supplement: [Table], caption: caption,
|
||||||
|
block(breakable: rows.len() > 8, width: 100%)[
|
||||||
|
#metadata(none)#anchor
|
||||||
|
#set text(size: 9pt)
|
||||||
|
#set par(leading: rhythm.small-leading, spacing: rhythm.small-spacing)
|
||||||
|
#table(
|
||||||
|
columns: if widths == auto { headers.len() } else { widths },
|
||||||
|
inset: rhythm.table-inset,
|
||||||
|
stroke: table-rules(headers.len(),header-row:1),
|
||||||
|
fill: (x, y) => if y == 1 { gray } else { none },
|
||||||
|
align: left + horizon,
|
||||||
|
table.header(repeat: true,
|
||||||
|
table.cell(colspan: headers.len(), inset: 0pt,
|
||||||
|
stroke:(top:none,bottom:table-stroke,left:none,right:none),fill: white)[
|
||||||
|
#context if here().page() > query(anchor).first().location().page() {
|
||||||
|
continuation-notice()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
..headers.map(h => table.cell(fill: gray, text(weight: "semibold", h))),
|
||||||
|
),
|
||||||
|
..rows.map(row => row.map(v => table.cell(breakable: false, v))).flatten(),
|
||||||
|
)
|
||||||
|
#if notes != none { block(above: rhythm.table-notes, breakable: false, text(9pt, fill: muted, notes)) }
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let ip(id) = {
|
||||||
|
let entry = data.catalog.find(e => e.id == id)
|
||||||
|
let windows = data.regions.filter(r => r.symbol in entry.regions)
|
||||||
|
let irqs = data.interrupts.filter(i => i.name in entry.irqs)
|
||||||
|
block(above: rhythm.metadata-before, below: rhythm.metadata-after, breakable: false, sticky: true)[
|
||||||
|
#entry.summary
|
||||||
|
#set text(size: 9pt)
|
||||||
|
#set par(leading: rhythm.small-leading, spacing: rhythm.small-spacing)
|
||||||
|
#table(columns: (1.3fr, 1fr, 0.7fr), inset: rhythm.table-inset,
|
||||||
|
stroke: table-rules(3),
|
||||||
|
table.header(..([Address window],[Base address],[Size]).map(h=>
|
||||||
|
table.cell(fill:gray,text(weight:"semibold",h)))),
|
||||||
|
..windows.map(r => (code(r.symbol), code(r.base_hex), r.size_label)).flatten(),
|
||||||
|
)
|
||||||
|
#block(above: rhythm.metadata-before)[
|
||||||
|
*Core IRQ bit:* #if irqs.len() == 0 { [No dedicated source.] } else {
|
||||||
|
irqs.map(i => str(i.core_bit) + " (" + i.name.replace("_", " ") + ")").join("; ")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
#for (n, p) in entry.sources.enumerate() {
|
||||||
|
source(p, title: if n == 0 { "Detailed contract / source" } else { "Additional source" })
|
||||||
|
if n < entry.sources.len() - 1 { [ · ] }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let template(body) = {
|
||||||
|
set document(title: doc.title, author: doc.author,
|
||||||
|
keywords: ("retroSoC", "Tiny", "Gen1", "datasheet", doc.document_id, "v"+doc.version, doc.status))
|
||||||
|
set text(font: "Inter", size: 10.5pt, fill: ink, lang: "en", weight: "regular")
|
||||||
|
show strong: it => text(weight:"bold",it.body)
|
||||||
|
set par(justify: false, leading: rhythm.body-leading, spacing: rhythm.body-spacing,first-line-indent:0pt)
|
||||||
|
set list(indent:rhythm.list-indent,body-indent:rhythm.list-body-indent,spacing:rhythm.list-spacing)
|
||||||
|
set enum(indent:rhythm.list-indent,body-indent:rhythm.list-body-indent,spacing:rhythm.list-spacing)
|
||||||
|
set page(paper: "a4", margin: (x: 19mm, top: 21mm, bottom: 20mm),
|
||||||
|
header: context {
|
||||||
|
let hs = query(heading.where(level: 1)).filter(h => h.location().page() <= here().page())
|
||||||
|
// Heading locations avoid the extra layout pass needed by page-valued metadata.
|
||||||
|
// A later chapter/section heading closes the preceding IP's header scope.
|
||||||
|
let navigation=query(selector(heading.where(level:1)).or(heading.where(level:2),
|
||||||
|
..ip-chapters.map(chapter=>label(chapter.id))))
|
||||||
|
.filter(h=>h.location().page()<=here().page())
|
||||||
|
let active=if navigation.len()==0 {none} else {
|
||||||
|
ip-chapters.find(chapter=>label(chapter.id)==navigation.last().fields().at("label",default:none))
|
||||||
|
}
|
||||||
|
set text(size: 9pt, fill: muted)
|
||||||
|
grid(columns: (1fr, 1fr), [#doc.brand_name · #doc.title], align(right)[
|
||||||
|
#if active!=none { upper(active.id) } else if hs.len() > 0 { hs.last().body } else { [Product datasheet] }
|
||||||
|
])
|
||||||
|
v(rhythm.header-gap)
|
||||||
|
line(length: 100%, stroke: 0.6pt + gold)
|
||||||
|
},
|
||||||
|
footer: context {
|
||||||
|
set text(size: 9pt, fill: muted)
|
||||||
|
set par(leading:0pt,spacing:0pt)
|
||||||
|
line(length: 100%, stroke: 0.35pt + rule)
|
||||||
|
v(rhythm.header-gap)
|
||||||
|
grid(columns: (1fr, auto, 1fr),
|
||||||
|
repository-footer-link(),
|
||||||
|
[#counter(page).display() / #counter(page).final().first()],
|
||||||
|
align(right)[#doc.date],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
set heading(numbering: "1.1.1.1.1", outlined: true)
|
||||||
|
show heading: heading-layout
|
||||||
|
show raw: set text(font: mono, size: 9pt, weight:"regular")
|
||||||
|
show raw.where(block:false): inline-code
|
||||||
|
show link: set text(fill:link-color)
|
||||||
|
show link: it => underline(stroke:0.4pt + link-color,offset:2pt,it)
|
||||||
|
show ref: it => {
|
||||||
|
if it.element!=none and it.element.func()==heading and it.form=="normal" {
|
||||||
|
set text(fill:link-color)
|
||||||
|
show underline: omit-link-underline
|
||||||
|
it
|
||||||
|
} else {it}
|
||||||
|
}
|
||||||
|
set figure(gap: rhythm.figure-caption)
|
||||||
|
show figure: set block(above:rhythm.figure-space,below:rhythm.figure-space,breakable:false)
|
||||||
|
set figure.caption(position:top)
|
||||||
|
show figure.where(kind: table): set figure(gap:rhythm.table-caption)
|
||||||
|
show figure.where(kind: table): set block(breakable: true)
|
||||||
|
show figure.caption: set text(size: 9pt, fill: black)
|
||||||
|
show figure.caption: it => block(above:0pt,below:0pt,sticky:it.position==top,it)
|
||||||
|
body
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"title": "retroSoC Tiny Gen1",
|
||||||
|
"document_id": "RS-TINY-DS",
|
||||||
|
"brand_name": "Rill",
|
||||||
|
"brand_name_zh": "清溪",
|
||||||
|
"version": "0.1",
|
||||||
|
"status": "DRAFT",
|
||||||
|
"date": "2026-09-25",
|
||||||
|
"author": "Yuchi Miao",
|
||||||
|
"author_email": "miaoyuchi@ict.ac.cn",
|
||||||
|
"maintainer": "ECOS Team, ICT, CAS",
|
||||||
|
"source_revision": "9c706ba0ff1da1028c299cfff573010a86dfd7ce",
|
||||||
|
"profile": "configs/ci/ihp130-tiny.mk",
|
||||||
|
"change_prefix": "tiny-v01-",
|
||||||
|
"entrypoint": "publications/datasheets/tiny/datasheet.typ",
|
||||||
|
"filename": "retrosoc-tiny-gen1-datasheet.pdf"
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,56 @@
|
|||||||
|
#import "@preview/jogs:0.2.4": compile-js, call-js-function
|
||||||
|
#import "style.typ": ink, gold, muted, data, rhythm
|
||||||
|
#let wave-bytecode=compile-js(read(data.wave_renderer))
|
||||||
|
|
||||||
|
// Thread the palette index through nested WaveDrom groups without mutating
|
||||||
|
// the source dictionary. Codes 2-9 and '=' differ only in bus fill color.
|
||||||
|
#let color-signals(signals, start: 0) = {
|
||||||
|
let next = start
|
||||||
|
let colored = ()
|
||||||
|
for signal in signals {
|
||||||
|
if type(signal) == array {
|
||||||
|
let (group, index) = color-signals(signal, start: next)
|
||||||
|
colored.push(group)
|
||||||
|
next = index
|
||||||
|
} else if type(signal) == dictionary and signal.at("wave", default: "").contains(regex("[=2-9]")) {
|
||||||
|
let row = signal
|
||||||
|
row.wave = row.wave.replace(regex("[=2-9]"), str(3 + calc.rem(next, 4)))
|
||||||
|
colored.push(row)
|
||||||
|
next += 1
|
||||||
|
} else {
|
||||||
|
colored.push(signal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(colored, next)
|
||||||
|
}
|
||||||
|
|
||||||
|
#let color-wave-source(source) = {
|
||||||
|
let colored = source
|
||||||
|
if "signal" in source { colored.signal = color-signals(source.signal).first() }
|
||||||
|
colored
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the pinned SVG renderer and its geometry; override only valid bus fills.
|
||||||
|
// Signal names and edges remain readable independently of color.
|
||||||
|
#let wave-svg(key) = {
|
||||||
|
let entry = data.waveforms.at(key)
|
||||||
|
let svg = call-js-function(wave-bytecode,"wavy",json.encode(color-wave-source(entry.source)))
|
||||||
|
svg = svg.replace(regex("font-family:[^;]+;"), "font-family:Inter;")
|
||||||
|
svg = svg.replace(regex("font-family=\"[^\"]+\""), "font-family=\"Inter\"")
|
||||||
|
svg = svg.replace(regex("(?i)#(?:0041c4|0000ff|00f)\\b"), "#292C31")
|
||||||
|
svg = svg.replace("fill:blue", "fill:#292C31")
|
||||||
|
svg.replace("</style>", "text{font-weight:400}.info{font-weight:400;font-style:normal}.s8{fill:#F4EFE4}.s9{fill:#E7EFF6}.s10{fill:#E8F1EA}.s11{fill:#EEEAF5}</style>")
|
||||||
|
}
|
||||||
|
|
||||||
|
#let timing(key, caption, instance:none) = {
|
||||||
|
let entry = data.waveforms.at(key)
|
||||||
|
block(width:100%,above:rhythm.figure-space,below:rhythm.figure-space,breakable:false)[
|
||||||
|
#context metadata((kind:"tiny-diagram",id:"wave-"+if instance==none {key} else {instance},page:here().page()))
|
||||||
|
#show figure: set block(above:0pt,below:0pt)
|
||||||
|
#figure(image(bytes(wave-svg(key))), caption: caption)
|
||||||
|
#block(width:100%,above:rhythm.metadata-before,below:0pt)[
|
||||||
|
#set par(leading:rhythm.small-leading,spacing:rhythm.small-spacing)
|
||||||
|
#align(center,text(9pt,fill:muted,entry.note))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include <retrosoc/core/archinfo.h>
|
||||||
|
|
||||||
|
rs_status_t example_archinfo(rs_archinfo_t *info)
|
||||||
|
{
|
||||||
|
rs_status_t result = rs_archinfo_read(info);
|
||||||
|
if (result != RS_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return rs_archinfo_validate_build(info);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <retrosoc/hal/clint.h>
|
||||||
|
|
||||||
|
rs_status_t example_clint(uint64_t *time)
|
||||||
|
{
|
||||||
|
return rs_clint_get_time(time);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#include <retrosoc/hal/dma.h>
|
||||||
|
|
||||||
|
rs_status_t example_dma(uint32_t channel, const rs_dma_config_t *config)
|
||||||
|
{
|
||||||
|
rs_status_t result = rs_dma_configure(channel, config);
|
||||||
|
if (result != RS_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return rs_dma_start(channel);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <retrosoc/hal/gpio.h>
|
||||||
|
|
||||||
|
rs_status_t example_gpio(uint32_t pin, const rs_gpio_config_t *config)
|
||||||
|
{
|
||||||
|
return rs_gpio_configure(pin, config);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <retrosoc/hal/i2c.h>
|
||||||
|
|
||||||
|
rs_status_t example_i2c(rs_i2c_bus_t bus, uint32_t source_clock_hz, uint32_t bus_hz)
|
||||||
|
{
|
||||||
|
return rs_i2c_init(bus, source_clock_hz, bus_hz);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <retrosoc/hal/pwm.h>
|
||||||
|
|
||||||
|
rs_status_t example_pwm(void)
|
||||||
|
{
|
||||||
|
return rs_pwm_probe();
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <retrosoc/hal/rtc.h>
|
||||||
|
|
||||||
|
rs_status_t example_rtc(void)
|
||||||
|
{
|
||||||
|
return rs_rtc_probe();
|
||||||
|
}
|
||||||
Loaded 100 of 253 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user