mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
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:
86 files changed
+11305
-48
No files matched your search
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,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
@@ -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.
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,4 +22,5 @@
|
||||
+incdir+/ip/multimedia
|
||||
+incdir+/ip/peripheral
|
||||
+incdir+/ip/security
|
||||
+incdir+/ip/usb
|
||||
+incdir+/third_party/model/sdram/
|
||||
@@ -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
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
/tech/tc_opipsram_delay.sv
|
||||
/tech/tc_pll.sv
|
||||
/tech/tc_sram.sv
|
||||
/tech/tc_usb2_packet_ram.sv
|
||||
@@ -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",
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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",
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -15,6 +15,7 @@ CDC_PRIMITIVES = {
|
||||
"async_fifo",
|
||||
"async_reqack",
|
||||
"cdc_2phase",
|
||||
"cdc_fifo",
|
||||
"cdc_sync",
|
||||
"cdc_fifo_warm_flush",
|
||||
"edge_det",
|
||||
|
||||
@@ -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
@@ -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,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
@@ -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()
|
||||
}
|
||||
Reference in new issue
Block a user