feat: add USB2 support and related tests

- Introduced a new USB2 transaction engine testbench to validate the functionality of the USB2 transaction engine.
- Updated clock and reset domain tests to include USB2 ULPI crossings.
- Enhanced memory map tests to verify USB2 base addresses and performance offsets.
- Modified pin map tests to ensure USB2 ULPI pins are correctly generated.
- Updated SOC topology tests to account for USB2 peripherals and IRQ mappings.
- Added static timing analysis constraints for USB2 clock domains.
- Created dedicated USB2 tests for various components including packet RAM, descriptor validation, and transaction engine.
- Implemented a register parity test to ensure ABI consistency between RTL and C header definitions.
This commit is contained in:
maksyuki committed 2026-08-22 07:15:41 +08:00
1 parent e6e3a06136
commit e219973c45
86 files changed
+11305 -48

No files matched your search

+1 -1
View File
@@ -23,7 +23,7 @@ on:
timeout_minutes:
description: Maximum job duration
required: false
default: 240
default: 360
type: number
toolset:
description: Space-separated locked tools required by this suite
+5 -1
View File
@@ -2,6 +2,7 @@
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/crypto.h>
#include <retrosoc/hal/sdio.h>
#include <retrosoc/hal/usb2.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/service/booter.h>
#include <retrosoc/service/test.h>
@@ -15,9 +16,12 @@ int main(void) {
if (rs_sdio_controller_selftest(RS_SDIO_INSTANCE_0) != RS_OK) {
rs_test_finish(RS_TEST_FAILED, 1U);
}
if (rs_crypto_selftest(RS_TIMEOUT_DEFAULT) != RS_OK) {
if (rs_usb2_controller_selftest() != RS_OK) {
rs_test_finish(RS_TEST_FAILED, 2U);
}
if (rs_crypto_selftest(RS_TIMEOUT_DEFAULT) != RS_OK) {
rs_test_finish(RS_TEST_FAILED, 3U);
}
rs_test_finish(RS_TEST_PASSED, 0U);
return 0;
}
+5
View File
@@ -9,6 +9,7 @@
#include <retrosoc/hal/sdram.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/hal/usb2.h>
#include <retrosoc/hal/user_ip.h>
#include <retrosoc/lib/printf.h>
#include <retrosoc/service/test.h>
@@ -320,6 +321,10 @@ int main(void) {
rs_test_finish(RS_TEST_FAILED, 10U);
}
printf("ci_smoke: crypto passed\n");
if (rs_usb2_controller_selftest() != RS_OK) {
rs_test_finish(RS_TEST_FAILED, 12U);
}
printf("ci_smoke: USB2 passed\n");
printf("ci_smoke: all peripheral tests passed\n");
rs_test_finish(RS_TEST_PASSED, 0U);
+1 -1
View File
@@ -23,7 +23,7 @@ HELLO_LOOP:
APP_INIT:
li t1, 1
li t2, 6
li t2, 1
APP_LOOP:
la s0, msg_luck
li a0, 67
+9
View File
@@ -76,6 +76,15 @@ bits `[7:0]`/strobe lane 0 is the first wire byte, and `DATA_START` is the
coordinated DMA/card-data launch. The SDK does not provide hotplug,
filesystem, combo-card, UHS, DDR, or 1.8 V support.
`<retrosoc/hal/usb2.h>` provides the ULPI USB 2.0 controller primitives for
role configuration, eight bidirectional device endpoints, sixteen host
channels, interrupts, USB3320 register viewport access, and 32-byte AXI4
scatter-gather descriptors. The register ABI is mirrored by hand in
`<retrosoc/hal/usb2_regs.h>` and checked against RTL without a register
generator. This layer is a DCD/HCD foundation; enumeration, class, hub, cache
maintenance, and USB-IF compliance remain integration responsibilities. See
the [USB2 controller contract](../docs/ip/usb2.md).
`<retrosoc/hal/spisd.h>` provides the separate SPI-mode SD Memory host API.
It supports bounded SDSC/SDHC enumeration, PIO and native-AXI SG-DMA sector
transfers, interrupt/error handling, CSD geometry, and an explicit verified
+1
View File
@@ -11,6 +11,7 @@ typedef struct {
uint64_t dma_wait;
uint64_t sdio0_wait;
uint64_t sdio1_wait;
uint64_t usb2_wait;
uint64_t apb4_periph_wait;
uint64_t apb4_system_wait;
uint64_t sdram_wait;
+1
View File
@@ -17,6 +17,7 @@ typedef enum {
RS_SYSCTRL_PERF_FLASH_WAIT = 7,
RS_SYSCTRL_PERF_SDIO0_WAIT = 8,
RS_SYSCTRL_PERF_SDIO1_WAIT = 9,
RS_SYSCTRL_PERF_USB2_WAIT = 10,
} rs_sysctrl_perf_counter_t;
typedef struct {
+134
View File
@@ -0,0 +1,134 @@
#ifndef RETROSOC_HAL_USB2_H
#define RETROSOC_HAL_USB2_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/status.h>
#include <retrosoc/hal/usb2_regs.h>
typedef enum {
RS_USB2_ROLE_IDLE = RS_USB2_ABI_ROLE_IDLE,
RS_USB2_ROLE_DEVICE = RS_USB2_ABI_ROLE_DEVICE,
RS_USB2_ROLE_HOST = RS_USB2_ABI_ROLE_HOST
} rs_usb2_role_t;
typedef enum {
RS_USB2_SPEED_HIGH = 0,
RS_USB2_SPEED_FULL = 1,
RS_USB2_SPEED_LOW = 2
} rs_usb2_speed_t;
typedef enum {
RS_USB2_TRANSFER_CONTROL = 0,
RS_USB2_TRANSFER_ISOCHRONOUS = 1,
RS_USB2_TRANSFER_BULK = 2,
RS_USB2_TRANSFER_INTERRUPT = 3
} rs_usb2_transfer_type_t;
typedef enum { RS_USB2_DIRECTION_OUT = 0, RS_USB2_DIRECTION_IN = 1 } rs_usb2_direction_t;
typedef struct {
rs_usb2_role_t role;
uint32_t transaction_timeout;
uint32_t irq_enable;
bool auto_role;
bool force_full_speed;
bool release_phy_reset;
bool enable_interrupts;
} rs_usb2_config_t;
typedef struct {
uint8_t number;
rs_usb2_transfer_type_t transfer_type;
uint16_t max_packet;
uint16_t ram_in_base;
uint16_t ram_in_length;
uint16_t ram_out_base;
uint16_t ram_out_length;
uintptr_t descriptor_in;
uintptr_t descriptor_out;
bool enable_in;
bool enable_out;
bool stall_in;
bool stall_out;
} rs_usb2_endpoint_config_t;
typedef struct {
uint8_t channel;
uint8_t device_address;
uint8_t endpoint;
rs_usb2_direction_t direction;
rs_usb2_transfer_type_t transfer_type;
rs_usb2_speed_t speed;
uint16_t max_packet;
uint16_t interval;
uint16_t ram_base;
uint16_t ram_length;
uintptr_t descriptor;
bool setup;
bool ping_enable;
bool data_toggle;
} rs_usb2_channel_config_t;
typedef struct {
uint32_t global;
uint32_t role;
uint32_t phy;
uint32_t frame;
uint32_t port;
uint32_t irq_status;
uint32_t error_status;
uint32_t error_code;
uint32_t error_info;
uint32_t error_descriptor;
uint32_t error_buffer;
bool enabled;
bool busy;
bool link_ready;
} rs_usb2_status_t;
static inline void rs_usb2_memory_barrier(void) {
#if defined(__riscv)
__asm__ volatile("fence rw, rw" ::: "memory");
#else
__atomic_thread_fence(__ATOMIC_SEQ_CST);
#endif
}
rs_status_t rs_usb2_validate_dma_buffer(const void *buffer, size_t byte_count);
rs_status_t rs_usb2_descriptor_prepare(rs_usb2_descriptor_t *descriptor, uintptr_t buffer,
size_t byte_count, uintptr_t next, bool end, bool irq,
bool short_ok, bool zero_packet, uint32_t frame);
rs_status_t rs_usb2_descriptor_validate(const rs_usb2_descriptor_t *descriptor);
rs_status_t rs_usb2_descriptor_chain_validate(const rs_usb2_descriptor_t *descriptors,
uint16_t count, uint32_t total_bytes);
rs_status_t rs_usb2_descriptor_publish(rs_usb2_descriptor_t *descriptor);
rs_status_t rs_usb2_descriptor_publish_chain(rs_usb2_descriptor_t *descriptors, uint16_t count);
rs_status_t rs_usb2_probe(uint32_t *ip_id, uint32_t *version, uint32_t *capability0,
uint32_t *capability1);
rs_status_t rs_usb2_reset(rs_timeout_t timeout);
rs_status_t rs_usb2_configure(const rs_usb2_config_t *config);
rs_status_t rs_usb2_enable(bool enable);
rs_status_t rs_usb2_status_get(rs_usb2_status_t *status);
rs_status_t rs_usb2_device_address_set(uint8_t address);
rs_status_t rs_usb2_endpoint_configure(const rs_usb2_endpoint_config_t *config);
rs_status_t rs_usb2_endpoint_submit(uint8_t endpoint, rs_usb2_direction_t direction);
rs_status_t rs_usb2_endpoint_cancel(uint8_t endpoint);
rs_status_t rs_usb2_endpoint_completion(uint32_t *in_mask, uint32_t *out_mask);
rs_status_t rs_usb2_endpoint_completion_clear(uint32_t in_mask, uint32_t out_mask);
rs_status_t rs_usb2_channel_configure(const rs_usb2_channel_config_t *config);
rs_status_t rs_usb2_channel_start(uint8_t channel);
rs_status_t rs_usb2_channel_cancel(uint8_t channel);
rs_status_t rs_usb2_channel_status_get(uint8_t channel, uint32_t *status, uint32_t *bytes);
rs_status_t rs_usb2_irq_enable(uint32_t mask);
rs_status_t rs_usb2_irq_pending(uint32_t *mask);
rs_status_t rs_usb2_irq_clear(uint32_t mask);
rs_status_t rs_usb2_irq_test(uint32_t mask);
rs_status_t rs_usb2_ulpi_read(uint8_t address, uint8_t *data, rs_timeout_t timeout);
rs_status_t rs_usb2_ulpi_write(uint8_t address, uint8_t data, rs_timeout_t timeout);
rs_status_t rs_usb2_controller_selftest(void);
#endif
+261
View File
@@ -0,0 +1,261 @@
#ifndef RETROSOC_HAL_USB2_REGS_H
#define RETROSOC_HAL_USB2_REGS_H
#include <stdint.h>
/* Hand-maintained mirror of rtl/ip/usb/usb2_define.svh. */
#define RS_USB2_ABI_IP_ID UINT32_C(0x000)
#define RS_USB2_ABI_IP_VERSION UINT32_C(0x004)
#define RS_USB2_ABI_CAPABILITY0 UINT32_C(0x008)
#define RS_USB2_ABI_CAPABILITY1 UINT32_C(0x00C)
#define RS_USB2_ABI_GLOBAL_CTRL UINT32_C(0x010)
#define RS_USB2_ABI_GLOBAL_STATUS UINT32_C(0x014)
#define RS_USB2_ABI_ROLE_CTRL UINT32_C(0x018)
#define RS_USB2_ABI_ROLE_STATUS UINT32_C(0x01C)
#define RS_USB2_ABI_PHY_CTRL UINT32_C(0x020)
#define RS_USB2_ABI_PHY_STATUS UINT32_C(0x024)
#define RS_USB2_ABI_ULPI_VIEWPORT UINT32_C(0x028)
#define RS_USB2_ABI_ULPI_VIEWPORT_DATA UINT32_C(0x02C)
#define RS_USB2_ABI_TIMEOUT UINT32_C(0x030)
#define RS_USB2_ABI_FRAME UINT32_C(0x034)
#define RS_USB2_ABI_TEST_CTRL UINT32_C(0x038)
#define RS_USB2_ABI_PIO_DATA UINT32_C(0x03C)
#define RS_USB2_ABI_IRQ_STATUS UINT32_C(0x040)
#define RS_USB2_ABI_IRQ_ENABLE UINT32_C(0x044)
#define RS_USB2_ABI_IRQ_TEST UINT32_C(0x048)
#define RS_USB2_ABI_ERROR_STATUS UINT32_C(0x04C)
#define RS_USB2_ABI_ERROR_CODE UINT32_C(0x050)
#define RS_USB2_ABI_ERROR_INFO UINT32_C(0x054)
#define RS_USB2_ABI_ERROR_DESC_ADDR UINT32_C(0x058)
#define RS_USB2_ABI_ERROR_BUFFER_ADDR UINT32_C(0x05C)
#define RS_USB2_ABI_PERF_TX_BYTES UINT32_C(0x060)
#define RS_USB2_ABI_PERF_RX_BYTES UINT32_C(0x064)
#define RS_USB2_ABI_PERF_PACKETS UINT32_C(0x068)
#define RS_USB2_ABI_PERF_RETRIES UINT32_C(0x06C)
#define RS_USB2_ABI_PERF_AXI_STALL UINT32_C(0x070)
#define RS_USB2_ABI_PERF_RAM_STALL UINT32_C(0x074)
#define RS_USB2_ABI_PERF_IRQ_COUNT UINT32_C(0x078)
#define RS_USB2_ABI_PERF_CTRL UINT32_C(0x07C)
#define RS_USB2_ABI_DEVICE_CTRL UINT32_C(0x100)
#define RS_USB2_ABI_DEVICE_ADDR UINT32_C(0x104)
#define RS_USB2_ABI_DEVICE_STATUS UINT32_C(0x108)
#define RS_USB2_ABI_SETUP0 UINT32_C(0x10C)
#define RS_USB2_ABI_SETUP1 UINT32_C(0x110)
#define RS_USB2_ABI_ENDPOINT_PENDING_IN UINT32_C(0x114)
#define RS_USB2_ABI_ENDPOINT_PENDING_OUT UINT32_C(0x118)
#define RS_USB2_ABI_ENDPOINT_COMPLETE_IN UINT32_C(0x11C)
#define RS_USB2_ABI_ENDPOINT_COMPLETE_OUT UINT32_C(0x120)
#define RS_USB2_ABI_ENDPOINT_BASE UINT32_C(0x200)
#define RS_USB2_ABI_ENDPOINT_STRIDE UINT32_C(0x040)
#define RS_USB2_ABI_ENDPOINT_CFG UINT32_C(0x000)
#define RS_USB2_ABI_ENDPOINT_RAM_IN UINT32_C(0x004)
#define RS_USB2_ABI_ENDPOINT_RAM_OUT UINT32_C(0x008)
#define RS_USB2_ABI_ENDPOINT_DESC_IN UINT32_C(0x00C)
#define RS_USB2_ABI_ENDPOINT_DESC_OUT UINT32_C(0x010)
#define RS_USB2_ABI_ENDPOINT_COMMAND UINT32_C(0x014)
#define RS_USB2_ABI_ENDPOINT_STATUS UINT32_C(0x018)
#define RS_USB2_ABI_ENDPOINT_BYTES_IN UINT32_C(0x01C)
#define RS_USB2_ABI_ENDPOINT_BYTES_OUT UINT32_C(0x020)
#define RS_USB2_ABI_HOST_CTRL UINT32_C(0x400)
#define RS_USB2_ABI_HOST_STATUS UINT32_C(0x404)
#define RS_USB2_ABI_PORT_CTRL UINT32_C(0x408)
#define RS_USB2_ABI_PORT_STATUS UINT32_C(0x40C)
#define RS_USB2_ABI_SCHEDULE_CTRL UINT32_C(0x410)
#define RS_USB2_ABI_SCHEDULE_STATUS UINT32_C(0x414)
#define RS_USB2_ABI_CHANNEL_BASE UINT32_C(0x500)
#define RS_USB2_ABI_CHANNEL_STRIDE UINT32_C(0x040)
#define RS_USB2_ABI_CHANNEL_CFG UINT32_C(0x000)
#define RS_USB2_ABI_CHANNEL_TARGET UINT32_C(0x004)
#define RS_USB2_ABI_CHANNEL_INTERVAL UINT32_C(0x008)
#define RS_USB2_ABI_CHANNEL_RAM UINT32_C(0x00C)
#define RS_USB2_ABI_CHANNEL_DESC UINT32_C(0x010)
#define RS_USB2_ABI_CHANNEL_COMMAND UINT32_C(0x014)
#define RS_USB2_ABI_CHANNEL_STATUS UINT32_C(0x018)
#define RS_USB2_ABI_CHANNEL_BYTES UINT32_C(0x01C)
#define RS_USB2_ABI_RAM_CTRL UINT32_C(0x900)
#define RS_USB2_ABI_RAM_STATUS UINT32_C(0x904)
#define RS_USB2_ABI_ECC_STATUS UINT32_C(0x908)
#define RS_USB2_ABI_ECC_CORRECTED_COUNT UINT32_C(0x90C)
#define RS_USB2_ABI_ECC_UNCORRECTABLE_COUNT UINT32_C(0x910)
#define RS_USB2_ABI_RAM_BIST UINT32_C(0x914)
#define RS_USB2_ABI_DEBUG_STATUS UINT32_C(0x918)
#define RS_USB2_ABI_GLOBAL_CTRL_ENABLE 0U
#define RS_USB2_ABI_GLOBAL_CTRL_SOFT_RESET 1U
#define RS_USB2_ABI_GLOBAL_CTRL_ABORT 2U
#define RS_USB2_ABI_GLOBAL_CTRL_IRQ_ENABLE 3U
#define RS_USB2_ABI_ROLE_CTRL_FORCE_LSB 0U
#define RS_USB2_ABI_ROLE_CTRL_AUTO 2U
#define RS_USB2_ABI_ROLE_IDLE 0U
#define RS_USB2_ABI_ROLE_DEVICE 1U
#define RS_USB2_ABI_ROLE_HOST 2U
#define RS_USB2_ABI_PHY_CTRL_RESET_N 0U
#define RS_USB2_ABI_PHY_CTRL_SUSPEND 1U
#define RS_USB2_ABI_PHY_CTRL_REMOTE_WAKE 2U
#define RS_USB2_ABI_PHY_CTRL_VIEWPORT_START 31U
#define RS_USB2_ABI_PHY_CTRL_VIEWPORT_WRITE 30U
#define RS_USB2_ABI_PHY_CTRL_VIEWPORT_ADDR_LSB 16U
#define RS_USB2_ABI_PHY_CTRL_VIEWPORT_DATA_LSB 0U
#define RS_USB2_ABI_IRQ_ROLE_CHANGE 0U
#define RS_USB2_ABI_IRQ_PORT_CHANGE 1U
#define RS_USB2_ABI_IRQ_BUS_RESET 2U
#define RS_USB2_ABI_IRQ_SUSPEND 3U
#define RS_USB2_ABI_IRQ_RESUME 4U
#define RS_USB2_ABI_IRQ_SETUP 5U
#define RS_USB2_ABI_IRQ_ENDPOINT 6U
#define RS_USB2_ABI_IRQ_CHANNEL 7U
#define RS_USB2_ABI_IRQ_DMA_DONE 8U
#define RS_USB2_ABI_IRQ_DMA_ERROR 9U
#define RS_USB2_ABI_IRQ_PHY_ERROR 10U
#define RS_USB2_ABI_IRQ_ULPI_ERROR 11U
#define RS_USB2_ABI_IRQ_ECC_CORRECTED 12U
#define RS_USB2_ABI_IRQ_ECC_UNCORRECTABLE 13U
#define RS_USB2_ABI_IRQ_TIMEOUT 14U
#define RS_USB2_ABI_IRQ_FATAL 15U
#define RS_USB2_ABI_ENDPOINT_CFG_IN_ENABLE 0U
#define RS_USB2_ABI_ENDPOINT_CFG_OUT_ENABLE 1U
#define RS_USB2_ABI_ENDPOINT_CFG_IN_STALL 2U
#define RS_USB2_ABI_ENDPOINT_CFG_OUT_STALL 3U
#define RS_USB2_ABI_ENDPOINT_CFG_TYPE_LSB 4U
#define RS_USB2_ABI_ENDPOINT_CFG_MAX_PACKET_LSB 16U
#define RS_USB2_ABI_ENDPOINT_COMMAND_PRIME_IN 0U
#define RS_USB2_ABI_ENDPOINT_COMMAND_ARM_OUT 1U
#define RS_USB2_ABI_ENDPOINT_COMMAND_CANCEL 2U
#define RS_USB2_ABI_ENDPOINT_COMMAND_RESET_TOGGLE 3U
#define RS_USB2_ABI_CHANNEL_CFG_ENABLE 0U
#define RS_USB2_ABI_CHANNEL_CFG_DIRECTION_IN 1U
#define RS_USB2_ABI_CHANNEL_CFG_TYPE_LSB 2U
#define RS_USB2_ABI_CHANNEL_CFG_LOW_SPEED 4U
#define RS_USB2_ABI_CHANNEL_CFG_SETUP 5U
#define RS_USB2_ABI_CHANNEL_CFG_PING_ENABLE 6U
#define RS_USB2_ABI_CHANNEL_CFG_MAX_PACKET_LSB 16U
#define RS_USB2_ABI_CHANNEL_TARGET_ADDR_LSB 0U
#define RS_USB2_ABI_CHANNEL_TARGET_ENDPOINT_LSB 8U
#define RS_USB2_ABI_CHANNEL_TARGET_TOGGLE 12U
#define RS_USB2_ABI_CHANNEL_COMMAND_START 0U
#define RS_USB2_ABI_CHANNEL_COMMAND_CANCEL 1U
#define RS_USB2_ABI_RAM_REGION_BASE_LSB 2U
#define RS_USB2_ABI_RAM_REGION_LENGTH_LSB 16U
#define RS_USB2_ABI_DESC_OWN 0U
#define RS_USB2_ABI_DESC_CHAIN 1U
#define RS_USB2_ABI_DESC_END 2U
#define RS_USB2_ABI_DESC_IRQ 3U
#define RS_USB2_ABI_DESC_SHORT_OK 4U
#define RS_USB2_ABI_DESC_ZERO_PACKET 5U
#define RS_USB2_ABI_DESC_DONE 16U
#define RS_USB2_ABI_DESC_SHORT 17U
#define RS_USB2_ABI_DESC_STALL 18U
#define RS_USB2_ABI_DESC_TIMEOUT 19U
#define RS_USB2_ABI_DESC_CRC_ERROR 20U
#define RS_USB2_ABI_DESC_PROTOCOL_ERROR 21U
#define RS_USB2_ABI_DESC_AXI_ERROR 22U
#define RS_USB2_ABI_DESC_ABORTED 23U
#define RS_USB2_REG_IP_ID RS_USB2_ABI_IP_ID
#define RS_USB2_REG_IP_VERSION RS_USB2_ABI_IP_VERSION
#define RS_USB2_REG_CAPABILITY0 RS_USB2_ABI_CAPABILITY0
#define RS_USB2_REG_CAPABILITY1 RS_USB2_ABI_CAPABILITY1
#define RS_USB2_REG_GLOBAL_CTRL RS_USB2_ABI_GLOBAL_CTRL
#define RS_USB2_REG_GLOBAL_STATUS RS_USB2_ABI_GLOBAL_STATUS
#define RS_USB2_REG_ROLE_CTRL RS_USB2_ABI_ROLE_CTRL
#define RS_USB2_REG_ROLE_STATUS RS_USB2_ABI_ROLE_STATUS
#define RS_USB2_REG_PHY_CTRL RS_USB2_ABI_PHY_CTRL
#define RS_USB2_REG_PHY_STATUS RS_USB2_ABI_PHY_STATUS
#define RS_USB2_REG_ULPI_VIEWPORT RS_USB2_ABI_ULPI_VIEWPORT
#define RS_USB2_REG_ULPI_VIEWPORT_DATA RS_USB2_ABI_ULPI_VIEWPORT_DATA
#define RS_USB2_REG_TIMEOUT RS_USB2_ABI_TIMEOUT
#define RS_USB2_REG_FRAME RS_USB2_ABI_FRAME
#define RS_USB2_REG_IRQ_STATUS RS_USB2_ABI_IRQ_STATUS
#define RS_USB2_REG_IRQ_ENABLE RS_USB2_ABI_IRQ_ENABLE
#define RS_USB2_REG_IRQ_TEST RS_USB2_ABI_IRQ_TEST
#define RS_USB2_REG_ERROR_STATUS RS_USB2_ABI_ERROR_STATUS
#define RS_USB2_REG_ENDPOINT_PENDING_IN RS_USB2_ABI_ENDPOINT_PENDING_IN
#define RS_USB2_REG_ENDPOINT_PENDING_OUT RS_USB2_ABI_ENDPOINT_PENDING_OUT
#define RS_USB2_REG_ENDPOINT_COMPLETE_IN RS_USB2_ABI_ENDPOINT_COMPLETE_IN
#define RS_USB2_REG_ENDPOINT_COMPLETE_OUT RS_USB2_ABI_ENDPOINT_COMPLETE_OUT
#define RS_USB2_REG_DEVICE_ADDR RS_USB2_ABI_DEVICE_ADDR
#define RS_USB2_REG_HOST_STATUS RS_USB2_ABI_HOST_STATUS
#define RS_USB2_REG_PORT_CTRL RS_USB2_ABI_PORT_CTRL
#define RS_USB2_REG_PORT_STATUS RS_USB2_ABI_PORT_STATUS
#define RS_USB2_IP_ID_VALUE UINT32_C(0x55534232)
#define RS_USB2_IP_VERSION_VALUE UINT32_C(0x00010000)
#define RS_USB2_CAPABILITY0_REQUIRED UINT32_C(0x00001FFF)
#define RS_USB2_CAPABILITY0_VALUE UINT32_C(0x0F071FFF)
#define RS_USB2_CAPABILITY1_VALUE UINT32_C(0x00104000)
#define RS_USB2_NUM_ENDPOINTS UINT32_C(8)
#define RS_USB2_NUM_CHANNELS UINT32_C(16)
#define RS_USB2_PACKET_RAM_BYTES UINT32_C(16384)
#define RS_USB2_MAX_TRANSFER_BYTES UINT32_C(32767)
#define RS_USB2_DESCRIPTOR_ALIGNMENT UINT32_C(32)
#define RS_USB2_DESCRIPTOR_SIZE UINT32_C(32)
#define RS_USB2_MAX_DESCRIPTOR_COUNT UINT32_C(256)
#define RS_USB2_GLOBAL_CTRL_ENABLE (UINT32_C(1) << RS_USB2_ABI_GLOBAL_CTRL_ENABLE)
#define RS_USB2_GLOBAL_CTRL_SOFT_RESET (UINT32_C(1) << RS_USB2_ABI_GLOBAL_CTRL_SOFT_RESET)
#define RS_USB2_GLOBAL_CTRL_ABORT (UINT32_C(1) << RS_USB2_ABI_GLOBAL_CTRL_ABORT)
#define RS_USB2_GLOBAL_CTRL_IRQ_ENABLE (UINT32_C(1) << RS_USB2_ABI_GLOBAL_CTRL_IRQ_ENABLE)
#define RS_USB2_GLOBAL_STATUS_ENABLED UINT32_C(0x00000001)
#define RS_USB2_GLOBAL_STATUS_BUSY UINT32_C(0x00000002)
#define RS_USB2_GLOBAL_STATUS_ROLE_MASK UINT32_C(0x0000000C)
#define RS_USB2_GLOBAL_STATUS_LINK_READY UINT32_C(0x00000010)
#define RS_USB2_GLOBAL_STATUS_DMA_BUSY UINT32_C(0x00000020)
#define RS_USB2_ROLE_CTRL_AUTO (UINT32_C(1) << RS_USB2_ABI_ROLE_CTRL_AUTO)
#define RS_USB2_PHY_CTRL_RESET_N (UINT32_C(1) << RS_USB2_ABI_PHY_CTRL_RESET_N)
#define RS_USB2_PHY_CTRL_SUSPEND (UINT32_C(1) << RS_USB2_ABI_PHY_CTRL_SUSPEND)
#define RS_USB2_PHY_CTRL_REMOTE_WAKE (UINT32_C(1) << RS_USB2_ABI_PHY_CTRL_REMOTE_WAKE)
#define RS_USB2_VIEWPORT_START (UINT32_C(1) << RS_USB2_ABI_PHY_CTRL_VIEWPORT_START)
#define RS_USB2_VIEWPORT_WRITE (UINT32_C(1) << RS_USB2_ABI_PHY_CTRL_VIEWPORT_WRITE)
#define RS_USB2_VIEWPORT_ERROR UINT32_C(0x00000100)
#define RS_USB2_IRQ_ROLE_CHANGE (UINT32_C(1) << RS_USB2_ABI_IRQ_ROLE_CHANGE)
#define RS_USB2_IRQ_PORT_CHANGE (UINT32_C(1) << RS_USB2_ABI_IRQ_PORT_CHANGE)
#define RS_USB2_IRQ_BUS_RESET (UINT32_C(1) << RS_USB2_ABI_IRQ_BUS_RESET)
#define RS_USB2_IRQ_SUSPEND (UINT32_C(1) << RS_USB2_ABI_IRQ_SUSPEND)
#define RS_USB2_IRQ_RESUME (UINT32_C(1) << RS_USB2_ABI_IRQ_RESUME)
#define RS_USB2_IRQ_SETUP (UINT32_C(1) << RS_USB2_ABI_IRQ_SETUP)
#define RS_USB2_IRQ_ENDPOINT (UINT32_C(1) << RS_USB2_ABI_IRQ_ENDPOINT)
#define RS_USB2_IRQ_CHANNEL (UINT32_C(1) << RS_USB2_ABI_IRQ_CHANNEL)
#define RS_USB2_IRQ_DMA_DONE (UINT32_C(1) << RS_USB2_ABI_IRQ_DMA_DONE)
#define RS_USB2_IRQ_DMA_ERROR (UINT32_C(1) << RS_USB2_ABI_IRQ_DMA_ERROR)
#define RS_USB2_IRQ_PHY_ERROR (UINT32_C(1) << RS_USB2_ABI_IRQ_PHY_ERROR)
#define RS_USB2_IRQ_ULPI_ERROR (UINT32_C(1) << RS_USB2_ABI_IRQ_ULPI_ERROR)
#define RS_USB2_IRQ_ECC_CORRECTED (UINT32_C(1) << RS_USB2_ABI_IRQ_ECC_CORRECTED)
#define RS_USB2_IRQ_ECC_UNCORRECTABLE (UINT32_C(1) << RS_USB2_ABI_IRQ_ECC_UNCORRECTABLE)
#define RS_USB2_IRQ_TIMEOUT (UINT32_C(1) << RS_USB2_ABI_IRQ_TIMEOUT)
#define RS_USB2_IRQ_FATAL (UINT32_C(1) << RS_USB2_ABI_IRQ_FATAL)
#define RS_USB2_IRQ_ALL UINT32_C(0x0000FFFF)
#define RS_USB2_ENDPOINT_COMMAND_PRIME_IN (UINT32_C(1) << RS_USB2_ABI_ENDPOINT_COMMAND_PRIME_IN)
#define RS_USB2_ENDPOINT_COMMAND_ARM_OUT (UINT32_C(1) << RS_USB2_ABI_ENDPOINT_COMMAND_ARM_OUT)
#define RS_USB2_ENDPOINT_COMMAND_CANCEL (UINT32_C(1) << RS_USB2_ABI_ENDPOINT_COMMAND_CANCEL)
#define RS_USB2_ENDPOINT_COMMAND_RESET_TOGGLE \
(UINT32_C(1) << RS_USB2_ABI_ENDPOINT_COMMAND_RESET_TOGGLE)
#define RS_USB2_CHANNEL_COMMAND_START (UINT32_C(1) << RS_USB2_ABI_CHANNEL_COMMAND_START)
#define RS_USB2_CHANNEL_COMMAND_CANCEL (UINT32_C(1) << RS_USB2_ABI_CHANNEL_COMMAND_CANCEL)
#define RS_USB2_PORT_CTRL_FORCE_FULL_SPEED UINT32_C(0x00000001)
#define RS_USB2_DESC_OWN (UINT32_C(1) << RS_USB2_ABI_DESC_OWN)
#define RS_USB2_DESC_CHAIN (UINT32_C(1) << RS_USB2_ABI_DESC_CHAIN)
#define RS_USB2_DESC_END (UINT32_C(1) << RS_USB2_ABI_DESC_END)
#define RS_USB2_DESC_IRQ (UINT32_C(1) << RS_USB2_ABI_DESC_IRQ)
#define RS_USB2_DESC_SHORT_OK (UINT32_C(1) << RS_USB2_ABI_DESC_SHORT_OK)
#define RS_USB2_DESC_ZERO_PACKET (UINT32_C(1) << RS_USB2_ABI_DESC_ZERO_PACKET)
#define RS_USB2_DESC_WRITEBACK_MASK UINT32_C(0x00FF0000)
#define RS_USB2_DESC_RESERVED_MASK UINT32_C(0xFF00FFC0)
#define RS_USB2_DESC_FRAME_RESERVED_MASK UINT32_C(0xF8000000)
typedef struct {
uint32_t buffer_addr;
uint32_t byte_length;
uint32_t next_addr;
uint32_t control;
uint32_t actual_length;
uint32_t status;
uint32_t frame;
uint32_t reserved;
} rs_usb2_descriptor_t;
_Static_assert(sizeof(rs_usb2_descriptor_t) == 32U, "USB2 descriptor ABI size");
#endif
+1
View File
@@ -28,6 +28,7 @@ rs_status_t rs_perf_snapshot(rs_perf_snapshot_t *snapshot) {
RS_OK) ||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_SDIO1_WAIT, &snapshot->sdio1_wait) !=
RS_OK) ||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_USB2_WAIT, &snapshot->usb2_wait) != RS_OK) ||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_APB4_PERIPH_WAIT,
&snapshot->apb4_periph_wait) != RS_OK) ||
(rs_sysctrl_read_perf_counter(RS_SYSCTRL_PERF_APB4_SYSTEM_WAIT,
+6
View File
@@ -26,6 +26,8 @@
#define RS_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET
#define RS_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET
#define RS_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET
#define RS_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET
#define RS_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET
#define RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET
#define RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET RS_SOC_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET
#define RS_SYSCTRL_PERF_APB4_SYSTEM_WAIT_LO_OFFSET RS_SOC_SYSCTRL_PERF_APB4_SYSTEM_WAIT_LO_OFFSET
@@ -96,6 +98,10 @@ static rs_status_t rs_sysctrl_perf_offsets(rs_sysctrl_perf_counter_t counter, ui
*low_offset = RS_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET;
*high_offset = RS_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET;
break;
case RS_SYSCTRL_PERF_USB2_WAIT:
*low_offset = RS_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET;
*high_offset = RS_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET;
break;
case RS_SYSCTRL_PERF_APB4_PERIPH_WAIT:
*low_offset = RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_LO_OFFSET;
*high_offset = RS_SYSCTRL_PERF_APB4_PERIPH_WAIT_HI_OFFSET;
+514
View File
@@ -0,0 +1,514 @@
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/core/soc.h>
#include <retrosoc/hal/usb2.h>
static volatile uint32_t *rs_usb2_reg(uint32_t offset) {
return (volatile uint32_t *)(uintptr_t)((uintptr_t)RS_SOC_APB4_USB2_BASE + (uintptr_t)offset);
}
static uint32_t rs_usb2_read(uint32_t offset) {
return *rs_usb2_reg(offset);
}
static void rs_usb2_write(uint32_t offset, uint32_t value) {
*rs_usb2_reg(offset) = value;
}
static bool rs_usb2_role_valid(rs_usb2_role_t role) {
return (role == RS_USB2_ROLE_IDLE) || (role == RS_USB2_ROLE_DEVICE) ||
(role == RS_USB2_ROLE_HOST);
}
static bool rs_usb2_transfer_type_valid(rs_usb2_transfer_type_t transfer_type) {
return (transfer_type == RS_USB2_TRANSFER_CONTROL) ||
(transfer_type == RS_USB2_TRANSFER_ISOCHRONOUS) ||
(transfer_type == RS_USB2_TRANSFER_BULK) ||
(transfer_type == RS_USB2_TRANSFER_INTERRUPT);
}
static bool rs_usb2_direction_valid(rs_usb2_direction_t direction) {
return (direction == RS_USB2_DIRECTION_OUT) || (direction == RS_USB2_DIRECTION_IN);
}
static bool rs_usb2_speed_valid(rs_usb2_speed_t speed) {
return (speed == RS_USB2_SPEED_HIGH) || (speed == RS_USB2_SPEED_FULL) ||
(speed == RS_USB2_SPEED_LOW);
}
static bool rs_usb2_descriptor_pointer_valid(uintptr_t address) {
return (address != (uintptr_t)0U) && (address <= (uintptr_t)UINT32_MAX) &&
((address % (uintptr_t)RS_USB2_DESCRIPTOR_ALIGNMENT) == (uintptr_t)0U) &&
((address & (uintptr_t)UINT32_C(0xFFF)) <= (uintptr_t)UINT32_C(0xFE0));
}
static bool rs_usb2_packet_region_valid(uint16_t base, uint16_t length) {
uint32_t end = (uint32_t)base + (uint32_t)length;
return ((base % sizeof(uint32_t)) == 0U) && (length != 0U) &&
((uint32_t)length <= RS_USB2_MAX_TRANSFER_BYTES) && (end <= RS_USB2_PACKET_RAM_BYTES);
}
static uint32_t rs_usb2_packet_region(uint16_t base, uint16_t length) {
return ((uint32_t)base >> RS_USB2_ABI_RAM_REGION_BASE_LSB) |
((uint32_t)length << RS_USB2_ABI_RAM_REGION_LENGTH_LSB);
}
static uint32_t rs_usb2_endpoint_offset(uint8_t endpoint, uint32_t reg) {
return RS_USB2_ABI_ENDPOINT_BASE + ((uint32_t)endpoint * RS_USB2_ABI_ENDPOINT_STRIDE) + reg;
}
static uint32_t rs_usb2_channel_offset(uint8_t channel, uint32_t reg) {
return RS_USB2_ABI_CHANNEL_BASE + ((uint32_t)channel * RS_USB2_ABI_CHANNEL_STRIDE) + reg;
}
static bool rs_usb2_configuration_writable(void) {
uint32_t status = rs_usb2_read(RS_USB2_REG_GLOBAL_STATUS);
return (status & (RS_USB2_GLOBAL_STATUS_ENABLED | RS_USB2_GLOBAL_STATUS_BUSY)) == 0U;
}
static bool rs_usb2_role_active(rs_usb2_role_t role) {
uint32_t status = rs_usb2_read(RS_USB2_REG_GLOBAL_STATUS);
uint32_t active = (status & RS_USB2_GLOBAL_STATUS_ROLE_MASK) >> 2U;
return ((status & RS_USB2_GLOBAL_STATUS_ENABLED) != 0U) && (active == (uint32_t)role);
}
static bool rs_usb2_host_packet_valid(const rs_usb2_channel_config_t *config) {
if ((config->max_packet == 0U) || (config->max_packet > 1024U)) {
return false;
}
if (config->speed == RS_USB2_SPEED_LOW) {
return ((config->transfer_type == RS_USB2_TRANSFER_CONTROL) ||
(config->transfer_type == RS_USB2_TRANSFER_INTERRUPT)) &&
(config->max_packet <= 8U);
}
if (config->speed == RS_USB2_SPEED_FULL) {
if (config->transfer_type == RS_USB2_TRANSFER_ISOCHRONOUS) {
return config->max_packet <= 1023U;
}
return config->max_packet <= 64U;
}
if ((config->transfer_type == RS_USB2_TRANSFER_CONTROL) && (config->max_packet > 64U)) {
return false;
}
if ((config->transfer_type == RS_USB2_TRANSFER_BULK) && (config->max_packet > 512U)) {
return false;
}
return true;
}
static rs_status_t rs_usb2_viewport_wait(rs_timeout_t timeout, uint32_t *viewport) {
uint32_t value;
if ((timeout == 0U) || (viewport == NULL)) {
return RS_EINVAL;
}
while (timeout-- != 0U) {
value = rs_usb2_read(RS_USB2_REG_ULPI_VIEWPORT);
if ((value & RS_USB2_VIEWPORT_START) == 0U) {
*viewport = value;
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_usb2_probe(uint32_t *ip_id, uint32_t *version, uint32_t *capability0,
uint32_t *capability1) {
if ((ip_id == NULL) || (version == NULL) || (capability0 == NULL) || (capability1 == NULL)) {
return RS_EINVAL;
}
*ip_id = rs_usb2_read(RS_USB2_REG_IP_ID);
*version = rs_usb2_read(RS_USB2_REG_IP_VERSION);
*capability0 = rs_usb2_read(RS_USB2_REG_CAPABILITY0);
*capability1 = rs_usb2_read(RS_USB2_REG_CAPABILITY1);
if ((*ip_id != RS_USB2_IP_ID_VALUE) || (*version != RS_USB2_IP_VERSION_VALUE) ||
((*capability0 & RS_USB2_CAPABILITY0_REQUIRED) != RS_USB2_CAPABILITY0_REQUIRED) ||
(*capability1 != RS_USB2_CAPABILITY1_VALUE)) {
return RS_EIO;
}
return RS_OK;
}
rs_status_t rs_usb2_reset(rs_timeout_t timeout) {
uint32_t control;
if (timeout == 0U) {
return RS_EINVAL;
}
control = rs_usb2_read(RS_USB2_REG_GLOBAL_CTRL) & RS_USB2_GLOBAL_CTRL_IRQ_ENABLE;
rs_usb2_write(RS_USB2_REG_GLOBAL_CTRL, control | RS_USB2_GLOBAL_CTRL_ABORT);
while (timeout-- != 0U) {
if ((rs_usb2_read(RS_USB2_REG_GLOBAL_STATUS) & RS_USB2_GLOBAL_STATUS_BUSY) == 0U) {
rs_usb2_write(RS_USB2_REG_GLOBAL_CTRL, control | RS_USB2_GLOBAL_CTRL_SOFT_RESET);
rs_usb2_write(RS_USB2_REG_IRQ_STATUS, RS_USB2_IRQ_ALL);
rs_usb2_write(RS_USB2_REG_ERROR_STATUS, UINT32_MAX);
rs_usb2_write(RS_USB2_REG_ENDPOINT_COMPLETE_IN, UINT32_MAX);
rs_usb2_write(RS_USB2_REG_ENDPOINT_COMPLETE_OUT, UINT32_MAX);
return RS_OK;
}
}
return RS_ETIMEOUT;
}
rs_status_t rs_usb2_configure(const rs_usb2_config_t *config) {
uint32_t role;
uint32_t phy = 0U;
uint32_t global = 0U;
if ((config == NULL) || !rs_usb2_role_valid(config->role) ||
(config->transaction_timeout == 0U) || ((config->irq_enable & ~RS_USB2_IRQ_ALL) != 0U) ||
!rs_usb2_configuration_writable()) {
return RS_EINVAL;
}
role = (uint32_t)config->role << RS_USB2_ABI_ROLE_CTRL_FORCE_LSB;
if (config->auto_role) {
role |= RS_USB2_ROLE_CTRL_AUTO;
}
if (config->release_phy_reset) {
phy |= RS_USB2_PHY_CTRL_RESET_N;
}
if (config->enable_interrupts) {
global |= RS_USB2_GLOBAL_CTRL_IRQ_ENABLE;
}
rs_usb2_write(RS_USB2_REG_ROLE_CTRL, role);
rs_usb2_write(RS_USB2_REG_PHY_CTRL, phy);
rs_usb2_write(RS_USB2_REG_PORT_CTRL,
config->force_full_speed ? RS_USB2_PORT_CTRL_FORCE_FULL_SPEED : 0U);
rs_usb2_write(RS_USB2_REG_TIMEOUT, config->transaction_timeout);
rs_usb2_write(RS_USB2_REG_IRQ_STATUS, RS_USB2_IRQ_ALL);
rs_usb2_write(RS_USB2_REG_IRQ_ENABLE, config->irq_enable);
rs_usb2_write(RS_USB2_REG_GLOBAL_CTRL, global);
return RS_OK;
}
rs_status_t rs_usb2_enable(bool enable) {
uint32_t control = rs_usb2_read(RS_USB2_REG_GLOBAL_CTRL);
if (enable) {
control |= RS_USB2_GLOBAL_CTRL_ENABLE;
} else {
control &= ~RS_USB2_GLOBAL_CTRL_ENABLE;
}
rs_usb2_write(RS_USB2_REG_GLOBAL_CTRL, control);
return RS_OK;
}
rs_status_t rs_usb2_status_get(rs_usb2_status_t *status) {
if (status == NULL) {
return RS_EINVAL;
}
status->global = rs_usb2_read(RS_USB2_REG_GLOBAL_STATUS);
status->role = rs_usb2_read(RS_USB2_REG_ROLE_STATUS);
status->phy = rs_usb2_read(RS_USB2_REG_PHY_STATUS);
status->frame = rs_usb2_read(RS_USB2_REG_FRAME);
status->port = rs_usb2_read(RS_USB2_REG_PORT_STATUS);
status->irq_status = rs_usb2_read(RS_USB2_REG_IRQ_STATUS);
status->error_status = rs_usb2_read(RS_USB2_REG_ERROR_STATUS);
status->error_code = rs_usb2_read(RS_USB2_ABI_ERROR_CODE);
status->error_info = rs_usb2_read(RS_USB2_ABI_ERROR_INFO);
status->error_descriptor = rs_usb2_read(RS_USB2_ABI_ERROR_DESC_ADDR);
status->error_buffer = rs_usb2_read(RS_USB2_ABI_ERROR_BUFFER_ADDR);
status->enabled = (status->global & RS_USB2_GLOBAL_STATUS_ENABLED) != 0U;
status->busy = (status->global & RS_USB2_GLOBAL_STATUS_BUSY) != 0U;
status->link_ready = (status->global & RS_USB2_GLOBAL_STATUS_LINK_READY) != 0U;
return RS_OK;
}
rs_status_t rs_usb2_device_address_set(uint8_t address) {
if (address > UINT8_C(127)) {
return RS_EINVAL;
}
rs_usb2_write(RS_USB2_REG_DEVICE_ADDR, (uint32_t)address);
return RS_OK;
}
rs_status_t rs_usb2_endpoint_configure(const rs_usb2_endpoint_config_t *config) {
uint32_t cfg;
if ((config == NULL) || ((uint32_t)config->number >= RS_USB2_NUM_ENDPOINTS) ||
!rs_usb2_transfer_type_valid(config->transfer_type) || (config->max_packet == 0U) ||
(config->max_packet > 1024U) || !rs_usb2_configuration_writable() ||
(config->enable_in &&
(!rs_usb2_packet_region_valid(config->ram_in_base, config->ram_in_length) ||
!rs_usb2_descriptor_pointer_valid(config->descriptor_in))) ||
(config->enable_out &&
(!rs_usb2_packet_region_valid(config->ram_out_base, config->ram_out_length) ||
!rs_usb2_descriptor_pointer_valid(config->descriptor_out)))) {
return RS_EINVAL;
}
cfg = (uint32_t)config->transfer_type << RS_USB2_ABI_ENDPOINT_CFG_TYPE_LSB;
cfg |= (uint32_t)config->max_packet << RS_USB2_ABI_ENDPOINT_CFG_MAX_PACKET_LSB;
if (config->enable_in) {
cfg |= UINT32_C(1) << RS_USB2_ABI_ENDPOINT_CFG_IN_ENABLE;
}
if (config->enable_out) {
cfg |= UINT32_C(1) << RS_USB2_ABI_ENDPOINT_CFG_OUT_ENABLE;
}
if (config->stall_in) {
cfg |= UINT32_C(1) << RS_USB2_ABI_ENDPOINT_CFG_IN_STALL;
}
if (config->stall_out) {
cfg |= UINT32_C(1) << RS_USB2_ABI_ENDPOINT_CFG_OUT_STALL;
}
rs_usb2_write(rs_usb2_endpoint_offset(config->number, RS_USB2_ABI_ENDPOINT_CFG), cfg);
rs_usb2_write(
rs_usb2_endpoint_offset(config->number, RS_USB2_ABI_ENDPOINT_RAM_IN),
config->enable_in ? rs_usb2_packet_region(config->ram_in_base, config->ram_in_length) : 0U);
rs_usb2_write(rs_usb2_endpoint_offset(config->number, RS_USB2_ABI_ENDPOINT_RAM_OUT),
config->enable_out
? rs_usb2_packet_region(config->ram_out_base, config->ram_out_length)
: 0U);
rs_usb2_write(rs_usb2_endpoint_offset(config->number, RS_USB2_ABI_ENDPOINT_DESC_IN),
config->enable_in ? (uint32_t)config->descriptor_in : 0U);
rs_usb2_write(rs_usb2_endpoint_offset(config->number, RS_USB2_ABI_ENDPOINT_DESC_OUT),
config->enable_out ? (uint32_t)config->descriptor_out : 0U);
return RS_OK;
}
rs_status_t rs_usb2_endpoint_submit(uint8_t endpoint, rs_usb2_direction_t direction) {
uint32_t command;
if (((uint32_t)endpoint >= RS_USB2_NUM_ENDPOINTS) || !rs_usb2_direction_valid(direction) ||
!rs_usb2_role_active(RS_USB2_ROLE_DEVICE)) {
return RS_EINVAL;
}
command = (direction == RS_USB2_DIRECTION_IN) ? RS_USB2_ENDPOINT_COMMAND_PRIME_IN
: RS_USB2_ENDPOINT_COMMAND_ARM_OUT;
rs_usb2_memory_barrier();
rs_usb2_write(rs_usb2_endpoint_offset(endpoint, RS_USB2_ABI_ENDPOINT_COMMAND), command);
return RS_OK;
}
rs_status_t rs_usb2_endpoint_cancel(uint8_t endpoint) {
if ((uint32_t)endpoint >= RS_USB2_NUM_ENDPOINTS) {
return RS_EINVAL;
}
rs_usb2_write(rs_usb2_endpoint_offset(endpoint, RS_USB2_ABI_ENDPOINT_COMMAND),
RS_USB2_ENDPOINT_COMMAND_CANCEL);
return RS_OK;
}
rs_status_t rs_usb2_endpoint_completion(uint32_t *in_mask, uint32_t *out_mask) {
if ((in_mask == NULL) || (out_mask == NULL)) {
return RS_EINVAL;
}
rs_usb2_memory_barrier();
*in_mask = rs_usb2_read(RS_USB2_REG_ENDPOINT_COMPLETE_IN) & UINT32_C(0xFF);
*out_mask = rs_usb2_read(RS_USB2_REG_ENDPOINT_COMPLETE_OUT) & UINT32_C(0xFF);
return RS_OK;
}
rs_status_t rs_usb2_endpoint_completion_clear(uint32_t in_mask, uint32_t out_mask) {
if (((in_mask | out_mask) & ~UINT32_C(0xFF)) != 0U) {
return RS_EINVAL;
}
rs_usb2_write(RS_USB2_REG_ENDPOINT_COMPLETE_IN, in_mask);
rs_usb2_write(RS_USB2_REG_ENDPOINT_COMPLETE_OUT, out_mask);
return RS_OK;
}
rs_status_t rs_usb2_channel_configure(const rs_usb2_channel_config_t *config) {
uint32_t cfg;
uint32_t interval_ticks;
uint32_t target;
if ((config == NULL) || ((uint32_t)config->channel >= RS_USB2_NUM_CHANNELS) ||
(config->device_address > UINT8_C(127)) || (config->endpoint > UINT8_C(15)) ||
!rs_usb2_direction_valid(config->direction) ||
!rs_usb2_transfer_type_valid(config->transfer_type) ||
!rs_usb2_speed_valid(config->speed) || !rs_usb2_host_packet_valid(config) ||
!rs_usb2_packet_region_valid(config->ram_base, config->ram_length) ||
!rs_usb2_descriptor_pointer_valid(config->descriptor) ||
(((config->transfer_type == RS_USB2_TRANSFER_ISOCHRONOUS) ||
(config->transfer_type == RS_USB2_TRANSFER_INTERRUPT)) &&
((config->interval == 0U) ||
((config->speed == RS_USB2_SPEED_HIGH) ? (config->interval > 8191U)
: (config->interval > 1023U)))) ||
(config->setup && ((config->direction != RS_USB2_DIRECTION_OUT) ||
(config->transfer_type != RS_USB2_TRANSFER_CONTROL))) ||
(config->ping_enable &&
((config->speed != RS_USB2_SPEED_HIGH) || (config->direction != RS_USB2_DIRECTION_OUT) ||
(config->transfer_type != RS_USB2_TRANSFER_BULK))) ||
!rs_usb2_configuration_writable()) {
return RS_EINVAL;
}
cfg = UINT32_C(1) << RS_USB2_ABI_CHANNEL_CFG_ENABLE;
cfg |= (uint32_t)config->direction << RS_USB2_ABI_CHANNEL_CFG_DIRECTION_IN;
cfg |= (uint32_t)config->transfer_type << RS_USB2_ABI_CHANNEL_CFG_TYPE_LSB;
cfg |= (uint32_t)config->max_packet << RS_USB2_ABI_CHANNEL_CFG_MAX_PACKET_LSB;
if (config->speed == RS_USB2_SPEED_LOW) {
cfg |= UINT32_C(1) << RS_USB2_ABI_CHANNEL_CFG_LOW_SPEED;
}
if (config->setup) {
cfg |= UINT32_C(1) << RS_USB2_ABI_CHANNEL_CFG_SETUP;
}
if (config->ping_enable) {
cfg |= UINT32_C(1) << RS_USB2_ABI_CHANNEL_CFG_PING_ENABLE;
}
target = (uint32_t)config->device_address << RS_USB2_ABI_CHANNEL_TARGET_ADDR_LSB;
target |= (uint32_t)config->endpoint << RS_USB2_ABI_CHANNEL_TARGET_ENDPOINT_LSB;
if (config->data_toggle) {
target |= UINT32_C(1) << RS_USB2_ABI_CHANNEL_TARGET_TOGGLE;
}
interval_ticks = (uint32_t)config->interval;
if (config->speed != RS_USB2_SPEED_HIGH) {
interval_ticks *= 8U;
}
rs_usb2_write(rs_usb2_channel_offset(config->channel, RS_USB2_ABI_CHANNEL_CFG), cfg);
rs_usb2_write(rs_usb2_channel_offset(config->channel, RS_USB2_ABI_CHANNEL_TARGET), target);
rs_usb2_write(rs_usb2_channel_offset(config->channel, RS_USB2_ABI_CHANNEL_INTERVAL),
interval_ticks);
rs_usb2_write(rs_usb2_channel_offset(config->channel, RS_USB2_ABI_CHANNEL_RAM),
rs_usb2_packet_region(config->ram_base, config->ram_length));
rs_usb2_write(rs_usb2_channel_offset(config->channel, RS_USB2_ABI_CHANNEL_DESC),
(uint32_t)config->descriptor);
return RS_OK;
}
rs_status_t rs_usb2_channel_start(uint8_t channel) {
if (((uint32_t)channel >= RS_USB2_NUM_CHANNELS) || !rs_usb2_role_active(RS_USB2_ROLE_HOST)) {
return RS_EINVAL;
}
rs_usb2_memory_barrier();
rs_usb2_write(rs_usb2_channel_offset(channel, RS_USB2_ABI_CHANNEL_COMMAND),
RS_USB2_CHANNEL_COMMAND_START);
return RS_OK;
}
rs_status_t rs_usb2_channel_cancel(uint8_t channel) {
if ((uint32_t)channel >= RS_USB2_NUM_CHANNELS) {
return RS_EINVAL;
}
rs_usb2_write(rs_usb2_channel_offset(channel, RS_USB2_ABI_CHANNEL_COMMAND),
RS_USB2_CHANNEL_COMMAND_CANCEL);
return RS_OK;
}
rs_status_t rs_usb2_channel_status_get(uint8_t channel, uint32_t *status, uint32_t *bytes) {
if (((uint32_t)channel >= RS_USB2_NUM_CHANNELS) || (status == NULL) || (bytes == NULL)) {
return RS_EINVAL;
}
*status = rs_usb2_read(rs_usb2_channel_offset(channel, RS_USB2_ABI_CHANNEL_STATUS));
*bytes = rs_usb2_read(rs_usb2_channel_offset(channel, RS_USB2_ABI_CHANNEL_BYTES));
return RS_OK;
}
rs_status_t rs_usb2_irq_enable(uint32_t mask) {
if ((mask & ~RS_USB2_IRQ_ALL) != 0U) {
return RS_EINVAL;
}
rs_usb2_write(RS_USB2_REG_IRQ_ENABLE, mask);
return RS_OK;
}
rs_status_t rs_usb2_irq_pending(uint32_t *mask) {
if (mask == NULL) {
return RS_EINVAL;
}
*mask = rs_usb2_read(RS_USB2_REG_IRQ_STATUS) & RS_USB2_IRQ_ALL;
return RS_OK;
}
rs_status_t rs_usb2_irq_clear(uint32_t mask) {
if ((mask & ~RS_USB2_IRQ_ALL) != 0U) {
return RS_EINVAL;
}
rs_usb2_write(RS_USB2_REG_IRQ_STATUS, mask);
return RS_OK;
}
rs_status_t rs_usb2_irq_test(uint32_t mask) {
if ((mask == 0U) || ((mask & ~RS_USB2_IRQ_ALL) != 0U)) {
return RS_EINVAL;
}
rs_usb2_write(RS_USB2_REG_IRQ_TEST, mask);
return RS_OK;
}
rs_status_t rs_usb2_ulpi_read(uint8_t address, uint8_t *data, rs_timeout_t timeout) {
uint32_t viewport;
uint32_t result;
rs_status_t status;
if ((address > UINT8_C(63)) || (data == NULL)) {
return RS_EINVAL;
}
status = rs_usb2_viewport_wait(timeout, &viewport);
if (status != RS_OK) {
return status;
}
(void)viewport;
rs_usb2_write(RS_USB2_REG_ULPI_VIEWPORT,
RS_USB2_VIEWPORT_START |
((uint32_t)address << RS_USB2_ABI_PHY_CTRL_VIEWPORT_ADDR_LSB));
status = rs_usb2_viewport_wait(timeout, &viewport);
if (status != RS_OK) {
return status;
}
result = rs_usb2_read(RS_USB2_REG_ULPI_VIEWPORT_DATA);
if ((result & RS_USB2_VIEWPORT_ERROR) != 0U) {
return RS_EIO;
}
*data = (uint8_t)(result & UINT32_C(0xFF));
return RS_OK;
}
rs_status_t rs_usb2_ulpi_write(uint8_t address, uint8_t data, rs_timeout_t timeout) {
uint32_t viewport;
uint32_t result;
rs_status_t status;
if (address > UINT8_C(63)) {
return RS_EINVAL;
}
status = rs_usb2_viewport_wait(timeout, &viewport);
if (status != RS_OK) {
return status;
}
(void)viewport;
rs_usb2_write(RS_USB2_REG_ULPI_VIEWPORT,
RS_USB2_VIEWPORT_START | RS_USB2_VIEWPORT_WRITE |
((uint32_t)address << RS_USB2_ABI_PHY_CTRL_VIEWPORT_ADDR_LSB) |
(uint32_t)data);
status = rs_usb2_viewport_wait(timeout, &viewport);
if (status != RS_OK) {
return status;
}
result = rs_usb2_read(RS_USB2_REG_ULPI_VIEWPORT_DATA);
return ((result & RS_USB2_VIEWPORT_ERROR) != 0U) ? RS_EIO : RS_OK;
}
rs_status_t rs_usb2_controller_selftest(void) {
const uint32_t test_irq = RS_USB2_IRQ_FATAL;
uint32_t ip_id;
uint32_t version;
uint32_t capability0;
uint32_t capability1;
uint32_t irq_before;
uint32_t irq_after;
if (rs_usb2_probe(&ip_id, &version, &capability0, &capability1) != RS_OK) {
return RS_EIO;
}
(void)ip_id;
(void)version;
(void)capability0;
(void)capability1;
if ((rs_usb2_read(RS_USB2_REG_GLOBAL_STATUS) & RS_USB2_GLOBAL_STATUS_BUSY) != 0U) {
return RS_EIO;
}
irq_before = rs_usb2_read(RS_USB2_REG_IRQ_STATUS);
rs_usb2_write(RS_USB2_REG_IRQ_TEST, test_irq);
irq_after = rs_usb2_read(RS_USB2_REG_IRQ_STATUS);
if ((irq_after & test_irq) == 0U) {
return RS_EIO;
}
if ((irq_before & test_irq) == 0U) {
rs_usb2_write(RS_USB2_REG_IRQ_STATUS, test_irq);
}
return RS_OK;
}
+180
View File
@@ -0,0 +1,180 @@
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <retrosoc/hal/usb2.h>
static bool rs_usb2_address_range_valid(uintptr_t address, size_t byte_count) {
uintptr_t last;
if ((address == (uintptr_t)0U) || (address > (uintptr_t)UINT32_MAX) || (byte_count == 0U) ||
(byte_count > (size_t)UINT32_MAX)) {
return false;
}
last = address + (uintptr_t)(byte_count - 1U);
return (last >= address) && (last <= (uintptr_t)UINT32_MAX);
}
static bool rs_usb2_descriptor_address_valid(uintptr_t address) {
return (address != (uintptr_t)0U) && (address <= (uintptr_t)UINT32_MAX) &&
((address % (uintptr_t)RS_USB2_DESCRIPTOR_ALIGNMENT) == (uintptr_t)0U) &&
((address & (uintptr_t)UINT32_C(0xFFF)) <= (uintptr_t)UINT32_C(0xFE0));
}
rs_status_t rs_usb2_validate_dma_buffer(const void *buffer, size_t byte_count) {
uintptr_t address;
if (buffer == NULL) {
return RS_EINVAL;
}
address = (uintptr_t)buffer;
if (((address % sizeof(uint32_t)) != 0U) || !rs_usb2_address_range_valid(address, byte_count)) {
return RS_EINVAL;
}
return RS_OK;
}
rs_status_t rs_usb2_descriptor_prepare(rs_usb2_descriptor_t *descriptor, uintptr_t buffer,
size_t byte_count, uintptr_t next, bool end, bool irq,
bool short_ok, bool zero_packet, uint32_t frame) {
uintptr_t descriptor_address;
uint32_t control;
if ((descriptor == NULL) || !rs_usb2_address_range_valid(buffer, byte_count) ||
((buffer % sizeof(uint32_t)) != 0U) || ((frame & RS_USB2_DESC_FRAME_RESERVED_MASK) != 0U)) {
return RS_EINVAL;
}
descriptor_address = (uintptr_t)descriptor;
if ((descriptor_address <= (uintptr_t)UINT32_MAX) &&
!rs_usb2_descriptor_address_valid(descriptor_address)) {
return RS_EINVAL;
}
if (end) {
if (next != (uintptr_t)0U) {
return RS_EINVAL;
}
control = RS_USB2_DESC_END;
} else {
if (!rs_usb2_descriptor_address_valid(next)) {
return RS_EINVAL;
}
control = RS_USB2_DESC_CHAIN;
}
if (irq) {
control |= RS_USB2_DESC_IRQ;
}
if (short_ok) {
control |= RS_USB2_DESC_SHORT_OK;
}
if (zero_packet) {
control |= RS_USB2_DESC_ZERO_PACKET;
}
descriptor->buffer_addr = (uint32_t)buffer;
descriptor->byte_length = (uint32_t)byte_count;
descriptor->next_addr = (uint32_t)next;
descriptor->control = control;
descriptor->actual_length = 0U;
descriptor->status = 0U;
descriptor->frame = frame;
descriptor->reserved = 0U;
return RS_OK;
}
rs_status_t rs_usb2_descriptor_validate(const rs_usb2_descriptor_t *descriptor) {
uintptr_t descriptor_address;
bool chain;
bool end;
if ((descriptor == NULL) ||
!rs_usb2_address_range_valid((uintptr_t)descriptor->buffer_addr,
(size_t)descriptor->byte_length) ||
((descriptor->buffer_addr % sizeof(uint32_t)) != 0U) ||
((descriptor->control & RS_USB2_DESC_RESERVED_MASK) != 0U) ||
((descriptor->control & RS_USB2_DESC_WRITEBACK_MASK) != 0U) ||
(descriptor->actual_length != 0U) || (descriptor->status != 0U) ||
((descriptor->frame & RS_USB2_DESC_FRAME_RESERVED_MASK) != 0U) ||
(descriptor->reserved != 0U)) {
return RS_EINVAL;
}
descriptor_address = (uintptr_t)descriptor;
if ((descriptor_address <= (uintptr_t)UINT32_MAX) &&
!rs_usb2_descriptor_address_valid(descriptor_address)) {
return RS_EINVAL;
}
chain = (descriptor->control & RS_USB2_DESC_CHAIN) != 0U;
end = (descriptor->control & RS_USB2_DESC_END) != 0U;
if (end) {
if (chain || (descriptor->next_addr != 0U)) {
return RS_EINVAL;
}
} else if (!chain || !rs_usb2_descriptor_address_valid((uintptr_t)descriptor->next_addr)) {
return RS_EINVAL;
}
return RS_OK;
}
rs_status_t rs_usb2_descriptor_chain_validate(const rs_usb2_descriptor_t *descriptors,
uint16_t count, uint32_t total_bytes) {
uint32_t total = 0U;
uint16_t index;
bool found_end = false;
if ((descriptors == NULL) || (count == 0U) ||
(count > (uint16_t)RS_USB2_MAX_DESCRIPTOR_COUNT) ||
(((uintptr_t)descriptors % (uintptr_t)RS_USB2_DESCRIPTOR_ALIGNMENT) != (uintptr_t)0U)) {
return RS_EINVAL;
}
for (index = 0U; index < count; index++) {
const rs_usb2_descriptor_t *descriptor = &descriptors[index];
bool end;
if ((rs_usb2_descriptor_validate(descriptor) != RS_OK) ||
(descriptor->byte_length > (UINT32_MAX - total))) {
return RS_EINVAL;
}
total += descriptor->byte_length;
end = (descriptor->control & RS_USB2_DESC_END) != 0U;
if (end) {
if ((index + 1U) != count) {
return RS_EINVAL;
}
found_end = true;
} else {
uintptr_t next = (uintptr_t)&descriptors[index + 1U];
if ((next <= (uintptr_t)UINT32_MAX) && (descriptor->next_addr != (uint32_t)next)) {
return RS_EINVAL;
}
}
}
if (!found_end || ((total_bytes != 0U) && (total != total_bytes))) {
return RS_EINVAL;
}
return RS_OK;
}
rs_status_t rs_usb2_descriptor_publish(rs_usb2_descriptor_t *descriptor) {
if (rs_usb2_descriptor_validate(descriptor) != RS_OK) {
return RS_EINVAL;
}
rs_usb2_memory_barrier();
descriptor->control |= RS_USB2_DESC_OWN;
rs_usb2_memory_barrier();
return RS_OK;
}
rs_status_t rs_usb2_descriptor_publish_chain(rs_usb2_descriptor_t *descriptors, uint16_t count) {
uint16_t remaining;
if (rs_usb2_descriptor_chain_validate(descriptors, count, 0U) != RS_OK) {
return RS_EINVAL;
}
remaining = count;
while (remaining != 0U) {
remaining--;
if (rs_usb2_descriptor_publish(&descriptors[remaining]) != RS_OK) {
return RS_EINVAL;
}
}
return RS_OK;
}
+5
View File
@@ -35,6 +35,11 @@ the root README and subsystem guides.
- [ip/crypto.md](ip/crypto.md) records the commercial AES/SHA/RSA survey and
defines the Mini SoC crypto architecture, APB/DMA ABI, security boundary,
HAL, verification evidence, and commercialization roadmap.
- [ip/usb2.md](ip/usb2.md) defines the ULPI USB 2.0 dual-role architecture,
AXI4 descriptor DMA, APB4 ABI, dedicated pads, HAL, and release gates.
- [ip/mini-npu.md](ip/mini-npu.md) evaluates commercial edge-AI SoCs and records the
optional Mini-AI NPU architecture direction, memory constraints, software
boundary, and integration prerequisites; it is not an implemented profile.
- [ip/ws2812.md](ip/ws2812.md) defines the WS2812 transmitter register ABI,
timing, FIFO, interrupt, DMA, and integration contracts.
- [ip/timer.md](ip/timer.md) defines the dual general timer register ABI,
+4
View File
@@ -14,6 +14,10 @@ extension interfaces.
- [i2c.md](i2c.md) defines the dual APB4 I2C controllers.
- [dvp.md](dvp.md) defines the APB4 DVP capture controller.
- [i2s.md](i2s.md) defines the APB4 I2S master transceiver.
- [usb2.md](usb2.md) defines the dual-role ULPI USB 2.0 controller, AXI4
descriptor DMA, dedicated-pad integration, HAL, and commercial release gates.
- [mini-npu.md](mini-npu.md) records the commercial NPU reference survey and optional
Mini-AI architecture direction; it does not define implemented RTL or ABI.
- [sdram.md](sdram.md) defines the AXI4 SDRAM data controller and APB4 configuration window.
- [xpi.md](xpi.md) defines the native-AXI4/APB4 XPI V2 controller, commercial reference survey, LUT and PHY contracts, HAL, JTAG NOR programming, and delivery boundary.
+144
View File
@@ -0,0 +1,144 @@
# Mini NPU Commercial Reference Survey
## Status and Scope
This document evaluates commercial SoCs as system-level references for an
optional retroSoC Mini AI accelerator. It is a roadmap study, not an
implemented RTL, register ABI, performance claim, or supported build profile.
The base Mini product does not require an NPU. Any derivative that adopts this
study should be identified separately, for example as `Mini-AI`, so that the
base product remains below Std in memory bandwidth, area, power, and software
complexity.
The executable Mini baseline remains defined by the committed configuration,
address map, topology, and RTL. In particular:
- the system fabric has 32-bit address and data paths;
- each master may own at most one read and one write transaction;
- the committed system clock is 72 MHz, giving a theoretical 288 MB/s fabric
payload rate before arbitration, protocol overhead, and target stalls;
- external-memory front ends currently serialize accepted AXI4 bursts into
ordered scalar engine operations; and
- the current address map exposes 128 KiB of on-chip SRAM and a 64 MiB SDRAM
window, while the planned Linux product target is 64-128 MiB of main memory.
These constraints make local data reuse more important than peak arithmetic
throughput. A large MAC array without banked local SRAM, layer fusion, and
native external-memory bursts would spend most of its time waiting for data.
## Evaluation Criteria
Commercial devices were selected for similarity along one or more of these
axes:
- one lightweight Linux application processor plus a separately controlled
MCU or management processor;
- 64-256 MiB-class product memory rather than desktop-class DDR capacity;
- approximately 0.5-1 TOPS of advertised INT8 inference performance;
- low-cost packaging and embedded vision, audio, gateway, or HMI use cases;
- a documented local-buffer, DMA, streaming, compiler, or Linux-driver model;
and
- enough public material to separate verified features from inference.
Vendor TOPS figures are not directly comparable. They may count one multiply
and one addition as two operations, assume a particular precision, sparsity,
clock, or layer shape, and exclude memory stalls. End-to-end model latency,
external-memory traffic, MAC utilization, accuracy after quantization, and
energy per inference are more useful acceptance metrics.
## Commercial References
| Priority | Commercial SoC | Publicly documented characteristics | Relevant lesson for Mini | Important mismatch |
| --- | --- | --- | --- | --- |
| 1 | Rockchip RV1106B | One Cortex-A7 application core, an MCU, a 0.5 TOPS INT8 NPU, 512 Mbit to 2 Gbit embedded DDR, and an 88-pin QFN package. See the [RV1106B product page](https://www.rock-chips.com/a/en/products/RV11_Series/2025/1208/2122.html). | Closest product-scale reference for a low-memory Linux processor, management MCU, small NPU, and compact package. | The ISP, codec, CPU frequency, memory subsystem, and proprietary fourth-generation NPU are substantially stronger than the current Mini baseline. |
| 2 | Allwinner V851S / V853 | V853 combines a Cortex-A7 Linux core, a RISC-V XuanTie E907 MCU, and a 1 TOPS INT8 NPU. Its public NPU guide identifies a 128 KiB internal cache. See the [V853 product page](https://www.allwinnertech.com/index.php?a=index&c=product&id=117) and [NPU guide](https://docs.aw-ol.com/v853/en/npu/dev_npu/). Public V851S ecosystem material describes a lower 0.5 TOPS, 64 MiB DDR2 variant. | Best heterogeneous lifecycle reference: the MCU handles low-power and control work while Linux uses a separately accelerated application domain. The 128 KiB NPU-local storage point is especially relevant. | Detailed NPU microarchitecture is not public, and the integrated camera and video pipeline is outside the defining Mini scope. Public V851S evidence is less authoritative than the V853 vendor material. |
| 3 | CVITEK CV1800B | RISC-V application and real-time cores, 64 MiB SiP DRAM, and an advertised 0.5 TOPS INT8 TPU. See the [CV1800B overview](https://milkv.io/chips/cv1800b), [SOPHGO CV180x/CV181x software documentation](https://doc.sophgo.com/cvitek-develop-docs/master/docs_latest_release/CV180x_CV181x/en/), and [Milk-V Buildroot SDK](https://github.com/milkv-duo/duo-buildroot-sdk-v2). | Closest RISC-V Linux, low-memory, low-cost TPU reference. Its separate TPU/TDMA software model and public Linux integration are useful for driver, reserved-memory, and model-deployment work. | The CPU is RV64, the multimedia subsystem is larger, and public compiler/runtime sources do not disclose a reusable TPU RTL implementation. |
| 4 | Kendryte K210 | A fixed-function KPU for convolution, batch normalization, activation, and pooling; DMA and direct camera input; 6 MiB general SRAM plus 2 MiB AI SRAM. See the [K210 functional description](https://github.com/kendryte/kendryte-doc-datasheet/blob/master/en/003.md) and [K210 product page](https://www.kendryte.com/product/kendryteai). | Strong reference for a simple first-generation accelerator in which dedicated local SRAM and a direct sensor data path matter more than a sophisticated coherent fabric. The associated [nncase compiler](https://github.com/kendryte/nncase) also demonstrates static allocation and quantized model lowering. | It is an older MCU-oriented design, its KPU has restrictive operator and model rules, and it is not a modern MMU-Linux reference. Its 28 nm memory budget must not be projected onto IHP130 or GF180 without physical evidence. |
| 5 | ST STM32N6 | A 600 GOPS Neural-ART accelerator, about 300 configurable MAC units, 4.2 MiB contiguous SRAM, streaming execution, an internal cache, and two 64-bit AXI memory ports. See the [STM32N6 overview](https://www.st.com/en/microcontrollers-microprocessors/stm32n6-series.html), [NPU introduction](https://www.st.com/resource/en/product_presentation/st-neural-art-accelerator-introduction.pdf), and [ST architecture discussion](https://blog.st.com/stm32n6/). | Useful upper reference for stream processing, layer fusion, local caching, camera-to-NPU integration, and the importance of memory bandwidth even in an MCU-class product. | Its RAM capacity, dual 64-bit interfaces, CPU frequency, process, and multimedia blocks are well beyond Mini. It should guide dataflow, not set Mini's throughput target. |
| 6 | NXP i.MX 93 | Linux Cortex-A55 application cores, a Cortex-M33 real-time processor, and a 0.5 TOPS Arm Ethos-U65 microNPU. See the [i.MX 93 product page](https://www.nxp.com/products/processors-and-microcontrollers/arm-processors/i-mx-applications-processors/i-mx-9-processors/i-mx-93-applications-processor-family-arm-cortex-a55-ml-acceleration-power-efficient-mpu%3Ai.MX93) and [family fact sheet](https://www.nxp.com/docs/en/fact-sheet/iMX93FAMFS.pdf). | Best higher-end reference for Linux driver integration, management-processor ownership, low-power wake-word use, security domains, product lifecycle, and a stable microNPU software contract. | Its coherent 64-bit application platform and DDR subsystem are not physical or performance peers for Mini. |
The recommended product reference combination is therefore:
- RV1106B for package, memory, and product-scale constraints;
- V851S/V853 for Linux-plus-management-MCU ownership;
- CV1800B for low-memory RISC-V Linux deployment and TPU/TDMA software
organization;
- K210 for explicit AI SRAM and sensor-to-accelerator dataflow;
- STM32N6 for streaming, fusion, and local-cache bandwidth reduction; and
- i.MX 93 for driver, isolation, recovery, and long-lived software contracts.
BL808 is relevant to the wider Mini heterogeneous architecture, but the public
NPU information and tooling are too incomplete to make it a primary NPU design
reference. Multi-TOPS devices such as K230, RK3568-class application
processors, and desktop-oriented accelerators assume memory bandwidth and
software complexity that belong in retroSoC Std or Pro.
## Recommended Mini-AI V1 Direction
The first implementation should be a small, parameterized integer inference
engine rather than an attempt to match the commercial devices' headline TOPS.
The following values are design-exploration starting points, not frozen
requirements:
| Area | Initial direction | Rationale |
| --- | --- | --- |
| Arithmetic | INT8/UINT8 operands with INT32 accumulation; no FP16 in V1 | Matches the useful precision of the closest commercial references while containing area and verification scope. |
| Array | Parameterized 8 by 8 MAC array; evaluate 16 by 16 only for a qualified denser process | Sixty-four MACs are a tractable IHP130 exploration point. Array size must remain independent of the software ABI. |
| Local memory | At least 128 KiB of banked SRAM, with weight, activation, and accumulator allocation selected per layer | A 32-bit system bus cannot feed the MAC array every cycle. Banked SRAM and tiling are mandatory architectural components, not optional optimizations. |
| Scheduling | Decoupled load, compute, and store engines with double buffering | Allows central DMA traffic to overlap computation and matches the reusable lesson from commercial streaming NPUs. |
| Operators | 1 by 1 and 3 by 3 convolution, depthwise convolution, GEMM/fully connected, add, ReLU-class activation, pooling, and requantization | Covers a useful quantized CNN subset while leaving uncommon operations to the CPU. Winograd, sparsity, and dynamic shapes should wait for workload evidence. |
| Control | APB4 configuration, capability discovery, versioned command descriptors, aggregate interrupt, timeout, abort, and reset | Fits the existing Mini control plane and permits a stable Linux and firmware contract. |
| Data | One framed 32-bit AXI4-Stream input and output, initially fed by central DMA; optional direct DVP input after arbitration and backpressure are verified | Reuses the existing non-coherent DMA and stream infrastructure without immediately adding another fabric master. A later native AXI4 master requires a deliberate interconnect revision. |
| Coherency | Explicit CPU cache clean/invalidate and owned-buffer transitions | The planned Linux configuration has no hardware I/O coherency. The driver must not hide this limitation. |
| Lifecycle | Hazard3 owns NPU clock, reset, bus admission, fault capture, and forced recovery; Linux owns job submission only | Preserves Mini's defining trusted-management model and prevents a failed Linux or NPU job from taking final lifecycle control. |
At 72 MHz, an 8 by 8 array has a 9.216 GOPS arithmetic ceiling if one MAC is
counted as two operations. This is intentionally far below commercial
marketing figures. A useful V1 result is sustained end-to-end acceleration of
selected models with bounded memory traffic, not a nominal TOPS target that
the existing memory system cannot sustain.
## Integration Prerequisites
Before an NPU becomes a supported Mini feature, the project should complete
all of the following:
1. Implement and verify native cache-line bursts in the selected external
memory controller. Measure sustained bandwidth under CPU, display, storage,
and NPU contention.
2. Select qualified SRAM macros and characterize the bank count, port width,
area, timing, leakage, and test strategy independently for every target
PDK.
3. Extend the canonical address map, interrupt topology, DMA request ownership,
SYSCTRL lifecycle controls, firewall rules, performance counters, and fault
records from their generated sources of truth.
4. Define a versioned command format and Linux UAPI before freezing the RTL.
Buffer ownership, cancellation, timeout, cache maintenance, and recovery
must be explicit.
5. Provide an offline model compiler that imports at least ONNX or TFLite,
performs calibration and INT8 quantization, tiles tensors into local SRAM,
fuses supported operators, emits a versioned binary, and reports every CPU
fallback.
6. Validate representative vision and audio models in a cycle-accurate model,
FPGA prototype, RTL simulation, synthesis, and post-layout memory-bandwidth
analysis before publishing performance or energy claims.
## Open Microarchitecture References
Commercial SoCs establish product expectations but do not provide reusable
NPU RTL. Microarchitecture work should therefore also study transparent
implementations:
- [Gemmini](https://github.com/ucb-bar/gemmini) for decoupled
load/execute/store control, a parameterized systolic array, private
scratchpad SRAM, and a separate accumulator;
- [NVDLA](https://nvdla.org/hw/v1/hwarch.html) for a configurable small
convolution pipeline, banked convolution buffer, fused activation/pooling,
and explicit memory-bandwidth tradeoffs; and
- [VTA](https://tvm.apache.org/2018/07/12/vta-release-announcement.html) for a
compact tensor instruction stream and end-to-end compiler/runtime
co-design.
These are reference starting points, not drop-in Mini IP. Their native CPU
coupling, bus protocol, generated RTL, verification assumptions, SRAM ports,
licenses, and toolchains require separate review before reuse.
+256
View File
@@ -0,0 +1,256 @@
# ULPI USB 2.0 Dual-Role Controller
This document is the architecture, register ABI, software, integration, and
verification contract for the self-owned `apb4_usb2` controller. The Mini SoC
management window is `APB4_USB2` at `0x10016000`. Payload data uses a dedicated
32-bit AXI4 master; configuration and status use APB4.
## Commercial reference selection
The reference review was refreshed on 2026-08-21 from vendor-owned material.
These products are references, not source-code dependencies and not evidence
that retroSoC has inherited their certification.
| Reference | Problem and architecture | Status and decision |
| --- | --- | --- |
| [Synopsys DesignWare USB 2.0 digital controllers](https://www.synopsys.com/designware-ip/interface-ip/usb/usb-2-0-digital-controllers.html) | Separates host/device/OTG digital control from either discrete ULPI or integrated UTMI/UTMI+ PHYs and emphasizes low-power integration. | The official product family remains published. Reuse the controller/PHY boundary and role-specific policy; do not add every PHY type or LPM mode to this fixed-ULPI design. |
| [Cadence USB 2.0 Controller IP](https://www.cadence.com/en_US/home/tools/silicon-solutions/design-ip/interface-ip/usb/usb2-controller.html) and [device-controller brief](https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/silicon-solutions/design-ip/design-ip-usb-2-0-device-controller-br.pdf) | Uses endpoint logic, configurable dual-port RAM, register control, and an AXI scatter-gather DMA engine; advertises up to 8 IN and 8 OUT endpoints. | The official product page remains active. Reuse the endpoint/RAM/DMA decomposition, software driver boundary, and deliverable categories. Avoid a hidden fixed FIFO partition and an opaque descriptor ABI. |
| [STM32 OTG HS software architecture](https://www.st.com/resource/en/user_manual/cd00289278-stm32f105xx-stm32f107xx-stm32f2xx-and-stm32f4xx-usb-onthego-host-and-device-library-stmicroelectronics.pdf) and [ST USB hardware guidance](https://www.st.com/resource/en/application_note/an4879-usb-hardware-design-guidelines-for-stm32-microcontrollers-stmicroelectronics.pdf) | Demonstrates a deployed dual-role core with external ULPI, programmable FIFO partitioning, DMA, interrupts, DCD/HCD software layers, dynamic role switching, and suspend/resume policy. | ST continues to publish the documents. Reuse explicit DCD/HCD primitives and dynamic packet-memory allocation. Avoid tying a high-speed peripheral to general GPIO mux policy or depending on UART output as a functional verdict. |
| [Microchip USB3320](https://www.microchip.com/en-us/product/USB3320), [data sheet](https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/00001792E.pdf), and [hardware checklist](https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/SupportingCollateral/USB3320-Hardware-Design-Checklist-00003003B.pdf) | Provides the external USB 2.0 HS PHY, 8-bit SDR ULPI DATA, DIR/NXT/STP arbitration, register access, and a 60 MHz ULPI clock. | Microchip lists USB3320 as in production. It is the primary PHY target. Board and ASIC timing must use the selected clock mode and the current data-sheet numbers, not generic GPIO timing. |
The selected architecture is a custom descriptor dual-role controller rather
than EHCI. EHCI would bring operating-system compatibility, but its queue-head,
periodic-tree, and hub/split-transaction cost is disproportionate for this
management-class SoC. The retained commercial patterns are a stable register
ABI, autonomous DMA, explicit endpoint/channel resources, configurable packet
memory, clock-domain isolation, interrupts, error capture, a HAL, and a
verification matrix.
## Delivered scope and maturity
The delivered RTL is a synthesizable controller foundation with these fixed
resources:
- USB 2.0 device and embedded-host roles, plus software-forced or ID/VBUS
selected role switching.
- Eight bidirectional device endpoint numbers: EP0 plus EP1..EP7, with
independent IN and OUT enable, stall, descriptor, packet-RAM, completion,
byte-count, and data-toggle state.
- Sixteen host channels with device address, endpoint, direction, transfer
type, speed, interval, data toggle, packet-RAM region, and descriptor head.
- Control, bulk, interrupt, and single-transaction isochronous data paths.
Interrupt and isochronous host commands are retained until their programmed
microframe interval is due.
- HS/FS device operation; HS/FS and direct-attached LS host transaction
encoding. Hubs and split transactions are not implemented.
- A 16 KiB dynamically partitioned packet RAM with SECDED protection and
saturating corrected/uncorrectable counters.
- A 32-bit AXI4 scatter-gather DMA master, 32-byte descriptors, 16-beat INCR
bursts, 4 KiB boundary protection, byte strobes on the final word, precise
response/ID/RLAST checks, and bounded abort.
- ULPI packet TX/RX, PID/CRC5/CRC16 checking, receive-command status capture,
register viewport, timeout handling, and link-owned DATA output enable.
- APB4 interrupts with sticky W1C status, first-error snapshots, endpoint W1C
completion maps, performance counters, and a SystemCtrl USB AXI wait counter.
- Handwritten C register ABI, descriptor helpers, low-level DCD/HCD-style HAL
primitives, and a non-invasive bring-up register self-test.
This is not yet a USB-IF-certified or production-qualified IP release. The
current repository tests establish digital block behavior and SoC integration;
they do not establish analog PHY, package, board, protocol compliance, CDC/RDC,
DFT, or silicon signoff. High-bandwidth isochronous transactions, OTG HNP/SRP,
LPM, hubs/splits, and a class stack remain outside this release.
## Architecture
The system and link sides are deliberately separate:
```text
management core -- APB4 -- register/IRQ/error/perf
|
role + scheduler
|
memory fabric <--- AXI4 SG-DMA <--> async data FIFOs
|
16 KiB SECDED packet RAM
|
transaction engine
|
PID/CRC SIE + ULPI link
|
dedicated 3.3 V ULPI pads
|
USB3320 PHY
```
`clk_i` owns APB, DMA, descriptors, role policy, scheduler state, interrupts,
and performance counters. `clk_ulpi_i` is the PHY-supplied 60 MHz clock and
owns packet RAM access, transaction state, SIE, and the physical ULPI pins.
Commands, results, setup packets, packet-store commands, viewport requests,
and packet streams cross through Common `async_reqack`, `cdc_fifo`, `cdc_sync`,
and `edge_det` components. Each packet-stream CDC FIFO has eight 37-bit words;
this covers pointer synchronization and short AXI/ULPI rate differences without
placing a 32-word data-array reset load on the 60 MHz ready path. A Common
`spill_register` at the ULPI-to-system packet-stream boundary cuts the packet
store backpressure path while retaining one-word-per-cycle steady-state flow.
Role changes wait for active work to quiesce, then flush queued commands and
reset the link domain before the new role starts.
The command front end retains endpoint and channel writes while the scheduler
is busy. Multiple bits for one resource coalesce; software must not submit a
second transfer for the same resource until completion. Endpoint commands have
priority over host channels, then the lowest numbered ready resource wins.
The packet RAM uses 4096 40-bit words: 32 data bits plus the encoded SECDED
bits. IHP130 maps this to two `4096x16` and one `4096x8` single-port macros.
The `Issue -> Wait -> Send` packet-store pipeline registers corrected read data
between SECDED decode and the DMA/SIE consumers. This keeps the SRAM read delay
and byte-wise TX CRC logic in separate 60 MHz timing stages.
Other profiles use the technology wrapper's inferred model. The `RAM_BIST`
register is reserved for a future DFT wrapper and is not advertised by
`CAPABILITY0`.
## AXI4 and performance contract
USB DMA is AXI master 6; management, user, central DMA, SDIO0, SDIO1, and
SPI-SD retain masters 0..5. Transfers are 32-bit INCR bursts with at most 16
beats and never cross 4 KiB. The engine permits one read or write operation at
a time and checks every response. Descriptor control and runtime words are
written back before hardware clears `OWN`.
The performance design target is at least 40 MiB/s sustained one-way HS bulk
payload with large descriptors, 512-byte max packets, memory without prolonged
backpressure, and interrupts coalesced at transfer rather than packet
granularity. The raw ULPI byte rate is 60 MB/s, so software, packet boundaries,
handshakes, AXI arbitration, and CDC overhead determine the measured result.
This target is a release gate, not a claim from the current unit tests. Measure
it on FPGA/silicon with the per-IP byte/retry/stall counters and SystemCtrl
`PERF_USB2_WAIT_LO/HI` counters.
## APB4 register contract
The window is 4 KiB. An accepted APB enable phase completes through a
registered `PREADY`. Misaligned or unknown accesses return `PSLVERR`.
Configuration that changes role, PHY, timeout, endpoint/channel layout, or
descriptor heads must be written while `GLOBAL_CTRL.ENABLE` is clear. Command,
reset, abort, performance-clear, BIST-reserved, IRQ-test, and viewport starts
are pulses. `IRQ_STATUS`, `ERROR_STATUS`, and endpoint completion maps are W1C.
Reads have no side effects except the unimplemented PIO aperture, which returns
`PSLVERR`; DMA is the supported payload path.
| Offset | Register group | Contract |
| ---: | --- | --- |
| 0x000-0x00c | IP_ID, IP_VERSION, CAPABILITY0/1 | RO identity, feature bits, endpoint/channel counts, 16 KiB RAM |
| 0x010-0x03c | global, role, PHY, viewport, timeout, frame, test | mixed control/status; viewport address is 6 bits |
| 0x040-0x05c | IRQ and first error snapshot | 16 IRQ sources; sticky W1C and descriptor/buffer fault addresses |
| 0x060-0x07c | performance | TX/RX bytes, packets, retries, AXI/RAM stalls, IRQ count, clear pulse |
| 0x100-0x120 | device global and completion maps | address, setup packet, pending and W1C completion |
| 0x200 + n*0x40 | endpoint n | CFG, IN/OUT RAM, IN/OUT descriptor, command, status, byte counters |
| 0x400-0x414 | host and schedule global | host/port controls, frame and scheduler status |
| 0x500 + n*0x40 | host channel n | CFG, target, interval, RAM, descriptor, command, status, bytes |
| 0x900-0x918 | packet RAM, ECC, debug | RAM state, ECC counters, reserved BIST pulse, debug snapshot |
The executable bit/offset source is `rtl/ip/usb/usb2_define.svh`. The direct,
hand-maintained C mirror is `<retrosoc/hal/usb2_regs.h>`; the parity test fails
if one side changes without the other. No register generator is used.
## DMA descriptor ABI
Each little-endian descriptor is 32-byte aligned and must not start above
offset `0xfe0` within a 4 KiB region:
| Word | Name | Ownership |
| ---: | --- | --- |
| 0 | BUFFER_ADDR | software; non-zero and 32-bit aligned |
| 1 | BYTE_LENGTH | software; positive, no 32-bit address wrap |
| 2 | NEXT_ADDR | software; 32-byte aligned and same boundary rule, zero at END |
| 3 | CONTROL | software flags and hardware completion flags |
| 4 | ACTUAL_LENGTH | hardware writeback |
| 5 | STATUS | hardware writeback |
| 6 | FRAME | software scheduling metadata, bits 31:27 reserved |
| 7 | RESERVED | zero |
`CONTROL` uses OWN/CHAIN/END/IRQ/SHORT_OK/ZERO_PACKET in bits 0..5 and
DONE/SHORT/STALL/TIMEOUT/CRC/PROTOCOL/AXI/ABORTED in bits 16..23. A chain has at
most 256 descriptors. Software initializes payload and runtime words, executes
a full memory barrier, and sets `OWN` last. A chain is published tail-first so
the DMA cannot reach an unpublished next descriptor. Hardware writes runtime
state and clears `OWN` last. Descriptors and buffers must be in DMA-visible,
non-cached memory or be maintained with platform cache operations.
## Software sequence
1. Hold the USB3320 in reset and call `rs_usb2_probe()` or the non-invasive
`rs_usb2_controller_selftest()`.
2. Call `rs_usb2_reset()`, then `rs_usb2_configure()` while disabled. Choose a
forced role or ID/VBUS auto-role, timeout, FS override, IRQ mask, and PHY
reset release policy.
3. Allocate non-overlapping packet-RAM regions. Prepare and publish DMA
descriptors. Configure endpoints with `rs_usb2_endpoint_configure()` or host
channels with `rs_usb2_channel_configure()`.
4. Enable the controller. Device software arms OUT or primes IN endpoints;
host software starts a channel. The HAL executes a memory barrier before the
command write.
5. Service setup/endpoint/channel/DMA/error interrupts. Read descriptor
writeback only after observing completion and a memory barrier. Clear W1C
bits after consuming state.
6. Disable or abort and wait for idle before changing protected layout. A role
switch invalidates queued transaction context and requires reconfiguration.
For periodic host transfers, HAL `interval` is in microframes at HS and frames
at FS/LS; it converts FS/LS intervals into the internal microframe time base.
The current layer provides controller primitives, not USB enumeration, class,
hub, or operating-system integration.
## ASIC and board boundary
The ASIC top exposes dedicated `usb2_ulpi_clk_i_pad`, `usb2_ulpi_data0_io_pad`
through `usb2_ulpi_data7_io_pad`, `usb2_ulpi_dir_i_pad`,
`usb2_ulpi_nxt_i_pad`, `usb2_ulpi_stp_o_pad`, and `usb2_ulpi_reset_n_o_pad`.
They do not enter the GPIO alternate-function mux. All ULPI signals and the PHY
reset pad belong to a reviewed 3.3 V I/O bank with an explicit pad-ring,
power-up, isolation, ESD, drive-strength, slew, and package assignment.
USB3320 output-clock mode is the integration default: the PHY supplies the
60 MHz `clk_ulpi_i`. STA must apply the selected data-sheet setup/hold and
output-delay values at the package boundary, including DATA turnaround. Reset,
reference clock/crystal, REFSEL straps, VBUS switch/current limit, ID/VBUS
sensing, connector ESD, and USB differential routing are board responsibilities.
The FPGA profile intentionally leaves these pads unbound until a board with a
3.3 V bank and reviewed constraints exists.
The core-STA profile declares `usb2_ulpi_clk_i_pad` as an asynchronous 60 MHz
root clock in `clock_reset_domains.json`. IHP130 timing runs always link the
`4096x16` and `4096x8` slow-corner Liberty views used by the USB packet RAM;
missing macro models or a missing `clk_usb2_ulpi` path group invalidate the STA
evidence even when the OpenSTA process exits successfully.
## Verification and commercial release gates
Current automated evidence covers PID/CRC vectors, descriptor rejection, ECC
correction/detection, packet-store paths, APB errors/W1C, DMA fetch/payload/
writeback, host transaction flow, ISO no-handshake completion, scheduler queue
retention and interval gating, ULPI link-domain smoke, generated address/pin/
IRQ topology, firmware self-test, synthesis compilation, SoC simulation, and
quality gates.
A commercial release additionally requires:
- USB protocol VIP for device and host randomized traffic, error injection,
reset/suspend/resume, all endpoint types, retry/toggle corner cases, and
coverage closure. The testbench boundary must permit replacing the local PHY
model with commercial VIP.
- Static CDC/RDC signoff of every system/ULPI crossing and reset sequence;
lint, synthesis, equivalence, DFT, scan, memory BIST integration, STA with
actual pads/macros, IR/EM, and gate-level reset/X testing.
- FPGA validation with USB3320 and protocol-analyzer traces, 24-hour stress,
cable attach/remove, malformed traffic, memory backpressure, suspend/resume,
and measured 40 MiB/s bulk throughput.
- USB-IF electrical and protocol compliance, selected PHY/package/board signal
integrity, production diagnostics, versioned release notes, integration
scripts, timing constraints, coverage reports, waivers, safety/security
review, and a supported DCD/HCD plus class stack.
Until those gates close, use the IP for architectural integration and digital
validation, not as a certified drop-in replacement for a licensed commercial
USB controller.
+6
View File
@@ -120,6 +120,12 @@ Mini is not intended to compete with general-purpose Linux application
processors that require RV64, cache-coherent multiprocessing, or
high-bandwidth DDR interfaces.
An optional NPU derivative may be explored separately as Mini-AI without
changing the base Mini requirements. The commercial survey, memory-system
constraints, and initial accelerator direction are recorded in
[Mini NPU Commercial Reference Survey](ip/mini-npu.md). No NPU is implemented by
the current Mini profiles.
### Current Mini Baseline
The current executable RTL and generated integration inputs define these
+7
View File
@@ -80,6 +80,12 @@ module retrosoc_top (
wire s_jtag_tms;
wire s_jtag_tdi;
wire s_jtag_trst_n;
wire s_usb2_ulpi_clk;
tri0 s_usb2_ulpi_dir;
tri0 s_usb2_ulpi_nxt;
tri0 [7:0] s_usb2_ulpi_data;
wire s_usb2_ulpi_stp;
wire s_usb2_ulpi_reset_n;
clk_wiz_0 u_clk_wiz_0 (
.clk_in1 (clk_i),
.clk_out1(s_sys_clk),
@@ -90,6 +96,7 @@ module retrosoc_top (
assign s_jtag_tms = 1'b0;
assign s_jtag_tdi = 1'b0;
assign s_jtag_trst_n = 1'b0;
assign s_usb2_ulpi_clk = s_sys_clk;
retrosoc_asic u_retrosoc (
`include "retrosoc_asic_fpga_mini_bindings.svh"
+6 -1
View File
@@ -3,8 +3,13 @@
ifeq ($(PDK),IHP130)
OPENSTA_LIBERTY := $(ROOT_PATH)/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/lib/sg13g2_stdcell_slow_1p08V_125C.lib
OPENSTA_LINK_LIBS := $(ROOT_PATH)/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_io/lib/sg13g2_io_slow_1p08V_3p0V_125C.lib
IHP130_USB2_SRAM_LIBS := \
$(ROOT_PATH)/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/lib/RM_IHPSG13_1P_4096x16_c3_bm_bist_slow_1p08V_125C.lib \
$(ROOT_PATH)/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/lib/RM_IHPSG13_1P_4096x8_c3_bm_bist_slow_1p08V_125C.lib
ifeq ($(HAVE_SRAM_MACRO),YES)
OPENSTA_SRAM_LIBS := $(wildcard $(ROOT_PATH)/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/lib/*_slow_1p08V_125C.lib)
else
OPENSTA_SRAM_LIBS := $(IHP130_USB2_SRAM_LIBS)
endif
else ifeq ($(PDK),GF180)
OPENSTA_LIBERTY := $(ROOT_PATH)/.cache/retrosoc/pdk/gf180/gf180mcu_fd_sc_mcu7t5v0__ss_125C_4v50.lib
@@ -19,4 +24,4 @@ else
$(error OpenSTA core-STA does not support PDK=$(PDK))
endif
OPENSTA_SRAM_LIBS ?=
OPENSTA_SRAM_LIBS ?=
+2
View File
@@ -2,3 +2,5 @@
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_core_behavioral_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_1024x64_c2_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_4096x16_c3_bm_bist.v
/physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_sram/verilog/RM_IHPSG13_1P_4096x8_c3_bm_bist.v
+1 -1
View File
@@ -3,7 +3,7 @@
This directory owns project RTL IP.
Self-owned IP is grouped by function: interconnect, util, memory, storage,
serial, multimedia, and peripheral. The Mini SoC selects active sources
serial, USB, multimedia, and peripheral. The Mini SoC selects active sources
explicitly in rtl/mini/filelist/ip.fl, whose source order is part of
the build contract.
+9
View File
@@ -54,6 +54,8 @@ module sysctrl_core (
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_SDIO1_WAIT_LO);
localparam sysctrl_offset_t PerfSdio1WaitHi =
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_SDIO1_WAIT_HI);
localparam sysctrl_offset_t PerfUsb2WaitLo = sysctrl_offset_t'(`APB4_SYSCTRL__PERF_USB2_WAIT_LO);
localparam sysctrl_offset_t PerfUsb2WaitHi = sysctrl_offset_t'(`APB4_SYSCTRL__PERF_USB2_WAIT_HI);
localparam sysctrl_offset_t PerfApb4PeriphWaitLo =
sysctrl_offset_t'(`APB4_SYSCTRL__PERF_APB4_PERIPH_WAIT_LO);
localparam sysctrl_offset_t PerfApb4PeriphWaitHi =
@@ -105,6 +107,7 @@ module sysctrl_core (
logic [63:0] s_perf_dma_wait_d, s_perf_dma_wait_q;
logic [63:0] s_perf_sdio0_wait_d, s_perf_sdio0_wait_q;
logic [63:0] s_perf_sdio1_wait_d, s_perf_sdio1_wait_q;
logic [63:0] s_perf_usb2_wait_d, s_perf_usb2_wait_q;
logic [63:0] s_perf_apb4_periph_wait_d, s_perf_apb4_periph_wait_q;
logic [63:0] s_perf_apb4_system_wait_d, s_perf_apb4_system_wait_q;
logic [63:0] s_perf_sdram_wait_d, s_perf_sdram_wait_q;
@@ -310,6 +313,7 @@ module sysctrl_core (
s_perf_dma_wait_d = s_perf_dma_wait_q;
s_perf_sdio0_wait_d = s_perf_sdio0_wait_q;
s_perf_sdio1_wait_d = s_perf_sdio1_wait_q;
s_perf_usb2_wait_d = s_perf_usb2_wait_q;
s_perf_apb4_periph_wait_d = s_perf_apb4_periph_wait_q;
s_perf_apb4_system_wait_d = s_perf_apb4_system_wait_q;
s_perf_sdram_wait_d = s_perf_sdram_wait_q;
@@ -324,6 +328,7 @@ module sysctrl_core (
s_perf_dma_wait_d = sysctrl.perf_dma_wait_i;
s_perf_sdio0_wait_d = sysctrl.perf_sdio0_wait_i;
s_perf_sdio1_wait_d = sysctrl.perf_sdio1_wait_i;
s_perf_usb2_wait_d = sysctrl.perf_usb2_wait_i;
s_perf_apb4_periph_wait_d = sysctrl.perf_apb4_periph_wait_i;
s_perf_apb4_system_wait_d = sysctrl.perf_apb4_system_wait_i;
s_perf_sdram_wait_d = sysctrl.perf_sdram_wait_i;
@@ -386,6 +391,7 @@ module sysctrl_core (
s_perf_dma_wait_q <= '0;
s_perf_sdio0_wait_q <= '0;
s_perf_sdio1_wait_q <= '0;
s_perf_usb2_wait_q <= '0;
s_perf_apb4_periph_wait_q <= '0;
s_perf_apb4_system_wait_q <= '0;
s_perf_sdram_wait_q <= '0;
@@ -424,6 +430,7 @@ module sysctrl_core (
s_perf_dma_wait_q <= s_perf_dma_wait_d;
s_perf_sdio0_wait_q <= s_perf_sdio0_wait_d;
s_perf_sdio1_wait_q <= s_perf_sdio1_wait_d;
s_perf_usb2_wait_q <= s_perf_usb2_wait_d;
s_perf_apb4_periph_wait_q <= s_perf_apb4_periph_wait_d;
s_perf_apb4_system_wait_q <= s_perf_apb4_system_wait_d;
s_perf_sdram_wait_q <= s_perf_sdram_wait_d;
@@ -482,6 +489,8 @@ module sysctrl_core (
PerfSdio0WaitHi: read_data_o = s_perf_sdio0_wait_q[63:32];
PerfSdio1WaitLo: read_data_o = s_perf_sdio1_wait_q[31:0];
PerfSdio1WaitHi: read_data_o = s_perf_sdio1_wait_q[63:32];
PerfUsb2WaitLo: read_data_o = s_perf_usb2_wait_q[31:0];
PerfUsb2WaitHi: read_data_o = s_perf_usb2_wait_q[63:32];
PerfApb4PeriphWaitLo: read_data_o = s_perf_apb4_periph_wait_q[31:0];
PerfApb4PeriphWaitHi: read_data_o = s_perf_apb4_periph_wait_q[63:32];
PerfApb4SystemWaitLo: read_data_o = s_perf_apb4_system_wait_q[31:0];
+2
View File
@@ -42,6 +42,8 @@
`define APB4_SYSCTRL__PERF_FLASH_WAIT_HI `SOC_SYSCTRL_PERF_FLASH_WAIT_HI_OFFSET
`define APB4_SYSCTRL__TEST_STATUS `SOC_SYSCTRL_TEST_STATUS_OFFSET
`define APB4_SYSCTRL__RTC_WAKE_STATUS `SOC_SYSCTRL_RTC_WAKE_STATUS_OFFSET
`define APB4_SYSCTRL__PERF_USB2_WAIT_LO `SOC_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET
`define APB4_SYSCTRL__PERF_USB2_WAIT_HI `SOC_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET
`define APB4_SYSCTRL__PLL_CMD_APPLY 0
`define APB4_SYSCTRL__PLL_CMD_CLEAR_ERROR 1
+2
View File
@@ -18,6 +18,7 @@ interface sysctrl_if ();
logic [ 63:0] perf_dma_wait_i;
logic [ 63:0] perf_sdio0_wait_i;
logic [ 63:0] perf_sdio1_wait_i;
logic [ 63:0] perf_usb2_wait_i;
logic [ 63:0] perf_apb4_periph_wait_i;
logic [ 63:0] perf_apb4_system_wait_i;
logic [ 63:0] perf_sdram_wait_i;
@@ -39,6 +40,7 @@ interface sysctrl_if ();
input perf_dma_wait_i,
input perf_sdio0_wait_i,
input perf_sdio1_wait_i,
input perf_usb2_wait_i,
input perf_apb4_periph_wait_i,
input perf_apb4_system_wait_i,
input perf_sdram_wait_i,
+16
View File
@@ -0,0 +1,16 @@
# USB 2.0 RTL
This directory owns the self-contained ULPI USB 2.0 dual-role controller RTL,
its APB4 ABI in `usb2_define.svh`, interfaces, link/SIE/transaction blocks,
packet store, scheduler, descriptor DMA, and top-level `apb4_usb2` wrapper.
Use Common register, CDC, FIFO, ECC, and AXI helpers. Technology-specific
packet RAM mapping belongs in `rtl/tech/tc_usb2_packet_ram.sv`; SoC address,
interrupt, AXI, and dedicated-pad integration belongs under `rtl/mini/`.
Register definitions are hand maintained and mirrored by
`crt/include/retrosoc/hal/usb2_regs.h`; do not introduce a register generator.
Validate focused behavior with `python3 -m pytest -q tests/test_usb2.py
tests/test_usb2_register_parity.py`, then run the selected firmware/simulator,
RTL quality checks, and the supported PR regression. The design and delivery
boundary are defined in `docs/ip/usb2.md`.
+970
View File
@@ -0,0 +1,970 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "usb2_define.svh"
module apb4_usb2 #(
parameter int NumEndpoints = 8,
parameter int NumChannels = 16
) (
// verilog_format: off -- preserve clock, bus, interrupt, and ULPI groups
input logic clk_i,
input logic rst_n_i,
input logic clk_ulpi_i,
apb4_if.slave apb4,
axi4_if.master dma_axi4,
output logic irq_o,
usb2_ulpi_if.dut ulpi
// verilog_format: on
);
logic s_en;
logic s_soft_reset;
logic s_abort;
logic [ 1:0] s_force_role;
logic s_auto_role;
logic s_phy_reset_n_cfg;
logic s_phy_suspend;
logic s_remote_wake;
logic [ 31:0] s_timeout;
logic [ 7:0] s_test_ctrl;
logic s_perf_clear;
logic [ 6:0] s_device_addr;
logic [ 15:0] s_device_ctrl;
logic [NumEndpoints-1:0][31:0] s_endpoint_cfg;
logic [NumEndpoints-1:0][31:0] s_endpoint_ram_in;
logic [NumEndpoints-1:0][31:0] s_endpoint_ram_out;
logic [NumEndpoints-1:0][31:0] s_endpoint_desc_in;
logic [NumEndpoints-1:0][31:0] s_endpoint_desc_out;
logic [NumEndpoints-1:0][31:0] s_endpoint_cmd;
logic [ 31:0] s_endpoint_complete_clear_in;
logic [ 31:0] s_endpoint_complete_clear_out;
logic [ 15:0] s_host_ctrl;
logic s_port_ctrl;
logic s_schedule_ctrl;
logic [ NumChannels-1:0][31:0] s_channel_cfg;
logic [ NumChannels-1:0][31:0] s_channel_target;
logic [ NumChannels-1:0][31:0] s_channel_interval;
logic [ NumChannels-1:0][31:0] s_channel_ram;
logic [ NumChannels-1:0][31:0] s_channel_desc;
logic [ NumChannels-1:0][31:0] s_channel_cmd;
logic [ 15:0] s_ram_ctrl;
logic s_ram_bist_start;
logic s_viewport_valid;
logic s_viewport_write;
logic [ 5:0] s_viewport_addr;
logic [ 7:0] s_viewport_write_data;
logic s_viewport_resp_valid_sys;
logic [ 8:0] s_viewport_resp_data_sys;
logic [ 31:0] s_global_stat;
logic [ 31:0] s_role_stat;
logic [ 31:0] s_phy_stat;
logic [ 31:0] s_frame_stat;
logic [ 31:0] s_device_stat;
logic [63:0] s_setup_packet_d, s_setup_packet_q;
logic [ 31:0] s_endpoint_pending_in;
logic [ 31:0] s_endpoint_pending_out;
logic [ 31:0] s_endpoint_complete_in;
logic [ 31:0] s_endpoint_complete_out;
logic [NumEndpoints-1:0][31:0] s_endpoint_stat;
logic [NumEndpoints-1:0][31:0] s_endpoint_bytes_in;
logic [NumEndpoints-1:0][31:0] s_endpoint_bytes_out;
logic [ 31:0] s_host_stat;
logic [ 31:0] s_port_stat;
logic [ 31:0] s_schedule_stat;
logic [ NumChannels-1:0][31:0] s_channel_stat;
logic [ NumChannels-1:0][31:0] s_channel_bytes;
logic [ 31:0] s_ram_stat;
logic [ 31:0] s_ecc_stat;
logic [ 31:0] s_ecc_corrected_count_sys;
logic [ 31:0] s_ecc_uncorrectable_count_sys;
logic [ 31:0] s_debug_stat;
logic [ 31:0] s_perf_tx_bytes;
logic [ 31:0] s_perf_rx_bytes;
logic [ 31:0] s_perf_packets;
logic [ 31:0] s_perf_retries;
logic [ 31:0] s_perf_axi_stall_d;
logic [ 31:0] s_perf_axi_stall_q;
logic [ 31:0] s_perf_ram_stall_d;
logic [ 31:0] s_perf_ram_stall_q;
logic [ 31:0] s_perf_irq_count;
logic [ 15:0] s_scheduler_irq_event;
logic [ 15:0] s_irq_event;
logic s_err_capture;
logic [ 31:0] s_err_stat;
logic [ 31:0] s_err_code;
logic [ 31:0] s_err_info;
logic [ 31:0] s_err_desc_addr;
logic [ 31:0] s_err_buffer_addr;
usb2_pkg::usb2_role_e s_active_role_sys;
usb2_pkg::usb2_role_e s_active_role_ulpi;
logic [ 1:0] s_active_role_ulpi_bits;
logic s_role_change;
logic s_role_reset;
logic s_role_reset_ulpi;
logic s_role_switch_pending;
logic s_transaction_busy_ulpi;
logic s_transaction_busy_sys;
logic s_scheduler_busy;
logic s_link_ready_ulpi;
logic s_link_ready_sys;
logic [ 1:0] s_line_state_ulpi;
logic [ 1:0] s_line_state_sys;
logic [ 1:0] s_vbus_state_ulpi;
logic [ 1:0] s_vbus_state_sys;
logic s_id_ground_ulpi;
logic s_id_ground_sys;
logic [ 13:0] s_frame_ulpi;
logic [ 13:0] s_frame_sys;
logic s_ulpi_rst_n;
logic s_link_rst_n;
logic [ 6:0] s_device_addr_ulpi;
logic [NumEndpoints-1:0][31:0] s_endpoint_cfg_ulpi;
logic [ 31:0] s_timeout_ulpi;
logic s_high_speed_ulpi;
logic s_en_ulpi;
logic s_phy_reset_n_ulpi;
logic s_work_valid_sys;
logic s_work_ready_sys;
logic [ 63:0] s_work_data_sys;
logic s_work_valid_ulpi;
logic s_work_ready_ulpi;
logic [ 63:0] s_work_data_ulpi;
logic s_result_valid_ulpi;
logic s_result_ready_ulpi;
logic [ 63:0] s_result_data_ulpi;
logic s_result_valid_sys;
logic s_result_ready_sys;
logic [ 63:0] s_result_data_sys;
logic s_setup_valid_ulpi;
logic s_setup_ready_ulpi;
logic [ 63:0] s_setup_data_ulpi;
logic s_setup_valid_sys;
logic [ 63:0] s_setup_data_sys;
logic s_fill_cmd_valid_sys;
logic s_fill_cmd_ready_sys;
logic [ 26:0] s_fill_cmd_data_sys;
logic s_fill_cmd_valid_ulpi;
logic s_fill_cmd_ready_ulpi;
logic [ 26:0] s_fill_cmd_data_ulpi;
logic s_drain_cmd_valid_sys;
logic s_drain_cmd_ready_sys;
logic [ 26:0] s_drain_cmd_data_sys;
logic s_drain_cmd_valid_ulpi;
logic s_drain_cmd_ready_ulpi;
logic [ 26:0] s_drain_cmd_data_ulpi;
logic s_buffer_event_valid_ulpi;
logic s_buffer_event_ready_ulpi;
logic [ 2:0] s_buffer_event_data_ulpi;
logic s_buffer_event_valid_sys;
logic s_buffer_event_ready_sys;
logic [ 2:0] s_buffer_event_data_sys;
logic s_view_req_valid_sys;
logic s_view_req_ready_sys;
logic [ 14:0] s_view_req_data_sys;
logic s_view_req_valid_ulpi;
logic s_view_req_ready_ulpi;
logic [ 14:0] s_view_req_data_ulpi;
logic s_view_resp_valid_ulpi;
logic s_view_resp_ready_ulpi;
logic [ 8:0] s_view_resp_data_ulpi;
logic s_fill_stream_valid_sys;
logic s_fill_stream_ready_sys;
logic [ 36:0] s_fill_stream_data_sys;
logic s_fill_stream_valid_ulpi;
logic s_fill_stream_ready_ulpi;
logic [ 36:0] s_fill_stream_data_ulpi;
logic s_drain_stream_valid_ulpi;
logic s_drain_stream_ready_ulpi;
logic [ 36:0] s_drain_stream_data_ulpi;
logic s_drain_fifo_valid_ulpi;
logic s_drain_fifo_ready_ulpi;
logic [ 36:0] s_drain_fifo_data_ulpi;
logic s_drain_stream_valid_sys;
logic s_drain_stream_ready_sys;
logic [ 36:0] s_drain_stream_data_sys;
logic s_dma_start;
logic s_dma_abort;
logic s_dma_memory_to_packet;
logic [ 31:0] s_dma_desc_base;
logic [ 31:0] s_dma_transfer_bytes;
logic s_dma_busy;
logic s_dma_done;
logic s_dma_err;
logic [ 7:0] s_dma_err_code;
logic [ 31:0] s_dma_current_desc;
// Retained for waveform and future debug CSR use.
/* verilator lint_off UNUSEDSIGNAL */
logic [ 31:0] s_dma_next_desc;
logic [ 31:0] s_dma_frame;
/* verilator lint_on UNUSEDSIGNAL */
logic [ 31:0] s_dma_bytes_done;
logic s_dma_descriptor_irq;
logic s_dma_abort_done;
logic s_retry_ulpi;
logic s_protocol_err_ulpi;
logic s_retry_toggle_d, s_retry_toggle_q;
logic s_protocol_toggle_d, s_protocol_toggle_q;
logic s_retry_event_sys;
logic s_protocol_event_sys;
logic s_ecc_corrected_event;
logic s_ecc_uncorrectable_event;
logic s_retry_re_sys, s_retry_fe_sys;
logic s_protocol_re_sys, s_protocol_fe_sys;
// edge_det levels are intentionally not consumed.
/* verilator lint_off UNUSEDSIGNAL */
logic s_retry_toggle_level_sys;
logic s_protocol_toggle_level_sys;
/* verilator lint_on UNUSEDSIGNAL */
logic s_view_pending_d, s_view_pending_q;
logic [14:0] s_view_data_d, s_view_data_q;
logic [31:0] s_ecc_corrected_previous_d, s_ecc_corrected_previous_q;
logic [31:0] s_ecc_uncorrectable_previous_d, s_ecc_uncorrectable_previous_q;
logic [31:0] s_ecc_corrected_count_ulpi;
logic [31:0] s_ecc_uncorrectable_count_ulpi;
assign s_link_rst_n = s_ulpi_rst_n && !s_role_reset_ulpi;
assign s_active_role_ulpi = usb2_pkg::usb2_role_e'(s_active_role_ulpi_bits);
assign s_retry_toggle_d = s_retry_toggle_q ^ s_retry_ulpi;
assign s_protocol_toggle_d = s_protocol_toggle_q ^ s_protocol_err_ulpi;
assign s_view_req_valid_sys = s_view_pending_q;
assign s_view_req_data_sys = s_view_data_q;
assign s_setup_packet_d = s_setup_valid_sys ? s_setup_data_sys : s_setup_packet_q;
assign s_ecc_corrected_event = s_ecc_corrected_count_sys != s_ecc_corrected_previous_q;
assign s_ecc_uncorrectable_event =
s_ecc_uncorrectable_count_sys != s_ecc_uncorrectable_previous_q;
assign s_ecc_corrected_previous_d = s_ecc_corrected_count_sys;
assign s_ecc_uncorrectable_previous_d = s_ecc_uncorrectable_count_sys;
rst_sync #(
.STAGE(2)
) u_ulpi_rst_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(rst_n_i),
.rst_n_o(s_ulpi_rst_n)
);
assign s_retry_event_sys = s_retry_re_sys || s_retry_fe_sys;
assign s_protocol_event_sys = s_protocol_re_sys || s_protocol_fe_sys;
always_comb begin
s_perf_axi_stall_d = s_perf_axi_stall_q;
s_perf_ram_stall_d = s_perf_ram_stall_q;
if (s_perf_clear) begin
s_perf_axi_stall_d = 32'd0;
s_perf_ram_stall_d = 32'd0;
end else begin
if (((dma_axi4.awvalid && !dma_axi4.awready) ||
(dma_axi4.wvalid && !dma_axi4.wready) ||
(dma_axi4.bvalid && !dma_axi4.bready) ||
(dma_axi4.arvalid && !dma_axi4.arready) ||
(dma_axi4.rvalid && !dma_axi4.rready)) && !(&s_perf_axi_stall_q)) begin
s_perf_axi_stall_d = s_perf_axi_stall_q + 1'b1;
end
if (((s_fill_stream_valid_sys && !s_fill_stream_ready_sys) ||
(s_drain_stream_valid_sys && !s_drain_stream_ready_sys)) && !(&s_perf_ram_stall_q)) begin
s_perf_ram_stall_d = s_perf_ram_stall_q + 1'b1;
end
end
end
always_comb begin
s_view_pending_d = s_view_pending_q;
s_view_data_d = s_view_data_q;
if (s_viewport_valid && !s_view_pending_q) begin
s_view_pending_d = 1'b1;
s_view_data_d = {s_viewport_write, s_viewport_addr, s_viewport_write_data};
end
if (s_view_pending_q && s_view_req_ready_sys) begin
s_view_pending_d = 1'b0;
end
end
always_comb begin
s_global_stat = 32'd0;
s_global_stat[0] = s_en;
s_global_stat[1] = s_scheduler_busy || s_dma_busy || s_transaction_busy_sys;
s_global_stat[3:2] = s_active_role_sys;
s_global_stat[4] = s_link_ready_sys;
s_global_stat[5] = s_dma_busy;
s_role_stat = 32'd0;
s_role_stat[1:0] = s_active_role_sys;
s_role_stat[2] = s_role_switch_pending;
s_role_stat[3] = s_id_ground_sys;
s_role_stat[5:4] = s_vbus_state_sys;
s_phy_stat = 32'd0;
s_phy_stat[1:0] = s_line_state_sys;
s_phy_stat[3:2] = s_vbus_state_sys;
s_phy_stat[4] = s_id_ground_sys;
s_phy_stat[5] = s_link_ready_sys;
s_frame_stat = {18'd0, s_frame_sys};
s_device_stat = 32'd0;
s_device_stat[0] = s_active_role_sys == usb2_pkg::Usb2RoleDevice;
s_device_stat[1] = s_phy_suspend;
s_device_stat[2] = s_remote_wake;
s_device_stat[31:16] = s_device_ctrl;
s_host_stat = 32'd0;
s_host_stat[0] = s_active_role_sys == usb2_pkg::Usb2RoleHost;
s_host_stat[1] = s_transaction_busy_sys;
s_host_stat[31:16] = s_host_ctrl;
s_port_stat = s_phy_stat;
s_schedule_stat = {18'd0, s_frame_sys};
s_schedule_stat[31] = s_schedule_ctrl;
s_ram_stat = 32'h4000_0000;
s_ram_stat[0] = s_transaction_busy_sys;
s_ecc_stat = 32'd0;
s_ecc_stat[0] = s_ecc_corrected_event;
s_ecc_stat[1] = s_ecc_uncorrectable_event;
s_debug_stat = 32'd0;
s_debug_stat[0] = s_scheduler_busy;
s_debug_stat[1] = s_dma_busy;
s_debug_stat[2] = s_transaction_busy_sys;
s_debug_stat[3] = s_role_reset;
s_debug_stat[4] = s_dma_descriptor_irq;
s_debug_stat[5] = s_dma_abort_done;
s_debug_stat[6] = s_ram_bist_start;
s_debug_stat[7] = 1'b0;
s_debug_stat[15:8] = s_test_ctrl;
s_debug_stat[31:16] = s_ram_ctrl;
s_irq_event = s_scheduler_irq_event;
s_irq_event[`APB4_USB2__IRQ_ROLE_CHANGE] |= s_role_change;
s_irq_event[`APB4_USB2__IRQ_SETUP] |= s_setup_valid_sys;
s_irq_event[`APB4_USB2__IRQ_ULPI_ERROR] |= s_protocol_event_sys;
s_irq_event[`APB4_USB2__IRQ_ECC_CORRECTED] |= s_ecc_corrected_event;
s_irq_event[`APB4_USB2__IRQ_ECC_UNCORRECTABLE] |= s_ecc_uncorrectable_event;
end
usb2_reg #(
.NumEndpoints(NumEndpoints),
.NumChannels (NumChannels)
) u_reg (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.apb4 (apb4),
.global_status_i (s_global_stat),
.role_status_i (s_role_stat),
.phy_status_i (s_phy_stat),
.frame_i (s_frame_stat),
.device_status_i (s_device_stat),
.setup_packet_i (s_setup_packet_q),
.endpoint_pending_in_i (s_endpoint_pending_in),
.endpoint_pending_out_i (s_endpoint_pending_out),
.endpoint_complete_in_i (s_endpoint_complete_in),
.endpoint_complete_out_i (s_endpoint_complete_out),
.endpoint_status_i (s_endpoint_stat),
.endpoint_bytes_in_i (s_endpoint_bytes_in),
.endpoint_bytes_out_i (s_endpoint_bytes_out),
.host_status_i (s_host_stat),
.port_status_i (s_port_stat),
.schedule_status_i (s_schedule_stat),
.channel_status_i (s_channel_stat),
.channel_bytes_i (s_channel_bytes),
.ram_status_i (s_ram_stat),
.ecc_status_i (s_ecc_stat),
.ecc_corrected_count_i (s_ecc_corrected_count_sys),
.ecc_uncorrectable_count_i (s_ecc_uncorrectable_count_sys),
.debug_status_i (s_debug_stat),
.perf_tx_bytes_i (s_perf_tx_bytes),
.perf_rx_bytes_i (s_perf_rx_bytes),
.perf_packets_i (s_perf_packets),
.perf_retries_i (s_perf_retries),
.perf_axi_stall_i (s_perf_axi_stall_q),
.perf_ram_stall_i (s_perf_ram_stall_q),
.perf_irq_count_i (s_perf_irq_count),
.irq_event_i (s_irq_event),
.error_capture_i (s_err_capture),
.error_status_i (s_err_stat),
.error_code_i (s_err_code),
.error_info_i (s_err_info),
.error_desc_addr_i (s_err_desc_addr),
.error_buffer_addr_i (s_err_buffer_addr),
.viewport_resp_valid_i (s_viewport_resp_valid_sys),
.viewport_read_data_i (s_viewport_resp_data_sys[7:0]),
.viewport_error_i (s_viewport_resp_data_sys[8]),
.enable_o (s_en),
.soft_reset_o (s_soft_reset),
.abort_o (s_abort),
.force_role_o (s_force_role),
.auto_role_o (s_auto_role),
.phy_reset_n_o (s_phy_reset_n_cfg),
.phy_suspend_o (s_phy_suspend),
.remote_wake_o (s_remote_wake),
.timeout_o (s_timeout),
.test_ctrl_o (s_test_ctrl),
.perf_clear_o (s_perf_clear),
.device_addr_o (s_device_addr),
.device_ctrl_o (s_device_ctrl),
.endpoint_cfg_o (s_endpoint_cfg),
.endpoint_ram_in_o (s_endpoint_ram_in),
.endpoint_ram_out_o (s_endpoint_ram_out),
.endpoint_desc_in_o (s_endpoint_desc_in),
.endpoint_desc_out_o (s_endpoint_desc_out),
.endpoint_command_o (s_endpoint_cmd),
.endpoint_complete_clear_in_o (s_endpoint_complete_clear_in),
.endpoint_complete_clear_out_o(s_endpoint_complete_clear_out),
.host_ctrl_o (s_host_ctrl),
.port_ctrl_o (s_port_ctrl),
.schedule_ctrl_o (s_schedule_ctrl),
.channel_cfg_o (s_channel_cfg),
.channel_target_o (s_channel_target),
.channel_interval_o (s_channel_interval),
.channel_ram_o (s_channel_ram),
.channel_desc_o (s_channel_desc),
.channel_command_o (s_channel_cmd),
.ram_ctrl_o (s_ram_ctrl),
.ram_bist_start_o (s_ram_bist_start),
.viewport_valid_o (s_viewport_valid),
.viewport_write_o (s_viewport_write),
.viewport_addr_o (s_viewport_addr),
.viewport_write_data_o (s_viewport_write_data),
.irq_o (irq_o)
);
usb2_role_controller u_role_controller (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (s_en),
.soft_reset_i (s_soft_reset),
.auto_role_i (s_auto_role),
.force_role_i (s_force_role),
.id_ground_i (s_id_ground_sys),
.vbus_valid_i (s_vbus_state_sys == 2'b11),
.transaction_busy_i(s_scheduler_busy || s_dma_busy || s_transaction_busy_sys),
.active_role_o (s_active_role_sys),
.role_change_o (s_role_change),
.role_reset_o (s_role_reset),
.switch_pending_o (s_role_switch_pending)
);
usb2_scheduler #(
.NumEndpoints(NumEndpoints),
.NumChannels (NumChannels)
) u_scheduler (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (s_en),
.abort_i (s_abort),
.reinitialize_i (s_soft_reset || s_role_reset),
.perf_clear_i (s_perf_clear),
.high_speed_i (!s_port_ctrl),
.frame_i (s_frame_sys),
.active_role_i (s_active_role_sys),
.endpoint_cfg_i (s_endpoint_cfg),
.endpoint_ram_in_i (s_endpoint_ram_in),
.endpoint_ram_out_i (s_endpoint_ram_out),
.endpoint_desc_in_i (s_endpoint_desc_in),
.endpoint_desc_out_i (s_endpoint_desc_out),
.endpoint_command_i (s_endpoint_cmd),
.endpoint_complete_clear_in_i (s_endpoint_complete_clear_in),
.endpoint_complete_clear_out_i(s_endpoint_complete_clear_out),
.channel_cfg_i (s_channel_cfg),
.channel_target_i (s_channel_target),
.channel_interval_i (s_channel_interval),
.channel_ram_i (s_channel_ram),
.channel_desc_i (s_channel_desc),
.channel_command_i (s_channel_cmd),
.work_valid_o (s_work_valid_sys),
.work_ready_i (s_work_ready_sys),
.work_data_o (s_work_data_sys),
.result_valid_i (s_result_valid_sys),
.result_ready_o (s_result_ready_sys),
.result_data_i (s_result_data_sys),
.fill_cmd_valid_o (s_fill_cmd_valid_sys),
.fill_cmd_ready_i (s_fill_cmd_ready_sys),
.fill_cmd_data_o (s_fill_cmd_data_sys),
.drain_cmd_valid_o (s_drain_cmd_valid_sys),
.drain_cmd_ready_i (s_drain_cmd_ready_sys),
.drain_cmd_data_o (s_drain_cmd_data_sys),
.buffer_event_valid_i (s_buffer_event_valid_sys),
.buffer_event_ready_o (s_buffer_event_ready_sys),
.buffer_event_data_i (s_buffer_event_data_sys),
.dma_start_o (s_dma_start),
.dma_abort_o (s_dma_abort),
.dma_memory_to_packet_o (s_dma_memory_to_packet),
.dma_desc_base_o (s_dma_desc_base),
.dma_transfer_bytes_o (s_dma_transfer_bytes),
.dma_busy_i (s_dma_busy),
.dma_done_i (s_dma_done),
.dma_error_i (s_dma_err),
.dma_error_code_i (s_dma_err_code),
.dma_current_desc_i (s_dma_current_desc),
.dma_bytes_done_i (s_dma_bytes_done),
.retry_i (s_retry_event_sys),
.endpoint_pending_in_o (s_endpoint_pending_in),
.endpoint_pending_out_o (s_endpoint_pending_out),
.endpoint_complete_in_o (s_endpoint_complete_in),
.endpoint_complete_out_o (s_endpoint_complete_out),
.endpoint_status_o (s_endpoint_stat),
.endpoint_bytes_in_o (s_endpoint_bytes_in),
.endpoint_bytes_out_o (s_endpoint_bytes_out),
.channel_status_o (s_channel_stat),
.channel_bytes_o (s_channel_bytes),
.irq_event_o (s_scheduler_irq_event),
.error_capture_o (s_err_capture),
.error_status_o (s_err_stat),
.error_code_o (s_err_code),
.error_info_o (s_err_info),
.error_desc_addr_o (s_err_desc_addr),
.error_buffer_addr_o (s_err_buffer_addr),
.perf_tx_bytes_o (s_perf_tx_bytes),
.perf_rx_bytes_o (s_perf_rx_bytes),
.perf_packets_o (s_perf_packets),
.perf_retries_o (s_perf_retries),
.perf_irq_count_o (s_perf_irq_count),
.busy_o (s_scheduler_busy)
);
usb2_dma u_dma (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (s_dma_start),
.abort_i (s_dma_abort),
.memory_to_packet_i(s_dma_memory_to_packet),
.allow_short_i (1'b1),
.desc_base_i (s_dma_desc_base),
.desc_limit_i (16'd256),
.transfer_bytes_i (s_dma_transfer_bytes),
.packet_in_valid_i (s_drain_stream_valid_sys),
.packet_in_ready_o (s_drain_stream_ready_sys),
.packet_in_data_i (s_drain_stream_data_sys[31:0]),
.packet_in_strb_i (s_drain_stream_data_sys[35:32]),
.packet_in_last_i (s_drain_stream_data_sys[36]),
.packet_out_valid_o(s_fill_stream_valid_sys),
.packet_out_ready_i(s_fill_stream_ready_sys),
.packet_out_data_o (s_fill_stream_data_sys[31:0]),
.packet_out_strb_o (s_fill_stream_data_sys[35:32]),
.packet_out_last_o (s_fill_stream_data_sys[36]),
.busy_o (s_dma_busy),
.done_o (s_dma_done),
.error_o (s_dma_err),
.error_code_o (s_dma_err_code),
.current_desc_o (s_dma_current_desc),
.next_desc_o (s_dma_next_desc),
.bytes_done_o (s_dma_bytes_done),
.frame_o (s_dma_frame),
.descriptor_irq_o (s_dma_descriptor_irq),
.abort_done_o (s_dma_abort_done),
.dma_axi4 (dma_axi4)
);
async_reqack #(
.DATA_WIDTH(64)
) u_work_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_valid_i(s_work_valid_sys),
.src_ready_o(s_work_ready_sys),
.src_data_i (s_work_data_sys),
.dst_clk_i (clk_ulpi_i),
.dst_rst_n_i(s_link_rst_n),
.dst_valid_o(s_work_valid_ulpi),
.dst_ready_i(s_work_ready_ulpi),
.dst_data_o (s_work_data_ulpi)
);
async_reqack #(
.DATA_WIDTH(64)
) u_result_cdc (
.src_clk_i (clk_ulpi_i),
.src_rst_n_i(s_link_rst_n),
.src_valid_i(s_result_valid_ulpi),
.src_ready_o(s_result_ready_ulpi),
.src_data_i (s_result_data_ulpi),
.dst_clk_i (clk_i),
.dst_rst_n_i(rst_n_i),
.dst_valid_o(s_result_valid_sys),
.dst_ready_i(s_result_ready_sys),
.dst_data_o (s_result_data_sys)
);
async_reqack #(
.DATA_WIDTH(64)
) u_setup_cdc (
.src_clk_i (clk_ulpi_i),
.src_rst_n_i(s_link_rst_n),
.src_valid_i(s_setup_valid_ulpi),
.src_ready_o(s_setup_ready_ulpi),
.src_data_i (s_setup_data_ulpi),
.dst_clk_i (clk_i),
.dst_rst_n_i(rst_n_i),
.dst_valid_o(s_setup_valid_sys),
.dst_ready_i(1'b1),
.dst_data_o (s_setup_data_sys)
);
async_reqack #(
.DATA_WIDTH(27)
) u_fill_command_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_valid_i(s_fill_cmd_valid_sys),
.src_ready_o(s_fill_cmd_ready_sys),
.src_data_i (s_fill_cmd_data_sys),
.dst_clk_i (clk_ulpi_i),
.dst_rst_n_i(s_link_rst_n),
.dst_valid_o(s_fill_cmd_valid_ulpi),
.dst_ready_i(s_fill_cmd_ready_ulpi),
.dst_data_o (s_fill_cmd_data_ulpi)
);
async_reqack #(
.DATA_WIDTH(27)
) u_drain_command_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_valid_i(s_drain_cmd_valid_sys),
.src_ready_o(s_drain_cmd_ready_sys),
.src_data_i (s_drain_cmd_data_sys),
.dst_clk_i (clk_ulpi_i),
.dst_rst_n_i(s_link_rst_n),
.dst_valid_o(s_drain_cmd_valid_ulpi),
.dst_ready_i(s_drain_cmd_ready_ulpi),
.dst_data_o (s_drain_cmd_data_ulpi)
);
async_reqack #(
.DATA_WIDTH(3)
) u_buffer_event_cdc (
.src_clk_i (clk_ulpi_i),
.src_rst_n_i(s_link_rst_n),
.src_valid_i(s_buffer_event_valid_ulpi),
.src_ready_o(s_buffer_event_ready_ulpi),
.src_data_i (s_buffer_event_data_ulpi),
.dst_clk_i (clk_i),
.dst_rst_n_i(rst_n_i),
.dst_valid_o(s_buffer_event_valid_sys),
.dst_ready_i(s_buffer_event_ready_sys),
.dst_data_o (s_buffer_event_data_sys)
);
async_reqack #(
.DATA_WIDTH(15)
) u_view_request_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_valid_i(s_view_req_valid_sys),
.src_ready_o(s_view_req_ready_sys),
.src_data_i (s_view_req_data_sys),
.dst_clk_i (clk_ulpi_i),
.dst_rst_n_i(s_link_rst_n),
.dst_valid_o(s_view_req_valid_ulpi),
.dst_ready_i(s_view_req_ready_ulpi),
.dst_data_o (s_view_req_data_ulpi)
);
async_reqack #(
.DATA_WIDTH(9)
) u_view_response_cdc (
.src_clk_i (clk_ulpi_i),
.src_rst_n_i(s_link_rst_n),
.src_valid_i(s_view_resp_valid_ulpi),
.src_ready_o(s_view_resp_ready_ulpi),
.src_data_i (s_view_resp_data_ulpi),
.dst_clk_i (clk_i),
.dst_rst_n_i(rst_n_i),
.dst_valid_o(s_viewport_resp_valid_sys),
.dst_ready_i(1'b1),
.dst_data_o (s_viewport_resp_data_sys)
);
cdc_fifo #(
.DATA_WIDTH (37),
.BUFFER_DEPTH(8)
) u_fill_stream_cdc (
.src_clk_i (clk_i),
.src_rst_n_i(rst_n_i),
.src_data_i (s_fill_stream_data_sys),
.src_valid_i(s_fill_stream_valid_sys),
.src_ready_o(s_fill_stream_ready_sys),
.dst_clk_i (clk_ulpi_i),
.dst_rst_n_i(s_link_rst_n),
.dst_data_o (s_fill_stream_data_ulpi),
.dst_valid_o(s_fill_stream_valid_ulpi),
.dst_ready_i(s_fill_stream_ready_ulpi)
);
cdc_fifo #(
.DATA_WIDTH (37),
.BUFFER_DEPTH(8)
) u_drain_stream_cdc (
.src_clk_i (clk_ulpi_i),
.src_rst_n_i(s_link_rst_n),
.src_data_i (s_drain_fifo_data_ulpi),
.src_valid_i(s_drain_fifo_valid_ulpi),
.src_ready_o(s_drain_fifo_ready_ulpi),
.dst_clk_i (clk_i),
.dst_rst_n_i(rst_n_i),
.dst_data_o (s_drain_stream_data_sys),
.dst_valid_o(s_drain_stream_valid_sys),
.dst_ready_i(s_drain_stream_ready_sys)
);
spill_register #(
.DATA_WIDTH(37)
) u_drain_stream_spill (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.flush_i(1'b0),
.valid_i(s_drain_stream_valid_ulpi),
.ready_o(s_drain_stream_ready_ulpi),
.data_i (s_drain_stream_data_ulpi),
.valid_o(s_drain_fifo_valid_ulpi),
.ready_i(s_drain_fifo_ready_ulpi),
.data_o (s_drain_fifo_data_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(2)
) u_role_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_active_role_sys),
.dat_o (s_active_role_ulpi_bits)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_role_reset_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_ulpi_rst_n),
.dat_i (s_role_reset),
.dat_o (s_role_reset_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_phy_reset_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_phy_reset_n_cfg && !s_role_reset),
.dat_o (s_phy_reset_n_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_high_speed_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (!s_port_ctrl),
.dat_o (s_high_speed_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_enable_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_en),
.dat_o (s_en_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(7)
) u_device_addr_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_device_addr),
.dat_o (s_device_addr_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(NumEndpoints * 32)
) u_endpoint_cfg_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_endpoint_cfg),
.dat_o (s_endpoint_cfg_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(32)
) u_timeout_sync (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_timeout),
.dat_o (s_timeout_ulpi)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(6)
) u_phy_status_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i ({s_id_ground_ulpi, s_vbus_state_ulpi, s_line_state_ulpi, s_link_ready_ulpi}),
.dat_o ({s_id_ground_sys, s_vbus_state_sys, s_line_state_sys, s_link_ready_sys})
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(1)
) u_transaction_busy_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_transaction_busy_ulpi),
.dat_o (s_transaction_busy_sys)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(14)
) u_frame_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frame_ulpi),
.dat_o (s_frame_sys)
);
cdc_sync #(
.STAGE (2),
.DATA_WIDTH(64)
) u_ecc_counts_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i ({s_ecc_uncorrectable_count_ulpi, s_ecc_corrected_count_ulpi}),
.dat_o ({s_ecc_uncorrectable_count_sys, s_ecc_corrected_count_sys})
);
edge_det #(
.STAGE(2)
) u_retry_event_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_retry_toggle_q),
.dat_o (s_retry_toggle_level_sys),
.re_o (s_retry_re_sys),
.fe_o (s_retry_fe_sys)
);
edge_det #(
.STAGE(2)
) u_protocol_event_sync (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_protocol_toggle_q),
.dat_o (s_protocol_toggle_level_sys),
.re_o (s_protocol_re_sys),
.fe_o (s_protocol_fe_sys)
);
usb2_link_domain #(
.NumEndpoints(NumEndpoints)
) u_link_domain (
.clk_i (clk_ulpi_i),
.rst_n_i (s_link_rst_n),
.enable_i (s_en_ulpi),
.phy_reset_n_i (s_phy_reset_n_ulpi),
.high_speed_i (s_high_speed_ulpi),
.active_role_i (s_active_role_ulpi),
.device_addr_i (s_device_addr_ulpi),
.endpoint_cfg_i (s_endpoint_cfg_ulpi),
.timeout_i (s_timeout_ulpi),
.work_valid_i (s_work_valid_ulpi),
.work_ready_o (s_work_ready_ulpi),
.work_data_i (s_work_data_ulpi),
.result_valid_o (s_result_valid_ulpi),
.result_ready_i (s_result_ready_ulpi),
.result_data_o (s_result_data_ulpi),
.setup_valid_o (s_setup_valid_ulpi),
.setup_ready_i (s_setup_ready_ulpi),
.setup_data_o (s_setup_data_ulpi),
.fill_cmd_valid_i (s_fill_cmd_valid_ulpi),
.fill_cmd_ready_o (s_fill_cmd_ready_ulpi),
.fill_cmd_data_i (s_fill_cmd_data_ulpi),
.fill_stream_valid_i (s_fill_stream_valid_ulpi),
.fill_stream_ready_o (s_fill_stream_ready_ulpi),
.fill_stream_data_i (s_fill_stream_data_ulpi),
.drain_cmd_valid_i (s_drain_cmd_valid_ulpi),
.drain_cmd_ready_o (s_drain_cmd_ready_ulpi),
.drain_cmd_data_i (s_drain_cmd_data_ulpi),
.drain_stream_valid_o (s_drain_stream_valid_ulpi),
.drain_stream_ready_i (s_drain_stream_ready_ulpi),
.drain_stream_data_o (s_drain_stream_data_ulpi),
.buffer_event_valid_o (s_buffer_event_valid_ulpi),
.buffer_event_ready_i (s_buffer_event_ready_ulpi),
.buffer_event_data_o (s_buffer_event_data_ulpi),
.viewport_valid_i (s_view_req_valid_ulpi),
.viewport_ready_o (s_view_req_ready_ulpi),
.viewport_data_i (s_view_req_data_ulpi),
.viewport_resp_valid_o (s_view_resp_valid_ulpi),
.viewport_resp_ready_i (s_view_resp_ready_ulpi),
.viewport_resp_data_o (s_view_resp_data_ulpi),
.line_state_o (s_line_state_ulpi),
.vbus_state_o (s_vbus_state_ulpi),
.id_ground_o (s_id_ground_ulpi),
.link_ready_o (s_link_ready_ulpi),
.frame_o (s_frame_ulpi),
.transaction_busy_o (s_transaction_busy_ulpi),
.retry_o (s_retry_ulpi),
.protocol_error_o (s_protocol_err_ulpi),
.ecc_corrected_count_o (s_ecc_corrected_count_ulpi),
.ecc_uncorrectable_count_o(s_ecc_uncorrectable_count_ulpi),
.ulpi (ulpi)
);
dffr #(
.DATA_WIDTH(64)
) u_setup_packet_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_setup_packet_d),
.dat_o (s_setup_packet_q)
);
dffr #(
.DATA_WIDTH(1)
) u_retry_toggle_dffr (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_retry_toggle_d),
.dat_o (s_retry_toggle_q)
);
dffr #(
.DATA_WIDTH(1)
) u_protocol_toggle_dffr (
.clk_i (clk_ulpi_i),
.rst_n_i(s_link_rst_n),
.dat_i (s_protocol_toggle_d),
.dat_o (s_protocol_toggle_q)
);
dffr #(
.DATA_WIDTH(32)
) u_ecc_corrected_previous_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ecc_corrected_previous_d),
.dat_o (s_ecc_corrected_previous_q)
);
dffr #(
.DATA_WIDTH(32)
) u_ecc_uncorrectable_previous_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ecc_uncorrectable_previous_d),
.dat_o (s_ecc_uncorrectable_previous_q)
);
dffr #(
.DATA_WIDTH(1)
) u_view_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_pending_d),
.dat_o (s_view_pending_q)
);
dffr #(
.DATA_WIDTH(15)
) u_view_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_data_d),
.dat_o (s_view_data_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_axi_stall_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_axi_stall_d),
.dat_o (s_perf_axi_stall_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_ram_stall_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_ram_stall_d),
.dat_o (s_perf_ram_stall_q)
);
endmodule
+167
View File
@@ -0,0 +1,167 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`ifndef RETROSOC_USB2_DEFINE_SVH
`define RETROSOC_USB2_DEFINE_SVH
// verilog_format: off -- preserve the reviewed APB ABI column alignment
`define APB4_USB2__IP_ID 12'h000
`define APB4_USB2__IP_VERSION 12'h004
`define APB4_USB2__CAPABILITY0 12'h008
`define APB4_USB2__CAPABILITY1 12'h00C
`define APB4_USB2__GLOBAL_CTRL 12'h010
`define APB4_USB2__GLOBAL_STATUS 12'h014
`define APB4_USB2__ROLE_CTRL 12'h018
`define APB4_USB2__ROLE_STATUS 12'h01C
`define APB4_USB2__PHY_CTRL 12'h020
`define APB4_USB2__PHY_STATUS 12'h024
`define APB4_USB2__ULPI_VIEWPORT 12'h028
`define APB4_USB2__ULPI_VIEWPORT_DATA 12'h02C
`define APB4_USB2__TIMEOUT 12'h030
`define APB4_USB2__FRAME 12'h034
`define APB4_USB2__TEST_CTRL 12'h038
`define APB4_USB2__PIO_DATA 12'h03C
`define APB4_USB2__IRQ_STATUS 12'h040
`define APB4_USB2__IRQ_ENABLE 12'h044
`define APB4_USB2__IRQ_TEST 12'h048
`define APB4_USB2__ERROR_STATUS 12'h04C
`define APB4_USB2__ERROR_CODE 12'h050
`define APB4_USB2__ERROR_INFO 12'h054
`define APB4_USB2__ERROR_DESC_ADDR 12'h058
`define APB4_USB2__ERROR_BUFFER_ADDR 12'h05C
`define APB4_USB2__PERF_TX_BYTES 12'h060
`define APB4_USB2__PERF_RX_BYTES 12'h064
`define APB4_USB2__PERF_PACKETS 12'h068
`define APB4_USB2__PERF_RETRIES 12'h06C
`define APB4_USB2__PERF_AXI_STALL 12'h070
`define APB4_USB2__PERF_RAM_STALL 12'h074
`define APB4_USB2__PERF_IRQ_COUNT 12'h078
`define APB4_USB2__PERF_CTRL 12'h07C
`define APB4_USB2__DEVICE_CTRL 12'h100
`define APB4_USB2__DEVICE_ADDR 12'h104
`define APB4_USB2__DEVICE_STATUS 12'h108
`define APB4_USB2__SETUP0 12'h10C
`define APB4_USB2__SETUP1 12'h110
`define APB4_USB2__ENDPOINT_PENDING_IN 12'h114
`define APB4_USB2__ENDPOINT_PENDING_OUT 12'h118
`define APB4_USB2__ENDPOINT_COMPLETE_IN 12'h11C
`define APB4_USB2__ENDPOINT_COMPLETE_OUT 12'h120
`define APB4_USB2__ENDPOINT_BASE 12'h200
`define APB4_USB2__ENDPOINT_STRIDE 12'h040
`define APB4_USB2__ENDPOINT_CFG 12'h000
`define APB4_USB2__ENDPOINT_RAM_IN 12'h004
`define APB4_USB2__ENDPOINT_RAM_OUT 12'h008
`define APB4_USB2__ENDPOINT_DESC_IN 12'h00C
`define APB4_USB2__ENDPOINT_DESC_OUT 12'h010
`define APB4_USB2__ENDPOINT_COMMAND 12'h014
`define APB4_USB2__ENDPOINT_STATUS 12'h018
`define APB4_USB2__ENDPOINT_BYTES_IN 12'h01C
`define APB4_USB2__ENDPOINT_BYTES_OUT 12'h020
`define APB4_USB2__HOST_CTRL 12'h400
`define APB4_USB2__HOST_STATUS 12'h404
`define APB4_USB2__PORT_CTRL 12'h408
`define APB4_USB2__PORT_STATUS 12'h40C
`define APB4_USB2__SCHEDULE_CTRL 12'h410
`define APB4_USB2__SCHEDULE_STATUS 12'h414
`define APB4_USB2__CHANNEL_BASE 12'h500
`define APB4_USB2__CHANNEL_STRIDE 12'h040
`define APB4_USB2__CHANNEL_CFG 12'h000
`define APB4_USB2__CHANNEL_TARGET 12'h004
`define APB4_USB2__CHANNEL_INTERVAL 12'h008
`define APB4_USB2__CHANNEL_RAM 12'h00C
`define APB4_USB2__CHANNEL_DESC 12'h010
`define APB4_USB2__CHANNEL_COMMAND 12'h014
`define APB4_USB2__CHANNEL_STATUS 12'h018
`define APB4_USB2__CHANNEL_BYTES 12'h01C
`define APB4_USB2__RAM_CTRL 12'h900
`define APB4_USB2__RAM_STATUS 12'h904
`define APB4_USB2__ECC_STATUS 12'h908
`define APB4_USB2__ECC_CORRECTED_COUNT 12'h90C
`define APB4_USB2__ECC_UNCORRECTABLE_COUNT 12'h910
`define APB4_USB2__RAM_BIST 12'h914
`define APB4_USB2__DEBUG_STATUS 12'h918
`define APB4_USB2__GLOBAL_CTRL_ENABLE 0
`define APB4_USB2__GLOBAL_CTRL_SOFT_RESET 1
`define APB4_USB2__GLOBAL_CTRL_ABORT 2
`define APB4_USB2__GLOBAL_CTRL_IRQ_ENABLE 3
`define APB4_USB2__ROLE_CTRL_FORCE_LSB 0
`define APB4_USB2__ROLE_CTRL_AUTO 2
`define APB4_USB2__ROLE_IDLE 0
`define APB4_USB2__ROLE_DEVICE 1
`define APB4_USB2__ROLE_HOST 2
`define APB4_USB2__PHY_CTRL_RESET_N 0
`define APB4_USB2__PHY_CTRL_SUSPEND 1
`define APB4_USB2__PHY_CTRL_REMOTE_WAKE 2
`define APB4_USB2__PHY_CTRL_VIEWPORT_START 31
`define APB4_USB2__PHY_CTRL_VIEWPORT_WRITE 30
`define APB4_USB2__PHY_CTRL_VIEWPORT_ADDR_LSB 16
`define APB4_USB2__PHY_CTRL_VIEWPORT_DATA_LSB 0
`define APB4_USB2__IRQ_ROLE_CHANGE 0
`define APB4_USB2__IRQ_PORT_CHANGE 1
`define APB4_USB2__IRQ_BUS_RESET 2
`define APB4_USB2__IRQ_SUSPEND 3
`define APB4_USB2__IRQ_RESUME 4
`define APB4_USB2__IRQ_SETUP 5
`define APB4_USB2__IRQ_ENDPOINT 6
`define APB4_USB2__IRQ_CHANNEL 7
`define APB4_USB2__IRQ_DMA_DONE 8
`define APB4_USB2__IRQ_DMA_ERROR 9
`define APB4_USB2__IRQ_PHY_ERROR 10
`define APB4_USB2__IRQ_ULPI_ERROR 11
`define APB4_USB2__IRQ_ECC_CORRECTED 12
`define APB4_USB2__IRQ_ECC_UNCORRECTABLE 13
`define APB4_USB2__IRQ_TIMEOUT 14
`define APB4_USB2__IRQ_FATAL 15
`define APB4_USB2__ENDPOINT_CFG_IN_ENABLE 0
`define APB4_USB2__ENDPOINT_CFG_OUT_ENABLE 1
`define APB4_USB2__ENDPOINT_CFG_IN_STALL 2
`define APB4_USB2__ENDPOINT_CFG_OUT_STALL 3
`define APB4_USB2__ENDPOINT_CFG_TYPE_LSB 4
`define APB4_USB2__ENDPOINT_CFG_MAX_PACKET_LSB 16
`define APB4_USB2__ENDPOINT_COMMAND_PRIME_IN 0
`define APB4_USB2__ENDPOINT_COMMAND_ARM_OUT 1
`define APB4_USB2__ENDPOINT_COMMAND_CANCEL 2
`define APB4_USB2__ENDPOINT_COMMAND_RESET_TOGGLE 3
`define APB4_USB2__CHANNEL_CFG_ENABLE 0
`define APB4_USB2__CHANNEL_CFG_DIRECTION_IN 1
`define APB4_USB2__CHANNEL_CFG_TYPE_LSB 2
`define APB4_USB2__CHANNEL_CFG_LOW_SPEED 4
`define APB4_USB2__CHANNEL_CFG_SETUP 5
`define APB4_USB2__CHANNEL_CFG_PING_ENABLE 6
`define APB4_USB2__CHANNEL_CFG_MAX_PACKET_LSB 16
`define APB4_USB2__CHANNEL_TARGET_ADDR_LSB 0
`define APB4_USB2__CHANNEL_TARGET_ENDPOINT_LSB 8
`define APB4_USB2__CHANNEL_TARGET_TOGGLE 12
`define APB4_USB2__CHANNEL_COMMAND_START 0
`define APB4_USB2__CHANNEL_COMMAND_CANCEL 1
`define APB4_USB2__RAM_REGION_BASE_LSB 2
`define APB4_USB2__RAM_REGION_LENGTH_LSB 16
`define APB4_USB2__DESC_OWN 0
`define APB4_USB2__DESC_CHAIN 1
`define APB4_USB2__DESC_END 2
`define APB4_USB2__DESC_IRQ 3
`define APB4_USB2__DESC_SHORT_OK 4
`define APB4_USB2__DESC_ZERO_PACKET 5
`define APB4_USB2__DESC_DONE 16
`define APB4_USB2__DESC_SHORT 17
`define APB4_USB2__DESC_STALL 18
`define APB4_USB2__DESC_TIMEOUT 19
`define APB4_USB2__DESC_CRC_ERROR 20
`define APB4_USB2__DESC_PROTOCOL_ERROR 21
`define APB4_USB2__DESC_AXI_ERROR 22
`define APB4_USB2__DESC_ABORTED 23
// verilog_format: on
`endif
+772
View File
@@ -0,0 +1,772 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "axi4_define.svh"
module usb2_dma #(
parameter int AddrWidth = 32,
parameter int DataWidth = 32,
parameter int MaxDescriptors = 256
) (
// verilog_format: off -- preserve command, packet stream, and completion groups
input logic clk_i,
input logic rst_n_i,
input logic start_i,
input logic abort_i,
input logic memory_to_packet_i,
input logic allow_short_i,
input logic [AddrWidth-1:0] desc_base_i,
input logic [15:0] desc_limit_i,
input logic [31:0] transfer_bytes_i,
input logic packet_in_valid_i,
output logic packet_in_ready_o,
input logic [DataWidth-1:0] packet_in_data_i,
input logic [DataWidth/8-1:0] packet_in_strb_i,
input logic packet_in_last_i,
output logic packet_out_valid_o,
input logic packet_out_ready_i,
output logic [DataWidth-1:0] packet_out_data_o,
output logic [DataWidth/8-1:0] packet_out_strb_o,
output logic packet_out_last_o,
output logic busy_o,
output logic done_o,
output logic error_o,
output logic [7:0] error_code_o,
output logic [AddrWidth-1:0] current_desc_o,
output logic [AddrWidth-1:0] next_desc_o,
output logic [31:0] bytes_done_o,
output logic [31:0] frame_o,
output logic descriptor_irq_o,
output logic abort_done_o,
axi4_if.master dma_axi4
// verilog_format: on
);
localparam int BeatBytes = DataWidth / 8;
localparam int DescIndexWidth = $clog2(MaxDescriptors);
localparam logic [7:0] ErrorDescriptor = 8'd1;
localparam logic [7:0] ErrorLength = 8'd2;
localparam logic [7:0] ErrorAxiRead = 8'd3;
localparam logic [7:0] ErrorAxiWrite = 8'd4;
localparam logic [7:0] ErrorPacket = 8'd5;
localparam logic [7:0] ErrorAbort = 8'd6;
localparam logic [7:0] ErrorWriteback = 8'd7;
typedef enum logic [3:0] {
Idle,
FetchStart,
FetchWait,
Validate,
PayloadReadStart,
PayloadReadWait,
PayloadWriteStart,
PayloadWriteWait,
WritebackStart,
WritebackWait,
OwnershipStart,
OwnershipWait,
Complete,
Terminal
} dma_state_e;
dma_state_e s_state_d, s_state_q;
logic [3:0] s_state_bits_q;
logic [AddrWidth-1:0] s_desc_addr_d, s_desc_addr_q;
logic [DescIndexWidth-1:0] s_desc_index_d, s_desc_index_q;
logic [15:0] s_desc_limit_d, s_desc_limit_q;
logic s_memory_to_packet_d, s_memory_to_packet_q;
logic s_allow_short_d, s_allow_short_q;
logic s_unlimited_d, s_unlimited_q;
logic [31:0] s_remaining_d, s_remaining_q;
logic [31:0] s_total_done_d, s_total_done_q;
logic [7:0][31:0] s_desc_words_d, s_desc_words_q;
logic [3:0] s_fetch_index_d, s_fetch_index_q;
logic [31:0] s_selected_bytes_d, s_selected_bytes_q;
logic [31:0] s_desc_progress_d, s_desc_progress_q;
logic [4:0] s_burst_beats_d, s_burst_beats_q;
logic [4:0] s_burst_index_d, s_burst_index_q;
logic [2:0] s_writeback_index_d, s_writeback_index_q;
logic s_fault_d, s_fault_q;
logic [7:0] s_err_code_d, s_err_code_q;
logic s_abort_pending_d, s_abort_pending_q;
logic s_done_d, s_done_q;
logic s_err_d, s_err_q;
logic s_descriptor_irq_d, s_descriptor_irq_q;
logic s_abort_done_d, s_abort_done_q;
logic s_desc_own;
logic s_desc_chain;
logic s_desc_end;
logic s_desc_irq;
logic s_desc_short_ok;
logic s_desc_zero_packet;
logic s_desc_valid;
usb2_pkg::usb2_desc_error_e s_desc_err;
logic [ 31:0] s_desc_buffer;
logic [ 31:0] s_desc_len;
logic [ 31:0] s_desc_next;
logic [ 31:0] s_desc_control;
logic [ 31:0] s_desc_frame;
logic [ 31:0] s_payload_addr;
logic [ 31:0] s_payload_left;
logic [ 4:0] s_payload_beats;
logic [ 2:0] s_payload_beat_bytes;
logic [ 3:0] s_payload_expected_strb;
logic [ 31:0] s_progress_after_beat;
logic [ 31:0] s_remaining_after_desc;
logic s_transfer_last;
logic s_descriptor_short;
logic [ 31:0] s_completion_stat;
logic [ 31:0] s_control_released;
logic s_packet_input_valid;
logic s_packet_input_fault;
logic s_read_start_valid;
logic s_read_start_ready;
logic [ AddrWidth-1:0] s_read_addr;
logic [ 4:0] s_read_beats;
logic s_read_beat_valid;
logic s_read_beat_ready;
logic [ DataWidth-1:0] s_read_data;
logic [ 1:0] s_read_resp;
logic s_read_last;
logic s_read_expected_last;
logic s_read_id_err;
logic s_read_done;
logic s_read_busy;
logic s_write_start_valid;
logic s_write_start_ready;
logic [ AddrWidth-1:0] s_write_addr;
logic [ 4:0] s_write_beats;
logic s_write_data_valid;
logic s_write_data_ready;
logic [ DataWidth-1:0] s_write_data;
logic [DataWidth/8-1:0] s_write_strb;
logic s_write_done;
logic [ 1:0] s_write_resp;
logic s_write_id_err;
logic s_write_busy;
logic s_axi_read_fault;
logic s_axi_write_fault;
function automatic logic [4:0] choose_burst(input logic [11:0] addr_low_i,
input logic [31:0] bytes_i);
logic [31:0] beats_needed;
logic [31:0] boundary_beats;
logic [31:0] selected;
begin
beats_needed = (bytes_i + (BeatBytes - 1)) / BeatBytes;
boundary_beats = (32'd4096 - {20'd0, addr_low_i}) / BeatBytes;
selected = beats_needed;
if (selected > 32'd16) selected = 32'd16;
if (selected > boundary_beats) selected = boundary_beats;
if (selected == 32'd0) selected = 32'd1;
return selected[4:0];
end
endfunction
function automatic logic [2:0] beat_bytes(input logic [31:0] bytes_i);
return (bytes_i >= BeatBytes) ? 3'(BeatBytes) : bytes_i[2:0];
endfunction
function automatic logic [3:0] byte_strobe(input logic [2:0] bytes_i);
unique case (bytes_i)
3'd0: return 4'b0000;
3'd1: return 4'b0001;
3'd2: return 4'b0011;
3'd3: return 4'b0111;
default: return 4'b1111;
endcase
endfunction
assign s_state_q = dma_state_e'(s_state_bits_q);
assign s_desc_buffer = s_desc_words_q[usb2_pkg::USB2_DESC_BUFFER_WORD];
assign s_desc_len = s_desc_words_q[usb2_pkg::USB2_DESC_LENGTH_WORD];
assign s_desc_next = s_desc_words_q[usb2_pkg::USB2_DESC_NEXT_WORD];
assign s_desc_control = s_desc_words_q[usb2_pkg::USB2_DESC_CONTROL_WORD];
assign s_desc_frame = s_desc_words_q[usb2_pkg::USB2_DESC_FRAME_WORD];
assign s_payload_addr = s_desc_buffer + s_desc_progress_q;
assign s_payload_left = s_selected_bytes_q - s_desc_progress_q;
assign s_payload_beats = choose_burst(s_payload_addr[11:0], s_payload_left);
assign s_payload_beat_bytes = beat_bytes(s_payload_left);
assign s_payload_expected_strb = byte_strobe(s_payload_beat_bytes);
assign s_progress_after_beat = s_desc_progress_q + {29'd0, s_payload_beat_bytes};
assign s_remaining_after_desc = s_remaining_q - s_selected_bytes_q;
assign s_transfer_last = s_unlimited_q ? s_desc_end : (s_remaining_q <= s_selected_bytes_q);
assign s_descriptor_short = s_selected_bytes_q < s_desc_len;
assign s_control_released = s_desc_control & 32'hFFFF_FFFE;
always_comb begin
s_completion_stat = 32'd0;
s_completion_stat[usb2_pkg::USB2_DESC_DONE] = 1'b1;
s_completion_stat[usb2_pkg::USB2_DESC_SHORT] = s_descriptor_short;
if (s_fault_q) begin
unique case (s_err_code_q)
ErrorAxiRead, ErrorAxiWrite, ErrorWriteback:
s_completion_stat[usb2_pkg::USB2_DESC_AXI_ERROR] = 1'b1;
ErrorAbort: s_completion_stat[usb2_pkg::USB2_DESC_ABORTED] = 1'b1;
default: s_completion_stat[usb2_pkg::USB2_DESC_PROTOCOL_ERROR] = 1'b1;
endcase
end
end
usb2_dma_descriptor #(
.MaxDescriptors(MaxDescriptors)
) u_descriptor (
.buffer_addr_i (s_desc_buffer),
.byte_length_i (s_desc_len),
.next_addr_i (s_desc_next),
.control_i (s_desc_control),
.actual_length_i(s_desc_words_q[usb2_pkg::USB2_DESC_ACTUAL_WORD]),
.status_i (s_desc_words_q[usb2_pkg::USB2_DESC_STATUS_WORD]),
.frame_i (s_desc_frame),
.reserved_i (s_desc_words_q[usb2_pkg::USB2_DESC_RESERVED_WORD]),
.desc_index_i (s_desc_index_q),
.own_o (s_desc_own),
.chain_o (s_desc_chain),
.end_o (s_desc_end),
.irq_o (s_desc_irq),
.short_ok_o (s_desc_short_ok),
.zero_packet_o (s_desc_zero_packet),
.valid_o (s_desc_valid),
.error_o (s_desc_err)
);
assign s_read_start_valid = !abort_i &&
((s_state_q == FetchStart) || (s_state_q == PayloadReadStart));
assign s_read_addr = (s_state_q == FetchStart) ? s_desc_addr_q : s_payload_addr;
assign s_read_beats = (s_state_q == FetchStart) ? 5'd8 : s_payload_beats;
assign s_axi_read_fault = (s_read_resp != `AXI4_RESP_OKAY) || s_read_id_err ||
(s_read_last != s_read_expected_last);
assign s_read_beat_ready = (s_state_q == FetchWait) ||
((s_state_q == PayloadReadWait) &&
(s_fault_q || s_abort_pending_q || packet_out_ready_i));
assign packet_out_valid_o = (s_state_q == PayloadReadWait) && s_read_beat_valid &&
!s_fault_q && !s_abort_pending_q && !s_axi_read_fault;
assign packet_out_data_o = s_read_data;
assign packet_out_strb_o = s_payload_expected_strb;
assign packet_out_last_o = (s_progress_after_beat >= s_selected_bytes_q) && s_transfer_last;
assign s_write_start_valid = (s_state_q == PayloadWriteStart) ||
(s_state_q == WritebackStart) ||
(s_state_q == OwnershipStart);
assign s_write_addr = (s_state_q == PayloadWriteStart) ? s_payload_addr :
((s_state_q == WritebackStart) ? (s_desc_addr_q + 32'd16) :
(s_desc_addr_q + 32'd12));
assign s_write_beats = (s_state_q == WritebackStart) ? 5'd2 :
((s_state_q == OwnershipStart) ? 5'd1 : s_payload_beats);
assign s_packet_input_valid = (s_state_q == PayloadWriteWait) && packet_in_valid_i;
assign s_packet_input_fault = s_packet_input_valid &&
((packet_in_strb_i != s_payload_expected_strb) ||
(packet_in_last_i !=
((s_progress_after_beat >= s_selected_bytes_q) &&
s_transfer_last)));
assign packet_in_ready_o = (s_state_q == PayloadWriteWait) && !s_fault_q &&
!s_abort_pending_q && s_write_data_ready;
assign s_write_data_valid = (s_state_q == PayloadWriteWait) ?
(packet_in_valid_i || s_fault_q || s_abort_pending_q) :
((s_state_q == WritebackWait) || (s_state_q == OwnershipWait));
assign s_write_data = (s_state_q == PayloadWriteWait) ?
((s_fault_q || s_abort_pending_q) ? '0 : packet_in_data_i) :
((s_state_q == WritebackWait) ?
((s_writeback_index_q == 3'd0) ? s_desc_progress_q :
s_completion_stat) :
s_control_released);
assign s_write_strb = ((s_state_q == PayloadWriteWait) &&
(s_fault_q || s_abort_pending_q)) ? '0 :
((s_state_q == PayloadWriteWait) ? packet_in_strb_i : '1);
assign s_axi_write_fault = (s_write_resp != `AXI4_RESP_OKAY) || s_write_id_err;
assign busy_o = ((s_state_q != Idle) && (s_state_q != Terminal)) || s_read_busy || s_write_busy;
assign done_o = s_done_q;
assign error_o = s_err_q;
assign error_code_o = s_err_code_q;
assign current_desc_o = s_desc_addr_q;
assign next_desc_o = s_desc_next;
assign bytes_done_o = s_total_done_q;
assign frame_o = s_desc_frame;
assign descriptor_irq_o = s_descriptor_irq_q;
assign abort_done_o = s_abort_done_q;
dma_axi4_master #(
.AddrWidth (AddrWidth),
.DataWidth (DataWidth),
.MaxBurstBeats(16)
) u_axi4_master (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.read_start_valid_i (s_read_start_valid),
.read_start_ready_o (s_read_start_ready),
.read_addr_i (s_read_addr),
.read_beats_i (s_read_beats),
.read_fixed_i (1'b0),
.read_busy_o (s_read_busy),
.read_beat_valid_o (s_read_beat_valid),
.read_beat_ready_i (s_read_beat_ready),
.read_data_o (s_read_data),
.read_resp_o (s_read_resp),
.read_last_o (s_read_last),
.read_expected_last_o(s_read_expected_last),
.read_id_error_o (s_read_id_err),
.read_done_o (s_read_done),
.write_start_valid_i (s_write_start_valid),
.write_start_ready_o (s_write_start_ready),
.write_addr_i (s_write_addr),
.write_beats_i (s_write_beats),
.write_fixed_i (1'b0),
.write_busy_o (s_write_busy),
.write_data_valid_i (s_write_data_valid),
.write_data_ready_o (s_write_data_ready),
.write_data_i (s_write_data),
.write_strb_i (s_write_strb),
.write_done_o (s_write_done),
.write_resp_o (s_write_resp),
.write_id_error_o (s_write_id_err),
.axi4 (dma_axi4)
);
always_comb begin
s_state_d = s_state_q;
s_desc_addr_d = s_desc_addr_q;
s_desc_index_d = s_desc_index_q;
s_desc_limit_d = s_desc_limit_q;
s_memory_to_packet_d = s_memory_to_packet_q;
s_allow_short_d = s_allow_short_q;
s_unlimited_d = s_unlimited_q;
s_remaining_d = s_remaining_q;
s_total_done_d = s_total_done_q;
s_desc_words_d = s_desc_words_q;
s_fetch_index_d = s_fetch_index_q;
s_selected_bytes_d = s_selected_bytes_q;
s_desc_progress_d = s_desc_progress_q;
s_burst_beats_d = s_burst_beats_q;
s_burst_index_d = s_burst_index_q;
s_writeback_index_d = s_writeback_index_q;
s_fault_d = s_fault_q;
s_err_code_d = s_err_code_q;
s_abort_pending_d = s_abort_pending_q || abort_i;
s_done_d = 1'b0;
s_err_d = s_err_q;
s_descriptor_irq_d = 1'b0;
s_abort_done_d = 1'b0;
unique case (s_state_q)
Idle: begin
s_abort_pending_d = 1'b0;
if (start_i) begin
s_desc_addr_d = desc_base_i;
s_desc_index_d = '0;
s_desc_limit_d = desc_limit_i;
s_memory_to_packet_d = memory_to_packet_i;
s_allow_short_d = allow_short_i;
s_unlimited_d = transfer_bytes_i == 32'd0;
s_remaining_d = transfer_bytes_i;
s_total_done_d = 32'd0;
s_desc_words_d = '0;
s_fetch_index_d = '0;
s_desc_progress_d = 32'd0;
s_fault_d = 1'b0;
s_err_code_d = 8'd0;
s_err_d = 1'b0;
if ((desc_base_i[4:0] != 5'd0) || (desc_base_i[11:0] > 12'hFE0) ||
(desc_limit_i == 16'd0) || (desc_limit_i > 16'(MaxDescriptors))) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorDescriptor;
s_state_d = Terminal;
end else begin
s_state_d = FetchStart;
end
end
end
FetchStart: begin
if (abort_i) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorAbort;
s_state_d = Terminal;
end else if (s_read_start_ready) begin
s_fetch_index_d = '0;
s_state_d = FetchWait;
end
end
FetchWait: begin
if (s_read_beat_valid && s_read_beat_ready) begin
if (s_axi_read_fault) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorAxiRead;
end else begin
s_desc_words_d[s_fetch_index_q] = s_read_data;
end
s_fetch_index_d = s_fetch_index_q + 1'b1;
if (s_read_done) begin
if (s_fault_q || s_axi_read_fault || abort_i) begin
s_fault_d = 1'b1;
s_err_code_d = abort_i ? ErrorAbort : ErrorAxiRead;
s_state_d = Terminal;
end else begin
s_state_d = Validate;
end
end
end
end
Validate: begin
s_desc_progress_d = 32'd0;
if (!s_desc_valid || (s_desc_err != usb2_pkg::Usb2DescOk) || !s_desc_own ||
(s_desc_zero_packet && !s_memory_to_packet_q)) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorDescriptor;
s_state_d = Terminal;
end else if (!s_unlimited_q && (s_remaining_q > s_desc_len) && s_desc_end) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorLength;
s_state_d = WritebackStart;
end else begin
if (!s_unlimited_q && (s_remaining_q < s_desc_len)) begin
s_selected_bytes_d = s_remaining_q;
end else begin
s_selected_bytes_d = s_desc_len;
end
if (!s_unlimited_q && (s_remaining_q < s_desc_len) &&
!(s_allow_short_q || s_desc_short_ok)) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorLength;
s_state_d = WritebackStart;
end else if (s_memory_to_packet_q) begin
s_state_d = PayloadReadStart;
end else begin
s_state_d = PayloadWriteStart;
end
end
end
PayloadReadStart: begin
if (s_read_start_ready) begin
s_burst_beats_d = s_payload_beats;
s_burst_index_d = '0;
s_state_d = PayloadReadWait;
end
end
PayloadReadWait: begin
if (s_read_beat_valid && s_read_beat_ready) begin
s_burst_index_d = s_burst_index_q + 1'b1;
if (s_axi_read_fault) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorAxiRead;
end else if (!s_fault_q && !s_abort_pending_q) begin
s_desc_progress_d = s_progress_after_beat;
s_total_done_d = s_total_done_q + {29'd0, s_payload_beat_bytes};
end
if (s_read_done) begin
if (s_fault_q || s_axi_read_fault || s_abort_pending_q || abort_i) begin
s_fault_d = 1'b1;
s_err_code_d = (s_abort_pending_q || abort_i) ? ErrorAbort : ErrorAxiRead;
s_state_d = WritebackStart;
end else if (s_progress_after_beat >= s_selected_bytes_q) begin
s_state_d = WritebackStart;
end else begin
s_state_d = PayloadReadStart;
end
end
end
end
PayloadWriteStart: begin
if (s_write_start_ready) begin
s_burst_beats_d = s_payload_beats;
s_burst_index_d = '0;
s_state_d = PayloadWriteWait;
end
end
PayloadWriteWait: begin
if (s_write_data_valid && s_write_data_ready) begin
s_burst_index_d = s_burst_index_q + 1'b1;
if (s_packet_input_fault && !s_fault_q) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorPacket;
end else if (!s_fault_q && !s_abort_pending_q) begin
s_desc_progress_d = s_progress_after_beat;
s_total_done_d = s_total_done_q + {29'd0, s_payload_beat_bytes};
end
end
if (s_write_done) begin
if (s_axi_write_fault || s_fault_q || s_packet_input_fault ||
s_abort_pending_q || abort_i) begin
s_fault_d = 1'b1;
if (s_abort_pending_q || abort_i) begin
s_err_code_d = ErrorAbort;
end else if (s_axi_write_fault) begin
s_err_code_d = ErrorAxiWrite;
end else begin
s_err_code_d = ErrorPacket;
end
s_state_d = WritebackStart;
end else if (s_desc_progress_q >= s_selected_bytes_q) begin
s_state_d = WritebackStart;
end else begin
s_state_d = PayloadWriteStart;
end
end
end
WritebackStart: begin
if (s_write_start_ready) begin
s_writeback_index_d = 3'd0;
s_state_d = WritebackWait;
end
end
WritebackWait: begin
if (s_write_data_valid && s_write_data_ready) begin
s_writeback_index_d = s_writeback_index_q + 1'b1;
end
if (s_write_done) begin
if (s_axi_write_fault) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorWriteback;
s_state_d = Terminal;
end else begin
s_state_d = OwnershipStart;
end
end
end
OwnershipStart: begin
if (s_write_start_ready) begin
s_state_d = OwnershipWait;
end
end
OwnershipWait: begin
if (s_write_done) begin
if (s_axi_write_fault) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorWriteback;
s_state_d = Terminal;
end else begin
s_descriptor_irq_d = s_desc_irq;
s_state_d = Complete;
end
end
end
Complete: begin
if (s_fault_q) begin
s_state_d = Terminal;
end else if ((!s_unlimited_q && (s_remaining_q <= s_selected_bytes_q)) ||
(s_unlimited_q && s_desc_end)) begin
s_state_d = Terminal;
end else if (!s_desc_chain || s_desc_end ||
(({8'd0, s_desc_index_q} + 1'b1) >= s_desc_limit_q)) begin
s_fault_d = 1'b1;
s_err_code_d = ErrorLength;
s_state_d = Terminal;
end else begin
s_desc_addr_d = s_desc_next;
s_desc_index_d = s_desc_index_q + 1'b1;
s_desc_words_d = '0;
s_desc_progress_d = 32'd0;
if (!s_unlimited_q) begin
s_remaining_d = s_remaining_after_desc;
end
s_state_d = FetchStart;
end
end
Terminal: begin
s_done_d = !s_fault_q;
s_err_d = s_fault_q;
s_abort_done_d = s_err_code_q == ErrorAbort;
s_state_d = Idle;
end
default: begin
s_fault_d = 1'b1;
s_err_code_d = ErrorPacket;
s_state_d = Terminal;
end
endcase
end
dffr #(
.DATA_WIDTH(4)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(AddrWidth)
) u_desc_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_desc_addr_d),
.dat_o (s_desc_addr_q)
);
dffr #(
.DATA_WIDTH(DescIndexWidth)
) u_desc_index_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_desc_index_d),
.dat_o (s_desc_index_q)
);
dffr #(
.DATA_WIDTH(16)
) u_desc_limit_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_desc_limit_d),
.dat_o (s_desc_limit_q)
);
dffr #(
.DATA_WIDTH(1)
) u_memory_to_packet_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_memory_to_packet_d),
.dat_o (s_memory_to_packet_q)
);
dffr #(
.DATA_WIDTH(1)
) u_allow_short_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_allow_short_d),
.dat_o (s_allow_short_q)
);
dffr #(
.DATA_WIDTH(1)
) u_unlimited_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_unlimited_d),
.dat_o (s_unlimited_q)
);
dffr #(
.DATA_WIDTH(32)
) u_remaining_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_remaining_d),
.dat_o (s_remaining_q)
);
dffr #(
.DATA_WIDTH(32)
) u_total_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_total_done_d),
.dat_o (s_total_done_q)
);
dffr #(
.DATA_WIDTH(256)
) u_desc_words_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_desc_words_d),
.dat_o (s_desc_words_q)
);
dffr #(
.DATA_WIDTH(4)
) u_fetch_index_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fetch_index_d),
.dat_o (s_fetch_index_q)
);
dffr #(
.DATA_WIDTH(32)
) u_selected_bytes_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_selected_bytes_d),
.dat_o (s_selected_bytes_q)
);
dffr #(
.DATA_WIDTH(32)
) u_desc_progress_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_desc_progress_d),
.dat_o (s_desc_progress_q)
);
dffr #(
.DATA_WIDTH(5)
) u_burst_beats_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_burst_beats_d),
.dat_o (s_burst_beats_q)
);
dffr #(
.DATA_WIDTH(5)
) u_burst_index_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_burst_index_d),
.dat_o (s_burst_index_q)
);
dffr #(
.DATA_WIDTH(3)
) u_writeback_index_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_writeback_index_d),
.dat_o (s_writeback_index_q)
);
dffr #(
.DATA_WIDTH(1)
) u_fault_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_d),
.dat_o (s_fault_q)
);
dffr #(
.DATA_WIDTH(8)
) u_error_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_code_d),
.dat_o (s_err_code_q)
);
dffr #(
.DATA_WIDTH(1)
) u_abort_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_abort_pending_d),
.dat_o (s_abort_pending_q)
);
dffr #(
.DATA_WIDTH(1)
) u_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_done_d),
.dat_o (s_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_d),
.dat_o (s_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_descriptor_irq_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_descriptor_irq_d),
.dat_o (s_descriptor_irq_q)
);
dffr #(
.DATA_WIDTH(1)
) u_abort_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_abort_done_d),
.dat_o (s_abort_done_q)
);
`ifndef SYNTHESIS
initial begin
if ((AddrWidth != 32) || (DataWidth != 32) || (MaxDescriptors < 2) ||
(MaxDescriptors > 256)) begin
$fatal(1, "usb2_dma: delivered configuration requires 32-bit AXI and 2..256 descriptors");
end
end
`endif
endmodule
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_dma_descriptor #(
parameter int MaxDescriptors = 256
) (
// verilog_format: off -- descriptor ABI ports are kept in word order
input logic [31:0] buffer_addr_i,
input logic [31:0] byte_length_i,
input logic [31:0] next_addr_i,
input logic [31:0] control_i,
input logic [31:0] actual_length_i,
input logic [31:0] status_i,
// Bits 26:0 are software scheduling metadata.
/* verilator lint_off UNUSEDSIGNAL */
input logic [31:0] frame_i,
/* verilator lint_on UNUSEDSIGNAL */
input logic [31:0] reserved_i,
input
logic [$clog2(MaxDescriptors)-1:0] desc_index_i,
output logic own_o,
output logic chain_o,
output logic end_o,
output logic irq_o,
output logic short_ok_o,
output logic zero_packet_o,
output logic valid_o,
output usb2_pkg::usb2_desc_error_e error_o
// verilog_format: on
);
logic s_control_reserved;
logic s_runtime_dirty;
logic s_next_required;
logic s_last_allowed;
logic [$clog2(MaxDescriptors):0] s_descriptor_position;
assign own_o = control_i[usb2_pkg::USB2_DESC_OWN];
assign chain_o = control_i[usb2_pkg::USB2_DESC_CHAIN];
assign end_o = control_i[usb2_pkg::USB2_DESC_END];
assign irq_o = control_i[usb2_pkg::USB2_DESC_IRQ];
assign short_ok_o = control_i[usb2_pkg::USB2_DESC_SHORT_OK];
assign zero_packet_o = control_i[usb2_pkg::USB2_DESC_ZERO_PACKET];
assign s_control_reserved = (control_i[15:6] != 10'd0) || (control_i[31:24] != 8'd0);
assign s_runtime_dirty = (actual_length_i != 32'd0) || (status_i != 32'd0);
assign s_next_required = !end_o || chain_o;
assign s_descriptor_position = {1'b0, desc_index_i} + 1'b1;
assign s_last_allowed = s_descriptor_position < ($clog2(MaxDescriptors) + 1)'(MaxDescriptors);
always_comb begin
error_o = usb2_pkg::Usb2DescOk;
if (!own_o) begin
error_o = usb2_pkg::Usb2DescNotOwned;
end else if (s_control_reserved || s_runtime_dirty || (reserved_i != 32'd0)) begin
error_o = usb2_pkg::Usb2DescReserved;
end else if (buffer_addr_i[1:0] != 2'b00) begin
error_o = usb2_pkg::Usb2DescBufferAlign;
end else if (byte_length_i == 32'd0) begin
error_o = usb2_pkg::Usb2DescLength;
end else if (usb2_pkg::usb2_range_wraps(buffer_addr_i, byte_length_i)) begin
error_o = usb2_pkg::Usb2DescAddressWrap;
end else if (s_next_required &&
((next_addr_i[4:0] != 5'd0) || (next_addr_i[11:0] > 12'hFE0))) begin
error_o = usb2_pkg::Usb2DescNextAlign;
end else if (end_o ? ((next_addr_i != 32'd0) || chain_o) :
(!chain_o || (next_addr_i == 32'd0) || !s_last_allowed)) begin
error_o = usb2_pkg::Usb2DescChain;
end else if (frame_i[31:27] != 5'd0) begin
error_o = usb2_pkg::Usb2DescFrameReserved;
end
end
assign valid_o = error_o == usb2_pkg::Usb2DescOk;
`ifndef SYNTHESIS
initial begin
if (MaxDescriptors < 2) begin
$fatal(1, "usb2_dma_descriptor: MaxDescriptors must be at least two");
end
end
`endif
endmodule
+529
View File
@@ -0,0 +1,529 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_link_domain #(
parameter int NumEndpoints = 8
) (
// verilog_format: off -- preserve control, CDC mailbox, stream, and status groups
input logic clk_i,
input logic rst_n_i,
input logic enable_i,
input logic phy_reset_n_i,
input logic high_speed_i,
input usb2_pkg::usb2_role_e active_role_i,
input logic [6:0] device_addr_i,
input logic [NumEndpoints-1:0][31:0] endpoint_cfg_i,
input logic [31:0] timeout_i,
input logic work_valid_i,
output logic work_ready_o,
input logic [63:0] work_data_i,
output logic result_valid_o,
input logic result_ready_i,
output logic [63:0] result_data_o,
output logic setup_valid_o,
input logic setup_ready_i,
output logic [63:0] setup_data_o,
input logic fill_cmd_valid_i,
output logic fill_cmd_ready_o,
input logic [26:0] fill_cmd_data_i,
input logic fill_stream_valid_i,
output logic fill_stream_ready_o,
input logic [36:0] fill_stream_data_i,
input logic drain_cmd_valid_i,
output logic drain_cmd_ready_o,
input logic [26:0] drain_cmd_data_i,
output logic drain_stream_valid_o,
input logic drain_stream_ready_i,
output logic [36:0] drain_stream_data_o,
output logic buffer_event_valid_o,
input logic buffer_event_ready_i,
output logic [2:0] buffer_event_data_o,
input logic viewport_valid_i,
output logic viewport_ready_o,
input logic [14:0] viewport_data_i,
output logic viewport_resp_valid_o,
input logic viewport_resp_ready_i,
output logic [8:0] viewport_resp_data_o,
output logic [1:0] line_state_o,
output logic [1:0] vbus_state_o,
output logic id_ground_o,
output logic link_ready_o,
output logic [13:0] frame_o,
output logic transaction_busy_o,
output logic retry_o,
output logic protocol_error_o,
output logic [31:0] ecc_corrected_count_o,
output logic [31:0] ecc_uncorrectable_count_o,
usb2_ulpi_if.dut ulpi
// verilog_format: on
);
localparam logic [15:0] HighSpeedMicroframeCycles = 16'd7500;
localparam logic [15:0] FullSpeedFrameCycles = 16'd60000;
logic s_link_rx_start;
logic s_link_rx_valid;
logic [ 7:0] s_link_rx_data;
logic s_link_rx_end;
logic s_link_rx_err;
logic s_link_tx_start_valid;
logic s_link_tx_start_ready;
logic [ 3:0] s_link_tx_pid;
logic s_link_tx_has_data;
logic s_link_tx_data_valid;
logic s_link_tx_data_ready;
logic [ 7:0] s_link_tx_data;
logic s_link_tx_data_last;
logic s_link_tx_done;
logic s_link_tx_err;
logic s_rx_payload_valid;
logic [ 7:0] s_rx_payload_data;
logic s_rx_packet_done;
logic s_rx_packet_good;
logic [ 3:0] s_rx_packet_pid;
logic [ 6:0] s_rx_token_addr;
logic [ 3:0] s_rx_token_endpoint;
logic [10:0] s_rx_frame_number;
logic [10:0] s_rx_payload_len;
logic s_rx_pid_err;
logic s_rx_crc_err;
logic s_rx_framing_err;
logic s_engine_tx_req_valid;
logic s_engine_tx_req_ready;
logic [ 3:0] s_engine_tx_req_pid;
logic [10:0] s_engine_tx_req_token;
logic [10:0] s_engine_tx_req_len;
logic s_engine_tx_payload_valid;
logic s_engine_tx_payload_ready;
logic s_sie_tx_payload_ready;
logic [ 7:0] s_engine_tx_payload_data;
logic s_engine_tx_done;
logic s_engine_tx_err;
logic s_sie_tx_req_valid;
logic s_sie_tx_req_ready;
logic [ 3:0] s_sie_tx_req_pid;
logic [10:0] s_sie_tx_req_token;
logic [10:0] s_sie_tx_req_len;
logic s_sie_tx_busy;
logic s_sie_tx_done;
logic s_sie_tx_err;
logic s_transaction_busy;
logic s_engine_protocol_err;
logic s_store_rx_start_valid;
logic s_store_rx_start_ready;
logic [11:0] s_store_rx_base;
logic [14:0] s_store_rx_limit;
logic s_store_rx_valid;
logic s_store_rx_ready;
logic [ 7:0] s_store_rx_data;
logic s_store_rx_commit;
logic s_store_rx_cancel;
logic s_store_rx_done;
logic [14:0] s_store_rx_bytes;
logic s_store_rx_overflow;
logic s_store_tx_start_valid;
logic s_store_tx_start_ready;
logic [11:0] s_store_tx_base;
logic [14:0] s_store_tx_bytes;
logic s_store_tx_valid;
logic s_store_tx_ready;
logic [ 7:0] s_store_tx_data;
logic s_store_tx_done;
logic s_store_busy;
logic s_store_clients_enabled;
logic s_store_fill_start_ready;
logic s_store_drain_start_ready;
logic s_store_rx_start_ready_raw;
logic s_store_tx_start_ready_raw;
logic s_fill_done;
logic s_fill_err;
logic s_drain_done;
logic s_ecc_corrected;
logic s_ecc_uncorrectable;
logic [15:0] s_frame_count_d, s_frame_count_q;
logic [13:0] s_frame_d, s_frame_q;
logic s_sof_pending_d, s_sof_pending_q;
logic s_tx_owner_engine_d, s_tx_owner_engine_q;
logic s_buffer_event_valid_d, s_buffer_event_valid_q;
logic [2:0] s_buffer_event_data_d, s_buffer_event_data_q;
logic s_view_resp_valid_d, s_view_resp_valid_q;
logic [8:0] s_view_resp_data_d, s_view_resp_data_q;
logic [31:0] s_ecc_corrected_count_d, s_ecc_corrected_count_q;
logic [31:0] s_ecc_uncorrectable_count_d, s_ecc_uncorrectable_count_q;
logic [15:0] s_frame_period;
logic s_frame_tick;
logic s_sof_selected;
logic s_link_view_resp_valid;
logic [ 7:0] s_link_view_read_data;
logic s_link_view_err;
assign s_frame_period = high_speed_i ? HighSpeedMicroframeCycles : FullSpeedFrameCycles;
assign s_frame_tick = s_frame_count_q == (s_frame_period - 1'b1);
assign s_sof_selected = s_sof_pending_q && !transaction_busy_o && !s_engine_tx_req_valid;
assign s_sie_tx_req_valid = s_engine_tx_req_valid || s_sof_selected;
assign s_sie_tx_req_pid = s_sof_selected ? usb2_pkg::Usb2PidSof : s_engine_tx_req_pid;
assign s_sie_tx_req_token = s_sof_selected ? s_frame_q[13:3] : s_engine_tx_req_token;
assign s_sie_tx_req_len = s_sof_selected ? 11'd0 : s_engine_tx_req_len;
assign s_engine_tx_req_ready = s_sie_tx_req_ready && !s_sof_selected;
assign s_engine_tx_payload_ready = s_tx_owner_engine_q && s_sie_tx_payload_ready;
assign s_engine_tx_done = s_sie_tx_done && s_tx_owner_engine_q;
assign s_engine_tx_err = s_sie_tx_err && s_tx_owner_engine_q;
assign s_store_clients_enabled = enable_i && phy_reset_n_i;
assign fill_cmd_ready_o = s_store_clients_enabled && s_store_fill_start_ready;
assign drain_cmd_ready_o = s_store_clients_enabled && s_store_drain_start_ready;
assign s_store_rx_start_ready = s_store_clients_enabled && s_store_rx_start_ready_raw;
assign s_store_tx_start_ready = s_store_clients_enabled && s_store_tx_start_ready_raw;
assign buffer_event_valid_o = s_buffer_event_valid_q;
assign buffer_event_data_o = s_buffer_event_data_q;
assign viewport_resp_valid_o = s_view_resp_valid_q;
assign viewport_resp_data_o = s_view_resp_data_q;
assign frame_o = s_frame_q;
assign transaction_busy_o = s_transaction_busy || s_store_busy || s_sie_tx_busy;
assign protocol_error_o = s_engine_protocol_err || s_rx_pid_err || s_rx_crc_err ||
s_rx_framing_err;
assign ecc_corrected_count_o = s_ecc_corrected_count_q;
assign ecc_uncorrectable_count_o = s_ecc_uncorrectable_count_q;
always_comb begin
s_frame_count_d = s_frame_count_q;
s_frame_d = s_frame_q;
s_sof_pending_d = s_sof_pending_q;
s_tx_owner_engine_d = s_tx_owner_engine_q;
s_buffer_event_valid_d = s_buffer_event_valid_q && !buffer_event_ready_i;
s_buffer_event_data_d = s_buffer_event_data_q;
s_view_resp_valid_d = s_view_resp_valid_q && !viewport_resp_ready_i;
s_view_resp_data_d = s_view_resp_data_q;
s_ecc_corrected_count_d = s_ecc_corrected_count_q;
s_ecc_uncorrectable_count_d = s_ecc_uncorrectable_count_q;
if ((active_role_i == usb2_pkg::Usb2RoleHost) && enable_i && phy_reset_n_i) begin
if (s_frame_tick) begin
s_frame_count_d = 16'd0;
if (high_speed_i) begin
s_frame_d = s_frame_q + 1'b1;
end else begin
s_frame_d = {s_frame_q[10:0] + 1'b1, 3'd0};
end
s_sof_pending_d = 1'b1;
end else begin
s_frame_count_d = s_frame_count_q + 1'b1;
end
end else if ((active_role_i == usb2_pkg::Usb2RoleDevice) && enable_i && phy_reset_n_i) begin
s_frame_count_d = 16'd0;
s_sof_pending_d = 1'b0;
if (s_rx_packet_done && s_rx_packet_good && (s_rx_packet_pid == usb2_pkg::Usb2PidSof)) begin
s_frame_d = {s_rx_frame_number, 3'd0};
end
end else begin
s_frame_count_d = 16'd0;
s_frame_d = 14'd0;
s_sof_pending_d = 1'b0;
end
if (s_sie_tx_req_valid && s_sie_tx_req_ready) begin
s_tx_owner_engine_d = !s_sof_selected;
if (s_sof_selected) begin
s_sof_pending_d = 1'b0;
end
end
if (s_sie_tx_done || s_sie_tx_err) begin
s_tx_owner_engine_d = 1'b0;
end
if (s_fill_done) begin
s_buffer_event_valid_d = 1'b1;
s_buffer_event_data_d = {s_fill_err, 2'd0};
end else if (s_drain_done) begin
s_buffer_event_valid_d = 1'b1;
s_buffer_event_data_d = 3'b001;
end
if (s_link_view_resp_valid) begin
s_view_resp_valid_d = 1'b1;
s_view_resp_data_d = {s_link_view_err, s_link_view_read_data};
end
if (s_ecc_corrected && !(&s_ecc_corrected_count_q)) begin
s_ecc_corrected_count_d = s_ecc_corrected_count_q + 1'b1;
end
if (s_ecc_uncorrectable && !(&s_ecc_uncorrectable_count_q)) begin
s_ecc_uncorrectable_count_d = s_ecc_uncorrectable_count_q + 1'b1;
end
end
usb2_ulpi_link u_ulpi_link (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (enable_i),
.phy_reset_n_i (phy_reset_n_i),
.tx_start_valid_i (s_link_tx_start_valid),
.tx_start_ready_o (s_link_tx_start_ready),
.tx_pid_i (s_link_tx_pid),
.tx_has_data_i (s_link_tx_has_data),
.tx_data_valid_i (s_link_tx_data_valid),
.tx_data_ready_o (s_link_tx_data_ready),
.tx_data_i (s_link_tx_data),
.tx_data_last_i (s_link_tx_data_last),
.tx_done_o (s_link_tx_done),
.tx_error_o (s_link_tx_err),
.rx_start_o (s_link_rx_start),
.rx_valid_o (s_link_rx_valid),
.rx_data_o (s_link_rx_data),
.rx_end_o (s_link_rx_end),
.rx_error_o (s_link_rx_err),
.line_state_o (line_state_o),
.vbus_state_o (vbus_state_o),
.id_ground_o (id_ground_o),
.viewport_valid_i (viewport_valid_i),
.viewport_ready_o (viewport_ready_o),
.viewport_write_i (viewport_data_i[14]),
.viewport_addr_i (viewport_data_i[13:8]),
.viewport_write_data_i(viewport_data_i[7:0]),
.viewport_resp_valid_o(s_link_view_resp_valid),
.viewport_read_data_o (s_link_view_read_data),
.viewport_error_o (s_link_view_err),
.link_ready_o (link_ready_o),
.ulpi (ulpi)
);
usb2_sie_rx u_sie_rx (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.rx_start_i (s_link_rx_start),
.rx_valid_i (s_link_rx_valid),
.rx_data_i (s_link_rx_data),
.rx_end_i (s_link_rx_end),
.rx_error_i (s_link_rx_err),
.payload_valid_o (s_rx_payload_valid),
.payload_data_o (s_rx_payload_data),
.packet_done_o (s_rx_packet_done),
.packet_good_o (s_rx_packet_good),
.packet_pid_o (s_rx_packet_pid),
.token_addr_o (s_rx_token_addr),
.token_endpoint_o(s_rx_token_endpoint),
.frame_number_o (s_rx_frame_number),
.payload_length_o(s_rx_payload_len),
.pid_error_o (s_rx_pid_err),
.crc_error_o (s_rx_crc_err),
.framing_error_o (s_rx_framing_err)
);
usb2_sie_tx u_sie_tx (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.request_valid_i (s_sie_tx_req_valid),
.request_ready_o (s_sie_tx_req_ready),
.request_pid_i (s_sie_tx_req_pid),
.request_token_i (s_sie_tx_req_token),
.request_length_i (s_sie_tx_req_len),
.payload_valid_i (s_engine_tx_payload_valid),
.payload_ready_o (s_sie_tx_payload_ready),
.payload_data_i (s_engine_tx_payload_data),
.link_start_valid_o(s_link_tx_start_valid),
.link_start_ready_i(s_link_tx_start_ready),
.link_pid_o (s_link_tx_pid),
.link_has_data_o (s_link_tx_has_data),
.link_data_valid_o (s_link_tx_data_valid),
.link_data_ready_i (s_link_tx_data_ready),
.link_data_o (s_link_tx_data),
.link_data_last_o (s_link_tx_data_last),
.link_done_i (s_link_tx_done),
.link_error_i (s_link_tx_err),
.busy_o (s_sie_tx_busy),
.done_o (s_sie_tx_done),
.error_o (s_sie_tx_err)
);
usb2_transaction_engine #(
.NumEndpoints(NumEndpoints)
) u_transaction_engine (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.active_role_i (active_role_i),
.device_addr_i (device_addr_i),
.endpoint_cfg_i (endpoint_cfg_i),
.timeout_i (timeout_i),
.work_valid_i (work_valid_i),
.work_ready_o (work_ready_o),
.work_data_i (work_data_i),
.result_valid_o (result_valid_o),
.result_ready_i (result_ready_i),
.result_data_o (result_data_o),
.setup_valid_o (setup_valid_o),
.setup_ready_i (setup_ready_i),
.setup_data_o (setup_data_o),
.tx_request_valid_o (s_engine_tx_req_valid),
.tx_request_ready_i (s_engine_tx_req_ready),
.tx_request_pid_o (s_engine_tx_req_pid),
.tx_request_token_o (s_engine_tx_req_token),
.tx_request_length_o (s_engine_tx_req_len),
.tx_payload_valid_o (s_engine_tx_payload_valid),
.tx_payload_ready_i (s_engine_tx_payload_ready),
.tx_payload_data_o (s_engine_tx_payload_data),
.tx_done_i (s_engine_tx_done),
.tx_error_i (s_engine_tx_err),
.rx_payload_valid_i (s_rx_payload_valid),
.rx_payload_data_i (s_rx_payload_data),
.rx_packet_done_i (s_rx_packet_done),
.rx_packet_good_i (s_rx_packet_good),
.rx_packet_pid_i (s_rx_packet_pid),
.rx_token_addr_i (s_rx_token_addr),
.rx_token_endpoint_i (s_rx_token_endpoint),
.rx_payload_length_i (s_rx_payload_len),
.store_rx_start_valid_o(s_store_rx_start_valid),
.store_rx_start_ready_i(s_store_rx_start_ready),
.store_rx_base_o (s_store_rx_base),
.store_rx_limit_o (s_store_rx_limit),
.store_rx_valid_o (s_store_rx_valid),
.store_rx_ready_i (s_store_rx_ready),
.store_rx_data_o (s_store_rx_data),
.store_rx_commit_o (s_store_rx_commit),
.store_rx_cancel_o (s_store_rx_cancel),
.store_rx_done_i (s_store_rx_done),
.store_rx_bytes_i (s_store_rx_bytes),
.store_rx_overflow_i (s_store_rx_overflow),
.store_tx_start_valid_o(s_store_tx_start_valid),
.store_tx_start_ready_i(s_store_tx_start_ready),
.store_tx_base_o (s_store_tx_base),
.store_tx_bytes_o (s_store_tx_bytes),
.store_tx_valid_i (s_store_tx_valid),
.store_tx_ready_o (s_store_tx_ready),
.store_tx_data_i (s_store_tx_data),
.store_tx_done_i (s_store_tx_done),
.busy_o (s_transaction_busy),
.retry_o (retry_o),
.protocol_error_o (s_engine_protocol_err)
);
usb2_packet_store u_packet_store (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.fill_start_valid_i (fill_cmd_valid_i && s_store_clients_enabled),
.fill_start_ready_o (s_store_fill_start_ready),
.fill_base_i (fill_cmd_data_i[11:0]),
.fill_bytes_i (fill_cmd_data_i[26:12]),
.fill_valid_i (fill_stream_valid_i),
.fill_ready_o (fill_stream_ready_o),
.fill_data_i (fill_stream_data_i[31:0]),
.fill_strb_i (fill_stream_data_i[35:32]),
.fill_last_i (fill_stream_data_i[36]),
.fill_done_o (s_fill_done),
.fill_error_o (s_fill_err),
.drain_start_valid_i(drain_cmd_valid_i && s_store_clients_enabled),
.drain_start_ready_o(s_store_drain_start_ready),
.drain_base_i (drain_cmd_data_i[11:0]),
.drain_bytes_i (drain_cmd_data_i[26:12]),
.drain_valid_o (drain_stream_valid_o),
.drain_ready_i (drain_stream_ready_i),
.drain_data_o (drain_stream_data_o[31:0]),
.drain_strb_o (drain_stream_data_o[35:32]),
.drain_last_o (drain_stream_data_o[36]),
.drain_done_o (s_drain_done),
.rx_start_valid_i (s_store_rx_start_valid && s_store_clients_enabled),
.rx_start_ready_o (s_store_rx_start_ready_raw),
.rx_base_i (s_store_rx_base),
.rx_limit_i (s_store_rx_limit),
.rx_valid_i (s_store_rx_valid),
.rx_ready_o (s_store_rx_ready),
.rx_data_i (s_store_rx_data),
.rx_commit_i (s_store_rx_commit),
.rx_cancel_i (s_store_rx_cancel),
.rx_done_o (s_store_rx_done),
.rx_bytes_o (s_store_rx_bytes),
.rx_overflow_o (s_store_rx_overflow),
.tx_start_valid_i (s_store_tx_start_valid && s_store_clients_enabled),
.tx_start_ready_o (s_store_tx_start_ready_raw),
.tx_base_i (s_store_tx_base),
.tx_bytes_i (s_store_tx_bytes),
.tx_valid_o (s_store_tx_valid),
.tx_ready_i (s_store_tx_ready),
.tx_data_o (s_store_tx_data),
.tx_done_o (s_store_tx_done),
.busy_o (s_store_busy),
.ecc_corrected_o (s_ecc_corrected),
.ecc_uncorrectable_o(s_ecc_uncorrectable)
);
dffr #(
.DATA_WIDTH(16)
) u_frame_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frame_count_d),
.dat_o (s_frame_count_q)
);
dffr #(
.DATA_WIDTH(14)
) u_frame_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frame_d),
.dat_o (s_frame_q)
);
dffr #(
.DATA_WIDTH(1)
) u_sof_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_sof_pending_d),
.dat_o (s_sof_pending_q)
);
dffr #(
.DATA_WIDTH(1)
) u_tx_owner_engine_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tx_owner_engine_d),
.dat_o (s_tx_owner_engine_q)
);
dffr #(
.DATA_WIDTH(1)
) u_buffer_event_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_buffer_event_valid_d),
.dat_o (s_buffer_event_valid_q)
);
dffr #(
.DATA_WIDTH(3)
) u_buffer_event_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_buffer_event_data_d),
.dat_o (s_buffer_event_data_q)
);
dffr #(
.DATA_WIDTH(1)
) u_view_resp_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_resp_valid_d),
.dat_o (s_view_resp_valid_q)
);
dffr #(
.DATA_WIDTH(9)
) u_view_resp_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_resp_data_d),
.dat_o (s_view_resp_data_q)
);
dffr #(
.DATA_WIDTH(32)
) u_ecc_corrected_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ecc_corrected_count_d),
.dat_o (s_ecc_corrected_count_q)
);
dffr #(
.DATA_WIDTH(32)
) u_ecc_uncorrectable_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ecc_uncorrectable_count_d),
.dat_o (s_ecc_uncorrectable_count_q)
);
endmodule
+96
View File
@@ -0,0 +1,96 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_packet_ram #(
parameter bit EnableFaultInjection = 1'b0
) (
// verilog_format: off -- preserve the memory request/response grouping
input logic clk_i,
input logic rst_n_i,
input logic write_i,
input logic read_i,
input logic [11:0] addr_i,
input logic [31:0] write_data_i,
input logic inject_single_i,
input logic inject_double_i,
output logic [31:0] read_data_o,
output logic read_valid_o,
output logic corrected_o,
output logic uncorrectable_o
// verilog_format: on
);
logic [38:0] s_write_code;
logic [38:0] s_write_code_injected;
logic [39:0] s_physical_write;
logic [39:0] s_physical_read;
logic [38:0] s_read_code;
logic s_read_pending_d;
logic s_read_pending_q;
logic s_decode_corrected;
logic s_decode_uncorrectable;
logic s_overall_parity_err;
logic [ 5:0] s_syndrome;
secded_encode #(
.DATA_WIDTH(32)
) u_encode (
.data_i(write_data_i),
.code_o(s_write_code)
);
always_comb begin
s_write_code_injected = s_write_code;
if (EnableFaultInjection && inject_single_i) begin
s_write_code_injected[0] = ~s_write_code[0];
end
if (EnableFaultInjection && inject_double_i) begin
s_write_code_injected[0] = ~s_write_code[0];
s_write_code_injected[1] = ~s_write_code[1];
end
end
assign s_physical_write = {1'b0, s_write_code_injected};
assign s_read_code = s_physical_read[38:0];
assign s_read_pending_d = read_i;
assign read_valid_o = s_read_pending_q;
assign corrected_o = s_read_pending_q &&
(s_decode_corrected || s_overall_parity_err || s_physical_read[39]);
assign uncorrectable_o = s_read_pending_q && s_decode_uncorrectable && (s_syndrome != 6'd0);
tc_usb2_packet_ram u_packet_ram (
.clk_i (clk_i),
.cs_i (write_i || read_i),
.write_i(write_i),
.addr_i (addr_i),
.data_i (s_physical_write),
.data_o (s_physical_read)
);
secded_decode #(
.DATA_WIDTH(32)
) u_decode (
.code_i (s_read_code),
.data_o (read_data_o),
.syndrome_o (s_syndrome),
.corrected_o (s_decode_corrected),
.overall_parity_error_o(s_overall_parity_err),
.uncorrectable_o (s_decode_uncorrectable)
);
dffr #(
.DATA_WIDTH(1)
) u_read_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_read_pending_d),
.dat_o (s_read_pending_q)
);
`ifndef SYNTHESIS
always_ff @(posedge clk_i) begin
if (rst_n_i && write_i && read_i) begin
$error("usb2_packet_ram: simultaneous read and write request");
end
end
`endif
endmodule
+445
View File
@@ -0,0 +1,445 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_packet_store (
// verilog_format: off -- preserve DMA fill/drain and protocol client groups
input logic clk_i,
input logic rst_n_i,
input logic fill_start_valid_i,
output logic fill_start_ready_o,
input logic [11:0] fill_base_i,
input logic [14:0] fill_bytes_i,
input logic fill_valid_i,
output logic fill_ready_o,
input logic [31:0] fill_data_i,
input logic [3:0] fill_strb_i,
input logic fill_last_i,
output logic fill_done_o,
output logic fill_error_o,
input logic drain_start_valid_i,
output logic drain_start_ready_o,
input logic [11:0] drain_base_i,
input logic [14:0] drain_bytes_i,
output logic drain_valid_o,
input logic drain_ready_i,
output logic [31:0] drain_data_o,
output logic [3:0] drain_strb_o,
output logic drain_last_o,
output logic drain_done_o,
input logic rx_start_valid_i,
output logic rx_start_ready_o,
input logic [11:0] rx_base_i,
input logic [14:0] rx_limit_i,
input logic rx_valid_i,
output logic rx_ready_o,
input logic [7:0] rx_data_i,
input logic rx_commit_i,
input logic rx_cancel_i,
output logic rx_done_o,
output logic [14:0] rx_bytes_o,
output logic rx_overflow_o,
input logic tx_start_valid_i,
output logic tx_start_ready_o,
input logic [11:0] tx_base_i,
input logic [14:0] tx_bytes_i,
output logic tx_valid_o,
input logic tx_ready_i,
output logic [7:0] tx_data_o,
output logic tx_done_o,
output logic busy_o,
output logic ecc_corrected_o,
output logic ecc_uncorrectable_o
// verilog_format: on
);
typedef enum logic [3:0] {
Idle,
Fill,
DrainIssue,
DrainWait,
DrainSend,
Receive,
ReceiveFlush,
TransmitIssue,
TransmitWait,
TransmitSend
} store_state_e;
store_state_e s_state_d, s_state_q;
logic [3:0] s_state_bits_q;
logic [11:0] s_addr_d, s_addr_q;
logic [14:0] s_count_d, s_count_q;
logic [14:0] s_limit_d, s_limit_q;
logic [31:0] s_word_d, s_word_q;
logic [1:0] s_lane_d, s_lane_q;
logic s_overflow_d, s_overflow_q;
logic s_fill_done_d, s_fill_done_q;
logic s_fill_err_d, s_fill_err_q;
logic s_drain_done_d, s_drain_done_q;
logic s_rx_done_d, s_rx_done_q;
logic s_tx_done_d, s_tx_done_q;
logic s_ram_write;
logic s_ram_read;
logic [11:0] s_ram_addr;
logic [31:0] s_ram_write_data;
logic [31:0] s_ram_read_data;
logic [31:0] s_read_word_q;
logic s_ram_read_valid;
logic s_ram_corrected;
logic s_ram_uncorrectable;
logic [ 2:0] s_active_bytes;
logic [ 3:0] s_expected_strb;
logic s_fill_last_expected;
logic s_receive_full_word;
function automatic logic [2:0] active_bytes(input logic [14:0] bytes_i);
if (bytes_i >= 15'd4) begin
return 3'd4;
end
return {1'b0, bytes_i[1:0]};
endfunction
function automatic logic [3:0] low_strobe(input logic [2:0] bytes_i);
unique case (bytes_i)
3'd0: return 4'b0000;
3'd1: return 4'b0001;
3'd2: return 4'b0011;
3'd3: return 4'b0111;
default: return 4'b1111;
endcase
endfunction
function automatic logic range_valid(input logic [11:0] base_i, input logic [14:0] bytes_i);
logic [15:0] words;
logic [15:0] end_word;
begin
words = ({1'b0, bytes_i} + 16'd3) >> 2;
end_word = {4'd0, base_i} + words;
return (bytes_i != 15'd0) && (end_word <= 16'd4096);
end
endfunction
assign s_state_q = store_state_e'(s_state_bits_q);
assign s_active_bytes = active_bytes(s_limit_q - s_count_q);
assign s_expected_strb = low_strobe(s_active_bytes);
assign s_fill_last_expected = (s_count_q + {12'd0, s_active_bytes}) >= s_limit_q;
assign s_receive_full_word = s_lane_q == 2'd3;
assign fill_start_ready_o = s_state_q == Idle;
assign drain_start_ready_o = s_state_q == Idle;
assign rx_start_ready_o = s_state_q == Idle;
assign tx_start_ready_o = s_state_q == Idle;
assign fill_ready_o = s_state_q == Fill;
assign drain_valid_o = s_state_q == DrainSend;
assign drain_data_o = s_read_word_q;
assign drain_strb_o = s_expected_strb;
assign drain_last_o = s_fill_last_expected;
assign rx_ready_o = s_state_q == Receive;
assign rx_bytes_o = s_count_q;
assign rx_overflow_o = s_overflow_q;
assign tx_valid_o = s_state_q == TransmitSend;
assign tx_data_o = s_read_word_q[(s_lane_q*8)+:8];
assign busy_o = s_state_q != Idle;
assign fill_done_o = s_fill_done_q;
assign fill_error_o = s_fill_err_q;
assign drain_done_o = s_drain_done_q;
assign rx_done_o = s_rx_done_q;
assign tx_done_o = s_tx_done_q;
assign ecc_corrected_o = s_ram_read_valid && s_ram_corrected;
assign ecc_uncorrectable_o = s_ram_read_valid && s_ram_uncorrectable;
always_comb begin
s_ram_write = 1'b0;
s_ram_read = 1'b0;
s_ram_addr = s_addr_q;
s_ram_write_data = s_word_q;
if ((s_state_q == Fill) && fill_valid_i && fill_ready_o) begin
s_ram_write = 1'b1;
s_ram_write_data = fill_data_i;
end else if ((s_state_q == Receive) && rx_valid_i && rx_ready_o &&
!s_overflow_q && s_receive_full_word) begin
s_ram_write = 1'b1;
s_ram_write_data = s_word_q;
s_ram_write_data[31:24] = rx_data_i;
end else if (s_state_q == ReceiveFlush) begin
s_ram_write = 1'b1;
end else if ((s_state_q == DrainIssue) || (s_state_q == TransmitIssue)) begin
s_ram_read = 1'b1;
end
end
always_comb begin
s_state_d = s_state_q;
s_addr_d = s_addr_q;
s_count_d = s_count_q;
s_limit_d = s_limit_q;
s_word_d = s_word_q;
s_lane_d = s_lane_q;
s_overflow_d = s_overflow_q;
s_fill_done_d = 1'b0;
s_fill_err_d = 1'b0;
s_drain_done_d = 1'b0;
s_rx_done_d = 1'b0;
s_tx_done_d = 1'b0;
unique case (s_state_q)
Idle: begin
s_count_d = '0;
s_lane_d = '0;
s_word_d = '0;
s_overflow_d = 1'b0;
if (fill_start_valid_i && fill_start_ready_o) begin
s_addr_d = fill_base_i;
s_limit_d = fill_bytes_i;
if (!range_valid(fill_base_i, fill_bytes_i)) begin
s_fill_done_d = 1'b1;
s_fill_err_d = 1'b1;
end else begin
s_state_d = Fill;
end
end else if (drain_start_valid_i && drain_start_ready_o) begin
s_addr_d = drain_base_i;
s_limit_d = drain_bytes_i;
if (!range_valid(drain_base_i, drain_bytes_i)) begin
s_drain_done_d = 1'b1;
end else begin
s_state_d = DrainIssue;
end
end else if (rx_start_valid_i && rx_start_ready_o) begin
s_addr_d = rx_base_i;
s_limit_d = rx_limit_i;
s_state_d = Receive;
end else if (tx_start_valid_i && tx_start_ready_o) begin
s_addr_d = tx_base_i;
s_limit_d = tx_bytes_i;
if (!range_valid(tx_base_i, tx_bytes_i)) begin
s_tx_done_d = 1'b1;
end else begin
s_state_d = TransmitIssue;
end
end
end
Fill: begin
if (fill_valid_i && fill_ready_o) begin
s_count_d = s_count_q + {12'd0, s_active_bytes};
s_addr_d = s_addr_q + 1'b1;
if ((fill_strb_i != s_expected_strb) || (fill_last_i != s_fill_last_expected)) begin
s_fill_err_d = 1'b1;
s_fill_done_d = 1'b1;
s_state_d = Idle;
end else if (s_fill_last_expected) begin
s_fill_done_d = 1'b1;
s_state_d = Idle;
end
end
end
DrainIssue: s_state_d = DrainWait;
DrainWait: begin
if (s_ram_read_valid) begin
s_state_d = DrainSend;
end
end
DrainSend: begin
if (drain_valid_o && drain_ready_i) begin
s_count_d = s_count_q + {12'd0, s_active_bytes};
s_addr_d = s_addr_q + 1'b1;
if (s_fill_last_expected) begin
s_drain_done_d = 1'b1;
s_state_d = Idle;
end else begin
s_state_d = DrainIssue;
end
end
end
Receive: begin
if (rx_valid_i && rx_ready_o) begin
if (s_count_q >= s_limit_q) begin
s_overflow_d = 1'b1;
end else begin
s_word_d[(s_lane_q*8)+:8] = rx_data_i;
s_count_d = s_count_q + 1'b1;
if (s_receive_full_word) begin
s_addr_d = s_addr_q + 1'b1;
s_lane_d = '0;
s_word_d = '0;
end else begin
s_lane_d = s_lane_q + 1'b1;
end
end
end
if (rx_cancel_i) begin
s_rx_done_d = 1'b1;
s_count_d = '0;
s_state_d = Idle;
end else if (rx_commit_i) begin
if ((s_lane_d != '0) && !s_overflow_d) begin
s_state_d = ReceiveFlush;
end else begin
s_rx_done_d = 1'b1;
s_state_d = Idle;
end
end
end
ReceiveFlush: begin
s_rx_done_d = 1'b1;
s_state_d = Idle;
end
TransmitIssue: s_state_d = TransmitWait;
TransmitWait: begin
if (s_ram_read_valid) begin
s_state_d = TransmitSend;
end
end
TransmitSend: begin
if (tx_valid_o && tx_ready_i) begin
s_count_d = s_count_q + 1'b1;
if ((s_count_q + 1'b1) >= s_limit_q) begin
s_tx_done_d = 1'b1;
s_state_d = Idle;
end else if (s_lane_q == 2'd3) begin
s_lane_d = '0;
s_addr_d = s_addr_q + 1'b1;
s_state_d = TransmitIssue;
end else begin
s_lane_d = s_lane_q + 1'b1;
end
end
end
default: s_state_d = Idle;
endcase
end
usb2_packet_ram u_packet_ram (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.write_i (s_ram_write),
.read_i (s_ram_read),
.addr_i (s_ram_addr),
.write_data_i (s_ram_write_data),
.inject_single_i(1'b0),
.inject_double_i(1'b0),
.read_data_o (s_ram_read_data),
.read_valid_o (s_ram_read_valid),
.corrected_o (s_ram_corrected),
.uncorrectable_o(s_ram_uncorrectable)
);
dffer #(
.DATA_WIDTH(32)
) u_read_word_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_ram_read_valid),
.dat_i (s_ram_read_data),
.dat_o (s_read_word_q)
);
dffr #(
.DATA_WIDTH(4)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(12)
) u_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_addr_d),
.dat_o (s_addr_q)
);
dffr #(
.DATA_WIDTH(15)
) u_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_count_d),
.dat_o (s_count_q)
);
dffr #(
.DATA_WIDTH(15)
) u_limit_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_limit_d),
.dat_o (s_limit_q)
);
dffr #(
.DATA_WIDTH(32)
) u_word_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_word_d),
.dat_o (s_word_q)
);
dffr #(
.DATA_WIDTH(2)
) u_lane_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_lane_d),
.dat_o (s_lane_q)
);
dffr #(
.DATA_WIDTH(1)
) u_overflow_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_overflow_d),
.dat_o (s_overflow_q)
);
dffr #(
.DATA_WIDTH(1)
) u_fill_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fill_done_d),
.dat_o (s_fill_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_fill_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fill_err_d),
.dat_o (s_fill_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_drain_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_drain_done_d),
.dat_o (s_drain_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_rx_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_rx_done_d),
.dat_o (s_rx_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_tx_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tx_done_d),
.dat_o (s_tx_done_q)
);
`ifndef SYNTHESIS
always_ff @(posedge clk_i) begin
if (rst_n_i && (s_state_q == Idle) &&
((fill_start_valid_i &&
(drain_start_valid_i || rx_start_valid_i || tx_start_valid_i)) ||
(drain_start_valid_i && (rx_start_valid_i || tx_start_valid_i)) ||
(rx_start_valid_i && tx_start_valid_i))) begin
$error("usb2_packet_store: simultaneous client start requests (%b)", {
fill_start_valid_i, drain_start_valid_i, rx_start_valid_i, tx_start_valid_i});
end
end
`endif
endmodule
+185
View File
@@ -0,0 +1,185 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
package usb2_pkg;
typedef enum logic [1:0] {
Usb2RoleIdle = 2'd0,
Usb2RoleDevice = 2'd1,
Usb2RoleHost = 2'd2
} usb2_role_e;
typedef enum logic [1:0] {
Usb2SpeedHigh = 2'd0,
Usb2SpeedFull = 2'd1,
Usb2SpeedLow = 2'd2
} usb2_speed_e;
typedef enum logic [1:0] {
Usb2TransferControl = 2'd0,
Usb2TransferIsochronous = 2'd1,
Usb2TransferBulk = 2'd2,
Usb2TransferInterrupt = 2'd3
} usb2_transfer_type_e;
typedef enum logic [3:0] {
Usb2PidOut = 4'h1,
Usb2PidAck = 4'h2,
Usb2PidData0 = 4'h3,
Usb2PidPing = 4'h4,
Usb2PidSof = 4'h5,
Usb2PidNyet = 4'h6,
Usb2PidData2 = 4'h7,
Usb2PidSplit = 4'h8,
Usb2PidIn = 4'h9,
Usb2PidNak = 4'hA,
Usb2PidData1 = 4'hB,
Usb2PidPre = 4'hC,
Usb2PidSetup = 4'hD,
Usb2PidStall = 4'hE,
Usb2PidMdata = 4'hF
} usb2_pid_e;
typedef enum logic [4:0] {
Usb2DescOk = 5'd0,
Usb2DescNotOwned = 5'd1,
Usb2DescReserved = 5'd2,
Usb2DescBufferAlign = 5'd3,
Usb2DescLength = 5'd4,
Usb2DescNextAlign = 5'd5,
Usb2DescChain = 5'd6,
Usb2DescFrameReserved = 5'd7,
Usb2DescAddressWrap = 5'd8
} usb2_desc_error_e;
localparam int unsigned USB2_DESC_BUFFER_WORD = 0;
localparam int unsigned USB2_DESC_LENGTH_WORD = 1;
localparam int unsigned USB2_DESC_NEXT_WORD = 2;
localparam int unsigned USB2_DESC_CONTROL_WORD = 3;
localparam int unsigned USB2_DESC_ACTUAL_WORD = 4;
localparam int unsigned USB2_DESC_STATUS_WORD = 5;
localparam int unsigned USB2_DESC_FRAME_WORD = 6;
localparam int unsigned USB2_DESC_RESERVED_WORD = 7;
localparam int unsigned USB2_DESC_OWN = 0;
localparam int unsigned USB2_DESC_CHAIN = 1;
localparam int unsigned USB2_DESC_END = 2;
localparam int unsigned USB2_DESC_IRQ = 3;
localparam int unsigned USB2_DESC_SHORT_OK = 4;
localparam int unsigned USB2_DESC_ZERO_PACKET = 5;
localparam int unsigned USB2_DESC_DONE = 16;
localparam int unsigned USB2_DESC_SHORT = 17;
// Completion ABI values are consumed by software.
/* verilator lint_off UNUSEDPARAM */
localparam int unsigned USB2_DESC_STALL = 18;
localparam int unsigned USB2_DESC_TIMEOUT = 19;
localparam int unsigned USB2_DESC_CRC_ERROR = 20;
/* verilator lint_on UNUSEDPARAM */
localparam int unsigned USB2_DESC_PROTOCOL_ERROR = 21;
localparam int unsigned USB2_DESC_AXI_ERROR = 22;
localparam int unsigned USB2_DESC_ABORTED = 23;
localparam int unsigned USB2_WORK_ROLE_LSB = 0;
localparam int unsigned USB2_WORK_INDEX_LSB = 2;
localparam int unsigned USB2_WORK_DIRECTION_IN = 6;
localparam int unsigned USB2_WORK_TYPE_LSB = 7;
localparam int unsigned USB2_WORK_ADDRESS_LSB = 9;
localparam int unsigned USB2_WORK_ENDPOINT_LSB = 16;
localparam int unsigned USB2_WORK_SPEED_LSB = 20;
localparam int unsigned USB2_WORK_RAM_BASE_LSB = 22;
localparam int unsigned USB2_WORK_LENGTH_LSB = 34;
localparam int unsigned USB2_WORK_SETUP = 49;
localparam int unsigned USB2_WORK_CANCEL = 50;
localparam int unsigned USB2_WORK_TOGGLE = 51;
localparam int unsigned USB2_RESULT_ROLE_LSB = 0;
localparam int unsigned USB2_RESULT_INDEX_LSB = 2;
localparam int unsigned USB2_RESULT_DIRECTION_IN = 6;
localparam int unsigned USB2_RESULT_CODE_LSB = 7;
localparam int unsigned USB2_RESULT_LENGTH_LSB = 11;
typedef enum logic [3:0] {
Usb2ResultSuccess = 4'd0,
Usb2ResultNak = 4'd1,
Usb2ResultStall = 4'd2,
Usb2ResultTimeout = 4'd3,
Usb2ResultCrc = 4'd4,
Usb2ResultProtocol = 4'd5,
Usb2ResultOverflow = 4'd6,
Usb2ResultCanceled = 4'd7
} usb2_result_e;
function automatic logic [7:0] usb2_pid_byte(input usb2_pid_e pid_i);
return {~4'(pid_i), 4'(pid_i)};
endfunction
function automatic logic usb2_pid_valid(input logic [7:0] pid_i);
return pid_i[7:4] == ~pid_i[3:0];
endfunction
function automatic logic usb2_pid_is_token(input logic [3:0] pid_i);
return (pid_i == Usb2PidOut) || (pid_i == Usb2PidIn) || (pid_i == Usb2PidSof) ||
(pid_i == Usb2PidSetup) || (pid_i == Usb2PidPing);
endfunction
function automatic logic usb2_pid_is_data(input logic [3:0] pid_i);
return (pid_i == Usb2PidData0) || (pid_i == Usb2PidData1) ||
(pid_i == Usb2PidData2) || (pid_i == Usb2PidMdata);
endfunction
function automatic logic usb2_pid_is_handshake(input logic [3:0] pid_i);
return (pid_i == Usb2PidAck) || (pid_i == Usb2PidNak) ||
(pid_i == Usb2PidStall) || (pid_i == Usb2PidNyet);
endfunction
function automatic logic [4:0] usb2_crc5_next(input logic [4:0] crc_i, input logic data_i);
logic feedback;
begin
feedback = crc_i[0] ^ data_i;
usb2_crc5_next = {feedback, crc_i[4], crc_i[3] ^ feedback, crc_i[2], crc_i[1]};
end
endfunction
function automatic logic [4:0] usb2_token_crc5(input logic [10:0] token_i);
logic [4:0] crc;
begin
crc = 5'h1F;
for (int unsigned index = 0; index < 11; index++) begin
crc = usb2_crc5_next(crc, token_i[index]);
end
return ~crc;
end
endfunction
function automatic logic [15:0] usb2_crc16_next(input logic [15:0] crc_i, input logic data_i);
logic feedback;
begin
feedback = crc_i[0] ^ data_i;
usb2_crc16_next = {1'b0, crc_i[15:1]};
if (feedback) begin
usb2_crc16_next = usb2_crc16_next ^ 16'hA001;
end
end
endfunction
function automatic logic [15:0] usb2_crc16_byte(input logic [15:0] crc_i,
input logic [7:0] data_i);
logic [15:0] crc;
begin
crc = crc_i;
for (int unsigned index = 0; index < 8; index++) begin
crc = usb2_crc16_next(crc, data_i[index]);
end
return crc;
end
endfunction
function automatic logic [15:0] usb2_crc16_finish(input logic [15:0] crc_i);
return ~crc_i;
endfunction
function automatic logic usb2_range_wraps(input logic [31:0] addr_i, input logic [31:0] length_i);
return (length_i != 32'd0) && (addr_i > (32'hFFFF_FFFF - (length_i - 1'b1)));
endfunction
endpackage
+944
View File
@@ -0,0 +1,944 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "usb2_define.svh"
module usb2_reg #(
parameter int NumEndpoints = 8,
parameter int NumChannels = 16
) (
// verilog_format: off -- preserve APB, status, and configuration port groups
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
input logic [31:0] global_status_i,
input logic [31:0] role_status_i,
input logic [31:0] phy_status_i,
input logic [31:0] frame_i,
input logic [31:0] device_status_i,
input logic [63:0] setup_packet_i,
input logic [31:0] endpoint_pending_in_i,
input logic [31:0] endpoint_pending_out_i,
input logic [31:0] endpoint_complete_in_i,
input logic [31:0] endpoint_complete_out_i,
input logic [NumEndpoints-1:0][31:0] endpoint_status_i,
input logic [NumEndpoints-1:0][31:0] endpoint_bytes_in_i,
input logic [NumEndpoints-1:0][31:0] endpoint_bytes_out_i,
input logic [31:0] host_status_i,
input logic [31:0] port_status_i,
input logic [31:0] schedule_status_i,
input logic [NumChannels-1:0][31:0] channel_status_i,
input logic [NumChannels-1:0][31:0] channel_bytes_i,
input logic [31:0] ram_status_i,
input logic [31:0] ecc_status_i,
input logic [31:0] ecc_corrected_count_i,
input logic [31:0] ecc_uncorrectable_count_i,
input logic [31:0] debug_status_i,
input logic [31:0] perf_tx_bytes_i,
input logic [31:0] perf_rx_bytes_i,
input logic [31:0] perf_packets_i,
input logic [31:0] perf_retries_i,
input logic [31:0] perf_axi_stall_i,
input logic [31:0] perf_ram_stall_i,
input logic [31:0] perf_irq_count_i,
input logic [15:0] irq_event_i,
input logic error_capture_i,
input logic [31:0] error_status_i,
input logic [31:0] error_code_i,
input logic [31:0] error_info_i,
input logic [31:0] error_desc_addr_i,
input logic [31:0] error_buffer_addr_i,
input logic viewport_resp_valid_i,
input logic [7:0] viewport_read_data_i,
input logic viewport_error_i,
output logic enable_o,
output logic soft_reset_o,
output logic abort_o,
output logic [1:0] force_role_o,
output logic auto_role_o,
output logic phy_reset_n_o,
output logic phy_suspend_o,
output logic remote_wake_o,
output logic [31:0] timeout_o,
output logic [7:0] test_ctrl_o,
output logic perf_clear_o,
output logic [6:0] device_addr_o,
output logic [15:0] device_ctrl_o,
output logic [NumEndpoints-1:0][31:0] endpoint_cfg_o,
output logic [NumEndpoints-1:0][31:0] endpoint_ram_in_o,
output logic [NumEndpoints-1:0][31:0] endpoint_ram_out_o,
output logic [NumEndpoints-1:0][31:0] endpoint_desc_in_o,
output logic [NumEndpoints-1:0][31:0] endpoint_desc_out_o,
output logic [NumEndpoints-1:0][31:0] endpoint_command_o,
output logic [31:0] endpoint_complete_clear_in_o,
output logic [31:0] endpoint_complete_clear_out_o,
output logic [15:0] host_ctrl_o,
output logic port_ctrl_o,
output logic schedule_ctrl_o,
output logic [NumChannels-1:0][31:0] channel_cfg_o,
output logic [NumChannels-1:0][31:0] channel_target_o,
output logic [NumChannels-1:0][31:0] channel_interval_o,
output logic [NumChannels-1:0][31:0] channel_ram_o,
output logic [NumChannels-1:0][31:0] channel_desc_o,
output logic [NumChannels-1:0][31:0] channel_command_o,
output logic [15:0] ram_ctrl_o,
output logic ram_bist_start_o,
output logic viewport_valid_o,
output logic viewport_write_o,
output logic [5:0] viewport_addr_o,
output logic [7:0] viewport_write_data_o,
output logic irq_o
// verilog_format: on
);
localparam logic [31:0] IpId = 32'h5553_4232;
localparam logic [31:0] IpVersion = 32'h0001_0000;
localparam logic [31:0] Capability0 =
(32'd1 << 0) | (32'd1 << 1) | (32'd1 << 2) | (32'd1 << 3) |
(32'd1 << 4) | (32'd1 << 5) | (32'd1 << 6) | (32'd1 << 7) |
(32'd1 << 8) | (32'd1 << 9) | (32'd1 << 10) | (32'd1 << 11) |
(32'd1 << 12) | (32'(NumEndpoints - 1) << 16) |
(32'(NumChannels - 1) << 24);
localparam logic [31:0] Capability1 = 32'h0010_4000;
logic s_apb4_ready_d, s_apb4_ready_q;
logic [31:0] s_apb4_rdata_d, s_apb4_rdata_q;
logic s_apb4_resp_err_d, s_apb4_resp_err_q;
logic s_req_accept;
logic s_write_access;
logic s_read_err;
logic s_write_err;
logic [ 31:0] s_read_data;
logic [ 11:0] s_offset;
logic s_endpoint_window;
logic s_channel_window;
logic [$clog2(NumEndpoints)-1:0] s_endpoint_index;
logic [ $clog2(NumChannels)-1:0] s_channel_index;
logic [ 8:0] s_endpoint_delta;
logic [ 9:0] s_channel_delta;
logic [ 11:0] s_endpoint_offset;
logic [ 11:0] s_channel_offset;
logic [ 15:0] s_irq_en_merged;
logic [3:0] s_global_ctrl_d, s_global_ctrl_q;
logic [2:0] s_role_ctrl_d, s_role_ctrl_q;
logic [2:0] s_phy_ctrl_d, s_phy_ctrl_q;
logic [31:0] s_timeout_d, s_timeout_q;
logic [31:0] s_test_ctrl_d, s_test_ctrl_q;
logic [31:0] s_device_ctrl_d, s_device_ctrl_q;
logic [6:0] s_device_addr_d, s_device_addr_q;
logic [31:0] s_host_ctrl_d, s_host_ctrl_q;
logic [31:0] s_port_ctrl_d, s_port_ctrl_q;
logic [31:0] s_schedule_ctrl_d, s_schedule_ctrl_q;
logic [31:0] s_ram_ctrl_d, s_ram_ctrl_q;
logic [15:0] s_irq_stat_d, s_irq_stat_q;
logic [15:0] s_irq_en_d, s_irq_en_q;
logic [31:0] s_err_stat_d, s_err_stat_q;
logic [31:0] s_err_code_d, s_err_code_q;
logic [31:0] s_err_info_d, s_err_info_q;
logic [31:0] s_err_desc_addr_d, s_err_desc_addr_q;
logic [31:0] s_err_buffer_addr_d, s_err_buffer_addr_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_cfg_d, s_endpoint_cfg_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_ram_in_d, s_endpoint_ram_in_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_ram_out_d, s_endpoint_ram_out_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_desc_in_d, s_endpoint_desc_in_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_desc_out_d, s_endpoint_desc_out_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_cmd_d, s_endpoint_cmd_q;
logic [NumChannels-1:0][31:0] s_channel_cfg_d, s_channel_cfg_q;
logic [NumChannels-1:0][31:0] s_channel_target_d, s_channel_target_q;
logic [NumChannels-1:0][31:0] s_channel_interval_d, s_channel_interval_q;
logic [NumChannels-1:0][31:0] s_channel_ram_d, s_channel_ram_q;
logic [NumChannels-1:0][31:0] s_channel_desc_d, s_channel_desc_q;
logic [NumChannels-1:0][31:0] s_channel_cmd_d, s_channel_cmd_q;
logic s_soft_reset_d, s_soft_reset_q;
logic s_abort_d, s_abort_q;
logic s_perf_clear_d, s_perf_clear_q;
logic s_ram_bist_start_d, s_ram_bist_start_q;
logic s_viewport_valid_d, s_viewport_valid_q;
logic s_viewport_write_d, s_viewport_write_q;
logic [5:0] s_viewport_addr_d, s_viewport_addr_q;
logic [7:0] s_viewport_write_data_d, s_viewport_write_data_q;
logic [7:0] s_viewport_read_data_d, s_viewport_read_data_q;
logic s_viewport_busy_d, s_viewport_busy_q;
logic s_viewport_err_d, s_viewport_err_q;
function automatic logic [31:0] apply_wstrb(input logic [31:0] prior_i, input logic [31:0] data_i,
input logic [3:0] strb_i);
logic [31:0] value;
begin
value = prior_i;
for (int unsigned byte_index = 0; byte_index < 4; byte_index++) begin
if (strb_i[byte_index]) begin
value[(byte_index*8)+:8] = data_i[(byte_index*8)+:8];
end
end
return value;
end
endfunction
assign s_offset = apb4.paddr[11:0];
assign s_endpoint_window = (s_offset >= `APB4_USB2__ENDPOINT_BASE) && (s_offset < 12'h400);
assign s_channel_window = (s_offset >= `APB4_USB2__CHANNEL_BASE) && (s_offset < 12'h900);
assign s_endpoint_delta = 9'(s_offset - `APB4_USB2__ENDPOINT_BASE);
assign s_channel_delta = 10'(s_offset - `APB4_USB2__CHANNEL_BASE);
assign s_endpoint_index = s_endpoint_delta[8:6];
assign s_channel_index = s_channel_delta[9:6];
assign s_endpoint_offset = {6'd0, s_endpoint_delta[5:0]};
assign s_channel_offset = {6'd0, s_channel_delta[5:0]};
assign s_irq_en_merged = 16'(apply_wstrb({16'd0, s_irq_en_q}, apb4.pwdata, apb4.pstrb));
assign s_req_accept = apb4.psel && apb4.penable && !s_apb4_ready_q;
assign s_write_access = s_req_accept && apb4.pwrite;
assign apb4.pready = s_apb4_ready_q;
assign apb4.prdata = s_apb4_rdata_q;
assign apb4.pslverr = s_apb4_resp_err_q;
assign enable_o = s_global_ctrl_q[`APB4_USB2__GLOBAL_CTRL_ENABLE];
assign soft_reset_o = s_soft_reset_q;
assign abort_o = s_abort_q;
assign force_role_o = s_role_ctrl_q[1:0];
assign auto_role_o = s_role_ctrl_q[`APB4_USB2__ROLE_CTRL_AUTO];
assign phy_reset_n_o = s_phy_ctrl_q[`APB4_USB2__PHY_CTRL_RESET_N];
assign phy_suspend_o = s_phy_ctrl_q[`APB4_USB2__PHY_CTRL_SUSPEND];
assign remote_wake_o = s_phy_ctrl_q[`APB4_USB2__PHY_CTRL_REMOTE_WAKE];
assign timeout_o = s_timeout_q;
assign test_ctrl_o = s_test_ctrl_q[7:0];
assign perf_clear_o = s_perf_clear_q;
assign device_addr_o = s_device_addr_q;
assign device_ctrl_o = s_device_ctrl_q[15:0];
assign host_ctrl_o = s_host_ctrl_q[15:0];
assign port_ctrl_o = s_port_ctrl_q[0];
assign schedule_ctrl_o = s_schedule_ctrl_q[0];
assign ram_ctrl_o = s_ram_ctrl_q[15:0];
assign ram_bist_start_o = s_ram_bist_start_q;
assign viewport_valid_o = s_viewport_valid_q;
assign viewport_write_o = s_viewport_write_q;
assign viewport_addr_o = s_viewport_addr_q;
assign viewport_write_data_o = s_viewport_write_data_q;
assign endpoint_complete_clear_in_o =
(s_write_access && !s_write_err &&
(s_offset == `APB4_USB2__ENDPOINT_COMPLETE_IN)) ?
apply_wstrb(
32'd0, apb4.pwdata, apb4.pstrb
) : 32'd0;
assign endpoint_complete_clear_out_o =
(s_write_access && !s_write_err &&
(s_offset == `APB4_USB2__ENDPOINT_COMPLETE_OUT)) ?
apply_wstrb(
32'd0, apb4.pwdata, apb4.pstrb
) : 32'd0;
assign irq_o = s_global_ctrl_q[`APB4_USB2__GLOBAL_CTRL_IRQ_ENABLE] &&
((s_irq_stat_q & s_irq_en_q) != 16'd0);
for (genvar endpoint = 0; endpoint < NumEndpoints; endpoint++) begin : gen_endpoint_outputs
assign endpoint_cfg_o[endpoint] = s_endpoint_cfg_q[endpoint];
assign endpoint_ram_in_o[endpoint] = s_endpoint_ram_in_q[endpoint];
assign endpoint_ram_out_o[endpoint] = s_endpoint_ram_out_q[endpoint];
assign endpoint_desc_in_o[endpoint] = s_endpoint_desc_in_q[endpoint];
assign endpoint_desc_out_o[endpoint] = s_endpoint_desc_out_q[endpoint];
assign endpoint_command_o[endpoint] = s_endpoint_cmd_q[endpoint];
end
for (genvar channel = 0; channel < NumChannels; channel++) begin : gen_channel_outputs
assign channel_cfg_o[channel] = s_channel_cfg_q[channel];
assign channel_target_o[channel] = s_channel_target_q[channel];
assign channel_interval_o[channel] = s_channel_interval_q[channel];
assign channel_ram_o[channel] = s_channel_ram_q[channel];
assign channel_desc_o[channel] = s_channel_desc_q[channel];
assign channel_command_o[channel] = s_channel_cmd_q[channel];
end
always_comb begin
s_read_data = 32'd0;
s_read_err = s_offset[1:0] != 2'b00;
if (!s_read_err) begin
if (s_endpoint_window) begin
unique case (s_endpoint_offset)
`APB4_USB2__ENDPOINT_CFG: s_read_data = s_endpoint_cfg_q[s_endpoint_index];
`APB4_USB2__ENDPOINT_RAM_IN: s_read_data = s_endpoint_ram_in_q[s_endpoint_index];
`APB4_USB2__ENDPOINT_RAM_OUT: s_read_data = s_endpoint_ram_out_q[s_endpoint_index];
`APB4_USB2__ENDPOINT_DESC_IN: s_read_data = s_endpoint_desc_in_q[s_endpoint_index];
`APB4_USB2__ENDPOINT_DESC_OUT: s_read_data = s_endpoint_desc_out_q[s_endpoint_index];
`APB4_USB2__ENDPOINT_COMMAND: s_read_data = 32'd0;
`APB4_USB2__ENDPOINT_STATUS: s_read_data = endpoint_status_i[s_endpoint_index];
`APB4_USB2__ENDPOINT_BYTES_IN: s_read_data = endpoint_bytes_in_i[s_endpoint_index];
`APB4_USB2__ENDPOINT_BYTES_OUT: s_read_data = endpoint_bytes_out_i[s_endpoint_index];
default: s_read_err = 1'b1;
endcase
end else if (s_channel_window) begin
unique case (s_channel_offset)
`APB4_USB2__CHANNEL_CFG: s_read_data = s_channel_cfg_q[s_channel_index];
`APB4_USB2__CHANNEL_TARGET: s_read_data = s_channel_target_q[s_channel_index];
`APB4_USB2__CHANNEL_INTERVAL: s_read_data = s_channel_interval_q[s_channel_index];
`APB4_USB2__CHANNEL_RAM: s_read_data = s_channel_ram_q[s_channel_index];
`APB4_USB2__CHANNEL_DESC: s_read_data = s_channel_desc_q[s_channel_index];
`APB4_USB2__CHANNEL_COMMAND: s_read_data = 32'd0;
`APB4_USB2__CHANNEL_STATUS: s_read_data = channel_status_i[s_channel_index];
`APB4_USB2__CHANNEL_BYTES: s_read_data = channel_bytes_i[s_channel_index];
default: s_read_err = 1'b1;
endcase
end else begin
unique case (s_offset)
`APB4_USB2__IP_ID: s_read_data = IpId;
`APB4_USB2__IP_VERSION: s_read_data = IpVersion;
`APB4_USB2__CAPABILITY0: s_read_data = Capability0;
`APB4_USB2__CAPABILITY1: s_read_data = Capability1;
`APB4_USB2__GLOBAL_CTRL: s_read_data = {28'd0, s_global_ctrl_q};
`APB4_USB2__GLOBAL_STATUS: s_read_data = global_status_i;
`APB4_USB2__ROLE_CTRL: s_read_data = {29'd0, s_role_ctrl_q};
`APB4_USB2__ROLE_STATUS: s_read_data = role_status_i;
`APB4_USB2__PHY_CTRL: s_read_data = {29'd0, s_phy_ctrl_q};
`APB4_USB2__PHY_STATUS: s_read_data = phy_status_i;
`APB4_USB2__ULPI_VIEWPORT:
s_read_data = {
s_viewport_busy_q, 1'b0, s_viewport_addr_q, 16'd0, s_viewport_write_data_q
};
`APB4_USB2__ULPI_VIEWPORT_DATA:
s_read_data = {23'd0, s_viewport_err_q, s_viewport_read_data_q};
`APB4_USB2__TIMEOUT: s_read_data = s_timeout_q;
`APB4_USB2__FRAME: s_read_data = frame_i;
`APB4_USB2__TEST_CTRL: s_read_data = s_test_ctrl_q;
`APB4_USB2__PIO_DATA: begin
s_read_data = 32'd0;
s_read_err = 1'b1;
end
`APB4_USB2__IRQ_STATUS: s_read_data = {16'd0, s_irq_stat_q};
`APB4_USB2__IRQ_ENABLE: s_read_data = {16'd0, s_irq_en_q};
`APB4_USB2__IRQ_TEST: s_read_data = 32'd0;
`APB4_USB2__ERROR_STATUS: s_read_data = s_err_stat_q;
`APB4_USB2__ERROR_CODE: s_read_data = s_err_code_q;
`APB4_USB2__ERROR_INFO: s_read_data = s_err_info_q;
`APB4_USB2__ERROR_DESC_ADDR: s_read_data = s_err_desc_addr_q;
`APB4_USB2__ERROR_BUFFER_ADDR: s_read_data = s_err_buffer_addr_q;
`APB4_USB2__PERF_TX_BYTES: s_read_data = perf_tx_bytes_i;
`APB4_USB2__PERF_RX_BYTES: s_read_data = perf_rx_bytes_i;
`APB4_USB2__PERF_PACKETS: s_read_data = perf_packets_i;
`APB4_USB2__PERF_RETRIES: s_read_data = perf_retries_i;
`APB4_USB2__PERF_AXI_STALL: s_read_data = perf_axi_stall_i;
`APB4_USB2__PERF_RAM_STALL: s_read_data = perf_ram_stall_i;
`APB4_USB2__PERF_IRQ_COUNT: s_read_data = perf_irq_count_i;
`APB4_USB2__PERF_CTRL: s_read_data = 32'd0;
`APB4_USB2__DEVICE_CTRL: s_read_data = s_device_ctrl_q;
`APB4_USB2__DEVICE_ADDR: s_read_data = {25'd0, s_device_addr_q};
`APB4_USB2__DEVICE_STATUS: s_read_data = device_status_i;
`APB4_USB2__SETUP0: s_read_data = setup_packet_i[31:0];
`APB4_USB2__SETUP1: s_read_data = setup_packet_i[63:32];
`APB4_USB2__ENDPOINT_PENDING_IN: s_read_data = endpoint_pending_in_i;
`APB4_USB2__ENDPOINT_PENDING_OUT: s_read_data = endpoint_pending_out_i;
`APB4_USB2__ENDPOINT_COMPLETE_IN: s_read_data = endpoint_complete_in_i;
`APB4_USB2__ENDPOINT_COMPLETE_OUT: s_read_data = endpoint_complete_out_i;
`APB4_USB2__HOST_CTRL: s_read_data = s_host_ctrl_q;
`APB4_USB2__HOST_STATUS: s_read_data = host_status_i;
`APB4_USB2__PORT_CTRL: s_read_data = s_port_ctrl_q;
`APB4_USB2__PORT_STATUS: s_read_data = port_status_i;
`APB4_USB2__SCHEDULE_CTRL: s_read_data = s_schedule_ctrl_q;
`APB4_USB2__SCHEDULE_STATUS: s_read_data = schedule_status_i;
`APB4_USB2__RAM_CTRL: s_read_data = s_ram_ctrl_q;
`APB4_USB2__RAM_STATUS: s_read_data = ram_status_i;
`APB4_USB2__ECC_STATUS: s_read_data = ecc_status_i;
`APB4_USB2__ECC_CORRECTED_COUNT: s_read_data = ecc_corrected_count_i;
`APB4_USB2__ECC_UNCORRECTABLE_COUNT: s_read_data = ecc_uncorrectable_count_i;
`APB4_USB2__RAM_BIST: s_read_data = 32'd0;
`APB4_USB2__DEBUG_STATUS: s_read_data = debug_status_i;
default: s_read_err = 1'b1;
endcase
end
end
end
always_comb begin
s_write_err = s_offset[1:0] != 2'b00;
if (!s_write_err) begin
if (s_endpoint_window) begin
unique case (s_endpoint_offset)
`APB4_USB2__ENDPOINT_CFG,
`APB4_USB2__ENDPOINT_RAM_IN,
`APB4_USB2__ENDPOINT_RAM_OUT,
`APB4_USB2__ENDPOINT_DESC_IN,
`APB4_USB2__ENDPOINT_DESC_OUT:
s_write_err = enable_o;
`APB4_USB2__ENDPOINT_COMMAND: s_write_err = apb4.pstrb != 4'b1111;
default: s_write_err = 1'b1;
endcase
end else if (s_channel_window) begin
unique case (s_channel_offset)
`APB4_USB2__CHANNEL_CFG,
`APB4_USB2__CHANNEL_TARGET,
`APB4_USB2__CHANNEL_INTERVAL,
`APB4_USB2__CHANNEL_RAM,
`APB4_USB2__CHANNEL_DESC:
s_write_err = enable_o;
`APB4_USB2__CHANNEL_COMMAND: s_write_err = apb4.pstrb != 4'b1111;
default: s_write_err = 1'b1;
endcase
end else begin
unique case (s_offset)
`APB4_USB2__GLOBAL_CTRL,
`APB4_USB2__IRQ_STATUS,
`APB4_USB2__IRQ_ENABLE,
`APB4_USB2__IRQ_TEST,
`APB4_USB2__ERROR_STATUS,
`APB4_USB2__PERF_CTRL,
`APB4_USB2__DEVICE_CTRL,
`APB4_USB2__DEVICE_ADDR,
`APB4_USB2__ENDPOINT_COMPLETE_IN,
`APB4_USB2__ENDPOINT_COMPLETE_OUT,
`APB4_USB2__HOST_CTRL,
`APB4_USB2__PORT_CTRL,
`APB4_USB2__SCHEDULE_CTRL,
`APB4_USB2__RAM_CTRL,
`APB4_USB2__RAM_BIST: begin
end
`APB4_USB2__ROLE_CTRL, `APB4_USB2__PHY_CTRL, `APB4_USB2__TIMEOUT, `APB4_USB2__TEST_CTRL:
s_write_err = enable_o;
`APB4_USB2__ULPI_VIEWPORT: begin
s_write_err = s_viewport_busy_q || !apb4.pwdata[31] ||
(apb4.pwdata[29:22] != 8'd0) || (apb4.pwdata[15:8] != 8'd0) ||
(apb4.pstrb != 4'b1111);
end
`APB4_USB2__PIO_DATA: s_write_err = 1'b1;
default: s_write_err = 1'b1;
endcase
end
end
end
always_comb begin
s_apb4_ready_d = s_req_accept;
s_apb4_rdata_d = s_apb4_rdata_q;
s_apb4_resp_err_d = 1'b0;
if (s_req_accept) begin
if (apb4.pwrite) begin
s_apb4_rdata_d = 32'd0;
s_apb4_resp_err_d = s_write_err;
end else begin
s_apb4_rdata_d = s_read_data;
s_apb4_resp_err_d = s_read_err;
end
end
end
always_comb begin
s_global_ctrl_d = s_global_ctrl_q;
s_role_ctrl_d = s_role_ctrl_q;
s_phy_ctrl_d = s_phy_ctrl_q;
s_timeout_d = s_timeout_q;
s_test_ctrl_d = s_test_ctrl_q;
s_device_ctrl_d = s_device_ctrl_q;
s_device_addr_d = s_device_addr_q;
s_host_ctrl_d = s_host_ctrl_q;
s_port_ctrl_d = s_port_ctrl_q;
s_schedule_ctrl_d = s_schedule_ctrl_q;
s_ram_ctrl_d = s_ram_ctrl_q;
s_irq_stat_d = s_irq_stat_q | irq_event_i;
s_irq_en_d = s_irq_en_q;
s_err_stat_d = s_err_stat_q | error_status_i;
s_err_code_d = s_err_code_q;
s_err_info_d = s_err_info_q;
s_err_desc_addr_d = s_err_desc_addr_q;
s_err_buffer_addr_d = s_err_buffer_addr_q;
s_soft_reset_d = 1'b0;
s_abort_d = 1'b0;
s_perf_clear_d = 1'b0;
s_ram_bist_start_d = 1'b0;
s_viewport_valid_d = 1'b0;
s_viewport_write_d = s_viewport_write_q;
s_viewport_addr_d = s_viewport_addr_q;
s_viewport_write_data_d = s_viewport_write_data_q;
s_viewport_read_data_d = s_viewport_read_data_q;
s_viewport_busy_d = s_viewport_busy_q;
s_viewport_err_d = s_viewport_err_q;
for (int endpoint = 0; endpoint < NumEndpoints; endpoint++) begin
s_endpoint_cfg_d[endpoint] = s_endpoint_cfg_q[endpoint];
s_endpoint_ram_in_d[endpoint] = s_endpoint_ram_in_q[endpoint];
s_endpoint_ram_out_d[endpoint] = s_endpoint_ram_out_q[endpoint];
s_endpoint_desc_in_d[endpoint] = s_endpoint_desc_in_q[endpoint];
s_endpoint_desc_out_d[endpoint] = s_endpoint_desc_out_q[endpoint];
s_endpoint_cmd_d[endpoint] = 32'd0;
end
for (int channel = 0; channel < NumChannels; channel++) begin
s_channel_cfg_d[channel] = s_channel_cfg_q[channel];
s_channel_target_d[channel] = s_channel_target_q[channel];
s_channel_interval_d[channel] = s_channel_interval_q[channel];
s_channel_ram_d[channel] = s_channel_ram_q[channel];
s_channel_desc_d[channel] = s_channel_desc_q[channel];
s_channel_cmd_d[channel] = 32'd0;
end
if (viewport_resp_valid_i) begin
s_viewport_busy_d = 1'b0;
s_viewport_read_data_d = viewport_read_data_i;
s_viewport_err_d = viewport_error_i;
end
if (error_capture_i && (s_err_stat_q == 32'd0)) begin
s_err_code_d = error_code_i;
s_err_info_d = error_info_i;
s_err_desc_addr_d = error_desc_addr_i;
s_err_buffer_addr_d = error_buffer_addr_i;
end
if (s_write_access && !s_write_err) begin
if (s_endpoint_window) begin
unique case (s_endpoint_offset)
`APB4_USB2__ENDPOINT_CFG:
s_endpoint_cfg_d[s_endpoint_index] =
apply_wstrb(s_endpoint_cfg_q[s_endpoint_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__ENDPOINT_RAM_IN:
s_endpoint_ram_in_d[s_endpoint_index] =
apply_wstrb(s_endpoint_ram_in_q[s_endpoint_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__ENDPOINT_RAM_OUT:
s_endpoint_ram_out_d[s_endpoint_index] =
apply_wstrb(s_endpoint_ram_out_q[s_endpoint_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__ENDPOINT_DESC_IN:
s_endpoint_desc_in_d[s_endpoint_index] =
apply_wstrb(s_endpoint_desc_in_q[s_endpoint_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__ENDPOINT_DESC_OUT:
s_endpoint_desc_out_d[s_endpoint_index] =
apply_wstrb(s_endpoint_desc_out_q[s_endpoint_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__ENDPOINT_COMMAND: s_endpoint_cmd_d[s_endpoint_index] = apb4.pwdata;
default: begin
end
endcase
end else if (s_channel_window) begin
unique case (s_channel_offset)
`APB4_USB2__CHANNEL_CFG:
s_channel_cfg_d[s_channel_index] =
apply_wstrb(s_channel_cfg_q[s_channel_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__CHANNEL_TARGET:
s_channel_target_d[s_channel_index] =
apply_wstrb(s_channel_target_q[s_channel_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__CHANNEL_INTERVAL:
s_channel_interval_d[s_channel_index] =
apply_wstrb(s_channel_interval_q[s_channel_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__CHANNEL_RAM:
s_channel_ram_d[s_channel_index] =
apply_wstrb(s_channel_ram_q[s_channel_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__CHANNEL_DESC:
s_channel_desc_d[s_channel_index] =
apply_wstrb(s_channel_desc_q[s_channel_index], apb4.pwdata, apb4.pstrb);
`APB4_USB2__CHANNEL_COMMAND: s_channel_cmd_d[s_channel_index] = apb4.pwdata;
default: begin
end
endcase
end else begin
unique case (s_offset)
`APB4_USB2__GLOBAL_CTRL: begin
if (apb4.pstrb[0]) begin
s_global_ctrl_d[3:0] = apb4.pwdata[3:0];
s_soft_reset_d = apb4.pwdata[`APB4_USB2__GLOBAL_CTRL_SOFT_RESET];
s_abort_d = apb4.pwdata[`APB4_USB2__GLOBAL_CTRL_ABORT];
s_global_ctrl_d[`APB4_USB2__GLOBAL_CTRL_SOFT_RESET] = 1'b0;
s_global_ctrl_d[`APB4_USB2__GLOBAL_CTRL_ABORT] = 1'b0;
end
end
`APB4_USB2__ROLE_CTRL: if (apb4.pstrb[0]) s_role_ctrl_d = apb4.pwdata[2:0];
`APB4_USB2__PHY_CTRL: if (apb4.pstrb[0]) s_phy_ctrl_d = apb4.pwdata[2:0];
`APB4_USB2__ULPI_VIEWPORT: begin
s_viewport_valid_d = 1'b1;
s_viewport_busy_d = 1'b1;
s_viewport_err_d = 1'b0;
s_viewport_write_d = apb4.pwdata[30];
s_viewport_addr_d = apb4.pwdata[21:16];
s_viewport_write_data_d = apb4.pwdata[7:0];
end
`APB4_USB2__TIMEOUT: s_timeout_d = apply_wstrb(s_timeout_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__TEST_CTRL:
s_test_ctrl_d = apply_wstrb(s_test_ctrl_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__IRQ_STATUS: s_irq_stat_d = (s_irq_stat_q & ~apb4.pwdata[15:0]) | irq_event_i;
`APB4_USB2__IRQ_ENABLE: s_irq_en_d = s_irq_en_merged[15:0];
`APB4_USB2__IRQ_TEST: s_irq_stat_d = s_irq_stat_q | apb4.pwdata[15:0] | irq_event_i;
`APB4_USB2__ERROR_STATUS: begin
s_err_stat_d = (s_err_stat_q & ~apb4.pwdata) | error_status_i;
if ((s_err_stat_q & ~apb4.pwdata) == 32'd0) begin
s_err_code_d = 32'd0;
s_err_info_d = 32'd0;
s_err_desc_addr_d = 32'd0;
s_err_buffer_addr_d = 32'd0;
end
end
`APB4_USB2__PERF_CTRL: s_perf_clear_d = apb4.pwdata[0];
`APB4_USB2__DEVICE_CTRL:
s_device_ctrl_d = apply_wstrb(s_device_ctrl_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__DEVICE_ADDR: if (apb4.pstrb[0]) s_device_addr_d = apb4.pwdata[6:0];
`APB4_USB2__HOST_CTRL:
s_host_ctrl_d = apply_wstrb(s_host_ctrl_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__PORT_CTRL:
s_port_ctrl_d = apply_wstrb(s_port_ctrl_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__SCHEDULE_CTRL:
s_schedule_ctrl_d = apply_wstrb(s_schedule_ctrl_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__RAM_CTRL: s_ram_ctrl_d = apply_wstrb(s_ram_ctrl_q, apb4.pwdata, apb4.pstrb);
`APB4_USB2__RAM_BIST: s_ram_bist_start_d = apb4.pwdata[0];
default: begin
end
endcase
end
end
end
dffr #(
.DATA_WIDTH(1)
) u_apb4_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_ready_d),
.dat_o (s_apb4_ready_q)
);
dffr #(
.DATA_WIDTH(32)
) u_apb4_rdata_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_rdata_d),
.dat_o (s_apb4_rdata_q)
);
dffr #(
.DATA_WIDTH(1)
) u_apb4_resp_err_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_apb4_resp_err_d),
.dat_o (s_apb4_resp_err_q)
);
dffr #(
.DATA_WIDTH(4)
) u_global_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_global_ctrl_d),
.dat_o (s_global_ctrl_q)
);
dffr #(
.DATA_WIDTH(3)
) u_role_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_role_ctrl_d),
.dat_o (s_role_ctrl_q)
);
dffrc #(
.DATA_WIDTH(3),
.RESET_VAL (3'b001)
) u_phy_ctrl_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_phy_ctrl_d),
.dat_o (s_phy_ctrl_q)
);
dffrc #(
.DATA_WIDTH(32),
.RESET_VAL (32'd720000)
) u_timeout_dffrc (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_timeout_d),
.dat_o (s_timeout_q)
);
dffr #(
.DATA_WIDTH(32)
) u_test_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_test_ctrl_d),
.dat_o (s_test_ctrl_q)
);
dffr #(
.DATA_WIDTH(32)
) u_device_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_ctrl_d),
.dat_o (s_device_ctrl_q)
);
dffr #(
.DATA_WIDTH(7)
) u_device_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_addr_d),
.dat_o (s_device_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_host_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_host_ctrl_d),
.dat_o (s_host_ctrl_q)
);
dffr #(
.DATA_WIDTH(32)
) u_port_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_port_ctrl_d),
.dat_o (s_port_ctrl_q)
);
dffr #(
.DATA_WIDTH(32)
) u_schedule_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_schedule_ctrl_d),
.dat_o (s_schedule_ctrl_q)
);
dffr #(
.DATA_WIDTH(32)
) u_ram_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ram_ctrl_d),
.dat_o (s_ram_ctrl_q)
);
dffr #(
.DATA_WIDTH(16)
) u_irq_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_stat_d),
.dat_o (s_irq_stat_q)
);
dffr #(
.DATA_WIDTH(16)
) u_irq_enable_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_en_d),
.dat_o (s_irq_en_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_stat_d),
.dat_o (s_err_stat_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_code_d),
.dat_o (s_err_code_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_info_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_info_d),
.dat_o (s_err_info_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_desc_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_desc_addr_d),
.dat_o (s_err_desc_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_buffer_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_buffer_addr_d),
.dat_o (s_err_buffer_addr_q)
);
dffr #(
.DATA_WIDTH(1)
) u_soft_reset_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_soft_reset_d),
.dat_o (s_soft_reset_q)
);
dffr #(
.DATA_WIDTH(1)
) u_abort_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_abort_d),
.dat_o (s_abort_q)
);
dffr #(
.DATA_WIDTH(1)
) u_perf_clear_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_clear_d),
.dat_o (s_perf_clear_q)
);
dffr #(
.DATA_WIDTH(1)
) u_ram_bist_start_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ram_bist_start_d),
.dat_o (s_ram_bist_start_q)
);
dffr #(
.DATA_WIDTH(1)
) u_viewport_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_valid_d),
.dat_o (s_viewport_valid_q)
);
dffr #(
.DATA_WIDTH(1)
) u_viewport_write_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_write_d),
.dat_o (s_viewport_write_q)
);
dffr #(
.DATA_WIDTH(6)
) u_viewport_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_addr_d),
.dat_o (s_viewport_addr_q)
);
dffr #(
.DATA_WIDTH(8)
) u_viewport_write_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_write_data_d),
.dat_o (s_viewport_write_data_q)
);
dffr #(
.DATA_WIDTH(8)
) u_viewport_read_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_read_data_d),
.dat_o (s_viewport_read_data_q)
);
dffr #(
.DATA_WIDTH(1)
) u_viewport_busy_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_busy_d),
.dat_o (s_viewport_busy_q)
);
dffr #(
.DATA_WIDTH(1)
) u_viewport_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_viewport_err_d),
.dat_o (s_viewport_err_q)
);
for (genvar endpoint = 0; endpoint < NumEndpoints; endpoint++) begin : gen_endpoint_registers
dffr #(
.DATA_WIDTH(32)
) u_cfg_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_cfg_d[endpoint]),
.dat_o (s_endpoint_cfg_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_ram_in_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_ram_in_d[endpoint]),
.dat_o (s_endpoint_ram_in_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_ram_out_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_ram_out_d[endpoint]),
.dat_o (s_endpoint_ram_out_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_desc_in_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_desc_in_d[endpoint]),
.dat_o (s_endpoint_desc_in_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_desc_out_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_desc_out_d[endpoint]),
.dat_o (s_endpoint_desc_out_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_command_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_cmd_d[endpoint]),
.dat_o (s_endpoint_cmd_q[endpoint])
);
end
for (genvar channel = 0; channel < NumChannels; channel++) begin : gen_channel_registers
dffr #(
.DATA_WIDTH(32)
) u_cfg_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_cfg_d[channel]),
.dat_o (s_channel_cfg_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_target_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_target_d[channel]),
.dat_o (s_channel_target_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_interval_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_interval_d[channel]),
.dat_o (s_channel_interval_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_ram_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_ram_d[channel]),
.dat_o (s_channel_ram_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_desc_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_desc_d[channel]),
.dat_o (s_channel_desc_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_command_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_cmd_d[channel]),
.dat_o (s_channel_cmd_q[channel])
);
end
`ifndef SYNTHESIS
initial begin
if ((NumEndpoints != 8) || (NumChannels != 16)) begin
$fatal(1, "usb2_reg: delivered ABI requires 8 endpoints and 16 channels");
end
end
`endif
endmodule
+109
View File
@@ -0,0 +1,109 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_role_controller #(
parameter int ResetCycles = 8
) (
// verilog_format: off -- preserve control, PHY sense, and status groups
input logic clk_i,
input logic rst_n_i,
input logic enable_i,
input logic soft_reset_i,
input logic auto_role_i,
input logic [1:0] force_role_i,
input logic id_ground_i,
input logic vbus_valid_i,
input logic transaction_busy_i,
output usb2_pkg::usb2_role_e active_role_o,
output logic role_change_o,
output logic role_reset_o,
output logic switch_pending_o
// verilog_format: on
);
localparam int ResetCountWidth = (ResetCycles > 1) ? $clog2(ResetCycles + 1) : 1;
usb2_pkg::usb2_role_e s_requested_role;
usb2_pkg::usb2_role_e s_active_role_d, s_active_role_q;
logic [1:0] s_active_role_bits_q;
logic [ResetCountWidth-1:0] s_reset_count_d, s_reset_count_q;
logic s_role_change_d, s_role_change_q;
always_comb begin
if (!enable_i) begin
s_requested_role = usb2_pkg::Usb2RoleIdle;
end else if (auto_role_i) begin
if (id_ground_i) begin
s_requested_role = usb2_pkg::Usb2RoleHost;
end else if (vbus_valid_i) begin
s_requested_role = usb2_pkg::Usb2RoleDevice;
end else begin
s_requested_role = usb2_pkg::Usb2RoleIdle;
end
end else begin
unique case (force_role_i)
2'd1: s_requested_role = usb2_pkg::Usb2RoleDevice;
2'd2: s_requested_role = usb2_pkg::Usb2RoleHost;
default: s_requested_role = usb2_pkg::Usb2RoleIdle;
endcase
end
end
assign s_active_role_q = usb2_pkg::usb2_role_e'(s_active_role_bits_q);
assign active_role_o = s_active_role_q;
assign role_change_o = s_role_change_q;
assign role_reset_o = soft_reset_i || (s_reset_count_q != '0);
assign switch_pending_o = s_requested_role != s_active_role_q;
always_comb begin
s_active_role_d = s_active_role_q;
s_reset_count_d = s_reset_count_q;
s_role_change_d = 1'b0;
if (s_reset_count_q != '0) begin
s_reset_count_d = s_reset_count_q - 1'b1;
end
if (soft_reset_i) begin
s_active_role_d = usb2_pkg::Usb2RoleIdle;
s_reset_count_d = ResetCountWidth'(ResetCycles);
s_role_change_d = s_active_role_q != usb2_pkg::Usb2RoleIdle;
end else if ((s_requested_role != s_active_role_q) && !transaction_busy_i &&
(s_reset_count_q == '0)) begin
s_active_role_d = s_requested_role;
s_reset_count_d = ResetCountWidth'(ResetCycles);
s_role_change_d = 1'b1;
end
end
dffr #(
.DATA_WIDTH(2)
) u_active_role_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_active_role_d),
.dat_o (s_active_role_bits_q)
);
dffr #(
.DATA_WIDTH(ResetCountWidth)
) u_reset_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_reset_count_d),
.dat_o (s_reset_count_q)
);
dffr #(
.DATA_WIDTH(1)
) u_role_change_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_role_change_d),
.dat_o (s_role_change_q)
);
`ifndef SYNTHESIS
initial begin
if (ResetCycles < 2) begin
$fatal(1, "usb2_role_controller: ResetCycles must be at least two");
end
end
`endif
endmodule
+926
View File
@@ -0,0 +1,926 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "usb2_define.svh"
module usb2_scheduler #(
parameter int NumEndpoints = 8,
parameter int NumChannels = 16
) (
// verilog_format: off -- preserve register, mailbox, DMA, and status groups
input logic clk_i,
input logic rst_n_i,
input logic enable_i,
input logic abort_i,
input logic reinitialize_i,
input logic perf_clear_i,
input logic high_speed_i,
input logic [13:0] frame_i,
input usb2_pkg::usb2_role_e active_role_i,
input logic [NumEndpoints-1:0][31:0] endpoint_cfg_i,
input logic [NumEndpoints-1:0][31:0] endpoint_ram_in_i,
input logic [NumEndpoints-1:0][31:0] endpoint_ram_out_i,
input logic [NumEndpoints-1:0][31:0] endpoint_desc_in_i,
input logic [NumEndpoints-1:0][31:0] endpoint_desc_out_i,
input logic [NumEndpoints-1:0][31:0] endpoint_command_i,
input logic [31:0] endpoint_complete_clear_in_i,
input logic [31:0] endpoint_complete_clear_out_i,
input logic [NumChannels-1:0][31:0] channel_cfg_i,
input logic [NumChannels-1:0][31:0] channel_target_i,
input logic [NumChannels-1:0][31:0] channel_interval_i,
input logic [NumChannels-1:0][31:0] channel_ram_i,
input logic [NumChannels-1:0][31:0] channel_desc_i,
input logic [NumChannels-1:0][31:0] channel_command_i,
output logic work_valid_o,
input logic work_ready_i,
output logic [63:0] work_data_o,
input logic result_valid_i,
output logic result_ready_o,
input logic [63:0] result_data_i,
output logic fill_cmd_valid_o,
input logic fill_cmd_ready_i,
output logic [26:0] fill_cmd_data_o,
output logic drain_cmd_valid_o,
input logic drain_cmd_ready_i,
output logic [26:0] drain_cmd_data_o,
input logic buffer_event_valid_i,
output logic buffer_event_ready_o,
input logic [2:0] buffer_event_data_i,
output logic dma_start_o,
output logic dma_abort_o,
output logic dma_memory_to_packet_o,
output logic [31:0] dma_desc_base_o,
output logic [31:0] dma_transfer_bytes_o,
input logic dma_busy_i,
input logic dma_done_i,
input logic dma_error_i,
input logic [7:0] dma_error_code_i,
input logic [31:0] dma_current_desc_i,
input logic [31:0] dma_bytes_done_i,
input logic retry_i,
output logic [31:0] endpoint_pending_in_o,
output logic [31:0] endpoint_pending_out_o,
output logic [31:0] endpoint_complete_in_o,
output logic [31:0] endpoint_complete_out_o,
output logic [NumEndpoints-1:0][31:0] endpoint_status_o,
output logic [NumEndpoints-1:0][31:0] endpoint_bytes_in_o,
output logic [NumEndpoints-1:0][31:0] endpoint_bytes_out_o,
output logic [NumChannels-1:0][31:0] channel_status_o,
output logic [NumChannels-1:0][31:0] channel_bytes_o,
output logic [15:0] irq_event_o,
output logic error_capture_o,
output logic [31:0] error_status_o,
output logic [31:0] error_code_o,
output logic [31:0] error_info_o,
output logic [31:0] error_desc_addr_o,
output logic [31:0] error_buffer_addr_o,
output logic [31:0] perf_tx_bytes_o,
output logic [31:0] perf_rx_bytes_o,
output logic [31:0] perf_packets_o,
output logic [31:0] perf_retries_o,
output logic [31:0] perf_irq_count_o,
output logic busy_o
// verilog_format: on
);
typedef enum logic [3:0] {
Idle,
FillCommand,
DmaStart,
TransferWait,
WorkSend,
ResultWait,
DrainCommand,
DrainDmaStart,
DrainWait,
Complete
} scheduler_state_e;
scheduler_state_e s_state_d, s_state_q;
logic [3:0] s_state_bits_q;
logic [63:0] s_work_d, s_work_q;
logic [31:0] s_desc_d, s_desc_q;
logic [11:0] s_ram_base_d, s_ram_base_q;
logic [14:0] s_len_d, s_len_q;
logic s_wait_result_d, s_wait_result_q;
logic s_dma_seen_d, s_dma_seen_q;
logic s_buffer_seen_d, s_buffer_seen_q;
logic s_fault_d, s_fault_q;
logic [7:0] s_fault_code_d, s_fault_code_q;
logic [3:0] s_completion_code_d, s_completion_code_q;
logic [31:0] s_pending_in_d, s_pending_in_q;
logic [31:0] s_pending_out_d, s_pending_out_q;
logic [31:0] s_complete_in_d, s_complete_in_q;
logic [31:0] s_complete_out_d, s_complete_out_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_stat_d, s_endpoint_stat_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_bytes_in_d, s_endpoint_bytes_in_q;
logic [NumEndpoints-1:0][31:0] s_endpoint_bytes_out_d, s_endpoint_bytes_out_q;
logic [NumChannels-1:0][31:0] s_channel_stat_d, s_channel_stat_q;
logic [NumChannels-1:0][31:0] s_channel_bytes_d, s_channel_bytes_q;
logic [31:0] s_perf_tx_bytes_d, s_perf_tx_bytes_q;
logic [31:0] s_perf_rx_bytes_d, s_perf_rx_bytes_q;
logic [31:0] s_perf_packets_d, s_perf_packets_q;
logic [31:0] s_perf_retries_d, s_perf_retries_q;
logic [31:0] s_perf_irq_count_d, s_perf_irq_count_q;
logic [NumEndpoints-1:0][ 3:0] s_endpoint_cmd_pending_d;
logic [NumEndpoints-1:0][ 3:0] s_endpoint_cmd_pending_q;
logic [ NumChannels-1:0][ 3:0] s_channel_cmd_pending_d;
logic [ NumChannels-1:0][ 3:0] s_channel_cmd_pending_q;
logic [ NumChannels-1:0][13:0] s_channel_next_frame_d;
logic [ NumChannels-1:0][13:0] s_channel_next_frame_q;
logic [NumEndpoints-1:0][ 3:0] s_endpoint_cmd_merged;
logic [ NumChannels-1:0][ 3:0] s_channel_cmd_merged;
logic [ NumChannels-1:0] s_channel_due;
logic [15:0] s_irq_event_d, s_irq_event_q;
logic s_err_capture_d, s_err_capture_q;
logic [31:0] s_err_stat_d, s_err_stat_q;
logic [31:0] s_err_code_d, s_err_code_q;
logic [31:0] s_err_info_d, s_err_info_q;
logic [31:0] s_err_desc_addr_d, s_err_desc_addr_q;
logic [31:0] s_err_buffer_addr_d, s_err_buffer_addr_q;
logic s_dma_start_d, s_dma_start_q;
logic s_cmd_valid;
logic s_cmd_is_channel;
logic [ 3:0] s_cmd_index;
logic [ 3:0] s_cmd_value;
logic [ 1:0] s_result_role;
logic [ 3:0] s_result_index;
logic s_result_direction_in;
logic [ 3:0] s_result_code;
logic [14:0] s_result_len;
logic s_current_direction_in;
logic [ 1:0] s_current_role;
logic [ 3:0] s_current_index;
logic s_transfer_fault;
logic s_buffer_event_match;
function automatic logic frame_reached(input logic [13:0] current_i, input logic [13:0] target_i);
return $signed(current_i - target_i) >= 0;
endfunction
for (genvar endpoint = 0; endpoint < NumEndpoints; endpoint++) begin : gen_command_merge
assign s_endpoint_cmd_merged[endpoint] =
s_endpoint_cmd_pending_q[endpoint] | endpoint_command_i[endpoint][3:0];
end
for (genvar channel = 0; channel < NumChannels; channel++) begin : gen_channel_schedule
assign s_channel_cmd_merged[channel] =
s_channel_cmd_pending_q[channel] | channel_command_i[channel][3:0];
assign s_channel_due[channel] =
((channel_cfg_i[channel][3:2] != usb2_pkg::Usb2TransferIsochronous) &&
(channel_cfg_i[channel][3:2] != usb2_pkg::Usb2TransferInterrupt)) ||
(channel_interval_i[channel][13:0] == 14'd0) ||
frame_reached(
frame_i, s_channel_next_frame_q[channel]
);
end
function automatic logic [63:0] make_work(
input logic [1:0] role_i, input logic [3:0] index_i, input logic direction_in_i,
input logic [1:0] type_i, input logic [6:0] address_i, input logic [3:0] endpoint_i,
input logic [1:0] speed_i, input logic [11:0] ram_base_i, input logic [14:0] length_i,
input logic setup_i, input logic cancel_i, input logic toggle_i);
logic [63:0] value;
begin
value = '0;
value[usb2_pkg::USB2_WORK_ROLE_LSB+:2] = role_i;
value[usb2_pkg::USB2_WORK_INDEX_LSB+:4] = index_i;
value[usb2_pkg::USB2_WORK_DIRECTION_IN] = direction_in_i;
value[usb2_pkg::USB2_WORK_TYPE_LSB+:2] = type_i;
value[usb2_pkg::USB2_WORK_ADDRESS_LSB+:7] = address_i;
value[usb2_pkg::USB2_WORK_ENDPOINT_LSB+:4] = endpoint_i;
value[usb2_pkg::USB2_WORK_SPEED_LSB+:2] = speed_i;
value[usb2_pkg::USB2_WORK_RAM_BASE_LSB+:12] = ram_base_i;
value[usb2_pkg::USB2_WORK_LENGTH_LSB+:15] = length_i;
value[usb2_pkg::USB2_WORK_SETUP] = setup_i;
value[usb2_pkg::USB2_WORK_CANCEL] = cancel_i;
value[usb2_pkg::USB2_WORK_TOGGLE] = toggle_i;
return value;
end
endfunction
always_comb begin
s_cmd_valid = 1'b0;
s_cmd_is_channel = 1'b0;
s_cmd_index = '0;
s_cmd_value = '0;
for (int endpoint = NumEndpoints - 1; endpoint >= 0; endpoint--) begin
if (s_endpoint_cmd_merged[endpoint] != 4'd0) begin
s_cmd_valid = 1'b1;
s_cmd_is_channel = 1'b0;
s_cmd_index = 4'(endpoint);
s_cmd_value = s_endpoint_cmd_merged[endpoint];
end
end
for (int channel = NumChannels - 1; channel >= 0; channel--) begin
if (!s_cmd_valid && (s_channel_cmd_merged[channel] != 4'd0) && s_channel_due[channel]) begin
s_cmd_valid = 1'b1;
s_cmd_is_channel = 1'b1;
s_cmd_index = 4'(channel);
s_cmd_value = s_channel_cmd_merged[channel];
end
end
end
assign s_state_q = scheduler_state_e'(s_state_bits_q);
assign s_result_role = result_data_i[usb2_pkg::USB2_RESULT_ROLE_LSB+:2];
assign s_result_index = result_data_i[usb2_pkg::USB2_RESULT_INDEX_LSB+:4];
assign s_result_direction_in = result_data_i[usb2_pkg::USB2_RESULT_DIRECTION_IN];
assign s_result_code = result_data_i[usb2_pkg::USB2_RESULT_CODE_LSB+:4];
assign s_result_len = result_data_i[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15];
assign s_current_role = s_work_q[usb2_pkg::USB2_WORK_ROLE_LSB+:2];
assign s_current_index = s_work_q[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
assign s_current_direction_in = s_work_q[usb2_pkg::USB2_WORK_DIRECTION_IN];
assign s_buffer_event_match = buffer_event_valid_i &&
(((s_state_q == TransferWait) &&
(buffer_event_data_i[1:0] == 2'd0)) ||
((s_state_q == DrainWait) &&
(buffer_event_data_i[1:0] == 2'd1)));
assign s_transfer_fault = s_fault_q || dma_error_i ||
(s_buffer_event_match && buffer_event_data_i[2]);
assign work_valid_o = s_state_q == WorkSend;
assign work_data_o = s_work_q;
assign result_ready_o = (s_state_q == Idle) || (s_state_q == ResultWait);
assign fill_cmd_valid_o = s_state_q == FillCommand;
assign fill_cmd_data_o = {s_len_q, s_ram_base_q};
assign drain_cmd_valid_o = s_state_q == DrainCommand;
assign drain_cmd_data_o = {s_len_q, s_ram_base_q};
assign buffer_event_ready_o = s_buffer_event_match;
assign dma_start_o = s_dma_start_q;
assign dma_abort_o = abort_i;
assign dma_memory_to_packet_o = (s_state_q != DrainDmaStart) && (s_state_q != DrainWait);
assign dma_desc_base_o = s_desc_q;
assign dma_transfer_bytes_o = {17'd0, s_len_q};
assign endpoint_pending_in_o = s_pending_in_q;
assign endpoint_pending_out_o = s_pending_out_q;
assign endpoint_complete_in_o = s_complete_in_q;
assign endpoint_complete_out_o = s_complete_out_q;
assign irq_event_o = s_irq_event_q;
assign error_capture_o = s_err_capture_q;
assign error_status_o = s_err_stat_q;
assign error_code_o = s_err_code_q;
assign error_info_o = s_err_info_q;
assign error_desc_addr_o = s_err_desc_addr_q;
assign error_buffer_addr_o = s_err_buffer_addr_q;
assign perf_tx_bytes_o = s_perf_tx_bytes_q;
assign perf_rx_bytes_o = s_perf_rx_bytes_q;
assign perf_packets_o = s_perf_packets_q;
assign perf_retries_o = s_perf_retries_q;
assign perf_irq_count_o = s_perf_irq_count_q;
assign busy_o = s_state_q != Idle;
for (genvar endpoint = 0; endpoint < NumEndpoints; endpoint++) begin : gen_endpoint_outputs
assign endpoint_status_o[endpoint] = s_endpoint_stat_q[endpoint];
assign endpoint_bytes_in_o[endpoint] = s_endpoint_bytes_in_q[endpoint];
assign endpoint_bytes_out_o[endpoint] = s_endpoint_bytes_out_q[endpoint];
end
for (genvar channel = 0; channel < NumChannels; channel++) begin : gen_channel_outputs
assign channel_status_o[channel] = s_channel_stat_q[channel];
assign channel_bytes_o[channel] = s_channel_bytes_q[channel];
end
always_comb begin
s_state_d = s_state_q;
s_work_d = s_work_q;
s_desc_d = s_desc_q;
s_ram_base_d = s_ram_base_q;
s_len_d = s_len_q;
s_wait_result_d = s_wait_result_q;
s_dma_seen_d = s_dma_seen_q || dma_done_i;
s_buffer_seen_d = s_buffer_seen_q || (s_buffer_event_match && buffer_event_ready_o);
s_fault_d = s_transfer_fault;
s_fault_code_d = dma_error_i ? dma_error_code_i : s_fault_code_q;
s_completion_code_d = s_completion_code_q;
s_pending_in_d = s_pending_in_q;
s_pending_out_d = s_pending_out_q;
s_complete_in_d = s_complete_in_q & ~endpoint_complete_clear_in_i;
s_complete_out_d = s_complete_out_q & ~endpoint_complete_clear_out_i;
s_endpoint_stat_d = s_endpoint_stat_q;
s_endpoint_bytes_in_d = s_endpoint_bytes_in_q;
s_endpoint_bytes_out_d = s_endpoint_bytes_out_q;
s_channel_stat_d = s_channel_stat_q;
s_channel_bytes_d = s_channel_bytes_q;
s_perf_tx_bytes_d = s_perf_tx_bytes_q;
s_perf_rx_bytes_d = s_perf_rx_bytes_q;
s_perf_packets_d = s_perf_packets_q;
s_perf_retries_d = s_perf_retries_q;
s_perf_irq_count_d = s_perf_irq_count_q;
for (int endpoint = 0; endpoint < NumEndpoints; endpoint++) begin
s_endpoint_cmd_pending_d[endpoint] = s_endpoint_cmd_merged[endpoint];
end
for (int channel = 0; channel < NumChannels; channel++) begin
s_channel_cmd_pending_d[channel] = s_channel_cmd_merged[channel];
s_channel_next_frame_d[channel] = s_channel_next_frame_q[channel];
end
s_irq_event_d = '0;
s_err_capture_d = 1'b0;
s_err_stat_d = '0;
s_err_code_d = '0;
s_err_info_d = '0;
s_err_desc_addr_d = '0;
s_err_buffer_addr_d = '0;
s_dma_start_d = 1'b0;
if (retry_i && !(&s_perf_retries_q)) begin
s_perf_retries_d = s_perf_retries_q + 1'b1;
end
if (perf_clear_i) begin
s_perf_tx_bytes_d = 32'd0;
s_perf_rx_bytes_d = 32'd0;
s_perf_packets_d = 32'd0;
s_perf_retries_d = 32'd0;
s_perf_irq_count_d = 32'd0;
end
unique case (s_state_q)
Idle: begin
s_dma_seen_d = 1'b0;
s_buffer_seen_d = 1'b0;
s_fault_d = 1'b0;
s_fault_code_d = '0;
s_completion_code_d = usb2_pkg::Usb2ResultSuccess;
if (result_valid_i && result_ready_o) begin
s_work_d = result_data_i;
s_len_d = s_result_len;
s_completion_code_d = s_result_code;
if (s_result_code != usb2_pkg::Usb2ResultSuccess) begin
s_fault_d = 1'b1;
s_fault_code_d = {4'h8, s_result_code};
end
if ((s_result_code == usb2_pkg::Usb2ResultSuccess) &&
(((s_result_role == usb2_pkg::Usb2RoleDevice) && !s_result_direction_in) ||
((s_result_role == usb2_pkg::Usb2RoleHost) && s_result_direction_in))) begin
if (s_result_role == usb2_pkg::Usb2RoleDevice) begin
s_ram_base_d = endpoint_ram_out_i[s_result_index][13:2];
s_desc_d = endpoint_desc_out_i[s_result_index];
end else begin
s_ram_base_d = channel_ram_i[s_result_index][13:2];
s_desc_d = channel_desc_i[s_result_index];
end
s_state_d = DrainCommand;
end else begin
s_state_d = Complete;
end
end else if (enable_i && s_cmd_valid &&
((s_cmd_is_channel &&
(active_role_i == usb2_pkg::Usb2RoleHost)) ||
(!s_cmd_is_channel &&
(active_role_i == usb2_pkg::Usb2RoleDevice)))) begin
if (s_cmd_is_channel) begin
s_channel_cmd_pending_d[s_cmd_index] = 4'd0;
if ((channel_cfg_i[s_cmd_index][3:2] ==
usb2_pkg::Usb2TransferIsochronous) ||
(channel_cfg_i[s_cmd_index][3:2] ==
usb2_pkg::Usb2TransferInterrupt)) begin
s_channel_next_frame_d[s_cmd_index] =
frame_i + ((channel_interval_i[s_cmd_index][13:0] == 14'd0) ?
14'd1 : channel_interval_i[s_cmd_index][13:0]);
end
s_work_d = make_work(
usb2_pkg::Usb2RoleHost,
s_cmd_index,
channel_cfg_i[s_cmd_index][`APB4_USB2__CHANNEL_CFG_DIRECTION_IN],
channel_cfg_i[s_cmd_index][3:2],
channel_target_i[s_cmd_index][6:0],
channel_target_i[s_cmd_index][11:8],
channel_cfg_i[s_cmd_index][`APB4_USB2__CHANNEL_CFG_LOW_SPEED] ?
usb2_pkg::Usb2SpeedLow :
(high_speed_i ? usb2_pkg::Usb2SpeedHigh : usb2_pkg::Usb2SpeedFull),
channel_ram_i[s_cmd_index][13:2],
channel_ram_i[s_cmd_index][30:16],
channel_cfg_i[s_cmd_index][`APB4_USB2__CHANNEL_CFG_SETUP],
s_cmd_value[`APB4_USB2__CHANNEL_COMMAND_CANCEL],
channel_target_i[s_cmd_index][`APB4_USB2__CHANNEL_TARGET_TOGGLE]
);
s_desc_d = channel_desc_i[s_cmd_index];
s_ram_base_d = channel_ram_i[s_cmd_index][13:2];
s_len_d = channel_ram_i[s_cmd_index][30:16];
s_wait_result_d = 1'b1;
s_channel_stat_d[s_cmd_index] = 32'h0000_0001;
if (s_cmd_value[`APB4_USB2__CHANNEL_COMMAND_CANCEL] ||
channel_cfg_i[s_cmd_index][`APB4_USB2__CHANNEL_CFG_DIRECTION_IN]) begin
s_state_d = WorkSend;
end else begin
s_state_d = FillCommand;
end
end else begin
s_endpoint_cmd_pending_d[s_cmd_index] = 4'd0;
if (s_cmd_value[`APB4_USB2__ENDPOINT_COMMAND_RESET_TOGGLE]) begin
s_endpoint_stat_d[s_cmd_index][9:8] = 2'b00;
end
if (s_cmd_value[`APB4_USB2__ENDPOINT_COMMAND_CANCEL]) begin
s_work_d = make_work(
usb2_pkg::Usb2RoleDevice,
s_cmd_index,
1'b0,
endpoint_cfg_i[s_cmd_index][5:4],
7'd0,
s_cmd_index,
high_speed_i ? usb2_pkg::Usb2SpeedHigh : usb2_pkg::Usb2SpeedFull,
12'd0,
15'd0,
1'b0,
1'b1,
1'b0
);
s_wait_result_d = 1'b0;
s_pending_in_d[{1'b0, s_cmd_index}] = 1'b0;
s_pending_out_d[{1'b0, s_cmd_index}] = 1'b0;
s_state_d = WorkSend;
end else if (s_cmd_value[`APB4_USB2__ENDPOINT_COMMAND_PRIME_IN]) begin
s_work_d = make_work(
usb2_pkg::Usb2RoleDevice,
s_cmd_index,
1'b1,
endpoint_cfg_i[s_cmd_index][5:4],
7'd0,
s_cmd_index,
high_speed_i ? usb2_pkg::Usb2SpeedHigh : usb2_pkg::Usb2SpeedFull,
endpoint_ram_in_i[s_cmd_index][13:2],
endpoint_ram_in_i[s_cmd_index][30:16],
1'b0,
1'b0,
s_endpoint_stat_q[s_cmd_index][8]
);
s_desc_d = endpoint_desc_in_i[s_cmd_index];
s_ram_base_d = endpoint_ram_in_i[s_cmd_index][13:2];
s_len_d = endpoint_ram_in_i[s_cmd_index][30:16];
s_wait_result_d = 1'b0;
s_pending_in_d[{1'b0, s_cmd_index}] = 1'b1;
s_complete_in_d[{1'b0, s_cmd_index}] = 1'b0;
s_endpoint_stat_d[s_cmd_index] = 32'h0000_0001;
s_state_d = FillCommand;
end else if (s_cmd_value[`APB4_USB2__ENDPOINT_COMMAND_ARM_OUT]) begin
s_work_d = make_work(
usb2_pkg::Usb2RoleDevice,
s_cmd_index,
1'b0,
endpoint_cfg_i[s_cmd_index][5:4],
7'd0,
s_cmd_index,
high_speed_i ? usb2_pkg::Usb2SpeedHigh : usb2_pkg::Usb2SpeedFull,
endpoint_ram_out_i[s_cmd_index][13:2],
endpoint_ram_out_i[s_cmd_index][30:16],
1'b0,
1'b0,
s_endpoint_stat_q[s_cmd_index][9]
);
s_wait_result_d = 1'b0;
s_pending_out_d[{1'b0, s_cmd_index}] = 1'b1;
s_complete_out_d[{1'b0, s_cmd_index}] = 1'b0;
s_endpoint_stat_d[s_cmd_index] = 32'h0000_0001;
s_state_d = WorkSend;
end
end
end
end
FillCommand: begin
if (fill_cmd_valid_o && fill_cmd_ready_i) begin
s_state_d = DmaStart;
end
end
DmaStart: begin
if (!dma_busy_i) begin
s_dma_start_d = 1'b1;
s_dma_seen_d = 1'b0;
s_buffer_seen_d = 1'b0;
s_state_d = TransferWait;
end
end
TransferWait: begin
if (s_transfer_fault) begin
s_state_d = Complete;
end else if ((s_dma_seen_d || dma_done_i) &&
(s_buffer_seen_d || s_buffer_event_match)) begin
s_perf_tx_bytes_d = s_perf_tx_bytes_q + dma_bytes_done_i;
s_state_d = WorkSend;
end
end
WorkSend: begin
if (work_valid_o && work_ready_i) begin
if (s_wait_result_q) begin
s_state_d = ResultWait;
end else begin
s_state_d = Idle;
end
end
end
ResultWait: begin
if (result_valid_i && result_ready_o) begin
s_work_d = result_data_i;
s_len_d = s_result_len;
s_completion_code_d = s_result_code;
if (s_result_code != usb2_pkg::Usb2ResultSuccess) begin
s_fault_d = 1'b1;
s_fault_code_d = {4'h8, s_result_code};
end
if ((s_result_code == usb2_pkg::Usb2ResultSuccess) && s_result_direction_in) begin
s_ram_base_d = channel_ram_i[s_result_index][13:2];
s_desc_d = channel_desc_i[s_result_index];
s_state_d = DrainCommand;
end else begin
s_state_d = Complete;
end
end
end
DrainCommand: begin
if (drain_cmd_valid_o && drain_cmd_ready_i) begin
s_state_d = DrainDmaStart;
end
end
DrainDmaStart: begin
if (!dma_busy_i) begin
s_dma_start_d = 1'b1;
s_dma_seen_d = 1'b0;
s_buffer_seen_d = 1'b0;
s_state_d = DrainWait;
end
end
DrainWait: begin
if (s_transfer_fault) begin
s_state_d = Complete;
end else if ((s_dma_seen_d || dma_done_i) &&
(s_buffer_seen_d || s_buffer_event_match)) begin
s_perf_rx_bytes_d = s_perf_rx_bytes_q + dma_bytes_done_i;
s_state_d = Complete;
end
end
Complete: begin
if (s_current_role == usb2_pkg::Usb2RoleDevice) begin
if (s_current_direction_in) begin
s_pending_in_d[{1'b0, s_current_index}] = 1'b0;
s_complete_in_d[{1'b0, s_current_index}] = 1'b1;
s_endpoint_bytes_in_d[s_current_index] = s_endpoint_bytes_in_q[s_current_index] +
{17'd0, s_len_q};
s_endpoint_stat_d[s_current_index] = 32'd0;
s_endpoint_stat_d[s_current_index][1] = 1'b1;
s_endpoint_stat_d[s_current_index][2] = s_fault_q;
s_endpoint_stat_d[s_current_index][7:4] =
s_fault_q ? s_fault_code_q[3:0] : s_completion_code_q;
s_endpoint_stat_d[s_current_index][8] = !s_endpoint_stat_q[s_current_index][8];
end else begin
s_pending_out_d[{1'b0, s_current_index}] = 1'b0;
s_complete_out_d[{1'b0, s_current_index}] = 1'b1;
s_endpoint_bytes_out_d[s_current_index] = s_endpoint_bytes_out_q[s_current_index] +
{17'd0, s_len_q};
s_endpoint_stat_d[s_current_index] = 32'd0;
s_endpoint_stat_d[s_current_index][1] = 1'b1;
s_endpoint_stat_d[s_current_index][2] = s_fault_q;
s_endpoint_stat_d[s_current_index][7:4] =
s_fault_q ? s_fault_code_q[3:0] : s_completion_code_q;
s_endpoint_stat_d[s_current_index][9] = !s_endpoint_stat_q[s_current_index][9];
end
s_irq_event_d[`APB4_USB2__IRQ_ENDPOINT] = 1'b1;
end else begin
s_channel_bytes_d[s_current_index] = s_channel_bytes_q[s_current_index] +
{17'd0, s_len_q};
s_channel_stat_d[s_current_index] = 32'd0;
s_channel_stat_d[s_current_index][1] = 1'b1;
s_channel_stat_d[s_current_index][2] = s_fault_q;
s_channel_stat_d[s_current_index][7:4] =
s_fault_q ? s_fault_code_q[3:0] : s_completion_code_q;
s_irq_event_d[`APB4_USB2__IRQ_CHANNEL] = 1'b1;
end
s_irq_event_d[s_fault_q?`APB4_USB2__IRQ_DMA_ERROR : `APB4_USB2__IRQ_DMA_DONE] = 1'b1;
s_perf_packets_d = s_perf_packets_q + 1'b1;
s_perf_irq_count_d = s_perf_irq_count_q + 1'b1;
if (s_fault_q) begin
s_err_capture_d = 1'b1;
s_err_stat_d = 32'h0000_0001;
s_err_code_d = {24'd0, s_fault_code_q};
s_err_info_d = {16'd0, s_current_role, 9'd0, s_current_direction_in, s_current_index};
s_err_desc_addr_d = dma_current_desc_i;
s_err_buffer_addr_d = {20'd0, s_ram_base_q} << 2;
end
s_state_d = Idle;
end
default: s_state_d = Idle;
endcase
if (abort_i) begin
s_fault_d = 1'b1;
s_fault_code_d = 8'h06;
if (s_state_q != Idle) begin
s_state_d = Complete;
end
end
if (reinitialize_i) begin
s_state_d = Idle;
s_pending_in_d = 32'd0;
s_pending_out_d = 32'd0;
s_complete_in_d = 32'd0;
s_complete_out_d = 32'd0;
s_endpoint_stat_d = '0;
s_channel_stat_d = '0;
for (int endpoint = 0; endpoint < NumEndpoints; endpoint++) begin
s_endpoint_cmd_pending_d[endpoint] = 4'd0;
end
for (int channel = 0; channel < NumChannels; channel++) begin
s_channel_cmd_pending_d[channel] = 4'd0;
s_channel_next_frame_d[channel] = 14'd0;
end
end
end
dffr #(
.DATA_WIDTH(4)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(64)
) u_work_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_work_d),
.dat_o (s_work_q)
);
dffr #(
.DATA_WIDTH(32)
) u_desc_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_desc_d),
.dat_o (s_desc_q)
);
dffr #(
.DATA_WIDTH(12)
) u_ram_base_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ram_base_d),
.dat_o (s_ram_base_q)
);
dffr #(
.DATA_WIDTH(15)
) u_length_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_len_d),
.dat_o (s_len_q)
);
dffr #(
.DATA_WIDTH(1)
) u_wait_result_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_wait_result_d),
.dat_o (s_wait_result_q)
);
dffr #(
.DATA_WIDTH(1)
) u_dma_seen_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_seen_d),
.dat_o (s_dma_seen_q)
);
dffr #(
.DATA_WIDTH(1)
) u_buffer_seen_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_buffer_seen_d),
.dat_o (s_buffer_seen_q)
);
dffr #(
.DATA_WIDTH(1)
) u_fault_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_d),
.dat_o (s_fault_q)
);
dffr #(
.DATA_WIDTH(8)
) u_fault_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_fault_code_d),
.dat_o (s_fault_code_q)
);
dffr #(
.DATA_WIDTH(4)
) u_completion_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_completion_code_d),
.dat_o (s_completion_code_q)
);
dffr #(
.DATA_WIDTH(32)
) u_pending_in_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pending_in_d),
.dat_o (s_pending_in_q)
);
dffr #(
.DATA_WIDTH(32)
) u_pending_out_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pending_out_d),
.dat_o (s_pending_out_q)
);
dffr #(
.DATA_WIDTH(32)
) u_complete_in_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_complete_in_d),
.dat_o (s_complete_in_q)
);
dffr #(
.DATA_WIDTH(32)
) u_complete_out_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_complete_out_d),
.dat_o (s_complete_out_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_tx_bytes_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_tx_bytes_d),
.dat_o (s_perf_tx_bytes_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_rx_bytes_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_rx_bytes_d),
.dat_o (s_perf_rx_bytes_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_packets_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_packets_d),
.dat_o (s_perf_packets_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_retries_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_retries_d),
.dat_o (s_perf_retries_q)
);
dffr #(
.DATA_WIDTH(32)
) u_perf_irq_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_perf_irq_count_d),
.dat_o (s_perf_irq_count_q)
);
dffr #(
.DATA_WIDTH(16)
) u_irq_event_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_irq_event_d),
.dat_o (s_irq_event_q)
);
dffr #(
.DATA_WIDTH(1)
) u_error_capture_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_capture_d),
.dat_o (s_err_capture_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_stat_d),
.dat_o (s_err_stat_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_code_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_code_d),
.dat_o (s_err_code_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_info_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_info_d),
.dat_o (s_err_info_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_desc_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_desc_addr_d),
.dat_o (s_err_desc_addr_q)
);
dffr #(
.DATA_WIDTH(32)
) u_error_buffer_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_buffer_addr_d),
.dat_o (s_err_buffer_addr_q)
);
dffr #(
.DATA_WIDTH(1)
) u_dma_start_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_dma_start_d),
.dat_o (s_dma_start_q)
);
for (genvar endpoint = 0; endpoint < NumEndpoints; endpoint++) begin : gen_endpoint_state
dffr #(
.DATA_WIDTH(4)
) u_command_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_cmd_pending_d[endpoint]),
.dat_o (s_endpoint_cmd_pending_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_stat_d[endpoint]),
.dat_o (s_endpoint_stat_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_bytes_in_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_bytes_in_d[endpoint]),
.dat_o (s_endpoint_bytes_in_q[endpoint])
);
dffr #(
.DATA_WIDTH(32)
) u_bytes_out_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_endpoint_bytes_out_d[endpoint]),
.dat_o (s_endpoint_bytes_out_q[endpoint])
);
end
for (genvar channel = 0; channel < NumChannels; channel++) begin : gen_channel_state
dffr #(
.DATA_WIDTH(4)
) u_command_pending_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_cmd_pending_d[channel]),
.dat_o (s_channel_cmd_pending_q[channel])
);
dffr #(
.DATA_WIDTH(14)
) u_next_frame_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_next_frame_d[channel]),
.dat_o (s_channel_next_frame_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_status_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_stat_d[channel]),
.dat_o (s_channel_stat_q[channel])
);
dffr #(
.DATA_WIDTH(32)
) u_bytes_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_channel_bytes_d[channel]),
.dat_o (s_channel_bytes_q[channel])
);
end
`ifndef SYNTHESIS
initial begin
if ((NumEndpoints != 8) || (NumChannels != 16)) begin
$fatal(1, "usb2_scheduler: delivered configuration requires 8 endpoints and 16 channels");
end
end
`endif
endmodule
+331
View File
@@ -0,0 +1,331 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_sie_rx (
// verilog_format: off -- preserve link input and decoded packet groups
input logic clk_i,
input logic rst_n_i,
input logic rx_start_i,
input logic rx_valid_i,
input logic [7:0] rx_data_i,
input logic rx_end_i,
input logic rx_error_i,
output logic payload_valid_o,
output logic [7:0] payload_data_o,
output logic packet_done_o,
output logic packet_good_o,
output logic [3:0] packet_pid_o,
output logic [6:0] token_addr_o,
output logic [3:0] token_endpoint_o,
output logic [10:0] frame_number_o,
output logic [10:0] payload_length_o,
output logic pid_error_o,
output logic crc_error_o,
output logic framing_error_o
// verilog_format: on
);
typedef enum logic [1:0] {
Idle,
Header,
Body
} rx_state_e;
rx_state_e s_state_d, s_state_q;
logic [1:0] s_state_bits_q;
logic [3:0] s_pid_d, s_pid_q;
logic [7:0] s_token_low_d, s_token_low_q;
logic [7:0] s_token_high_d, s_token_high_q;
logic [1:0] s_token_count_d, s_token_count_q;
logic [7:0] s_tail_first_d, s_tail_first_q;
logic [7:0] s_tail_second_d, s_tail_second_q;
logic [1:0] s_tail_count_d, s_tail_count_q;
logic [15:0] s_crc16_d, s_crc16_q;
logic [10:0] s_payload_len_d, s_payload_len_q;
logic s_link_err_d, s_link_err_q;
logic s_payload_valid_d, s_payload_valid_q;
logic [7:0] s_payload_data_d, s_payload_data_q;
logic s_packet_done_d, s_packet_done_q;
logic s_packet_good_d, s_packet_good_q;
logic s_pid_err_d, s_pid_err_q;
logic s_crc_err_d, s_crc_err_q;
logic s_framing_err_d, s_framing_err_q;
logic [10:0] s_token_value;
logic [ 4:0] s_token_crc;
logic s_is_token;
logic s_is_data;
logic s_is_handshake;
assign s_state_q = rx_state_e'(s_state_bits_q);
assign s_token_value = {s_token_high_q[2:0], s_token_low_q};
assign s_token_crc = s_token_high_q[7:3];
assign s_is_token = usb2_pkg::usb2_pid_is_token(s_pid_q);
assign s_is_data = usb2_pkg::usb2_pid_is_data(s_pid_q);
assign s_is_handshake = usb2_pkg::usb2_pid_is_handshake(s_pid_q);
assign payload_valid_o = s_payload_valid_q;
assign payload_data_o = s_payload_data_q;
assign packet_done_o = s_packet_done_q;
assign packet_good_o = s_packet_good_q;
assign packet_pid_o = s_pid_q;
assign token_addr_o = s_token_value[6:0];
assign token_endpoint_o = s_token_value[10:7];
assign frame_number_o = s_token_value;
assign payload_length_o = s_payload_len_q;
assign pid_error_o = s_pid_err_q;
assign crc_error_o = s_crc_err_q;
assign framing_error_o = s_framing_err_q;
always_comb begin
s_state_d = s_state_q;
s_pid_d = s_pid_q;
s_token_low_d = s_token_low_q;
s_token_high_d = s_token_high_q;
s_token_count_d = s_token_count_q;
s_tail_first_d = s_tail_first_q;
s_tail_second_d = s_tail_second_q;
s_tail_count_d = s_tail_count_q;
s_crc16_d = s_crc16_q;
s_payload_len_d = s_payload_len_q;
s_link_err_d = s_link_err_q || rx_error_i;
s_payload_valid_d = 1'b0;
s_payload_data_d = s_payload_data_q;
s_packet_done_d = 1'b0;
s_packet_good_d = 1'b0;
s_pid_err_d = 1'b0;
s_crc_err_d = 1'b0;
s_framing_err_d = 1'b0;
if (rx_start_i) begin
s_state_d = Header;
s_token_count_d = '0;
s_tail_count_d = '0;
s_crc16_d = 16'hFFFF;
s_payload_len_d = '0;
s_link_err_d = rx_error_i;
end
if (rx_valid_i) begin
unique case (s_state_q)
Header: begin
if (usb2_pkg::usb2_pid_valid(rx_data_i)) begin
s_pid_d = rx_data_i[3:0];
s_state_d = Body;
end else begin
s_pid_d = rx_data_i[3:0];
s_pid_err_d = 1'b1;
s_link_err_d = 1'b1;
s_state_d = Body;
end
end
Body: begin
if (s_is_token) begin
if (s_token_count_q == 2'd0) begin
s_token_low_d = rx_data_i;
s_token_count_d = 2'd1;
end else if (s_token_count_q == 2'd1) begin
s_token_high_d = rx_data_i;
s_token_count_d = 2'd2;
end else begin
s_link_err_d = 1'b1;
end
end else if (s_is_data) begin
if (s_tail_count_q == 2'd0) begin
s_tail_first_d = rx_data_i;
s_tail_count_d = 2'd1;
end else if (s_tail_count_q == 2'd1) begin
s_tail_second_d = rx_data_i;
s_tail_count_d = 2'd2;
end else begin
s_payload_valid_d = 1'b1;
s_payload_data_d = s_tail_first_q;
s_payload_len_d = s_payload_len_q + 1'b1;
s_crc16_d = usb2_pkg::usb2_crc16_byte(s_crc16_q, s_tail_first_q);
s_tail_first_d = s_tail_second_q;
s_tail_second_d = rx_data_i;
end
end else if (!s_is_handshake) begin
s_link_err_d = 1'b1;
end
end
default: begin
s_link_err_d = 1'b1;
end
endcase
end
if (rx_end_i && (s_state_q != Idle)) begin
s_packet_done_d = 1'b1;
s_state_d = Idle;
if (s_is_token) begin
if ((s_token_count_q == 2'd2) && (usb2_pkg::usb2_token_crc5(
s_token_value
) == s_token_crc) && !s_link_err_q) begin
s_packet_good_d = 1'b1;
end else begin
s_crc_err_d = s_token_count_q == 2'd2;
s_framing_err_d = s_token_count_q != 2'd2;
end
end else if (s_is_data) begin
if ((s_tail_count_q == 2'd2) &&
({s_tail_second_q, s_tail_first_q} == usb2_pkg::usb2_crc16_finish(
s_crc16_q
)) && !s_link_err_q) begin
s_packet_good_d = 1'b1;
end else begin
s_crc_err_d = s_tail_count_q == 2'd2;
s_framing_err_d = s_tail_count_q != 2'd2;
end
end else if (s_is_handshake && !s_link_err_q && (s_token_count_q == 2'd0) &&
(s_tail_count_q == 2'd0)) begin
s_packet_good_d = 1'b1;
end else begin
s_framing_err_d = 1'b1;
end
end
end
dffr #(
.DATA_WIDTH(2)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(4)
) u_pid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pid_d),
.dat_o (s_pid_q)
);
dffr #(
.DATA_WIDTH(8)
) u_token_low_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_token_low_d),
.dat_o (s_token_low_q)
);
dffr #(
.DATA_WIDTH(8)
) u_token_high_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_token_high_d),
.dat_o (s_token_high_q)
);
dffr #(
.DATA_WIDTH(2)
) u_token_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_token_count_d),
.dat_o (s_token_count_q)
);
dffr #(
.DATA_WIDTH(8)
) u_tail_first_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tail_first_d),
.dat_o (s_tail_first_q)
);
dffr #(
.DATA_WIDTH(8)
) u_tail_second_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tail_second_d),
.dat_o (s_tail_second_q)
);
dffr #(
.DATA_WIDTH(2)
) u_tail_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tail_count_d),
.dat_o (s_tail_count_q)
);
dffr #(
.DATA_WIDTH(16)
) u_crc16_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crc16_d),
.dat_o (s_crc16_q)
);
dffr #(
.DATA_WIDTH(11)
) u_payload_length_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_payload_len_d),
.dat_o (s_payload_len_q)
);
dffr #(
.DATA_WIDTH(1)
) u_link_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_link_err_d),
.dat_o (s_link_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_payload_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_payload_valid_d),
.dat_o (s_payload_valid_q)
);
dffr #(
.DATA_WIDTH(8)
) u_payload_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_payload_data_d),
.dat_o (s_payload_data_q)
);
dffr #(
.DATA_WIDTH(1)
) u_packet_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_packet_done_d),
.dat_o (s_packet_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_packet_good_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_packet_good_d),
.dat_o (s_packet_good_q)
);
dffr #(
.DATA_WIDTH(1)
) u_pid_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pid_err_d),
.dat_o (s_pid_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_crc_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crc_err_d),
.dat_o (s_crc_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_framing_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_framing_err_d),
.dat_o (s_framing_err_q)
);
endmodule
+263
View File
@@ -0,0 +1,263 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_sie_tx (
// verilog_format: off -- preserve request, source stream, and ULPI link groups
input logic clk_i,
input logic rst_n_i,
input logic request_valid_i,
output logic request_ready_o,
input logic [3:0] request_pid_i,
input logic [10:0] request_token_i,
input logic [10:0] request_length_i,
input logic payload_valid_i,
output logic payload_ready_o,
input logic [7:0] payload_data_i,
output logic link_start_valid_o,
input logic link_start_ready_i,
output logic [3:0] link_pid_o,
output logic link_has_data_o,
output logic link_data_valid_o,
input logic link_data_ready_i,
output logic [7:0] link_data_o,
output logic link_data_last_o,
input logic link_done_i,
input logic link_error_i,
output logic busy_o,
output logic done_o,
output logic error_o
// verilog_format: on
);
typedef enum logic [3:0] {
Idle,
Start,
TokenLow,
TokenHigh,
Payload,
CrcLow,
CrcHigh,
WaitDone
} tx_state_e;
tx_state_e s_state_d, s_state_q;
logic [3:0] s_state_bits_q;
logic [3:0] s_pid_d, s_pid_q;
logic [10:0] s_token_d, s_token_q;
logic [10:0] s_len_d, s_len_q;
logic [10:0] s_count_d, s_count_q;
logic [15:0] s_crc_d, s_crc_q;
logic [15:0] s_crc_finished;
logic [ 4:0] s_token_crc;
logic s_is_token;
logic s_is_data;
logic s_is_handshake;
logic s_done_d, s_done_q;
logic s_err_d, s_err_q;
assign s_state_q = tx_state_e'(s_state_bits_q);
assign s_is_token = usb2_pkg::usb2_pid_is_token(s_pid_q);
assign s_is_data = usb2_pkg::usb2_pid_is_data(s_pid_q);
assign s_is_handshake = usb2_pkg::usb2_pid_is_handshake(s_pid_q);
assign s_token_crc = usb2_pkg::usb2_token_crc5(s_token_q);
assign s_crc_finished = usb2_pkg::usb2_crc16_finish(s_crc_q);
assign request_ready_o = s_state_q == Idle;
assign payload_ready_o = (s_state_q == Payload) && link_data_ready_i;
assign link_start_valid_o = s_state_q == Start;
assign link_pid_o = s_pid_q;
assign link_has_data_o = !s_is_handshake;
assign busy_o = s_state_q != Idle;
assign done_o = s_done_q;
assign error_o = s_err_q;
always_comb begin
link_data_valid_o = 1'b0;
link_data_o = 8'd0;
link_data_last_o = 1'b0;
unique case (s_state_q)
TokenLow: begin
link_data_valid_o = 1'b1;
link_data_o = s_token_q[7:0];
end
TokenHigh: begin
link_data_valid_o = 1'b1;
link_data_o = {s_token_crc, s_token_q[10:8]};
link_data_last_o = 1'b1;
end
Payload: begin
link_data_valid_o = payload_valid_i;
link_data_o = payload_data_i;
end
CrcLow: begin
link_data_valid_o = 1'b1;
link_data_o = s_crc_finished[7:0];
end
CrcHigh: begin
link_data_valid_o = 1'b1;
link_data_o = s_crc_finished[15:8];
link_data_last_o = 1'b1;
end
default: begin
end
endcase
end
always_comb begin
s_state_d = s_state_q;
s_pid_d = s_pid_q;
s_token_d = s_token_q;
s_len_d = s_len_q;
s_count_d = s_count_q;
s_crc_d = s_crc_q;
s_done_d = 1'b0;
s_err_d = 1'b0;
unique case (s_state_q)
Idle: begin
if (request_valid_i) begin
s_pid_d = request_pid_i;
s_token_d = request_token_i;
s_len_d = request_length_i;
s_count_d = '0;
s_crc_d = 16'hFFFF;
s_state_d = Start;
end
end
Start: begin
if (link_start_ready_i) begin
if (s_is_token) begin
s_state_d = TokenLow;
end else if (s_is_data) begin
if (s_len_q == 11'd0) begin
s_state_d = CrcLow;
end else begin
s_state_d = Payload;
end
end else if (s_is_handshake) begin
s_state_d = WaitDone;
end else begin
s_state_d = Idle;
s_err_d = 1'b1;
end
end
end
TokenLow: begin
if (link_data_valid_o && link_data_ready_i) begin
s_state_d = TokenHigh;
end
end
TokenHigh: begin
if (link_data_valid_o && link_data_ready_i) begin
s_state_d = WaitDone;
end
end
Payload: begin
if (payload_valid_i && payload_ready_o) begin
s_crc_d = usb2_pkg::usb2_crc16_byte(s_crc_q, payload_data_i);
s_count_d = s_count_q + 1'b1;
if ((s_count_q + 1'b1) == s_len_q) begin
s_state_d = CrcLow;
end
end
end
CrcLow: begin
if (link_data_valid_o && link_data_ready_i) begin
s_state_d = CrcHigh;
end
end
CrcHigh: begin
if (link_data_valid_o && link_data_ready_i) begin
s_state_d = WaitDone;
end
end
WaitDone: begin
if (link_error_i) begin
s_state_d = Idle;
s_err_d = 1'b1;
end else if (link_done_i) begin
s_state_d = Idle;
s_done_d = 1'b1;
end
end
default: begin
s_state_d = Idle;
s_err_d = 1'b1;
end
endcase
end
dffr #(
.DATA_WIDTH(4)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(4)
) u_pid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_pid_d),
.dat_o (s_pid_q)
);
dffr #(
.DATA_WIDTH(11)
) u_token_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_token_d),
.dat_o (s_token_q)
);
dffr #(
.DATA_WIDTH(11)
) u_length_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_len_d),
.dat_o (s_len_q)
);
dffr #(
.DATA_WIDTH(11)
) u_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_count_d),
.dat_o (s_count_q)
);
dffr #(
.DATA_WIDTH(16)
) u_crc_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crc_d),
.dat_o (s_crc_q)
);
dffr #(
.DATA_WIDTH(1)
) u_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_done_d),
.dat_o (s_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_d),
.dat_o (s_err_q)
);
`ifndef SV_ASSRT_DISABLE
property p_payload_stable_when_stalled;
@(posedge clk_i) disable iff (!rst_n_i)
(payload_valid_i && !payload_ready_o && s_state_q == Payload) |=>
$stable(
payload_data_i
);
endproperty
assert property (p_payload_stable_when_stalled);
`endif
endmodule
+827
View File
@@ -0,0 +1,827 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_transaction_engine #(
parameter int NumEndpoints = 8,
parameter int MaxRetries = 3
) (
// verilog_format: off -- preserve work/result, SIE, and packet-store groups
input logic clk_i,
input logic rst_n_i,
input usb2_pkg::usb2_role_e active_role_i,
input logic [6:0] device_addr_i,
input logic [NumEndpoints-1:0][31:0] endpoint_cfg_i,
input logic [31:0] timeout_i,
input logic work_valid_i,
output logic work_ready_o,
input logic [63:0] work_data_i,
output logic result_valid_o,
input logic result_ready_i,
output logic [63:0] result_data_o,
output logic setup_valid_o,
input logic setup_ready_i,
output logic [63:0] setup_data_o,
output logic tx_request_valid_o,
input logic tx_request_ready_i,
output logic [3:0] tx_request_pid_o,
output logic [10:0] tx_request_token_o,
output logic [10:0] tx_request_length_o,
output logic tx_payload_valid_o,
input logic tx_payload_ready_i,
output logic [7:0] tx_payload_data_o,
input logic tx_done_i,
input logic tx_error_i,
input logic rx_payload_valid_i,
input logic [7:0] rx_payload_data_i,
input logic rx_packet_done_i,
input logic rx_packet_good_i,
input logic [3:0] rx_packet_pid_i,
input logic [6:0] rx_token_addr_i,
input logic [3:0] rx_token_endpoint_i,
input logic [10:0] rx_payload_length_i,
output logic store_rx_start_valid_o,
input logic store_rx_start_ready_i,
output logic [11:0] store_rx_base_o,
output logic [14:0] store_rx_limit_o,
output logic store_rx_valid_o,
input logic store_rx_ready_i,
output logic [7:0] store_rx_data_o,
output logic store_rx_commit_o,
output logic store_rx_cancel_o,
input logic store_rx_done_i,
input logic [14:0] store_rx_bytes_i,
input logic store_rx_overflow_i,
output logic store_tx_start_valid_o,
input logic store_tx_start_ready_i,
output logic [11:0] store_tx_base_o,
output logic [14:0] store_tx_bytes_o,
input logic store_tx_valid_i,
output logic store_tx_ready_o,
input logic [7:0] store_tx_data_i,
input logic store_tx_done_i,
output logic busy_o,
output logic retry_o,
output logic protocol_error_o
// verilog_format: on
);
typedef enum logic [4:0] {
Idle,
HostTokenRequest,
HostTokenWait,
ReceiveArm,
ReceiveWait,
ReceiveCommit,
ReceiveCancel,
ReceiveDiscard,
TransmitArm,
TransmitRequest,
TransmitWait,
HandshakeRequest,
HandshakeWait,
ResultWait
} transaction_state_e;
transaction_state_e s_state_d, s_state_q;
logic [4:0] s_state_bits_q;
logic [63:0] s_work_d, s_work_q;
logic [NumEndpoints-1:0] s_device_in_ready_d, s_device_in_ready_q;
logic [NumEndpoints-1:0] s_device_out_armed_d, s_device_out_armed_q;
logic [NumEndpoints-1:0] s_device_in_toggle_d, s_device_in_toggle_q;
logic [NumEndpoints-1:0] s_device_out_toggle_d, s_device_out_toggle_q;
logic [NumEndpoints-1:0][11:0] s_device_in_base_d, s_device_in_base_q;
logic [NumEndpoints-1:0][11:0] s_device_out_base_d, s_device_out_base_q;
logic [NumEndpoints-1:0][14:0] s_device_in_len_d, s_device_in_len_q;
logic [NumEndpoints-1:0][14:0] s_device_out_limit_d, s_device_out_limit_q;
logic [3:0] s_selected_index_d, s_selected_index_q;
logic s_context_host_d, s_context_host_q;
logic s_direction_in_d, s_direction_in_q;
logic [1:0] s_transfer_type_d, s_transfer_type_q;
logic s_setup_d, s_setup_q;
logic [3:0] s_handshake_pid_d, s_handshake_pid_q;
logic [3:0] s_retry_count_d, s_retry_count_q;
logic [31:0] s_timeout_count_d, s_timeout_count_q;
logic [14:0] s_rx_len_d, s_rx_len_q;
logic [63:0] s_result_data_d, s_result_data_q;
logic s_result_valid_d, s_result_valid_q;
logic [63:0] s_setup_data_d, s_setup_data_q;
logic [3:0] s_setup_count_d, s_setup_count_q;
logic s_setup_valid_d, s_setup_valid_q;
logic s_retry_d, s_retry_q;
logic s_protocol_err_d, s_protocol_err_q;
logic s_store_tx_complete_d, s_store_tx_complete_q;
logic s_store_rx_commit;
logic s_store_rx_cancel;
logic [ 1:0] s_work_role;
logic [ 3:0] s_work_index;
logic s_work_direction_in;
logic [ 1:0] s_work_type;
logic [11:0] s_work_ram_base;
logic [14:0] s_work_len;
logic s_work_setup;
logic s_work_cancel;
logic s_work_toggle;
logic [ 3:0] s_active_index;
logic [ 6:0] s_active_addr;
logic [ 3:0] s_active_endpoint;
logic [11:0] s_active_base;
logic [14:0] s_active_len;
logic s_active_toggle;
logic s_timeout_expired;
logic s_device_token_match;
logic s_device_endpoint_valid;
localparam int EndpointIndexWidth = $clog2(NumEndpoints);
assign s_state_q = transaction_state_e'(s_state_bits_q);
assign s_work_role = work_data_i[usb2_pkg::USB2_WORK_ROLE_LSB+:2];
assign s_work_index = work_data_i[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
assign s_work_direction_in = work_data_i[usb2_pkg::USB2_WORK_DIRECTION_IN];
assign s_work_type = work_data_i[usb2_pkg::USB2_WORK_TYPE_LSB+:2];
assign s_work_ram_base = work_data_i[usb2_pkg::USB2_WORK_RAM_BASE_LSB+:12];
assign s_work_len = work_data_i[usb2_pkg::USB2_WORK_LENGTH_LSB+:15];
assign s_work_setup = work_data_i[usb2_pkg::USB2_WORK_SETUP];
assign s_work_cancel = work_data_i[usb2_pkg::USB2_WORK_CANCEL];
assign s_work_toggle = work_data_i[usb2_pkg::USB2_WORK_TOGGLE];
assign s_active_index = s_work_q[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
assign s_active_addr = s_work_q[usb2_pkg::USB2_WORK_ADDRESS_LSB+:7];
assign s_active_endpoint = s_work_q[usb2_pkg::USB2_WORK_ENDPOINT_LSB+:4];
assign s_active_base = s_work_q[usb2_pkg::USB2_WORK_RAM_BASE_LSB+:12];
assign s_active_len = s_work_q[usb2_pkg::USB2_WORK_LENGTH_LSB+:15];
assign s_active_toggle = s_work_q[usb2_pkg::USB2_WORK_TOGGLE];
assign s_timeout_expired = s_timeout_count_q >= timeout_i;
assign s_device_token_match = (rx_token_addr_i == device_addr_i) &&
(rx_token_endpoint_i < 4'(NumEndpoints));
assign s_device_endpoint_valid = s_selected_index_q < 4'(NumEndpoints);
assign work_ready_o = (s_state_q == Idle) && !s_result_valid_q;
assign result_valid_o = s_result_valid_q;
assign result_data_o = s_result_data_q;
assign setup_valid_o = s_setup_valid_q;
assign setup_data_o = s_setup_data_q;
assign busy_o = (s_state_q != Idle) || s_result_valid_q;
assign retry_o = s_retry_q;
assign protocol_error_o = s_protocol_err_q;
always_comb begin
tx_request_valid_o = 1'b0;
tx_request_pid_o = usb2_pkg::Usb2PidAck;
tx_request_token_o = {s_active_endpoint, s_active_addr};
tx_request_length_o = 11'(s_active_len);
tx_payload_valid_o = 1'b0;
tx_payload_data_o = store_tx_data_i;
store_tx_ready_o = 1'b0;
store_rx_start_valid_o = s_state_q == ReceiveArm;
store_rx_base_o = s_active_base;
store_rx_limit_o = s_active_len;
if (!s_context_host_q && s_device_endpoint_valid) begin
store_rx_base_o = s_device_out_base_q[s_selected_index_q];
store_rx_limit_o = s_device_out_limit_q[s_selected_index_q];
end
store_rx_valid_o = (s_state_q == ReceiveWait) && rx_payload_valid_i;
store_rx_data_o = rx_payload_data_i;
store_rx_commit_o = s_store_rx_commit;
store_rx_cancel_o = s_store_rx_cancel;
store_tx_start_valid_o = s_state_q == TransmitArm;
store_tx_base_o = s_active_base;
store_tx_bytes_o = s_active_len;
if (!s_context_host_q && s_device_endpoint_valid) begin
store_tx_base_o = s_device_in_base_q[s_selected_index_q];
store_tx_bytes_o = s_device_in_len_q[s_selected_index_q];
end
unique case (s_state_q)
HostTokenRequest: begin
tx_request_valid_o = 1'b1;
if (s_setup_q) begin
tx_request_pid_o = usb2_pkg::Usb2PidSetup;
end else if (s_direction_in_q) begin
tx_request_pid_o = usb2_pkg::Usb2PidIn;
end else begin
tx_request_pid_o = usb2_pkg::Usb2PidOut;
end
tx_request_length_o = 11'd0;
end
TransmitRequest: begin
tx_request_valid_o = 1'b1;
tx_request_pid_o = (s_context_host_q ? s_active_toggle :
s_device_in_toggle_q[s_selected_index_q[EndpointIndexWidth-1:0]]) ?
usb2_pkg::Usb2PidData1 : usb2_pkg::Usb2PidData0;
tx_request_length_o = 11'(store_tx_bytes_o);
end
TransmitWait: begin
tx_payload_valid_o = store_tx_valid_i;
store_tx_ready_o = tx_payload_ready_i;
end
HandshakeRequest: begin
tx_request_valid_o = 1'b1;
tx_request_pid_o = s_handshake_pid_q;
tx_request_length_o = 11'd0;
end
default: begin
end
endcase
end
always_comb begin
s_state_d = s_state_q;
s_work_d = s_work_q;
s_device_in_ready_d = s_device_in_ready_q;
s_device_out_armed_d = s_device_out_armed_q;
s_device_in_toggle_d = s_device_in_toggle_q;
s_device_out_toggle_d = s_device_out_toggle_q;
s_device_in_base_d = s_device_in_base_q;
s_device_out_base_d = s_device_out_base_q;
s_device_in_len_d = s_device_in_len_q;
s_device_out_limit_d = s_device_out_limit_q;
s_selected_index_d = s_selected_index_q;
s_context_host_d = s_context_host_q;
s_direction_in_d = s_direction_in_q;
s_transfer_type_d = s_transfer_type_q;
s_setup_d = s_setup_q;
s_handshake_pid_d = s_handshake_pid_q;
s_retry_count_d = s_retry_count_q;
s_timeout_count_d = s_timeout_count_q;
s_rx_len_d = s_rx_len_q;
s_result_data_d = s_result_data_q;
s_result_valid_d = s_result_valid_q && !result_ready_i;
s_setup_data_d = s_setup_data_q;
s_setup_count_d = s_setup_count_q;
s_setup_valid_d = s_setup_valid_q && !setup_ready_i;
s_retry_d = 1'b0;
s_protocol_err_d = 1'b0;
s_store_tx_complete_d = s_store_tx_complete_q || store_tx_done_i;
s_store_rx_commit = 1'b0;
s_store_rx_cancel = 1'b0;
if (s_state_q != Idle) begin
if (!s_timeout_expired) begin
s_timeout_count_d = s_timeout_count_q + 1'b1;
end
end else begin
s_timeout_count_d = 32'd0;
end
if ((s_state_q == ReceiveWait) && rx_payload_valid_i && s_setup_q &&
(s_setup_count_q < 4'd8)) begin
s_setup_data_d[(s_setup_count_q*8)+:8] = rx_payload_data_i;
s_setup_count_d = s_setup_count_q + 1'b1;
end
unique case (s_state_q)
Idle: begin
if (work_valid_i && work_ready_o) begin
if (s_work_role == usb2_pkg::Usb2RoleDevice) begin
if (s_work_index < 4'(NumEndpoints)) begin
if (s_work_cancel) begin
s_device_in_ready_d[s_work_index[EndpointIndexWidth-1:0]] = 1'b0;
s_device_out_armed_d[s_work_index[EndpointIndexWidth-1:0]] = 1'b0;
end else if (s_work_direction_in) begin
s_device_in_ready_d[s_work_index[EndpointIndexWidth-1:0]] = 1'b1;
s_device_in_base_d[s_work_index[EndpointIndexWidth-1:0]] = s_work_ram_base;
s_device_in_len_d[s_work_index[EndpointIndexWidth-1:0]] = s_work_len;
s_device_in_toggle_d[s_work_index[EndpointIndexWidth-1:0]] = s_work_toggle;
end else begin
s_device_out_armed_d[s_work_index[EndpointIndexWidth-1:0]] = 1'b1;
s_device_out_base_d[s_work_index[EndpointIndexWidth-1:0]] = s_work_ram_base;
s_device_out_limit_d[s_work_index[EndpointIndexWidth-1:0]] = s_work_len;
s_device_out_toggle_d[s_work_index[EndpointIndexWidth-1:0]] = s_work_toggle;
end
end
end else if (s_work_role == usb2_pkg::Usb2RoleHost) begin
s_work_d = work_data_i;
s_selected_index_d = s_work_index;
s_context_host_d = 1'b1;
s_direction_in_d = s_work_direction_in;
s_transfer_type_d = s_work_type;
s_setup_d = s_work_setup;
s_retry_count_d = '0;
if (s_work_cancel) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_work_index;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultCanceled;
s_result_valid_d = 1'b1;
end else begin
s_state_d = HostTokenRequest;
end
end
end else if ((active_role_i == usb2_pkg::Usb2RoleDevice) && rx_packet_done_i &&
rx_packet_good_i && s_device_token_match &&
((rx_packet_pid_i == usb2_pkg::Usb2PidOut) ||
(rx_packet_pid_i == usb2_pkg::Usb2PidSetup) ||
(rx_packet_pid_i == usb2_pkg::Usb2PidIn))) begin
s_selected_index_d = rx_token_endpoint_i;
s_context_host_d = 1'b0;
s_direction_in_d = rx_packet_pid_i == usb2_pkg::Usb2PidIn;
s_transfer_type_d = endpoint_cfg_i[rx_token_endpoint_i][5:4];
s_setup_d = rx_packet_pid_i == usb2_pkg::Usb2PidSetup;
s_setup_count_d = '0;
s_setup_data_d = '0;
if (rx_packet_pid_i == usb2_pkg::Usb2PidIn) begin
if (endpoint_cfg_i[rx_token_endpoint_i][2]) begin
s_handshake_pid_d = usb2_pkg::Usb2PidStall;
s_state_d = HandshakeRequest;
end else if (s_device_in_ready_q[rx_token_endpoint_i[EndpointIndexWidth-1:0]]) begin
s_state_d = TransmitArm;
end else begin
s_handshake_pid_d = usb2_pkg::Usb2PidNak;
s_state_d = HandshakeRequest;
end
end else if (endpoint_cfg_i[rx_token_endpoint_i][3] ||
!s_device_out_armed_q[rx_token_endpoint_i[EndpointIndexWidth-1:0]]) begin
s_handshake_pid_d = endpoint_cfg_i[rx_token_endpoint_i][3] ?
usb2_pkg::Usb2PidStall : usb2_pkg::Usb2PidNak;
s_state_d = ReceiveDiscard;
end else begin
s_state_d = ReceiveArm;
end
end
end
HostTokenRequest: begin
if (tx_request_valid_o && tx_request_ready_i) begin
s_timeout_count_d = 32'd0;
s_state_d = HostTokenWait;
end
end
HostTokenWait: begin
if (tx_error_i || s_timeout_expired) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_active_index;
s_result_data_d[usb2_pkg::USB2_RESULT_DIRECTION_IN] = s_direction_in_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] =
s_timeout_expired ? usb2_pkg::Usb2ResultTimeout : usb2_pkg::Usb2ResultProtocol;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else if (tx_done_i) begin
s_timeout_count_d = 32'd0;
s_state_d = s_direction_in_q ? ReceiveArm : TransmitArm;
end
end
ReceiveArm: begin
if (store_rx_start_valid_o && store_rx_start_ready_i) begin
s_timeout_count_d = 32'd0;
s_state_d = ReceiveWait;
end
end
ReceiveWait: begin
if (rx_payload_valid_i && !store_rx_ready_i) begin
s_store_rx_cancel = 1'b1;
s_protocol_err_d = 1'b1;
s_state_d = ReceiveCancel;
end else if (rx_packet_done_i) begin
if (rx_packet_good_i && usb2_pkg::usb2_pid_is_data(rx_packet_pid_i)) begin
s_rx_len_d = {4'd0, rx_payload_length_i};
s_store_rx_commit = 1'b1;
s_state_d = ReceiveCommit;
end else if (rx_packet_good_i && s_context_host_q && usb2_pkg::usb2_pid_is_handshake(
rx_packet_pid_i
)) begin
s_store_rx_cancel = 1'b1;
s_handshake_pid_d = rx_packet_pid_i;
s_state_d = ReceiveCancel;
end else begin
s_store_rx_cancel = 1'b1;
s_protocol_err_d = 1'b1;
s_state_d = ReceiveCancel;
end
end else if (s_timeout_expired) begin
s_store_rx_cancel = 1'b1;
s_handshake_pid_d = usb2_pkg::Usb2PidNak;
s_state_d = ReceiveCancel;
end
end
ReceiveCommit: begin
if (store_rx_done_i) begin
s_rx_len_d = store_rx_bytes_i;
if (store_rx_overflow_i) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] =
s_context_host_q ? usb2_pkg::Usb2RoleHost : usb2_pkg::Usb2RoleDevice;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultOverflow;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else if (s_transfer_type_q == usb2_pkg::Usb2TransferIsochronous) begin
s_handshake_pid_d = usb2_pkg::Usb2PidAck;
s_state_d = HandshakeWait;
end else begin
s_handshake_pid_d = usb2_pkg::Usb2PidAck;
s_state_d = HandshakeRequest;
end
end
end
ReceiveCancel: begin
if (store_rx_done_i) begin
if (s_context_host_q &&
((s_handshake_pid_q == usb2_pkg::Usb2PidNak) ||
(s_handshake_pid_q == usb2_pkg::Usb2PidNyet)) &&
(s_retry_count_q < 4'(MaxRetries))) begin
s_retry_count_d = s_retry_count_q + 1'b1;
s_retry_d = 1'b1;
s_timeout_count_d = 32'd0;
s_state_d = HostTokenRequest;
end else if (s_context_host_q) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_DIRECTION_IN] = 1'b1;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] =
(s_handshake_pid_q == usb2_pkg::Usb2PidStall) ?
usb2_pkg::Usb2ResultStall :
(s_timeout_expired ? usb2_pkg::Usb2ResultTimeout : usb2_pkg::Usb2ResultCrc);
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else begin
s_state_d = Idle;
end
end
end
ReceiveDiscard: begin
if (rx_packet_done_i) begin
if (rx_packet_good_i && usb2_pkg::usb2_pid_is_data(rx_packet_pid_i)) begin
s_state_d = HandshakeRequest;
end else begin
s_state_d = Idle;
end
end
end
TransmitArm: begin
s_store_tx_complete_d = 1'b0;
if (store_tx_start_valid_o && store_tx_start_ready_i) begin
s_state_d = TransmitRequest;
end
end
TransmitRequest: begin
if (tx_request_valid_o && tx_request_ready_i) begin
s_timeout_count_d = 32'd0;
s_state_d = TransmitWait;
end
end
TransmitWait: begin
if (tx_error_i || s_timeout_expired) begin
s_protocol_err_d = 1'b1;
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] =
s_context_host_q ? usb2_pkg::Usb2RoleHost : usb2_pkg::Usb2RoleDevice;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_DIRECTION_IN] = !s_context_host_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] =
s_timeout_expired ? usb2_pkg::Usb2ResultTimeout : usb2_pkg::Usb2ResultProtocol;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else if (tx_done_i && s_store_tx_complete_q) begin
if (s_transfer_type_q == usb2_pkg::Usb2TransferIsochronous) begin
if (!s_context_host_q && s_device_endpoint_valid) begin
s_device_in_ready_d[s_selected_index_q[EndpointIndexWidth-1:0]] = 1'b0;
end
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] =
s_context_host_q ? usb2_pkg::Usb2RoleHost : usb2_pkg::Usb2RoleDevice;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_DIRECTION_IN] = !s_context_host_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultSuccess;
s_result_data_d[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] = s_active_len;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else begin
s_timeout_count_d = 32'd0;
s_state_d = HandshakeWait;
end
end
end
HandshakeRequest: begin
if (tx_request_valid_o && tx_request_ready_i) begin
s_timeout_count_d = 32'd0;
s_state_d = HandshakeWait;
end
end
HandshakeWait: begin
if (!s_context_host_q && !s_direction_in_q) begin
if (tx_done_i || (s_transfer_type_q == usb2_pkg::Usb2TransferIsochronous)) begin
s_device_out_armed_d[s_selected_index_q[EndpointIndexWidth-1:0]] = 1'b0;
s_device_out_toggle_d[s_selected_index_q[EndpointIndexWidth-1:0]] =
!s_device_out_toggle_q[s_selected_index_q[EndpointIndexWidth-1:0]];
if (s_setup_q && (s_setup_count_q == 4'd8)) begin
s_setup_valid_d = 1'b1;
end
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleDevice;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultSuccess;
s_result_data_d[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] = s_rx_len_q;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end
end else if (s_context_host_q && s_direction_in_q) begin
if (tx_done_i || (s_transfer_type_q == usb2_pkg::Usb2TransferIsochronous)) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_DIRECTION_IN] = 1'b1;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultSuccess;
s_result_data_d[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] = s_rx_len_q;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end
end else if (rx_packet_done_i && rx_packet_good_i && usb2_pkg::usb2_pid_is_handshake(
rx_packet_pid_i
)) begin
if (rx_packet_pid_i == usb2_pkg::Usb2PidAck) begin
if (s_context_host_q) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultSuccess;
s_result_data_d[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] = s_active_len;
s_result_valid_d = 1'b1;
end else begin
s_device_in_ready_d[s_selected_index_q[EndpointIndexWidth-1:0]] = 1'b0;
s_device_in_toggle_d[s_selected_index_q[EndpointIndexWidth-1:0]] =
!s_device_in_toggle_q[s_selected_index_q[EndpointIndexWidth-1:0]];
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleDevice;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_DIRECTION_IN] = 1'b1;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultSuccess;
s_result_data_d[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] =
s_device_in_len_q[s_selected_index_q];
s_result_valid_d = 1'b1;
end
s_state_d = ResultWait;
end else if (s_context_host_q &&
((rx_packet_pid_i == usb2_pkg::Usb2PidNak) ||
(rx_packet_pid_i == usb2_pkg::Usb2PidNyet)) &&
(s_retry_count_q < 4'(MaxRetries))) begin
s_retry_count_d = s_retry_count_q + 1'b1;
s_retry_d = 1'b1;
s_state_d = HostTokenRequest;
end else if (rx_packet_pid_i == usb2_pkg::Usb2PidStall) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] =
s_context_host_q ? usb2_pkg::Usb2RoleHost : usb2_pkg::Usb2RoleDevice;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultStall;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else if (!s_context_host_q) begin
s_state_d = Idle;
end
end else if (s_timeout_expired) begin
if (s_context_host_q) begin
s_result_data_d = '0;
s_result_data_d[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
s_result_data_d[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] = s_selected_index_q;
s_result_data_d[usb2_pkg::USB2_RESULT_CODE_LSB+:4] = usb2_pkg::Usb2ResultTimeout;
s_result_valid_d = 1'b1;
s_state_d = ResultWait;
end else begin
s_state_d = Idle;
end
end
end
ResultWait: begin
if (!s_result_valid_d) begin
s_state_d = Idle;
end
end
default: begin
s_protocol_err_d = 1'b1;
s_state_d = Idle;
end
endcase
if (active_role_i == usb2_pkg::Usb2RoleIdle) begin
s_state_d = Idle;
end
end
dffr #(
.DATA_WIDTH(5)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(64)
) u_work_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_work_d),
.dat_o (s_work_q)
);
dffr #(
.DATA_WIDTH(NumEndpoints)
) u_device_in_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_in_ready_d),
.dat_o (s_device_in_ready_q)
);
dffr #(
.DATA_WIDTH(NumEndpoints)
) u_device_out_armed_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_out_armed_d),
.dat_o (s_device_out_armed_q)
);
dffr #(
.DATA_WIDTH(NumEndpoints)
) u_device_in_toggle_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_in_toggle_d),
.dat_o (s_device_in_toggle_q)
);
dffr #(
.DATA_WIDTH(NumEndpoints)
) u_device_out_toggle_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_out_toggle_d),
.dat_o (s_device_out_toggle_q)
);
dffr #(
.DATA_WIDTH(4)
) u_selected_index_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_selected_index_d),
.dat_o (s_selected_index_q)
);
dffr #(
.DATA_WIDTH(1)
) u_context_host_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_context_host_d),
.dat_o (s_context_host_q)
);
dffr #(
.DATA_WIDTH(1)
) u_direction_in_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_direction_in_d),
.dat_o (s_direction_in_q)
);
dffr #(
.DATA_WIDTH(2)
) u_transfer_type_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_transfer_type_d),
.dat_o (s_transfer_type_q)
);
dffr #(
.DATA_WIDTH(1)
) u_setup_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_setup_d),
.dat_o (s_setup_q)
);
dffr #(
.DATA_WIDTH(4)
) u_handshake_pid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_handshake_pid_d),
.dat_o (s_handshake_pid_q)
);
dffr #(
.DATA_WIDTH(4)
) u_retry_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_retry_count_d),
.dat_o (s_retry_count_q)
);
dffr #(
.DATA_WIDTH(32)
) u_timeout_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_timeout_count_d),
.dat_o (s_timeout_count_q)
);
dffr #(
.DATA_WIDTH(15)
) u_rx_length_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_rx_len_d),
.dat_o (s_rx_len_q)
);
dffr #(
.DATA_WIDTH(64)
) u_result_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_data_d),
.dat_o (s_result_data_q)
);
dffr #(
.DATA_WIDTH(1)
) u_result_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_result_valid_d),
.dat_o (s_result_valid_q)
);
dffr #(
.DATA_WIDTH(64)
) u_setup_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_setup_data_d),
.dat_o (s_setup_data_q)
);
dffr #(
.DATA_WIDTH(4)
) u_setup_count_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_setup_count_d),
.dat_o (s_setup_count_q)
);
dffr #(
.DATA_WIDTH(1)
) u_setup_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_setup_valid_d),
.dat_o (s_setup_valid_q)
);
dffr #(
.DATA_WIDTH(1)
) u_retry_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_retry_d),
.dat_o (s_retry_q)
);
dffr #(
.DATA_WIDTH(1)
) u_protocol_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_protocol_err_d),
.dat_o (s_protocol_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_store_tx_complete_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_store_tx_complete_d),
.dat_o (s_store_tx_complete_q)
);
for (genvar endpoint = 0; endpoint < NumEndpoints; endpoint++) begin : gen_device_context
dffr #(
.DATA_WIDTH(12)
) u_in_base_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_in_base_d[endpoint]),
.dat_o (s_device_in_base_q[endpoint])
);
dffr #(
.DATA_WIDTH(12)
) u_out_base_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_out_base_d[endpoint]),
.dat_o (s_device_out_base_q[endpoint])
);
dffr #(
.DATA_WIDTH(15)
) u_in_length_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_in_len_d[endpoint]),
.dat_o (s_device_in_len_q[endpoint])
);
dffr #(
.DATA_WIDTH(15)
) u_out_limit_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_device_out_limit_d[endpoint]),
.dat_o (s_device_out_limit_q[endpoint])
);
end
`ifndef SYNTHESIS
initial begin
if ((NumEndpoints != 8) || (MaxRetries < 0) || (MaxRetries > 15)) begin
$fatal(1, "usb2_transaction_engine: unsupported endpoint or retry configuration");
end
end
`endif
endmodule
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
interface usb2_ulpi_if;
logic [7:0] data_di_i;
logic [7:0] data_do_o;
logic data_oe_o;
logic dir_i;
logic nxt_i;
logic stp_o;
logic reset_n_o;
modport dut(
input data_di_i,
input dir_i,
input nxt_i,
output data_do_o,
output data_oe_o,
output stp_o,
output reset_n_o
);
modport phy(
output data_di_i,
output dir_i,
output nxt_i,
input data_do_o,
input data_oe_o,
input stp_o,
input reset_n_o
);
endinterface
+435
View File
@@ -0,0 +1,435 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_ulpi_link #(
parameter int ViewportTimeoutCycles = 1024
) (
// verilog_format: off -- preserve ULPI, packet, and viewport port groups
input logic clk_i,
input logic rst_n_i,
input logic enable_i,
input logic phy_reset_n_i,
input logic tx_start_valid_i,
output logic tx_start_ready_o,
input logic [3:0] tx_pid_i,
input logic tx_has_data_i,
input logic tx_data_valid_i,
output logic tx_data_ready_o,
input logic [7:0] tx_data_i,
input logic tx_data_last_i,
output logic tx_done_o,
output logic tx_error_o,
output logic rx_start_o,
output logic rx_valid_o,
output logic [7:0] rx_data_o,
output logic rx_end_o,
output logic rx_error_o,
output logic [1:0] line_state_o,
output logic [1:0] vbus_state_o,
output logic id_ground_o,
input logic viewport_valid_i,
output logic viewport_ready_o,
input logic viewport_write_i,
input logic [5:0] viewport_addr_i,
input logic [7:0] viewport_write_data_i,
output logic viewport_resp_valid_o,
output logic [7:0] viewport_read_data_o,
output logic viewport_error_o,
output logic link_ready_o,
usb2_ulpi_if.dut ulpi
// verilog_format: on
);
localparam int ViewportCountWidth = (ViewportTimeoutCycles > 1) ? $clog2(
ViewportTimeoutCycles + 1
) : 1;
typedef enum logic [3:0] {
Idle,
TxCommand,
TxPayload,
TxStop,
ViewCommand,
ViewWriteData,
ViewWriteStop,
ViewReadTurn,
ViewReadData,
ViewReadRelease
} link_state_e;
link_state_e s_state_d, s_state_q;
logic [3:0] s_state_bits_q;
logic [3:0] s_tx_pid_d, s_tx_pid_q;
logic s_tx_has_data_d, s_tx_has_data_q;
logic s_view_write_d, s_view_write_q;
logic [5:0] s_view_addr_d, s_view_addr_q;
logic [7:0] s_view_write_data_d, s_view_write_data_q;
logic [7:0] s_view_read_data_d, s_view_read_data_q;
logic [ViewportCountWidth-1:0] s_view_timeout_d, s_view_timeout_q;
logic [1:0] s_line_state_d, s_line_state_q;
logic [1:0] s_vbus_state_d, s_vbus_state_q;
logic s_id_ground_d, s_id_ground_q;
logic s_rx_active_d, s_rx_active_q;
logic s_rx_err_d, s_rx_err_q;
logic s_tx_done_d, s_tx_done_q;
logic s_tx_err_d, s_tx_err_q;
logic s_view_resp_valid_d, s_view_resp_valid_q;
logic s_view_err_d, s_view_err_q;
logic s_view_timeout_expired;
logic s_rx_cmd;
logic s_rx_active_now;
assign s_state_q = link_state_e'(s_state_bits_q);
assign s_rx_cmd = enable_i && phy_reset_n_i && ulpi.dir_i && !ulpi.nxt_i && (s_state_q == Idle);
assign s_rx_active_now = s_rx_cmd ? ulpi.data_di_i[4] : s_rx_active_q;
assign s_view_timeout_expired = s_view_timeout_q == ViewportCountWidth'(ViewportTimeoutCycles);
assign tx_start_ready_o = enable_i && phy_reset_n_i && (s_state_q == Idle) && !ulpi.dir_i;
assign viewport_ready_o = phy_reset_n_i && (s_state_q == Idle) && !ulpi.dir_i &&
!tx_start_valid_i;
assign tx_data_ready_o = (s_state_q == TxPayload) && !ulpi.dir_i && ulpi.nxt_i;
assign tx_done_o = s_tx_done_q;
assign tx_error_o = s_tx_err_q;
assign viewport_resp_valid_o = s_view_resp_valid_q;
assign viewport_read_data_o = s_view_read_data_q;
assign viewport_error_o = s_view_err_q;
assign link_ready_o = enable_i && phy_reset_n_i && (s_state_q == Idle) && !ulpi.dir_i;
assign line_state_o = s_line_state_q;
assign vbus_state_o = s_vbus_state_q;
assign id_ground_o = s_id_ground_q;
assign rx_start_o = s_rx_cmd && ulpi.data_di_i[4] && !s_rx_active_q;
assign rx_end_o = s_rx_cmd && !ulpi.data_di_i[4] && s_rx_active_q;
assign rx_valid_o = enable_i && phy_reset_n_i && (s_state_q == Idle) && ulpi.dir_i &&
ulpi.nxt_i && s_rx_active_q;
assign rx_data_o = ulpi.data_di_i;
assign rx_error_o = s_rx_err_q || (s_rx_cmd && ulpi.data_di_i[5]);
always_comb begin
s_state_d = s_state_q;
s_tx_pid_d = s_tx_pid_q;
s_tx_has_data_d = s_tx_has_data_q;
s_view_write_d = s_view_write_q;
s_view_addr_d = s_view_addr_q;
s_view_write_data_d = s_view_write_data_q;
s_view_read_data_d = s_view_read_data_q;
s_view_timeout_d = s_view_timeout_q;
s_line_state_d = s_line_state_q;
s_vbus_state_d = s_vbus_state_q;
s_id_ground_d = s_id_ground_q;
s_rx_active_d = s_rx_active_now;
s_rx_err_d = s_rx_err_q;
s_tx_done_d = 1'b0;
s_tx_err_d = 1'b0;
s_view_resp_valid_d = 1'b0;
s_view_err_d = 1'b0;
if (s_rx_cmd) begin
s_line_state_d = ulpi.data_di_i[1:0];
s_vbus_state_d = ulpi.data_di_i[3:2];
s_id_ground_d = ulpi.data_di_i[6];
if (ulpi.data_di_i[5]) begin
s_rx_err_d = 1'b1;
end
if (!ulpi.data_di_i[4]) begin
s_rx_err_d = 1'b0;
end
end
unique case (s_state_q)
Idle: begin
s_view_timeout_d = '0;
if (!enable_i || !phy_reset_n_i) begin
s_rx_active_d = 1'b0;
s_rx_err_d = 1'b0;
end else if (tx_start_valid_i && tx_start_ready_o) begin
s_tx_pid_d = tx_pid_i;
s_tx_has_data_d = tx_has_data_i;
s_state_d = TxCommand;
end else if (viewport_valid_i && viewport_ready_o) begin
s_view_write_d = viewport_write_i;
s_view_addr_d = viewport_addr_i;
s_view_write_data_d = viewport_write_data_i;
s_state_d = ViewCommand;
end
end
TxCommand: begin
if (ulpi.dir_i) begin
s_state_d = Idle;
s_tx_err_d = 1'b1;
end else if (ulpi.nxt_i) begin
s_state_d = s_tx_has_data_q ? TxPayload : TxStop;
end
end
TxPayload: begin
if (ulpi.dir_i) begin
s_state_d = Idle;
s_tx_err_d = 1'b1;
end else if (tx_data_valid_i && tx_data_ready_o && tx_data_last_i) begin
s_state_d = TxStop;
end
end
TxStop: begin
s_state_d = Idle;
s_tx_done_d = 1'b1;
end
ViewCommand: begin
s_view_timeout_d = s_view_timeout_q + 1'b1;
if (ulpi.dir_i || s_view_timeout_expired) begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
s_view_err_d = 1'b1;
end else if (ulpi.nxt_i) begin
s_view_timeout_d = '0;
s_state_d = s_view_write_q ? ViewWriteData : ViewReadTurn;
end
end
ViewWriteData: begin
s_view_timeout_d = s_view_timeout_q + 1'b1;
if (ulpi.dir_i || s_view_timeout_expired) begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
s_view_err_d = 1'b1;
end else if (ulpi.nxt_i) begin
s_state_d = ViewWriteStop;
end
end
ViewWriteStop: begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
end
ViewReadTurn: begin
s_view_timeout_d = s_view_timeout_q + 1'b1;
if (s_view_timeout_expired) begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
s_view_err_d = 1'b1;
end else if (ulpi.dir_i) begin
s_view_timeout_d = '0;
s_state_d = ViewReadData;
end
end
ViewReadData: begin
s_view_timeout_d = s_view_timeout_q + 1'b1;
if (s_view_timeout_expired) begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
s_view_err_d = 1'b1;
end else if (ulpi.dir_i && ulpi.nxt_i) begin
s_view_read_data_d = ulpi.data_di_i;
s_view_timeout_d = '0;
s_state_d = ViewReadRelease;
end
end
ViewReadRelease: begin
s_view_timeout_d = s_view_timeout_q + 1'b1;
if (!ulpi.dir_i) begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
end else if (s_view_timeout_expired) begin
s_state_d = Idle;
s_view_resp_valid_d = 1'b1;
s_view_err_d = 1'b1;
end
end
default: begin
s_state_d = Idle;
s_tx_err_d = 1'b1;
end
endcase
end
always_comb begin
ulpi.data_do_o = 8'd0;
ulpi.data_oe_o = 1'b0;
ulpi.stp_o = 1'b0;
ulpi.reset_n_o = phy_reset_n_i;
unique case (s_state_q)
TxCommand: begin
ulpi.data_do_o = {2'b01, 2'b00, s_tx_pid_q};
ulpi.data_oe_o = !ulpi.dir_i;
end
TxPayload: begin
ulpi.data_do_o = tx_data_i;
ulpi.data_oe_o = !ulpi.dir_i && tx_data_valid_i;
end
TxStop: begin
ulpi.stp_o = 1'b1;
end
ViewCommand: begin
ulpi.data_do_o = {s_view_write_q ? 2'b10 : 2'b11, s_view_addr_q};
ulpi.data_oe_o = !ulpi.dir_i;
end
ViewWriteData: begin
ulpi.data_do_o = s_view_write_data_q;
ulpi.data_oe_o = !ulpi.dir_i;
end
ViewWriteStop: begin
ulpi.stp_o = 1'b1;
end
default: begin
end
endcase
end
dffr #(
.DATA_WIDTH(4)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(4)
) u_tx_pid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tx_pid_d),
.dat_o (s_tx_pid_q)
);
dffr #(
.DATA_WIDTH(1)
) u_tx_has_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tx_has_data_d),
.dat_o (s_tx_has_data_q)
);
dffr #(
.DATA_WIDTH(1)
) u_view_write_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_write_d),
.dat_o (s_view_write_q)
);
dffr #(
.DATA_WIDTH(6)
) u_view_addr_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_addr_d),
.dat_o (s_view_addr_q)
);
dffr #(
.DATA_WIDTH(8)
) u_view_write_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_write_data_d),
.dat_o (s_view_write_data_q)
);
dffr #(
.DATA_WIDTH(8)
) u_view_read_data_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_read_data_d),
.dat_o (s_view_read_data_q)
);
dffr #(
.DATA_WIDTH(ViewportCountWidth)
) u_view_timeout_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_timeout_d),
.dat_o (s_view_timeout_q)
);
dffr #(
.DATA_WIDTH(2)
) u_line_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_line_state_d),
.dat_o (s_line_state_q)
);
dffr #(
.DATA_WIDTH(2)
) u_vbus_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_vbus_state_d),
.dat_o (s_vbus_state_q)
);
dffr #(
.DATA_WIDTH(1)
) u_id_ground_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_id_ground_d),
.dat_o (s_id_ground_q)
);
dffr #(
.DATA_WIDTH(1)
) u_rx_active_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_rx_active_d),
.dat_o (s_rx_active_q)
);
dffr #(
.DATA_WIDTH(1)
) u_rx_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_rx_err_d),
.dat_o (s_rx_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_tx_done_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tx_done_d),
.dat_o (s_tx_done_q)
);
dffr #(
.DATA_WIDTH(1)
) u_tx_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_tx_err_d),
.dat_o (s_tx_err_q)
);
dffr #(
.DATA_WIDTH(1)
) u_view_resp_valid_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_resp_valid_d),
.dat_o (s_view_resp_valid_q)
);
dffr #(
.DATA_WIDTH(1)
) u_view_error_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_view_err_d),
.dat_o (s_view_err_q)
);
`ifndef SV_ASSRT_DISABLE
property p_link_never_drives_with_dir;
@(posedge clk_i) disable iff (!rst_n_i) ulpi.dir_i |-> !ulpi.data_oe_o;
endproperty
assert property (p_link_never_drives_with_dir);
property p_tx_payload_stable_when_stalled;
@(posedge clk_i) disable iff (!rst_n_i)
(s_state_q == TxPayload && tx_data_valid_i && !tx_data_ready_o) |=>
$stable(
tx_data_i
) && $stable(
tx_data_last_i
);
endproperty
assert property (p_tx_payload_stable_when_stalled);
`endif
`ifndef SYNTHESIS
initial begin
if (ViewportTimeoutCycles < 2) begin
$fatal(1, "usb2_ulpi_link: ViewportTimeoutCycles must be at least two");
end
end
`endif
endmodule
+17
View File
@@ -141,6 +141,14 @@
{
"symbol": "PERF_SDIO1_WAIT_HI",
"offset": "0x98"
},
{
"symbol": "PERF_USB2_WAIT_LO",
"offset": "0x9C"
},
{
"symbol": "PERF_USB2_WAIT_HI",
"offset": "0xA0"
}
],
"regions": [
@@ -362,6 +370,15 @@
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_USB2",
"base": "0x10016000",
"size": "0x00001000",
"route": "apb4_periph",
"kind": "active",
"public": true,
"user_access": "none"
},
{
"symbol": "APB4_ARCHINFO",
"base": "0x20000000",
+14 -7
View File
@@ -61,6 +61,12 @@ module retrosoc_tb;
wire s_sdio1_clk;
tri1 s_sdio1_cmd;
tri1 [ 3:0] s_sdio1_dat;
wire s_usb2_ulpi_clk;
tri0 s_usb2_ulpi_dir;
tri0 s_usb2_ulpi_nxt;
tri0 [ 7:0] s_usb2_ulpi_data;
wire s_usb2_ulpi_stp;
wire s_usb2_ulpi_reset_n;
wire s_sdram_clk;
wire s_sdram_cke;
wire s_sdram_cs_n;
@@ -83,13 +89,14 @@ module retrosoc_tb;
always #(1000 / AUD_CPU_FREQ / 2) r_aud_clk = (r_aud_clk === 1'b0);
// connect inout pad
assign s_ext_clk = r_ext_clk;
assign s_aud_clk = r_aud_clk;
assign s_rst_n = r_rst_n;
assign s_jtag_tck = 1'b0;
assign s_jtag_tms = 1'b0;
assign s_jtag_tdi = 1'b0;
assign s_jtag_trst_n = 1'b0;
assign s_ext_clk = r_ext_clk;
assign s_aud_clk = r_aud_clk;
assign s_rst_n = r_rst_n;
assign s_jtag_tck = 1'b0;
assign s_jtag_tms = 1'b0;
assign s_jtag_tdi = 1'b0;
assign s_jtag_trst_n = 1'b0;
assign s_usb2_ulpi_clk = s_ext_clk;
retrosoc_asic u_retrosoc_asic (
`include "retrosoc_asic_tb_bindings.svh"
+20 -10
View File
@@ -71,17 +71,27 @@ module retrosoc_top (
wire s_jtag_tdi;
wire s_jtag_trst_n;
wire s_jtag_tdo;
wire s_usb2_ulpi_clk;
tri0 s_usb2_ulpi_dir;
tri0 s_usb2_ulpi_nxt;
// This wrapper intentionally has no external ULPI PHY model.
// verilator lint_off UNUSEDSIGNAL
tri0 [ 7:0] s_usb2_ulpi_data;
wire s_usb2_ulpi_stp;
wire s_usb2_ulpi_reset_n;
// verilator lint_on UNUSEDSIGNAL
assign s_clk = ext_clk_i;
assign s_rst_n = rst_n_i;
assign s_jtag_tck = jtag_tck_i;
assign s_jtag_tms = jtag_tms_i;
assign s_jtag_tdi = jtag_tdi_i;
assign s_jtag_trst_n = jtag_trst_n_i;
assign jtag_tdo_o = s_jtag_tdo;
assign test_done_o = u_retrosoc_asic.s_test_done;
assign test_pass_o = u_retrosoc_asic.s_test_pass;
assign test_code_o = u_retrosoc_asic.s_test_code;
assign s_clk = ext_clk_i;
assign s_rst_n = rst_n_i;
assign s_jtag_tck = jtag_tck_i;
assign s_jtag_tms = jtag_tms_i;
assign s_jtag_tdi = jtag_tdi_i;
assign s_jtag_trst_n = jtag_trst_n_i;
assign jtag_tdo_o = s_jtag_tdo;
assign test_done_o = u_retrosoc_asic.s_test_done;
assign test_pass_o = u_retrosoc_asic.s_test_pass;
assign test_code_o = u_retrosoc_asic.s_test_code;
assign s_usb2_ulpi_clk = s_clk;
retrosoc_asic u_retrosoc_asic (
`include "retrosoc_asic_verilator_bindings.svh"
);
+1
View File
@@ -14,6 +14,7 @@
/clusterip/common/rtl/utils/lfsr.sv
/clusterip/common/rtl/utils/bin2gray.sv
/clusterip/common/rtl/utils/gray2bin.sv
/clusterip/common/rtl/base/ecc_secded.sv
/clusterip/common/rtl/utils/edge_det.sv
/clusterip/common/rtl/cdc/cdc_sync.sv
/clusterip/common/rtl/cdc/cdc_rst_ctrlr.sv
+1
View File
@@ -22,4 +22,5 @@
+incdir+/ip/multimedia
+incdir+/ip/peripheral
+incdir+/ip/security
+incdir+/ip/usb
+incdir+/third_party/model/sdram/
+16
View File
@@ -66,6 +66,22 @@
/ip/peripheral/dma_define.svh
/ip/peripheral/dma_req_if.sv
/ip/peripheral/dma_axi4_master.sv
/ip/usb/usb2_pkg.sv
/ip/usb/usb2_define.svh
/ip/usb/usb2_ulpi_if.sv
/ip/usb/usb2_dma_descriptor.sv
/ip/usb/usb2_packet_ram.sv
/ip/usb/usb2_ulpi_link.sv
/ip/usb/usb2_sie_rx.sv
/ip/usb/usb2_sie_tx.sv
/ip/usb/usb2_reg.sv
/ip/usb/usb2_dma.sv
/ip/usb/usb2_role_controller.sv
/ip/usb/usb2_packet_store.sv
/ip/usb/usb2_transaction_engine.sv
/ip/usb/usb2_link_domain.sv
/ip/usb/usb2_scheduler.sv
/ip/usb/apb4_usb2.sv
/ip/security/crypto_pkg.sv
/ip/security/crypto_define.svh
/ip/security/crypto_aes_core.sv
+1
View File
@@ -5,3 +5,4 @@
/tech/tc_opipsram_delay.sv
/tech/tc_pll.sv
/tech/tc_sram.sv
/tech/tc_usb2_packet_ram.sv
+6 -5
View File
@@ -11,7 +11,7 @@ decoder template. `apb4_system` is instantiated as `u_apb4_system`; its AXI4
configuration port is wired by generated `apb4_system_fabric.svh`.
`apb4_periph` remains the self-owned APB4 peripheral container. Generated
fabric links are explicit 32-bit AXI4 interfaces; `mgmt`, `user`, `dma`,
`sdio0`, and `sdio1` are the five native AXI4 masters, while `cfg` and
`sdio0`, `sdio1`, and `usb2` are generated native AXI4 links, while `cfg` and
`system` are target-side links.
Interface arrays are confined to the AXI4 bus implementation and flattened by
the existing synthesis/export flow before FPGA or netlist simulation.
@@ -21,9 +21,9 @@ RIBP island at `0x1000_0000`. Every active `apb4_periph` memory-map region
must appear exactly once. Disabled targets keep their slot and interface
declaration but cannot own an address region. This list stays separate from
`apb4_system_targets`, which owns the `0x2000_0000` `apb4_system` island.
Slot 15 is the active SDIO0 management window; SDIO1 is appended at slot 18
and the management-only crypto controller at slot 19, so the existing slots
0..17 remain stable.
Slot 15 is the active SDIO0 management window; SDIO1 is appended at slot 18,
the management-only crypto controller at slot 19, and USB2 at slot 20, so the
existing slots 0..17 remain stable.
Each `gpio_alt_functions` entry defines both alternate modes for one GPIO pin.
`inputs` are driven from the selected GPIO input, and `do` and `oe` define the
@@ -45,7 +45,8 @@ wrapper bindings, the core-vector wiring, and simulation-only assertions.
Existing allocated core IRQ bits retain their compatibility mapping; additions
must use currently unallocated bits and preserve the existing entries. SDIO0
and SDIO1 use APB4 group bits 16 and 17 and core bits 10 and 21. Crypto uses
APB4 group bit 18 and core bit 23. All three remain management-only in the
APB4 group bit 18 and core bit 23. USB2 uses APB4 group bit 19 and core bit 24.
All four remain management-only in the
generated user IRQ mask. Removing a source leaves its
core IRQ bit unallocated and driven low. Firmware does not
expose a generic external interrupt API until the SoC includes a claim/complete
@@ -76,6 +76,21 @@
"period_ns": 41.666666667,
"async_group": "dvp"
}
},
{
"name": "usb2_ulpi",
"clock": "clk_ulpi_i",
"reset": "s_ulpi_rst_n",
"reset_source": "rst_n_i",
"reset_primitive": "rst_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_ulpi_rst_sync",
"sta": {
"source_port": "usb2_ulpi_clk_i_pad",
"object_type": "port",
"period_ns": 16.666666667,
"async_group": "usb2_ulpi"
}
}
],
"crossings": [
@@ -222,6 +237,198 @@
"primitive": "cdc_sync",
"path": "rtl/managed/clusterip/wdg/rtl/apb4_wdg.sv",
"instance": "u_reset_cause_sync"
},
{
"name": "usb2_work",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_work_cdc"
},
{
"name": "usb2_result",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_result_cdc"
},
{
"name": "usb2_setup",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_setup_cdc"
},
{
"name": "usb2_fill_command",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_fill_command_cdc"
},
{
"name": "usb2_drain_command",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_drain_command_cdc"
},
{
"name": "usb2_buffer_event",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_buffer_event_cdc"
},
{
"name": "usb2_view_request",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_view_request_cdc"
},
{
"name": "usb2_view_response",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "async_reqack",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_view_response_cdc"
},
{
"name": "usb2_fill_stream",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_fifo",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_fill_stream_cdc"
},
{
"name": "usb2_drain_stream",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "cdc_fifo",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_drain_stream_cdc"
},
{
"name": "usb2_role",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_role_sync"
},
{
"name": "usb2_role_reset",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_role_reset_sync"
},
{
"name": "usb2_phy_reset",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_phy_reset_sync"
},
{
"name": "usb2_high_speed",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_high_speed_sync"
},
{
"name": "usb2_enable",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_enable_sync"
},
{
"name": "usb2_device_addr",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_device_addr_sync"
},
{
"name": "usb2_endpoint_config",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_endpoint_cfg_sync"
},
{
"name": "usb2_timeout",
"source": "system",
"destination": "usb2_ulpi",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_timeout_sync"
},
{
"name": "usb2_phy_status",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_phy_status_sync"
},
{
"name": "usb2_transaction_busy",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_transaction_busy_sync"
},
{
"name": "usb2_frame",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_frame_sync"
},
{
"name": "usb2_ecc_counts",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "cdc_sync",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_ecc_counts_sync"
},
{
"name": "usb2_retry_event",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "edge_det",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_retry_event_sync"
},
{
"name": "usb2_protocol_event",
"source": "usb2_ulpi",
"destination": "system",
"primitive": "edge_det",
"path": "rtl/ip/usb/apb4_usb2.sv",
"instance": "u_protocol_event_sync"
}
],
"same_domain_contracts": [
@@ -16,7 +16,7 @@ from typing import Any
SV_IDENTIFIER_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
SV_REFERENCE_RE = re.compile(r"[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*(?:\[[0-9]+\])?$")
SV_CONSTANT_RE = re.compile(r"(?:1'[bB][01]|'[01])$")
FABRIC_LINK_NAMES = ("mgmt", "user", "dma", "sdio0", "sdio1", "cfg", "system")
FABRIC_LINK_NAMES = ("mgmt", "user", "dma", "sdio0", "sdio1", "usb2", "cfg", "system")
FABRIC_PROTOCOLS = {name: "axi4" for name in FABRIC_LINK_NAMES}
IRQ_GROUP_NAMES = ("apb4_periph", "apb4_system")
IRQ_VECTOR_WIDTH = 32
@@ -635,6 +635,7 @@ def render_bus_fabric_connections(links: list[FabricLink]) -> str:
"dma": "dma_axi4",
"sdio0": "sdio0_axi4",
"sdio1": "sdio1_axi4",
"usb2": "usb2_axi4",
"cfg": "cfg_axi4",
"system": "system_axi4",
}
@@ -811,7 +812,8 @@ def generate(topology_path: Path, memory_map_path: Path, output_dir: Path) -> No
render_fabric_connection(fabric_links, "cfg", "cfg_axi4")
+ render_fabric_connection(fabric_links, "dma", "dma_axi4")
+ render_fabric_connection(fabric_links, "sdio0", "sdio0_axi4")
+ render_fabric_connection(fabric_links, "sdio1", "sdio1_axi4"),
+ render_fabric_connection(fabric_links, "sdio1", "sdio1_axi4")
+ render_fabric_connection(fabric_links, "usb2", "usb2_axi4"),
)
atomic_write(
rtl_dir / "apb4_system_fabric.svh",
+22 -1
View File
@@ -5,7 +5,7 @@
"irq_groups": [
{
"name": "apb4_periph",
"width": 19,
"width": 20,
"top_signal": "s_apb4_periph_irq"
},
{
@@ -213,6 +213,15 @@
"core_bit": 23,
"signal": "s_crypto_irq",
"user_visible": false
},
{
"name": "usb2",
"description": "ULPI USB 2.0 dual-role controller interrupt",
"group": "apb4_periph",
"group_bit": 19,
"core_bit": 24,
"signal": "s_usb2_irq",
"user_visible": false
}
],
"wake_sources": [
@@ -249,6 +258,11 @@
"interface": "u_sdio1_axi4_if",
"protocol": "axi4"
},
{
"name": "usb2",
"interface": "u_usb2_axi4_if",
"protocol": "axi4"
},
{
"name": "cfg",
"interface": "u_cfg_axi4_if",
@@ -408,6 +422,13 @@
"timed_interface": "u_crypto_apb4_if",
"pure_interface": "u_crypto_apb4_pure_if",
"region": "APB4_CRYPTO"
},
{
"slot": 20,
"name": "usb2",
"timed_interface": "u_usb2_apb4_if",
"pure_interface": "u_usb2_apb4_pure_if",
"region": "APB4_USB2"
}
],
"apb4_system_targets": [
+3 -1
View File
@@ -110,6 +110,8 @@ CRT_SRCS := $(ROOT_PATH)/crt/arch/riscv/startup.S \
$(ROOT_PATH)/crt/src/hal/sdram.c \
$(ROOT_PATH)/crt/src/hal/sdio_math.c \
$(ROOT_PATH)/crt/src/hal/sdio.c \
$(ROOT_PATH)/crt/src/hal/usb2_math.c \
$(ROOT_PATH)/crt/src/hal/usb2.c \
$(ROOT_PATH)/crt/src/hal/spisd_math.c \
$(ROOT_PATH)/crt/src/hal/spisd.c \
$(ROOT_PATH)/crt/src/hal/xpi.c \
@@ -213,4 +215,4 @@ firmware: $(FIRMWARE_ELF)
firmware asm: | manifest
.PHONY: FORCE_VERSION
.PHONY: FORCE_VERSION
+4 -1
View File
@@ -8,7 +8,10 @@ edit generated files.
The map intentionally does not contain FPGA package locations. Board-level
locations remain owned by the FPGA constraint file. Dedicated SDIO1 pads are
therefore emitted but intentionally unbound in the FPGA profile until a
package pin and 3.3 V I/O-bank contract exists. The generic
package pin and 3.3 V I/O-bank contract exists. The 13 USB2 ULPI clock,
DATA[7:0], DIR, NXT, STP, and PHY-reset pads are also dedicated, never enter
the GPIO mux, and remain unbound until a USB3320-capable 3.3 V board and timing
contract are approved. The generic
`peripheral_bidir` kind requires explicit input, output, and output-enable
signals and is reusable for any peripheral, not just SDIO. Validate changes
with `make check-pin-map` and the pin-map tests before running a regression.
+66
View File
@@ -98,6 +98,33 @@
}
]
},
{
"kind": "tri_input",
"direction": "inout",
"ports": [
{"name": "usb2_ulpi_clk_i_pad", "signal": "s_usb2_ulpi_clk"},
{"name": "usb2_ulpi_dir_i_pad", "signal": "u_usb2_ulpi_if.dir_i"},
{"name": "usb2_ulpi_nxt_i_pad", "signal": "u_usb2_ulpi_if.nxt_i"}
]
},
{
"kind": "peripheral_bidir",
"direction": "inout",
"prefix": "usb2_ulpi_data",
"suffix": "_io_pad",
"count": 8,
"input": "u_usb2_ulpi_if.data_di_i[{index}]",
"output": "u_usb2_ulpi_if.data_do_o[{index}]",
"output_enable": "u_usb2_ulpi_if.data_oe_o"
},
{
"kind": "peripheral_output",
"direction": "output",
"ports": [
{"name": "usb2_ulpi_stp_o_pad", "signal": "u_usb2_ulpi_if.stp_o"},
{"name": "usb2_ulpi_reset_n_o_pad", "signal": "u_usb2_ulpi_if.reset_n_o"}
]
},
{
"kind": "tri_output",
"direction": "output",
@@ -218,6 +245,19 @@
"sdio1_dat1_io_pad": null,
"sdio1_dat2_io_pad": null,
"sdio1_dat3_io_pad": null,
"usb2_ulpi_clk_i_pad": "s_usb2_ulpi_clk",
"usb2_ulpi_dir_i_pad": "s_usb2_ulpi_dir",
"usb2_ulpi_nxt_i_pad": "s_usb2_ulpi_nxt",
"usb2_ulpi_data0_io_pad": "s_usb2_ulpi_data[0]",
"usb2_ulpi_data1_io_pad": "s_usb2_ulpi_data[1]",
"usb2_ulpi_data2_io_pad": "s_usb2_ulpi_data[2]",
"usb2_ulpi_data3_io_pad": "s_usb2_ulpi_data[3]",
"usb2_ulpi_data4_io_pad": "s_usb2_ulpi_data[4]",
"usb2_ulpi_data5_io_pad": "s_usb2_ulpi_data[5]",
"usb2_ulpi_data6_io_pad": "s_usb2_ulpi_data[6]",
"usb2_ulpi_data7_io_pad": "s_usb2_ulpi_data[7]",
"usb2_ulpi_stp_o_pad": "s_usb2_ulpi_stp",
"usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n",
"uart0_tx_o_pad": "uart0_tx_o",
"uart0_rx_i_pad": "uart0_rx_i",
"xpi_sck_o_pad": "xpi_sck_o",
@@ -291,6 +331,19 @@
"sdio1_dat1_io_pad": "sdio1_dat1_io",
"sdio1_dat2_io_pad": "sdio1_dat2_io",
"sdio1_dat3_io_pad": "sdio1_dat3_io",
"usb2_ulpi_clk_i_pad": "s_usb2_ulpi_clk",
"usb2_ulpi_dir_i_pad": "s_usb2_ulpi_dir",
"usb2_ulpi_nxt_i_pad": "s_usb2_ulpi_nxt",
"usb2_ulpi_data0_io_pad": "s_usb2_ulpi_data[0]",
"usb2_ulpi_data1_io_pad": "s_usb2_ulpi_data[1]",
"usb2_ulpi_data2_io_pad": "s_usb2_ulpi_data[2]",
"usb2_ulpi_data3_io_pad": "s_usb2_ulpi_data[3]",
"usb2_ulpi_data4_io_pad": "s_usb2_ulpi_data[4]",
"usb2_ulpi_data5_io_pad": "s_usb2_ulpi_data[5]",
"usb2_ulpi_data6_io_pad": "s_usb2_ulpi_data[6]",
"usb2_ulpi_data7_io_pad": "s_usb2_ulpi_data[7]",
"usb2_ulpi_stp_o_pad": "s_usb2_ulpi_stp",
"usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n",
"xpi_sck_o_pad": "s_xpi_sck_o",
"xpi_nss0_o_pad": "s_xpi_nss0_o",
"xpi_dat0_io_pad": "s_xpi_dat0_io",
@@ -368,6 +421,19 @@
"sdio1_dat1_io_pad": "s_sdio1_dat[1]",
"sdio1_dat2_io_pad": "s_sdio1_dat[2]",
"sdio1_dat3_io_pad": "s_sdio1_dat[3]",
"usb2_ulpi_clk_i_pad": "s_usb2_ulpi_clk",
"usb2_ulpi_dir_i_pad": "s_usb2_ulpi_dir",
"usb2_ulpi_nxt_i_pad": "s_usb2_ulpi_nxt",
"usb2_ulpi_data0_io_pad": "s_usb2_ulpi_data[0]",
"usb2_ulpi_data1_io_pad": "s_usb2_ulpi_data[1]",
"usb2_ulpi_data2_io_pad": "s_usb2_ulpi_data[2]",
"usb2_ulpi_data3_io_pad": "s_usb2_ulpi_data[3]",
"usb2_ulpi_data4_io_pad": "s_usb2_ulpi_data[4]",
"usb2_ulpi_data5_io_pad": "s_usb2_ulpi_data[5]",
"usb2_ulpi_data6_io_pad": "s_usb2_ulpi_data[6]",
"usb2_ulpi_data7_io_pad": "s_usb2_ulpi_data[7]",
"usb2_ulpi_stp_o_pad": "s_usb2_ulpi_stp",
"usb2_ulpi_reset_n_o_pad": "s_usb2_ulpi_reset_n",
"uart0_tx_o_pad": "s_uart0_tx",
"uart0_rx_i_pad": "s_uart0_rx",
"xpi_sck_o_pad": "s_xpi_sck_o",
+14
View File
@@ -17,6 +17,7 @@ module apb4_periph (
input logic rst_n_i,
input logic clk_aud_i,
input logic rst_aud_n_i,
input logic clk_ulpi_i,
input logic debug_halted_i,
input logic timebase_tick_i,
axi4_if.slave cfg_axi4,
@@ -36,12 +37,14 @@ module apb4_periph (
axi4_if.master dma_axi4,
axi4_if.master sdio0_axi4,
axi4_if.master sdio1_axi4,
axi4_if.master usb2_axi4,
sysctrl_if.dut sysctrl,
pll_ctrl_if.sysctrl pll_ctrl,
apb4_if.master sdram_cfg,
dvp_if.dut dvp,
sdio_if.dut sdio0,
sdio_if.dut sdio1,
usb2_ulpi_if.dut usb2,
opipsram_if.dut opipsram,
i2c_if.dut i2c1,
input logic fault_valid_i,
@@ -115,6 +118,7 @@ axi4_stream_if #(
logic s_dma_crypto_in_proc;
logic s_dma_crypto_out_proc;
logic s_crypto_irq;
logic s_usb2_irq;
logic s_tim0_irq, s_tim1_irq;
logic s_dvp_irq;
logic s_xpi_irq;
@@ -330,6 +334,16 @@ axi4_stream_if #(
.sdio (sdio1)
);
apb4_usb2 u_apb4_usb2 (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.clk_ulpi_i(clk_ulpi_i),
.apb4 (u_usb2_apb4_if),
.dma_axi4 (usb2_axi4),
.irq_o (s_usb2_irq),
.ulpi (usb2)
);
apb4_opipsram u_apb4_opipsram (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
+9 -1
View File
@@ -17,6 +17,7 @@ module axi4_bus (
axi4_if.slave dma_axi4,
axi4_if.slave sdio0_axi4,
axi4_if.slave sdio1_axi4,
axi4_if.slave usb2_axi4,
axi4_if.slave spisd_axi4,
axi4_if.master cfg_axi4,
axi4_if.master system_axi4,
@@ -43,6 +44,7 @@ module axi4_bus (
output logic [63:0] perf_dma_wait_o,
output logic [63:0] perf_sdio0_wait_o,
output logic [63:0] perf_sdio1_wait_o,
output logic [63:0] perf_usb2_wait_o,
output logic [63:0] perf_apb4_periph_wait_o,
output logic [63:0] perf_apb4_system_wait_o,
output logic [63:0] perf_sdram_wait_o,
@@ -51,7 +53,7 @@ module axi4_bus (
output logic [63:0] perf_opipsram_wait_o
// verilog_format: on
);
localparam int NumMasters = 6;
localparam int NumMasters = 7;
localparam int NumTargets = 10;
axi4_if #(
@@ -114,6 +116,11 @@ module axi4_bus (
.sink (u_master_axi4_if[5])
);
axi4_connector u_usb2_connector (
.source(usb2_axi4),
.sink (u_master_axi4_if[6])
);
axi4_connector u_cfg_connector (
.source(u_target_axi4_if[0]),
.sink (cfg_axi4)
@@ -173,6 +180,7 @@ module axi4_bus (
.perf_dma_wait_o (perf_dma_wait_o),
.perf_sdio0_wait_o (perf_sdio0_wait_o),
.perf_sdio1_wait_o (perf_sdio1_wait_o),
.perf_usb2_wait_o (perf_usb2_wait_o),
.perf_apb4_periph_wait_o(perf_apb4_periph_wait_o),
.perf_apb4_system_wait_o(perf_apb4_system_wait_o),
.perf_sdram_wait_o (perf_sdram_wait_o),
+4 -2
View File
@@ -7,7 +7,7 @@
`include "rib_defs.svh"
module axi4_interconnect #(
parameter int NumMasters = 6,
parameter int NumMasters = 7,
parameter int NumTargets = 10
) (
input logic clk_i,
@@ -30,6 +30,7 @@ module axi4_interconnect #(
output logic [63:0] perf_dma_wait_o,
output logic [63:0] perf_sdio0_wait_o,
output logic [63:0] perf_sdio1_wait_o,
output logic [63:0] perf_usb2_wait_o,
output logic [63:0] perf_apb4_periph_wait_o,
output logic [63:0] perf_apb4_system_wait_o,
output logic [63:0] perf_sdram_wait_o,
@@ -584,6 +585,7 @@ module axi4_interconnect #(
assign perf_dma_wait_o = s_perf_master_wait[2] + s_perf_master_wait[5];
assign perf_sdio0_wait_o = s_perf_master_wait[3];
assign perf_sdio1_wait_o = s_perf_master_wait[4];
assign perf_usb2_wait_o = s_perf_master_wait[6];
assign perf_apb4_periph_wait_o = s_perf_target_wait[TARGET_CFG];
assign perf_apb4_system_wait_o = s_perf_target_wait[TARGET_APB4_SYSTEM];
assign perf_sdram_wait_o = s_perf_target_wait[TARGET_SDRAM];
@@ -597,7 +599,7 @@ module axi4_interconnect #(
`ifndef SYNTHESIS
initial begin
if (NumMasters != 6 || ((NumTargets != 9) && (NumTargets != 10))) begin
if (NumMasters != 7 || ((NumTargets != 9) && (NumTargets != 10))) begin
$fatal(1, "axi4_interconnect: invalid topology dimensions");
end
end
+7
View File
@@ -18,6 +18,7 @@ module retrosoc (
input logic rst_n_i,
input logic clk_aud_i,
input logic rst_aud_n_i,
input logic clk_ulpi_i,
input logic clkdiv4_i,
input logic timebase_tick_i,
pll_ctrl_if.sysctrl pll_ctrl,
@@ -30,6 +31,7 @@ module retrosoc (
xpi_if.dut xpi,
sdram_if.dut sdram,
sdio_if.dut sdio1,
usb2_ulpi_if.dut usb2,
input logic jtag_tck_i,
input logic jtag_tms_i,
input logic jtag_tdi_i,
@@ -92,6 +94,7 @@ module retrosoc (
logic [ 63:0] s_perf_dma_wait;
logic [ 63:0] s_perf_sdio0_wait;
logic [ 63:0] s_perf_sdio1_wait;
logic [ 63:0] s_perf_usb2_wait;
logic [ 63:0] s_perf_apb4_periph_wait;
logic [ 63:0] s_perf_apb4_system_wait;
logic [ 63:0] s_perf_sdram_wait;
@@ -114,6 +117,7 @@ module retrosoc (
assign u_sysctrl_if.perf_dma_wait_i = s_perf_dma_wait;
assign u_sysctrl_if.perf_sdio0_wait_i = s_perf_sdio0_wait;
assign u_sysctrl_if.perf_sdio1_wait_i = s_perf_sdio1_wait;
assign u_sysctrl_if.perf_usb2_wait_i = s_perf_usb2_wait;
assign u_sysctrl_if.perf_apb4_periph_wait_i = s_perf_apb4_periph_wait;
assign u_sysctrl_if.perf_apb4_system_wait_i = s_perf_apb4_system_wait;
assign u_sysctrl_if.perf_sdram_wait_i = s_perf_sdram_wait;
@@ -186,6 +190,7 @@ core_wrapper u_core_wrapper (
.perf_dma_wait_o (s_perf_dma_wait),
.perf_sdio0_wait_o (s_perf_sdio0_wait),
.perf_sdio1_wait_o (s_perf_sdio1_wait),
.perf_usb2_wait_o (s_perf_usb2_wait),
.perf_apb4_periph_wait_o(s_perf_apb4_periph_wait),
.perf_apb4_system_wait_o(s_perf_apb4_system_wait),
.perf_sdram_wait_o (s_perf_sdram_wait),
@@ -199,6 +204,7 @@ core_wrapper u_core_wrapper (
.rst_n_i (rst_n_i),
.clk_aud_i (clk_aud_i),
.rst_aud_n_i (rst_aud_n_i),
.clk_ulpi_i (clk_ulpi_i),
.debug_halted_i (s_mgmt_debug_halted),
.timebase_tick_i (timebase_tick_i),
`include "apb4_periph_fabric.svh"
@@ -221,6 +227,7 @@ core_wrapper u_core_wrapper (
.dvp (u_dvp_if),
.sdio0 (u_sdio0_if),
.sdio1 (sdio1),
.usb2 (usb2),
.opipsram (u_opipsram_if),
.i2c1 (u_i2c1_if),
.fault_valid_i (s_bus_fault_valid),
+5
View File
@@ -52,6 +52,7 @@
// GPIO31 OPI_PSRAM_DQS BIDI PWM_CAPTURE1 IN
//
// Dedicated SDIO1 pads: SDIO1_CLK, SDIO1_CMD, SDIO1_DAT[3:0].
// Dedicated 3.3 V ULPI pads: CLK, DATA[7:0], DIR, NXT, STP, RESET_N.
module retrosoc_asic (
`include "retrosoc_asic_ports.svh"
@@ -76,6 +77,7 @@ module retrosoc_asic (
logic s_jtag_tdo;
logic s_uart0_rx;
logic s_uart0_tx;
logic s_usb2_ulpi_clk;
(* keep = "true" *)logic s_test_done;
(* keep = "true" *)logic s_test_pass;
(* keep = "true" *)logic [7:0] s_test_code;
@@ -88,6 +90,7 @@ module retrosoc_asic (
xpi_if u_xpi_if ();
sdram_if u_sdram_if ();
sdio_if u_sdio1_if ();
usb2_ulpi_if u_usb2_ulpi_if ();
pll_ctrl_if u_pll_ctrl_if ();
`include "retrosoc_asic_pad_bindings.svh"
@@ -124,6 +127,7 @@ rcu #(
.rst_n_i (s_sys_rst_n),
.clk_aud_i (s_aud_clk_buf),
.rst_aud_n_i (s_aud_rst_n),
.clk_ulpi_i (s_usb2_ulpi_clk),
.clkdiv4_i (s_sys_clkdiv4),
.timebase_tick_i(s_timebase_tick),
.pll_ctrl (u_pll_ctrl_if),
@@ -136,6 +140,7 @@ rcu #(
.xpi (u_xpi_if),
.sdram (u_sdram_if),
.sdio1 (u_sdio1_if),
.usb2 (u_usb2_ulpi_if),
.jtag_tck_i (s_jtag_tck),
.jtag_tms_i (s_jtag_tms),
.jtag_tdi_i (s_jtag_tdi),
+27
View File
@@ -244,6 +244,24 @@
"rtl/ip/util": [
"async_fifo.sv"
],
"rtl/ip/usb": [
"apb4_usb2.sv",
"usb2_define.svh",
"usb2_dma.sv",
"usb2_dma_descriptor.sv",
"usb2_link_domain.sv",
"usb2_packet_ram.sv",
"usb2_packet_store.sv",
"usb2_pkg.sv",
"usb2_reg.sv",
"usb2_role_controller.sv",
"usb2_scheduler.sv",
"usb2_sie_rx.sv",
"usb2_sie_tx.sv",
"usb2_transaction_engine.sv",
"usb2_ulpi_if.sv",
"usb2_ulpi_link.sv"
],
"rtl/mini/top": [
"ahbl2axi4.sv",
"ahbl2ribp.sv",
@@ -322,6 +340,15 @@
"result": "retained implementation",
"rationale": "Dual-clock storage, Gray-pointer synchronization, and inferred-memory behavior cannot be replaced by single-clock Common registers without equivalence evidence."
},
{
"paths": [
"rtl/ip/usb/apb4_usb2.sv",
"rtl/ip/usb/usb2_link_domain.sv",
"rtl/ip/usb/usb2_packet_ram.sv"
],
"result": "reviewed CDC and technology-memory boundary",
"rationale": "The system/ULPI boundary uses managed request-acknowledge synchronizers, asynchronous FIFOs, level synchronizers, and event toggles. Packet RAM retains a single-port technology wrapper with SECDED encoding; replacement requires CDC and memory-equivalence evidence."
},
{
"paths": [
"rtl/mini/top/axi4_interconnect.sv"
+90
View File
@@ -0,0 +1,90 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module tc_usb2_packet_ram (
input logic clk_i,
input logic cs_i,
input logic write_i,
input logic [11:0] addr_i,
input logic [39:0] data_i,
output logic [39:0] data_o
);
`ifdef PDK_IHP130
logic [15:0] s_data_low;
logic [15:0] s_data_high;
logic [ 7:0] s_data_ecc;
RM_IHPSG13_1P_4096x16_c3_bm_bist u_data_low (
.A_CLK (clk_i),
.A_MEN (cs_i),
.A_WEN (write_i),
.A_REN (!write_i),
.A_ADDR (addr_i),
.A_DIN (data_i[15:0]),
.A_DLY (1'b0),
.A_DOUT (s_data_low),
.A_BM (16'hFFFF),
.A_BIST_CLK (1'b0),
.A_BIST_EN (1'b0),
.A_BIST_MEN (1'b0),
.A_BIST_WEN (1'b0),
.A_BIST_REN (1'b0),
.A_BIST_ADDR(12'd0),
.A_BIST_DIN (16'd0),
.A_BIST_BM (16'd0)
);
RM_IHPSG13_1P_4096x16_c3_bm_bist u_data_high (
.A_CLK (clk_i),
.A_MEN (cs_i),
.A_WEN (write_i),
.A_REN (!write_i),
.A_ADDR (addr_i),
.A_DIN (data_i[31:16]),
.A_DLY (1'b0),
.A_DOUT (s_data_high),
.A_BM (16'hFFFF),
.A_BIST_CLK (1'b0),
.A_BIST_EN (1'b0),
.A_BIST_MEN (1'b0),
.A_BIST_WEN (1'b0),
.A_BIST_REN (1'b0),
.A_BIST_ADDR(12'd0),
.A_BIST_DIN (16'd0),
.A_BIST_BM (16'd0)
);
RM_IHPSG13_1P_4096x8_c3_bm_bist u_ecc (
.A_CLK (clk_i),
.A_MEN (cs_i),
.A_WEN (write_i),
.A_REN (!write_i),
.A_ADDR (addr_i),
.A_DIN (data_i[39:32]),
.A_DLY (1'b0),
.A_DOUT (s_data_ecc),
.A_BM (8'hFF),
.A_BIST_CLK (1'b0),
.A_BIST_EN (1'b0),
.A_BIST_MEN (1'b0),
.A_BIST_WEN (1'b0),
.A_BIST_REN (1'b0),
.A_BIST_ADDR(12'd0),
.A_BIST_DIN (8'd0),
.A_BIST_BM (8'd0)
);
assign data_o = {s_data_ecc, s_data_high, s_data_low};
`else
logic [39:0] s_memory[0:4095];
always_ff @(posedge clk_i) begin
if (cs_i) begin
if (write_i) begin
s_memory[addr_i] <= data_i;
end else begin
data_o <= s_memory[addr_i];
end
end
end
`endif
endmodule
+1
View File
@@ -15,6 +15,7 @@ CDC_PRIMITIVES = {
"async_fifo",
"async_reqack",
"cdc_2phase",
"cdc_fifo",
"cdc_sync",
"cdc_fifo_warm_flush",
"edge_det",
+1
View File
@@ -28,6 +28,7 @@ TEST_SOURCES = (
"crt/src/hal/psram_math.c",
"crt/src/hal/sdram_math.c",
"crt/src/hal/sdio_math.c",
"crt/src/hal/usb2_math.c",
"crt/src/hal/spisd_math.c",
"crt/src/hal/i2s_math.c",
"crt/src/hal/dma_math.c",
+38 -7
View File
@@ -17,6 +17,7 @@
#include <retrosoc/hal/spisd.h>
#include <retrosoc/hal/timer.h>
#include <retrosoc/hal/uart.h>
#include <retrosoc/hal/usb2.h>
#include <retrosoc/hal/user_ip.h>
#include <retrosoc/hal/ws2812.h>
#include <retrosoc/lib/printf.h>
@@ -580,6 +581,36 @@ static int test_sdio_helpers(void) {
return 0;
}
static int test_usb2_helpers(void) {
_Alignas(32) rs_usb2_descriptor_t descriptors[2];
if ((rs_usb2_validate_dma_buffer((const void *)(uintptr_t)UINT32_C(0x40000000), 4U) != RS_OK) ||
(rs_usb2_validate_dma_buffer((const void *)(uintptr_t)UINT32_C(0x40000002), 4U) !=
RS_EINVAL) ||
(rs_usb2_descriptor_prepare(&descriptors[0], (uintptr_t)UINT32_C(0x40000000), 64U,
(uintptr_t)UINT32_C(0x40000020), false, false, true, false,
UINT32_C(0x1234)) != RS_OK) ||
(rs_usb2_descriptor_prepare(&descriptors[1], (uintptr_t)UINT32_C(0x40000100), 64U,
(uintptr_t)0U, true, true, false, false,
UINT32_C(0x1235)) != RS_OK) ||
(rs_usb2_descriptor_chain_validate(descriptors, 2U, 128U) != RS_OK) ||
(rs_usb2_descriptor_publish_chain(descriptors, 2U) != RS_OK) ||
((descriptors[0].control & RS_USB2_DESC_OWN) == 0U) ||
((descriptors[1].control & RS_USB2_DESC_OWN) == 0U)) {
return 1;
}
if ((rs_usb2_descriptor_prepare(&descriptors[0], (uintptr_t)UINT32_C(0xFFFFFFFC), 8U,
(uintptr_t)0U, true, false, false, false, 0U) != RS_EINVAL) ||
(rs_usb2_descriptor_prepare(&descriptors[0], (uintptr_t)UINT32_C(0x40000000), 64U,
(uintptr_t)UINT32_C(0x40000FE4), false, false, false, false,
0U) != RS_EINVAL) ||
(rs_usb2_descriptor_prepare(NULL, (uintptr_t)UINT32_C(0x40000000), 64U, (uintptr_t)0U, true,
false, false, false, 0U) != RS_EINVAL)) {
return 2;
}
return 0;
}
static int test_spisd_helpers(void) {
rs_spisd_clock_t clock;
rs_spisd_card_info_t info = {0};
@@ -788,13 +819,13 @@ static int test_video_parser(void) {
int main(void) {
const int results[] = {
test_string_helpers(), test_formatter(), test_compiler_helpers(),
test_wait_helper(), test_ws2812_helpers(), test_timer_helpers(),
test_psram_helpers(), test_sdram_helpers(), test_uart_helpers(),
test_i2s_helpers(), test_i2c_helpers(), test_sdio_helpers(),
test_spisd_helpers(), test_gpio_helpers(), test_dma_config_validation(),
test_opipsram_helpers(), test_user_ip_validation(), test_ps2_decoders(),
test_wav_parser(), test_video_parser(),
test_string_helpers(), test_formatter(), test_compiler_helpers(),
test_wait_helper(), test_ws2812_helpers(), test_timer_helpers(),
test_psram_helpers(), test_sdram_helpers(), test_uart_helpers(),
test_i2s_helpers(), test_i2c_helpers(), test_sdio_helpers(),
test_usb2_helpers(), test_spisd_helpers(), test_gpio_helpers(),
test_dma_config_validation(), test_opipsram_helpers(), test_user_ip_validation(),
test_ps2_decoders(), test_wav_parser(), test_video_parser(),
};
for (size_t index = 0U; index < (sizeof(results) / sizeof(results[0])); ++index) {
+4 -2
View File
@@ -4,7 +4,7 @@
`include "rib_defs.svh"
module axi4_interconnect_tb;
localparam int NumMasters = 6;
localparam int NumMasters = 7;
localparam int NumTargets = 9;
logic clk_i = 1'b0;
@@ -64,6 +64,7 @@ module axi4_interconnect_tb;
.perf_dma_wait_o (),
.perf_sdio0_wait_o (),
.perf_sdio1_wait_o (),
.perf_usb2_wait_o (),
.perf_apb4_periph_wait_o(),
.perf_apb4_system_wait_o(),
.perf_sdram_wait_o (),
@@ -194,7 +195,8 @@ module axi4_interconnect_tb;
while (!masters[0].rvalid && cycles < 100) begin
if (masters[3].arvalid || masters[3].awvalid || masters[3].wvalid ||
masters[4].arvalid || masters[4].awvalid || masters[4].wvalid ||
masters[5].arvalid || masters[5].awvalid || masters[5].wvalid) begin
masters[5].arvalid || masters[5].awvalid || masters[5].wvalid ||
masters[6].arvalid || masters[6].awvalid || masters[6].wvalid) begin
$fatal(1, "inactive storage master drove a request during management boot traffic");
end
@(negedge clk_i);
+2 -1
View File
@@ -39,6 +39,7 @@ module soc_irq_topology_tb;
16: expect_irq(32'd1 << 10);
17: expect_irq(32'd1 << 21);
18: expect_irq(32'd1 << 23);
19: expect_irq(32'd1 << 24);
default: expect_irq(32'd1 << (bit_index + 7));
endcase
end
@@ -63,7 +64,7 @@ module soc_irq_topology_tb;
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
s_irq[10], s_irq[21]);
end
if ((s_irq[31:24] !== '0) || (s_irq[19:18] !== '0)) begin
if ((s_irq[31:25] !== '0) || (s_irq[19:18] !== '0)) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
end
+3
View File
@@ -86,6 +86,7 @@ module sysctrl_tb;
sysctrl.perf_dma_wait_i = 64'd13;
sysctrl.perf_sdio0_wait_i = 64'd131;
sysctrl.perf_sdio1_wait_i = 64'd141;
sysctrl.perf_usb2_wait_i = 64'd151;
sysctrl.perf_apb4_periph_wait_i = 64'd14;
sysctrl.perf_apb4_system_wait_i = 64'd15;
sysctrl.perf_sdram_wait_i = 64'd16;
@@ -150,6 +151,8 @@ module sysctrl_tb;
if (read_data !== 32'd131) $fatal(1, "SDIO0 performance snapshot was not recorded");
read_register(32'h1000_B094, read_data);
if (read_data !== 32'd141) $fatal(1, "SDIO1 performance snapshot was not recorded");
read_register(32'h1000_B09C, read_data);
if (read_data !== 32'd151) $fatal(1, "USB2 performance snapshot was not recorded");
read_register(32'h1000_B020, read_data);
if (read_data !== 32'h0000_003F) $fatal(1, "user cores were not held in reset");
+84
View File
@@ -0,0 +1,84 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_descriptor_tb;
logic [31:0] s_buffer_addr;
logic [31:0] s_byte_length;
logic [31:0] s_next_addr;
logic [31:0] s_control;
logic [31:0] s_actual_length;
logic [31:0] s_status;
logic [31:0] s_frame;
logic [31:0] s_reserved;
logic [ 3:0] s_desc_index;
logic s_valid;
usb2_pkg::usb2_desc_error_e s_error;
usb2_dma_descriptor #(
.MaxDescriptors(16)
) u_descriptor (
.buffer_addr_i (s_buffer_addr),
.byte_length_i (s_byte_length),
.next_addr_i (s_next_addr),
.control_i (s_control),
.actual_length_i(s_actual_length),
.status_i (s_status),
.frame_i (s_frame),
.reserved_i (s_reserved),
.desc_index_i (s_desc_index),
.own_o (),
.chain_o (),
.end_o (),
.irq_o (),
.short_ok_o (),
.zero_packet_o (),
.valid_o (s_valid),
.error_o (s_error)
);
initial begin
s_buffer_addr = 32'h2000_0000;
s_byte_length = 32'd512;
s_next_addr = 32'd0;
s_control = 32'h0000_000D;
s_actual_length = 32'd0;
s_status = 32'd0;
s_frame = 32'd0;
s_reserved = 32'd0;
s_desc_index = 4'd0;
#1;
if (!s_valid || (s_error != usb2_pkg::Usb2DescOk)) begin
$fatal(1, "USB2 terminal descriptor rejected");
end
s_buffer_addr = 32'h2000_0001;
#1;
if (s_valid || (s_error != usb2_pkg::Usb2DescBufferAlign)) begin
$fatal(1, "USB2 unaligned buffer accepted");
end
s_buffer_addr = 32'hFFFF_FFFC;
s_byte_length = 32'd8;
#1;
if (s_valid || (s_error != usb2_pkg::Usb2DescAddressWrap)) begin
$fatal(1, "USB2 wrapping buffer accepted");
end
s_buffer_addr = 32'h2000_0000;
s_byte_length = 32'd512;
s_control = 32'h0000_0003;
s_next_addr = 32'h2000_0FE0;
#1;
if (!s_valid) begin
$fatal(1, "USB2 aligned chained descriptor rejected");
end
s_next_addr = 32'h2000_0FF0;
#1;
if (s_valid || (s_error != usb2_pkg::Usb2DescNextAlign)) begin
$fatal(1, "USB2 descriptor crossing 4 KiB boundary accepted");
end
$display("USB2 descriptor validation passed");
$finish;
end
endmodule
+216
View File
@@ -0,0 +1,216 @@
`timescale 1ns / 1ps
module usb2_dma_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic start_i = 1'b0;
logic abort_i = 1'b0;
logic memory_to_packet_i = 1'b1;
logic allow_short_i = 1'b0;
logic [31:0] desc_base_i = 32'd0;
logic [15:0] desc_limit_i = 16'd1;
logic [31:0] transfer_bytes_i = 32'd5;
logic packet_in_valid_i = 1'b0;
logic packet_in_ready_o;
logic [31:0] packet_in_data_i = '0;
logic [ 3:0] packet_in_strb_i = '0;
logic packet_in_last_i = 1'b0;
logic packet_out_valid_o;
logic packet_out_ready_i = 1'b1;
logic [31:0] packet_out_data_o;
logic [ 3:0] packet_out_strb_o;
logic packet_out_last_o;
logic busy_o;
logic done_o;
logic error_o;
logic [ 7:0] error_code_o;
logic [31:0] current_desc_o;
logic [31:0] next_desc_o;
logic [31:0] bytes_done_o;
logic [31:0] frame_o;
logic descriptor_irq_o;
logic abort_done_o;
integer output_count;
logic [31:0] output_data [0:1];
logic [ 3:0] output_strb [0:1];
logic output_last [0:1];
logic descriptor_irq_seen;
axi4_if #(
.ADDR_WIDTH(32),
.DATA_WIDTH(32),
.ID_WIDTH (1),
.USER_WIDTH(1)
) dma_axi4 (
.aclk (clk_i),
.aresetn(rst_n_i)
);
always #5 clk_i = ~clk_i;
always_ff @(posedge clk_i) begin
if (!rst_n_i) begin
output_count <= 0;
descriptor_irq_seen <= 1'b0;
end else begin
if (packet_out_valid_o && packet_out_ready_i) begin
output_data[output_count] <= packet_out_data_o;
output_strb[output_count] <= packet_out_strb_o;
output_last[output_count] <= packet_out_last_o;
output_count <= output_count + 1;
end
if (descriptor_irq_o) begin
descriptor_irq_seen <= 1'b1;
end
end
end
usb2_dma #(
.AddrWidth (32),
.DataWidth (32),
.MaxDescriptors(16)
) u_usb2_dma (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.start_i (start_i),
.abort_i (abort_i),
.memory_to_packet_i(memory_to_packet_i),
.allow_short_i (allow_short_i),
.desc_base_i (desc_base_i),
.desc_limit_i (desc_limit_i),
.transfer_bytes_i (transfer_bytes_i),
.packet_in_valid_i (packet_in_valid_i),
.packet_in_ready_o (packet_in_ready_o),
.packet_in_data_i (packet_in_data_i),
.packet_in_strb_i (packet_in_strb_i),
.packet_in_last_i (packet_in_last_i),
.packet_out_valid_o(packet_out_valid_o),
.packet_out_ready_i(packet_out_ready_i),
.packet_out_data_o (packet_out_data_o),
.packet_out_strb_o (packet_out_strb_o),
.packet_out_last_o (packet_out_last_o),
.busy_o (busy_o),
.done_o (done_o),
.error_o (error_o),
.error_code_o (error_code_o),
.current_desc_o (current_desc_o),
.next_desc_o (next_desc_o),
.bytes_done_o (bytes_done_o),
.frame_o (frame_o),
.descriptor_irq_o (descriptor_irq_o),
.abort_done_o (abort_done_o),
.dma_axi4 (dma_axi4)
);
sdio_axi_memory_responder #(
.DepthWords(16384)
) u_memory (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.axi4 (dma_axi4)
);
task automatic reset_dut;
begin
rst_n_i = 1'b0;
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
@(posedge clk_i);
end
endtask
task automatic start_dma;
begin
@(negedge clk_i);
start_i = 1'b1;
@(negedge clk_i);
start_i = 1'b0;
end
endtask
task automatic send_packet_word(input logic [31:0] data_i, input logic [3:0] strb_i,
input logic last_i);
begin
@(negedge clk_i);
packet_in_data_i = data_i;
packet_in_strb_i = strb_i;
packet_in_last_i = last_i;
packet_in_valid_i = 1'b1;
while (!packet_in_ready_o) @(negedge clk_i);
@(negedge clk_i);
packet_in_valid_i = 1'b0;
end
endtask
initial begin
u_memory.clear_memory();
reset_dut();
// Memory-to-packet transfer splits at 4 KiB and finishes with one valid byte.
u_memory.write_word(32'h0000_0000, 32'h0000_0FFC);
u_memory.write_word(32'h0000_0004, 32'd5);
u_memory.write_word(32'h0000_0008, 32'd0);
u_memory.write_word(32'h0000_000C, 32'h0000_000D);
u_memory.write_word(32'h0000_0010, 32'd0);
u_memory.write_word(32'h0000_0014, 32'd0);
u_memory.write_word(32'h0000_0018, 32'd0);
u_memory.write_word(32'h0000_001C, 32'd0);
u_memory.write_word(32'h0000_0FFC, 32'h4433_2211);
u_memory.write_word(32'h0000_1000, 32'h0000_0055);
memory_to_packet_i = 1'b1;
start_dma();
wait (done_o || error_o);
@(posedge clk_i);
if (error_o || (bytes_done_o != 32'd5) || (output_count != 2) ||
(output_data[0] != 32'h4433_2211) || (output_strb[0] != 4'hF) || output_last[0] ||
(output_data[1] != 32'h0000_0055) || (output_strb[1] != 4'h1) || !output_last[1] ||
(u_memory.read_burst_count != 3) || (u_memory.max_read_beats > 16) ||
(u_memory.read_word(
32'h0000_0010
) != 32'd5) || (u_memory.read_word(
32'h0000_0014
) != 32'h0001_0000) || (u_memory.read_word(
32'h0000_000C
) != 32'h0000_000C) || !descriptor_irq_seen) begin
$fatal(1, "memory-to-packet DMA failed err=%b code=%h bytes=%0d outputs=%0d", error_o,
error_code_o, bytes_done_o, output_count);
end
// Packet-to-memory transfer preserves tail strobes and commits OWN last.
reset_dut();
u_memory.write_word(32'h0000_0000, 32'h0000_0200);
u_memory.write_word(32'h0000_0004, 32'd5);
u_memory.write_word(32'h0000_0008, 32'd0);
u_memory.write_word(32'h0000_000C, 32'h0000_000D);
u_memory.write_word(32'h0000_0010, 32'd0);
u_memory.write_word(32'h0000_0014, 32'd0);
u_memory.write_word(32'h0000_0018, 32'd0);
u_memory.write_word(32'h0000_001C, 32'd0);
u_memory.write_word(32'h0000_0200, 32'd0);
u_memory.write_word(32'h0000_0204, 32'd0);
memory_to_packet_i = 1'b0;
start_dma();
send_packet_word(32'h8877_6655, 4'hF, 1'b0);
send_packet_word(32'h0000_0099, 4'h1, 1'b1);
wait (done_o || error_o);
@(posedge clk_i);
if (error_o || (bytes_done_o != 32'd5) || (u_memory.read_word(
32'h0000_0200
) != 32'h8877_6655) || (u_memory.read_word(
32'h0000_0204
) != 32'h0000_0099) || (u_memory.read_word(
32'h0000_0010
) != 32'd5) || (u_memory.read_word(
32'h0000_0014
) != 32'h0001_0000) || (u_memory.read_word(
32'h0000_000C
) != 32'h0000_000C) || !descriptor_irq_seen) begin
$fatal(1, "packet-to-memory DMA failed err=%b code=%h bytes=%0d data=%h tail=%h", error_o,
error_code_o, bytes_done_o, u_memory.read_word(32'h0000_0200), u_memory.read_word(
32'h0000_0204));
end
$display("USB2 descriptor DMA test passed");
$finish;
end
endmodule
+115
View File
@@ -0,0 +1,115 @@
`timescale 1ns / 1ps
module usb2_link_domain_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic enable_i = 1'b0;
logic fill_cmd_valid_i = 1'b1;
logic drain_cmd_valid_i = 1'b1;
logic [ 7:0][31:0] endpoint_cfg_i = '0;
logic work_ready_o;
logic result_valid_o;
logic [63:0] result_data_o;
logic setup_valid_o;
logic [63:0] setup_data_o;
logic fill_cmd_ready_o;
logic fill_stream_ready_o;
logic drain_cmd_ready_o;
logic drain_stream_valid_o;
logic [36:0] drain_stream_data_o;
logic buffer_event_valid_o;
logic [ 2:0] buffer_event_data_o;
logic viewport_ready_o;
logic viewport_resp_valid_o;
logic [ 8:0] viewport_resp_data_o;
logic [ 1:0] line_state_o;
logic [ 1:0] vbus_state_o;
logic id_ground_o;
logic link_ready_o;
logic [13:0] frame_o;
logic transaction_busy_o;
logic retry_o;
logic protocol_error_o;
logic [31:0] ecc_corrected_count_o;
logic [31:0] ecc_uncorrectable_count_o;
usb2_ulpi_if ulpi ();
always #5 clk_i = ~clk_i;
assign ulpi.data_di_i = 8'd0;
assign ulpi.dir_i = 1'b0;
assign ulpi.nxt_i = 1'b1;
usb2_link_domain u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.enable_i (enable_i),
.phy_reset_n_i (1'b1),
.high_speed_i (1'b1),
.active_role_i (usb2_pkg::Usb2RoleIdle),
.device_addr_i (7'd0),
.endpoint_cfg_i (endpoint_cfg_i),
.timeout_i (32'd1000),
.work_valid_i (1'b0),
.work_ready_o (work_ready_o),
.work_data_i ('0),
.result_valid_o (result_valid_o),
.result_ready_i (1'b1),
.result_data_o (result_data_o),
.setup_valid_o (setup_valid_o),
.setup_ready_i (1'b1),
.setup_data_o (setup_data_o),
.fill_cmd_valid_i (fill_cmd_valid_i),
.fill_cmd_ready_o (fill_cmd_ready_o),
.fill_cmd_data_i ('0),
.fill_stream_valid_i (1'b0),
.fill_stream_ready_o (fill_stream_ready_o),
.fill_stream_data_i ('0),
.drain_cmd_valid_i (drain_cmd_valid_i),
.drain_cmd_ready_o (drain_cmd_ready_o),
.drain_cmd_data_i ('0),
.drain_stream_valid_o (drain_stream_valid_o),
.drain_stream_ready_i (1'b1),
.drain_stream_data_o (drain_stream_data_o),
.buffer_event_valid_o (buffer_event_valid_o),
.buffer_event_ready_i (1'b1),
.buffer_event_data_o (buffer_event_data_o),
.viewport_valid_i (1'b0),
.viewport_ready_o (viewport_ready_o),
.viewport_data_i ('0),
.viewport_resp_valid_o (viewport_resp_valid_o),
.viewport_resp_ready_i (1'b1),
.viewport_resp_data_o (viewport_resp_data_o),
.line_state_o (line_state_o),
.vbus_state_o (vbus_state_o),
.id_ground_o (id_ground_o),
.link_ready_o (link_ready_o),
.frame_o (frame_o),
.transaction_busy_o (transaction_busy_o),
.retry_o (retry_o),
.protocol_error_o (protocol_error_o),
.ecc_corrected_count_o (ecc_corrected_count_o),
.ecc_uncorrectable_count_o(ecc_uncorrectable_count_o),
.ulpi (ulpi)
);
initial begin
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
repeat (4) @(posedge clk_i);
if (fill_cmd_ready_o || drain_cmd_ready_o || transaction_busy_o) begin
$fatal(1, "USB2 disabled packet-store isolation failed");
end
fill_cmd_valid_i = 1'b0;
drain_cmd_valid_i = 1'b0;
enable_i = 1'b1;
repeat (8) @(posedge clk_i);
if (!link_ready_o || !ulpi.reset_n_o || ulpi.data_oe_o || ulpi.stp_o ||
transaction_busy_o || result_valid_o || setup_valid_o || protocol_error_o ||
(ecc_corrected_count_o != 32'd0) || (ecc_uncorrectable_count_o != 32'd0)) begin
$fatal(1, "USB2 link domain idle smoke failed");
end
$display("USB2 link domain smoke passed");
$finish;
end
endmodule
+96
View File
@@ -0,0 +1,96 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_packet_ram_tb;
logic s_clk;
logic s_rst_n;
logic s_write;
logic s_read;
logic [11:0] s_addr;
logic [31:0] s_write_data;
logic s_inject_single;
logic s_inject_double;
logic [31:0] s_read_data;
logic s_read_valid;
logic s_corrected;
logic s_uncorrectable;
usb2_packet_ram #(
.EnableFaultInjection(1'b1)
) u_packet_ram (
.clk_i (s_clk),
.rst_n_i (s_rst_n),
.write_i (s_write),
.read_i (s_read),
.addr_i (s_addr),
.write_data_i (s_write_data),
.inject_single_i(s_inject_single),
.inject_double_i(s_inject_double),
.read_data_o (s_read_data),
.read_valid_o (s_read_valid),
.corrected_o (s_corrected),
.uncorrectable_o(s_uncorrectable)
);
always #5 s_clk = ~s_clk;
task automatic write_word(input logic [11:0] addr_i, input logic [31:0] data_i,
input logic single_i, input logic double_i);
begin
@(negedge s_clk);
s_addr = addr_i;
s_write_data = data_i;
s_inject_single = single_i;
s_inject_double = double_i;
s_write = 1'b1;
@(negedge s_clk);
s_write = 1'b0;
s_inject_single = 1'b0;
s_inject_double = 1'b0;
end
endtask
task automatic read_word(input logic [11:0] addr_i);
begin
@(negedge s_clk);
s_addr = addr_i;
s_read = 1'b1;
@(posedge s_clk);
#1;
s_read = 1'b0;
end
endtask
initial begin
s_clk = 1'b0;
s_rst_n = 1'b0;
s_write = 1'b0;
s_read = 1'b0;
s_addr = '0;
s_write_data = '0;
s_inject_single = 1'b0;
s_inject_double = 1'b0;
repeat (2) @(negedge s_clk);
s_rst_n = 1'b1;
write_word(12'h010, 32'h1357_9BDF, 1'b0, 1'b0);
read_word(12'h010);
if (!s_read_valid || s_corrected || s_uncorrectable || (s_read_data != 32'h1357_9BDF)) begin
$fatal(1, "USB2 packet RAM clean read failed");
end
write_word(12'h011, 32'h2468_ACE0, 1'b1, 1'b0);
read_word(12'h011);
if (!s_read_valid || !s_corrected || s_uncorrectable || (s_read_data != 32'h2468_ACE0)) begin
$fatal(1, "USB2 packet RAM single-bit correction failed");
end
write_word(12'h012, 32'h55AA_F00D, 1'b0, 1'b1);
read_word(12'h012);
if (!s_read_valid || !s_uncorrectable) begin
$fatal(1, "USB2 packet RAM double-bit detection failed");
end
$display("USB2 packet RAM ECC test passed");
$finish;
end
endmodule
+213
View File
@@ -0,0 +1,213 @@
`timescale 1ns / 1ps
module usb2_packet_store_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
logic fill_start_valid_i = 1'b0;
logic fill_start_ready_o;
logic [11:0] fill_base_i = '0;
logic [14:0] fill_bytes_i = '0;
logic fill_valid_i = 1'b0;
logic fill_ready_o;
logic [31:0] fill_data_i = '0;
logic [ 3:0] fill_strb_i = '0;
logic fill_last_i = 1'b0;
logic fill_done_o;
logic fill_error_o;
logic drain_start_valid_i = 1'b0;
logic drain_start_ready_o;
logic [11:0] drain_base_i = '0;
logic [14:0] drain_bytes_i = '0;
logic drain_valid_o;
logic drain_ready_i = 1'b1;
logic [31:0] drain_data_o;
logic [ 3:0] drain_strb_o;
logic drain_last_o;
logic drain_done_o;
logic rx_start_valid_i = 1'b0;
logic rx_start_ready_o;
logic [11:0] rx_base_i = '0;
logic [14:0] rx_limit_i = '0;
logic rx_valid_i = 1'b0;
logic rx_ready_o;
logic [ 7:0] rx_data_i = '0;
logic rx_commit_i = 1'b0;
logic rx_cancel_i = 1'b0;
logic rx_done_o;
logic [14:0] rx_bytes_o;
logic rx_overflow_o;
logic tx_start_valid_i = 1'b0;
logic tx_start_ready_o;
logic [11:0] tx_base_i = '0;
logic [14:0] tx_bytes_i = '0;
logic tx_valid_o;
logic tx_ready_i = 1'b1;
logic [ 7:0] tx_data_o;
logic tx_done_o;
logic busy_o;
logic ecc_corrected_o;
logic ecc_uncorrectable_o;
integer tx_count;
integer drain_count;
logic [ 7:0] tx_bytes [0:4];
logic [31:0] drain_words [0:1];
logic [ 3:0] drain_strb [0:1];
always #5 clk_i = ~clk_i;
always_ff @(posedge clk_i) begin
if (!rst_n_i) begin
tx_count <= 0;
drain_count <= 0;
end else begin
if (tx_valid_o && tx_ready_i) begin
tx_bytes[tx_count] <= tx_data_o;
tx_count <= tx_count + 1;
end
if (drain_valid_o && drain_ready_i) begin
drain_words[drain_count] <= drain_data_o;
drain_strb[drain_count] <= drain_strb_o;
drain_count <= drain_count + 1;
end
end
end
usb2_packet_store u_dut (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.fill_start_valid_i (fill_start_valid_i),
.fill_start_ready_o (fill_start_ready_o),
.fill_base_i (fill_base_i),
.fill_bytes_i (fill_bytes_i),
.fill_valid_i (fill_valid_i),
.fill_ready_o (fill_ready_o),
.fill_data_i (fill_data_i),
.fill_strb_i (fill_strb_i),
.fill_last_i (fill_last_i),
.fill_done_o (fill_done_o),
.fill_error_o (fill_error_o),
.drain_start_valid_i(drain_start_valid_i),
.drain_start_ready_o(drain_start_ready_o),
.drain_base_i (drain_base_i),
.drain_bytes_i (drain_bytes_i),
.drain_valid_o (drain_valid_o),
.drain_ready_i (drain_ready_i),
.drain_data_o (drain_data_o),
.drain_strb_o (drain_strb_o),
.drain_last_o (drain_last_o),
.drain_done_o (drain_done_o),
.rx_start_valid_i (rx_start_valid_i),
.rx_start_ready_o (rx_start_ready_o),
.rx_base_i (rx_base_i),
.rx_limit_i (rx_limit_i),
.rx_valid_i (rx_valid_i),
.rx_ready_o (rx_ready_o),
.rx_data_i (rx_data_i),
.rx_commit_i (rx_commit_i),
.rx_cancel_i (rx_cancel_i),
.rx_done_o (rx_done_o),
.rx_bytes_o (rx_bytes_o),
.rx_overflow_o (rx_overflow_o),
.tx_start_valid_i (tx_start_valid_i),
.tx_start_ready_o (tx_start_ready_o),
.tx_base_i (tx_base_i),
.tx_bytes_i (tx_bytes_i),
.tx_valid_o (tx_valid_o),
.tx_ready_i (tx_ready_i),
.tx_data_o (tx_data_o),
.tx_done_o (tx_done_o),
.busy_o (busy_o),
.ecc_corrected_o (ecc_corrected_o),
.ecc_uncorrectable_o(ecc_uncorrectable_o)
);
task automatic pulse_fill(input logic [31:0] data_i, input logic [3:0] strb_i,
input logic last_i);
begin
@(negedge clk_i);
fill_data_i = data_i;
fill_strb_i = strb_i;
fill_last_i = last_i;
fill_valid_i = 1'b1;
while (!fill_ready_o) @(negedge clk_i);
@(negedge clk_i);
fill_valid_i = 1'b0;
end
endtask
task automatic pulse_rx(input logic [7:0] data_i);
begin
@(negedge clk_i);
rx_data_i = data_i;
rx_valid_i = 1'b1;
while (!rx_ready_o) @(negedge clk_i);
@(negedge clk_i);
rx_valid_i = 1'b0;
end
endtask
initial begin
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
@(negedge clk_i);
fill_base_i = 12'd8;
fill_bytes_i = 15'd5;
fill_start_valid_i = 1'b1;
@(negedge clk_i);
fill_start_valid_i = 1'b0;
pulse_fill(32'h4433_2211, 4'hF, 1'b0);
pulse_fill(32'h0000_0055, 4'h1, 1'b1);
wait (fill_done_o);
if (fill_error_o) $fatal(1, "packet store fill failed");
@(negedge clk_i);
tx_base_i = 12'd8;
tx_bytes_i = 15'd5;
tx_start_valid_i = 1'b1;
@(negedge clk_i);
tx_start_valid_i = 1'b0;
wait (tx_done_o);
@(posedge clk_i);
if ((tx_count != 5) || (tx_bytes[0] != 8'h11) || (tx_bytes[1] != 8'h22) ||
(tx_bytes[2] != 8'h33) || (tx_bytes[3] != 8'h44) || (tx_bytes[4] != 8'h55)) begin
$fatal(1, "packet store transmit mismatch count=%0d", tx_count);
end
@(negedge clk_i);
rx_base_i = 12'd16;
rx_limit_i = 15'd5;
rx_start_valid_i = 1'b1;
@(negedge clk_i);
rx_start_valid_i = 1'b0;
pulse_rx(8'hA1);
pulse_rx(8'hB2);
pulse_rx(8'hC3);
pulse_rx(8'hD4);
pulse_rx(8'hE5);
@(negedge clk_i);
rx_commit_i = 1'b1;
@(negedge clk_i);
rx_commit_i = 1'b0;
wait (rx_done_o);
if ((rx_bytes_o != 15'd5) || rx_overflow_o) $fatal(1, "packet store receive failed");
@(negedge clk_i);
drain_base_i = 12'd16;
drain_bytes_i = 15'd5;
drain_start_valid_i = 1'b1;
@(negedge clk_i);
drain_start_valid_i = 1'b0;
wait (drain_done_o);
@(posedge clk_i);
if ((drain_count != 2) || (drain_words[0] != 32'hD4C3_B2A1) ||
(drain_words[1][7:0] != 8'hE5) || (drain_strb[0] != 4'hF) ||
(drain_strb[1] != 4'h1)) begin
$fatal(1, "packet store drain mismatch count=%0d", drain_count);
end
if (ecc_corrected_o || ecc_uncorrectable_o) $fatal(1, "unexpected ECC event");
$display("USB2 packet store paths passed");
$finish;
end
endmodule
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_pkg_tb;
logic [15:0] s_crc16;
initial begin
if (usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidOut) != 8'hE1) begin
$fatal(1, "USB2 OUT PID encoding mismatch");
end
if (!usb2_pkg::usb2_pid_valid(8'hD2) || usb2_pkg::usb2_pid_valid(8'h52)) begin
$fatal(1, "USB2 PID complement validation mismatch");
end
if (usb2_pkg::usb2_token_crc5(
11'd0
) != 5'h02 || usb2_pkg::usb2_token_crc5(
11'd1
) != 5'h1D) begin
$fatal(1, "USB2 token CRC5 vector mismatch");
end
s_crc16 = 16'hFFFF;
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "1");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "2");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "3");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "4");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "5");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "6");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "7");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "8");
s_crc16 = usb2_pkg::usb2_crc16_byte(s_crc16, "9");
if (usb2_pkg::usb2_crc16_finish(s_crc16) != 16'hB4C8) begin
$fatal(1, "USB2 CRC16 vector mismatch");
end
$display("USB2 PID and CRC vectors passed");
$finish;
end
endmodule
+235
View File
@@ -0,0 +1,235 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
`include "usb2_define.svh"
module usb2_reg_tb;
localparam logic [31:0] UsbBase = 32'h1001_6000;
logic s_clk;
logic s_rst_n;
logic [15:0] s_irq_event;
logic s_irq;
logic s_enable;
logic [ 1:0] s_force_role;
logic s_auto_role;
logic [ 7:0][31:0] s_endpoint_status;
logic [ 7:0][31:0] s_endpoint_bytes_in;
logic [ 7:0][31:0] s_endpoint_bytes_out;
logic [15:0][31:0] s_channel_status;
logic [15:0][31:0] s_channel_bytes;
logic [ 7:0][31:0] s_endpoint_cfg;
logic [ 7:0][31:0] s_endpoint_ram_in;
logic [ 7:0][31:0] s_endpoint_ram_out;
logic [ 7:0][31:0] s_endpoint_desc_in;
logic [ 7:0][31:0] s_endpoint_desc_out;
logic [ 7:0][31:0] s_endpoint_command;
logic [31:0] s_endpoint_complete_clear_in;
logic [31:0] s_endpoint_complete_clear_out;
logic s_endpoint_clear_seen;
logic [15:0][31:0] s_channel_cfg;
logic [15:0][31:0] s_channel_target;
logic [15:0][31:0] s_channel_interval;
logic [15:0][31:0] s_channel_ram;
logic [15:0][31:0] s_channel_desc;
logic [15:0][31:0] s_channel_command;
apb4_if u_apb4 (
.pclk (s_clk),
.presetn(s_rst_n)
);
usb2_reg u_reg (
.clk_i (s_clk),
.rst_n_i (s_rst_n),
.global_status_i (32'd0),
.role_status_i (32'd0),
.phy_status_i (32'd0),
.frame_i (32'd0),
.device_status_i (32'd0),
.setup_packet_i (64'd0),
.endpoint_pending_in_i (32'd0),
.endpoint_pending_out_i (32'd0),
.endpoint_complete_in_i (32'd0),
.endpoint_complete_out_i (32'd0),
.endpoint_status_i (s_endpoint_status),
.endpoint_bytes_in_i (s_endpoint_bytes_in),
.endpoint_bytes_out_i (s_endpoint_bytes_out),
.host_status_i (32'd0),
.port_status_i (32'd0),
.schedule_status_i (32'd0),
.channel_status_i (s_channel_status),
.channel_bytes_i (s_channel_bytes),
.ram_status_i (32'd0),
.ecc_status_i (32'd0),
.ecc_corrected_count_i (32'd0),
.ecc_uncorrectable_count_i (32'd0),
.debug_status_i (32'd0),
.perf_tx_bytes_i (32'd0),
.perf_rx_bytes_i (32'd0),
.perf_packets_i (32'd0),
.perf_retries_i (32'd0),
.perf_axi_stall_i (32'd0),
.perf_ram_stall_i (32'd0),
.perf_irq_count_i (32'd0),
.irq_event_i (s_irq_event),
.error_capture_i (1'b0),
.error_status_i (32'd0),
.error_code_i (32'd0),
.error_info_i (32'd0),
.error_desc_addr_i (32'd0),
.error_buffer_addr_i (32'd0),
.viewport_resp_valid_i (1'b0),
.viewport_read_data_i (8'd0),
.viewport_error_i (1'b0),
.enable_o (s_enable),
.soft_reset_o (),
.abort_o (),
.force_role_o (s_force_role),
.auto_role_o (s_auto_role),
.phy_reset_n_o (),
.phy_suspend_o (),
.remote_wake_o (),
.timeout_o (),
.test_ctrl_o (),
.perf_clear_o (),
.device_addr_o (),
.device_ctrl_o (),
.endpoint_cfg_o (s_endpoint_cfg),
.endpoint_ram_in_o (s_endpoint_ram_in),
.endpoint_ram_out_o (s_endpoint_ram_out),
.endpoint_desc_in_o (s_endpoint_desc_in),
.endpoint_desc_out_o (s_endpoint_desc_out),
.endpoint_command_o (s_endpoint_command),
.endpoint_complete_clear_in_o (s_endpoint_complete_clear_in),
.endpoint_complete_clear_out_o(s_endpoint_complete_clear_out),
.host_ctrl_o (),
.port_ctrl_o (),
.schedule_ctrl_o (),
.channel_cfg_o (s_channel_cfg),
.channel_target_o (s_channel_target),
.channel_interval_o (s_channel_interval),
.channel_ram_o (s_channel_ram),
.channel_desc_o (s_channel_desc),
.channel_command_o (s_channel_command),
.ram_ctrl_o (),
.ram_bist_start_o (),
.viewport_valid_o (),
.viewport_write_o (),
.viewport_addr_o (),
.viewport_write_data_o (),
.irq_o (s_irq),
.apb4 (u_apb4)
);
always #5 s_clk = ~s_clk;
always_ff @(posedge s_clk or negedge s_rst_n) begin
if (!s_rst_n) begin
s_endpoint_clear_seen <= 1'b0;
end else if ((s_endpoint_complete_clear_in == 32'h0000_00A5) &&
(s_endpoint_complete_clear_out == 32'd0)) begin
s_endpoint_clear_seen <= 1'b1;
end
end
task automatic apb_write(input logic [31:0] addr_i, input logic [31:0] data_i,
input logic expected_error_i);
begin
@(negedge s_clk);
u_apb4.paddr = addr_i;
u_apb4.pwdata = data_i;
u_apb4.pstrb = 4'hF;
u_apb4.pwrite = 1'b1;
u_apb4.psel = 1'b1;
u_apb4.penable = 1'b0;
@(negedge s_clk);
u_apb4.penable = 1'b1;
@(posedge s_clk);
#1;
if (!u_apb4.pready || (u_apb4.pslverr != expected_error_i)) begin
$fatal(1, "USB2 APB write failure addr=%h ready=%b error=%b", addr_i, u_apb4.pready,
u_apb4.pslverr);
end
@(negedge s_clk);
u_apb4.psel = 1'b0;
u_apb4.penable = 1'b0;
end
endtask
task automatic apb_read(input logic [31:0] addr_i, output logic [31:0] data_o,
input logic expected_error_i);
begin
@(negedge s_clk);
u_apb4.paddr = addr_i;
u_apb4.pwdata = 32'd0;
u_apb4.pstrb = 4'd0;
u_apb4.pwrite = 1'b0;
u_apb4.psel = 1'b1;
u_apb4.penable = 1'b0;
@(negedge s_clk);
u_apb4.penable = 1'b1;
@(posedge s_clk);
#1;
if (!u_apb4.pready || (u_apb4.pslverr != expected_error_i)) begin
$fatal(1, "USB2 APB read failure addr=%h ready=%b error=%b", addr_i, u_apb4.pready,
u_apb4.pslverr);
end
data_o = u_apb4.prdata;
@(negedge s_clk);
u_apb4.psel = 1'b0;
u_apb4.penable = 1'b0;
end
endtask
initial begin
logic [31:0] value;
s_clk = 1'b0;
s_rst_n = 1'b0;
s_irq_event = 16'd0;
u_apb4.paddr = 32'd0;
u_apb4.pprot = 3'd0;
u_apb4.psel = 1'b0;
u_apb4.penable = 1'b0;
u_apb4.pwrite = 1'b0;
u_apb4.pwdata = 32'd0;
u_apb4.pstrb = 4'd0;
for (int endpoint = 0; endpoint < 8; endpoint++) begin
s_endpoint_status[endpoint] = 32'd0;
s_endpoint_bytes_in[endpoint] = 32'd0;
s_endpoint_bytes_out[endpoint] = 32'd0;
end
for (int channel = 0; channel < 16; channel++) begin
s_channel_status[channel] = 32'd0;
s_channel_bytes[channel] = 32'd0;
end
repeat (3) @(posedge s_clk);
s_rst_n = 1'b1;
apb_read(UsbBase + `APB4_USB2__IP_ID, value, 1'b0);
if (value != 32'h5553_4232) $fatal(1, "USB2 IP ID mismatch");
apb_read(UsbBase + `APB4_USB2__CAPABILITY0, value, 1'b0);
if ((value[19:16] != 4'd7) || (value[27:24] != 4'd15)) begin
$fatal(1, "USB2 capability scale mismatch: %h", value);
end
apb_write(UsbBase + `APB4_USB2__ROLE_CTRL, 32'h0000_0006, 1'b0);
if ((s_force_role != 2'd2) || !s_auto_role) $fatal(1, "USB2 role control mismatch");
apb_write(
UsbBase + `APB4_USB2__ENDPOINT_BASE + `APB4_USB2__ENDPOINT_STRIDE +
`APB4_USB2__ENDPOINT_CFG,
32'h0000_020D, 1'b0);
apb_read(
UsbBase + `APB4_USB2__ENDPOINT_BASE + `APB4_USB2__ENDPOINT_STRIDE +
`APB4_USB2__ENDPOINT_CFG,
value, 1'b0);
if (value != 32'h0000_020D) $fatal(1, "USB2 endpoint configuration mismatch");
apb_write(UsbBase + `APB4_USB2__ENDPOINT_COMPLETE_IN, 32'h0000_00A5, 1'b0);
if (!s_endpoint_clear_seen) $fatal(1, "USB2 endpoint completion W1C mismatch");
apb_write(UsbBase + `APB4_USB2__IRQ_ENABLE, 32'h0000_0020, 1'b0);
apb_write(UsbBase + `APB4_USB2__IRQ_TEST, 32'h0000_0020, 1'b0);
apb_write(UsbBase + `APB4_USB2__GLOBAL_CTRL, 32'h0000_0009, 1'b0);
if (!s_enable || !s_irq) $fatal(1, "USB2 interrupt test failed");
apb_write(UsbBase + `APB4_USB2__ROLE_CTRL, 32'h0000_0001, 1'b1);
apb_read(UsbBase + 32'h0000_0001, value, 1'b1);
$display("USB2 APB register contract passed");
$finish;
end
endmodule
+180
View File
@@ -0,0 +1,180 @@
`timescale 1ns / 1ps
module usb2_scheduler_tb;
logic s_clk = 1'b0;
logic s_rst_n = 1'b0;
logic s_reinitialize = 1'b0;
logic [13:0] s_frame = 14'd0;
logic [ 7:0][31:0] s_endpoint_cfg = '0;
logic [ 7:0][31:0] s_endpoint_ram_in = '0;
logic [ 7:0][31:0] s_endpoint_ram_out = '0;
logic [ 7:0][31:0] s_endpoint_desc_in = '0;
logic [ 7:0][31:0] s_endpoint_desc_out = '0;
logic [ 7:0][31:0] s_endpoint_command = '0;
logic [15:0][31:0] s_channel_cfg = '0;
logic [15:0][31:0] s_channel_target = '0;
logic [15:0][31:0] s_channel_interval = '0;
logic [15:0][31:0] s_channel_ram = '0;
logic [15:0][31:0] s_channel_desc = '0;
logic [15:0][31:0] s_channel_command = '0;
logic s_work_valid;
logic [63:0] s_work_data;
logic s_result_valid = 1'b0;
logic [63:0] s_result_data = '0;
logic s_fill_ready = 1'b0;
logic s_dma_start;
logic s_dma_done = 1'b0;
logic s_buffer_event_valid = 1'b0;
logic [ 2:0] s_buffer_event_data = 3'd0;
logic s_busy;
logic [ 3:0] s_seen_index [0:3];
integer s_work_count = 0;
always #5 s_clk = ~s_clk;
always_ff @(posedge s_clk) begin
s_dma_done <= 1'b0;
s_buffer_event_valid <= 1'b0;
s_result_valid <= 1'b0;
if (s_dma_start) begin
s_dma_done <= 1'b1;
s_buffer_event_valid <= 1'b1;
end
if (s_work_valid) begin
s_seen_index[s_work_count] <= s_work_data[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
s_work_count <= s_work_count + 1;
s_result_data <= 64'd0;
s_result_data[usb2_pkg::USB2_RESULT_ROLE_LSB+:2] <= usb2_pkg::Usb2RoleHost;
s_result_data[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] <=
s_work_data[usb2_pkg::USB2_WORK_INDEX_LSB+:4];
s_result_data[usb2_pkg::USB2_RESULT_LENGTH_LSB+:15] <= 15'd4;
s_result_valid <= 1'b1;
end
end
usb2_scheduler u_scheduler (
.clk_i (s_clk),
.rst_n_i (s_rst_n),
.enable_i (1'b1),
.abort_i (1'b0),
.reinitialize_i (s_reinitialize),
.perf_clear_i (1'b0),
.high_speed_i (1'b1),
.frame_i (s_frame),
.active_role_i (usb2_pkg::Usb2RoleHost),
.endpoint_cfg_i (s_endpoint_cfg),
.endpoint_ram_in_i (s_endpoint_ram_in),
.endpoint_ram_out_i (s_endpoint_ram_out),
.endpoint_desc_in_i (s_endpoint_desc_in),
.endpoint_desc_out_i (s_endpoint_desc_out),
.endpoint_command_i (s_endpoint_command),
.endpoint_complete_clear_in_i (32'd0),
.endpoint_complete_clear_out_i(32'd0),
.channel_cfg_i (s_channel_cfg),
.channel_target_i (s_channel_target),
.channel_interval_i (s_channel_interval),
.channel_ram_i (s_channel_ram),
.channel_desc_i (s_channel_desc),
.channel_command_i (s_channel_command),
.work_valid_o (s_work_valid),
.work_ready_i (1'b1),
.work_data_o (s_work_data),
.result_valid_i (s_result_valid),
.result_ready_o (),
.result_data_i (s_result_data),
.fill_cmd_valid_o (),
.fill_cmd_ready_i (s_fill_ready),
.fill_cmd_data_o (),
.drain_cmd_valid_o (),
.drain_cmd_ready_i (1'b1),
.drain_cmd_data_o (),
.buffer_event_valid_i (s_buffer_event_valid),
.buffer_event_ready_o (),
.buffer_event_data_i (s_buffer_event_data),
.dma_start_o (s_dma_start),
.dma_abort_o (),
.dma_memory_to_packet_o (),
.dma_desc_base_o (),
.dma_transfer_bytes_o (),
.dma_busy_i (1'b0),
.dma_done_i (s_dma_done),
.dma_error_i (1'b0),
.dma_error_code_i (8'd0),
.dma_current_desc_i (32'd0),
.dma_bytes_done_i (32'd4),
.retry_i (1'b0),
.endpoint_pending_in_o (),
.endpoint_pending_out_o (),
.endpoint_complete_in_o (),
.endpoint_complete_out_o (),
.endpoint_status_o (),
.endpoint_bytes_in_o (),
.endpoint_bytes_out_o (),
.channel_status_o (),
.channel_bytes_o (),
.irq_event_o (),
.error_capture_o (),
.error_status_o (),
.error_code_o (),
.error_info_o (),
.error_desc_addr_o (),
.error_buffer_addr_o (),
.perf_tx_bytes_o (),
.perf_rx_bytes_o (),
.perf_packets_o (),
.perf_retries_o (),
.perf_irq_count_o (),
.busy_o (s_busy)
);
task automatic issue_channel(input int channel_i);
begin
@(negedge s_clk);
s_channel_command[channel_i] = 32'd1;
@(negedge s_clk);
s_channel_command[channel_i] = 32'd0;
end
endtask
initial begin
for (int channel = 0; channel < 16; channel++) begin
s_channel_cfg[channel] = 32'h0040_0009;
s_channel_ram[channel] = 32'h0004_0000;
s_channel_desc[channel] = 32'h4000_0000 + (32'(channel) << 5);
end
s_channel_cfg[3][3:2] = usb2_pkg::Usb2TransferInterrupt;
s_channel_interval[3] = 32'd8;
repeat (4) @(posedge s_clk);
s_rst_n = 1'b1;
issue_channel(1);
wait (s_busy);
issue_channel(2);
repeat (2) @(posedge s_clk);
s_fill_ready = 1'b1;
wait (s_work_count == 2);
wait (!s_busy);
if ((s_seen_index[0] != 4'd1) || (s_seen_index[1] != 4'd2)) begin
$fatal(1, "USB2 busy command queue dropped or reordered work");
end
issue_channel(3);
wait (s_work_count == 3);
wait (!s_busy);
s_frame = 14'd1;
issue_channel(3);
repeat (8) @(posedge s_clk);
if (s_work_count != 3) $fatal(1, "USB2 periodic command ran before its interval");
s_frame = 14'd8;
wait (s_work_count == 4);
if (s_seen_index[3] != 4'd3) $fatal(1, "USB2 periodic command was not retained");
@(negedge s_clk);
s_reinitialize = 1'b1;
@(negedge s_clk);
s_reinitialize = 1'b0;
$display("USB2 scheduler queue and interval passed");
$finish;
end
endmodule
+208
View File
@@ -0,0 +1,208 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module usb2_sie_tb;
logic s_clk;
logic s_rst_n;
logic s_rx_start;
logic s_rx_valid;
logic [ 7:0] s_rx_data;
logic s_rx_end;
logic s_rx_error;
logic s_payload_valid;
logic [ 7:0] s_payload_data;
logic s_packet_done;
logic s_packet_good;
logic [ 3:0] s_packet_pid;
logic [ 6:0] s_token_addr;
logic [ 3:0] s_token_endpoint;
logic [10:0] s_payload_length;
logic s_crc_error;
logic s_request_valid;
logic s_request_ready;
logic [ 3:0] s_request_pid;
logic [10:0] s_request_token;
logic [10:0] s_request_length;
logic s_link_start_valid;
logic s_link_start_ready;
logic [ 3:0] s_link_pid;
logic s_link_has_data;
logic s_link_data_valid;
logic s_link_data_ready;
logic [ 7:0] s_link_data;
logic s_link_data_last;
logic s_link_done;
logic s_tx_done;
logic [ 7:0] s_payload_seen [0:1];
logic [ 1:0] s_payload_seen_count;
logic [ 7:0] s_tx_seen [0:1];
logic [ 1:0] s_tx_seen_count;
usb2_sie_rx u_rx (
.clk_i (s_clk),
.rst_n_i (s_rst_n),
.rx_start_i (s_rx_start),
.rx_valid_i (s_rx_valid),
.rx_data_i (s_rx_data),
.rx_end_i (s_rx_end),
.rx_error_i (s_rx_error),
.payload_valid_o (s_payload_valid),
.payload_data_o (s_payload_data),
.packet_done_o (s_packet_done),
.packet_good_o (s_packet_good),
.packet_pid_o (s_packet_pid),
.token_addr_o (s_token_addr),
.token_endpoint_o(s_token_endpoint),
.frame_number_o (),
.payload_length_o(s_payload_length),
.pid_error_o (),
.crc_error_o (s_crc_error),
.framing_error_o ()
);
usb2_sie_tx u_tx (
.clk_i (s_clk),
.rst_n_i (s_rst_n),
.request_valid_i (s_request_valid),
.request_ready_o (s_request_ready),
.request_pid_i (s_request_pid),
.request_token_i (s_request_token),
.request_length_i (s_request_length),
.payload_valid_i (1'b0),
.payload_ready_o (),
.payload_data_i (8'd0),
.link_start_valid_o(s_link_start_valid),
.link_start_ready_i(s_link_start_ready),
.link_pid_o (s_link_pid),
.link_has_data_o (s_link_has_data),
.link_data_valid_o (s_link_data_valid),
.link_data_ready_i (s_link_data_ready),
.link_data_o (s_link_data),
.link_data_last_o (s_link_data_last),
.link_done_i (s_link_done),
.link_error_i (1'b0),
.busy_o (),
.done_o (s_tx_done),
.error_o ()
);
always #5 s_clk = ~s_clk;
always_ff @(posedge s_clk) begin
if (s_payload_valid) begin
s_payload_seen[s_payload_seen_count] <= s_payload_data;
s_payload_seen_count <= s_payload_seen_count + 1'b1;
end
if (s_link_data_valid && s_link_data_ready) begin
s_tx_seen[s_tx_seen_count] <= s_link_data;
s_tx_seen_count <= s_tx_seen_count + 1'b1;
end
end
task automatic rx_begin;
begin
@(negedge s_clk);
s_rx_start = 1'b1;
@(negedge s_clk);
s_rx_start = 1'b0;
end
endtask
task automatic rx_byte(input logic [7:0] data_i);
begin
s_rx_data = data_i;
s_rx_valid = 1'b1;
@(negedge s_clk);
s_rx_valid = 1'b0;
@(negedge s_clk);
end
endtask
task automatic rx_finish;
begin
s_rx_end = 1'b1;
@(posedge s_clk);
#1;
s_rx_end = 1'b0;
end
endtask
initial begin
logic [15:0] data_crc;
s_clk = 1'b0;
s_rst_n = 1'b0;
s_rx_start = 1'b0;
s_rx_valid = 1'b0;
s_rx_data = 8'd0;
s_rx_end = 1'b0;
s_rx_error = 1'b0;
s_request_valid = 1'b0;
s_request_pid = usb2_pkg::Usb2PidOut;
s_request_token = 11'd0;
s_request_length = 11'd0;
s_link_start_ready = 1'b1;
s_link_data_ready = 1'b1;
s_link_done = 1'b0;
s_payload_seen_count = '0;
s_tx_seen_count = '0;
repeat (2) @(negedge s_clk);
s_rst_n = 1'b1;
rx_begin();
rx_byte(usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidOut));
rx_byte(8'h01);
rx_byte(8'hE8);
rx_finish();
if (!s_packet_done || !s_packet_good || (s_packet_pid != usb2_pkg::Usb2PidOut) ||
(s_token_addr != 7'd1) || (s_token_endpoint != 4'd0)) begin
$fatal(1, "USB2 token packet decode failed");
end
data_crc = usb2_pkg::usb2_crc16_byte(16'hFFFF, 8'h41);
data_crc = usb2_pkg::usb2_crc16_byte(data_crc, 8'h42);
data_crc = usb2_pkg::usb2_crc16_finish(data_crc);
rx_begin();
rx_byte(usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidData0));
rx_byte(8'h41);
rx_byte(8'h42);
rx_byte(data_crc[7:0]);
rx_byte(data_crc[15:8]);
rx_finish();
if (!s_packet_done || !s_packet_good || (s_payload_length != 11'd2) ||
(s_payload_seen_count != 2'd2) || (s_payload_seen[0] != 8'h41) ||
(s_payload_seen[1] != 8'h42)) begin
$fatal(1, "USB2 data packet decode failed");
end
rx_begin();
rx_byte(usb2_pkg::usb2_pid_byte(usb2_pkg::Usb2PidData0));
rx_byte(8'h01);
rx_byte(8'h00);
rx_finish();
if (!s_packet_done || s_packet_good || !s_crc_error) begin
$fatal(1, "USB2 bad data CRC accepted");
end
@(negedge s_clk);
s_request_pid = usb2_pkg::Usb2PidOut;
s_request_token = 11'd1;
s_request_valid = 1'b1;
@(negedge s_clk);
s_request_valid = 1'b0;
wait (s_link_data_last);
@(negedge s_clk);
s_link_done = 1'b1;
@(posedge s_clk);
@(posedge s_clk);
#1;
s_link_done = 1'b0;
if (!s_tx_done || (s_link_pid != usb2_pkg::Usb2PidOut) || !s_link_has_data ||
(s_tx_seen_count != 2'd2) || (s_tx_seen[0] != 8'h01) || (s_tx_seen[1] != 8'hE8)) begin
$fatal(1, "USB2 token packet encode failed");
end
$display("USB2 SIE packet encode/decode passed");
$finish;
end
endmodule
+277
View File
@@ -0,0 +1,277 @@
`timescale 1ns / 1ps
module usb2_transaction_engine_tb;
logic clk_i = 1'b0;
logic rst_n_i = 1'b0;
usb2_pkg::usb2_role_e active_role_i = usb2_pkg::Usb2RoleHost;
logic [ 7:0][31:0] endpoint_cfg_i = '0;
logic work_valid_i = 1'b0;
logic work_ready_o;
logic [63:0] work_data_i = '0;
logic result_valid_o;
logic [63:0] result_data_o;
logic setup_valid_o;
logic [63:0] setup_data_o;
logic tx_request_valid_o;
logic tx_request_ready_i = 1'b1;
logic [ 3:0] tx_request_pid_o;
logic [10:0] tx_request_token_o;
logic [10:0] tx_request_length_o;
logic tx_payload_valid_o;
logic tx_payload_ready_i = 1'b1;
logic [ 7:0] tx_payload_data_o;
logic tx_done_i = 1'b0;
logic tx_error_i = 1'b0;
logic rx_payload_valid_i = 1'b0;
logic [ 7:0] rx_payload_data_i = '0;
logic rx_packet_done_i = 1'b0;
logic rx_packet_good_i = 1'b0;
logic [ 3:0] rx_packet_pid_i = '0;
logic store_rx_start_valid;
logic store_rx_start_ready;
logic [11:0] store_rx_base;
logic [14:0] store_rx_limit;
logic store_rx_valid;
logic store_rx_ready;
logic [ 7:0] store_rx_data;
logic store_rx_commit;
logic store_rx_cancel;
logic store_rx_done;
logic [14:0] store_rx_bytes;
logic store_rx_overflow;
logic store_tx_start_valid;
logic store_tx_start_ready;
logic [11:0] store_tx_base;
logic [14:0] store_tx_bytes;
logic store_tx_valid;
logic store_tx_ready;
logic [ 7:0] store_tx_data;
logic store_tx_done;
logic fill_start_valid_i = 1'b0;
logic fill_start_ready_o;
logic fill_valid_i = 1'b0;
logic fill_ready_o;
logic [31:0] fill_data_i = '0;
logic [ 3:0] fill_strb_i = '0;
logic fill_last_i = 1'b0;
logic fill_done_o;
logic fill_error_o;
logic busy_o;
logic retry_o;
logic protocol_error_o;
logic pending_tx_q = 1'b0;
logic pending_data_q = 1'b0;
logic data_consumed_q = 1'b0;
integer pid_count;
logic [ 3:0] observed_pid [0:3];
always #5 clk_i = ~clk_i;
always_ff @(posedge clk_i) begin
tx_done_i <= 1'b0;
rx_packet_done_i <= 1'b0;
rx_packet_good_i <= 1'b0;
if (!rst_n_i) begin
pending_tx_q <= 1'b0;
pending_data_q <= 1'b0;
data_consumed_q <= 1'b0;
pid_count <= 0;
end else begin
if (tx_request_valid_o && tx_request_ready_i) begin
observed_pid[pid_count] <= tx_request_pid_o;
pid_count <= pid_count + 1;
if (usb2_pkg::usb2_pid_is_data(tx_request_pid_o)) begin
pending_data_q <= 1'b1;
end else begin
pending_tx_q <= 1'b1;
end
end
if (pending_tx_q) begin
tx_done_i <= 1'b1;
pending_tx_q <= 1'b0;
end
if (pending_data_q && tx_payload_valid_o && tx_payload_ready_i &&
(tx_payload_data_o == 8'h55)) begin
data_consumed_q <= 1'b1;
end
if (pending_data_q && data_consumed_q) begin
tx_done_i <= 1'b1;
pending_data_q <= 1'b0;
data_consumed_q <= 1'b0;
end
if ((pid_count == 2) && !pending_data_q && !pending_tx_q && tx_done_i) begin
rx_packet_pid_i <= usb2_pkg::Usb2PidAck;
rx_packet_done_i <= 1'b1;
rx_packet_good_i <= 1'b1;
end
end
end
usb2_transaction_engine u_engine (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.active_role_i (active_role_i),
.device_addr_i (7'd0),
.endpoint_cfg_i (endpoint_cfg_i),
.timeout_i (32'd1000),
.work_valid_i (work_valid_i),
.work_ready_o (work_ready_o),
.work_data_i (work_data_i),
.result_valid_o (result_valid_o),
.result_ready_i (1'b1),
.result_data_o (result_data_o),
.setup_valid_o (setup_valid_o),
.setup_ready_i (1'b1),
.setup_data_o (setup_data_o),
.tx_request_valid_o (tx_request_valid_o),
.tx_request_ready_i (tx_request_ready_i),
.tx_request_pid_o (tx_request_pid_o),
.tx_request_token_o (tx_request_token_o),
.tx_request_length_o (tx_request_length_o),
.tx_payload_valid_o (tx_payload_valid_o),
.tx_payload_ready_i (tx_payload_ready_i),
.tx_payload_data_o (tx_payload_data_o),
.tx_done_i (tx_done_i),
.tx_error_i (tx_error_i),
.rx_payload_valid_i (rx_payload_valid_i),
.rx_payload_data_i (rx_payload_data_i),
.rx_packet_done_i (rx_packet_done_i),
.rx_packet_good_i (rx_packet_good_i),
.rx_packet_pid_i (rx_packet_pid_i),
.rx_token_addr_i (7'd0),
.rx_token_endpoint_i (4'd0),
.rx_payload_length_i (11'd0),
.store_rx_start_valid_o(store_rx_start_valid),
.store_rx_start_ready_i(store_rx_start_ready),
.store_rx_base_o (store_rx_base),
.store_rx_limit_o (store_rx_limit),
.store_rx_valid_o (store_rx_valid),
.store_rx_ready_i (store_rx_ready),
.store_rx_data_o (store_rx_data),
.store_rx_commit_o (store_rx_commit),
.store_rx_cancel_o (store_rx_cancel),
.store_rx_done_i (store_rx_done),
.store_rx_bytes_i (store_rx_bytes),
.store_rx_overflow_i (store_rx_overflow),
.store_tx_start_valid_o(store_tx_start_valid),
.store_tx_start_ready_i(store_tx_start_ready),
.store_tx_base_o (store_tx_base),
.store_tx_bytes_o (store_tx_bytes),
.store_tx_valid_i (store_tx_valid),
.store_tx_ready_o (store_tx_ready),
.store_tx_data_i (store_tx_data),
.store_tx_done_i (store_tx_done),
.busy_o (busy_o),
.retry_o (retry_o),
.protocol_error_o (protocol_error_o)
);
usb2_packet_store u_store (
.clk_i (clk_i),
.rst_n_i (rst_n_i),
.fill_start_valid_i (fill_start_valid_i),
.fill_start_ready_o (fill_start_ready_o),
.fill_base_i (12'd4),
.fill_bytes_i (15'd5),
.fill_valid_i (fill_valid_i),
.fill_ready_o (fill_ready_o),
.fill_data_i (fill_data_i),
.fill_strb_i (fill_strb_i),
.fill_last_i (fill_last_i),
.fill_done_o (fill_done_o),
.fill_error_o (fill_error_o),
.drain_start_valid_i(1'b0),
.drain_start_ready_o(),
.drain_base_i ('0),
.drain_bytes_i ('0),
.drain_valid_o (),
.drain_ready_i (1'b0),
.drain_data_o (),
.drain_strb_o (),
.drain_last_o (),
.drain_done_o (),
.rx_start_valid_i (store_rx_start_valid),
.rx_start_ready_o (store_rx_start_ready),
.rx_base_i (store_rx_base),
.rx_limit_i (store_rx_limit),
.rx_valid_i (store_rx_valid),
.rx_ready_o (store_rx_ready),
.rx_data_i (store_rx_data),
.rx_commit_i (store_rx_commit),
.rx_cancel_i (store_rx_cancel),
.rx_done_o (store_rx_done),
.rx_bytes_o (store_rx_bytes),
.rx_overflow_o (store_rx_overflow),
.tx_start_valid_i (store_tx_start_valid),
.tx_start_ready_o (store_tx_start_ready),
.tx_base_i (store_tx_base),
.tx_bytes_i (store_tx_bytes),
.tx_valid_o (store_tx_valid),
.tx_ready_i (store_tx_ready),
.tx_data_o (store_tx_data),
.tx_done_o (store_tx_done),
.busy_o (),
.ecc_corrected_o (),
.ecc_uncorrectable_o()
);
initial begin
repeat (4) @(posedge clk_i);
rst_n_i = 1'b1;
@(negedge clk_i);
fill_start_valid_i = 1'b1;
@(negedge clk_i);
fill_start_valid_i = 1'b0;
@(negedge clk_i);
fill_data_i = 32'h4433_2211;
fill_strb_i = 4'hF;
fill_last_i = 1'b0;
fill_valid_i = 1'b1;
@(negedge clk_i);
fill_data_i = 32'h0000_0055;
fill_strb_i = 4'h1;
fill_last_i = 1'b1;
@(negedge clk_i);
fill_valid_i = 1'b0;
wait (fill_done_o);
if (fill_error_o) $fatal(1, "protocol test prefill failed");
work_data_i = '0;
work_data_i[usb2_pkg::USB2_WORK_ROLE_LSB+:2] = usb2_pkg::Usb2RoleHost;
work_data_i[usb2_pkg::USB2_WORK_INDEX_LSB+:4] = 4'd3;
work_data_i[usb2_pkg::USB2_WORK_DIRECTION_IN] = 1'b0;
work_data_i[usb2_pkg::USB2_WORK_TYPE_LSB+:2] = usb2_pkg::Usb2TransferBulk;
work_data_i[usb2_pkg::USB2_WORK_ADDRESS_LSB+:7] = 7'd5;
work_data_i[usb2_pkg::USB2_WORK_ENDPOINT_LSB+:4] = 4'd2;
work_data_i[usb2_pkg::USB2_WORK_RAM_BASE_LSB+:12] = 12'd4;
work_data_i[usb2_pkg::USB2_WORK_LENGTH_LSB+:15] = 15'd5;
@(negedge clk_i);
work_valid_i = 1'b1;
@(negedge clk_i);
work_valid_i = 1'b0;
wait (result_valid_o);
if ((result_data_o[usb2_pkg::USB2_RESULT_CODE_LSB+:4] != usb2_pkg::Usb2ResultSuccess) ||
(result_data_o[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] != 4'd3) || (pid_count != 2) ||
(observed_pid[0] != usb2_pkg::Usb2PidOut) ||
(observed_pid[1] != usb2_pkg::Usb2PidData0) || retry_o || protocol_error_o) begin
$fatal(1, "host OUT transaction failed result=%h pids=%h,%h", result_data_o, observed_pid[0],
observed_pid[1]);
end
wait (work_ready_o);
work_data_i[usb2_pkg::USB2_WORK_INDEX_LSB+:4] = 4'd4;
work_data_i[usb2_pkg::USB2_WORK_TYPE_LSB+:2] = usb2_pkg::Usb2TransferIsochronous;
@(negedge clk_i);
work_valid_i = 1'b1;
@(negedge clk_i);
work_valid_i = 1'b0;
wait (result_valid_o);
if ((result_data_o[usb2_pkg::USB2_RESULT_CODE_LSB+:4] != usb2_pkg::Usb2ResultSuccess) ||
(result_data_o[usb2_pkg::USB2_RESULT_INDEX_LSB+:4] != 4'd4) || (pid_count != 4) ||
(observed_pid[2] != usb2_pkg::Usb2PidOut) ||
(observed_pid[3] != usb2_pkg::Usb2PidData0) || retry_o || protocol_error_o) begin
$fatal(1, "host ISO OUT transaction waited for a handshake result=%h", result_data_o);
end
$display("USB2 transaction engine host OUT passed");
$finish;
end
endmodule
+27
View File
@@ -39,6 +39,7 @@ def test_clock_reset_domain_inventory_matches_the_rcu() -> None:
"audio",
"jtag",
"dvp",
"usb2_ulpi",
}
assert {
(crossing["name"], crossing["source"], crossing["destination"])
@@ -51,6 +52,32 @@ def test_clock_reset_domain_inventory_matches_the_rcu() -> None:
crossing for crossing in document["crossings"] if crossing["name"] == "clint_timebase"
)
assert clint["primitive"] == "edge_det"
assert {
"usb2_work",
"usb2_result",
"usb2_setup",
"usb2_fill_command",
"usb2_drain_command",
"usb2_buffer_event",
"usb2_view_request",
"usb2_view_response",
"usb2_fill_stream",
"usb2_drain_stream",
"usb2_role",
"usb2_role_reset",
"usb2_phy_reset",
"usb2_high_speed",
"usb2_enable",
"usb2_device_addr",
"usb2_endpoint_config",
"usb2_timeout",
"usb2_phy_status",
"usb2_transaction_busy",
"usb2_frame",
"usb2_ecc_counts",
"usb2_retry_event",
"usb2_protocol_event",
} <= {crossing["name"] for crossing in document["crossings"]}
assert {
"rtc_command",
"rtc_response",
+7
View File
@@ -65,6 +65,7 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "RS_SOC_APB4_SDIO_BASE" not in header
assert "RS_SOC_APB4_SDIO0_BASE UINT32_C(0x1000F000)" in header
assert "RS_SOC_APB4_SDIO1_BASE UINT32_C(0x10015000)" in header
assert "RS_SOC_APB4_USB2_BASE UINT32_C(0x10016000)" in header
assert "RS_SOC_NMI_" not in header
assert "RS_SOC_OPIPSRAM_BASE UINT32_C(0x48000000)" in header
assert "RS_SOC_HAS_SRAM 1U" in header
@@ -76,6 +77,8 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET 32'h00000090" in rtl
assert "SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET 32'h00000094" in rtl
assert "SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET 32'h00000098" in rtl
assert "SOC_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET 32'h0000009C" in rtl
assert "SOC_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET 32'h000000A0" in rtl
assert "SOC_ADDR_IS_APB4_PERIPH(addr)" in rtl
assert "SOC_ADDR_IS_NMI" not in rtl
user_policy = rtl.split("`define SOC_USER_ADDR_READABLE", 1)[1].split(
@@ -92,6 +95,8 @@ def test_generated_artifacts_share_the_capacity_baseline(tmp_path: Path) -> None
assert "RS_SOC_SYSCTRL_PERF_SDIO0_WAIT_HI_OFFSET UINT32_C(0x00000090)" in header
assert "RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_LO_OFFSET UINT32_C(0x00000094)" in header
assert "RS_SOC_SYSCTRL_PERF_SDIO1_WAIT_HI_OFFSET UINT32_C(0x00000098)" in header
assert "RS_SOC_SYSCTRL_PERF_USB2_WAIT_LO_OFFSET UINT32_C(0x0000009C)" in header
assert "RS_SOC_SYSCTRL_PERF_USB2_WAIT_HI_OFFSET UINT32_C(0x000000A0)" in header
assert "PSRAM (wxa!ri) : ORIGIN = 0x40000000, LENGTH = 0x02000000" in linker
assert "OPIPSRAM (wxa!ri) : ORIGIN = 0x48000000, LENGTH = 0x08000000" in linker
@@ -124,8 +129,10 @@ def test_user_ip_is_always_emitted_for_the_fixed_platform(tmp_path: Path) -> Non
assert "#define RS_SOC_APB4_OPIPSRAM_BASE UINT32_C(0x10010000)" in header
assert "`define SOC_ADDR_APB4_SDIO0_BASE 32'h1000F000" in rtl
assert "`define SOC_ADDR_APB4_SDIO1_BASE 32'h10015000" in rtl
assert "`define SOC_ADDR_APB4_USB2_BASE 32'h10016000" in rtl
assert "`define SOC_ADDR_APB4_CRYPTO_BASE 32'h1000C000" in rtl
assert "#define RS_SOC_APB4_CRYPTO_BASE UINT32_C(0x1000C000)" in header
assert "#define RS_SOC_APB4_USB2_BASE UINT32_C(0x10016000)" in header
def test_bootstrap_assembly_uses_the_generated_gpio_admin_base() -> None:
+17
View File
@@ -57,6 +57,10 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
assert "output sdio1_clk_o_pad," in ports
assert "inout sdio1_cmd_io_pad," in ports
assert "inout sdio1_dat3_io_pad," in ports
assert "inout usb2_ulpi_clk_i_pad," in ports
assert "inout usb2_ulpi_data7_io_pad," in ports
assert "output usb2_ulpi_stp_o_pad," in ports
assert "output usb2_ulpi_reset_n_o_pad," in ports
assert "HAVE_DEBUG" not in ports
assert "HAVE_DEBUG" not in pads
assert "u_gpio_31_io_pad" in pads
@@ -67,6 +71,13 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
assert ".c2p(u_sdio1_if.cmd_do_o)" in pads
assert ".c2p_en(u_sdio1_if.cmd_oe_o)" in pads
assert ".p2c(u_sdio1_if.cmd_di_i)" in pads
assert "tc_io_tri_pad u_usb2_ulpi_clk_i_pad" in pads
assert ".p2c(s_usb2_ulpi_clk)" in pads
assert "u_usb2_ulpi_data7_io_pad" in pads
assert ".c2p(u_usb2_ulpi_if.data_do_o[7])" in pads
assert ".c2p_en(u_usb2_ulpi_if.data_oe_o)" in pads
assert ".p2c(u_usb2_ulpi_if.data_di_i[7])" in pads
assert "tc_io_out_pad u_usb2_ulpi_stp_o_pad" in pads
assert ".gpio_30_io_pad(gpio_io30)" in fpga
assert ".gpio_15_io_pad()" in fpga
assert ".gpio_24_io_pad(s_psram_dat1)" in testbench
@@ -78,11 +89,17 @@ def test_pin_map_generates_asic_and_platform_bindings(tmp_path: Path) -> None:
assert ".sdio1_clk_o_pad(sdio1_clk_o)" in verilator
assert ".sdio1_cmd_io_pad(sdio1_cmd_io)" in verilator
assert ".sdio1_dat3_io_pad(sdio1_dat3_io)" in verilator
assert ".usb2_ulpi_clk_i_pad(s_usb2_ulpi_clk)" in verilator
assert ".usb2_ulpi_data7_io_pad(s_usb2_ulpi_data[7])" in verilator
assert ".sdio1_clk_o_pad(s_sdio1_clk)" in testbench
assert ".sdio1_cmd_io_pad(s_sdio1_cmd)" in testbench
assert ".sdio1_dat0_io_pad(s_sdio1_dat[0])" in testbench
assert ".usb2_ulpi_clk_i_pad(s_usb2_ulpi_clk)" in testbench
assert ".usb2_ulpi_data0_io_pad(s_usb2_ulpi_data[0])" in testbench
assert ".sdio1_clk_o_pad()" in fpga
assert ".sdio1_cmd_io_pad()" in fpga
assert ".usb2_ulpi_clk_i_pad(s_usb2_ulpi_clk)" in fpga
assert ".usb2_ulpi_data0_io_pad(s_usb2_ulpi_data[0])" in fpga
assert ".jtag_tck_i_pad(s_jtag_tck)" in testbench
assert "user_gpio_" not in ports
assert "user_gpio_" not in pads
+11 -3
View File
@@ -67,6 +67,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
fabric = (tmp_path / "rtl/soc_fabric_interfaces.svh").read_text(encoding="utf-8")
bus_fabric = (tmp_path / "rtl/soc_bus_fabric.svh").read_text(encoding="utf-8")
apb_system_fabric = (tmp_path / "rtl/apb4_system_fabric.svh").read_text(encoding="utf-8")
apb_periph_fabric = (tmp_path / "rtl/apb4_periph_fabric.svh").read_text(encoding="utf-8")
irq_config = (tmp_path / "rtl/soc_irq_config.svh").read_text(encoding="utf-8")
rib_irq = (tmp_path / "rtl/apb4_periph_irq_bindings.svh").read_text(encoding="utf-8")
apb_irq = (tmp_path / "rtl/apb4_system_irq_bindings.svh").read_text(encoding="utf-8")
@@ -74,7 +75,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
irq_sva = (tmp_path / "rtl/soc_irq_sva.svh").read_text(encoding="utf-8")
filelist = (tmp_path / "soc_topology.fl").read_text(encoding="utf-8")
assert interfaces.count("apb4_if u_") == 19
assert interfaces.count("apb4_if u_") == 20
assert "nmi_if" not in interfaces
assert "soc_nmi" not in interfaces
assert "assign s_psel_comb[17] = `SOC_ADDR_IS_APB4_I2C1(s_decode_addr);" in routes
@@ -84,6 +85,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign s_psel_comb[16] = `SOC_ADDR_IS_APB4_OPIPSRAM(s_decode_addr);" in routes
assert "assign s_psel_comb[18] = `SOC_ADDR_IS_APB4_SDIO1(s_decode_addr);" in routes
assert "assign s_psel_comb[19] = `SOC_ADDR_IS_APB4_CRYPTO(s_decode_addr);" in routes
assert "assign s_psel_comb[20] = `SOC_ADDR_IS_APB4_USB2(s_decode_addr);" in routes
assert gpio.count("// GPIO") == 64
assert "u_uart1_if" not in gpio
assert "assign u_uart0_if.cts_n_i = u_gpio_if.di_i[0];" in gpio
@@ -117,18 +119,20 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign user_ip.paddr = s_addr_q;" in apb_routes
assert "({32{s_psel_q[7]}} & user_ip.prdata)" in apb_response
assert "localparam int NSLV = 8;" in apb_declarations
assert fabric.count("axi4_if #(") == 7
assert fabric.count("axi4_if #(") == 8
assert ".mgmt_axi4(u_mgmt_axi4_if)" in bus_fabric
assert ".user_axi4(u_user_axi4_if)" in bus_fabric
assert ".dma_axi4(u_dma_axi4_if)" in bus_fabric
assert ".sdio0_axi4(u_sdio0_axi4_if)" in bus_fabric
assert ".sdio1_axi4(u_sdio1_axi4_if)" in bus_fabric
assert ".usb2_axi4(u_usb2_axi4_if)" in bus_fabric
assert ".cfg_axi4(u_cfg_axi4_if)" in bus_fabric
assert ".system_axi4(u_system_axi4_if)" in bus_fabric
assert ".axi4(u_system_axi4_if)" in apb_system_fabric
assert ".usb2_axi4(u_usb2_axi4_if)" in apb_periph_fabric
assert "`define SOC_IRQ_VECTOR_WIDTH 32" in irq_config
assert "`define SOC_USER_IRQ_MASK 32'h004EFBFC" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 19" in irq_config
assert "`define SOC_IRQ_APB4_PERIPH_WIDTH 20" in irq_config
assert "`define SOC_IRQ_APB4_SYSTEM_WIDTH 5" in irq_config
assert "assign irq_o[0] = u_clint_if.software_irq_o[0];" in rib_irq
assert "assign irq_o[10] = ws2812.irq_o;" in rib_irq
@@ -140,6 +144,7 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "assign irq_o[16] = sdio0.irq_o;" in rib_irq
assert "assign irq_o[17] = sdio1.irq_o;" in rib_irq
assert "assign irq_o[18] = s_crypto_irq;" in rib_irq
assert "assign irq_o[19] = s_usb2_irq;" in rib_irq
assert "assign irq_o[0] = pwm.irq_o;" in apb_irq
assert "assign irq_o[4] = s_rng_irq;" in apb_irq
assert "s_irq[10] = s_apb4_periph_irq[16];" in irq_wiring
@@ -149,12 +154,14 @@ def test_topology_generates_complete_rib_apb_and_gpio_bindings(tmp_path: Path) -
assert "s_irq[22] = s_apb4_periph_irq[15];" in irq_wiring
assert "s_irq[21] = s_apb4_periph_irq[17];" in irq_wiring
assert "s_irq[23] = s_apb4_periph_irq[18];" in irq_wiring
assert "s_irq[24] = s_apb4_periph_irq[19];" in irq_wiring
assert "irq_i[10] == apb4_periph_irq_i[16]" in irq_sva
assert "irq_i[15] == apb4_periph_irq_i[13]" in irq_sva
assert "irq_i[20] == apb4_periph_irq_i[14]" in irq_sva
assert "irq_i[22] == apb4_periph_irq_i[15]" in irq_sva
assert "irq_i[21] == apb4_periph_irq_i[17]" in irq_sva
assert "irq_i[23] == apb4_periph_irq_i[18]" in irq_sva
assert "irq_i[24] == apb4_periph_irq_i[19]" in irq_sva
assert "irq_i[31] == 1'b0" in irq_sva
assert "bind retrosoc soc_irq_topology_sva" in irq_sva
assert filelist.startswith("+incdir+")
@@ -198,6 +205,7 @@ def test_topology_preserves_default_irq_compatibility_mapping() -> None:
("opipsram", "apb4_periph", 15, 22, "opipsram.irq_o"),
("sdio1", "apb4_periph", 17, 21, "sdio1.irq_o"),
("crypto", "apb4_periph", 18, 23, "s_crypto_irq"),
("usb2", "apb4_periph", 19, 24, "s_usb2_irq"),
]
+14
View File
@@ -17,6 +17,7 @@ DOMAINS = ROOT / "rtl/mini/integration/clock_reset_domains.json"
PIN_MAP = ROOT / "rtl/mini/pin_map/pin_map.json"
GENERATOR = ROOT / "physical/smoke/sta/opensta/generate_sdc.py"
OPENSTA_MAKEFILE = ROOT / "physical/smoke/sta/opensta/opensta.mk"
PDK_TIMING_MAKEFILE = ROOT / "physical/smoke/sta/opensta/pdk_timing.mk"
RCU = ROOT / "rtl/mini/top/rcu.sv"
RETROSOC_ASIC = ROOT / "rtl/mini/top/retrosoc_asic.sv"
GF180_GENERATOR = ROOT / "physical/pdk/generate_gf180_liberty.py"
@@ -52,9 +53,14 @@ def test_core_sdc_covers_current_clock_domains(tmp_path: Path) -> None:
assert "create_clock -name clk_audio -period 54.253472222" in sdc
assert "create_clock -name clk_jtag -period 100" in sdc
assert "create_clock -name clk_dvp -period 41.666666667" in sdc
assert "create_clock -name clk_usb2_ulpi -period 16.666666667" in sdc
assert "get_pins -quiet" in sdc
assert "get_ports -quiet" in sdc
assert "set clk_jtag_port [require_ports \"clock jtag\" {jtag_tck_i_pad}]" in sdc
assert (
"set clk_usb2_ulpi_port [require_ports \"clock usb2_ulpi\" "
"{usb2_ulpi_clk_i_pad}]"
) in sdc
assert "u_retrosoc.u_apb4_periph.u_axi4_dvp.u_dvp_pclk_clk_buf/clk_o" in sdc
assert "-group [get_clocks {clk_external clk_system}]" in sdc
assert "set_clock_transition 0.1 [get_clocks {clk_dvp}]" in sdc
@@ -79,6 +85,14 @@ def test_opensta_invocation_exits_after_a_tcl_error() -> None:
assert "$(OPENSTA) -no_init -exit -threads $(OPENSTA_THREADS)" in makefile
def test_ihp130_core_sta_loads_usb2_packet_ram_liberties() -> None:
makefile = PDK_TIMING_MAKEFILE.read_text(encoding="utf-8")
assert "RM_IHPSG13_1P_4096x16_c3_bm_bist_slow_1p08V_125C.lib" in makefile
assert "RM_IHPSG13_1P_4096x8_c3_bm_bist_slow_1p08V_125C.lib" in makefile
assert "OPENSTA_SRAM_LIBS := $(IHP130_USB2_SRAM_LIBS)" in makefile
def test_core_sdc_rejects_pads_missing_from_the_pin_map(tmp_path: Path) -> None:
document = json.loads(DOMAINS.read_text(encoding="utf-8"))
document["domains"][3]["sta"]["source_port"] = "missing_pad"
+233
View File
@@ -0,0 +1,233 @@
"""Focused standalone USB2 RTL regressions."""
from __future__ import annotations
import shutil
import subprocess
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BUILD = ROOT / "build/test-usb2"
CONVERTER = ROOT / "rtl/mini/script/convt_sv2v.py"
def test_usb2_drain_stream_has_registered_ready_cut() -> None:
source = (ROOT / "rtl/ip/usb/apb4_usb2.sv").read_text(encoding="utf-8")
assert "u_drain_stream_spill" in source
assert ".ready_o(s_drain_stream_ready_ulpi)" in source
assert ".src_ready_o(s_drain_fifo_ready_ulpi)" in source
assert source.count(".BUFFER_DEPTH(8)") == 2
assert ".BUFFER_DEPTH(32)" not in source
def test_usb2_packet_store_registers_ecc_read_data() -> None:
source = (ROOT / "rtl/ip/usb/usb2_packet_store.sv").read_text(encoding="utf-8")
assert "u_read_word_dffer" in source
assert ".en_i (s_ram_read_valid)" in source
assert "assign drain_data_o = s_read_word_q;" in source
assert "assign tx_data_o = s_read_word_q" in source
def _run_iverilog(name: str, top: str, sources: list[str], marker: str) -> None:
iverilog = shutil.which("iverilog")
vvp = shutil.which("vvp")
if iverilog is None or vvp is None:
return
BUILD.mkdir(parents=True, exist_ok=True)
filelist = BUILD / f"{name}.fl"
filelist.write_text(
"\n".join(
[
"+define+SV_ASSRT_DISABLE",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/interface'}",
f"+incdir+{ROOT / 'rtl/managed/clusterip/common/rtl/utils'}",
f"+incdir+{ROOT / 'rtl/ip/usb'}",
*(str(ROOT / source) for source in sources),
]
)
+ "\n",
encoding="utf-8",
)
converted = BUILD / f"{name}.v"
simulation = BUILD / f"{name}.vvp"
subprocess.run(
["python3", str(CONVERTER), "-f", str(filelist), "--output", str(converted)],
check=True,
)
subprocess.run(
[
iverilog,
"-g2012",
"-DSV_ASSRT_DISABLE",
"-s",
top,
"-o",
str(simulation),
str(converted),
],
check=True,
)
result = subprocess.run(
[vvp, str(simulation)], check=True, capture_output=True, text=True
)
assert marker in result.stdout
def test_usb2_pid_and_crc() -> None:
_run_iverilog(
"pkg",
"usb2_pkg_tb",
["rtl/ip/usb/usb2_pkg.sv", "tests/rtl/usb2_pkg_tb.sv"],
"USB2 PID and CRC vectors passed",
)
def test_usb2_descriptor_validation() -> None:
_run_iverilog(
"descriptor",
"usb2_descriptor_tb",
[
"rtl/ip/usb/usb2_pkg.sv",
"rtl/ip/usb/usb2_dma_descriptor.sv",
"tests/rtl/usb2_descriptor_tb.sv",
],
"USB2 descriptor validation passed",
)
def test_usb2_packet_ram_ecc() -> None:
_run_iverilog(
"packet-ram",
"usb2_packet_ram_tb",
[
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/managed/clusterip/common/rtl/base/ecc_secded.sv",
"rtl/tech/tc_usb2_packet_ram.sv",
"rtl/ip/usb/usb2_packet_ram.sv",
"tests/rtl/usb2_packet_ram_tb.sv",
],
"USB2 packet RAM ECC test passed",
)
def test_usb2_sie_packets() -> None:
_run_iverilog(
"sie",
"usb2_sie_tb",
[
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/ip/usb/usb2_pkg.sv",
"rtl/ip/usb/usb2_sie_rx.sv",
"rtl/ip/usb/usb2_sie_tx.sv",
"tests/rtl/usb2_sie_tb.sv",
],
"USB2 SIE packet encode/decode passed",
)
def test_usb2_register_contract() -> None:
_run_iverilog(
"register",
"usb2_reg_tb",
[
"rtl/managed/clusterip/common/rtl/interface/apb4_if.sv",
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/ip/usb/usb2_define.svh",
"rtl/ip/usb/usb2_reg.sv",
"tests/rtl/usb2_reg_tb.sv",
],
"USB2 APB register contract passed",
)
def test_usb2_descriptor_dma() -> None:
_run_iverilog(
"dma",
"usb2_dma_tb",
[
"rtl/managed/clusterip/common/rtl/interface/axi4_if.sv",
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/ip/usb/usb2_pkg.sv",
"rtl/ip/peripheral/dma_axi4_master.sv",
"rtl/ip/usb/usb2_dma_descriptor.sv",
"rtl/ip/usb/usb2_dma.sv",
"tests/rtl/sdio_axi_memory_responder.sv",
"tests/rtl/usb2_dma_tb.sv",
],
"USB2 descriptor DMA test passed",
)
def test_usb2_packet_store_paths() -> None:
_run_iverilog(
"packet-store",
"usb2_packet_store_tb",
[
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/managed/clusterip/common/rtl/base/ecc_secded.sv",
"rtl/tech/tc_usb2_packet_ram.sv",
"rtl/ip/usb/usb2_packet_ram.sv",
"rtl/ip/usb/usb2_packet_store.sv",
"tests/rtl/usb2_packet_store_tb.sv",
],
"USB2 packet store paths passed",
)
def test_usb2_transaction_engine_host_out() -> None:
_run_iverilog(
"transaction-engine",
"usb2_transaction_engine_tb",
[
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/managed/clusterip/common/rtl/base/ecc_secded.sv",
"rtl/tech/tc_usb2_packet_ram.sv",
"rtl/ip/usb/usb2_pkg.sv",
"rtl/ip/usb/usb2_packet_ram.sv",
"rtl/ip/usb/usb2_packet_store.sv",
"rtl/ip/usb/usb2_transaction_engine.sv",
"tests/rtl/usb2_transaction_engine_tb.sv",
],
"USB2 transaction engine host OUT passed",
)
def test_usb2_link_domain_smoke() -> None:
_run_iverilog(
"link-domain",
"usb2_link_domain_tb",
[
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/managed/clusterip/common/rtl/base/ecc_secded.sv",
"rtl/tech/tc_usb2_packet_ram.sv",
"rtl/ip/usb/usb2_pkg.sv",
"rtl/ip/usb/usb2_ulpi_if.sv",
"rtl/ip/usb/usb2_packet_ram.sv",
"rtl/ip/usb/usb2_packet_store.sv",
"rtl/ip/usb/usb2_ulpi_link.sv",
"rtl/ip/usb/usb2_sie_rx.sv",
"rtl/ip/usb/usb2_sie_tx.sv",
"rtl/ip/usb/usb2_transaction_engine.sv",
"rtl/ip/usb/usb2_link_domain.sv",
"tests/rtl/usb2_link_domain_tb.sv",
],
"USB2 link domain smoke passed",
)
def test_usb2_scheduler_queue_and_interval() -> None:
_run_iverilog(
"scheduler",
"usb2_scheduler_tb",
[
"rtl/managed/clusterip/common/rtl/utils/register.sv",
"rtl/ip/usb/usb2_pkg.sv",
"rtl/ip/usb/usb2_scheduler.sv",
"tests/rtl/usb2_scheduler_tb.sv",
],
"USB2 scheduler queue and interval passed",
)
+56
View File
@@ -0,0 +1,56 @@
"""Check the handwritten USB2 ABI against the RTL register contract."""
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
RTL_DEFINE = ROOT / "rtl/ip/usb/usb2_define.svh"
C_HEADER = ROOT / "crt/include/retrosoc/hal/usb2_regs.h"
def _rtl_values() -> dict[str, int]:
values: dict[str, int] = {}
pattern = re.compile(
r"^`define\s+(APB4_USB2__\w+)\s+"
r"(?:(\d+)'([hHdDbB])([0-9a-fA-F_xXzZ]+)|(\d+))\s*$"
)
for line in RTL_DEFINE.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is None:
continue
name, width, base, digits, decimal = match.groups()
del width
if decimal is not None:
values[name] = int(decimal, 10)
else:
radix = {"h": 16, "d": 10, "b": 2}[base.lower()]
values[name] = int(digits.replace("_", ""), radix)
return values
def _c_values() -> dict[str, int]:
values: dict[str, int] = {}
pattern = re.compile(
r"^#define\s+(RS_USB2_ABI_\w+)\s+"
r"(?:UINT32_C\((0[xX][0-9a-fA-F]+)\)|(\d+)[uU]?)\s*$"
)
for line in C_HEADER.read_text(encoding="utf-8").splitlines():
match = pattern.match(line)
if match is None:
continue
name, hexadecimal, decimal = match.groups()
values[name.removeprefix("RS_USB2_ABI_")] = (
int(hexadecimal, 16) if hexadecimal is not None else int(decimal, 10)
)
return values
def test_usb2_register_constants_match_expected_abi() -> None:
rtl = _rtl_values()
c_header = _c_values()
assert c_header == {
name.removeprefix("APB4_USB2__"): value for name, value in rtl.items()
}