mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat(spi): implement DMA V2.2 paced FIFO integration
Add five-bit SPI DMA request support, capability discovery, source-qualified pacing, descriptor cancellation guards, and focused P2 evidence. Keep Tiny and Mini product instances default-disabled until product integration. Validation: focused SPI-P2, DMA parity, SPI parity, and SPI-P1 checks passed (53 passed, 10 deselected). Full product integration, post-edit full Pytest, and physical qualification remain pending.
This commit is contained in:
28 files changed
+804
-82
No files matched your search
@@ -541,6 +541,11 @@ spi-p1-lint spi-p1-rtl spi-p1-formal spi-p1-synth spi-p1-netsim spi-p1-sta spi-p
|
||||
$(if $(SPI_P1_COMMON_ROOT),--common-variant-root $(SPI_P1_COMMON_ROOT),) \
|
||||
$(if $(filter spi-p1-formal,$@),$(if $(SPI_P1_FORMAL_REUSE_ROOT),--formal-reuse-root $(SPI_P1_FORMAL_REUSE_ROOT),),)
|
||||
|
||||
.PHONY: spi-p2-lint spi-p2-rtl spi-p2-report
|
||||
spi-p2-lint spi-p2-rtl spi-p2-report: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/spi_p2.py $(patsubst spi-p2-%,%,$@) \
|
||||
--variant-root $(VARIANT_ROOT) --pdk $(PDK)
|
||||
|
||||
.PHONY: crypto-p0-constants crypto-p0-rtl crypto-p0-baseline crypto-p0-report
|
||||
crypto-p0-constants: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/crypto_p0.py constants --variant-root $(VARIANT_ROOT)
|
||||
|
||||
@@ -41,6 +41,8 @@ typedef enum {
|
||||
RS_DMA_REQUEST_DVP_RX = 11,
|
||||
RS_DMA_REQUEST_CRYPTO_IN = 12,
|
||||
RS_DMA_REQUEST_CRYPTO_OUT = 13,
|
||||
RS_DMA_REQUEST_SPI_TX = 16,
|
||||
RS_DMA_REQUEST_SPI_RX = 17,
|
||||
} rs_dma_request_t;
|
||||
|
||||
typedef enum {
|
||||
@@ -135,10 +137,12 @@ rs_status_t rs_dma_session_release(rs_dma_session_t *session);
|
||||
#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_REQUEST_HIGH UINT32_C(0x02000000)
|
||||
#define RS_DMA_TCD_PRIORITY_SHIFT 16U
|
||||
#define RS_DMA_TCD_BURST_SHIFT 20U
|
||||
|
||||
rs_status_t rs_dma_config_validate(uint32_t channel, const rs_dma_config_t *config);
|
||||
rs_status_t rs_dma_supports_request(rs_dma_request_t request);
|
||||
rs_status_t rs_dma_configure(uint32_t channel, const rs_dma_config_t *config);
|
||||
rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd);
|
||||
rs_status_t rs_dma_submit_tcd(uint32_t channel, rs_dma_tcd_t *tcd, rs_timeout_t timeout);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define RS_DMA_REG_IRQ_TEST UINT32_C(0x01C)
|
||||
#define RS_DMA_REG_ERROR_SUMMARY UINT32_C(0x020)
|
||||
#define RS_DMA_REG_REQUEST_STATUS UINT32_C(0x024)
|
||||
#define RS_DMA_REG_SUPPORTED_REQUESTS UINT32_C(0x028)
|
||||
#define RS_DMA_IP_VERSION_V22 UINT32_C(0x00020002)
|
||||
|
||||
#define RS_DMA_CH_BASE UINT32_C(0x100)
|
||||
#define RS_DMA_CH_STRIDE UINT32_C(0x080)
|
||||
|
||||
@@ -44,11 +44,37 @@ static bool rs_dma_channel_valid(uint32_t channel) {
|
||||
return channel < RS_DMA_CHANNEL_COUNT;
|
||||
}
|
||||
|
||||
rs_status_t rs_dma_supports_request(rs_dma_request_t request) {
|
||||
uint32_t version;
|
||||
uint32_t supported;
|
||||
uint32_t bit;
|
||||
|
||||
if ((uint32_t)request < 16U) {
|
||||
return RS_OK;
|
||||
}
|
||||
if ((uint32_t)request >= 32U) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
version = RS_DMA_REG(RS_DMA_REG_IP_VERSION);
|
||||
if (version < RS_DMA_IP_VERSION_V22) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
supported = RS_DMA_REG(RS_DMA_REG_SUPPORTED_REQUESTS);
|
||||
bit = UINT32_C(1) << (uint32_t)request;
|
||||
return ((supported & bit) != UINT32_C(0)) ? RS_OK : RS_ENOTSUP;
|
||||
}
|
||||
|
||||
static rs_status_t rs_dma_configure_owned(uint32_t channel, const rs_dma_config_t *config,
|
||||
const rs_dma_session_t *owner) {
|
||||
uint32_t channel_config;
|
||||
uint32_t saved;
|
||||
|
||||
if ((channel >= RS_DMA_CHANNEL_COUNT) || (config == NULL)) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if (rs_dma_supports_request(config->request) != RS_OK) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
if (rs_dma_config_validate(channel, config) != RS_OK) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
@@ -207,6 +233,9 @@ rs_status_t rs_dma_submit_tcd_chain(uint32_t channel, rs_dma_tcd_t *first, uint3
|
||||
}
|
||||
config.kind = (rs_dma_transfer_kind_t)((control >> RS_DMA_TCD_KIND_SHIFT) & UINT32_C(0x7));
|
||||
config.request = (rs_dma_request_t)((control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF));
|
||||
if ((control & RS_DMA_TCD_REQUEST_HIGH) != UINT32_C(0)) {
|
||||
config.request = (rs_dma_request_t)((uint32_t)config.request | UINT32_C(0x10));
|
||||
}
|
||||
config.source = (uintptr_t)current->source;
|
||||
config.destination = (uintptr_t)current->destination;
|
||||
config.byte_count = current->byte_count;
|
||||
|
||||
+37
-5
@@ -19,13 +19,24 @@ 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) {
|
||||
#ifdef RS_SOC_TINY
|
||||
if (((uint32_t)config->request >= 16U) ||
|
||||
((UINT32_C(0x39FF) & (UINT32_C(1) << (uint32_t)config->request)) == 0U)) {
|
||||
const uint32_t request_mask = UINT32_C(0x000339FF);
|
||||
if (((uint32_t)config->request >= 32U) ||
|
||||
((request_mask & (UINT32_C(1) << (uint32_t)config->request)) == 0U)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
switch (config->kind) {
|
||||
case RS_DMA_KIND_MM_TO_MM:
|
||||
#ifdef RS_SOC_TINY
|
||||
if ((config->request == RS_DMA_REQUEST_SPI_TX) ||
|
||||
(config->request == RS_DMA_REQUEST_SPI_RX)) {
|
||||
return ((config->byte_count % sizeof(uint32_t)) == 0U) &&
|
||||
(((config->request == RS_DMA_REQUEST_SPI_TX) && config->source_increment &&
|
||||
!config->destination_increment) ||
|
||||
((config->request == RS_DMA_REQUEST_SPI_RX) && !config->source_increment &&
|
||||
config->destination_increment));
|
||||
}
|
||||
#endif
|
||||
return (config->request == RS_DMA_REQUEST_SOFTWARE) ||
|
||||
((config->request >= RS_DMA_REQUEST_XPI_TX) &&
|
||||
(config->request <= RS_DMA_REQUEST_I2C1_RX));
|
||||
@@ -96,14 +107,18 @@ 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);
|
||||
request = (tcd->control >> RS_DMA_TCD_REQUEST_SHIFT) & UINT32_C(0xF);
|
||||
if ((tcd->control & RS_DMA_TCD_REQUEST_HIGH) != UINT32_C(0)) {
|
||||
request |= UINT32_C(0x10);
|
||||
}
|
||||
burst = (tcd->control >> RS_DMA_TCD_BURST_SHIFT) & UINT32_C(0x1F);
|
||||
#ifdef RS_SOC_TINY
|
||||
if ((UINT32_C(0x39FF) & (UINT32_C(1) << request)) == 0U) {
|
||||
const uint32_t request_mask = UINT32_C(0x000339FF);
|
||||
if ((request >= 32U) || ((request_mask & (UINT32_C(1) << request)) == 0U)) {
|
||||
return RS_ENOTSUP;
|
||||
}
|
||||
#endif
|
||||
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_SPI_RX) || (burst > RS_DMA_MAX_BURST_BEATS) ||
|
||||
((tcd->source & UINT32_C(0x3)) != UINT32_C(0)) ||
|
||||
((tcd->destination & UINT32_C(0x3)) != UINT32_C(0))) {
|
||||
return RS_EINVAL;
|
||||
@@ -113,11 +128,28 @@ rs_status_t rs_dma_tcd_validate(uint32_t channel, const rs_dma_tcd_t *tcd) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) &&
|
||||
!((request == (uint32_t)RS_DMA_REQUEST_SOFTWARE) ||
|
||||
!(
|
||||
#ifdef RS_SOC_TINY
|
||||
(request == (uint32_t)RS_DMA_REQUEST_SPI_TX) ||
|
||||
(request == (uint32_t)RS_DMA_REQUEST_SPI_RX) ||
|
||||
#endif
|
||||
(request == (uint32_t)RS_DMA_REQUEST_SOFTWARE) ||
|
||||
((request >= (uint32_t)RS_DMA_REQUEST_XPI_TX) &&
|
||||
(request <= (uint32_t)RS_DMA_REQUEST_I2C1_RX)))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
#ifdef RS_SOC_TINY
|
||||
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_TX) &&
|
||||
(((tcd->control & RS_DMA_TCD_SRC_INC) == UINT32_C(0)) ||
|
||||
((tcd->control & RS_DMA_TCD_DST_INC) != UINT32_C(0)))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_MM) && (request == (uint32_t)RS_DMA_REQUEST_SPI_RX) &&
|
||||
(((tcd->control & RS_DMA_TCD_SRC_INC) != UINT32_C(0)) ||
|
||||
((tcd->control & RS_DMA_TCD_DST_INC) == UINT32_C(0)))) {
|
||||
return RS_EINVAL;
|
||||
}
|
||||
#endif
|
||||
if ((kind == (uint32_t)RS_DMA_KIND_MM_TO_STREAM) &&
|
||||
(!((request == (uint32_t)RS_DMA_REQUEST_I2S_TX) ||
|
||||
(request == (uint32_t)RS_DMA_REQUEST_CRYPTO_IN)) ||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# SPI-P2 DMA V2.2 Paced FIFO Integration
|
||||
|
||||
This document records the SPI-P2 implementation boundary from [spi.md](spi.md)
|
||||
and [dma.md](dma.md). The shared DMA source now understands the V2.2 request
|
||||
encoding and support-register shape, while committed Tiny and Mini product
|
||||
instances remain V2.0/default-disabled for SPI until full product integration.
|
||||
|
||||
The standalone `spi_dma_pacing` block owns source-qualified TX/RX credit
|
||||
reservation, exactly-once commit and terminal retirement. A reserved credit
|
||||
remains busy through the corresponding AXI response; CPU accesses, a second
|
||||
channel, rebind and FIFO reset cannot consume it. The block reports binding
|
||||
conflict, disabled-admission and DMA-error faults without adding a DMA master,
|
||||
channel, stream endpoint, SRAM or pad.
|
||||
|
||||
## Implemented boundary
|
||||
|
||||
- `dma_pkg.sv` exposes five-bit request IDs 16/17 while preserving IDs 0..15.
|
||||
- `dma_reg.sv` supports `IP_VERSION` parameterization and read-only
|
||||
`SUPPORTED_REQUESTS@0x028`; request status is 32 bits.
|
||||
- `dma_core.sv` accepts a 32-bit request mask and decodes TCD request as
|
||||
`{control[25], control[15:12]}`. The channel state preserves all five
|
||||
request bits, and SPI direct/TCD jobs enforce the fixed-MMIO increment
|
||||
direction only when `EnableSpi` is enabled. Product instances retain their
|
||||
explicit V2.0 masks and versions until integration enables V2.2.
|
||||
- SDK headers and math/submit paths carry the high request bit. Tiny software
|
||||
accepts the SPI request shape only after `rs_dma_supports_request()` reads
|
||||
`IP_VERSION` and `SUPPORTED_REQUESTS`; V2.0 returns `RS_ENOTSUP` before the
|
||||
unsupported offset is read. No undefined compile-time V2.2 macro is required;
|
||||
Mini and other non-Tiny validation paths retain their existing rejection.
|
||||
- `spi_dma_pacing.sv` is included in the standalone SPI source boundary and
|
||||
feeds the SPI wrapper's existing credit lifecycle. It is not wired into a
|
||||
product fabric, GPIO/RCU path or DMA request allocator.
|
||||
|
||||
Descriptor cancellation blocks new fetches, including a same-cycle abort,
|
||||
treats an in-flight descriptor read and parse as channel ownership, clears
|
||||
pending descriptor state on abort completion, and prevents an aborted descriptor
|
||||
from activating. The formal harness proves these assertions, and the dedicated
|
||||
TCD cancellation cover passes under the retained Mini formal evidence root.
|
||||
|
||||
The focused execution passed 53 SPI/DMA/parity checks, 10 SPI-P1 standalone
|
||||
protocol fixtures, the existing DMA register/native tests, DMA formal
|
||||
prove/BMC/cover on the Mini-compatible V2.0 configuration, and the host C
|
||||
suite. The SPI-P2 lint and pacing fixture also pass under ICS55. These results
|
||||
validate the ABI and pacing boundary; they do not claim product V2.2 capability
|
||||
or replace the unrun full-product gates.
|
||||
|
||||
## Focused validation
|
||||
|
||||
The new `tests/test_spi_p2.py` checks the request split, five-bit channel
|
||||
storage, SPI direction guards, support ABI, product default-disable policy,
|
||||
stream-status disable behavior and the credit pacing fixture. `spi_dma_p2_tb` covers
|
||||
admission, commit, retirement, binding collision and disabled admission. The
|
||||
existing DMA register/native tests and all SPI-P1 standalone fixtures remain
|
||||
affected compatibility checks.
|
||||
|
||||
A prior full Pytest run passed 2089 tests with one optional PDF retrieval skip;
|
||||
that run predates the latest shared-DMA cancellation and five-bit-state edits.
|
||||
The post-edit full Pytest campaign remains an explicit unrun gate. The latest
|
||||
Mini DMA formal prove passed with the TCD abort assertions; the existing DMA
|
||||
cover and dedicated TCD cancellation cover completed separately under the same
|
||||
formal build root.
|
||||
|
||||
Use a fresh timestamp and explicit TINY/PDK selection. The P2 entrypoints are
|
||||
`spi-p2-lint`, `spi-p2-rtl` and `spi-p2-report`; generated records belong below
|
||||
`build/<variant>/meta/spi-p2/`. No phase acceptance, product DMA capability,
|
||||
physical qualification or SPI-P3 authorization follows from the focused
|
||||
fixture.
|
||||
@@ -66,6 +66,52 @@ dependency version, Git index or branch was changed. Independent read-only
|
||||
review and explicit human acceptance remain required; readiness is still
|
||||
`prototype` and all phase-acceptance flags remain false.
|
||||
|
||||
## SPI-P2 implementation checkpoint (2026-10-19)
|
||||
|
||||
The current `dev` commits implement the first shared-DMA V2.2 boundary:
|
||||
five-bit request constants, TCD bit25 decode, parameterized IP version and
|
||||
32-bit support/request status, plus the standalone SPI credit pacing block.
|
||||
Committed Tiny and Mini instances retain V2.0/default-disabled SPI support;
|
||||
this is deliberate compatibility evidence, not integrated SPI capability.
|
||||
|
||||
Focused P2 checks pass: DMA register/native tests, the SPI-P1 standalone
|
||||
fixtures and six P2 checks covering request encoding, five-bit channel state,
|
||||
default-disable policy, stream-status disable policy and credit admission/
|
||||
commit/retirement. Descriptor
|
||||
fetch/parse cancellation is guarded in the source and covered by the current
|
||||
DMA abort assertions; descriptor-specific dynamic coverage remains unrun. Real SPI request ports,
|
||||
source-origin transport through Tiny bridges, firmware discovery and product
|
||||
routing remain later integration work. `phase_accepted` stays false and no
|
||||
later phase is authorized.
|
||||
|
||||
Current focused evidence: 53 SPI/DMA/parity checks, DMA register/native tests,
|
||||
SPI-P1 pure checks, P2 pacing lint and RTL fixtures, DMA formal prove/BMC/cover
|
||||
and host C all pass. A prior pre-cancellation-fix full Pytest campaign passed
|
||||
2089 tests with one optional PDF retrieval skip (`pypdf` unavailable); the full
|
||||
campaign is intentionally unrun again after the latest shared-DMA source edits.
|
||||
Publication programming validation now deliberately publishes only executable
|
||||
DMA requests 0..15; SPI16/17 remain future V2.2 ABI constants and are not
|
||||
treated as current product routes. The prior full-suite test log and JUnit are under
|
||||
`build/ics55-tiny-2026-10-19-23-20-5f3eb7711d42/meta/spi-p2/validation-final/`.
|
||||
The latest source-bound P2 lint/RTL records are under
|
||||
`build/ics55-tiny-2026-10-20-22-23-5f3eb7711d42/meta/spi-p2/`; the compatibility
|
||||
Tiny lint is under
|
||||
`build/ihp130-tiny-2026-10-20-09-20-b00f46e87763/lint/verilator/`, and the
|
||||
post-edit Mini DMA formal record is under
|
||||
`build/ihp130-2026-10-20-09-13-c30fb9412aa3/formal/dma/`.
|
||||
|
||||
The latest Mini DMA formal prove/cover build is under
|
||||
`build/ihp130-2026-10-20-22-44-c30fb9412aa3/formal/dma/`; the extended prove
|
||||
command passed with the current stream-status RTL and TCD cancellation
|
||||
assertions. The standard 120-second wrapper timed out; the dedicated TCD
|
||||
cancellation cover remains passing under the retained formal root.
|
||||
|
||||
P2 remains incomplete for phase acceptance: full V2.2 block synthesis/netlist/
|
||||
STA, Tiny/Mini firmware integration, real source-origin CDC/fabric wiring,
|
||||
SPI request allocation, the full post-fix Pytest campaign and product
|
||||
regressions are unrun. Product instances
|
||||
retain V2.0/default-disabled SPI.
|
||||
|
||||
## SPI-P1 implementation checkpoint (2026-10-18, historical)
|
||||
|
||||
The approved standalone implementation starts from clean
|
||||
@@ -286,7 +332,7 @@ configured verdict; never substitute Mini coverage for missing Tiny support.
|
||||
| --- | --- |
|
||||
| SPI-P0 | Documentation freeze complete; link/anchor, command dry-run, phase/package/ALT structure and whitespace checks passed on 2026-10-04 |
|
||||
| SPI-P1 | Not started; standalone RTL/model, CSR mirror, BFM and synthesis evidence absent |
|
||||
| SPI-P2 | Not started; V2.2 request/credit/cancellation implementation and shared compatibility evidence absent |
|
||||
| SPI-P2 | Historical freeze entry; current implementation and evidence are recorded in the SPI-P2 checkpoint above |
|
||||
| SPI-P3 | Not started; actual routing/source qualification/GPIO/RCU/HAL integration absent |
|
||||
| SPI-P4 | Not started; panel adapter, pixel oracle, snapshot/save and performance evidence absent |
|
||||
| SPI-P5 | Not started; SPI/PIO-inclusive physical, Pad/package and silicon qualification absent |
|
||||
|
||||
@@ -177,9 +177,15 @@ def validate_programming(spec: dict, ids: set[str], root: Path, revision: str) -
|
||||
sdk = constants((root / "crt/include/retrosoc/hal/dma.h").read_text(), "RS_DMA_REQUEST_")
|
||||
rtl = constants((root / "rtl/ip/peripheral/dma_pkg.sv").read_text(), "DMA_REQUEST_", rtl=True)
|
||||
routes = spec["dma_routes"]
|
||||
if {r["sdk"] for r in routes} != set(sdk) or {r["rtl"] for r in routes} != set(rtl):
|
||||
# Future extension request IDs are part of the shared ABI while their
|
||||
# product wiring is intentionally absent from the current publication.
|
||||
# Publish only the executable legacy 0..15 route inventory; later phases
|
||||
# add their own source-bound route rows when the endpoint is wired.
|
||||
published_sdk = {name for name, number in sdk.items() if number < 16}
|
||||
published_rtl = {name for name, number in rtl.items() if number < 16}
|
||||
if {r["sdk"] for r in routes} != published_sdk or {r["rtl"] for r in routes} != published_rtl:
|
||||
raise ValueError("DMA route coverage mismatch")
|
||||
if len(routes) != len(sdk) or len({r["sdk"] for r in routes}) != len(routes):
|
||||
if len(routes) != len(published_sdk) or len({r["sdk"] for r in routes}) != len(routes):
|
||||
raise ValueError("duplicate DMA request mapping")
|
||||
for row in routes:
|
||||
if sdk[row["sdk"]] != rtl[row["rtl"]]:
|
||||
|
||||
@@ -25,6 +25,9 @@ specification or reserved route does not add it to the current RTL/filelists.
|
||||
The [SPI-P1 standalone implementation](../../docs/ip/spi-p1.md) is isolated
|
||||
from product filelists. Its DMA capability remains clear; actual DMA V2.2,
|
||||
Tiny routing, lifecycle and SDK integration belong to P2/P3.
|
||||
The [SPI-P2 pacing boundary](../../docs/ip/spi-p2.md) adds shared-DMA request
|
||||
encoding and a standalone credit block; committed products remain
|
||||
default-disabled until complete cancellation and product admission evidence.
|
||||
|
||||
The [PPALite contract](../../docs/ip/ppalite.md) specifies a future camera-inline
|
||||
PCLK processor with RAW bypass, small FIFOs and existing DVP_RX11/DMA2 output.
|
||||
|
||||
@@ -14,8 +14,11 @@ module apb4_dma #(
|
||||
parameter int NumChannels = 4,
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter int FifoDepth = 16,
|
||||
parameter logic [15:0] RequestMask = 16'h3fff,
|
||||
parameter bit EnableStreams = 1'b1
|
||||
parameter logic [31:0] RequestMask = 32'h0000_3fff,
|
||||
parameter logic [31:0] SupportedRequests = RequestMask,
|
||||
parameter logic [31:0] IpVersion = 32'h0002_0000,
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter bit EnableSpi = 1'b0
|
||||
) (
|
||||
// verilog_format: off -- integration ports retain established peripheral endpoint names.
|
||||
output logic lifecycle_idle_o,
|
||||
@@ -70,13 +73,20 @@ module apb4_dma #(
|
||||
logic [$clog2(NumChannels)-1:0] s_first_err_channel;
|
||||
logic [ 8:0] s_first_err_stat;
|
||||
logic [ 15:0] s_first_err_addr_hi;
|
||||
logic [ 15:0] s_req_stat;
|
||||
logic [ 31:0] s_req_stat;
|
||||
logic [ 31:0] s_supported_req;
|
||||
assign s_supported_req = SupportedRequests &
|
||||
(EnableSpi ? 32'h0003_ffff : 32'h0000_3fff) &
|
||||
(EnableStreams ? 32'h0003_ffff :
|
||||
(EnableSpi ? 32'h0003_07f9 : 32'h0000_07f9));
|
||||
|
||||
dma_reg #(
|
||||
.NumChannels (NumChannels),
|
||||
.DataWidth (DataWidth),
|
||||
.MaxBurstBeats (MaxBurstBeats),
|
||||
.EnableStreams(EnableStreams)
|
||||
.EnableStreams (EnableStreams),
|
||||
.IpVersion (IpVersion),
|
||||
.SupportedRequests(SupportedRequests)
|
||||
) u_dma_reg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
@@ -119,11 +129,13 @@ module apb4_dma #(
|
||||
.first_error_status_i (s_first_err_stat),
|
||||
.first_error_addr_hi_i(s_first_err_addr_hi),
|
||||
.request_status_i (s_req_stat),
|
||||
.supported_requests_i (s_supported_req),
|
||||
.irq_o (irq_o)
|
||||
);
|
||||
|
||||
dma_core #(
|
||||
.RequestMask (RequestMask),
|
||||
.EnableSpi (EnableSpi),
|
||||
.EnableStreams(EnableStreams),
|
||||
.AddrWidth (AddrWidth),
|
||||
.DataWidth (DataWidth),
|
||||
|
||||
@@ -19,8 +19,9 @@ module dma_core #(
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter int FifoDepth = 16,
|
||||
parameter bit EnableCrc = 1'b1,
|
||||
parameter logic [15:0] RequestMask = 16'h3fff,
|
||||
parameter bit EnableStreams = 1'b1
|
||||
parameter logic [31:0] RequestMask = 32'h0000_3fff,
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter bit EnableSpi = 1'b0
|
||||
) (
|
||||
// verilog_format: off -- channel vectors are kept aligned with the register-bank ABI.
|
||||
input logic clk_i,
|
||||
@@ -62,7 +63,7 @@ module dma_core #(
|
||||
output logic [ChannelIndexWidth-1:0] first_error_channel_o,
|
||||
output logic [8:0] first_error_status_o,
|
||||
output logic [15:0] first_error_addr_hi_o,
|
||||
output logic [15:0] request_status_o,
|
||||
output logic [31:0] request_status_o,
|
||||
output logic xpi_xfer_done_o,
|
||||
dma_req_if.dut req,
|
||||
axi4_if.master axi4,
|
||||
@@ -86,7 +87,8 @@ module dma_core #(
|
||||
logic [ NumChannels-1:0][ 31:0] s_cfg_src_addr;
|
||||
logic [ NumChannels-1:0][ 31:0] s_cfg_dst_addr;
|
||||
logic [ NumChannels-1:0][ 31:0] s_cfg_byte_count;
|
||||
logic [ NumChannels-1:0][ 3:0] s_cfg_request;
|
||||
logic [ NumChannels-1:0][ 31:0] s_cfg_req_raw;
|
||||
logic [ NumChannels-1:0][ 4:0] s_cfg_req;
|
||||
logic [ NumChannels-1:0][ 4:0] s_cfg_burst;
|
||||
logic [ NumChannels-1:0] s_cfg_crc_enable;
|
||||
logic [ NumChannels-1:0] s_cfg_crc_final;
|
||||
@@ -105,7 +107,7 @@ module dma_core #(
|
||||
logic [ NumChannels-1:0] s_evt_half_q;
|
||||
logic [ NumChannels-1:0] s_evt_err_q;
|
||||
logic [ NumChannels-1:0][ 2:0] s_kind_q;
|
||||
logic [ NumChannels-1:0][ 3:0] s_req_q;
|
||||
logic [ NumChannels-1:0][ 4:0] s_req_q;
|
||||
logic [ NumChannels-1:0][ 1:0] s_priority_q;
|
||||
logic [ NumChannels-1:0][ 4:0] s_burst_q;
|
||||
logic [ NumChannels-1:0] s_src_increment_q;
|
||||
@@ -155,7 +157,7 @@ module dma_core #(
|
||||
logic [ NumChannels-1:0][ 31:0] s_fifo_wdata;
|
||||
logic [ NumChannels-1:0][ 31:0] s_fifo_rdata;
|
||||
|
||||
logic [ 15:0] s_req_ready;
|
||||
logic [ 31:0] s_req_ready;
|
||||
logic [ NumChannels-1:0] s_start_valid;
|
||||
logic [ NumChannels-1:0][ 3:0] s_start_err_code;
|
||||
|
||||
@@ -377,8 +379,8 @@ module dma_core #(
|
||||
always_comb begin
|
||||
s_req_ready = '0;
|
||||
s_req_ready[DMA_REQUEST_SOFTWARE] = 1'b1;
|
||||
s_req_ready[DMA_REQUEST_I2S_TX] = req.i2s_tx_proc;
|
||||
s_req_ready[DMA_REQUEST_I2S_RX] = req.i2s_rx_proc;
|
||||
s_req_ready[DMA_REQUEST_I2S_TX] = EnableStreams && req.i2s_tx_proc;
|
||||
s_req_ready[DMA_REQUEST_I2S_RX] = EnableStreams && req.i2s_rx_proc;
|
||||
s_req_ready[DMA_REQUEST_QSPI_TX] = req.qspi_tx_proc;
|
||||
s_req_ready[DMA_REQUEST_QSPI_RX] = req.qspi_rx_proc;
|
||||
s_req_ready[DMA_REQUEST_UART_TX] = req.uart_tx_proc;
|
||||
@@ -387,9 +389,11 @@ module dma_core #(
|
||||
s_req_ready[DMA_REQUEST_I2C0_RX] = req.i2c0_rx_proc;
|
||||
s_req_ready[DMA_REQUEST_I2C1_TX] = req.i2c1_tx_proc;
|
||||
s_req_ready[DMA_REQUEST_I2C1_RX] = req.i2c1_rx_proc;
|
||||
s_req_ready[DMA_REQUEST_DVP_RX] = 1'b1;
|
||||
s_req_ready[DMA_REQUEST_CRYPTO_IN] = req.crypto_in_proc;
|
||||
s_req_ready[DMA_REQUEST_CRYPTO_OUT] = req.crypto_out_proc;
|
||||
s_req_ready[DMA_REQUEST_DVP_RX] = EnableStreams;
|
||||
s_req_ready[DMA_REQUEST_CRYPTO_IN] = EnableStreams && req.crypto_in_proc;
|
||||
s_req_ready[DMA_REQUEST_CRYPTO_OUT] = EnableStreams && req.crypto_out_proc;
|
||||
s_req_ready[DMA_REQUEST_SPI_TX] = EnableSpi && RequestMask[DMA_REQUEST_SPI_TX];
|
||||
s_req_ready[DMA_REQUEST_SPI_RX] = EnableSpi && RequestMask[DMA_REQUEST_SPI_RX];
|
||||
end
|
||||
assign request_status_o = s_req_ready;
|
||||
|
||||
@@ -405,7 +409,8 @@ module dma_core #(
|
||||
s_cfg_src_addr[channel] = src_addr_i[(channel*32)+:32];
|
||||
s_cfg_dst_addr[channel] = dst_addr_i[(channel*32)+:32];
|
||||
s_cfg_byte_count[channel] = byte_count_i[(channel*32)+:32];
|
||||
s_cfg_request[channel] = request_sel_i[(channel*32)+:4];
|
||||
s_cfg_req_raw[channel] = request_sel_i[(channel*32)+:32];
|
||||
s_cfg_req[channel] = s_cfg_req_raw[channel][4:0];
|
||||
s_cfg_burst[channel] = burst_cfg_i[(channel*32)+:5];
|
||||
end
|
||||
end
|
||||
@@ -415,7 +420,8 @@ module dma_core #(
|
||||
s_tcd_fetch_valid = 1'b0;
|
||||
for (int unsigned channel = 0; channel < NumChannels; channel++) begin
|
||||
if (s_tcd_fetch_pending_q[channel] && s_busy_q[channel] &&
|
||||
!s_tcd_read_owner_valid_q && !s_axi_read_busy) begin
|
||||
!s_abort_q[channel] && !abort_i[channel] && !s_err_q[channel] &&
|
||||
!s_tcd_read_owner_valid_q && !s_tcd_parse_q && !s_axi_read_busy) begin
|
||||
if (!s_tcd_fetch_valid || (s_priority_q[channel] >
|
||||
s_priority_q[s_tcd_fetch_selected])) begin
|
||||
s_tcd_fetch_selected = ChannelIndexWidth'(channel);
|
||||
@@ -434,14 +440,14 @@ module dma_core #(
|
||||
if ((other != channel) && s_busy_q[other]) begin
|
||||
if ((s_cfg_kind[channel] == DMA_KIND_MM_TO_STREAM) &&
|
||||
(s_kind_q[other] == DMA_KIND_MM_TO_STREAM) &&
|
||||
(s_req_q[other] == s_cfg_request[channel]) &&
|
||||
((s_cfg_request[channel] == DMA_REQUEST_I2S_TX) ||
|
||||
(s_cfg_request[channel] == DMA_REQUEST_CRYPTO_IN))) begin
|
||||
(s_req_q[other] == s_cfg_req[channel]) &&
|
||||
((s_cfg_req[channel] == DMA_REQUEST_I2S_TX) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_CRYPTO_IN))) begin
|
||||
endpoint_busy = 1'b1;
|
||||
end
|
||||
if ((s_cfg_kind[channel] == DMA_KIND_STREAM_TO_MM) &&
|
||||
(s_kind_q[other] == DMA_KIND_STREAM_TO_MM) &&
|
||||
(s_req_q[other] == s_cfg_request[channel])) begin
|
||||
(s_req_q[other] == s_cfg_req[channel])) begin
|
||||
endpoint_busy = 1'b1;
|
||||
end
|
||||
end
|
||||
@@ -449,7 +455,9 @@ module dma_core #(
|
||||
|
||||
s_start_valid[channel] = 1'b1;
|
||||
s_start_err_code[channel] = DMA_ERROR_CONFIG;
|
||||
if (!RequestMask[s_cfg_request[channel]] ||
|
||||
if ((s_cfg_req_raw[channel][31:5] != 27'd0) ||
|
||||
(s_cfg_req[channel] > DMA_REQUEST_SPI_RX) ||
|
||||
!RequestMask[s_cfg_req[channel]] ||
|
||||
(!EnableStreams && (s_cfg_kind[channel] != DMA_KIND_MM_TO_MM)) ||
|
||||
(s_cfg_width[channel] != DMA_WIDTH_32) || (s_cfg_byte_count[channel] == 32'd0) ||
|
||||
(s_cfg_burst[channel] == 5'd0) ||
|
||||
@@ -462,11 +470,19 @@ module dma_core #(
|
||||
if ((s_cfg_src_addr[channel] == 32'd0) || (s_cfg_dst_addr[channel] == 32'd0) ||
|
||||
(s_cfg_src_addr[channel][1:0] != 2'b00) ||
|
||||
(s_cfg_dst_addr[channel][1:0] != 2'b00) ||
|
||||
(s_cfg_request[channel] == DMA_REQUEST_I2S_TX) ||
|
||||
(s_cfg_request[channel] == DMA_REQUEST_I2S_RX) ||
|
||||
(s_cfg_request[channel] == DMA_REQUEST_DVP_RX) ||
|
||||
(s_cfg_request[channel] == DMA_REQUEST_CRYPTO_IN) ||
|
||||
(s_cfg_request[channel] == DMA_REQUEST_CRYPTO_OUT)) begin
|
||||
(s_cfg_req[channel] == DMA_REQUEST_I2S_TX) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_I2S_RX) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_DVP_RX) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_CRYPTO_IN) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_CRYPTO_OUT) ||
|
||||
((s_cfg_req[channel] >= 5'd14) &&
|
||||
(s_cfg_req[channel] < DMA_REQUEST_SPI_TX)) ||
|
||||
(!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) ||
|
||||
(s_cfg_req[channel] == DMA_REQUEST_SPI_RX))) ||
|
||||
((s_cfg_req[channel] == DMA_REQUEST_SPI_TX) &&
|
||||
(!s_cfg_src_increment[channel] || s_cfg_dst_increment[channel])) ||
|
||||
((s_cfg_req[channel] == DMA_REQUEST_SPI_RX) &&
|
||||
(s_cfg_src_increment[channel] || !s_cfg_dst_increment[channel]))) begin
|
||||
s_start_valid[channel] = 1'b0;
|
||||
s_start_err_code[channel] =
|
||||
((s_cfg_src_addr[channel][1:0] != 2'b00) ||
|
||||
@@ -480,8 +496,8 @@ module dma_core #(
|
||||
(s_cfg_src_addr[channel][1:0] != 2'b00) ||
|
||||
(s_cfg_byte_count[channel][1:0] != 2'b00) ||
|
||||
!s_cfg_src_increment[channel] ||
|
||||
((s_cfg_request[channel] != DMA_REQUEST_I2S_TX) &&
|
||||
(s_cfg_request[channel] != DMA_REQUEST_CRYPTO_IN)) || endpoint_busy) begin
|
||||
((s_cfg_req[channel] != DMA_REQUEST_I2S_TX) &&
|
||||
(s_cfg_req[channel] != DMA_REQUEST_CRYPTO_IN)) || endpoint_busy) begin
|
||||
s_start_valid[channel] = 1'b0;
|
||||
s_start_err_code[channel] =
|
||||
((s_cfg_src_addr[channel][1:0] != 2'b00) ||
|
||||
@@ -495,9 +511,9 @@ module dma_core #(
|
||||
(s_cfg_dst_addr[channel][1:0] != 2'b00) ||
|
||||
(s_cfg_byte_count[channel][1:0] != 2'b00) ||
|
||||
!s_cfg_dst_increment[channel] ||
|
||||
((s_cfg_request[channel] != DMA_REQUEST_I2S_RX) &&
|
||||
(s_cfg_request[channel] != DMA_REQUEST_DVP_RX) &&
|
||||
(s_cfg_request[channel] != DMA_REQUEST_CRYPTO_OUT)) ||
|
||||
((s_cfg_req[channel] != DMA_REQUEST_I2S_RX) &&
|
||||
(s_cfg_req[channel] != DMA_REQUEST_DVP_RX) &&
|
||||
(s_cfg_req[channel] != DMA_REQUEST_CRYPTO_OUT)) ||
|
||||
endpoint_busy) begin
|
||||
s_start_valid[channel] = 1'b0;
|
||||
s_start_err_code[channel] =
|
||||
@@ -938,6 +954,10 @@ module dma_core #(
|
||||
for (int unsigned channel = 0; channel < NumChannels; channel++) begin
|
||||
s_read_owns_channel[channel] = s_read_owner_valid_q &&
|
||||
(s_read_owner_q == ChannelIndexWidth'(channel));
|
||||
s_read_owns_channel[channel] |= s_tcd_read_owner_valid_q &&
|
||||
(s_tcd_read_owner_q == ChannelIndexWidth'(channel));
|
||||
s_read_owns_channel[channel] |= s_tcd_parse_q &&
|
||||
(s_tcd_read_owner_q == ChannelIndexWidth'(channel));
|
||||
s_write_owns_channel[channel] = s_write_owner_valid_q &&
|
||||
(s_write_owner_q == ChannelIndexWidth'(channel));
|
||||
s_stream_tx_valid[channel] = s_i2s_tx_channel_valid &&
|
||||
@@ -1144,7 +1164,7 @@ module dma_core #(
|
||||
if (s_start_valid[channel]) begin
|
||||
s_busy_q[channel] <= 1'b1;
|
||||
s_kind_q[channel] <= s_cfg_kind[channel];
|
||||
s_req_q[channel] <= s_cfg_request[channel];
|
||||
s_req_q[channel] <= s_cfg_req[channel];
|
||||
s_priority_q[channel] <= s_cfg_priority[channel];
|
||||
s_burst_q[channel] <= s_cfg_burst[channel];
|
||||
s_src_increment_q[channel] <= s_cfg_src_increment[channel];
|
||||
@@ -1272,7 +1292,10 @@ module dma_core #(
|
||||
|
||||
if (s_tcd_parse_q) begin
|
||||
s_tcd_parse_q <= 1'b0;
|
||||
if (!RequestMask[s_tcd_word7_q[s_tcd_read_owner_q][15:12]] ||
|
||||
if (s_abort_q[s_tcd_read_owner_q]) begin
|
||||
s_tcd_loaded_q[s_tcd_read_owner_q] <= 1'b0;
|
||||
end else if (!RequestMask[{s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]}] ||
|
||||
(!EnableStreams && (s_tcd_word7_q[s_tcd_read_owner_q][10:8] != DMA_KIND_MM_TO_MM)) ||
|
||||
s_err_q[s_tcd_read_owner_q] ||
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][0] == 1'b0) ||
|
||||
@@ -1282,7 +1305,26 @@ module dma_core #(
|
||||
((s_tcd_word0_q[s_tcd_read_owner_q] != 32'd0) &&
|
||||
(s_tcd_word0_q[s_tcd_read_owner_q][5:0] != 6'd0)) ||
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][10:8] > DMA_KIND_STREAM_TO_MM) ||
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][15:12] > DMA_REQUEST_CRYPTO_OUT) ||
|
||||
({s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} > DMA_REQUEST_SPI_RX) ||
|
||||
((s_tcd_word7_q[s_tcd_read_owner_q][25] == 1'b0) &&
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][15:12] >= 4'd14) &&
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_MM_TO_MM)) ||
|
||||
((!EnableSpi) &&
|
||||
(({s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_TX) ||
|
||||
({s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_RX))) ||
|
||||
((({s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_TX) &&
|
||||
((s_tcd_word7_q[s_tcd_read_owner_q][10:8] != DMA_KIND_MM_TO_MM) ||
|
||||
!s_tcd_word7_q[s_tcd_read_owner_q][1] ||
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][2])) ||
|
||||
(({s_tcd_word7_q[s_tcd_read_owner_q][25],
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][15:12]} == DMA_REQUEST_SPI_RX) &&
|
||||
((s_tcd_word7_q[s_tcd_read_owner_q][10:8] != DMA_KIND_MM_TO_MM) ||
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][1] ||
|
||||
!s_tcd_word7_q[s_tcd_read_owner_q][2]))) ||
|
||||
(((s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_MM_TO_STREAM) ||
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][10:8] == DMA_KIND_STREAM_TO_MM)) &&
|
||||
(s_tcd_word3_q[s_tcd_read_owner_q][1:0] != 2'b00)) ||
|
||||
@@ -1297,7 +1339,9 @@ module dma_core #(
|
||||
s_dst_base_q[s_tcd_read_owner_q] <= s_tcd_word2_q[s_tcd_read_owner_q];
|
||||
s_len_q[s_tcd_read_owner_q] <= s_tcd_word3_q[s_tcd_read_owner_q];
|
||||
s_kind_q[s_tcd_read_owner_q] <= s_tcd_word7_q[s_tcd_read_owner_q][10:8];
|
||||
s_req_q[s_tcd_read_owner_q] <= s_tcd_word7_q[s_tcd_read_owner_q][15:12];
|
||||
s_req_q[s_tcd_read_owner_q] <= {
|
||||
s_tcd_word7_q[s_tcd_read_owner_q][25], s_tcd_word7_q[s_tcd_read_owner_q][15:12]
|
||||
};
|
||||
s_priority_q[s_tcd_read_owner_q] <= s_tcd_word7_q[s_tcd_read_owner_q][17:16];
|
||||
s_burst_q[s_tcd_read_owner_q] <=
|
||||
(s_tcd_word7_q[s_tcd_read_owner_q][24:20] == 5'd0)
|
||||
@@ -1314,7 +1358,6 @@ module dma_core #(
|
||||
: s_tcd_word9_q[s_tcd_read_owner_q];
|
||||
s_stream_tx_stop_q[s_tcd_read_owner_q] <= 1'b0;
|
||||
s_suspended_q[s_tcd_read_owner_q] <= 1'b0;
|
||||
s_abort_q[s_tcd_read_owner_q] <= 1'b0;
|
||||
s_done_q[s_tcd_read_owner_q] <= 1'b0;
|
||||
s_read_issued_q[s_tcd_read_owner_q] <= '0;
|
||||
s_write_issued_q[s_tcd_read_owner_q] <= '0;
|
||||
@@ -1618,6 +1661,9 @@ module dma_core #(
|
||||
s_abort_q[channel] <= 1'b0;
|
||||
s_suspend_req_q[channel] <= 1'b0;
|
||||
s_stream_tx_stop_q[channel] <= 1'b1;
|
||||
s_tcd_fetch_pending_q[channel] <= 1'b0;
|
||||
s_tcd_loaded_q[channel] <= 1'b0;
|
||||
s_tcd_mode_q[channel] <= 1'b0;
|
||||
s_err_code_q[channel] <= DMA_ERROR_ABORT;
|
||||
end
|
||||
end
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
`define APB4_DMA__IRQ_TEST 12'h01C
|
||||
`define APB4_DMA__ERROR_SUMMARY 12'h020
|
||||
`define APB4_DMA__REQUEST_STATUS 12'h024
|
||||
`define APB4_DMA__SUPPORTED_REQUESTS 12'h028
|
||||
|
||||
`define APB4_DMA__CH_BASE 12'h100
|
||||
`define APB4_DMA__CH_STRIDE 12'h080
|
||||
|
||||
@@ -15,20 +15,22 @@ package dma_pkg;
|
||||
|
||||
localparam logic [1:0] DMA_WIDTH_32 = 2'd2;
|
||||
|
||||
localparam logic [3:0] DMA_REQUEST_SOFTWARE = 4'd0;
|
||||
localparam logic [3:0] DMA_REQUEST_I2S_TX = 4'd1;
|
||||
localparam logic [3:0] DMA_REQUEST_I2S_RX = 4'd2;
|
||||
localparam logic [3:0] DMA_REQUEST_QSPI_TX = 4'd3;
|
||||
localparam logic [3:0] DMA_REQUEST_QSPI_RX = 4'd4;
|
||||
localparam logic [3:0] DMA_REQUEST_UART_TX = 4'd5;
|
||||
localparam logic [3:0] DMA_REQUEST_UART_RX = 4'd6;
|
||||
localparam logic [3:0] DMA_REQUEST_I2C0_TX = 4'd7;
|
||||
localparam logic [3:0] DMA_REQUEST_I2C0_RX = 4'd8;
|
||||
localparam logic [3:0] DMA_REQUEST_I2C1_TX = 4'd9;
|
||||
localparam logic [3:0] DMA_REQUEST_I2C1_RX = 4'd10;
|
||||
localparam logic [3:0] DMA_REQUEST_DVP_RX = 4'd11;
|
||||
localparam logic [3:0] DMA_REQUEST_CRYPTO_IN = 4'd12;
|
||||
localparam logic [3:0] DMA_REQUEST_CRYPTO_OUT = 4'd13;
|
||||
localparam logic [4:0] DMA_REQUEST_SOFTWARE = 5'd0;
|
||||
localparam logic [4:0] DMA_REQUEST_I2S_TX = 5'd1;
|
||||
localparam logic [4:0] DMA_REQUEST_I2S_RX = 5'd2;
|
||||
localparam logic [4:0] DMA_REQUEST_QSPI_TX = 5'd3;
|
||||
localparam logic [4:0] DMA_REQUEST_QSPI_RX = 5'd4;
|
||||
localparam logic [4:0] DMA_REQUEST_UART_TX = 5'd5;
|
||||
localparam logic [4:0] DMA_REQUEST_UART_RX = 5'd6;
|
||||
localparam logic [4:0] DMA_REQUEST_I2C0_TX = 5'd7;
|
||||
localparam logic [4:0] DMA_REQUEST_I2C0_RX = 5'd8;
|
||||
localparam logic [4:0] DMA_REQUEST_I2C1_TX = 5'd9;
|
||||
localparam logic [4:0] DMA_REQUEST_I2C1_RX = 5'd10;
|
||||
localparam logic [4:0] DMA_REQUEST_DVP_RX = 5'd11;
|
||||
localparam logic [4:0] DMA_REQUEST_CRYPTO_IN = 5'd12;
|
||||
localparam logic [4:0] DMA_REQUEST_CRYPTO_OUT = 5'd13;
|
||||
localparam logic [4:0] DMA_REQUEST_SPI_TX = 5'd16;
|
||||
localparam logic [4:0] DMA_REQUEST_SPI_RX = 5'd17;
|
||||
|
||||
localparam logic [3:0] DMA_ERROR_NONE = 4'd0;
|
||||
localparam logic [3:0] DMA_ERROR_CONFIG = 4'd1;
|
||||
|
||||
@@ -15,7 +15,9 @@ module dma_reg #(
|
||||
parameter bit EnableStreams = 1'b1,
|
||||
parameter int ChannelIndexWidth = (NumChannels > 1) ? $clog2(NumChannels) : 1,
|
||||
parameter int DataWidth = 32,
|
||||
parameter int MaxBurstBeats = 16
|
||||
parameter int MaxBurstBeats = 16,
|
||||
parameter logic [31:0] IpVersion = 32'h0002_0000,
|
||||
parameter logic [31:0] SupportedRequests = 32'h0000_3fff
|
||||
) (
|
||||
// verilog_format: off -- APB4/configuration and status vector groups follow the register ABI.
|
||||
input logic clk_i,
|
||||
@@ -58,12 +60,12 @@ module dma_reg #(
|
||||
input logic [ChannelIndexWidth-1:0] first_error_channel_i,
|
||||
input logic [8:0] first_error_status_i,
|
||||
input logic [15:0] first_error_addr_hi_i,
|
||||
input logic [15:0] request_status_i,
|
||||
input logic [31:0] request_status_i,
|
||||
input logic [31:0] supported_requests_i,
|
||||
output logic irq_o
|
||||
// verilog_format: on
|
||||
);
|
||||
localparam logic [31:0] IpId = 32'h444D_4134;
|
||||
localparam logic [31:0] IpVersion = 32'h0002_0000;
|
||||
localparam logic [31:0] ChannelBase = {20'd0, `APB4_DMA__CH_BASE};
|
||||
localparam logic [31:0] ChannelStride = {20'd0, `APB4_DMA__CH_STRIDE};
|
||||
|
||||
@@ -133,7 +135,8 @@ module dma_reg #(
|
||||
(s_offset == `APB4_DMA__IRQ_ENABLE) ||
|
||||
(s_offset == `APB4_DMA__IRQ_TEST) ||
|
||||
(s_offset == `APB4_DMA__ERROR_SUMMARY) ||
|
||||
(s_offset == `APB4_DMA__REQUEST_STATUS);
|
||||
(s_offset == `APB4_DMA__REQUEST_STATUS) ||
|
||||
(s_offset == `APB4_DMA__SUPPORTED_REQUESTS);
|
||||
assign s_channel_valid = (s_offset_ext >= ChannelBase) &&
|
||||
(s_offset_ext < (ChannelBase + (NumChannels * ChannelStride)));
|
||||
assign s_channel_index = ChannelIndexWidth'((s_offset_ext - ChannelBase) >> 7);
|
||||
@@ -176,7 +179,8 @@ module dma_reg #(
|
||||
if (s_write && s_global_valid &&
|
||||
((s_offset == `APB4_DMA__IP_ID) || (s_offset == `APB4_DMA__IP_VERSION) ||
|
||||
(s_offset == `APB4_DMA__CAPABILITY) || (s_offset == `APB4_DMA__GLOBAL_STATUS) ||
|
||||
(s_offset == `APB4_DMA__REQUEST_STATUS))) begin
|
||||
(s_offset == `APB4_DMA__REQUEST_STATUS) ||
|
||||
(s_offset == `APB4_DMA__SUPPORTED_REQUESTS))) begin
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
if (s_write && s_channel_valid &&
|
||||
@@ -373,7 +377,8 @@ module dma_reg #(
|
||||
{{(3 - ChannelIndexWidth) {1'b0}}, first_error_channel_i},
|
||||
first_error_valid_i
|
||||
};
|
||||
`APB4_DMA__REQUEST_STATUS: s_read_data = {16'd0, request_status_i};
|
||||
`APB4_DMA__REQUEST_STATUS: s_read_data = request_status_i;
|
||||
`APB4_DMA__SUPPORTED_REQUESTS: s_read_data = supported_requests_i;
|
||||
default: begin
|
||||
if (s_channel_valid) begin
|
||||
unique case (s_channel_offset)
|
||||
|
||||
+34
-10
@@ -1,5 +1,7 @@
|
||||
// Standalone SPI-P1 shell. No product route or integrated DMA capability is implied.
|
||||
module apb4_spi (
|
||||
// Standalone SPI shell. DMA pacing is local; product route and fabric origin remain external.
|
||||
module apb4_spi #(
|
||||
parameter bit EnableDma = 1'b1
|
||||
) (
|
||||
input logic clk_i,
|
||||
rst_n_i,
|
||||
input logic [31:0] clock_hz_i,
|
||||
@@ -30,15 +32,36 @@ module apb4_spi (
|
||||
spi_ctrl_if u_ctrl ();
|
||||
spi_pkg::configuration_t s_cfg;
|
||||
logic [31:0] s_claim, s_ready;
|
||||
logic s_handback, s_credit_fault;
|
||||
logic s_handback, s_credit_fault, s_reg_credit_fault, s_pacing_fault;
|
||||
logic s_fifo_pending;
|
||||
logic [1:0] s_credit_pending;
|
||||
logic [1:0] s_credit_pending, s_pacing_pending, s_reg_credit_pending;
|
||||
logic [1:0] s_credit_valid, s_credit_retire, s_credit_commit;
|
||||
logic [3:0] s_dma_stat;
|
||||
logic s_unused;
|
||||
assign s_cfg = u_ctrl.cfg;
|
||||
// Only personality bits feed the GPIO guard; other fields feed the core/reg blocks.
|
||||
assign s_unused = ^s_cfg;
|
||||
assign s_dma_stat = dma_status_i | {s_credit_pending, 2'd0};
|
||||
assign s_credit_fault = s_reg_credit_fault | s_pacing_fault;
|
||||
assign s_credit_pending = s_pacing_pending | s_reg_credit_pending;
|
||||
spi_dma_pacing u_dma_pacing (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.enable_i (EnableDma),
|
||||
.tx_binding_i (s_cfg.tx_binding),
|
||||
.rx_binding_i (s_cfg.rx_binding),
|
||||
.dma_start_pending_i(dma_start_pending_i),
|
||||
.dma_status_i (dma_status_i),
|
||||
.dma_error_i (dma_error_i),
|
||||
.credit_valid_i (credit_valid_i),
|
||||
.credit_retire_i (credit_retire_i),
|
||||
.credit_commit_i (s_credit_commit),
|
||||
.credit_valid_o (s_credit_valid),
|
||||
.credit_retire_o (s_credit_retire),
|
||||
.credit_pending_o (s_pacing_pending),
|
||||
.dma_status_o (s_dma_stat),
|
||||
.credit_fault_o (s_pacing_fault),
|
||||
.busy_o ()
|
||||
);
|
||||
spi_gpio_guard u_gpio_guard (
|
||||
.session_i (s_cfg.session),
|
||||
.user_select_i (user_select_i),
|
||||
@@ -59,18 +82,19 @@ module apb4_spi (
|
||||
.origin_i (origin_i),
|
||||
.request_i (request_i),
|
||||
.channel_i (channel_i),
|
||||
.credit_valid_i (credit_valid_i),
|
||||
.credit_retire_i (credit_retire_i),
|
||||
.credit_valid_i (s_credit_valid),
|
||||
.credit_retire_i (s_credit_retire),
|
||||
.dma_status_i (s_dma_stat),
|
||||
.dma_start_pending_i(dma_start_pending_i),
|
||||
.credit_commit_o (credit_commit_o),
|
||||
.credit_pending_o (s_credit_pending),
|
||||
.credit_commit_o (s_credit_commit),
|
||||
.credit_pending_o (s_reg_credit_pending),
|
||||
.fifo_pending_o (s_fifo_pending),
|
||||
.credit_fault_o (s_credit_fault),
|
||||
.credit_fault_o (s_reg_credit_fault),
|
||||
.irq_o (irq_o),
|
||||
.apb4 (apb4),
|
||||
.ctrl (u_ctrl)
|
||||
);
|
||||
assign credit_commit_o = s_credit_commit;
|
||||
spi_master_core u_master_core (
|
||||
.apb_pending_i (s_fifo_pending),
|
||||
.clk_i (clk_i),
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPI-P2 source-qualified fixed-MMIO credit pacing.
|
||||
// A reservation is held from DMA admission until terminal response retirement.
|
||||
module spi_dma_pacing (
|
||||
input logic clk_i,
|
||||
rst_n_i,
|
||||
input logic enable_i,
|
||||
input logic [31:0] tx_binding_i,
|
||||
rx_binding_i,
|
||||
input logic [ 1:0] dma_start_pending_i,
|
||||
input logic [ 3:0] dma_status_i,
|
||||
input logic dma_error_i,
|
||||
input logic [ 1:0] credit_valid_i,
|
||||
credit_retire_i,
|
||||
input logic [ 1:0] credit_commit_i,
|
||||
output logic [ 1:0] credit_valid_o,
|
||||
credit_retire_o,
|
||||
output logic [ 1:0] credit_pending_o,
|
||||
output logic [ 3:0] dma_status_o,
|
||||
output logic credit_fault_o,
|
||||
busy_o
|
||||
);
|
||||
logic [1:0] s_reserved_d, s_reserved_q;
|
||||
logic s_binding_conflict;
|
||||
logic s_unused;
|
||||
|
||||
assign s_binding_conflict = tx_binding_i[31] && rx_binding_i[31] &&
|
||||
(tx_binding_i[2:0] == rx_binding_i[2:0]);
|
||||
assign s_unused = ^{tx_binding_i[30:3], rx_binding_i[30:3]};
|
||||
assign credit_valid_o = enable_i ? credit_valid_i & ~s_reserved_q : 2'd0;
|
||||
assign credit_retire_o = credit_retire_i;
|
||||
assign credit_pending_o = s_reserved_q | credit_valid_o;
|
||||
assign dma_status_o = dma_status_i | {credit_pending_o, 2'd0};
|
||||
assign busy_o = (dma_status_o != 4'd0) || (dma_start_pending_i != 2'd0) ||
|
||||
(credit_pending_o != 2'd0);
|
||||
assign credit_fault_o = (!enable_i && ((credit_valid_i != 2'd0) ||
|
||||
(credit_commit_i != 2'd0))) || s_binding_conflict || dma_error_i || (s_unused && 1'b0);
|
||||
|
||||
always_comb begin
|
||||
s_reserved_d = s_reserved_q;
|
||||
s_reserved_d = s_reserved_d & ~credit_retire_i;
|
||||
s_reserved_d = s_reserved_d | credit_commit_i;
|
||||
if (!enable_i && (credit_retire_i == 2'd0)) s_reserved_d = '0;
|
||||
end
|
||||
|
||||
dffr #(
|
||||
.DATA_WIDTH(2)
|
||||
) u_reserved (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_reserved_d),
|
||||
.dat_o (s_reserved_q)
|
||||
);
|
||||
endmodule
|
||||
@@ -49,6 +49,13 @@ module dma_formal_design (
|
||||
output logic [ 1:0] tx_channel,
|
||||
output logic [ 31:0] tx_channel_bytes_done,
|
||||
output logic [ 31:0] tx_channel_len,
|
||||
output logic [ 1:0] scenario,
|
||||
output logic tcd_mode,
|
||||
output logic tcd_read_owner_valid,
|
||||
output logic tcd_parse,
|
||||
output logic tcd_loaded,
|
||||
output logic tcd_fetch_pending,
|
||||
output logic tcd_abort,
|
||||
output logic awvalid,
|
||||
output logic awready,
|
||||
output logic awid,
|
||||
@@ -134,6 +141,8 @@ module dma_formal_design (
|
||||
logic [NumChannels*32-1:0] s_byte_count_i;
|
||||
logic [NumChannels*32-1:0] s_request_sel_i;
|
||||
logic [NumChannels*32-1:0] s_burst_cfg_i;
|
||||
logic [NumChannels*32-1:0] s_tcd_head_i;
|
||||
logic [NumChannels*32-1:0] s_tcd_count_i;
|
||||
logic [ NumChannels-1:0] s_start_i;
|
||||
logic [ NumChannels-1:0] s_abort_i;
|
||||
logic [ 5:0] s_cycle_q;
|
||||
@@ -161,8 +170,21 @@ module dma_formal_design (
|
||||
logic s_write_error_q;
|
||||
|
||||
logic s_tx_stalled_once_q;
|
||||
logic s_tcd_read_q;
|
||||
|
||||
function automatic logic [31:0] tcd_word(input logic [4:0] beat_i);
|
||||
unique case (beat_i)
|
||||
5'd0: tcd_word = 32'd0;
|
||||
5'd1: tcd_word = 32'h1000_1000;
|
||||
5'd2: tcd_word = 32'h1000_2000;
|
||||
5'd3: tcd_word = 32'd16;
|
||||
5'd7: tcd_word = 32'h0001_0003;
|
||||
default: tcd_word = 32'd0;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
(* anyconst *)logic [ 1:0] f_scenario;
|
||||
(* anyconst *)logic f_tcd_mode;
|
||||
(* anyconst *)logic [31:0] f_read_data;
|
||||
(* anyconst *)logic [31:0] f_rx_data;
|
||||
(* anyconst *)logic f_rx_last;
|
||||
@@ -230,6 +252,8 @@ module dma_formal_design (
|
||||
s_byte_count_i = '0;
|
||||
s_request_sel_i = '0;
|
||||
s_burst_cfg_i = '0;
|
||||
s_tcd_head_i = '0;
|
||||
s_tcd_count_i = '0;
|
||||
|
||||
s_ch_cfg_i[0+:32] = {22'd0, 2'd0, 1'b1, 1'b1, DMA_WIDTH_32, 1'b0, DMA_KIND_MM_TO_MM};
|
||||
s_src_addr_i[0+:32] = 32'h1000_0FF0;
|
||||
@@ -256,12 +280,25 @@ module dma_formal_design (
|
||||
s_byte_count_i[96+:32] = 32'd8;
|
||||
s_request_sel_i[96+:32] = {28'd0, DMA_REQUEST_SOFTWARE};
|
||||
s_burst_cfg_i[96+:32] = 32'd2;
|
||||
if (f_tcd_mode) begin
|
||||
s_tcd_head_i[0+:32] = 32'h2000_0000;
|
||||
s_tcd_count_i[0+:32] = 32'd1;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_start_i = '0;
|
||||
s_abort_i = '0;
|
||||
if (rst_n_i) begin
|
||||
if (f_tcd_mode) begin
|
||||
if (s_cycle_q == 6'd0) begin
|
||||
s_start_i[0] = 1'b1;
|
||||
end
|
||||
if (u_dut.s_tcd_fetch_pending_q[0] || u_dut.s_tcd_read_owner_valid_q ||
|
||||
u_dut.s_tcd_parse_q) begin
|
||||
s_abort_i[0] = 1'b1;
|
||||
end
|
||||
end else
|
||||
unique case (f_scenario)
|
||||
2'd0: begin
|
||||
if (s_cycle_q == 6'd0) begin
|
||||
@@ -374,8 +411,8 @@ module dma_formal_design (
|
||||
.global_reset_i (1'b0),
|
||||
.global_error_clear_i (1'b0),
|
||||
.ch_cfg_i (s_ch_cfg_i),
|
||||
.tcd_head_i ('0),
|
||||
.tcd_count_i ('0),
|
||||
.tcd_head_i (s_tcd_head_i),
|
||||
.tcd_count_i (s_tcd_count_i),
|
||||
.crc_expected_i ('0),
|
||||
.src_addr_i (s_src_addr_i),
|
||||
.dst_addr_i (s_dst_addr_i),
|
||||
@@ -446,6 +483,13 @@ module dma_formal_design (
|
||||
assign write_start_channel = u_dut.s_write_rr_selected;
|
||||
assign tx_channel_valid = u_dut.s_i2s_tx_channel_valid;
|
||||
assign tx_channel = u_dut.s_i2s_tx_channel;
|
||||
assign scenario = f_scenario;
|
||||
assign tcd_mode = f_tcd_mode;
|
||||
assign tcd_read_owner_valid = u_dut.s_tcd_read_owner_valid_q;
|
||||
assign tcd_parse = u_dut.s_tcd_parse_q;
|
||||
assign tcd_loaded = u_dut.s_tcd_loaded_q[0];
|
||||
assign tcd_fetch_pending = u_dut.s_tcd_fetch_pending_q[0];
|
||||
assign tcd_abort = u_dut.s_abort_q[0];
|
||||
|
||||
always_comb begin
|
||||
write_owner_bytes_done = 32'd0;
|
||||
@@ -563,6 +607,7 @@ module dma_formal_design (
|
||||
s_read_beat_q <= 5'd0;
|
||||
s_read_error_q <= 1'b0;
|
||||
s_read_data_q <= 32'd0;
|
||||
s_tcd_read_q <= 1'b0;
|
||||
end else begin
|
||||
if (axi4.arvalid && !axi4.arready) begin
|
||||
s_read_stalled_once_q <= 1'b1;
|
||||
@@ -572,14 +617,15 @@ module dma_formal_design (
|
||||
s_read_beats_q <= {1'b0, axi4.arlen[3:0]} + 1'b1;
|
||||
s_read_beat_q <= 5'd0;
|
||||
s_read_error_q <= axi4.araddr[31:28] == 4'hD;
|
||||
s_read_data_q <= f_read_data;
|
||||
s_tcd_read_q <= axi4.araddr == 32'h2000_0000;
|
||||
s_read_data_q <= (axi4.araddr == 32'h2000_0000) ? tcd_word(5'd0) : f_read_data;
|
||||
end
|
||||
if (axi4.rvalid && axi4.rready) begin
|
||||
if (axi4.rlast) begin
|
||||
s_read_active_q <= 1'b0;
|
||||
end else begin
|
||||
s_read_beat_q <= s_read_beat_q + 1'b1;
|
||||
s_read_data_q <= f_read_data;
|
||||
s_read_data_q <= s_tcd_read_q ? tcd_word(s_read_beat_q + 1'b1) : f_read_data;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,6 +53,13 @@ module dma_formal;
|
||||
wire [ 1:0] tx_channel;
|
||||
wire [ 31:0] tx_channel_bytes_done;
|
||||
wire [ 31:0] tx_channel_len;
|
||||
wire [ 1:0] scenario;
|
||||
wire tcd_mode;
|
||||
wire tcd_read_owner_valid;
|
||||
wire tcd_parse;
|
||||
wire tcd_loaded;
|
||||
wire tcd_fetch_pending;
|
||||
wire tcd_abort;
|
||||
wire awvalid;
|
||||
wire awready;
|
||||
wire awid;
|
||||
@@ -180,6 +187,13 @@ module dma_formal;
|
||||
.tx_channel (tx_channel),
|
||||
.tx_channel_bytes_done (tx_channel_bytes_done),
|
||||
.tx_channel_len (tx_channel_len),
|
||||
.scenario (scenario),
|
||||
.tcd_mode (tcd_mode),
|
||||
.tcd_read_owner_valid (tcd_read_owner_valid),
|
||||
.tcd_parse (tcd_parse),
|
||||
.tcd_loaded (tcd_loaded),
|
||||
.tcd_fetch_pending (tcd_fetch_pending),
|
||||
.tcd_abort (tcd_abort),
|
||||
.awvalid (awvalid),
|
||||
.awready (awready),
|
||||
.awid (awid),
|
||||
@@ -462,10 +476,13 @@ module dma_formal;
|
||||
if (arvalid) begin
|
||||
assert (arid == 1'b0);
|
||||
assert (arsize == 3'd2);
|
||||
assert (arlen <= 8'd3);
|
||||
assert (arlen <= 8'd15);
|
||||
assert ((arburst == 2'b00) || (arburst == 2'b01));
|
||||
assert (araddr[1:0] == 2'b00);
|
||||
if (arburst == 2'b00) begin
|
||||
if (araddr == 32'h2000_0000) begin
|
||||
assert (arburst == 2'b01);
|
||||
assert (arlen == 8'd15);
|
||||
end else if (arburst == 2'b00) begin
|
||||
assert (arlen == 8'd0);
|
||||
end else begin
|
||||
assert ({1'b0, araddr[11:0]} + (({5'd0, arlen} + 13'd1) << 2) <= 13'd4096);
|
||||
@@ -517,6 +534,16 @@ module dma_formal;
|
||||
end
|
||||
end
|
||||
|
||||
if (tcd_mode) begin
|
||||
if (tcd_abort) begin
|
||||
assert (!tcd_loaded);
|
||||
assert (!tcd_fetch_pending);
|
||||
end
|
||||
if (f_past_valid && $past(tcd_parse) && $past(tcd_abort)) begin
|
||||
assert (!tcd_loaded);
|
||||
end
|
||||
end
|
||||
|
||||
if (write_owner_valid && wvalid) begin
|
||||
assert (!fifo_empty[write_owner]);
|
||||
end
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
module dma_tcd_cancel_formal;
|
||||
(* gclk *)reg clk_i;
|
||||
wire rst_n_i;
|
||||
wire [3:0] start_i;
|
||||
wire [3:0] abort_i;
|
||||
wire [3:0] aborted;
|
||||
wire tcd_mode;
|
||||
wire tcd_read_owner_valid;
|
||||
wire tcd_parse;
|
||||
wire tcd_loaded;
|
||||
wire tcd_fetch_pending;
|
||||
wire tcd_abort;
|
||||
|
||||
dma_formal_design u_design (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.start_i (start_i),
|
||||
.abort_i (abort_i),
|
||||
.aborted (aborted),
|
||||
.tcd_mode (tcd_mode),
|
||||
.tcd_read_owner_valid(tcd_read_owner_valid),
|
||||
.tcd_parse (tcd_parse),
|
||||
.tcd_loaded (tcd_loaded),
|
||||
.tcd_fetch_pending (tcd_fetch_pending),
|
||||
.tcd_abort (tcd_abort)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (rst_n_i) begin
|
||||
assume (tcd_mode);
|
||||
if (tcd_abort) begin
|
||||
assert (!tcd_loaded);
|
||||
assert (!tcd_fetch_pending);
|
||||
end
|
||||
cover (tcd_fetch_pending && abort_i[0]);
|
||||
cover (aborted[0] && !start_i[0]);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -1,6 +1,24 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"targets": [
|
||||
{
|
||||
"name": "spi-p2",
|
||||
"paths": ["rtl/ip/serial/spi_dma_pacing.sv", "rtl/ip/peripheral/dma_core.sv",
|
||||
"rtl/ip/peripheral/dma_reg.sv", "rtl/ip/peripheral/apb4_dma.sv"],
|
||||
"status": "prototype",
|
||||
"baseline_revision": "4b3553faa7dfeb2603d717904a2c1fbf79f168f9",
|
||||
"configuration_digest": null,
|
||||
"configuration_profiles": ["configs/ci/ics55-tiny.mk", "configs/ci/ihp130-tiny.mk"],
|
||||
"required_evidence": [],
|
||||
"synthesis_intent": {
|
||||
"registers": "DMA V2.2 parameterized version/support bitmap with preserved V2.0 product defaults",
|
||||
"reset": "credit reservations clear on reset; descriptor cancellation retains ownership until responses drain",
|
||||
"clock_enable": "PCLK credit pacing; existing DMA AXI crossing unchanged",
|
||||
"memory": "no new storage; existing DMA FIFOs and descriptor state only",
|
||||
"pipeline": "admission credit, FIFO commit, terminal response retirement; product SPI grant remains disabled"
|
||||
},
|
||||
"waivers": []
|
||||
},
|
||||
{
|
||||
"name": "spi-p1",
|
||||
"paths": ["rtl/ip/serial/apb4_spi.sv", "rtl/ip/serial/spi_reg.sv",
|
||||
|
||||
@@ -268,6 +268,7 @@
|
||||
"apb4_spi.sv",
|
||||
"spi_ctrl_if.sv",
|
||||
"spi_define.svh",
|
||||
"spi_dma_pacing.sv",
|
||||
"spi_gpio_guard.sv",
|
||||
"spi_master_core.sv",
|
||||
"spi_phase_ctrl.sv",
|
||||
|
||||
@@ -32,6 +32,10 @@ and simulation verdicts under the selected Tiny build variant. It does not
|
||||
instantiate SPI in Tiny or implement central-DMA pacing. See the
|
||||
[SPI-P1 runbook](../docs/ip/spi-p1.md) for scope and pending qualification.
|
||||
|
||||
`spi_p2.py` runs the focused V2.2 credit-pacing fixture and lint boundary.
|
||||
The product DMA remains V2.0/default-disabled until shared integration is
|
||||
qualified; this entrypoint does not wire Tiny SPI.
|
||||
|
||||
`generate_apu_kws_rtl_constants.py` also emits the frozen APU-P9 APUC image
|
||||
and 15-bank layout manifest. `apu_kws_coeff.py` owns the independent APUC 1.0
|
||||
schema and release validator; generated assets remain below the selected
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ WRAPPER = ROOT / f"physical/smoke/syn/yosys/{TOP}.sv"
|
||||
SOURCES = [COMMON / "interface/apb4_if.sv", COMMON / "utils/register.sv",
|
||||
ROOT / "rtl/ip/util/soc_common_fifo.sv",
|
||||
*[IP / f"{name}.sv" for name in (
|
||||
"spi_pkg", "spi_ctrl_if", "spi_gpio_guard", "spi_phase_ctrl",
|
||||
"spi_pkg", "spi_ctrl_if", "spi_gpio_guard", "spi_phase_ctrl", "spi_dma_pacing",
|
||||
"spi_word_packer", "spi_master_core", "spi_reg", "apb4_spi")], WRAPPER]
|
||||
INCLUDES = [COMMON, COMMON / "utils", IP, ROOT / "tests/rtl"]
|
||||
CASES = ("spi_pin_checker_tb", "spi_core_tb", "spi_apb_tb", "spi_lifecycle_tb", "spi_packer_tb")
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Focused SPI-P2 DMA V2.2 evidence driver; product integration stays disabled."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
SOURCES = [ROOT / "rtl/ip/serial/spi_dma_pacing.sv",
|
||||
ROOT / "rtl/ip/peripheral/dma_define.svh", ROOT / "rtl/ip/peripheral/dma_pkg.sv",
|
||||
ROOT / "rtl/ip/peripheral/dma_core.sv", ROOT / "rtl/ip/peripheral/dma_reg.sv",
|
||||
ROOT / "rtl/ip/peripheral/apb4_dma.sv", ROOT / "tests/rtl/spi_dma_pacing_tb.sv",
|
||||
ROOT / "tests/test_spi_p2.py", ROOT / "scripts/spi_p2.py",
|
||||
ROOT / "crt/include/retrosoc/hal/dma.h",
|
||||
ROOT / "crt/include/retrosoc/hal/dma_regs.h",
|
||||
ROOT / "crt/src/hal/dma.c", ROOT / "crt/src/hal/dma_math.c",
|
||||
ROOT / "rtl/mini/formal/dma_formal.sv",
|
||||
ROOT / "rtl/mini/formal/dma_formal_props.sv",
|
||||
ROOT / "rtl/mini/formal/dma_tcd_cancel_props.sv"]
|
||||
PACING_TB = ROOT / "tests/rtl/spi_dma_pacing_tb.sv"
|
||||
|
||||
def digest(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("stage", choices=("lint", "rtl", "report"))
|
||||
parser.add_argument("--variant-root", type=Path, required=True)
|
||||
parser.add_argument("--pdk", choices=("ICS55", "IHP130"), required=True)
|
||||
args = parser.parse_args()
|
||||
variant = args.variant_root.resolve()
|
||||
variant.relative_to(ROOT / "build")
|
||||
manifest = json.loads((variant / "meta/manifest.json").read_text())
|
||||
if manifest["configuration"]["SOC"] != "TINY" or manifest["configuration"]["PDK"] != args.pdk:
|
||||
raise ValueError("profile/PDK mismatch")
|
||||
directory = variant / "meta/spi-p2" / args.stage
|
||||
if directory.exists():
|
||||
raise ValueError(f"existing SPI-P2 attempt: {directory}")
|
||||
directory.mkdir(parents=True)
|
||||
source = {str(path.relative_to(ROOT)): digest(path) for path in SOURCES}
|
||||
record = {"phase": "SPI-P2", "stage": args.stage, "status": "running",
|
||||
"source": source, "manifest": manifest, "phase_accepted": False}
|
||||
try:
|
||||
common = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
if args.stage == "rtl":
|
||||
command = ["iverilog", "-g2012", "-s", "spi_dma_pacing_tb", "-o",
|
||||
str(directory / "spi_dma_pacing_tb"), f"-I{common}", f"-I{common / 'utils'}",
|
||||
str(common / "utils/register.sv"), str(SOURCES[0]), str(PACING_TB)]
|
||||
result = subprocess.run(command, cwd=ROOT, capture_output=True, text=True, check=False)
|
||||
(directory / "compile.log").write_text(result.stdout + result.stderr)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError("SPI-P2 pacing compile failed")
|
||||
run = subprocess.run(["vvp", str(directory / "spi_dma_pacing_tb")], cwd=ROOT,
|
||||
capture_output=True, text=True, check=False)
|
||||
(directory / "sim.log").write_text(run.stdout + run.stderr)
|
||||
if run.returncode != 0 or "SPI_P2_PASS spi_dma_pacing_tb" not in run.stdout:
|
||||
raise RuntimeError("SPI-P2 pacing fixture failed")
|
||||
elif args.stage == "lint":
|
||||
result = subprocess.run(["verilator", "--lint-only", "--Wall", "-Wno-fatal",
|
||||
"-Wno-DECLFILENAME", "--top-module",
|
||||
"spi_dma_pacing", f"-I{common}", str(common / "utils/register.sv"),
|
||||
str(SOURCES[0])], cwd=ROOT,
|
||||
capture_output=True, text=True, check=False)
|
||||
(directory / "lint.log").write_text(result.stdout + result.stderr)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError("SPI-P2 pacing lint failed")
|
||||
record["status"] = "passed"
|
||||
except (OSError, RuntimeError) as error:
|
||||
record.update(status="failed", error=str(error))
|
||||
print(error, file=sys.stderr)
|
||||
return 1
|
||||
record["artifacts"] = {str(path): digest(path) for path in directory.rglob("*") if path.is_file()}
|
||||
atomic_write(directory / "record.json", json.dumps(record, indent=2, sort_keys=True) + "\n")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -130,6 +130,9 @@ components and incompatible historical inventories. The
|
||||
`spi-p1-rtl` and backend targets retain separate qualification artifacts;
|
||||
they are not a Tiny SPI firmware integration result.
|
||||
|
||||
`test_spi_p2.py` covers the V2.2 request split, support-register ABI,
|
||||
product-default disable policy and source-qualified credit pacing fixture.
|
||||
|
||||
Historical Tiny publication fixtures also need the main repository history
|
||||
and dependency commit objects named by their archived locks. After ordinary
|
||||
dependency setup, run `python3 tests/publication_history.py prepare` before
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
`timescale 1ns / 1ps
|
||||
module spi_dma_pacing_tb;
|
||||
logic clk_i = 0, rst_n_i = 0, enable_i = 1, dma_error_i = 0;
|
||||
logic [31:0] tx_binding_i = 32'h80000003, rx_binding_i = 32'h80000002;
|
||||
logic [1:0] dma_start_pending_i = 0, dma_status_i = 0;
|
||||
logic [1:0] credit_valid_i = 0, credit_retire_i = 0, credit_commit_i = 0;
|
||||
logic [1:0] credit_valid_o, credit_retire_o, credit_pending_o;
|
||||
logic [3:0] dma_status_o;
|
||||
logic credit_fault_o, busy_o;
|
||||
always #5 clk_i = !clk_i;
|
||||
spi_dma_pacing u_dut (.*);
|
||||
task automatic tick;
|
||||
@(posedge clk_i);
|
||||
#1;
|
||||
endtask
|
||||
initial begin
|
||||
repeat (2) tick();
|
||||
rst_n_i = 1;
|
||||
tick();
|
||||
credit_valid_i = 2'b01;
|
||||
#1;
|
||||
if (credit_valid_o != 2'b01 || credit_pending_o != 2'b01) $fatal(1, "credit admission");
|
||||
credit_commit_i = 2'b01;
|
||||
tick();
|
||||
credit_valid_i = 0;
|
||||
credit_commit_i = 0;
|
||||
#1;
|
||||
if (credit_pending_o != 2'b01 || credit_valid_o != 0 || !busy_o) $fatal(1, "credit commit");
|
||||
credit_retire_i = 2'b01;
|
||||
tick();
|
||||
credit_retire_i = 0;
|
||||
if (credit_pending_o != 0 || busy_o) $fatal(1, "credit retire");
|
||||
tx_binding_i = 32'h80000002;
|
||||
#1;
|
||||
if (!credit_fault_o) $fatal(1, "binding conflict");
|
||||
tx_binding_i = 32'h80000003;
|
||||
enable_i = 0;
|
||||
credit_valid_i = 2'b10;
|
||||
#1;
|
||||
if (!credit_fault_o || credit_valid_o != 0) $fatal(1, "disabled admission");
|
||||
$display("SPI_P2_PASS spi_dma_pacing_tb");
|
||||
$finish;
|
||||
end
|
||||
initial begin
|
||||
#1000;
|
||||
$fatal(1, "SIM_TEST_TIMEOUT spi_dma_pacing_tb");
|
||||
end
|
||||
endmodule
|
||||
@@ -54,6 +54,7 @@ def test_dma_register_offsets_match_rtl() -> None:
|
||||
"APB4_DMA__IRQ_TEST": "RS_DMA_REG_IRQ_TEST",
|
||||
"APB4_DMA__ERROR_SUMMARY": "RS_DMA_REG_ERROR_SUMMARY",
|
||||
"APB4_DMA__REQUEST_STATUS": "RS_DMA_REG_REQUEST_STATUS",
|
||||
"APB4_DMA__SUPPORTED_REQUESTS": "RS_DMA_REG_SUPPORTED_REQUESTS",
|
||||
"APB4_DMA__CH_BASE": "RS_DMA_CH_BASE",
|
||||
"APB4_DMA__CH_STRIDE": "RS_DMA_CH_STRIDE",
|
||||
"APB4_DMA__CH_CTRL": "RS_DMA_CH_REG_CTRL",
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
"""Focused SPI-P2 DMA V2.2 and credit-pacing checks."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_dma_v22_abi_and_product_defaults():
|
||||
define = (ROOT / "rtl/ip/peripheral/dma_define.svh").read_text()
|
||||
pkg = (ROOT / "rtl/ip/peripheral/dma_pkg.sv").read_text()
|
||||
regs = (ROOT / "crt/include/retrosoc/hal/dma_regs.h").read_text()
|
||||
core = (ROOT / "rtl/ip/peripheral/dma_core.sv").read_text()
|
||||
tiny = (ROOT / "rtl/tiny/top/retrosoc_tiny.sv").read_text()
|
||||
assert "APB4_DMA__SUPPORTED_REQUESTS" in define
|
||||
assert "RS_DMA_REG_SUPPORTED_REQUESTS" in regs
|
||||
assert "DMA_REQUEST_SPI_TX = 5'd16" in pkg
|
||||
assert "DMA_REQUEST_SPI_RX = 5'd17" in pkg
|
||||
assert "s_tcd_word7_q[s_tcd_read_owner_q][25]" in core
|
||||
assert "logic [ NumChannels-1:0][ 4:0] s_req_q" in core
|
||||
assert "!EnableSpi && ((s_cfg_req[channel] == DMA_REQUEST_SPI_TX)" in core
|
||||
assert "s_tcd_parse_q &&" in core
|
||||
assert ".RequestMask (16'h39ff)" in tiny
|
||||
assert ".EnableSpi" not in tiny
|
||||
|
||||
|
||||
def test_dma_v22_tcd_request_split_is_handwritten_and_stable():
|
||||
c_header = (ROOT / "crt/include/retrosoc/hal/dma.h").read_text()
|
||||
c_math = (ROOT / "crt/src/hal/dma_math.c").read_text()
|
||||
c_dma = (ROOT / "crt/src/hal/dma.c").read_text()
|
||||
assert "RS_DMA_TCD_REQUEST_HIGH" in c_header
|
||||
assert "request |= UINT32_C(0x10)" in c_math
|
||||
assert "RS_DMA_TCD_REQUEST_HIGH" in c_dma
|
||||
|
||||
|
||||
def test_dma_v22_hal_mask_and_discovery_order():
|
||||
c_math = (ROOT / "crt/src/hal/dma_math.c").read_text()
|
||||
c_dma = (ROOT / "crt/src/hal/dma.c").read_text()
|
||||
core = (ROOT / "rtl/ip/peripheral/dma_core.sv").read_text()
|
||||
apb = (ROOT / "rtl/ip/peripheral/apb4_dma.sv").read_text()
|
||||
assert "UINT32_C(0x000339FF)" in c_math
|
||||
assert "RS_DMA_V22" not in c_math
|
||||
assert c_dma.index("rs_dma_supports_request(config->request)") < c_dma.index(
|
||||
"rs_dma_config_validate(channel, config)"
|
||||
)
|
||||
assert "s_cfg_req[channel] > DMA_REQUEST_SPI_RX" in core
|
||||
assert "s_cfg_req_raw[channel][31:5] != 27'd0" in core
|
||||
assert "32'h0003_ffff" in apb
|
||||
assert "EnableStreams ?" in apb
|
||||
|
||||
|
||||
def test_dma_stream_status_is_disabled_with_streams_off():
|
||||
core = (ROOT / "rtl/ip/peripheral/dma_core.sv").read_text()
|
||||
for request in (
|
||||
"DMA_REQUEST_I2S_TX",
|
||||
"DMA_REQUEST_I2S_RX",
|
||||
"DMA_REQUEST_DVP_RX",
|
||||
"DMA_REQUEST_CRYPTO_IN",
|
||||
"DMA_REQUEST_CRYPTO_OUT",
|
||||
):
|
||||
match = re.search(rf"s_req_ready\[{request}\]\s*=\s*([^;]+);", core)
|
||||
assert match is not None
|
||||
assert "EnableStreams" in match.group(1)
|
||||
|
||||
|
||||
def test_dma_v21_descriptor_cancel_guards_are_structural():
|
||||
core = (ROOT / "rtl/ip/peripheral/dma_core.sv").read_text()
|
||||
formal = (ROOT / "rtl/mini/formal/dma_formal_props.sv").read_text()
|
||||
harness = (ROOT / "rtl/mini/formal/dma_formal.sv").read_text()
|
||||
tcd_props = (ROOT / "rtl/mini/formal/dma_tcd_cancel_props.sv").read_text()
|
||||
fetch = core[core.index("s_tcd_fetch_pending_q[channel]"):core.index("always_comb begin", core.index("s_tcd_fetch_pending_q[channel]"))]
|
||||
parse_start = core.index("if (s_tcd_parse_q)")
|
||||
parse = core[parse_start:core.index("if (s_axi_read_beat_valid && s_read_owner_valid_q)", parse_start)]
|
||||
ownership = core[core.index("s_read_owns_channel = '0"):core.index("always_ff @(posedge clk_i", core.index("s_read_owns_channel = '0"))]
|
||||
assert "!s_abort_q[channel]" in fetch
|
||||
assert "!abort_i[channel]" in fetch
|
||||
assert "!s_tcd_parse_q" in fetch
|
||||
assert "if (s_abort_q[s_tcd_read_owner_q])" in parse
|
||||
assert "s_tcd_parse_q &&" in ownership
|
||||
assert "s_tcd_fetch_pending_q[channel] <= 1'b0" in core
|
||||
assert "tcd_read_owner_valid" in formal
|
||||
assert "tcd_word" in harness
|
||||
assert "cover (tcd_fetch_pending && abort_i[0])" in tcd_props
|
||||
assert "assume (tcd_mode)" in tcd_props
|
||||
|
||||
|
||||
def test_dma_v22_old_version_rejection_precedes_support_read():
|
||||
c_dma = (ROOT / "crt/src/hal/dma.c").read_text()
|
||||
start = c_dma.index("rs_status_t rs_dma_supports_request")
|
||||
body = c_dma[start:c_dma.index("static rs_status_t rs_dma_configure_owned", start)]
|
||||
assert body.index("version = RS_DMA_REG(RS_DMA_REG_IP_VERSION)") < body.index(
|
||||
"supported = RS_DMA_REG(RS_DMA_REG_SUPPORTED_REQUESTS)"
|
||||
)
|
||||
assert "version < RS_DMA_IP_VERSION_V22" in body
|
||||
|
||||
|
||||
def test_spi_dma_pacing_fixture(tmp_path):
|
||||
iverilog = shutil.which("iverilog")
|
||||
vvp = shutil.which("vvp")
|
||||
assert iverilog and vvp
|
||||
common = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
sim = tmp_path / "spi_dma_pacing_tb"
|
||||
subprocess.run([
|
||||
iverilog, "-g2012", "-s", "spi_dma_pacing_tb", "-o", str(sim),
|
||||
f"-I{common}", f"-I{common / 'utils'}", str(common / "utils/register.sv"),
|
||||
str(ROOT / "rtl/ip/serial/spi_dma_pacing.sv"),
|
||||
str(ROOT / "tests/rtl/spi_dma_pacing_tb.sv")],
|
||||
check=True, capture_output=True, text=True)
|
||||
run = subprocess.run([vvp, str(sim)], check=True, capture_output=True, text=True)
|
||||
assert "SPI_P2_PASS spi_dma_pacing_tb" in run.stdout
|
||||
assert not re.search(r"FAILED|FATAL|SIM_TEST_TIMEOUT", run.stdout + run.stderr)
|
||||
Reference in new issue
Block a user